linux/drivers/clk/clk-scmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * System Control and Power Interface (SCMI) Protocol based clock driver
   4 *
   5 * Copyright (C) 2018-2021 ARM Ltd.
   6 */
   7
   8#include <linux/clk-provider.h>
   9#include <linux/device.h>
  10#include <linux/err.h>
  11#include <linux/of.h>
  12#include <linux/module.h>
  13#include <linux/scmi_protocol.h>
  14#include <asm/div64.h>
  15
  16static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
  17
  18struct scmi_clk {
  19        u32 id;
  20        struct clk_hw hw;
  21        const struct scmi_clock_info *info;
  22        const struct scmi_protocol_handle *ph;
  23};
  24
  25#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
  26
  27static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
  28                                          unsigned long parent_rate)
  29{
  30        int ret;
  31        u64 rate;
  32        struct scmi_clk *clk = to_scmi_clk(hw);
  33
  34        ret = scmi_proto_clk_ops->rate_get(clk->ph, clk->id, &rate);
  35        if (ret)
  36                return 0;
  37        return rate;
  38}
  39
  40static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
  41                                unsigned long *parent_rate)
  42{
  43        u64 fmin, fmax, ftmp;
  44        struct scmi_clk *clk = to_scmi_clk(hw);
  45
  46        /*
  47         * We can't figure out what rate it will be, so just return the
  48         * rate back to the caller. scmi_clk_recalc_rate() will be called
  49         * after the rate is set and we'll know what rate the clock is
  50         * running at then.
  51         */
  52        if (clk->info->rate_discrete)
  53                return rate;
  54
  55        fmin = clk->info->range.min_rate;
  56        fmax = clk->info->range.max_rate;
  57        if (rate <= fmin)
  58                return fmin;
  59        else if (rate >= fmax)
  60                return fmax;
  61
  62        ftmp = rate - fmin;
  63        ftmp += clk->info->range.step_size - 1; /* to round up */
  64        do_div(ftmp, clk->info->range.step_size);
  65
  66        return ftmp * clk->info->range.step_size + fmin;
  67}
  68
  69static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
  70                             unsigned long parent_rate)
  71{
  72        struct scmi_clk *clk = to_scmi_clk(hw);
  73
  74        return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
  75}
  76
  77static int scmi_clk_enable(struct clk_hw *hw)
  78{
  79        struct scmi_clk *clk = to_scmi_clk(hw);
  80
  81        return scmi_proto_clk_ops->enable(clk->ph, clk->id);
  82}
  83
  84static void scmi_clk_disable(struct clk_hw *hw)
  85{
  86        struct scmi_clk *clk = to_scmi_clk(hw);
  87
  88        scmi_proto_clk_ops->disable(clk->ph, clk->id);
  89}
  90
  91static const struct clk_ops scmi_clk_ops = {
  92        .recalc_rate = scmi_clk_recalc_rate,
  93        .round_rate = scmi_clk_round_rate,
  94        .set_rate = scmi_clk_set_rate,
  95        /*
  96         * We can't provide enable/disable callback as we can't perform the same
  97         * in atomic context. Since the clock framework provides standard API
  98         * clk_prepare_enable that helps cases using clk_enable in non-atomic
  99         * context, it should be fine providing prepare/unprepare.
 100         */
 101        .prepare = scmi_clk_enable,
 102        .unprepare = scmi_clk_disable,
 103};
 104
 105static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
 106{
 107        int ret;
 108        unsigned long min_rate, max_rate;
 109
 110        struct clk_init_data init = {
 111                .flags = CLK_GET_RATE_NOCACHE,
 112                .num_parents = 0,
 113                .ops = &scmi_clk_ops,
 114                .name = sclk->info->name,
 115        };
 116
 117        sclk->hw.init = &init;
 118        ret = devm_clk_hw_register(dev, &sclk->hw);
 119        if (ret)
 120                return ret;
 121
 122        if (sclk->info->rate_discrete) {
 123                int num_rates = sclk->info->list.num_rates;
 124
 125                if (num_rates <= 0)
 126                        return -EINVAL;
 127
 128                min_rate = sclk->info->list.rates[0];
 129                max_rate = sclk->info->list.rates[num_rates - 1];
 130        } else {
 131                min_rate = sclk->info->range.min_rate;
 132                max_rate = sclk->info->range.max_rate;
 133        }
 134
 135        clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate);
 136        return ret;
 137}
 138
 139static int scmi_clocks_probe(struct scmi_device *sdev)
 140{
 141        int idx, count, err;
 142        struct clk_hw **hws;
 143        struct clk_hw_onecell_data *clk_data;
 144        struct device *dev = &sdev->dev;
 145        struct device_node *np = dev->of_node;
 146        const struct scmi_handle *handle = sdev->handle;
 147        struct scmi_protocol_handle *ph;
 148
 149        if (!handle)
 150                return -ENODEV;
 151
 152        scmi_proto_clk_ops =
 153                handle->devm_protocol_get(sdev, SCMI_PROTOCOL_CLOCK, &ph);
 154        if (IS_ERR(scmi_proto_clk_ops))
 155                return PTR_ERR(scmi_proto_clk_ops);
 156
 157        count = scmi_proto_clk_ops->count_get(ph);
 158        if (count < 0) {
 159                dev_err(dev, "%pOFn: invalid clock output count\n", np);
 160                return -EINVAL;
 161        }
 162
 163        clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count),
 164                                GFP_KERNEL);
 165        if (!clk_data)
 166                return -ENOMEM;
 167
 168        clk_data->num = count;
 169        hws = clk_data->hws;
 170
 171        for (idx = 0; idx < count; idx++) {
 172                struct scmi_clk *sclk;
 173
 174                sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
 175                if (!sclk)
 176                        return -ENOMEM;
 177
 178                sclk->info = scmi_proto_clk_ops->info_get(ph, idx);
 179                if (!sclk->info) {
 180                        dev_dbg(dev, "invalid clock info for idx %d\n", idx);
 181                        continue;
 182                }
 183
 184                sclk->id = idx;
 185                sclk->ph = ph;
 186
 187                err = scmi_clk_ops_init(dev, sclk);
 188                if (err) {
 189                        dev_err(dev, "failed to register clock %d\n", idx);
 190                        devm_kfree(dev, sclk);
 191                        hws[idx] = NULL;
 192                } else {
 193                        dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
 194                        hws[idx] = &sclk->hw;
 195                }
 196        }
 197
 198        return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
 199                                           clk_data);
 200}
 201
 202static const struct scmi_device_id scmi_id_table[] = {
 203        { SCMI_PROTOCOL_CLOCK, "clocks" },
 204        { },
 205};
 206MODULE_DEVICE_TABLE(scmi, scmi_id_table);
 207
 208static struct scmi_driver scmi_clocks_driver = {
 209        .name = "scmi-clocks",
 210        .probe = scmi_clocks_probe,
 211        .id_table = scmi_id_table,
 212};
 213module_scmi_driver(scmi_clocks_driver);
 214
 215MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
 216MODULE_DESCRIPTION("ARM SCMI clock driver");
 217MODULE_LICENSE("GPL v2");
 218