cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-scpi.c (7594B)


      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
     17struct 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
     26static struct platform_device *cpufreq_dev;
     27
     28static 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
     36static 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
     48static 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
     56static 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 */
     63static 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
     82static 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
     96static 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
    104static 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
    115static 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
    126static 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
    132static const struct of_device_id scpi_clk_match[] __maybe_unused = {
    133	{ .compatible = "arm,scpi-dvfs-clocks", .data = &scpi_dvfs_ops, },
    134	{ .compatible = "arm,scpi-variable-clocks", .data = &scpi_clk_ops, },
    135	{}
    136};
    137
    138static int
    139scpi_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
    170struct scpi_clk_data {
    171	struct scpi_clk **clk;
    172	unsigned int clk_num;
    173};
    174
    175static struct clk_hw *
    176scpi_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
    191static 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
    249static 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
    264static 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
    296static const struct of_device_id scpi_clocks_ids[] = {
    297	{ .compatible = "arm,scpi-clocks", },
    298	{}
    299};
    300MODULE_DEVICE_TABLE(of, scpi_clocks_ids);
    301
    302static 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};
    310module_platform_driver(scpi_clocks_driver);
    311
    312MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
    313MODULE_DESCRIPTION("ARM SCPI clock driver");
    314MODULE_LICENSE("GPL v2");