| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- /*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-11-26 GuEe-GUI first version
- */
- #include <rtthread.h>
- #include <rtdevice.h>
- #define DBG_TAG "clk.scmi"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- struct scmi_clk
- {
- struct rt_clk_node parent;
- struct rt_scmi_device *sdev;
- };
- #define raw_to_scmi_clk(raw) rt_container_of(raw, struct scmi_clk, parent)
- struct scmi_clk_data
- {
- struct rt_clk_cell cell;
- int id;
- rt_bool_t rate_discrete;
- union
- {
- struct
- {
- int rates_nr;
- rt_uint64_t rates[];
- } list;
- struct
- {
- rt_uint64_t min_rate;
- rt_uint64_t max_rate;
- rt_uint64_t step_size;
- } range;
- } info;
- };
- #define cell_to_scmi_clk_data(cell) rt_container_of(cell, struct scmi_clk_data, cell)
- static rt_err_t scmi_clk_op_gate(struct scmi_clk *sclk, int clk_id, rt_bool_t enable)
- {
- struct scmi_clk_state_in in =
- {
- .clock_id = rt_cpu_to_le32(clk_id),
- .attributes = rt_cpu_to_le32(enable),
- };
- struct scmi_clk_state_out out;
- struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_CONFIG_SET, &in, &out);
- return rt_scmi_process_msg(sclk->sdev, &msg);
- }
- static rt_base_t scmi_clk_op_get_rate(struct scmi_clk *sclk, int clk_id)
- {
- rt_ubase_t res;
- struct scmi_clk_rate_get_in in =
- {
- .clock_id = rt_cpu_to_le32(clk_id),
- };
- struct scmi_clk_rate_get_out out;
- struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_RATE_GET, &in, &out);
- res = rt_scmi_process_msg(sclk->sdev, &msg);
- if ((rt_base_t)res >= 0)
- {
- res = (rt_ubase_t)(((rt_uint64_t)out.rate_msb << 32) | out.rate_lsb);
- }
- return res;
- }
- static rt_base_t scmi_clk_op_set_rate(struct scmi_clk *sclk, int clk_id, rt_ubase_t rate)
- {
- struct scmi_clk_rate_set_in in =
- {
- .clock_id = rt_cpu_to_le32(clk_id),
- .flags = rt_cpu_to_le32(SCMI_CLK_RATE_ROUND_CLOSEST),
- .rate_lsb = rt_cpu_to_le32((rt_uint32_t)rate),
- .rate_msb = rt_cpu_to_le32((rt_uint32_t)((rt_uint64_t)rate >> 32)),
- };
- struct scmi_clk_rate_set_out out;
- struct rt_scmi_msg msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_RATE_SET, &in, &out);
- return rt_scmi_process_msg(sclk->sdev, &msg);
- }
- static rt_err_t scmi_clk_enable(struct rt_clk_cell *cell)
- {
- struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
- struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
- return scmi_clk_op_gate(sclk, clk_data->id, RT_TRUE);
- }
- static void scmi_clk_disable(struct rt_clk_cell *cell)
- {
- struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
- struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
- scmi_clk_op_gate(sclk, clk_data->id, RT_FALSE);
- }
- static rt_ubase_t scmi_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
- {
- struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
- struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
- return scmi_clk_op_get_rate(sclk, clk_data->id);
- }
- static rt_base_t scmi_clk_round_rate(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate)
- {
- rt_uint64_t fmin, fmax, ftmp;
- struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
- if (clk_data->rate_discrete)
- {
- return rate;
- }
- fmin = clk_data->info.range.min_rate;
- fmax = clk_data->info.range.max_rate;
- if (drate <= fmin)
- {
- return fmin;
- }
- if (drate >= fmax)
- {
- return fmax;
- }
- ftmp = drate - fmin;
- ftmp += clk_data->info.range.step_size - 1;
- rt_do_div(ftmp, clk_data->info.range.step_size);
- return ftmp * clk_data->info.range.step_size + fmin;
- }
- static rt_err_t scmi_clk_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate)
- {
- rt_err_t err;
- rt_ubase_t res_rate;
- struct scmi_clk *sclk = raw_to_scmi_clk(cell->clk_np);
- struct scmi_clk_data *clk_data = cell_to_scmi_clk_data(cell);
- if (!(err = scmi_clk_op_set_rate(sclk, clk_data->id, rate)))
- {
- res_rate = scmi_clk_op_get_rate(sclk, clk_data->id);
- if ((rt_err_t)res_rate < 0)
- {
- err = (rt_err_t)res_rate;
- }
- }
- return err;
- }
- static const struct rt_clk_ops scmi_clk_ops =
- {
- .enable = scmi_clk_enable,
- .disable = scmi_clk_disable,
- .recalc_rate = scmi_clk_recalc_rate,
- .round_rate = scmi_clk_round_rate,
- .set_rate = scmi_clk_set_rate,
- };
- static rt_err_t scmi_clk_probe(struct rt_scmi_device *sdev)
- {
- rt_err_t err;
- rt_size_t cell_count, out_size;
- struct rt_scmi_msg msg;
- struct rt_device *dev = &sdev->parent;
- struct rt_clk_cell **cells_list = RT_NULL, *cell;
- struct scmi_clk_data *clk_data;
- struct scmi_clk_describe_rates_out *out = RT_NULL;
- struct scmi_clk_describe_rates_in in;
- struct scmi_clk_attributes attr;
- struct scmi_clk_name_in name_in;
- struct scmi_clk_name_out name_out;
- struct scmi_clk_describe_attributes_in clk_attr_in;
- struct scmi_clk_describe_attributes_out clk_attr_out;
- struct scmi_clk *sclk = rt_calloc(1, sizeof(*sclk));
- if (!sclk)
- {
- return -RT_ENOMEM;
- }
- sclk->sdev = sdev;
- msg = RT_SCMI_MSG_OUT(SCMI_COM_MSG_ATTRIBUTES, &attr);
- if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
- {
- goto _fail;
- }
- cell_count = rt_le16_to_cpu(attr.num_clocks);
- if (!(cells_list = rt_calloc(cell_count, sizeof(*cells_list))))
- {
- err = -RT_ENOMEM;
- goto _fail;
- }
- out_size = rt_offsetof(struct scmi_clk_describe_rates_out, rate[SCMI_MAX_NUM_RATES]);
- if (!(out = rt_malloc(out_size)))
- {
- err = -RT_ENOMEM;
- goto _fail;
- }
- for (int id = 0; id < cell_count; ++id)
- {
- const char *clk_name;
- rt_uint32_t flags, rates_nr, rate_discrete;
- in.id = rt_cpu_to_le32(id);
- in.rate_index = rt_cpu_to_le32(0);
- msg = RT_SCMI_MSG_RAW(SCMI_CLOCK_DESCRIBE_RATES, &in, sizeof(in), out, out_size);
- if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
- {
- goto _fail;
- }
- flags = rt_le32_to_cpu(out->num_rates_flags);
- rates_nr = SCMI_NUM_REMAINING(flags);
- rate_discrete = SCMI_RATE_DISCRETE(flags);
- if (rate_discrete)
- {
- clk_data = rt_malloc(rt_offsetof(struct scmi_clk_data,
- info.list.rates[SCMI_MAX_NUM_RATES]));
- }
- else
- {
- clk_data = rt_malloc(sizeof(*clk_data));
- }
- if (!clk_data)
- {
- err = -RT_ENOMEM;
- break;
- }
- if (rate_discrete)
- {
- for (int i = 0; i < rates_nr; ++i)
- {
- clk_data->info.list.rates[i] = SCMI_RATE_TO_U64(out->rate[i]);
- }
- clk_data->info.list.rates_nr = rates_nr;
- }
- else
- {
- clk_data->info.range.min_rate = SCMI_RATE_TO_U64(out->rate[0]);
- clk_data->info.range.max_rate = SCMI_RATE_TO_U64(out->rate[1]);
- clk_data->info.range.step_size = SCMI_RATE_TO_U64(out->rate[2]);
- }
- clk_data->rate_discrete = rate_discrete;
- clk_data->id = id;
- cell = &clk_data->cell;
- rt_memset(cell, 0, sizeof(*cell));
- clk_attr_in.clock_id = rt_cpu_to_le32(id);
- msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_ATTRIBUTES, &clk_attr_in, &clk_attr_out);
- if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
- {
- rt_free(clk_data);
- goto _fail;
- }
- if (SUPPORTS_EXTENDED_NAMES(clk_attr_out.attributes))
- {
- name_in.clock_id = rt_cpu_to_le32(id);
- msg = RT_SCMI_MSG_IN_OUT(SCMI_CLOCK_NAME_GET, &name_in, &name_out);
- if ((err = rt_scmi_process_msg(sclk->sdev, &msg)))
- {
- rt_free(clk_data);
- goto _fail;
- }
- clk_name = (const char *)name_out.name;
- }
- else
- {
- clk_name = (const char *)clk_attr_out.name;
- }
- if (!(cell->name = rt_strdup(clk_name)))
- {
- rt_free(clk_data);
- err = -RT_ENOMEM;
- goto _fail;
- }
- cell->ops = &scmi_clk_ops;
- cell->flags = RT_CLK_F_GET_RATE_NOCACHE;
- cells_list[id] = cell;
- }
- sclk->parent.dev = dev;
- sclk->parent.cells = cells_list;
- sclk->parent.cells_nr = cell_count;
- if ((err = rt_clk_register(&sclk->parent)))
- {
- goto _fail;
- }
- for (int id = 0; id < cell_count; ++id)
- {
- struct rt_clk *clk = rt_clk_cell_get_clk(cells_list[id], RT_NULL);
- if (clk)
- {
- rt_ubase_t min_rate, max_rate;
- clk_data = cell_to_scmi_clk_data(cells_list[id]);
- if (clk_data->rate_discrete)
- {
- min_rate = clk_data->info.list.rates[0];
- max_rate = clk_data->info.list.rates[clk_data->info.list.rates_nr - 1];
- }
- else
- {
- min_rate = clk_data->info.range.min_rate;
- max_rate = clk_data->info.range.max_rate;
- }
- rt_clk_set_rate_range(clk, min_rate, max_rate);
- }
- }
- return RT_EOK;
- _fail:
- if (out)
- {
- rt_free(out);
- }
- if (cells_list)
- {
- for (int id = 0; id < cell_count; ++id)
- {
- if (!cells_list[id])
- {
- break;
- }
- cell = cells_list[id];
- clk_data = cell_to_scmi_clk_data(cell);
- rt_free(clk_data);
- }
- rt_free(cells_list);
- }
- rt_free(sclk);
- return err;
- }
- static const struct rt_scmi_device_id scmi_clk_ids[] =
- {
- { SCMI_PROTOCOL_ID_CLOCK, "clocks" },
- { /* sentinel */ },
- };
- static struct rt_scmi_driver scmi_clk_driver =
- {
- .name = "clk-scmi",
- .ids = scmi_clk_ids,
- .probe = scmi_clk_probe,
- };
- static int scmi_clk_drv_register(void)
- {
- rt_scmi_driver_register(&scmi_clk_driver);
- return 0;
- }
- INIT_SUBSYS_EXPORT(scmi_clk_drv_register);
|