mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-02-06 09:02:20 +08:00
[dm][firmware][scmi] support ARM-SCMI interface #11069
Some checks failed
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
doc_doxygen / doxygen_doc generate (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
doc_doxygen / deploy (push) Has been cancelled
Weekly CI Scheduler / Trigger and Monitor CIs (push) Has been cancelled
Weekly CI Scheduler / Create Discussion Report (push) Has been cancelled
Some checks failed
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
doc_doxygen / doxygen_doc generate (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
doc_doxygen / deploy (push) Has been cancelled
Weekly CI Scheduler / Trigger and Monitor CIs (push) Has been cancelled
Weekly CI Scheduler / Create Discussion Report (push) Has been cancelled
This commit is contained in:
@@ -30,6 +30,7 @@ rsource "ata/Kconfig"
|
||||
rsource "nvme/Kconfig"
|
||||
rsource "block/Kconfig"
|
||||
rsource "scsi/Kconfig"
|
||||
rsource "firmware/Kconfig"
|
||||
rsource "hwcache/Kconfig"
|
||||
rsource "regulator/Kconfig"
|
||||
rsource "reset/Kconfig"
|
||||
|
||||
@@ -4,6 +4,12 @@ menuconfig RT_USING_CLK
|
||||
select RT_USING_ADT_REF
|
||||
default y
|
||||
|
||||
config RT_CLK_SCMI
|
||||
bool "Clock driver controlled via SCMI interface"
|
||||
depends on RT_USING_CLK
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
if RT_USING_CLK
|
||||
osource "$(SOC_DM_CLK_DIR)/Kconfig"
|
||||
endif
|
||||
|
||||
@@ -14,6 +14,8 @@ src = ['clk.c']
|
||||
if GetDepend(['RT_USING_OFW']):
|
||||
src += ['clk-fixed-rate.c']
|
||||
|
||||
if GetDepend(['RT_CLK_SCMI']):
|
||||
src += ['clk-scmi.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
|
||||
411
components/drivers/clk/clk-scmi.c
Executable file
411
components/drivers/clk/clk-scmi.c
Executable file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "clk.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct scmi_clk
|
||||
{
|
||||
struct rt_clk_node parent;
|
||||
|
||||
struct rt_scmi_device *sdev;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_clk(raw) rt_container_of(raw, struct scmi_clk, parent)
|
||||
|
||||
struct scmi_clk_data
|
||||
{
|
||||
struct rt_clk_cell cell;
|
||||
|
||||
int id;
|
||||
rt_bool_t rate_discrete;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int rates_nr;
|
||||
rt_uint64_t rates[];
|
||||
} list;
|
||||
struct
|
||||
{
|
||||
rt_uint64_t min_rate;
|
||||
rt_uint64_t max_rate;
|
||||
rt_uint64_t step_size;
|
||||
} range;
|
||||
} info;
|
||||
};
|
||||
|
||||
#define cell_to_scmi_clk_data(cell) rt_container_of(cell, struct scmi_clk_data, cell)
|
||||
|
||||
static rt_err_t scmi_clk_op_gate(struct scmi_clk *sclk, int clk_id, rt_bool_t enable)
|
||||
{
|
||||
struct scmi_clk_state_in in =
|
||||
{
|
||||
.clock_id = rt_cpu_to_le32(clk_id),
|
||||
.attributes = rt_cpu_to_le32(enable),
|
||||
};
|
||||
struct scmi_clk_state_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_CONFIG_SET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sclk->sdev, &msg);
|
||||
}
|
||||
|
||||
static rt_base_t scmi_clk_op_get_rate(struct scmi_clk *sclk, int clk_id)
|
||||
{
|
||||
rt_ubase_t res;
|
||||
struct scmi_clk_rate_get_in in =
|
||||
{
|
||||
.clock_id = rt_cpu_to_le32(clk_id),
|
||||
};
|
||||
struct scmi_clk_rate_get_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_RATE_GET, &in, &out);
|
||||
|
||||
res = rt_scmi_process_msg(sclk->sdev, &msg);
|
||||
|
||||
if ((rt_base_t)res >= 0)
|
||||
{
|
||||
res = (rt_ubase_t)(((rt_uint64_t)out.rate_msb << 32) | out.rate_lsb);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static rt_base_t scmi_clk_op_set_rate(struct scmi_clk *sclk, int clk_id, rt_ubase_t rate)
|
||||
{
|
||||
struct scmi_clk_rate_set_in in =
|
||||
{
|
||||
.clock_id = rt_cpu_to_le32(clk_id),
|
||||
.flags = rt_cpu_to_le32(SCMI_CLK_RATE_ROUND_CLOSEST),
|
||||
.rate_lsb = rt_cpu_to_le32((rt_uint32_t)rate),
|
||||
.rate_msb = rt_cpu_to_le32((rt_uint32_t)((rt_uint64_t)rate >> 32)),
|
||||
};
|
||||
struct scmi_clk_rate_set_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_RATE_SET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sclk->sdev, &msg);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_clk_enable(struct rt_clk_cell *cell)
|
||||
{
|
||||
struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
|
||||
struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
return scmi_clk_op_gate(sclk, clk_data->id, RT_TRUE);
|
||||
}
|
||||
|
||||
static void scmi_clk_disable(struct rt_clk_cell *cell)
|
||||
{
|
||||
struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
|
||||
struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
scmi_clk_op_gate(sclk, clk_data->id, RT_FALSE);
|
||||
}
|
||||
|
||||
static rt_ubase_t scmi_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
|
||||
{
|
||||
struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
|
||||
struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
return scmi_clk_op_get_rate(sclk, clk_data->id);
|
||||
}
|
||||
|
||||
static rt_base_t scmi_clk_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate)
|
||||
{
|
||||
rt_uint64_t fmin, fmax, ftmp;
|
||||
struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
if (clk_data->rate_discrete)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
fmin = clk_data->info.range.min_rate;
|
||||
fmax = clk_data->info.range.max_rate;
|
||||
|
||||
if (drate <= fmin)
|
||||
{
|
||||
return fmin;
|
||||
}
|
||||
|
||||
if (drate >= fmax)
|
||||
{
|
||||
return fmax;
|
||||
}
|
||||
|
||||
ftmp = drate - fmin;
|
||||
ftmp += clk_data->info.range.step_size - 1;
|
||||
rt_do_div(ftmp, clk_data->info.range.step_size);
|
||||
|
||||
return ftmp * clk_data->info.range.step_size + fmin;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_clk_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_ubase_t res_rate;
|
||||
struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
|
||||
struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
if (!(err = scmi_clk_op_set_rate(sclk, clk_data->id, rate)))
|
||||
{
|
||||
res_rate = scmi_clk_op_get_rate(sclk, clk_data->id);
|
||||
|
||||
if ((rt_err_t)res_rate < 0)
|
||||
{
|
||||
err = (rt_err_t)res_rate;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_clk_ops scmi_clk_ops =
|
||||
{
|
||||
.enable = scmi_clk_enable,
|
||||
.disable = scmi_clk_disable,
|
||||
.recalc_rate = scmi_clk_recalc_rate,
|
||||
.round_rate = scmi_clk_round_rate,
|
||||
.set_rate = scmi_clk_set_rate,
|
||||
};
|
||||
|
||||
static rt_err_t scmi_clk_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_size_t cell_count, out_size;
|
||||
struct rt_scmi_msg msg;
|
||||
struct rt_device *dev = &sdev->parent;
|
||||
struct rt_clk_cell **cells_list = RT_NULL, *cell;
|
||||
struct scmi_clk_data *clk_data;
|
||||
struct scmi_clk_describe_rates_out *out = RT_NULL;
|
||||
struct scmi_clk_describe_rates_in in;
|
||||
struct scmi_clk_attributes attr;
|
||||
struct scmi_clk_name_in name_in;
|
||||
struct scmi_clk_name_out name_out;
|
||||
struct scmi_clk_describe_attributes_in clk_attr_in;
|
||||
struct scmi_clk_describe_attributes_out clk_attr_out;
|
||||
struct scmi_clk *sclk = rt_calloc(1, sizeof(*sclk));
|
||||
|
||||
if (!sclk)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
sclk->sdev = sdev;
|
||||
|
||||
msg = RT_SCMI_MSG_OUT(SCMI_COM_MSG_ATTRIBUTES, &attr);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
cell_count = rt_le16_to_cpu(attr.num_clocks);
|
||||
|
||||
if (!(cells_list = rt_calloc(cell_count, sizeof(*cells_list))))
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
out_size = rt_offsetof(struct scmi_clk_describe_rates_out, rate[SCMI_MAX_NUM_RATES]);
|
||||
|
||||
if (!(out = rt_malloc(out_size)))
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
for (int id = 0; id < cell_count; ++id)
|
||||
{
|
||||
const char *clk_name;
|
||||
rt_uint32_t flags, rates_nr, rate_discrete;
|
||||
|
||||
in.id = rt_cpu_to_le32(id);
|
||||
in.rate_index = rt_cpu_to_le32(0);
|
||||
msg = RT_SCMI_MSG_RAW(SCMI_CLOCK_DESCRIBE_RATES, &in, sizeof(in), out, out_size);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
flags = rt_le32_to_cpu(out->num_rates_flags);
|
||||
rates_nr = SCMI_NUM_REMAINING(flags);
|
||||
rate_discrete = SCMI_RATE_DISCRETE(flags);
|
||||
|
||||
if (rate_discrete)
|
||||
{
|
||||
clk_data = rt_malloc(rt_offsetof(struct scmi_clk_data,
|
||||
info.list.rates[SCMI_MAX_NUM_RATES]));
|
||||
}
|
||||
else
|
||||
{
|
||||
clk_data = rt_malloc(sizeof(*clk_data));
|
||||
}
|
||||
|
||||
if (!clk_data)
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate_discrete)
|
||||
{
|
||||
for (int i = 0; i < rates_nr; ++i)
|
||||
{
|
||||
clk_data->info.list.rates[i] = SCMI_RATE_TO_U64(out->rate[i]);
|
||||
}
|
||||
|
||||
clk_data->info.list.rates_nr = rates_nr;
|
||||
}
|
||||
else
|
||||
{
|
||||
clk_data->info.range.min_rate = SCMI_RATE_TO_U64(out->rate[0]);
|
||||
clk_data->info.range.max_rate = SCMI_RATE_TO_U64(out->rate[1]);
|
||||
clk_data->info.range.step_size = SCMI_RATE_TO_U64(out->rate[2]);
|
||||
}
|
||||
|
||||
clk_data->rate_discrete = rate_discrete;
|
||||
clk_data->id = id;
|
||||
|
||||
cell = &clk_data->cell;
|
||||
rt_memset(cell, 0, sizeof(*cell));
|
||||
|
||||
clk_attr_in.clock_id = rt_cpu_to_le32(id);
|
||||
msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_ATTRIBUTES, &clk_attr_in, &clk_attr_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
|
||||
{
|
||||
rt_free(clk_data);
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (SUPPORTS_EXTENDED_NAMES(clk_attr_out.attributes))
|
||||
{
|
||||
name_in.clock_id = rt_cpu_to_le32(id);
|
||||
msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_NAME_GET, &name_in, &name_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
|
||||
{
|
||||
rt_free(clk_data);
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
clk_name = (const char *)name_out.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
clk_name = (const char *)clk_attr_out.name;
|
||||
}
|
||||
|
||||
if (!(cell->name = rt_strdup(clk_name)))
|
||||
{
|
||||
rt_free(clk_data);
|
||||
err = -RT_ENOMEM;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
cell->ops = &scmi_clk_ops;
|
||||
cell->flags = RT_CLK_F_GET_RATE_NOCACHE;
|
||||
|
||||
cells_list[id] = cell;
|
||||
}
|
||||
|
||||
sclk->parent.dev = dev;
|
||||
sclk->parent.cells = cells_list;
|
||||
sclk->parent.cells_nr = cell_count;
|
||||
|
||||
if ((err = rt_clk_register(&sclk->parent)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
for (int id = 0; id < cell_count; ++id)
|
||||
{
|
||||
struct rt_clk *clk = rt_clk_cell_get_clk(cells_list[id], RT_NULL);
|
||||
|
||||
if (clk)
|
||||
{
|
||||
rt_ubase_t min_rate, max_rate;
|
||||
|
||||
clk_data = cell_to_scmi_clk_data(cells_list[id]);
|
||||
|
||||
if (clk_data->rate_discrete)
|
||||
{
|
||||
min_rate = clk_data->info.list.rates[0];
|
||||
max_rate = clk_data->info.list.rates[clk_data->info.list.rates_nr - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
min_rate = clk_data->info.range.min_rate;
|
||||
max_rate = clk_data->info.range.max_rate;
|
||||
}
|
||||
|
||||
rt_clk_set_rate_range(clk, min_rate, max_rate);
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
if (out)
|
||||
{
|
||||
rt_free(out);
|
||||
}
|
||||
|
||||
if (cells_list)
|
||||
{
|
||||
for (int id = 0; id < cell_count; ++id)
|
||||
{
|
||||
if (!cells_list[id])
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cell = cells_list[id];
|
||||
clk_data = cell_to_scmi_clk_data(cell);
|
||||
|
||||
rt_free(clk_data);
|
||||
}
|
||||
|
||||
rt_free(cells_list);
|
||||
}
|
||||
|
||||
rt_free(sclk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_clk_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_CLOCK, "clocks" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_clk_driver =
|
||||
{
|
||||
.name = "clk-scmi",
|
||||
.ids = scmi_clk_ids,
|
||||
|
||||
.probe = scmi_clk_probe,
|
||||
};
|
||||
|
||||
static int scmi_clk_drv_register(void)
|
||||
{
|
||||
rt_scmi_driver_register(&scmi_clk_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_SUBSYS_EXPORT(scmi_clk_drv_register);
|
||||
9
components/drivers/firmware/Kconfig
Executable file
9
components/drivers/firmware/Kconfig
Executable file
@@ -0,0 +1,9 @@
|
||||
menuconfig RT_USING_FIRMWARE
|
||||
bool "Using Firmware"
|
||||
depends on RT_USING_DM
|
||||
default n
|
||||
|
||||
if RT_USING_FIRMWARE
|
||||
rsource "arm_scmi/Kconfig"
|
||||
osource "$(SOC_DM_FIRMWARE_DIR)/Kconfig"
|
||||
endif
|
||||
15
components/drivers/firmware/SConscript
Executable file
15
components/drivers/firmware/SConscript
Executable file
@@ -0,0 +1,15 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
list = os.listdir(cwd)
|
||||
objs = []
|
||||
|
||||
if not GetDepend('RT_USING_FIRMWARE'):
|
||||
Return('objs')
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
|
||||
Return('objs')
|
||||
23
components/drivers/firmware/arm_scmi/Kconfig
Executable file
23
components/drivers/firmware/arm_scmi/Kconfig
Executable file
@@ -0,0 +1,23 @@
|
||||
menuconfig RT_FIRMWARE_ARM_SCMI
|
||||
bool "ARM System Control and Management Interface Protocol (SCMI)"
|
||||
depends on RT_USING_FIRMWARE
|
||||
depends on ARCH_ARM_CORTEX_A || ARCH_ARMV8
|
||||
depends on RT_USING_OFW
|
||||
default n
|
||||
|
||||
config RT_FIRMWARE_ARM_SCMI_TRANSPORT_MAILBOX
|
||||
bool "SCMI transport based on mailbox"
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
depends on RT_USING_MBOX
|
||||
default y
|
||||
|
||||
config RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC
|
||||
bool "SCMI transport based on SMC"
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default y
|
||||
|
||||
if RT_VIRTIO_SCMI
|
||||
config RT_FIRMWARE_ARM_SCMI_TRANSPORT_VIRTIO
|
||||
bool
|
||||
default y
|
||||
endif
|
||||
21
components/drivers/firmware/arm_scmi/SConscript
Executable file
21
components/drivers/firmware/arm_scmi/SConscript
Executable file
@@ -0,0 +1,21 @@
|
||||
from building import *
|
||||
|
||||
group = []
|
||||
|
||||
if not GetDepend(['RT_FIRMWARE_ARM_SCMI']):
|
||||
Return('group')
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
CPPPATH = [cwd + '/../../include']
|
||||
|
||||
src = ['agent.c', 'bus.c', 'shmem.c']
|
||||
|
||||
if GetDepend(['RT_FIRMWARE_ARM_SCMI_TRANSPORT_MAILBOX']):
|
||||
src += ['agent-mailbox.c']
|
||||
|
||||
if GetDepend(['RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC']):
|
||||
src += ['agent-smc.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
176
components/drivers/firmware/arm_scmi/agent-mailbox.c
Executable file
176
components/drivers/firmware/arm_scmi/agent-mailbox.c
Executable file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
|
||||
#include <drivers/mailbox.h>
|
||||
#include <dt-bindings/size.h>
|
||||
|
||||
#define DBG_TAG "scmi.agent.mailbox"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
struct scmi_agent_mailbox
|
||||
{
|
||||
struct rt_mbox_client mbox_client;
|
||||
|
||||
struct rt_mbox_chan *chan;
|
||||
struct scmi_shared_mem *shmem;
|
||||
|
||||
struct rt_spinlock lock;
|
||||
};
|
||||
#define raw_to_scmi_mailbox(raw) rt_container_of(raw, struct scmi_agent_mailbox, mbox_client)
|
||||
|
||||
static void scmi_mailbox_rx_callback(struct rt_mbox_client *client, void *data)
|
||||
{
|
||||
struct rt_scmi_msg *msg = data;
|
||||
|
||||
if (msg->rx_callback)
|
||||
{
|
||||
msg->rx_callback(msg->sdev, msg->out_msg, msg->out_msg_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void scmi_mailbox_tx_prepare(struct rt_mbox_client *client, const void *data)
|
||||
{
|
||||
struct rt_scmi_msg *msg = (void *)data;
|
||||
struct scmi_agent_mailbox *ambox = raw_to_scmi_mailbox(client);
|
||||
|
||||
scmi_shmem_msg_write(ambox->shmem, msg);
|
||||
}
|
||||
|
||||
static void scmi_mailbox_tx_done(struct rt_mbox_client *client, const void *data,
|
||||
rt_err_t err)
|
||||
{
|
||||
struct scmi_agent_mailbox *ambox = raw_to_scmi_mailbox(client);
|
||||
|
||||
if (!err)
|
||||
{
|
||||
scmi_shmem_clear_channel(ambox->shmem);
|
||||
}
|
||||
}
|
||||
|
||||
static rt_err_t scmi_agent_mailbox_setup(struct scmi_agent *agent,
|
||||
struct rt_device *dev)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_uint64_t shm_addr, shm_size;
|
||||
int mbox_chan, mbox_count, shmem_count;
|
||||
struct rt_ofw_node *np = dev->ofw_node, *shmem_np;
|
||||
struct scmi_agent_mailbox *ambox = rt_calloc(1, sizeof(*ambox));
|
||||
|
||||
if (!ambox)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
mbox_count = rt_ofw_count_phandle_cells(np, "mboxes", "#mbox-cells");
|
||||
shmem_count = rt_ofw_count_phandle_cells(np, "shmem", RT_NULL);
|
||||
|
||||
if (mbox_count < 0)
|
||||
{
|
||||
err = mbox_count;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (shmem_count < 0)
|
||||
{
|
||||
err = shmem_count;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
mbox_chan = 0;
|
||||
if (mbox_count == 2 && shmem_count == 2)
|
||||
{
|
||||
mbox_chan = 1;
|
||||
}
|
||||
else if (mbox_count == 3)
|
||||
{
|
||||
mbox_chan = 2;
|
||||
}
|
||||
|
||||
ambox->mbox_client.dev = dev;
|
||||
ambox->mbox_client.rx_callback = scmi_mailbox_rx_callback;
|
||||
ambox->mbox_client.tx_prepare = scmi_mailbox_tx_prepare;
|
||||
ambox->mbox_client.tx_done = scmi_mailbox_tx_done;
|
||||
|
||||
ambox->chan = rt_mbox_request_by_index(&ambox->mbox_client, mbox_chan);
|
||||
if (rt_is_err_or_null(ambox->chan))
|
||||
{
|
||||
err = -RT_EIO;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
shmem_np = rt_ofw_parse_phandle(np, "shmem", 0);
|
||||
|
||||
if (!rt_ofw_node_is_compatible(shmem_np, "arm,scmi-shmem"))
|
||||
{
|
||||
err = -RT_EINVAL;
|
||||
rt_ofw_node_put(shmem_np);
|
||||
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((err = rt_ofw_get_address(shmem_np, 0, &shm_addr, &shm_size)))
|
||||
{
|
||||
rt_ofw_node_put(shmem_np);
|
||||
goto _fail;
|
||||
}
|
||||
rt_ofw_node_put(shmem_np);
|
||||
|
||||
ambox->shmem = rt_ioremap((void *)shm_addr, shm_size);
|
||||
|
||||
if (!ambox->shmem)
|
||||
{
|
||||
err = -RT_EIO;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
agent->priv = ambox;
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
if (!rt_is_err_or_null(ambox->chan))
|
||||
{
|
||||
rt_mbox_release(ambox->chan);
|
||||
}
|
||||
if (ambox->shmem)
|
||||
{
|
||||
rt_iounmap(ambox->shmem);
|
||||
}
|
||||
rt_free(ambox);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_agent_mailbox_process_msg(struct scmi_agent *agent,
|
||||
struct rt_scmi_msg *msg)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct scmi_agent_mailbox *ambox = agent->priv;
|
||||
|
||||
rt_hw_spin_lock(&ambox->lock.lock);
|
||||
|
||||
err = rt_mbox_send(ambox->chan, (const void *)msg, 30);
|
||||
|
||||
rt_hw_spin_unlock(&ambox->lock.lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct scmi_agent_ops scmi_agent_mailbox_ops =
|
||||
{
|
||||
.name = "mailbox",
|
||||
.setup = scmi_agent_mailbox_setup,
|
||||
.process_msg = scmi_agent_mailbox_process_msg,
|
||||
};
|
||||
200
components/drivers/firmware/arm_scmi/agent-smc.c
Executable file
200
components/drivers/firmware/arm_scmi/agent-smc.c
Executable file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
|
||||
#include <mmu.h>
|
||||
#include <smccc.h>
|
||||
#include <cpuport.h>
|
||||
#include <dt-bindings/size.h>
|
||||
|
||||
#define DBG_TAG "scmi.agent.smc"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
struct scmi_agent_smc
|
||||
{
|
||||
int irq;
|
||||
rt_uint32_t func_id;
|
||||
|
||||
#define SHMEM_SIZE (4 * SIZE_KB)
|
||||
#define SHMEM_SHIFT 12
|
||||
#define SHMEM_PAGE(x) (((rt_ubase_t)(x) >> SHMEM_SHIFT))
|
||||
#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1))
|
||||
rt_uint32_t param_page;
|
||||
rt_uint32_t param_offset;
|
||||
rt_ubase_t cap_id;
|
||||
|
||||
rt_bool_t done;
|
||||
|
||||
struct rt_spinlock lock;
|
||||
struct scmi_shared_mem *shmem;
|
||||
};
|
||||
|
||||
static void scmi_agent_smc_isr(int irqno, void *param)
|
||||
{
|
||||
struct scmi_agent_smc *asmc = param;
|
||||
|
||||
HWREG32(&asmc->done) = RT_TRUE;
|
||||
rt_hw_dmb();
|
||||
}
|
||||
|
||||
static rt_err_t scmi_agent_smc_setup(struct scmi_agent *agent,
|
||||
struct rt_device *dev)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_uint64_t shm_addr, shm_size;
|
||||
struct rt_ofw_node *np = dev->ofw_node, *shmem_np;
|
||||
struct scmi_agent_smc *asmc = rt_calloc(1, sizeof(*asmc));
|
||||
|
||||
if (!asmc)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
if ((err = rt_ofw_prop_read_u32(np, "arm,smc-id", &asmc->func_id)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
shmem_np = rt_ofw_parse_phandle(np, "shmem", 0);
|
||||
|
||||
if (!rt_ofw_node_is_compatible(shmem_np, "arm,scmi-shmem"))
|
||||
{
|
||||
err = -RT_EINVAL;
|
||||
rt_ofw_node_put(shmem_np);
|
||||
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((err = rt_ofw_get_address(shmem_np, 0, &shm_addr, &shm_size)))
|
||||
{
|
||||
rt_ofw_node_put(shmem_np);
|
||||
goto _fail;
|
||||
}
|
||||
rt_ofw_node_put(shmem_np);
|
||||
|
||||
asmc->shmem = rt_ioremap((void *)shm_addr, shm_size);
|
||||
|
||||
if (!asmc->shmem)
|
||||
{
|
||||
err = -RT_EIO;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (rt_ofw_node_is_compatible(np, "qcom,scmi-smc"))
|
||||
{
|
||||
void *cap_id_map = (void *)asmc->shmem + (shm_size - 8);
|
||||
|
||||
rt_memcpy(&asmc->cap_id, cap_id_map, sizeof(asmc->cap_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
asmc->cap_id = ~0UL;
|
||||
}
|
||||
|
||||
if (rt_ofw_node_is_compatible(np, "arm,scmi-smc-param"))
|
||||
{
|
||||
rt_ubase_t base = (rt_ubase_t)rt_kmem_v2p(asmc->shmem);
|
||||
|
||||
asmc->param_page = SHMEM_PAGE(base);
|
||||
asmc->param_offset = SHMEM_OFFSET(base);
|
||||
}
|
||||
|
||||
asmc->irq = rt_ofw_get_irq_by_name(np, "a2p");
|
||||
|
||||
if (asmc->irq >= 0)
|
||||
{
|
||||
rt_hw_interrupt_install(asmc->irq, scmi_agent_smc_isr, asmc, "scmi");
|
||||
rt_hw_interrupt_umask(asmc->irq);
|
||||
}
|
||||
|
||||
rt_spin_lock_init(&asmc->lock);
|
||||
|
||||
agent->priv = asmc;
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
if (asmc->shmem)
|
||||
{
|
||||
rt_iounmap(asmc->shmem);
|
||||
}
|
||||
rt_free(asmc);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_agent_smc_process_msg(struct scmi_agent *agent,
|
||||
struct rt_scmi_msg *msg)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct arm_smccc_res_t res;
|
||||
struct scmi_shared_mem *shmem;
|
||||
struct scmi_agent_smc *asmc = agent->priv;
|
||||
|
||||
rt_spin_lock(&asmc->lock);
|
||||
|
||||
if (asmc->irq >= 0)
|
||||
{
|
||||
while (HWREG32(&asmc->done))
|
||||
{
|
||||
rt_hw_cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
shmem = asmc->shmem;
|
||||
|
||||
if ((err = scmi_shmem_msg_write(shmem, msg)))
|
||||
{
|
||||
goto _out_lock;
|
||||
}
|
||||
|
||||
if (asmc->irq >= 0)
|
||||
{
|
||||
HWREG32(&asmc->done) = RT_FALSE;
|
||||
}
|
||||
|
||||
if (asmc->cap_id == ~0UL)
|
||||
{
|
||||
arm_smccc_smc(asmc->func_id, asmc->param_page, asmc->param_offset,
|
||||
0, 0, 0, 0, 0, &res, RT_NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
arm_smccc_smc(asmc->func_id, asmc->cap_id,
|
||||
0, 0, 0, 0, 0, 0, &res, RT_NULL);
|
||||
}
|
||||
|
||||
if (res.a0)
|
||||
{
|
||||
err = -RT_EIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = scmi_shmem_msg_read(shmem, msg);
|
||||
}
|
||||
|
||||
scmi_shmem_clear_channel(shmem);
|
||||
|
||||
_out_lock:
|
||||
rt_spin_unlock(&asmc->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct scmi_agent_ops scmi_agent_smc_ops =
|
||||
{
|
||||
.name = "smc",
|
||||
.setup = scmi_agent_smc_setup,
|
||||
.process_msg = scmi_agent_smc_process_msg,
|
||||
};
|
||||
177
components/drivers/firmware/arm_scmi/agent.c
Executable file
177
components/drivers/firmware/arm_scmi/agent.c
Executable file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
#define DBG_TAG "scmi.agent"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
rt_err_t rt_scmi_process_msg(struct rt_scmi_device *sdev, struct rt_scmi_msg *msg)
|
||||
{
|
||||
struct scmi_agent *agent;
|
||||
|
||||
if (!sdev || !msg)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
agent = sdev->agent;
|
||||
msg->sdev = sdev;
|
||||
|
||||
return agent->ops->process_msg(agent, msg);
|
||||
}
|
||||
|
||||
static const char * const _scmi_error_table[] =
|
||||
{
|
||||
[-SCMI_SUCCESS] = "SUCCESS",
|
||||
[-SCMI_ERR_SUPPORT] = "SUPPORT",
|
||||
[-SCMI_ERR_PARAMS] = "PARAMS",
|
||||
[-SCMI_ERR_ACCESS] = "ACCESS",
|
||||
[-SCMI_ERR_ENTRY] = "ENTRY",
|
||||
[-SCMI_ERR_RANGE] = "RANGE",
|
||||
[-SCMI_ERR_BUSY] = "BUSY",
|
||||
[-SCMI_ERR_COMMS] = "COMMS",
|
||||
[-SCMI_ERR_GENERIC] = "GENERIC",
|
||||
[-SCMI_ERR_HARDWARE] = "HARDWARE",
|
||||
[-SCMI_ERR_PROTOCOL] = "PROTOCOL",
|
||||
};
|
||||
|
||||
const char *rt_scmi_strerror(rt_base_t err)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
err = err < 0 ? -err : err;
|
||||
|
||||
if (err < RT_ARRAY_SIZE(_scmi_error_table))
|
||||
{
|
||||
str = _scmi_error_table[err];
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "UNKNOWN";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_channels_setup(struct scmi_agent *agent,
|
||||
struct rt_platform_device *pdev)
|
||||
{
|
||||
struct rt_ofw_node *np = pdev->parent.ofw_node, *chn;
|
||||
|
||||
rt_ofw_foreach_available_child_node(np, chn)
|
||||
{
|
||||
rt_uint32_t prot_id;
|
||||
struct rt_scmi_device *sdev;
|
||||
|
||||
if (rt_ofw_prop_read_u32(chn, "reg", &prot_id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sdev = rt_calloc(1, sizeof(*sdev));
|
||||
|
||||
if (!sdev)
|
||||
{
|
||||
rt_ofw_node_put(chn);
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
sdev->parent.ofw_node = chn;
|
||||
sdev->protocol_id = prot_id;
|
||||
sdev->agent = agent;
|
||||
|
||||
rt_scmi_device_register(sdev);
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_probe(struct rt_platform_device *pdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct rt_scmi_device *base_sdev;
|
||||
struct scmi_agent *agent = rt_malloc(sizeof(*agent) + sizeof(*base_sdev));
|
||||
const struct scmi_agent_ops *agent_ops;
|
||||
|
||||
if (!agent)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
agent_ops = pdev->id->data;
|
||||
agent->ops = agent_ops;
|
||||
|
||||
if ((err = agent_ops->setup(agent, &pdev->parent)))
|
||||
{
|
||||
LOG_E("Setup interface %s error = %s", agent_ops->name, rt_strerror(err));
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((err = scmi_channels_setup(agent, pdev)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
base_sdev = (void *)&agent[1];
|
||||
rt_memset(base_sdev, 0, sizeof(*base_sdev));
|
||||
|
||||
base_sdev->protocol_id = SCMI_PROTOCOL_ID_BASE;
|
||||
base_sdev->agent = agent;
|
||||
|
||||
if ((err = rt_scmi_device_register(base_sdev)))
|
||||
{
|
||||
LOG_W("Base protocol register error = %s", rt_strerror(err));
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
rt_free(agent);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
extern struct rt_scmi_agent_ops scmi_agent_mailbox_ops;
|
||||
extern struct rt_scmi_agent_ops scmi_agent_smc_ops;
|
||||
extern struct rt_scmi_agent_ops scmi_agent_virtio_ops;
|
||||
|
||||
static const struct rt_ofw_node_id scmi_ofw_ids[] =
|
||||
{
|
||||
#ifdef RT_FIRMWARE_ARM_SCMI_TRANSPORT_MAILBOX
|
||||
{ .compatible = "arm,scmi", .data = &scmi_agent_mailbox_ops },
|
||||
#endif
|
||||
#ifdef RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC
|
||||
{ .compatible = "arm,scmi-smc", .data = &scmi_agent_smc_ops },
|
||||
{ .compatible = "arm,scmi-smc-param", .data = &scmi_agent_smc_ops },
|
||||
{ .compatible = "qcom,scmi-smc", .data = &scmi_agent_smc_ops},
|
||||
#endif
|
||||
#ifdef RT_FIRMWARE_ARM_SCMI_TRANSPORT_VIRTIO
|
||||
{ .compatible = "arm,scmi-virtio", .data = &scmi_agent_virtio_ops },
|
||||
#endif
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct rt_platform_driver scmi_driver =
|
||||
{
|
||||
.name = "arm-scmi",
|
||||
.ids = scmi_ofw_ids,
|
||||
|
||||
.probe = scmi_probe,
|
||||
};
|
||||
|
||||
static int scmi_drv_register(void)
|
||||
{
|
||||
rt_platform_driver_register(&scmi_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_SUBSYS_EXPORT(scmi_drv_register);
|
||||
36
components/drivers/firmware/arm_scmi/agent.h
Executable file
36
components/drivers/firmware/arm_scmi/agent.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __SCMI_AGENT_H__
|
||||
#define __SCMI_AGENT_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#include "shmem.h"
|
||||
|
||||
struct scmi_agent;
|
||||
|
||||
struct scmi_agent_ops
|
||||
{
|
||||
const char *name;
|
||||
|
||||
rt_err_t (*setup)(struct scmi_agent *agent, struct rt_device *dev);
|
||||
rt_err_t (*process_msg)(struct scmi_agent *agent, struct rt_scmi_msg *msg);
|
||||
};
|
||||
|
||||
struct scmi_agent
|
||||
{
|
||||
const struct scmi_agent_ops *ops;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#endif /* __SCMI_AGENT_H__ */
|
||||
84
components/drivers/firmware/arm_scmi/bus.c
Executable file
84
components/drivers/firmware/arm_scmi/bus.c
Executable file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "scmi.bus"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
static struct rt_bus scmi_bus;
|
||||
|
||||
rt_err_t rt_scmi_driver_register(struct rt_scmi_driver *driver)
|
||||
{
|
||||
RT_ASSERT(driver != RT_NULL);
|
||||
|
||||
driver->parent.bus = &scmi_bus;
|
||||
|
||||
return rt_driver_register(&driver->parent);
|
||||
}
|
||||
|
||||
rt_err_t rt_scmi_device_register(struct rt_scmi_device *device)
|
||||
{
|
||||
RT_ASSERT(device != RT_NULL);
|
||||
|
||||
return rt_bus_add_device(&scmi_bus, &device->parent);
|
||||
}
|
||||
static rt_bool_t scmi_match(rt_driver_t drv, rt_device_t dev)
|
||||
{
|
||||
const struct rt_scmi_device_id *id;
|
||||
struct rt_scmi_driver *driver = rt_container_of(drv, struct rt_scmi_driver, parent);
|
||||
struct rt_scmi_device *device = rt_container_of(dev, struct rt_scmi_device, parent);
|
||||
|
||||
for (id = driver->ids; id->protocol_id; ++id)
|
||||
{
|
||||
if (id->protocol_id == device->protocol_id)
|
||||
{
|
||||
if (!id->name || !device->name || !rt_strcmp(id->name, device->name))
|
||||
{
|
||||
return RT_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_probe(rt_device_t dev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct rt_scmi_driver *driver = rt_container_of(dev->drv, struct rt_scmi_driver, parent);
|
||||
struct rt_scmi_device *device = rt_container_of(dev, struct rt_scmi_device, parent);
|
||||
|
||||
if (!device->agent)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
err = driver->probe(device);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct rt_bus scmi_bus =
|
||||
{
|
||||
.name = "scmi",
|
||||
.match = scmi_match,
|
||||
.probe = scmi_probe,
|
||||
};
|
||||
|
||||
static int scmi_bus_init(void)
|
||||
{
|
||||
rt_bus_register(&scmi_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
INIT_CORE_EXPORT(scmi_bus_init);
|
||||
110
components/drivers/firmware/arm_scmi/shmem.c
Executable file
110
components/drivers/firmware/arm_scmi/shmem.c
Executable file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#define DBG_TAG "scmi.shmem"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include "shmem.h"
|
||||
|
||||
struct scmi_shared_mem
|
||||
{
|
||||
rt_le32_t reserved;
|
||||
rt_le32_t channel_status;
|
||||
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR rt_cpu_to_le32(RT_BIT(1))
|
||||
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE rt_cpu_to_le32(RT_BIT(0))
|
||||
rt_le32_t reserved1[2];
|
||||
rt_le32_t flags;
|
||||
#define SCMI_SHMEM_FLAG_INTR_ENABLED rt_cpu_to_le32(RT_BIT(0))
|
||||
rt_le32_t length;
|
||||
rt_le32_t msg_header;
|
||||
rt_uint8_t msg_payload[];
|
||||
};
|
||||
|
||||
rt_err_t scmi_shmem_msg_write(struct scmi_shared_mem *shmem,
|
||||
struct rt_scmi_msg *msg)
|
||||
{
|
||||
if (!shmem || !msg)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if ((!msg->in_msg && msg->in_msg_size) || (!msg->out_msg && msg->out_msg_size))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if (!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))
|
||||
{
|
||||
LOG_E("Channel busy");
|
||||
|
||||
return -RT_EBUSY;
|
||||
}
|
||||
|
||||
/* Load message in shared memory */
|
||||
shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
|
||||
shmem->length = rt_cpu_to_le32(msg->in_msg_size + sizeof(shmem->msg_header));
|
||||
shmem->msg_header = rt_cpu_to_le32(scmi_header(
|
||||
msg->message_id, 0, msg->sdev->protocol_id, 0));
|
||||
|
||||
if (msg->in_msg)
|
||||
{
|
||||
rt_memcpy(shmem->msg_payload, msg->in_msg, msg->in_msg_size);
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t scmi_shmem_msg_read(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg)
|
||||
{
|
||||
rt_size_t length;
|
||||
|
||||
if (!shmem || !msg)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if (!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))
|
||||
{
|
||||
LOG_E("Channel unexpectedly busy");
|
||||
|
||||
return -RT_EBUSY;
|
||||
}
|
||||
|
||||
if (shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR)
|
||||
{
|
||||
LOG_E("Channel error reported, reset channel");
|
||||
|
||||
return -RT_EIO;
|
||||
}
|
||||
|
||||
length = rt_le32_to_cpu(shmem->length);
|
||||
|
||||
if (length > msg->out_msg_size + sizeof(shmem->msg_header))
|
||||
{
|
||||
LOG_E("Buffer < %u too small", length);
|
||||
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
msg->out_msg_size = length - sizeof(shmem->msg_header);
|
||||
|
||||
rt_memcpy(msg->out_msg, shmem->msg_payload, msg->out_msg_size);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
void scmi_shmem_clear_channel(struct scmi_shared_mem *shmem)
|
||||
{
|
||||
if (shmem)
|
||||
{
|
||||
shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
|
||||
}
|
||||
}
|
||||
23
components/drivers/firmware/arm_scmi/shmem.h
Executable file
23
components/drivers/firmware/arm_scmi/shmem.h
Executable file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#ifndef __SCMI_SHMEM_H__
|
||||
#define __SCMI_SHMEM_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
struct scmi_shared_mem;
|
||||
|
||||
rt_err_t scmi_shmem_msg_write(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg);
|
||||
rt_err_t scmi_shmem_msg_read(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg);
|
||||
void scmi_shmem_clear_channel(struct scmi_shared_mem *shmem);
|
||||
|
||||
#endif /* __SCMI_SHMEM_H__ */
|
||||
1092
components/drivers/include/drivers/scmi.h
Executable file
1092
components/drivers/include/drivers/scmi.h
Executable file
File diff suppressed because it is too large
Load Diff
@@ -131,6 +131,12 @@ extern "C" {
|
||||
#include "drivers/thermal.h"
|
||||
#endif /* RT_USING_THERMAL */
|
||||
|
||||
#ifdef RT_USING_FIRMWARE
|
||||
#ifdef RT_FIRMWARE_ARM_SCMI
|
||||
#include "drivers/scmi.h"
|
||||
#endif /* RT_FIRMWARE_ARM_SCMI */
|
||||
#endif /* RT_USING_FIRMWARE */
|
||||
|
||||
#ifdef RT_USING_HWCACHE
|
||||
#include "drivers/hwcache.h"
|
||||
#endif /* RT_USING_HWCACHE */
|
||||
|
||||
@@ -4,6 +4,12 @@ menuconfig RT_USING_PINCTRL
|
||||
depends on RT_USING_PIN
|
||||
default n
|
||||
|
||||
config RT_PINCTRL_SCMI
|
||||
bool "Pinctrl driver via ARM SCMI interface"
|
||||
depends on RT_USING_PINCTRL
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
config RT_PINCTRL_SINGLE
|
||||
bool "Single Pinctrl driver"
|
||||
depends on RT_USING_PINCTRL
|
||||
|
||||
@@ -11,6 +11,9 @@ CPPPATH = [cwd + '/../include']
|
||||
|
||||
src = ['pinctrl.c']
|
||||
|
||||
if GetDepend(['RT_PINCTRL_SCMI']):
|
||||
src += ['pinctrl-scmi.c']
|
||||
|
||||
if GetDepend(['RT_PINCTRL_SINGLE']):
|
||||
src += ['pinctrl-single.c']
|
||||
|
||||
|
||||
485
components/drivers/pinctrl/pinctrl-scmi.c
Executable file
485
components/drivers/pinctrl/pinctrl-scmi.c
Executable file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "pinctrl.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define ATTR_SEL(x) ((rt_uint32_t)(x) & 0x3U)
|
||||
#define ATTR_NUM(n) ((((rt_uint32_t)(n)) & 0xffU) << 2)
|
||||
#define ATTR_FUNCSEL (1u << 10)
|
||||
|
||||
struct scmi_pinctrl_info
|
||||
{
|
||||
rt_uint32_t identifier;
|
||||
char name[64];
|
||||
};
|
||||
|
||||
struct scmi_pinctrl
|
||||
{
|
||||
struct rt_device_pin parent;
|
||||
|
||||
struct rt_scmi_device *sdev;
|
||||
|
||||
rt_size_t pins_nr;
|
||||
struct scmi_pinctrl_info *pins;
|
||||
|
||||
rt_size_t groups_nr;
|
||||
struct scmi_pinctrl_info *groups;
|
||||
|
||||
rt_size_t function_nr;
|
||||
struct scmi_pinctrl_info *function;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_pinctrl(raw) rt_container_of(raw, struct scmi_pinctrl, parent)
|
||||
|
||||
static const struct rt_pin_ctrl_conf_params scmi_conf_params[] =
|
||||
{
|
||||
{ "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
|
||||
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
|
||||
{ "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
|
||||
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
|
||||
{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
|
||||
{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
|
||||
{ "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
|
||||
{ "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
|
||||
{ "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
|
||||
{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
|
||||
{ "drive-strength-microamp", PIN_CONFIG_DRIVE_STRENGTH_UA, 0 },
|
||||
{ "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
|
||||
{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
|
||||
{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
|
||||
{ "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
|
||||
{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
|
||||
{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
|
||||
{ "low-power-disable", PIN_CONFIG_MODE_LOW_POWER, 0 },
|
||||
{ "low-power-enable", PIN_CONFIG_MODE_LOW_POWER, 1 },
|
||||
{ "output-disable", PIN_CONFIG_OUTPUT_ENABLE, 0 },
|
||||
{ "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 },
|
||||
{ "output-high", PIN_CONFIG_OUTPUT, 1, },
|
||||
{ "output-impedance-ohms", PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS, 0 },
|
||||
{ "output-low", PIN_CONFIG_OUTPUT, 0, },
|
||||
{ "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
|
||||
{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
|
||||
};
|
||||
|
||||
static enum scmi_pinctrl_conf_type scmi_conf_params_map[] =
|
||||
{
|
||||
SCMI_PINCTRL_BIAS_BUS_HOLD,
|
||||
SCMI_PINCTRL_BIAS_DISABLE,
|
||||
SCMI_PINCTRL_BIAS_HIGH_IMPEDANCE,
|
||||
SCMI_PINCTRL_BIAS_PULL_UP,
|
||||
SCMI_PINCTRL_BIAS_PULL_DEFAULT,
|
||||
SCMI_PINCTRL_BIAS_PULL_DOWN,
|
||||
SCMI_PINCTRL_DRIVE_OPEN_DRAIN,
|
||||
SCMI_PINCTRL_DRIVE_OPEN_SOURCE,
|
||||
SCMI_PINCTRL_DRIVE_PUSH_PULL,
|
||||
SCMI_PINCTRL_DRIVE_STRENGTH,
|
||||
SCMI_PINCTRL_DRIVE_STRENGTH,
|
||||
SCMI_PINCTRL_INPUT_DEBOUNCE,
|
||||
SCMI_PINCTRL_INPUT_MODE,
|
||||
SCMI_PINCTRL_INPUT_MODE,
|
||||
SCMI_PINCTRL_INPUT_SCHMITT,
|
||||
SCMI_PINCTRL_INPUT_MODE,
|
||||
SCMI_PINCTRL_INPUT_MODE,
|
||||
SCMI_PINCTRL_LOW_POWER_MODE,
|
||||
SCMI_PINCTRL_LOW_POWER_MODE,
|
||||
SCMI_PINCTRL_OUTPUT_MODE,
|
||||
SCMI_PINCTRL_OUTPUT_MODE,
|
||||
SCMI_PINCTRL_OUTPUT_VALUE,
|
||||
SCMI_PINCTRL_OUTPUT_VALUE,
|
||||
SCMI_PINCTRL_OUTPUT_VALUE,
|
||||
SCMI_PINCTRL_POWER_SOURCE,
|
||||
SCMI_PINCTRL_SLEW_RATE,
|
||||
};
|
||||
|
||||
static rt_bool_t scmi_pinconf_prop_name_to_param(const char *propname,
|
||||
rt_uint32_t *default_value, rt_uint32_t *out_type)
|
||||
{
|
||||
const struct rt_pin_ctrl_conf_params *params = scmi_conf_params;
|
||||
|
||||
for (int i = 0; i < RT_ARRAY_SIZE(scmi_conf_params); ++i, ++params)
|
||||
{
|
||||
if (!rt_strcmp(params->propname, propname))
|
||||
{
|
||||
*out_type = scmi_conf_params_map[i];
|
||||
*default_value = params->default_value;
|
||||
|
||||
return RT_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
static rt_bool_t scmi_lookup_id(const struct scmi_pinctrl_info *info,
|
||||
rt_size_t nr, const char *name, rt_uint32_t *out_id)
|
||||
{
|
||||
for (rt_size_t i = 0; i < nr; ++i)
|
||||
{
|
||||
if (!rt_strcmp((const char *)info[i].name, name))
|
||||
{
|
||||
*out_id = info[i].identifier;
|
||||
return RT_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_pinctrl_confs_apply(struct rt_device *device, void *fw_conf_np)
|
||||
{
|
||||
rt_err_t err = RT_EOK;
|
||||
const char *string;
|
||||
rt_uint32_t function_id = 0xffffffffU;
|
||||
rt_size_t pins_nr = 0, groups_nr = 0, params_nr = 0;
|
||||
rt_uint32_t pins_id[32], groups_id[32], params_type[32], params_val[32];
|
||||
struct rt_ofw_prop *prop;
|
||||
struct rt_ofw_node *np = fw_conf_np;
|
||||
struct scmi_pinctrl *spctl = raw_to_scmi_pinctrl(device);
|
||||
|
||||
LOG_D("Pinctrl apply '%s'", rt_ofw_node_full_name(np));
|
||||
|
||||
rt_ofw_foreach_prop(np, prop)
|
||||
{
|
||||
if (!rt_strcmp(prop->name, "phandle"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (!rt_strcmp(prop->name, "groups"))
|
||||
{
|
||||
for (string = rt_ofw_prop_next_string(prop, RT_NULL); string;
|
||||
string = rt_ofw_prop_next_string(prop, string))
|
||||
{
|
||||
if (groups_nr >= RT_ARRAY_SIZE(groups_id))
|
||||
{
|
||||
return -RT_EFULL;
|
||||
}
|
||||
|
||||
if (!scmi_lookup_id(spctl->groups, spctl->groups_nr, string, &groups_id[groups_nr]))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
++groups_nr;
|
||||
}
|
||||
}
|
||||
else if (!rt_strcmp(prop->name, "pins"))
|
||||
{
|
||||
for (string = rt_ofw_prop_next_string(prop, RT_NULL); string;
|
||||
string = rt_ofw_prop_next_string(prop, string))
|
||||
{
|
||||
if (pins_nr >= RT_ARRAY_SIZE(pins_id))
|
||||
{
|
||||
return -RT_EFULL;
|
||||
}
|
||||
|
||||
if (!scmi_lookup_id(spctl->pins, spctl->pins_nr, string, &pins_id[pins_nr]))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
++pins_nr;
|
||||
}
|
||||
}
|
||||
else if (!rt_strcmp(prop->name, "function"))
|
||||
{
|
||||
string = rt_ofw_prop_next_string(prop, RT_NULL);
|
||||
|
||||
if (!scmi_lookup_id(spctl->function, spctl->function_nr, string, &function_id))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (params_nr >= RT_ARRAY_SIZE(params_type))
|
||||
{
|
||||
return -RT_EFULL;
|
||||
}
|
||||
|
||||
if (!scmi_pinconf_prop_name_to_param(prop->name, ¶ms_val[params_nr], ¶ms_type[params_nr]))
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if (prop->length >= sizeof(rt_uint32_t))
|
||||
{
|
||||
rt_ofw_prop_next_u32(prop, RT_NULL, ¶ms_val[params_nr]);
|
||||
}
|
||||
|
||||
++params_nr;
|
||||
}
|
||||
}
|
||||
|
||||
if (function_id != 0xffffffffU)
|
||||
{
|
||||
for (rt_size_t i = 0; i < groups_nr; ++i)
|
||||
{
|
||||
struct scmi_pinctrl_settings_conf_in in =
|
||||
{
|
||||
.identifier = rt_cpu_to_le32(groups_id[i]),
|
||||
.function_id = rt_cpu_to_le32(function_id),
|
||||
.attributes = rt_cpu_to_le32(ATTR_SEL(SCMI_PINCTRL_TYPE_GROUP) | ATTR_FUNCSEL),
|
||||
};
|
||||
struct scmi_pinctrl_settings_conf_out out = {0};
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_PINCTRL_SETTINGS_CONFIGURE, &in, &out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(spctl->sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(out.status) != 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params_nr)
|
||||
{
|
||||
struct
|
||||
{
|
||||
struct scmi_pinctrl_settings_conf_in hdr;
|
||||
rt_le32_t config[2 * 32];
|
||||
} in;
|
||||
struct scmi_pinctrl_settings_conf_out out = {0};
|
||||
struct rt_scmi_msg msg = {
|
||||
.sdev = spctl->sdev,
|
||||
.message_id = SCMI_PINCTRL_SETTINGS_CONFIGURE,
|
||||
.out_msg = (rt_uint8_t *)&out,
|
||||
.out_msg_size = sizeof(out),
|
||||
};
|
||||
|
||||
for (rt_size_t i = 0; i < params_nr; ++i)
|
||||
{
|
||||
in.config[2 * i + 0] = rt_cpu_to_le32(params_type[i]);
|
||||
in.config[2 * i + 1] = rt_cpu_to_le32(params_val[i]);
|
||||
}
|
||||
|
||||
for (rt_size_t i = 0; i < groups_nr; i++)
|
||||
{
|
||||
in.hdr.identifier = rt_cpu_to_le32(groups_id[i]);
|
||||
in.hdr.function_id = rt_cpu_to_le32(0xffffffffU);
|
||||
in.hdr.attributes = rt_cpu_to_le32(ATTR_SEL(SCMI_PINCTRL_TYPE_GROUP) | ATTR_NUM(params_nr));
|
||||
|
||||
msg.in_msg = (rt_uint8_t *)∈
|
||||
msg.in_msg_size = (rt_uint32_t)(sizeof(in.hdr) + params_nr * 2 * sizeof(rt_le32_t));
|
||||
|
||||
if ((err = rt_scmi_process_msg(spctl->sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(out.status) != 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (rt_size_t i = 0; i < pins_nr; i++)
|
||||
{
|
||||
in.hdr.identifier = rt_cpu_to_le32(pins_id[i]);
|
||||
in.hdr.function_id = rt_cpu_to_le32(0xffffffffU);
|
||||
in.hdr.attributes = rt_cpu_to_le32(ATTR_SEL(SCMI_PINCTRL_TYPE_PIN) | ATTR_NUM(params_nr));
|
||||
|
||||
msg.in_msg = (rt_uint8_t *)∈
|
||||
msg.in_msg_size = (rt_uint32_t)(sizeof(in.hdr) + params_nr * 2 * sizeof(rt_le32_t));
|
||||
|
||||
if ((err = rt_scmi_process_msg(spctl->sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(out.status) != 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_pin_ops scmi_pinctrl_ops =
|
||||
{
|
||||
.pin_ctrl_confs_apply = scmi_pinctrl_confs_apply,
|
||||
};
|
||||
|
||||
static rt_err_t scmi_pinctrl_name_parse_one(struct rt_scmi_device *sdev,
|
||||
enum scmi_pinctrl_selector_type sel, rt_size_t id, char *name)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct scmi_pinctrl_attributes_in attr_in =
|
||||
{
|
||||
.identifier = rt_cpu_to_le32(id),
|
||||
.flags = rt_cpu_to_le32(sel),
|
||||
};
|
||||
struct scmi_pinctrl_attributes_out attr_out = {0};
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_PINCTRL_ATTRIBUTES, &attr_in, &attr_out);
|
||||
|
||||
name[0] = '\0';
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(attr_out.status) != 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
rt_strncpy(name, (char *)attr_out.name, sizeof(attr_out.name));
|
||||
|
||||
if (SCMI_PINCTRL_EXT_NAME_FLAG(rt_le32_to_cpu(attr_out.attributes)))
|
||||
{
|
||||
struct scmi_pinctrl_name_get_in name_in =
|
||||
{
|
||||
.identifier = rt_cpu_to_le32(id),
|
||||
.flags = rt_cpu_to_le32(sel),
|
||||
};
|
||||
struct scmi_pinctrl_name_get_out name_out = {0};
|
||||
|
||||
msg = RT_SCMI_MSG_IN_OUT(SCMI_PINCTRL_NAME_GET, &name_in, &name_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(name_out.status) != 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
rt_strncpy(name, (char *)name_out.name, sizeof(name_out.name));
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_pinctrl_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct rt_scmi_msg msg;
|
||||
struct rt_device *dev = &sdev->parent;
|
||||
struct scmi_pinctrl_protocol_attributes protocol_attr_out;
|
||||
struct scmi_pinctrl *spctl = rt_calloc(1, sizeof(*spctl));
|
||||
|
||||
if (!spctl)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
rt_memset(&protocol_attr_out, 0, sizeof(protocol_attr_out));
|
||||
msg = RT_SCMI_MSG_OUT(SCMI_COM_MSG_ATTRIBUTES, &protocol_attr_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (rt_le32_to_cpu(protocol_attr_out.status) != 0)
|
||||
{
|
||||
err = -RT_ERROR;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
spctl->pins_nr = SCMI_PINCTRL_PINS_NR(rt_le32_to_cpu(protocol_attr_out.attributes_low));
|
||||
spctl->groups_nr = SCMI_PINCTRL_GROUPS_NR(rt_le32_to_cpu(protocol_attr_out.attributes_low));
|
||||
spctl->function_nr = SCMI_PINCTRL_FUNCTIONS_NR(rt_le32_to_cpu(protocol_attr_out.attributes_high));
|
||||
|
||||
spctl->pins = rt_malloc(sizeof(*spctl->pins) * spctl->pins_nr);
|
||||
spctl->groups = rt_malloc(sizeof(*spctl->groups) * spctl->groups_nr);
|
||||
spctl->function = rt_malloc(sizeof(*spctl->function) * spctl->function_nr);
|
||||
|
||||
if (!spctl->pins || !spctl->groups || !spctl->function)
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
for (rt_size_t i = 0; i < spctl->pins_nr; ++i)
|
||||
{
|
||||
if (scmi_pinctrl_name_parse_one(sdev, SCMI_PINCTRL_TYPE_PIN, i, spctl->pins[i].name))
|
||||
{
|
||||
LOG_E("%s parse identifier = %d fail", "Pin", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
spctl->pins[i].identifier = i;
|
||||
}
|
||||
|
||||
for (rt_size_t i = 0; i < spctl->groups_nr; ++i)
|
||||
{
|
||||
if (scmi_pinctrl_name_parse_one(sdev, SCMI_PINCTRL_TYPE_GROUP, i, spctl->groups[i].name))
|
||||
{
|
||||
LOG_E("%s parse identifier = %d fail", "Group", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
spctl->groups[i].identifier = i;
|
||||
}
|
||||
|
||||
for (rt_size_t i = 0; i < spctl->function_nr; ++i)
|
||||
{
|
||||
if (scmi_pinctrl_name_parse_one(sdev, SCMI_PINCTRL_TYPE_FUNCTION, i, spctl->function[i].name))
|
||||
{
|
||||
LOG_E("%s parse identifier = %d fail", "Function", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
spctl->function[i].identifier = i;
|
||||
}
|
||||
|
||||
spctl->parent.ops = &scmi_pinctrl_ops;
|
||||
spctl->sdev = sdev;
|
||||
rt_ofw_data(dev->ofw_node) = &spctl->parent;
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
if (spctl->pins)
|
||||
{
|
||||
rt_free(spctl->pins);
|
||||
}
|
||||
|
||||
if (spctl->groups)
|
||||
{
|
||||
rt_free(spctl->groups);
|
||||
}
|
||||
|
||||
if (spctl->function)
|
||||
{
|
||||
rt_free(spctl->function);
|
||||
}
|
||||
|
||||
rt_free(spctl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_pinctrl_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_PINCTRL, "pinctrl" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_pinctrl_driver =
|
||||
{
|
||||
.name = "pinctrl-scmi",
|
||||
.ids = scmi_pinctrl_ids,
|
||||
|
||||
.probe = scmi_pinctrl_probe,
|
||||
};
|
||||
RT_SCMI_DRIVER_EXPORT(scmi_pinctrl_driver);
|
||||
@@ -1,6 +1,11 @@
|
||||
if RT_USING_DM
|
||||
menu "Power Management (PM) Domains device drivers"
|
||||
|
||||
config RT_PMDOMAIN_SCMI
|
||||
bool "ARM SCMI"
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
osource "$(SOC_DM_PMDOMAIN_DIR)/Kconfig"
|
||||
endmenu
|
||||
endif
|
||||
|
||||
@@ -10,6 +10,9 @@ CPPPATH = [cwd + '/../include']
|
||||
|
||||
src = []
|
||||
|
||||
if GetDepend(['RT_PMDOMAIN_SCMI']):
|
||||
src += ['pm-domain-scmi.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
|
||||
134
components/drivers/pmdomain/pm-domain-scmi.c
Executable file
134
components/drivers/pmdomain/pm-domain-scmi.c
Executable file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-21 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "pm-domain.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct scmi_pm_domain_proxy;
|
||||
|
||||
struct scmi_pm_domain
|
||||
{
|
||||
struct rt_dm_power_domain parent;
|
||||
|
||||
rt_uint32_t domain;
|
||||
|
||||
struct scmi_pm_domain_proxy *proxy;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_pm_domain(raw) rt_container_of(raw, struct scmi_pm_domain, parent)
|
||||
|
||||
struct scmi_pm_domain_proxy
|
||||
{
|
||||
struct rt_dm_power_domain_proxy parent;
|
||||
|
||||
struct rt_scmi_device *sdev;
|
||||
|
||||
rt_uint32_t num_domains;
|
||||
struct scmi_pm_domain domains[];
|
||||
};
|
||||
|
||||
#define raw_to_scmi_pm_domain_proxy(raw) rt_container_of(raw, struct scmi_pm_domain_proxy, parent)
|
||||
|
||||
static rt_err_t scmi_pm_domain_power(struct scmi_pm_domain *scmi_pd, rt_bool_t power_on)
|
||||
{
|
||||
struct scmi_power_state_set_in in =
|
||||
{
|
||||
.flags = rt_cpu_to_le32(0),
|
||||
.domain = rt_cpu_to_le32(scmi_pd->domain),
|
||||
.state = rt_cpu_to_le32(power_on ? SCMI_POWER_STATE_GENERIC_ON : SCMI_POWER_STATE_GENERIC_OFF),
|
||||
};
|
||||
struct scmi_power_state_set_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_POWER_STATE_SET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(scmi_pd->proxy->sdev, &msg);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_pd_power_on(struct rt_dm_power_domain *domain)
|
||||
{
|
||||
return scmi_pm_domain_power(raw_to_scmi_pm_domain(domain), RT_TRUE);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_pd_power_off(struct rt_dm_power_domain *domain)
|
||||
{
|
||||
return scmi_pm_domain_power(raw_to_scmi_pm_domain(domain), RT_FALSE);
|
||||
}
|
||||
|
||||
static struct rt_dm_power_domain *scmi_pm_domain_proxy_ofw_parse(
|
||||
struct rt_dm_power_domain_proxy *proxy, struct rt_ofw_cell_args *args)
|
||||
{
|
||||
struct scmi_pm_domain_proxy *scmi_proxy = raw_to_scmi_pm_domain_proxy(proxy);
|
||||
|
||||
return &scmi_proxy->domains[args->args[0]].parent;
|
||||
}
|
||||
|
||||
static rt_err_t scmi_pm_domain_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_uint32_t num_domains;
|
||||
struct scmi_pm_domain *scmi_pds;
|
||||
struct scmi_pm_domain_proxy *scmi_proxy;
|
||||
struct scmi_power_attributes attr = {};
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_OUT(SCMI_COM_MSG_ATTRIBUTES, &attr);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
num_domains = rt_le16_to_cpu(attr.num_domains);
|
||||
scmi_proxy = rt_calloc(1, sizeof(*scmi_proxy) + sizeof(*scmi_pds) * num_domains);
|
||||
|
||||
if (!scmi_proxy)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
scmi_proxy->sdev = sdev;
|
||||
scmi_proxy->num_domains = num_domains;
|
||||
|
||||
scmi_pds = scmi_proxy->domains;
|
||||
|
||||
for (int i = 0; i < num_domains; ++i, ++scmi_pds)
|
||||
{
|
||||
struct rt_dm_power_domain *domain = &scmi_pds->parent;
|
||||
|
||||
domain->power_off = scmi_pd_power_off;
|
||||
domain->power_on = scmi_pd_power_on;
|
||||
|
||||
scmi_pds->domain = i;
|
||||
scmi_pds->proxy = scmi_proxy;
|
||||
|
||||
rt_dm_power_domain_register(domain);
|
||||
}
|
||||
|
||||
scmi_proxy->parent.ofw_parse = scmi_pm_domain_proxy_ofw_parse;
|
||||
rt_dm_power_domain_proxy_ofw_bind(&scmi_proxy->parent, sdev->parent.ofw_node);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_pm_domain_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_POWER, "genpd" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_pm_domain_driver =
|
||||
{
|
||||
.name = "pm-domain-scmi",
|
||||
.ids = scmi_pm_domain_ids,
|
||||
|
||||
.probe = scmi_pm_domain_probe,
|
||||
};
|
||||
RT_SCMI_DRIVER_EXPORT(scmi_pm_domain_driver);
|
||||
@@ -18,6 +18,13 @@ config RT_REGULATOR_GPIO
|
||||
depends on RT_USING_PIN
|
||||
default y
|
||||
|
||||
config RT_REGULATOR_SCMI
|
||||
bool "SCMI regulator support"
|
||||
depends on RT_USING_REGULATOR
|
||||
depends on RT_USING_OFW
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
if RT_USING_REGULATOR
|
||||
osource "$(SOC_DM_REGULATOR_DIR)/Kconfig"
|
||||
endif
|
||||
|
||||
@@ -16,6 +16,9 @@ if GetDepend(['RT_REGULATOR_FIXED']):
|
||||
if GetDepend(['RT_REGULATOR_GPIO']):
|
||||
src += ['regulator-gpio.c']
|
||||
|
||||
if GetDepend(['RT_REGULATOR_SCMI']):
|
||||
src += ['regulator-scmi.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
|
||||
206
components/drivers/regulator/regulator-scmi.c
Executable file
206
components/drivers/regulator/regulator-scmi.c
Executable file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-21 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "regulator.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct scmi_regulator
|
||||
{
|
||||
struct rt_regulator_node parent;
|
||||
struct rt_regulator_param param;
|
||||
|
||||
struct rt_device dev;
|
||||
struct rt_scmi_device *sdev;
|
||||
|
||||
rt_uint32_t domain_id;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_regulator(raw) rt_container_of(raw, struct scmi_regulator, parent)
|
||||
|
||||
static rt_err_t scmi_regulator_set_enable(struct scmi_regulator *sreg, rt_bool_t enable)
|
||||
{
|
||||
struct scmi_voltage_config_set_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(sreg->domain_id),
|
||||
.config = rt_cpu_to_le32(enable ? SCMI_VOLTAGE_CONFIG_ON : SCMI_VOLTAGE_CONFIG_OFF),
|
||||
};
|
||||
struct scmi_voltage_config_set_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_VOLTAGE_DOMAIN_CONFIG_SET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sreg->sdev, &msg) ? : (out.status ? -RT_ERROR : RT_EOK);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_regulator_enable(struct rt_regulator_node *reg_np)
|
||||
{
|
||||
struct scmi_regulator *sreg = raw_to_scmi_regulator(reg_np);
|
||||
|
||||
return scmi_regulator_set_enable(sreg, RT_TRUE);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_regulator_disable(struct rt_regulator_node *reg_np)
|
||||
{
|
||||
struct scmi_regulator *sreg = raw_to_scmi_regulator(reg_np);
|
||||
|
||||
return scmi_regulator_set_enable(sreg, RT_FALSE);
|
||||
}
|
||||
|
||||
static rt_bool_t scmi_regulator_is_enabled(struct rt_regulator_node *reg_np)
|
||||
{
|
||||
struct scmi_regulator *sreg = raw_to_scmi_regulator(reg_np);
|
||||
struct scmi_voltage_config_get_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(sreg->domain_id),
|
||||
};
|
||||
struct scmi_voltage_config_get_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_VOLTAGE_DOMAIN_CONFIG_GET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sreg->sdev, &msg) ? RT_FALSE : (out.config == SCMI_VOLTAGE_CONFIG_ON);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_regulator_set_voltage(struct rt_regulator_node *reg_np,
|
||||
int min_uvolt, int max_uvolt)
|
||||
{
|
||||
struct scmi_regulator *sreg = raw_to_scmi_regulator(reg_np);
|
||||
struct scmi_voltage_level_set_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(sreg->domain_id),
|
||||
.voltage_level = rt_cpu_to_le32((max_uvolt + min_uvolt) >> 1),
|
||||
};
|
||||
struct scmi_voltage_level_set_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_VOLTAGE_DOMAIN_LEVEL_SET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sreg->sdev, &msg);
|
||||
}
|
||||
|
||||
static int scmi_regulator_get_voltage(struct rt_regulator_node *reg_np)
|
||||
{
|
||||
struct scmi_regulator *sreg = raw_to_scmi_regulator(reg_np);
|
||||
struct scmi_voltage_level_get_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(sreg->domain_id),
|
||||
};
|
||||
struct scmi_voltage_level_get_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_VOLTAGE_DOMAIN_LEVEL_GET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(sreg->sdev, &msg) ? : out.voltage_level;
|
||||
}
|
||||
|
||||
static const struct rt_regulator_ops scmi_regulator_voltage_ops =
|
||||
{
|
||||
.enable = scmi_regulator_enable,
|
||||
.disable = scmi_regulator_disable,
|
||||
.is_enabled = scmi_regulator_is_enabled,
|
||||
.set_voltage = scmi_regulator_set_voltage,
|
||||
.get_voltage = scmi_regulator_get_voltage,
|
||||
};
|
||||
|
||||
static rt_err_t scmi_regulator_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
rt_uint32_t dom_nr;
|
||||
struct rt_regulator_node *rnp;
|
||||
struct rt_ofw_node *np, *child_np;
|
||||
struct scmi_regulator *sregs = RT_NULL, *sreg;
|
||||
|
||||
np = rt_ofw_get_child_by_tag(sdev->parent.ofw_node, "regulators");
|
||||
|
||||
if (!np)
|
||||
{
|
||||
err = -RT_EINVAL;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
dom_nr = rt_ofw_get_child_count(np);
|
||||
|
||||
if (!dom_nr)
|
||||
{
|
||||
err = -RT_EEMPTY;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
sregs = rt_calloc(dom_nr, sizeof(*sregs));
|
||||
|
||||
if (!sregs)
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
sreg = &sregs[0];
|
||||
|
||||
rt_ofw_foreach_child_node(np, child_np)
|
||||
{
|
||||
if ((err = rt_ofw_prop_read_u32(child_np, "reg", &sreg->domain_id)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
sreg->sdev = sdev;
|
||||
|
||||
rnp = &sreg->parent;
|
||||
sreg->dev.ofw_node = child_np;
|
||||
|
||||
rt_ofw_prop_read_string(child_np, "regulator-name", &rnp->supply_name);
|
||||
rnp->ops = &scmi_regulator_voltage_ops;
|
||||
rnp->param = &sreg->param;
|
||||
rnp->dev = &sreg->dev;
|
||||
|
||||
++sreg;
|
||||
}
|
||||
rt_ofw_node_put(np);
|
||||
|
||||
sreg = &sregs[0];
|
||||
|
||||
for (int i = 0; i < dom_nr; ++i, ++sreg)
|
||||
{
|
||||
if ((err = rt_regulator_register(&sreg->parent)))
|
||||
{
|
||||
while (i --> 0)
|
||||
{
|
||||
--sreg;
|
||||
|
||||
rt_regulator_unregister(&sreg->parent);
|
||||
}
|
||||
|
||||
goto _fail;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
rt_ofw_node_put(np);
|
||||
|
||||
if (sregs)
|
||||
{
|
||||
rt_free(sregs);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_regulator_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_VOLTAGE, "regulator" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_regulator_driver =
|
||||
{
|
||||
.name = "scmi-regulator",
|
||||
.ids = scmi_regulator_ids,
|
||||
|
||||
.probe = scmi_regulator_probe,
|
||||
};
|
||||
RT_SCMI_DRIVER_EXPORT(scmi_regulator_driver);
|
||||
@@ -4,6 +4,12 @@ menuconfig RT_USING_RESET
|
||||
depends on RT_USING_OFW
|
||||
default n
|
||||
|
||||
config RT_RESET_SCMI
|
||||
bool "Reset driver controlled via ARM SCMI interface"
|
||||
depends on RT_USING_RESET
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
config RT_RESET_SIMPLE
|
||||
bool "Simple Reset Controller Driver"
|
||||
depends on RT_USING_RESET
|
||||
|
||||
@@ -13,6 +13,9 @@ src = ['reset.c']
|
||||
if GetDepend(['RT_RESET_SIMPLE']):
|
||||
src += ['reset-simple.c']
|
||||
|
||||
if GetDepend(['RT_RESET_SCMI']):
|
||||
src += ['reset-scmi.c']
|
||||
|
||||
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
|
||||
129
components/drivers/reset/reset-scmi.c
Executable file
129
components/drivers/reset/reset-scmi.c
Executable file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "reset.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct scmi_reset
|
||||
{
|
||||
struct rt_reset_controller parent;
|
||||
|
||||
struct rt_scmi_device *sdev;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_reset(raw) rt_container_of(raw, struct scmi_reset, parent)
|
||||
|
||||
static rt_err_t scmi_reset_do(struct scmi_reset *srst, int domain,
|
||||
rt_uint32_t flags, rt_uint32_t state)
|
||||
{
|
||||
struct scmi_reset_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(domain),
|
||||
.flags = rt_cpu_to_le32(flags),
|
||||
.reset_state = rt_cpu_to_le32(state),
|
||||
};
|
||||
struct scmi_reset_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_RESET_RESET, &in, &out);
|
||||
|
||||
return rt_scmi_process_msg(srst->sdev, &msg);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_reset_reset(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct scmi_reset *srst = raw_to_scmi_reset(rstc);
|
||||
|
||||
return scmi_reset_do(srst, rstc->id, SCMI_RESET_FLAG_RESET, SCMI_ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_reset_assert(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct scmi_reset *srst = raw_to_scmi_reset(rstc);
|
||||
|
||||
return scmi_reset_do(srst, rstc->id, SCMI_RESET_FLAG_ASSERT, SCMI_ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_reset_deassert(struct rt_reset_control *rstc)
|
||||
{
|
||||
struct scmi_reset *srst = raw_to_scmi_reset(rstc);
|
||||
|
||||
return scmi_reset_do(srst, rstc->id, 0, SCMI_ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static rt_err_t scmi_reset_ofw_parse(struct rt_reset_control *rstc,
|
||||
struct rt_ofw_cell_args *args)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct scmi_reset *srst = raw_to_scmi_reset(rstc);
|
||||
struct scmi_reset_attr_in in =
|
||||
{
|
||||
.domain_id = rt_cpu_to_le32(rstc->id),
|
||||
};
|
||||
struct scmi_reset_attr_out out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_RESET_DOMAIN_ATTRIBUTES, &in, &out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(srst->sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
return rt_le32_to_cpu(out.status) == 0 ? RT_EOK : -RT_ERROR;
|
||||
}
|
||||
|
||||
static const struct rt_reset_control_ops scmi_reset_ops =
|
||||
{
|
||||
.ofw_parse = scmi_reset_ofw_parse,
|
||||
.reset = scmi_reset_reset,
|
||||
.assert = scmi_reset_assert,
|
||||
.deassert = scmi_reset_deassert,
|
||||
};
|
||||
|
||||
static rt_err_t scmi_reset_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct rt_reset_controller *rstcer;
|
||||
struct scmi_reset *srst = rt_calloc(1, sizeof(*srst));
|
||||
|
||||
if (!srst)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
rstcer = &srst->parent;
|
||||
|
||||
rstcer->priv = srst;
|
||||
rstcer->ofw_node = sdev->parent.ofw_node;
|
||||
rstcer->ops = &scmi_reset_ops;
|
||||
|
||||
if ((err = rt_reset_controller_register(&srst->parent)))
|
||||
{
|
||||
rt_free(srst);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_reset_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_RESET, "reset" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_reset_driver =
|
||||
{
|
||||
.name = "reset-scmi",
|
||||
.ids = scmi_reset_ids,
|
||||
|
||||
.probe = scmi_reset_probe,
|
||||
};
|
||||
RT_SCMI_DRIVER_EXPORT(scmi_reset_driver);
|
||||
@@ -7,6 +7,12 @@ if RT_USING_THERMAL
|
||||
comment "Thermal Sensors Drivers"
|
||||
endif
|
||||
|
||||
config RT_THERMAL_SCMI
|
||||
bool "ARM SCMI interface"
|
||||
depends on RT_USING_THERMAL
|
||||
depends on RT_FIRMWARE_ARM_SCMI
|
||||
default n
|
||||
|
||||
if RT_USING_THERMAL
|
||||
osource "$(SOC_DM_THERMAL_DIR)/Kconfig"
|
||||
endif
|
||||
|
||||
@@ -10,6 +10,9 @@ CPPPATH = [cwd + '/../include']
|
||||
|
||||
src = ['thermal.c', 'thermal_dm.c']
|
||||
|
||||
if GetDepend(['RT_THERMAL_SCMI']):
|
||||
src += ['thermal-scmi.c']
|
||||
|
||||
if GetDepend(['RT_THERMAL_COOL_PWM_FAN']):
|
||||
src += ['thermal-cool-pwm-fan.c']
|
||||
|
||||
|
||||
170
components/drivers/thermal/thermal-scmi.c
Executable file
170
components/drivers/thermal/thermal-scmi.c
Executable file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-11-26 GuEe-GUI first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "thermal.scmi"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct scmi_thermal
|
||||
{
|
||||
struct rt_thermal_zone_device parent;
|
||||
|
||||
rt_uint32_t sensor_id;
|
||||
rt_uint32_t scale;
|
||||
struct rt_scmi_device *sdev;
|
||||
};
|
||||
|
||||
#define raw_to_scmi_thermal(raw) rt_container_of(raw, struct scmi_thermal, parent)
|
||||
|
||||
static rt_err_t scmi_thermal_zone_get_temp(struct rt_thermal_zone_device *zdev,
|
||||
int *out_temp)
|
||||
{
|
||||
int scale;
|
||||
rt_err_t err;
|
||||
rt_uint64_t value, factor = 1;
|
||||
struct scmi_thermal *st = raw_to_scmi_thermal(zdev);
|
||||
struct scmi_sensor_reading_in reading_in =
|
||||
{
|
||||
.id = rt_cpu_to_le32(st->sensor_id),
|
||||
.flags = rt_cpu_to_le32(0),
|
||||
};
|
||||
struct scmi_sensor_reading_out reading_out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_SENSOR_READING_GET, &reading_in, &reading_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(st->sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
value = rt_le32_to_cpu(reading_out.value_high);
|
||||
value <<= 32;
|
||||
value |= rt_le32_to_cpu(reading_out.value_low);
|
||||
|
||||
scale = st->scale + 3;
|
||||
|
||||
if (scale == 0)
|
||||
{
|
||||
goto _end;
|
||||
}
|
||||
|
||||
if (scale > 19 || scale < -19)
|
||||
{
|
||||
return -RT_EIO;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rt_abs(scale); i++)
|
||||
{
|
||||
factor *= 10;
|
||||
}
|
||||
|
||||
if (scale > 0)
|
||||
{
|
||||
value *= factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = rt_div_u64(value, factor);
|
||||
}
|
||||
|
||||
_end:
|
||||
*out_temp = (int)value;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
const static struct rt_thermal_zone_ops scmi_thermal_zone_ops =
|
||||
{
|
||||
.get_temp = scmi_thermal_zone_get_temp,
|
||||
};
|
||||
|
||||
static rt_err_t scmi_thermal_probe(struct rt_scmi_device *sdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct scmi_sensor_attributes attr = {};
|
||||
struct scmi_sensor_description_get_out *desc_out;
|
||||
struct rt_scmi_msg msg = RT_SCMI_MSG_OUT(SCMI_COM_MSG_ATTRIBUTES, &attr);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
desc_out = rt_malloc(sizeof(*desc_out) + sizeof(desc_out->desc[0]));
|
||||
|
||||
if (!desc_out)
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
|
||||
for (int i = 0, ts_nr = 0; i < attr.num_sensors; ++i)
|
||||
{
|
||||
struct scmi_thermal *st;
|
||||
struct rt_thermal_zone_device *tz;
|
||||
struct scmi_sensor_description_get_in desc_in;
|
||||
|
||||
desc_in.desc_index = i;
|
||||
msg = RT_SCMI_MSG_IN_OUT(SCMI_SENSOR_DESCRIPTION_GET, &desc_in, desc_out);
|
||||
|
||||
if ((err = rt_scmi_process_msg(sdev, &msg)))
|
||||
{
|
||||
goto _end;
|
||||
}
|
||||
|
||||
if (SCMI_SENSOR_TYPE(rt_le32_to_cpu(desc_out->desc[0].attributes_high)) !=
|
||||
SCMI_SENSOR_TYPE_TEMPERATURE_C)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(st = rt_calloc(1, sizeof(*st))))
|
||||
{
|
||||
err = -RT_ENOMEM;
|
||||
goto _end;
|
||||
}
|
||||
|
||||
st->sdev = sdev;
|
||||
st->sensor_id = rt_le32_to_cpu(desc_out->desc[0].id);
|
||||
st->scale = SCMI_SENSOR_SCALE(desc_out->desc[0].attributes_high);
|
||||
|
||||
tz = &st->parent;
|
||||
tz->zone_id = ts_nr;
|
||||
tz->ops = &scmi_thermal_zone_ops;
|
||||
tz->parent.ofw_node = sdev->parent.ofw_node;
|
||||
|
||||
rt_dm_dev_set_name(&tz->parent, "scmi-%s", desc_out->desc[0].name);
|
||||
|
||||
rt_thermal_zone_device_register(tz);
|
||||
|
||||
++ts_nr;
|
||||
}
|
||||
|
||||
_end:
|
||||
rt_free(desc_out);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rt_scmi_device_id scmi_thermal_ids[] =
|
||||
{
|
||||
{ SCMI_PROTOCOL_ID_SENSOR, "thermal" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct rt_scmi_driver scmi_thermal_driver =
|
||||
{
|
||||
.name = "thermal-scmi",
|
||||
.ids = scmi_thermal_ids,
|
||||
|
||||
.probe = scmi_thermal_probe,
|
||||
};
|
||||
RT_SCMI_DRIVER_EXPORT(scmi_thermal_driver);
|
||||
Reference in New Issue
Block a user