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