| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * RISC-V MPXY Based Clock Driver
- *
- * Copyright (C) 2025 Ventana Micro Systems Ltd.
- */
- #include <linux/clk-provider.h>
- #include <linux/err.h>
- #include <linux/mailbox_client.h>
- #include <linux/mailbox/riscv-rpmi-message.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/types.h>
- #include <linux/slab.h>
- #include <linux/wordpart.h>
- #define RPMI_CLK_DISCRETE_MAX_NUM_RATES 16
- #define RPMI_CLK_NAME_LEN 16
- #define to_rpmi_clk(clk) container_of(clk, struct rpmi_clk, hw)
- enum rpmi_clk_config {
- RPMI_CLK_DISABLE = 0,
- RPMI_CLK_ENABLE = 1,
- RPMI_CLK_CONFIG_MAX_IDX
- };
- #define RPMI_CLK_TYPE_MASK GENMASK(1, 0)
- enum rpmi_clk_type {
- RPMI_CLK_DISCRETE = 0,
- RPMI_CLK_LINEAR = 1,
- RPMI_CLK_TYPE_MAX_IDX
- };
- struct rpmi_clk_context {
- struct device *dev;
- struct mbox_chan *chan;
- struct mbox_client client;
- u32 max_msg_data_size;
- };
- /*
- * rpmi_clk_rates represents the rates format
- * as specified by the RPMI specification.
- * No other data format (e.g., struct linear_range)
- * is required to avoid to and from conversion.
- */
- union rpmi_clk_rates {
- u64 discrete[RPMI_CLK_DISCRETE_MAX_NUM_RATES];
- struct {
- u64 min;
- u64 max;
- u64 step;
- } linear;
- };
- struct rpmi_clk {
- struct rpmi_clk_context *context;
- u32 id;
- u32 num_rates;
- u32 transition_latency;
- enum rpmi_clk_type type;
- union rpmi_clk_rates *rates;
- char name[RPMI_CLK_NAME_LEN];
- struct clk_hw hw;
- };
- struct rpmi_clk_rate_discrete {
- __le32 lo;
- __le32 hi;
- };
- struct rpmi_clk_rate_linear {
- __le32 min_lo;
- __le32 min_hi;
- __le32 max_lo;
- __le32 max_hi;
- __le32 step_lo;
- __le32 step_hi;
- };
- struct rpmi_get_num_clocks_rx {
- __le32 status;
- __le32 num_clocks;
- };
- struct rpmi_get_attrs_tx {
- __le32 clkid;
- };
- struct rpmi_get_attrs_rx {
- __le32 status;
- __le32 flags;
- __le32 num_rates;
- __le32 transition_latency;
- char name[RPMI_CLK_NAME_LEN];
- };
- struct rpmi_get_supp_rates_tx {
- __le32 clkid;
- __le32 clk_rate_idx;
- };
- struct rpmi_get_supp_rates_rx {
- __le32 status;
- __le32 flags;
- __le32 remaining;
- __le32 returned;
- __le32 rates[];
- };
- struct rpmi_get_rate_tx {
- __le32 clkid;
- };
- struct rpmi_get_rate_rx {
- __le32 status;
- __le32 lo;
- __le32 hi;
- };
- struct rpmi_set_rate_tx {
- __le32 clkid;
- __le32 flags;
- __le32 lo;
- __le32 hi;
- };
- struct rpmi_set_rate_rx {
- __le32 status;
- };
- struct rpmi_set_config_tx {
- __le32 clkid;
- __le32 config;
- };
- struct rpmi_set_config_rx {
- __le32 status;
- };
- static inline u64 rpmi_clkrate_u64(u32 __hi, u32 __lo)
- {
- return (((u64)(__hi) << 32) | (u32)(__lo));
- }
- static u32 rpmi_clk_get_num_clocks(struct rpmi_clk_context *context)
- {
- struct rpmi_get_num_clocks_rx rx, *resp;
- struct rpmi_mbox_message msg;
- int ret;
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_NUM_CLOCKS,
- NULL, 0, &rx, sizeof(rx));
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return 0;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp || resp->status)
- return 0;
- return le32_to_cpu(resp->num_clocks);
- }
- static int rpmi_clk_get_attrs(u32 clkid, struct rpmi_clk *rpmi_clk)
- {
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_mbox_message msg;
- struct rpmi_get_attrs_tx tx;
- struct rpmi_get_attrs_rx rx, *resp;
- u8 format;
- int ret;
- tx.clkid = cpu_to_le32(clkid);
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_ATTRIBUTES,
- &tx, sizeof(tx), &rx, sizeof(rx));
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- rpmi_clk->id = clkid;
- rpmi_clk->num_rates = le32_to_cpu(resp->num_rates);
- rpmi_clk->transition_latency = le32_to_cpu(resp->transition_latency);
- strscpy(rpmi_clk->name, resp->name, RPMI_CLK_NAME_LEN);
- format = le32_to_cpu(resp->flags) & RPMI_CLK_TYPE_MASK;
- if (format >= RPMI_CLK_TYPE_MAX_IDX)
- return -EINVAL;
- rpmi_clk->type = format;
- return 0;
- }
- static int rpmi_clk_get_supported_rates(u32 clkid, struct rpmi_clk *rpmi_clk)
- {
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_clk_rate_discrete *rate_discrete;
- struct rpmi_clk_rate_linear *rate_linear;
- struct rpmi_get_supp_rates_tx tx;
- struct rpmi_get_supp_rates_rx *resp;
- struct rpmi_mbox_message msg;
- size_t clk_rate_idx;
- int ret, rateidx, j;
- tx.clkid = cpu_to_le32(clkid);
- tx.clk_rate_idx = 0;
- /*
- * Make sure we allocate rx buffer sufficient to be accommodate all
- * the rates sent in one RPMI message.
- */
- struct rpmi_get_supp_rates_rx *rx __free(kfree) =
- kzalloc(context->max_msg_data_size, GFP_KERNEL);
- if (!rx)
- return -ENOMEM;
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_SUPPORTED_RATES,
- &tx, sizeof(tx), rx, context->max_msg_data_size);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- if (!le32_to_cpu(resp->returned))
- return -EINVAL;
- if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
- rate_discrete = (struct rpmi_clk_rate_discrete *)resp->rates;
- for (rateidx = 0; rateidx < le32_to_cpu(resp->returned); rateidx++) {
- rpmi_clk->rates->discrete[rateidx] =
- rpmi_clkrate_u64(le32_to_cpu(rate_discrete[rateidx].hi),
- le32_to_cpu(rate_discrete[rateidx].lo));
- }
- /*
- * Keep sending the request message until all
- * the rates are received.
- */
- clk_rate_idx = 0;
- while (le32_to_cpu(resp->remaining)) {
- clk_rate_idx += le32_to_cpu(resp->returned);
- tx.clk_rate_idx = cpu_to_le32(clk_rate_idx);
- rpmi_mbox_init_send_with_response(&msg,
- RPMI_CLK_SRV_GET_SUPPORTED_RATES,
- &tx, sizeof(tx),
- rx, context->max_msg_data_size);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- if (!le32_to_cpu(resp->returned))
- return -EINVAL;
- for (j = 0; j < le32_to_cpu(resp->returned); j++) {
- if (rateidx >= clk_rate_idx + le32_to_cpu(resp->returned))
- break;
- rpmi_clk->rates->discrete[rateidx++] =
- rpmi_clkrate_u64(le32_to_cpu(rate_discrete[j].hi),
- le32_to_cpu(rate_discrete[j].lo));
- }
- }
- } else if (rpmi_clk->type == RPMI_CLK_LINEAR) {
- rate_linear = (struct rpmi_clk_rate_linear *)resp->rates;
- rpmi_clk->rates->linear.min = rpmi_clkrate_u64(le32_to_cpu(rate_linear->min_hi),
- le32_to_cpu(rate_linear->min_lo));
- rpmi_clk->rates->linear.max = rpmi_clkrate_u64(le32_to_cpu(rate_linear->max_hi),
- le32_to_cpu(rate_linear->max_lo));
- rpmi_clk->rates->linear.step = rpmi_clkrate_u64(le32_to_cpu(rate_linear->step_hi),
- le32_to_cpu(rate_linear->step_lo));
- }
- return 0;
- }
- static unsigned long rpmi_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_mbox_message msg;
- struct rpmi_get_rate_tx tx;
- struct rpmi_get_rate_rx rx, *resp;
- int ret;
- tx.clkid = cpu_to_le32(rpmi_clk->id);
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_RATE,
- &tx, sizeof(tx), &rx, sizeof(rx));
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- return rpmi_clkrate_u64(le32_to_cpu(resp->hi), le32_to_cpu(resp->lo));
- }
- static int rpmi_clk_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
- {
- struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
- u64 fmin, fmax, ftmp;
- /*
- * Keep the requested rate if the clock format
- * is of discrete type. Let the platform which
- * is actually controlling the clock handle that.
- */
- if (rpmi_clk->type == RPMI_CLK_DISCRETE)
- return 0;
- fmin = rpmi_clk->rates->linear.min;
- fmax = rpmi_clk->rates->linear.max;
- if (req->rate <= fmin) {
- req->rate = fmin;
- return 0;
- } else if (req->rate >= fmax) {
- req->rate = fmax;
- return 0;
- }
- ftmp = req->rate - fmin;
- ftmp += rpmi_clk->rates->linear.step - 1;
- do_div(ftmp, rpmi_clk->rates->linear.step);
- req->rate = ftmp * rpmi_clk->rates->linear.step + fmin;
- return 0;
- }
- static int rpmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
- {
- struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_mbox_message msg;
- struct rpmi_set_rate_tx tx;
- struct rpmi_set_rate_rx rx, *resp;
- int ret;
- tx.clkid = cpu_to_le32(rpmi_clk->id);
- tx.lo = cpu_to_le32(lower_32_bits(rate));
- tx.hi = cpu_to_le32(upper_32_bits(rate));
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_RATE,
- &tx, sizeof(tx), &rx, sizeof(rx));
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- return 0;
- }
- static int rpmi_clk_enable(struct clk_hw *hw)
- {
- struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_mbox_message msg;
- struct rpmi_set_config_tx tx;
- struct rpmi_set_config_rx rx, *resp;
- int ret;
- tx.config = cpu_to_le32(RPMI_CLK_ENABLE);
- tx.clkid = cpu_to_le32(rpmi_clk->id);
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
- &tx, sizeof(tx), &rx, sizeof(rx));
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return ret;
- resp = rpmi_mbox_get_msg_response(&msg);
- if (!resp)
- return -EINVAL;
- if (resp->status)
- return rpmi_to_linux_error(le32_to_cpu(resp->status));
- return 0;
- }
- static void rpmi_clk_disable(struct clk_hw *hw)
- {
- struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
- struct rpmi_clk_context *context = rpmi_clk->context;
- struct rpmi_mbox_message msg;
- struct rpmi_set_config_tx tx;
- struct rpmi_set_config_rx rx;
- tx.config = cpu_to_le32(RPMI_CLK_DISABLE);
- tx.clkid = cpu_to_le32(rpmi_clk->id);
- rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
- &tx, sizeof(tx), &rx, sizeof(rx));
- rpmi_mbox_send_message(context->chan, &msg);
- }
- static const struct clk_ops rpmi_clk_ops = {
- .recalc_rate = rpmi_clk_recalc_rate,
- .determine_rate = rpmi_clk_determine_rate,
- .set_rate = rpmi_clk_set_rate,
- .prepare = rpmi_clk_enable,
- .unprepare = rpmi_clk_disable,
- };
- static struct clk_hw *rpmi_clk_enumerate(struct rpmi_clk_context *context, u32 clkid)
- {
- struct device *dev = context->dev;
- unsigned long min_rate, max_rate;
- union rpmi_clk_rates *rates;
- struct rpmi_clk *rpmi_clk;
- struct clk_init_data init = {};
- struct clk_hw *clk_hw;
- int ret;
- rates = devm_kzalloc(dev, sizeof(*rates), GFP_KERNEL);
- if (!rates)
- return ERR_PTR(-ENOMEM);
- rpmi_clk = devm_kzalloc(dev, sizeof(*rpmi_clk), GFP_KERNEL);
- if (!rpmi_clk)
- return ERR_PTR(-ENOMEM);
- rpmi_clk->context = context;
- rpmi_clk->rates = rates;
- ret = rpmi_clk_get_attrs(clkid, rpmi_clk);
- if (ret)
- return dev_err_ptr_probe(dev, ret,
- "Failed to get clk-%u attributes\n",
- clkid);
- ret = rpmi_clk_get_supported_rates(clkid, rpmi_clk);
- if (ret)
- return dev_err_ptr_probe(dev, ret,
- "Get supported rates failed for clk-%u\n",
- clkid);
- init.flags = CLK_GET_RATE_NOCACHE;
- init.num_parents = 0;
- init.ops = &rpmi_clk_ops;
- init.name = rpmi_clk->name;
- clk_hw = &rpmi_clk->hw;
- clk_hw->init = &init;
- ret = devm_clk_hw_register(dev, clk_hw);
- if (ret)
- return dev_err_ptr_probe(dev, ret,
- "Unable to register clk-%u\n",
- clkid);
- if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
- min_rate = rpmi_clk->rates->discrete[0];
- max_rate = rpmi_clk->rates->discrete[rpmi_clk->num_rates - 1];
- } else {
- min_rate = rpmi_clk->rates->linear.min;
- max_rate = rpmi_clk->rates->linear.max;
- }
- clk_hw_set_rate_range(clk_hw, min_rate, max_rate);
- return clk_hw;
- }
- static void rpmi_clk_mbox_chan_release(void *data)
- {
- struct mbox_chan *chan = data;
- mbox_free_channel(chan);
- }
- static int rpmi_clk_probe(struct platform_device *pdev)
- {
- int ret;
- unsigned int num_clocks, i;
- struct clk_hw_onecell_data *clk_data;
- struct rpmi_clk_context *context;
- struct rpmi_mbox_message msg;
- struct clk_hw *hw_ptr;
- struct device *dev = &pdev->dev;
- context = devm_kzalloc(dev, sizeof(*context), GFP_KERNEL);
- if (!context)
- return -ENOMEM;
- context->dev = dev;
- platform_set_drvdata(pdev, context);
- context->client.dev = context->dev;
- context->client.rx_callback = NULL;
- context->client.tx_block = false;
- context->client.knows_txdone = true;
- context->client.tx_tout = 0;
- context->chan = mbox_request_channel(&context->client, 0);
- if (IS_ERR(context->chan))
- return PTR_ERR(context->chan);
- ret = devm_add_action_or_reset(dev, rpmi_clk_mbox_chan_release, context->chan);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to add rpmi mbox channel cleanup\n");
- rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SPEC_VERSION);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get spec version\n");
- if (msg.attr.value < RPMI_MKVER(1, 0)) {
- return dev_err_probe(dev, -EINVAL,
- "msg protocol version mismatch, expected 0x%x, found 0x%x\n",
- RPMI_MKVER(1, 0), msg.attr.value);
- }
- rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_ID);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get service group ID\n");
- if (msg.attr.value != RPMI_SRVGRP_CLOCK) {
- return dev_err_probe(dev, -EINVAL,
- "service group match failed, expected 0x%x, found 0x%x\n",
- RPMI_SRVGRP_CLOCK, msg.attr.value);
- }
- rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_VERSION);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get service group version\n");
- if (msg.attr.value < RPMI_MKVER(1, 0)) {
- return dev_err_probe(dev, -EINVAL,
- "service group version failed, expected 0x%x, found 0x%x\n",
- RPMI_MKVER(1, 0), msg.attr.value);
- }
- rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_MAX_MSG_DATA_SIZE);
- ret = rpmi_mbox_send_message(context->chan, &msg);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get max message data size\n");
- context->max_msg_data_size = msg.attr.value;
- num_clocks = rpmi_clk_get_num_clocks(context);
- if (!num_clocks)
- return dev_err_probe(dev, -ENODEV, "No clocks found\n");
- clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_clocks),
- GFP_KERNEL);
- if (!clk_data)
- return dev_err_probe(dev, -ENOMEM, "No memory for clock data\n");
- clk_data->num = num_clocks;
- for (i = 0; i < clk_data->num; i++) {
- hw_ptr = rpmi_clk_enumerate(context, i);
- if (IS_ERR(hw_ptr)) {
- return dev_err_probe(dev, PTR_ERR(hw_ptr),
- "Failed to register clk-%d\n", i);
- }
- clk_data->hws[i] = hw_ptr;
- }
- ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to register clock HW provider\n");
- return 0;
- }
- static const struct of_device_id rpmi_clk_of_match[] = {
- { .compatible = "riscv,rpmi-clock" },
- { }
- };
- MODULE_DEVICE_TABLE(of, rpmi_clk_of_match);
- static struct platform_driver rpmi_clk_driver = {
- .driver = {
- .name = "riscv-rpmi-clock",
- .of_match_table = rpmi_clk_of_match,
- },
- .probe = rpmi_clk_probe,
- };
- module_platform_driver(rpmi_clk_driver);
- MODULE_AUTHOR("Rahul Pathak <rpathak@ventanamicro.com>");
- MODULE_DESCRIPTION("Clock Driver based on RPMI message protocol");
- MODULE_LICENSE("GPL");
|