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