Re: [RFC PATCH 8/8] clk: Add clock driver for the RISC-V RPMI clock service group

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Leyfoon,

On Tue, Dec 24, 2024 at 11:50 AM Leyfoon Tan
<leyfoon.tan@xxxxxxxxxxxxxxxx> wrote:
>
>
>
> > -----Original Message-----
> > From: Anup Patel <apatel@xxxxxxxxxxxxxxxx>
> > Sent: Monday, December 16, 2024 4:48 PM
> > To: Michael Turquette <mturquette@xxxxxxxxxxxx>; Stephen Boyd
> > <sboyd@xxxxxxxxxx>; Rob Herring <robh@xxxxxxxxxx>; Krzysztof Kozlowski
> > <krzk+dt@xxxxxxxxxx>; Conor Dooley <conor+dt@xxxxxxxxxx>; Jassi Brar
> > <jassisinghbrar@xxxxxxxxx>
> > Cc: Palmer Dabbelt <palmer@xxxxxxxxxxx>; Paul Walmsley
> > <paul.walmsley@xxxxxxxxxx>; Sunil V L <sunilvl@xxxxxxxxxxxxxxxx>; Rahul
> > Pathak <rpathak@xxxxxxxxxxxxxxxx>; Leyfoon Tan
> > <leyfoon.tan@xxxxxxxxxxxxxxxx>; Atish Patra <atishp@xxxxxxxxxxxxxx>;
> > Andrew Jones <ajones@xxxxxxxxxxxxxxxx>; Anup Patel
> > <anup@xxxxxxxxxxxxxx>; linux-clk@xxxxxxxxxxxxxxx;
> > devicetree@xxxxxxxxxxxxxxx; linux-riscv@xxxxxxxxxxxxxxxxxxx; linux-
> > kernel@xxxxxxxxxxxxxxx; Anup Patel <apatel@xxxxxxxxxxxxxxxx>
> > Subject: [RFC PATCH 8/8] clk: Add clock driver for the RISC-V RPMI clock
> > service group
> >
> > From: Rahul Pathak <rpathak@xxxxxxxxxxxxxxxx>
> >
> > The RPMI specification defines a clock service group which can be accessed via
> > SBI MPXY extension or dedicated S-mode RPMI transport.
> >
> > Add mailbox client based clock driver for the RISC-V RPMI clock service group.
> >
> > Co-developed-by: Anup Patel <apatel@xxxxxxxxxxxxxxxx>
> > Signed-off-by: Anup Patel <apatel@xxxxxxxxxxxxxxxx>
> > Signed-off-by: Rahul Pathak <rpathak@xxxxxxxxxxxxxxxx>
> > ---
>
>
> >  obj-y                                        += actions/
> > diff --git a/drivers/clk/clk-rpmi.c b/drivers/clk/clk-rpmi.c new file mode
> > 100644 index 000000000000..ed8e32527d3d
> > --- /dev/null
> > +++ b/drivers/clk/clk-rpmi.c
> > @@ -0,0 +1,588 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * RISC-V MPXY Based Clock Driver
> > + *
> > + * Copyright (C) 2024 Ventana Micro Systems Ltd.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/mm.h>
> > +#include <linux/of.h>
> > +#include <linux/err.h>
> > +#include <linux/slab.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/mailbox/riscv-rpmi-message.h>
> > +#include <linux/mailbox_client.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/clk-provider.h>
>
> Sorting header files in alphabetical order.

Already taken care of. also removed which are not needed.
>
> > +
> > +#define RPMI_CLK_MAX_NUM_RATES               16
> This macro only used for discrete clocks, so suggest change to RPMI_CLK_MAX_DISCRETE_NUM_RATES.
> Is 16 too few?

I will change the name. We can keep 16 for now and
may change later if we find this insufficient.

>
> > +#define RPMI_CLK_NAME_LEN            16
> > +
> > +#define GET_RATE_LO_U32(rate_u64)    ((u32)rate_u64)
> > +#define GET_RATE_HI_U32(rate_u64)    ((u32)((u64)(rate_u64) >> 32))
> > +#define GET_RATE_U64(hi_u32, lo_u32) ((u64)(hi_u32) << 32 |
> > (lo_u32))
> > +
> > +enum rpmi_clk_config {
> > +     RPMI_CLK_DISABLE = 0,
> > +     RPMI_CLK_ENABLE = 1,
> > +};
> > +
>
> [...]
>
> > +static int rpmi_clk_get_supported_rates(u32 clkid, struct rpmi_clk
> > +*rpmi_clk) {
> > +     struct rpmi_clk_context *context = rpmi_clk->context;
> > +     struct rpmi_get_supp_rates_tx tx;
> > +     struct rpmi_get_supp_rates_rx rx;
> > +     struct rpmi_mbox_message msg;
> > +     size_t clk_rate_idx = 0;
> > +     int ret, rateidx, j;
> > +
> > +     tx.clkid = cpu_to_le32(clkid);
> > +     tx.clk_rate_idx = 0;
> > +
> > +     rpmi_mbox_init_send_with_response(&msg,
> > RPMI_CLK_SRV_GET_SUPPORTED_RATES,
> > +                                       &tx, sizeof(tx), &rx, sizeof(rx));
> > +     ret = rpmi_mbox_send_message(context->chan, &msg);
> > +     if (ret)
> > +             return ret;
> > +     if (rx.status)
> > +             return rpmi_to_linux_error(rx.status);
> > +     if (!rx.returned)
> > +             return -EINVAL;
> > +
> > +     if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
> > +             for (rateidx = 0; rateidx < rx.returned; rateidx++) {
>
> Need to check RPMI_CLK_MAX_NUM_RATES limit as well.

Sure, i will update

>
> > +                     rpmi_clk->rates->discrete[rateidx] =
> > +
> >       GET_RATE_U64(rx.rates.discrete[rateidx].hi,
> > +
> > rx.rates.discrete[rateidx].lo);
> > +             }
> > +
> > +             while (rx.remaining) {
> > +                     clk_rate_idx += rx.returned;
> > +                     tx.clk_rate_idx = clk_rate_idx;
> > +
> > +                     rpmi_mbox_init_send_with_response(&msg,
> > +
> > RPMI_CLK_SRV_GET_SUPPORTED_RATES,
> > +                                                       &tx, sizeof(tx), &rx,
> > sizeof(rx));
> > +                     ret = rpmi_mbox_send_message(context->chan,
> > &msg);
> > +                     if (ret)
> > +                             return ret;
>
> Need check if (rx.status) here?

Sure, i will update

>
> > +
> > +                     for (j = 0; j < rx.returned; j++) {
> Same here, check RPMI_CLK_MAX_NUM_RATES.

Sure, i will update

>
> > +                             if (rateidx >= (clk_rate_idx + rx.returned))
> > +                                     break;
> > +                             rpmi_clk->rates->discrete[rateidx++] =
> > +                                     GET_RATE_U64(rx.rates.discrete[j].hi,
> > +                                                  rx.rates.discrete[j].lo);
> > +                     }
> > +             }
> > +     } else if (rpmi_clk->type == RPMI_CLK_LINEAR) {
> > +             rpmi_clk->rates->linear.min =
> > +                             GET_RATE_U64(rx.rates.linear.min_hi,
> > +                                          rx.rates.linear.min_lo);
> > +             rpmi_clk->rates->linear.max =
> > +                             GET_RATE_U64(rx.rates.linear.max_hi,
> > +                                          rx.rates.linear.max_lo);
> > +             rpmi_clk->rates->linear.step =
> > +                             GET_RATE_U64(rx.rates.linear.step_hi,
> > +                                          rx.rates.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;
> > +     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;
> > +     if (rx.status)
> > +             return rx.status;
> > +
> > +     return GET_RATE_U64(rx.hi, rx.lo);
> > +}
> > +
> > +static long rpmi_clk_round_rate(struct clk_hw *hw,
> > +                             unsigned long rate,
> > +                             unsigned long *parent_rate)
> > +{
> > +     struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
> > +     u64 fmin, fmax, ftmp;
> > +
> > +     if (rpmi_clk->type == RPMI_CLK_DISCRETE)
> > +             return rate;
> > +
> > +     fmin = rpmi_clk->rates->linear.min;
> > +     fmax = rpmi_clk->rates->linear.max;
> > +
> > +     if (rate <= fmin)
> > +             return fmin;
> > +     else if (rate >=  fmax)
> > +             return fmax;
> > +
> > +     ftmp = rate - fmin;
> > +     ftmp += rpmi_clk->rates->linear.step - 1;
> > +     do_div(ftmp, rpmi_clk->rates->linear.step);
> > +
> > +     return ftmp * rpmi_clk->rates->linear.step + fmin; }
> > +
> > +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;
> > +     int ret;
> > +
> > +     tx.clkid = cpu_to_le32(rpmi_clk->id);
> > +     tx.lo = cpu_to_le32(GET_RATE_LO_U32(rate));
> > +     tx.hi = cpu_to_le32(GET_RATE_HI_U32(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;
> > +     if (rx.status)
> > +             return rpmi_to_linux_error(rx.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;
> > +     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;
> > +     if (rx.status)
> > +             return rpmi_to_linux_error(rx.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;
> > +     int ret;
> > +
> > +     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));
> > +     ret = rpmi_mbox_send_message(context->chan, &msg);
> > +     if (ret || rx.status)
> > +             pr_err("Failed to disable clk-%u\n", rpmi_clk->id); }
> > +
> > +static const struct clk_ops rpmi_clk_ops = {
> > +     .recalc_rate = rpmi_clk_recalc_rate,
> > +     .round_rate = rpmi_clk_round_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(union rpmi_clk_rates), GFP_KERNEL);
> > +     if (!rates)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     rpmi_clk = devm_kzalloc(dev, sizeof(struct 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) {
> > +             dev_err(dev, "Failed to get clk-%u attributes\n", clkid);
> > +             return ERR_PTR(ret);
> > +     }
> > +
> > +     ret = rpmi_clk_get_supported_rates(clkid, rpmi_clk);
> > +     if (ret) {
> > +             dev_err(dev, "Get supported rates failed for clk-%u, %d\n",
> > +                     clkid, ret);
> > +             return ERR_PTR(ret);
> > +     }
> > +
> > +     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) {
> > +             dev_err(dev, "Unable to register clk-%u\n", clkid);
> > +             return ERR_PTR(ret);
> > +     }
> > +
> > +     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 NULL;
> > +}
> > +
> > +static void rpmi_clk_receive_message(struct mbox_client *cl, void *msg)
> > +{
> > +     /* Nothing to do here. */
> > +}
> > +
> > +static int rpmi_clk_probe(struct platform_device *pdev) {
> > +     struct device *dev = &pdev->dev;
> > +     struct clk_hw_onecell_data *clk_data;
> > +     struct rpmi_clk_context *context;
> > +     struct rpmi_mbox_message msg;
> > +     int ret, num_clocks, i;
> > +     struct clk_hw *hw_ptr;
> > +
> > +     /* Allocate RPMI clock context */
> > +     context = devm_kzalloc(dev, sizeof(*context), GFP_KERNEL);
> > +     if (!context)
> > +             return -ENOMEM;
> > +     context->dev = dev;
> > +     platform_set_drvdata(pdev, context);
> > +
> > +     /* Setup mailbox client */
> > +     context->client.dev             = context->dev;
> > +     context->client.rx_callback     = rpmi_clk_receive_message;
> > +     context->client.tx_block        = false;
> > +     context->client.knows_txdone    = true;
> > +     context->client.tx_tout         = 0;
> > +
> > +     /* Request mailbox channel */
> > +     context->chan = mbox_request_channel(&context->client, 0);
> > +     if (IS_ERR(context->chan))
> > +             return PTR_ERR(context->chan);
> > +
> > +     /* Validate RPMI specification version */
> > +     rpmi_mbox_init_get_attribute(&msg,
> > RPMI_MBOX_ATTR_SPEC_VERSION);
> > +     ret = rpmi_mbox_send_message(context->chan, &msg);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to get spec version\n");
> > +             goto fail_free_channel;
> > +     }
> > +     if (msg.attr.value < RPMI_MKVER(1, 0)) {
> > +             dev_err(dev,
> > +                     "msg protocol version mismatch, expected 0x%x,
> > found 0x%x\n",
> > +                     RPMI_MKVER(1, 0), msg.attr.value);
> > +             ret = -EINVAL;
> > +             goto fail_free_channel;
> > +     }
> > +
> > +     /* Validate clock service group ID */
> > +     rpmi_mbox_init_get_attribute(&msg,
> > RPMI_MBOX_ATTR_SERVICEGROUP_ID);
> > +     ret = rpmi_mbox_send_message(context->chan, &msg);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to get service group ID\n");
> > +             goto fail_free_channel;
> > +     }
> > +     if (msg.attr.value != RPMI_SRVGRP_CLOCK) {
> > +             dev_err(dev,
> > +                     "service group match failed, expected 0x%x, found
> > 0x%x\n",
> > +                     RPMI_SRVGRP_CLOCK, msg.attr.value);
> > +             ret = -EINVAL;
> > +             goto fail_free_channel;
> > +     }
> > +
> > +     /* Validate clock service group version */
> > +     rpmi_mbox_init_get_attribute(&msg,
> > RPMI_MBOX_ATTR_SERVICEGROUP_VERSION);
> > +     ret = rpmi_mbox_send_message(context->chan, &msg);
> > +     if (ret) {
> > +             dev_err(dev, "Failed to get service group version\n");
> > +             goto fail_free_channel;
> > +     }
> > +     if (msg.attr.value < RPMI_MKVER(1, 0)) {
> > +             dev_err(dev,
> > +                     "service group version failed, expected 0x%x, found
> > 0x%x\n",
> > +                     RPMI_MKVER(1, 0), msg.attr.value);
> > +             ret = -EINVAL;
> > +             goto fail_free_channel;
> > +     }
> > +
> > +     /* Find-out number of clocks */
> > +     num_clocks = rpmi_clk_get_num_clocks(context);
> > +     if (!num_clocks) {
> > +             dev_err(dev, "No clocks found\n");
> > +             ret = -ENODEV;
> > +             goto fail_free_channel;
> > +     }
> > +
> > +     /* Allocate clock data */
> > +     clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_clocks),
> > +                             GFP_KERNEL);
> > +     if (!clk_data) {
> > +             ret = -ENOMEM;
> > +             goto fail_free_channel;
> > +     }
> > +     clk_data->num = num_clocks;
> > +
> > +     /* Setup clock data */
> > +     for (i = 0; i < clk_data->num; i++) {
> > +             hw_ptr = rpmi_clk_enumerate(context, i);
> > +             if (IS_ERR(hw_ptr)) {
> > +                     dev_err(dev, "failed to register clk-%d\n", i);
> > +                     ret = PTR_ERR(hw_ptr);
> > +                     goto fail_free_channel;
> > +             }
> > +             clk_data->hws[i] = hw_ptr;
> > +     }
> > +
> > +     /* Register clock HW provider */
> > +     ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
> > clk_data);
> > +     if (ret) {
> > +             dev_err(dev, "failed to register clock HW provider\n");
> > +             goto fail_free_channel;
> > +     }
> > +
> > +     dev_info(dev, "clk HW provider registered with %d clocks\n",
> > +              num_clocks);
> > +     return 0;
> > +
> > +fail_free_channel:
> > +     mbox_free_channel(context->chan);
> > +     return ret;
> > +}
> > +
> > +static void rpmi_clk_remove(struct platform_device *pdev) {
> > +     struct rpmi_clk_context *context = platform_get_drvdata(pdev);
> > +
> > +     mbox_free_channel(context->chan);
> > +}
> > +
> > +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,
> > +     .remove = rpmi_clk_remove,
> > +};
> > +module_platform_driver(rpmi_clk_driver);
> > +
> > +MODULE_AUTHOR("Rahul Pathak <rpathak@xxxxxxxxxxxxxxxx>");
> > +MODULE_DESCRIPTION("Clock Driver based on RPMI message protocol");
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/linux/mailbox/riscv-rpmi-message.h
> > b/include/linux/mailbox/riscv-rpmi-message.h
> > index 8f4b3a0edbce..4e9478c4c0a3 100644
> > --- a/include/linux/mailbox/riscv-rpmi-message.h
> > +++ b/include/linux/mailbox/riscv-rpmi-message.h
> > @@ -89,6 +89,22 @@ static inline int rpmi_to_linux_error(int rpmi_error)
> >       }
> >  }
> >
> > +/** RPMI service group IDs */
> > +#define RPMI_SRVGRP_CLOCK            0x00007
> > +
> > +/** RPMI clock service IDs */
> > +enum rpmi_clock_service_id {
> > +     RPMI_CLK_SRV_ENABLE_NOTIFICATION = 0x01,
> > +     RPMI_CLK_SRV_GET_NUM_CLOCKS = 0x02,
> > +     RPMI_CLK_SRV_GET_ATTRIBUTES = 0x03,
> > +     RPMI_CLK_SRV_GET_SUPPORTED_RATES = 0x04,
> > +     RPMI_CLK_SRV_SET_CONFIG = 0x05,
> > +     RPMI_CLK_SRV_GET_CONFIG = 0x06,
> > +     RPMI_CLK_SRV_SET_RATE = 0x07,
> > +     RPMI_CLK_SRV_GET_RATE = 0x08,
> > +     RPMI_CLK_SRV_ID_MAX_COUNT,
> > +};
> > +
> >  /** RPMI linux mailbox attribute IDs */  enum rpmi_mbox_attribute_id {
> >       RPMI_MBOX_ATTR_SPEC_VERSION = 0,
> > --
> > 2.43.0
>





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux