diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 87e37864bd0..a654ae4f652 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -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" diff --git a/components/drivers/clk/Kconfig b/components/drivers/clk/Kconfig index c13ccbf2591..6cc65c3c2f5 100755 --- a/components/drivers/clk/Kconfig +++ b/components/drivers/clk/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 diff --git a/components/drivers/clk/SConscript b/components/drivers/clk/SConscript index 928697863d3..09c1e27c763 100644 --- a/components/drivers/clk/SConscript +++ b/components/drivers/clk/SConscript @@ -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) diff --git a/components/drivers/clk/clk-scmi.c b/components/drivers/clk/clk-scmi.c new file mode 100755 index 00000000000..ca51ee1ddc4 --- /dev/null +++ b/components/drivers/clk/clk-scmi.c @@ -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 +#include + +#define DBG_TAG "clk.scmi" +#define DBG_LVL DBG_INFO +#include + +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); diff --git a/components/drivers/firmware/Kconfig b/components/drivers/firmware/Kconfig new file mode 100755 index 00000000000..4261e6157ec --- /dev/null +++ b/components/drivers/firmware/Kconfig @@ -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 diff --git a/components/drivers/firmware/SConscript b/components/drivers/firmware/SConscript new file mode 100755 index 00000000000..77728612f67 --- /dev/null +++ b/components/drivers/firmware/SConscript @@ -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') diff --git a/components/drivers/firmware/arm_scmi/Kconfig b/components/drivers/firmware/arm_scmi/Kconfig new file mode 100755 index 00000000000..e83cc78b4d9 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/Kconfig @@ -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 diff --git a/components/drivers/firmware/arm_scmi/SConscript b/components/drivers/firmware/arm_scmi/SConscript new file mode 100755 index 00000000000..6415bc52b15 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/SConscript @@ -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') diff --git a/components/drivers/firmware/arm_scmi/agent-mailbox.c b/components/drivers/firmware/arm_scmi/agent-mailbox.c new file mode 100755 index 00000000000..4a258605605 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent-mailbox.c @@ -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 + +#include +#include + +#define DBG_TAG "scmi.agent.mailbox" +#define DBG_LVL DBG_INFO +#include + +#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, +}; diff --git a/components/drivers/firmware/arm_scmi/agent-smc.c b/components/drivers/firmware/arm_scmi/agent-smc.c new file mode 100755 index 00000000000..8bff749f98e --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent-smc.c @@ -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 + +#include +#include +#include +#include + +#define DBG_TAG "scmi.agent.smc" +#define DBG_LVL DBG_INFO +#include + +#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, +}; diff --git a/components/drivers/firmware/arm_scmi/agent.c b/components/drivers/firmware/arm_scmi/agent.c new file mode 100755 index 00000000000..2ff38de4bca --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent.c @@ -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 + +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); diff --git a/components/drivers/firmware/arm_scmi/agent.h b/components/drivers/firmware/arm_scmi/agent.h new file mode 100755 index 00000000000..a28572f43e7 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/agent.h @@ -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 +#include + +#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__ */ diff --git a/components/drivers/firmware/arm_scmi/bus.c b/components/drivers/firmware/arm_scmi/bus.c new file mode 100755 index 00000000000..e4e9c7e12a7 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/bus.c @@ -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 +#include + +#define DBG_TAG "scmi.bus" +#define DBG_LVL DBG_INFO +#include + +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); diff --git a/components/drivers/firmware/arm_scmi/shmem.c b/components/drivers/firmware/arm_scmi/shmem.c new file mode 100755 index 00000000000..876398471e8 --- /dev/null +++ b/components/drivers/firmware/arm_scmi/shmem.c @@ -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 + +#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; + } +} diff --git a/components/drivers/firmware/arm_scmi/shmem.h b/components/drivers/firmware/arm_scmi/shmem.h new file mode 100755 index 00000000000..fd30213addd --- /dev/null +++ b/components/drivers/firmware/arm_scmi/shmem.h @@ -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 +#include + +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__ */ diff --git a/components/drivers/include/drivers/scmi.h b/components/drivers/include/drivers/scmi.h new file mode 100755 index 00000000000..0605ce3ca7c --- /dev/null +++ b/components/drivers/include/drivers/scmi.h @@ -0,0 +1,1092 @@ +/* + * 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_H__ +#define __SCMI_H__ + +#include +#include +#include + +#define SCMI_PROTOCOL_ID_BASE 0x10 +#define SCMI_PROTOCOL_ID_POWER 0x11 +#define SCMI_PROTOCOL_ID_SYSTEM 0x12 +#define SCMI_PROTOCOL_ID_PERF 0x13 +#define SCMI_PROTOCOL_ID_CLOCK 0x14 +#define SCMI_PROTOCOL_ID_SENSOR 0x15 +#define SCMI_PROTOCOL_ID_RESET 0x16 +#define SCMI_PROTOCOL_ID_VOLTAGE 0x17 +#define SCMI_PROTOCOL_ID_POWERCAP 0x18 +#define SCMI_PROTOCOL_ID_PINCTRL 0x19 + +#define SCMI_SUCCESS 0 /* Success */ +#define SCMI_ERR_SUPPORT (-1) /* Not supported */ +#define SCMI_ERR_PARAMS (-2) /* Invalid Parameters */ +#define SCMI_ERR_ACCESS (-3) /* Invalid access/permission denied */ +#define SCMI_ERR_ENTRY (-4) /* Not found */ +#define SCMI_ERR_RANGE (-5) /* Value out of range */ +#define SCMI_ERR_BUSY (-6) /* Device busy */ +#define SCMI_ERR_COMMS (-7) /* Communication Error */ +#define SCMI_ERR_GENERIC (-8) /* Generic Error */ +#define SCMI_ERR_HARDWARE (-9) /* Hardware Error */ +#define SCMI_ERR_PROTOCOL (-10) /* Protocol Error */ + +#define SCMI_MAX_STR_SIZE 64 +#define SCMI_SHORT_NAME_MAX_SIZE 16 +#define SCMI_MAX_NUM_RATES 16 + +struct rt_scmi_device; + +typedef void (*rt_scmi_msg_callback)(struct rt_scmi_device *sdev, rt_uint8_t *msg, rt_size_t size); + +/* + * struct rt_scmi_msg - Context of a SCMI message sent and the response received + * + */ +struct rt_scmi_msg +{ + struct rt_scmi_device *sdev; + + rt_scmi_msg_callback rx_callback; + + rt_uint32_t message_id; + + rt_uint8_t *in_msg; + rt_size_t in_msg_size; + + rt_uint8_t *out_msg; + rt_size_t out_msg_size; +}; + +/* Helper macro to match a message on input/output array references */ +#define RT_SCMI_MSG_RAW(MSG_ID, IN, IN_SIZE, OUT, OUT_SIZE) \ +(struct rt_scmi_msg) { \ + .message_id = MSG_ID, \ + .in_msg = (rt_uint8_t *)(IN), \ + .in_msg_size = IN_SIZE, \ + .out_msg = (rt_uint8_t *)(OUT), \ + .out_msg_size = OUT_SIZE, \ +} + +#define RT_SCMI_MSG_IN_OUT(MSG_ID, IN, OUT) \ + RT_SCMI_MSG_RAW(MSG_ID, IN, sizeof(*(IN)), OUT, sizeof(*(OUT))) + +#define RT_SCMI_MSG_IN(MSG_ID, IN) \ + RT_SCMI_MSG_RAW(MSG_ID, IN, sizeof(*(IN)), RT_NULL, 0) + +#define RT_SCMI_MSG_OUT(MSG_ID, OUT) \ + RT_SCMI_MSG_RAW(MSG_ID, RT_NULL, 0, OUT, sizeof(*(OUT))) + +#define SCMI_HDR_TOKEN(token) (((token) << 18) & RT_GENMASK(31, 18)) +#define SCMI_HDR_PROTOCOL_ID(proto) (((proto) << 10) & RT_GENMASK(17, 10)) +#define SCMI_HDR_MESSAGE_TYPE(type) (((type) << 18) & RT_GENMASK(9, 8)) +#define SCMI_HDR_MESSAGE_ID(id) ((id) & RT_GENMASK(7, 0)) + +rt_inline rt_uint32_t scmi_header(unsigned msg_id, unsigned msg_type, + unsigned protocol_id, unsigned token) +{ + return SCMI_HDR_TOKEN(token) | + SCMI_HDR_MESSAGE_TYPE(msg_type) | + SCMI_HDR_PROTOCOL_ID(protocol_id) | + SCMI_HDR_MESSAGE_ID(msg_id); +} + +enum scmi_common_message_id +{ + SCMI_COM_MSG_VERSION = 0x0, + SCMI_COM_MSG_ATTRIBUTES = 0x1, + SCMI_COM_MSG_MESSAGE_ATTRIBUTES = 0x2, +}; + +/* + * SCMI Power Protocol + */ +enum scmi_power_protocol_cmd +{ + SCMI_POWER_DOMAIN_ATTRIBUTES = 0x3, + SCMI_POWER_STATE_SET = 0x4, + SCMI_POWER_STATE_GET = 0x5, + SCMI_POWER_STATE_NOTIFY = 0x6, + SCMI_POWER_DOMAIN_NAME_GET = 0x8, +}; + +/** + * struct scmi_power_attributes - Response payload for SCMI_COM_MSG_ATTRIBUTES command + */ +struct scmi_power_attributes +{ + rt_le32_t state; + rt_le16_t num_domains; + rt_le16_t reserved; + rt_le32_t stats_addr_low; + rt_le32_t stats_addr_high; + rt_le32_t stats_size; +}; + +/** + * struct scmi_power_state_set_in - Message payload for SCMI_POWER_STATE_SET command + */ +struct scmi_power_state_set_in +{ + rt_le32_t flags; + rt_le32_t domain; +#define SCMI_POWER_STATE_TYPE_SHIFT 30 +#define SCMI_POWER_STATE_ID_MASK (RT_BIT(28) - 1) +#define SCMI_POWER_STATE_PARAM(type, id) ((((type) & RT_BIT(0)) << SCMI_POWER_STATE_TYPE_SHIFT) | ((id) & SCMI_POWER_STATE_ID_MASK)) +#define SCMI_POWER_STATE_GENERIC_ON SCMI_POWER_STATE_PARAM(0, 0) +#define SCMI_POWER_STATE_GENERIC_OFF SCMI_POWER_STATE_PARAM(1, 0) + rt_le32_t state; +}; + +/** + * struct scmi_power_state_set_out - Response payload for SCMI_POWER_STATE_SET command + */ +struct scmi_power_state_set_out +{ + rt_le32_t state; +}; + +/* + * SCMI Clock Protocol + */ +enum scmi_clock_message_id +{ + SCMI_CLOCK_ATTRIBUTES = 0x3, + SCMI_CLOCK_DESCRIBE_RATES = 0x4, + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, + SCMI_CLOCK_NAME_GET = 0x8, +}; + +/** + * struct scmi_clk_attributes - Response payload for SCMI_COM_MSG_ATTRIBUTES command + */ +struct scmi_clk_attributes +{ + rt_le32_t status; + rt_le16_t num_clocks; + rt_uint8_t max_async_req; + rt_uint8_t reserved; +}; + +/** + * struct scmi_clk_describe_attributes_in - Message payload for SCMI_CLOCK_ATTRIBUTES command + */ +struct scmi_clk_describe_attributes_in +{ + rt_le32_t clock_id; +}; + +/** + * struct scmi_clk_describe_attributes_out - Response payload for SCMI_CLOCK_ATTRIBUTES command + * clock in response to a clock enable request from an agent + */ +struct scmi_clk_describe_attributes_out +{ + rt_le32_t status; + rt_le32_t attributes; +#define CLOCK_ENABLE RT_BIT(0) +#define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & RT_BIT(31)) +#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & RT_BIT(30)) +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & RT_BIT(29)) + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; + rt_le32_t clock_enable_latency; +}; + +/** + * struct scmi_clk_describe_rates_in - Message payload for SCMI_CLOCK_DESCRIBE_RATES command + */ +struct scmi_clk_describe_rates_in +{ + rt_le32_t id; + rt_le32_t rate_index; +}; + +/** + * struct scmi_clk_describe_rates_out - Response payload for SCMI_CLOCK_DESCRIBE_RATES command + */ +struct scmi_clk_describe_rates_out +{ + rt_le32_t status; + rt_le32_t num_rates_flags; +#define SCMI_NUM_RETURNED(x) ((x) & 0xfff) +#define SCMI_RATE_DISCRETE(x) !((x) & RT_BIT(12)) +#define SCMI_NUM_REMAINING(x) ((x) >> 16) + struct + { + rt_le32_t value_low; + rt_le32_t value_high; + } rate[]; +#define SCMI_RATE_TO_U64(X) \ +({ \ + typeof(X) x = (X); \ + rt_le32_to_cpu((x).value_low) | (rt_uint64_t)rt_le32_to_cpu((x).value_high) << 32; \ +}) +}; + +/** + * struct scmi_clk_state_in - Message payload for SCMI_CLOCK_RATE_SET command + */ +struct scmi_clk_rate_set_in +{ +#define SCMI_CLK_RATE_ASYNC_NOTIFY RT_BIT(0) +#define SCMI_CLK_RATE_ASYNC_NORESP (RT_BIT(0) | RT_BIT(1)) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP RT_BIT(2) +#define SCMI_CLK_RATE_ROUND_CLOSEST RT_BIT(3) + rt_le32_t flags; + rt_le32_t clock_id; + rt_le32_t rate_lsb; + rt_le32_t rate_msb; +}; + +/** + * struct scmi_clk_rate_set_out - Response payload for SCMI_CLOCK_RATE_SET command + */ +struct scmi_clk_rate_set_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_clk_state_in - Message payload for SCMI_CLOCK_RATE_GET command + */ +struct scmi_clk_rate_get_in +{ + rt_le32_t clock_id; +}; + +/** + * struct scmi_clk_rate_get_out - Response payload for SCMI_CLOCK_RATE_GET command + */ +struct scmi_clk_rate_get_out +{ + rt_le32_t status; + rt_le32_t rate_lsb; + rt_le32_t rate_msb; +}; + +/** + * struct scmi_clk_state_in - Message payload for SCMI_CLOCK_CONFIG_SET command + */ +struct scmi_clk_state_in +{ + rt_le32_t clock_id; + rt_le32_t attributes; + rt_le32_t ext_config_val; +}; + +/** + * struct scmi_clk_state_out - Response payload for SCMI_CLOCK_CONFIG_SET command + */ +struct scmi_clk_state_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_clk_name_in - Message payload for SCMI_CLOCK_NAME_GET command + */ +struct scmi_clk_name_in +{ + rt_le32_t clock_id; +}; + +/** + * struct scmi_clk_name_out - Response payload for SCMI_CLOCK_NAME_GET command + */ +struct scmi_clk_name_out +{ + rt_le32_t status; + rt_le32_t flags; + rt_uint8_t name[SCMI_MAX_STR_SIZE]; +}; + +/* + * SCMI Sensor Domain Protocol + */ +enum scmi_sensor_message_id +{ + SCMI_SENSOR_DESCRIPTION_GET = 0x3, + SCMI_SENSOR_TRIP_POINT_NOTIFY = 0x4, + SCMI_SENSOR_TRIP_POINT_CONFIG = 0x5, + SCMI_SENSOR_READING_GET = 0x6, + SCMI_SENSOR_AXIS_DESCRIPTION_GET = 0x7, + SCMI_SENSOR_LIST_UPDATE_INTERVALS = 0x8, + SCMI_SENSOR_CONFIG_GET = 0x9, + SCMI_SENSOR_CONFIG_SET = 0xa, + SCMI_SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xb, + SCMI_SENSOR_NAME_GET = 0xc, + SCMI_SENSOR_AXIS_NAME_GET = 0xd, +}; + +enum scmi_sensor_type +{ + SCMI_SENSOR_TYPE_NONE = 0x0, + SCMI_SENSOR_TYPE_UNSPEC = 0x1, + SCMI_SENSOR_TYPE_TEMPERATURE_C = 0x2, + SCMI_SENSOR_TYPE_TEMPERATURE_F = 0x3, + SCMI_SENSOR_TYPE_TEMPERATURE_K = 0x4, + SCMI_SENSOR_TYPE_VOLTAGE = 0x5, + SCMI_SENSOR_TYPE_CURRENT = 0x6, + SCMI_SENSOR_TYPE_POWER = 0x7, + SCMI_SENSOR_TYPE_ENERGY = 0x8, + SCMI_SENSOR_TYPE_CHARGE = 0x9, + SCMI_SENSOR_TYPE_VOLTAMPERE = 0xa, + SCMI_SENSOR_TYPE_NITS = 0xb, + SCMI_SENSOR_TYPE_LUMENS = 0xc, + SCMI_SENSOR_TYPE_LUX = 0xd, + SCMI_SENSOR_TYPE_CANDELAS = 0xe, + SCMI_SENSOR_TYPE_KPA = 0xf, + SCMI_SENSOR_TYPE_PSI = 0x10, + SCMI_SENSOR_TYPE_NEWTON = 0x11, + SCMI_SENSOR_TYPE_CFM = 0x12, + SCMI_SENSOR_TYPE_RPM = 0x13, + SCMI_SENSOR_TYPE_HERTZ = 0x14, + SCMI_SENSOR_TYPE_SECS = 0x15, + SCMI_SENSOR_TYPE_MINS = 0x16, + SCMI_SENSOR_TYPE_HOURS = 0x17, + SCMI_SENSOR_TYPE_DAYS = 0x18, + SCMI_SENSOR_TYPE_WEEKS = 0x19, + SCMI_SENSOR_TYPE_MILS = 0x1a, + SCMI_SENSOR_TYPE_INCHES = 0x1b, + SCMI_SENSOR_TYPE_FEET = 0x1c, + SCMI_SENSOR_TYPE_CUBIC_INCHES = 0x1d, + SCMI_SENSOR_TYPE_CUBIC_FEET = 0x1e, + SCMI_SENSOR_TYPE_METERS = 0x1f, + SCMI_SENSOR_TYPE_CUBIC_CM = 0x20, + SCMI_SENSOR_TYPE_CUBIC_METERS = 0x21, + SCMI_SENSOR_TYPE_LITERS = 0x22, + SCMI_SENSOR_TYPE_FLUID_OUNCES = 0x23, + SCMI_SENSOR_TYPE_RADIANS = 0x24, + SCMI_SENSOR_TYPE_STERADIANS = 0x25, + SCMI_SENSOR_TYPE_REVOLUTIONS = 0x26, + SCMI_SENSOR_TYPE_CYCLES = 0x27, + SCMI_SENSOR_TYPE_GRAVITIES = 0x28, + SCMI_SENSOR_TYPE_OUNCES = 0x29, + SCMI_SENSOR_TYPE_POUNDS = 0x2a, + SCMI_SENSOR_TYPE_FOOT_POUNDS = 0x2b, + SCMI_SENSOR_TYPE_OUNCE_INCHES = 0x2c, + SCMI_SENSOR_TYPE_GAUSS = 0x2d, + SCMI_SENSOR_TYPE_GILBERTS = 0x2e, + SCMI_SENSOR_TYPE_HENRIES = 0x2f, + SCMI_SENSOR_TYPE_FARADS = 0x30, + SCMI_SENSOR_TYPE_OHMS = 0x31, + SCMI_SENSOR_TYPE_SIEMENS = 0x32, + SCMI_SENSOR_TYPE_MOLES = 0x33, + SCMI_SENSOR_TYPE_BECQUERELS = 0x34, + SCMI_SENSOR_TYPE_PPM = 0x35, + SCMI_SENSOR_TYPE_DECIBELS = 0x36, + SCMI_SENSOR_TYPE_DBA = 0x37, + SCMI_SENSOR_TYPE_DBC = 0x38, + SCMI_SENSOR_TYPE_GRAYS = 0x39, + SCMI_SENSOR_TYPE_SIEVERTS = 0x3a, + SCMI_SENSOR_TYPE_COLOR_TEMP_K = 0x3b, + SCMI_SENSOR_TYPE_BITS = 0x3c, + SCMI_SENSOR_TYPE_BYTES = 0x3d, + SCMI_SENSOR_TYPE_WORDS = 0x3e, + SCMI_SENSOR_TYPE_DWORDS = 0x3f, + SCMI_SENSOR_TYPE_QWORDS = 0x40, + SCMI_SENSOR_TYPE_PERCENTAGE = 0x41, + SCMI_SENSOR_TYPE_PASCALS = 0x42, + SCMI_SENSOR_TYPE_COUNTS = 0x43, + SCMI_SENSOR_TYPE_GRAMS = 0x44, + SCMI_SENSOR_TYPE_NEWTON_METERS = 0x45, + SCMI_SENSOR_TYPE_HITS = 0x46, + SCMI_SENSOR_TYPE_MISSES = 0x47, + SCMI_SENSOR_TYPE_RETRIES = 0x48, + SCMI_SENSOR_TYPE_OVERRUNS = 0x49, + SCMI_SENSOR_TYPE_UNDERRUNS = 0x4a, + SCMI_SENSOR_TYPE_COLLISIONS = 0x4b, + SCMI_SENSOR_TYPE_PACKETS = 0x4c, + SCMI_SENSOR_TYPE_MESSAGES = 0x4d, + SCMI_SENSOR_TYPE_CHARS = 0x4e, + SCMI_SENSOR_TYPE_ERRORS = 0x4f, + SCMI_SENSOR_TYPE_CORRECTED_ERRS = 0x50, + SCMI_SENSOR_TYPE_UNCORRECTABLE_ERRS = 0x51, + SCMI_SENSOR_TYPE_SQ_MILS = 0x52, + SCMI_SENSOR_TYPE_SQ_INCHES = 0x53, + SCMI_SENSOR_TYPE_SQ_FEET = 0x54, + SCMI_SENSOR_TYPE_SQ_CM = 0x55, + SCMI_SENSOR_TYPE_SQ_METERS = 0x56, + SCMI_SENSOR_TYPE_RADIANS_SEC = 0x57, + SCMI_SENSOR_TYPE_BPM = 0x58, + SCMI_SENSOR_TYPE_METERS_SEC_SQUARED = 0x59, + SCMI_SENSOR_TYPE_METERS_SEC = 0x5a, + SCMI_SENSOR_TYPE_CUBIC_METERS_SEC = 0x5b, + SCMI_SENSOR_TYPE_MM_MERCURY = 0x5c, + SCMI_SENSOR_TYPE_RADIANS_SEC_SQUARED = 0x5d, + SCMI_SENSOR_TYPE_OEM_UNIT = 0xff +}; + +/** + * struct scmi_sensor_attributes - Response payload for SCMI_COM_MSG_ATTRIBUTES command + */ +struct scmi_sensor_attributes +{ + rt_le16_t num_sensors; + rt_uint8_t max_requests; + rt_uint8_t reserved; + rt_le32_t reg_addr_low; + rt_le32_t reg_addr_high; + rt_le32_t reg_size; +}; + +/** + * struct scmi_sensor_description_get_in - Payload for SCMI_SENSOR_DESCRIPTION_GET command + */ +struct scmi_sensor_description_get_in +{ + rt_le32_t desc_index; +}; + +/** + * struct scmi_sensor_description_get_out - Response payload for SCMI_SENSOR_DESCRIPTION_GET command + */ +struct scmi_sensor_description_get_out +{ + rt_le32_t status; +#define SCMI_SENSOR_DESC_RETURNED_NR(x) RT_FIELD_GET(RT_GENMASK(11, 0), (x)) +#define SCMI_SENSOR_DESC_REMAINING_NR(x) RT_FIELD_GET(RT_GENMASK(31, 16), (x)) + rt_le32_t num_sensor_flags; + struct + { + rt_le32_t id; +#define SCMI_SENSOR_ASYNC_READ(x) RT_FIELD_GET(RT_BIT(31), (x)) +#define SCMI_SENSOR_EXT_NAMES(x) RT_FIELD_GET(RT_BIT(29), (x)) +#define SCMI_SENSOR_TRIP_POINTS_NR(x) RT_FIELD_GET(RT_GENMASK(7, 0), (x)) + rt_le32_t attributes_low; +#define SCMI_SENSOR_SCALE(x) RT_FIELD_GET(RT_GENMASK(15, 11), (x)) +#define SCMI_SENSOR_SCALE_SIGN RT_BIT(4) +#define SCMI_SENSOR_SCALE_EXTEND RT_GENMASK(31, 5) +#define SCMI_SENSOR_TYPE(x) RT_FIELD_GET(RT_GENMASK(7, 0), (x)) + rt_le32_t attributes_high; + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; + rt_le32_t power; + rt_le32_t resolution; + rt_le32_t min_range_low; + rt_le32_t min_range_high; + rt_le32_t max_range_low; + rt_le32_t max_range_high; + } desc[]; +}; + +/** + * struct scmi_scmi_sensor_axis_description_get_in - Payload for SCMI_SENSOR_AXIS_DESCRIPTION_GET command + */ +struct scmi_scmi_sensor_axis_description_get_in +{ + rt_le32_t id; + rt_le32_t axis_desc_index; +}; + +/** + * struct scmi_scmi_sensor_axis_description_get_out - Response payload for SCMI_SENSOR_AXIS_DESCRIPTION_GET command + */ +struct scmi_scmi_sensor_axis_description_get_out +{ + rt_le32_t status; +#define SCMI_SENSOR_AXIS_RETURNED_NR(x) RT_FIELD_GET(RT_GENMASK(5, 0), (x)) +#define SCMI_SENSOR_AXIS_REMAINING_NR(x) RT_FIELD_GET(RT_GENMASK(31, 26), (x)) + rt_le32_t num_axis_flags; + struct + { + rt_le32_t axis_id; +#define SCMI_SENSOR_AXIS_EXT_AXIS_NAME(x) RT_FIELD_GET(RT_BIT(9), (x)) + rt_le32_t axis_attributes_low; +#define SCMI_SENSOR_AXIS_SCALE(x) RT_FIELD_GET(RT_GENMASK(15, 11), (x)) +#define SCMI_SENSOR_AXIS_TYPE(x) RT_FIELD_GET(RT_GENMASK(7, 0), (x)) + rt_le32_t axis_attributes_high; + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; + rt_le32_t axis_resolution; + rt_le32_t axis_min_range_low; + rt_le32_t axis_min_range_high; + rt_le32_t axis_max_range_low; + rt_le32_t axis_max_range_high; + } desc[]; +}; + +/** + * struct scmi_sensor_list_update_intervals_in - Payload for SCMI_SENSOR_LIST_UPDATE_INTERVALS command + */ +struct scmi_sensor_list_update_intervals_in +{ + rt_le32_t id; + rt_le32_t index; +}; + +/** + * struct scmi_sensor_list_update_intervals_out - Response payload for SCMI_SENSOR_LIST_UPDATE_INTERVALS command + */ +struct scmi_sensor_list_update_intervals_out +{ + rt_le32_t status; + rt_le32_t flags; + rt_le32_t intervals[]; +}; + +/** + * struct scmi_sensor_trip_point_notify_in - Payload for SCMI_SENSOR_TRIP_POINT_NOTIFY command + */ +struct scmi_sensor_trip_point_notify_in +{ + rt_le32_t id; +#define SCMI_SENSOR_NOTIFY_ALL RT_BIT(0) + rt_le32_t event_control; +}; + +/** + * struct scmi_sensor_trip_point_notify_out - Response payload for SCMI_SENSOR_TRIP_POINT_NOTIFY command + */ +struct scmi_sensor_trip_point_notify_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_sensor_trip_point_config_in - Payload for SCMI_SENSOR_TRIP_POINT_CONFIG command + */ +struct scmi_sensor_trip_point_config_in +{ + rt_le32_t id; +#define SCMI_SENSOR_TRIP_POINT_EVENT_MASK 0x3 +#define SCMI_SENSOR_TRIP_POINT_DISABLED 0x0 +#define SCMI_SENSOR_TRIP_POINT_POSITIVE 0x1 +#define SCMI_SENSOR_TRIP_POINT_NEGATIVE 0x2 +#define SCMI_SENSOR_TRIP_POINT_BOTH 0x3 +#define SCMI_SENSOR_TRIP_POINT_ID(x) (((x) & 0xff) << 4) + rt_le32_t trip_point_ev_ctrl; + rt_le32_t trip_point_val_low; + rt_le32_t trip_point_val_high; +}; + +/** + * struct scmi_sensor_trip_point_config_out - Response payload for SCMI_SENSOR_TRIP_POINT_CONFIG command + */ +struct scmi_sensor_trip_point_config_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_sensor_config_get_in - Payload for SCMI_SENSOR_CONFIG_GET command + */ +struct scmi_sensor_config_get_in +{ + rt_le32_t id; +}; + +/** + * struct scmi_sensor_config_get_out - Response payload for SCMI_SENSOR_CONFIG_GET command + */ +struct scmi_sensor_config_get_out +{ + rt_le32_t status; +#define SCMI_SENSOR_INTERVALS_SEC(x) RT_FIELD_GET(RT_GENMASK(31, 16), (x)) +#define SCMI_SENSOR_INTERVALS_EXP(x) RT_FIELD_GET(RT_GENMASK(15, 11), (x)) /* SEC x (10 ^ EXP) */ +#define SCMI_SENSOR_TEMP_RP(x) RT_FIELD_GET(RT_BIT(1), (x)) +#define SCMI_SENSOR_STATUS_EN(x) RT_FIELD_GET(RT_BIT(0), (x)) + rt_le32_t config; +}; + +/** + * struct scmi_sensor_config_set_in - Payload for SCMI_SENSOR_CONFIG_SET command + */ +struct scmi_sensor_config_set_in +{ + rt_le32_t id; + rt_le32_t config; +}; + +/** + * struct scmi_sensor_config_set_out - Response payload for SCMI_SENSOR_CONFIG_SET command + */ +struct scmi_sensor_config_set_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_sensor_reading_in - Payload for SCMI_SENSOR_READING_GET command + */ +struct scmi_sensor_reading_in +{ + rt_le32_t id; +#define SCMI_SENSOR_FLAG_ASYNC RT_BIT(0) + rt_le32_t flags; +}; + +/** + * struct scmi_sensor_reading_out - Response payload for SCMI_SENSOR_READING_GET command + */ +struct scmi_sensor_reading_out +{ + rt_le32_t status; + struct + { + rt_le32_t value_low; + rt_le32_t value_high; + rt_le32_t timestamp_low; + rt_le32_t timestamp_high; + } readings[]; +}; + +/** + * struct scmi_sensor_continuous_update_notify_in - Payload for SCMI_SENSOR_CONTINUOUS_UPDATE_NOTIFY command + */ +struct scmi_sensor_continuous_update_notify_in +{ + rt_le32_t id; +#define SCMI_SENSOR_FLAG_NOTIFY_ENABLE RT_BIT(0) + rt_le32_t notify_enable; +}; + +/** + * struct scmi_sensor_continuous_update_notify_out - Response payload for SCMI_SENSOR_CONTINUOUS_UPDATE_NOTIFY command + */ +struct scmi_sensor_continuous_update_notify_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_sensor_name_in - Payload for SCMI_SENSOR_NAME_GET command + */ +struct scmi_sensor_name_in +{ + rt_le32_t id; +}; + +/** + * struct scmi_sensor_name_out - Response payload for SCMI_SENSOR_NAME_GET command + */ +struct scmi_sensor_name_out +{ + rt_le32_t status; + rt_le32_t flags; + rt_uint8_t name[SCMI_MAX_STR_SIZE]; +}; + +/** + * struct scmi_sensor_axis_name_in - Payload for SCMI_SENSOR_AXIS_NAME_GET command + */ +struct scmi_sensor_axis_name_in +{ + rt_le32_t id; + rt_le32_t axis_id; +}; + +/** + * struct scmi_sensor_axis_name_out - Response payload for SCMI_SENSOR_AXIS_NAME_GET command + */ +struct scmi_sensor_axis_name_out +{ + rt_le32_t status; + rt_le32_t flags; + struct + { + rt_le32_t axis_id; + rt_uint8_t name[SCMI_MAX_STR_SIZE]; + } desc[]; +}; + +/* + * SCMI Reset Domain Protocol + */ + +enum scmi_reset_message_id +{ + SCMI_RESET_DOMAIN_ATTRIBUTES = 0x3, + SCMI_RESET_RESET = 0x4, +}; + +#define SCMI_RESET_ATTRIBUTES_FLAG_ASYNC RT_BIT(31) +#define SCMI_RESET_ATTRIBUTES_FLAG_NOTIF RT_BIT(30) + +/** + * struct scmi_reset_attr_in - Payload for SCMI_RESET_DOMAIN_ATTRIBUTES message + */ +struct scmi_reset_attr_in +{ + rt_le32_t domain_id; +}; + +/** + * struct scmi_reset_attr_out - Payload for SCMI_RESET_DOMAIN_ATTRIBUTES response + */ +struct scmi_reset_attr_out +{ + rt_le32_t status; + rt_le32_t attributes; + rt_le32_t latency; + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; +}; + +/** + * struct scmi_reset_in - Message payload for SCMI_RESET_RESET command + */ +struct scmi_reset_in +{ + rt_le32_t domain_id; +#define SCMI_RESET_FLAG_RESET RT_BIT(0) +#define SCMI_RESET_FLAG_ASSERT RT_BIT(1) +#define SCMI_RESET_FLAG_ASYNC RT_BIT(2) + rt_le32_t flags; +#define SCMI_ARCH_COLD_RESET 0 + rt_le32_t reset_state; +}; + +/** + * struct scmi_reset_out - Response payload for SCMI_RESET_RESET command + */ +struct scmi_reset_out +{ + rt_le32_t status; +}; + +/* + * SCMI Voltage Domain Protocol + */ + +enum scmi_voltage_domain_message_id +{ + SCMI_VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET = 0x5, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET = 0x6, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET = 0x7, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET = 0x8, +}; + +#define SCMI_VOLTAGE_CONFIG_MASK RT_GENMASK(3, 0) +#define SCMI_VOLTAGE_CONFIG_OFF 0 +#define SCMI_VOLTAGE_CONFIG_ON 0x7 + +/** + * struct scmi_voltage_attributes - Response payload for SCMI_COM_MSG_ATTRIBUTES command + */ +struct scmi_voltage_attributes +{ + rt_le32_t status; + rt_le16_t num_domains; + rt_le16_t reserved; +}; + +/** + * struct scmi_voltage_attr_in - Payload for SCMI_VOLTAGE_DOMAIN_ATTRIBUTES message + */ +struct scmi_voltage_attr_in +{ + rt_le32_t domain_id; +}; + +/** + * struct scmi_voltage_attr_out - Payload for SCMI_VOLTAGE_DOMAIN_ATTRIBUTES response + */ +struct scmi_voltage_attr_out +{ + rt_le32_t status; + rt_le32_t attributes; + char name[SCMI_SHORT_NAME_MAX_SIZE]; +}; + +/** + * struct scmi_voltage_config_set_in - Message payload for SCMI_VOLTAGE_DOMAIN_CONFIG_SET cmd + */ +struct scmi_voltage_config_set_in +{ + rt_le32_t domain_id; + rt_le32_t config; +}; + +/** + * struct scmi_voltage_config_set_out - Response for SCMI_VOLTAGE_DOMAIN_CONFIG_SET command + */ +struct scmi_voltage_config_set_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_voltage_config_get_in - Message payload for SCMI_VOLTAGE_CONFIG_GET cmd + */ +struct scmi_voltage_config_get_in +{ + rt_le32_t domain_id; +}; + +/** + * struct scmi_voltage_config_get_out - Response for SCMI_VOLTAGE_CONFIG_GET command + */ +struct scmi_voltage_config_get_out +{ + rt_le32_t status; + rt_le32_t config; +}; + +/** + * struct scmi_voltage_level_set_in - Message payload for SCMI_VOLTAGE_DOMAIN_LEVEL_SET cmd + */ +struct scmi_voltage_level_set_in +{ + rt_le32_t domain_id; + rt_le32_t flags; + rt_le32_t voltage_level; +}; + +/** + * struct scmi_voltage_level_set_out - Response for SCMI_VOLTAGE_DOMAIN_LEVEL_SET command + */ +struct scmi_voltage_level_set_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_voltage_level_get_in - Message payload for SCMI_VOLTAGE_DOMAIN_LEVEL_GET cmd + */ +struct scmi_voltage_level_get_in +{ + rt_le32_t domain_id; +}; + +/** + * struct scmi_voltage_level_get_out - Response for SCMI_VOLTAGE_DOMAIN_LEVEL_GET command + */ +struct scmi_voltage_level_get_out +{ + rt_le32_t status; + rt_le32_t voltage_level; +}; + +/* + * SCMI Pinctrl Protocol + */ + +enum scmi_pinctrl_message_id +{ + SCMI_PINCTRL_ATTRIBUTES = 0x3, + SCMI_PINCTRL_LIST_ASSOCIATIONS = 0x4, + SCMI_PINCTRL_SETTINGS_GET = 0x5, + SCMI_PINCTRL_SETTINGS_CONFIGURE = 0x6, + SCMI_PINCTRL_REQUEST = 0x7, + SCMI_PINCTRL_RELEASE = 0x8, + SCMI_PINCTRL_NAME_GET = 0x9, + SCMI_PINCTRL_SET_PERMISSIONS = 0xa, +}; + +enum scmi_pinctrl_selector_type +{ + SCMI_PINCTRL_TYPE_PIN = 0, + SCMI_PINCTRL_TYPE_GROUP, + SCMI_PINCTRL_TYPE_FUNCTION, +}; + +enum scmi_pinctrl_conf_type +{ + SCMI_PINCTRL_DEFAULT = 0, + SCMI_PINCTRL_BIAS_BUS_HOLD = 1, + SCMI_PINCTRL_BIAS_DISABLE = 2, + SCMI_PINCTRL_BIAS_HIGH_IMPEDANCE = 3, + SCMI_PINCTRL_BIAS_PULL_UP = 4, + SCMI_PINCTRL_BIAS_PULL_DEFAULT = 5, + SCMI_PINCTRL_BIAS_PULL_DOWN = 6, + SCMI_PINCTRL_DRIVE_OPEN_DRAIN = 7, + SCMI_PINCTRL_DRIVE_OPEN_SOURCE = 8, + SCMI_PINCTRL_DRIVE_PUSH_PULL = 9, + SCMI_PINCTRL_DRIVE_STRENGTH = 10, + SCMI_PINCTRL_INPUT_DEBOUNCE = 11, + SCMI_PINCTRL_INPUT_MODE = 12, + SCMI_PINCTRL_PULL_MODE = 13, + SCMI_PINCTRL_INPUT_VALUE = 14, + SCMI_PINCTRL_INPUT_SCHMITT = 15, + SCMI_PINCTRL_LOW_POWER_MODE = 16, + SCMI_PINCTRL_OUTPUT_MODE = 17, + SCMI_PINCTRL_OUTPUT_VALUE = 18, + SCMI_PINCTRL_POWER_SOURCE = 19, + SCMI_PINCTRL_SLEW_RATE = 20, + SCMI_PINCTRL_OEM_START = 192, + SCMI_PINCTRL_OEM_END = 255, +}; + +/** + * struct scmi_pinctrl_protocol_attributes - Response payload for SCMI_COM_MSG_ATTRIBUTES command + */ +struct scmi_pinctrl_protocol_attributes +{ + rt_le32_t status; +#define SCMI_PINCTRL_GROUPS_NR(x) RT_FIELD_GET(RT_GENMASK(31, 16), (x)) +#define SCMI_PINCTRL_PINS_NR(x) RT_FIELD_GET(RT_GENMASK(15, 0), (x)) +#define SCMI_PINCTRL_FUNCTIONS_NR(x) RT_FIELD_GET(RT_GENMASK(15, 0), (x)) + rt_le32_t attributes_low; + rt_le32_t attributes_high; +}; + +/** + * struct scmi_pinctrl_attributes_in - Message payload for SCMI_PINCTRL_ATTRIBUTES command + */ +struct scmi_pinctrl_attributes_in +{ + rt_le32_t identifier; + rt_le32_t flags; +}; + +/** + * struct scmi_pinctrl_attributes_out - Response payload for SCMI_PINCTRL_ATTRIBUTES command + */ +struct scmi_pinctrl_attributes_out +{ +#define SCMI_PINCTRL_EXT_NAME_FLAG(x) RT_FIELD_GET(RT_BIT(31), (x)) +#define SCMI_PINCTRL_NUM_ELEMS(x) RT_FIELD_GET(RT_GENMASK(15, 0), (x)) + rt_le32_t status; + rt_le32_t attributes; + rt_uint8_t name[SCMI_SHORT_NAME_MAX_SIZE]; +}; + +/** + * struct scmi_pinctrl_list_assoc_in - Message payload for SCMI_PINCTRL_LIST_ASSOCIATIONS command + */ +struct scmi_pinctrl_list_assoc_in +{ + rt_le32_t identifier; + rt_le32_t flags; + rt_le32_t index; +}; + +/** + * struct scmi_pinctrl_list_assoc_out - Response payload for SCMI_PINCTRL_LIST_ASSOCIATIONS command + */ +struct scmi_pinctrl_list_assoc_out +{ +#define SCMI_PINCTRL_REMAINING(x) RT_FIELD_GET(RT_GENMASK(31, 16), (x)) +#define SCMI_PINCTRL_RETURNED(x) RT_FIELD_GET(RT_GENMASK(11, 0), (x)) + rt_le32_t status; + rt_le32_t flags; + rt_le16_t array[]; +}; + +/** + * struct scmi_pinctrl_settings_get_in - Message payload for SCMI_PINCTRL_SETTINGS_GET command + */ +struct scmi_pinctrl_settings_get_in +{ +#define SCMI_PINCTRL_CONFIG_FLAG_MASK RT_GENMASK(19, 18) +#define SCMI_PINCTRL_SELECTOR_MASK RT_GENMASK(17, 16) +#define SCMI_PINCTRL_SKIP_CONFIGS_MASK RT_GENMASK(15, 8) +#define SCMI_PINCTRL_CONFIG_TYPE_MASK RT_GENMASK(7, 0) + rt_le32_t identifier; + rt_le32_t attributes; +}; + +/** + * struct scmi_pinctrl_settings_get_out - Response payload for SCMI_PINCTRL_SETTINGS_GET command + */ +struct scmi_pinctrl_settings_get_out +{ + rt_le32_t status; + rt_le32_t function_selected; + rt_le32_t num_configs; + rt_le32_t configs[]; +}; + +/** + * struct scmi_pinctrl_settings_conf_in - Message payload for SCMI_PINCTRL_SETTINGS_CONFIGURE command + */ +struct scmi_pinctrl_settings_conf_in +{ + rt_le32_t identifier; + rt_le32_t function_id; + rt_le32_t attributes; + rt_le32_t configs[]; +}; + +/** + * struct scmi_pinctrl_settings_conf_out - Response payload for SCMI_PINCTRL_SETTINGS_CONFIGURE command + */ +struct scmi_pinctrl_settings_conf_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_pinctrl_request_in - Message payload for SCMI_PINCTRL_REQUEST command + */ +struct scmi_pinctrl_request_in +{ + rt_le32_t identifier; + rt_le32_t flags; +}; + +/** + * struct scmi_pinctrl_request_out - Response payload for SCMI_PINCTRL_REQUEST command + */ +struct scmi_pinctrl_request_out +{ + rt_le32_t status; +}; + +/** + * struct scmi_pinctrl_request_in - Message payload for SCMI_PINCTRL_NAME_GET command + */ +struct scmi_pinctrl_name_get_in +{ + rt_le32_t identifier; + rt_le32_t flags; +}; + +/** + * struct scmi_pinctrl_name_get_out - Response payload for SCMI_PINCTRL_NAME_GET command + */ +struct scmi_pinctrl_name_get_out +{ + rt_le32_t status; + rt_le32_t flags; + rt_uint8_t name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_agent; + +struct rt_scmi_device_id +{ + rt_uint8_t protocol_id; + const char *name; +}; + +struct rt_scmi_device +{ + struct rt_device parent; + + const char *name; + rt_uint8_t protocol_id; + + struct scmi_agent *agent; +}; + +struct rt_scmi_driver +{ + struct rt_driver parent; + + const char *name; + const struct rt_scmi_device_id *ids; + + rt_err_t (*probe)(struct rt_scmi_device *sdev); + rt_err_t (*remove)(struct rt_scmi_device *sdev); + rt_err_t (*shutdown)(struct rt_scmi_device *sdev); +}; + +rt_err_t rt_scmi_driver_register(struct rt_scmi_driver *driver); +rt_err_t rt_scmi_device_register(struct rt_scmi_device *device); + +#define RT_SCMI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, scmi, BUILIN) + +rt_err_t rt_scmi_process_msg(struct rt_scmi_device *sdev, struct rt_scmi_msg *msg); +const char *rt_scmi_strerror(rt_base_t err); + +#endif /* __SCMI_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index cd4ef4f4447..86bf747cdca 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -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 */ diff --git a/components/drivers/pinctrl/Kconfig b/components/drivers/pinctrl/Kconfig index 0a459af218d..ac758e2672c 100644 --- a/components/drivers/pinctrl/Kconfig +++ b/components/drivers/pinctrl/Kconfig @@ -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 diff --git a/components/drivers/pinctrl/SConscript b/components/drivers/pinctrl/SConscript index fb4d53f4208..213913231f3 100644 --- a/components/drivers/pinctrl/SConscript +++ b/components/drivers/pinctrl/SConscript @@ -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'] diff --git a/components/drivers/pinctrl/pinctrl-scmi.c b/components/drivers/pinctrl/pinctrl-scmi.c new file mode 100755 index 00000000000..ada365de2ec --- /dev/null +++ b/components/drivers/pinctrl/pinctrl-scmi.c @@ -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 +#include + +#define DBG_TAG "pinctrl.scmi" +#define DBG_LVL DBG_INFO +#include + +#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); diff --git a/components/drivers/pmdomain/Kconfig b/components/drivers/pmdomain/Kconfig index 3d899f74633..89503f4aadb 100644 --- a/components/drivers/pmdomain/Kconfig +++ b/components/drivers/pmdomain/Kconfig @@ -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 diff --git a/components/drivers/pmdomain/SConscript b/components/drivers/pmdomain/SConscript index 957416cfca9..d63b9587d1c 100644 --- a/components/drivers/pmdomain/SConscript +++ b/components/drivers/pmdomain/SConscript @@ -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') diff --git a/components/drivers/pmdomain/pm-domain-scmi.c b/components/drivers/pmdomain/pm-domain-scmi.c new file mode 100755 index 00000000000..7a232ec0e88 --- /dev/null +++ b/components/drivers/pmdomain/pm-domain-scmi.c @@ -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 +#include + +#define DBG_TAG "pm-domain.scmi" +#define DBG_LVL DBG_INFO +#include + +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); diff --git a/components/drivers/regulator/Kconfig b/components/drivers/regulator/Kconfig index 99bfdac1e86..9137bc4e58d 100644 --- a/components/drivers/regulator/Kconfig +++ b/components/drivers/regulator/Kconfig @@ -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 diff --git a/components/drivers/regulator/SConscript b/components/drivers/regulator/SConscript index 29b567fad94..f95f49b196d 100755 --- a/components/drivers/regulator/SConscript +++ b/components/drivers/regulator/SConscript @@ -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') diff --git a/components/drivers/regulator/regulator-scmi.c b/components/drivers/regulator/regulator-scmi.c new file mode 100755 index 00000000000..d4b0697e392 --- /dev/null +++ b/components/drivers/regulator/regulator-scmi.c @@ -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 +#include + +#define DBG_TAG "regulator.scmi" +#define DBG_LVL DBG_INFO +#include + +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); diff --git a/components/drivers/reset/Kconfig b/components/drivers/reset/Kconfig index ccba5757505..238fee2e985 100644 --- a/components/drivers/reset/Kconfig +++ b/components/drivers/reset/Kconfig @@ -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 diff --git a/components/drivers/reset/SConscript b/components/drivers/reset/SConscript index 8e64f786439..5d9adee7284 100755 --- a/components/drivers/reset/SConscript +++ b/components/drivers/reset/SConscript @@ -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') diff --git a/components/drivers/reset/reset-scmi.c b/components/drivers/reset/reset-scmi.c new file mode 100755 index 00000000000..d10d296d86f --- /dev/null +++ b/components/drivers/reset/reset-scmi.c @@ -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 +#include + +#define DBG_TAG "reset.scmi" +#define DBG_LVL DBG_INFO +#include + +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); diff --git a/components/drivers/thermal/Kconfig b/components/drivers/thermal/Kconfig index b993aafd82b..91a394fe8bd 100644 --- a/components/drivers/thermal/Kconfig +++ b/components/drivers/thermal/Kconfig @@ -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 diff --git a/components/drivers/thermal/SConscript b/components/drivers/thermal/SConscript index d33d2d20461..31ba3023819 100644 --- a/components/drivers/thermal/SConscript +++ b/components/drivers/thermal/SConscript @@ -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'] diff --git a/components/drivers/thermal/thermal-scmi.c b/components/drivers/thermal/thermal-scmi.c new file mode 100755 index 00000000000..05e0322d634 --- /dev/null +++ b/components/drivers/thermal/thermal-scmi.c @@ -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 +#include + +#define DBG_TAG "thermal.scmi" +#define DBG_LVL DBG_INFO +#include + +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);