root/drivers/clk/clk-scpi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. scpi_clk_recalc_rate
  2. scpi_clk_round_rate
  3. scpi_clk_set_rate
  4. __scpi_dvfs_round_rate
  5. scpi_dvfs_recalc_rate
  6. scpi_dvfs_round_rate
  7. __scpi_find_dvfs_index
  8. scpi_dvfs_set_rate
  9. scpi_clk_ops_init
  10. scpi_of_clk_src_get
  11. scpi_clk_add
  12. scpi_clocks_remove
  13. scpi_clocks_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * System Control and Power Interface (SCPI) Protocol based clock driver
   4  *
   5  * Copyright (C) 2015 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/of_platform.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/scpi_protocol.h>
  16 
  17 struct scpi_clk {
  18         u32 id;
  19         struct clk_hw hw;
  20         struct scpi_dvfs_info *info;
  21         struct scpi_ops *scpi_ops;
  22 };
  23 
  24 #define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
  25 
  26 static struct platform_device *cpufreq_dev;
  27 
  28 static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
  29                                           unsigned long parent_rate)
  30 {
  31         struct scpi_clk *clk = to_scpi_clk(hw);
  32 
  33         return clk->scpi_ops->clk_get_val(clk->id);
  34 }
  35 
  36 static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
  37                                 unsigned long *parent_rate)
  38 {
  39         /*
  40          * We can't figure out what rate it will be, so just return the
  41          * rate back to the caller. scpi_clk_recalc_rate() will be called
  42          * after the rate is set and we'll know what rate the clock is
  43          * running at then.
  44          */
  45         return rate;
  46 }
  47 
  48 static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
  49                              unsigned long parent_rate)
  50 {
  51         struct scpi_clk *clk = to_scpi_clk(hw);
  52 
  53         return clk->scpi_ops->clk_set_val(clk->id, rate);
  54 }
  55 
  56 static const struct clk_ops scpi_clk_ops = {
  57         .recalc_rate = scpi_clk_recalc_rate,
  58         .round_rate = scpi_clk_round_rate,
  59         .set_rate = scpi_clk_set_rate,
  60 };
  61 
  62 /* find closest match to given frequency in OPP table */
  63 static long __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
  64 {
  65         int idx;
  66         unsigned long fmin = 0, fmax = ~0, ftmp;
  67         const struct scpi_opp *opp = clk->info->opps;
  68 
  69         for (idx = 0; idx < clk->info->count; idx++, opp++) {
  70                 ftmp = opp->freq;
  71                 if (ftmp >= rate) {
  72                         if (ftmp <= fmax)
  73                                 fmax = ftmp;
  74                         break;
  75                 } else if (ftmp >= fmin) {
  76                         fmin = ftmp;
  77                 }
  78         }
  79         return fmax != ~0 ? fmax : fmin;
  80 }
  81 
  82 static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
  83                                            unsigned long parent_rate)
  84 {
  85         struct scpi_clk *clk = to_scpi_clk(hw);
  86         int idx = clk->scpi_ops->dvfs_get_idx(clk->id);
  87         const struct scpi_opp *opp;
  88 
  89         if (idx < 0)
  90                 return 0;
  91 
  92         opp = clk->info->opps + idx;
  93         return opp->freq;
  94 }
  95 
  96 static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
  97                                  unsigned long *parent_rate)
  98 {
  99         struct scpi_clk *clk = to_scpi_clk(hw);
 100 
 101         return __scpi_dvfs_round_rate(clk, rate);
 102 }
 103 
 104 static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
 105 {
 106         int idx, max_opp = clk->info->count;
 107         const struct scpi_opp *opp = clk->info->opps;
 108 
 109         for (idx = 0; idx < max_opp; idx++, opp++)
 110                 if (opp->freq == rate)
 111                         return idx;
 112         return -EINVAL;
 113 }
 114 
 115 static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
 116                               unsigned long parent_rate)
 117 {
 118         struct scpi_clk *clk = to_scpi_clk(hw);
 119         int ret = __scpi_find_dvfs_index(clk, rate);
 120 
 121         if (ret < 0)
 122                 return ret;
 123         return clk->scpi_ops->dvfs_set_idx(clk->id, (u8)ret);
 124 }
 125 
 126 static const struct clk_ops scpi_dvfs_ops = {
 127         .recalc_rate = scpi_dvfs_recalc_rate,
 128         .round_rate = scpi_dvfs_round_rate,
 129         .set_rate = scpi_dvfs_set_rate,
 130 };
 131 
 132 static const struct of_device_id scpi_clk_match[] = {
 133         { .compatible = "arm,scpi-dvfs-clocks", .data = &scpi_dvfs_ops, },
 134         { .compatible = "arm,scpi-variable-clocks", .data = &scpi_clk_ops, },
 135         {}
 136 };
 137 
 138 static int
 139 scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
 140                   struct scpi_clk *sclk, const char *name)
 141 {
 142         struct clk_init_data init;
 143         unsigned long min = 0, max = 0;
 144         int ret;
 145 
 146         init.name = name;
 147         init.flags = 0;
 148         init.num_parents = 0;
 149         init.ops = match->data;
 150         sclk->hw.init = &init;
 151         sclk->scpi_ops = get_scpi_ops();
 152 
 153         if (init.ops == &scpi_dvfs_ops) {
 154                 sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id);
 155                 if (IS_ERR(sclk->info))
 156                         return PTR_ERR(sclk->info);
 157         } else if (init.ops == &scpi_clk_ops) {
 158                 if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max)
 159                         return -EINVAL;
 160         } else {
 161                 return -EINVAL;
 162         }
 163 
 164         ret = devm_clk_hw_register(dev, &sclk->hw);
 165         if (!ret && max)
 166                 clk_hw_set_rate_range(&sclk->hw, min, max);
 167         return ret;
 168 }
 169 
 170 struct scpi_clk_data {
 171         struct scpi_clk **clk;
 172         unsigned int clk_num;
 173 };
 174 
 175 static struct clk_hw *
 176 scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
 177 {
 178         struct scpi_clk *sclk;
 179         struct scpi_clk_data *clk_data = data;
 180         unsigned int idx = clkspec->args[0], count;
 181 
 182         for (count = 0; count < clk_data->clk_num; count++) {
 183                 sclk = clk_data->clk[count];
 184                 if (idx == sclk->id)
 185                         return &sclk->hw;
 186         }
 187 
 188         return ERR_PTR(-EINVAL);
 189 }
 190 
 191 static int scpi_clk_add(struct device *dev, struct device_node *np,
 192                         const struct of_device_id *match)
 193 {
 194         int idx, count, err;
 195         struct scpi_clk_data *clk_data;
 196 
 197         count = of_property_count_strings(np, "clock-output-names");
 198         if (count < 0) {
 199                 dev_err(dev, "%pOFn: invalid clock output count\n", np);
 200                 return -EINVAL;
 201         }
 202 
 203         clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL);
 204         if (!clk_data)
 205                 return -ENOMEM;
 206 
 207         clk_data->clk_num = count;
 208         clk_data->clk = devm_kcalloc(dev, count, sizeof(*clk_data->clk),
 209                                      GFP_KERNEL);
 210         if (!clk_data->clk)
 211                 return -ENOMEM;
 212 
 213         for (idx = 0; idx < count; idx++) {
 214                 struct scpi_clk *sclk;
 215                 const char *name;
 216                 u32 val;
 217 
 218                 sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
 219                 if (!sclk)
 220                         return -ENOMEM;
 221 
 222                 if (of_property_read_string_index(np, "clock-output-names",
 223                                                   idx, &name)) {
 224                         dev_err(dev, "invalid clock name @ %pOFn\n", np);
 225                         return -EINVAL;
 226                 }
 227 
 228                 if (of_property_read_u32_index(np, "clock-indices",
 229                                                idx, &val)) {
 230                         dev_err(dev, "invalid clock index @ %pOFn\n", np);
 231                         return -EINVAL;
 232                 }
 233 
 234                 sclk->id = val;
 235 
 236                 err = scpi_clk_ops_init(dev, match, sclk, name);
 237                 if (err) {
 238                         dev_err(dev, "failed to register clock '%s'\n", name);
 239                         return err;
 240                 }
 241 
 242                 dev_dbg(dev, "Registered clock '%s'\n", name);
 243                 clk_data->clk[idx] = sclk;
 244         }
 245 
 246         return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data);
 247 }
 248 
 249 static int scpi_clocks_remove(struct platform_device *pdev)
 250 {
 251         struct device *dev = &pdev->dev;
 252         struct device_node *child, *np = dev->of_node;
 253 
 254         if (cpufreq_dev) {
 255                 platform_device_unregister(cpufreq_dev);
 256                 cpufreq_dev = NULL;
 257         }
 258 
 259         for_each_available_child_of_node(np, child)
 260                 of_clk_del_provider(np);
 261         return 0;
 262 }
 263 
 264 static int scpi_clocks_probe(struct platform_device *pdev)
 265 {
 266         int ret;
 267         struct device *dev = &pdev->dev;
 268         struct device_node *child, *np = dev->of_node;
 269         const struct of_device_id *match;
 270 
 271         if (!get_scpi_ops())
 272                 return -ENXIO;
 273 
 274         for_each_available_child_of_node(np, child) {
 275                 match = of_match_node(scpi_clk_match, child);
 276                 if (!match)
 277                         continue;
 278                 ret = scpi_clk_add(dev, child, match);
 279                 if (ret) {
 280                         scpi_clocks_remove(pdev);
 281                         of_node_put(child);
 282                         return ret;
 283                 }
 284 
 285                 if (match->data != &scpi_dvfs_ops)
 286                         continue;
 287                 /* Add the virtual cpufreq device if it's DVFS clock provider */
 288                 cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
 289                                                               -1, NULL, 0);
 290                 if (IS_ERR(cpufreq_dev))
 291                         pr_warn("unable to register cpufreq device");
 292         }
 293         return 0;
 294 }
 295 
 296 static const struct of_device_id scpi_clocks_ids[] = {
 297         { .compatible = "arm,scpi-clocks", },
 298         {}
 299 };
 300 MODULE_DEVICE_TABLE(of, scpi_clocks_ids);
 301 
 302 static struct platform_driver scpi_clocks_driver = {
 303         .driver = {
 304                 .name = "scpi_clocks",
 305                 .of_match_table = scpi_clocks_ids,
 306         },
 307         .probe = scpi_clocks_probe,
 308         .remove = scpi_clocks_remove,
 309 };
 310 module_platform_driver(scpi_clocks_driver);
 311 
 312 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
 313 MODULE_DESCRIPTION("ARM SCPI clock driver");
 314 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */