aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2017-06-06 06:27:57 -0400
committerSudeep Holla <sudeep.holla@arm.com>2018-02-28 11:37:57 -0500
commit5f6c6430e904d21bfe5d0076b1ff3e8b9ed94ba0 (patch)
tree2354252fed5659119bd1723ac50d41f0ee5f6fae
parenta9e3fbfaa0ff885aacafe6f33e72448a2993d072 (diff)
firmware: arm_scmi: add initial support for clock protocol
The clock protocol is intended for management of clocks. It is used to enable or disable clocks, and to set and get the clock rates. This protocol provides commands to describe the protocol version, discover various implementation specific attributes, describe a clock, enable and disable a clock and get/set the rate of the clock synchronously or asynchronously. This patch adds initial support for the clock protocol. Cc: Arnd Bergmann <arnd@arndb.de> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
-rw-r--r--drivers/firmware/arm_scmi/Makefile2
-rw-r--r--drivers/firmware/arm_scmi/clock.c342
-rw-r--r--include/linux/scmi_protocol.h44
3 files changed, 387 insertions, 1 deletions
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 687cbbfb3af6..2130ee9ac825 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,4 +1,4 @@
1obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o 1obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o
2scmi-bus-y = bus.o 2scmi-bus-y = bus.o
3scmi-driver-y = driver.o 3scmi-driver-y = driver.o
4scmi-protocols-y = base.o perf.o 4scmi-protocols-y = base.o clock.o perf.o
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
new file mode 100644
index 000000000000..e8ffad33a0ff
--- /dev/null
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -0,0 +1,342 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * System Control and Management Interface (SCMI) Clock Protocol
4 *
5 * Copyright (C) 2018 ARM Ltd.
6 */
7
8#include "common.h"
9
10enum scmi_clock_protocol_cmd {
11 CLOCK_ATTRIBUTES = 0x3,
12 CLOCK_DESCRIBE_RATES = 0x4,
13 CLOCK_RATE_SET = 0x5,
14 CLOCK_RATE_GET = 0x6,
15 CLOCK_CONFIG_SET = 0x7,
16};
17
18struct scmi_msg_resp_clock_protocol_attributes {
19 __le16 num_clocks;
20 u8 max_async_req;
21 u8 reserved;
22};
23
24struct scmi_msg_resp_clock_attributes {
25 __le32 attributes;
26#define CLOCK_ENABLE BIT(0)
27 u8 name[SCMI_MAX_STR_SIZE];
28};
29
30struct scmi_clock_set_config {
31 __le32 id;
32 __le32 attributes;
33};
34
35struct scmi_msg_clock_describe_rates {
36 __le32 id;
37 __le32 rate_index;
38};
39
40struct scmi_msg_resp_clock_describe_rates {
41 __le32 num_rates_flags;
42#define NUM_RETURNED(x) ((x) & 0xfff)
43#define RATE_DISCRETE(x) !((x) & BIT(12))
44#define NUM_REMAINING(x) ((x) >> 16)
45 struct {
46 __le32 value_low;
47 __le32 value_high;
48 } rate[0];
49#define RATE_TO_U64(X) \
50({ \
51 typeof(X) x = (X); \
52 le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) << 32; \
53})
54};
55
56struct scmi_clock_set_rate {
57 __le32 flags;
58#define CLOCK_SET_ASYNC BIT(0)
59#define CLOCK_SET_DELAYED BIT(1)
60#define CLOCK_SET_ROUND_UP BIT(2)
61#define CLOCK_SET_ROUND_AUTO BIT(3)
62 __le32 id;
63 __le32 value_low;
64 __le32 value_high;
65};
66
67struct clock_info {
68 int num_clocks;
69 int max_async_req;
70 struct scmi_clock_info *clk;
71};
72
73static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle,
74 struct clock_info *ci)
75{
76 int ret;
77 struct scmi_xfer *t;
78 struct scmi_msg_resp_clock_protocol_attributes *attr;
79
80 ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
81 SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
82 if (ret)
83 return ret;
84
85 attr = t->rx.buf;
86
87 ret = scmi_do_xfer(handle, t);
88 if (!ret) {
89 ci->num_clocks = le16_to_cpu(attr->num_clocks);
90 ci->max_async_req = attr->max_async_req;
91 }
92
93 scmi_one_xfer_put(handle, t);
94 return ret;
95}
96
97static int scmi_clock_attributes_get(const struct scmi_handle *handle,
98 u32 clk_id, struct scmi_clock_info *clk)
99{
100 int ret;
101 struct scmi_xfer *t;
102 struct scmi_msg_resp_clock_attributes *attr;
103
104 ret = scmi_one_xfer_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
105 sizeof(clk_id), sizeof(*attr), &t);
106 if (ret)
107 return ret;
108
109 *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
110 attr = t->rx.buf;
111
112 ret = scmi_do_xfer(handle, t);
113 if (!ret)
114 memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
115 else
116 clk->name[0] = '\0';
117
118 scmi_one_xfer_put(handle, t);
119 return ret;
120}
121
122static int
123scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
124 struct scmi_clock_info *clk)
125{
126 u64 *rate;
127 int ret, cnt;
128 bool rate_discrete;
129 u32 tot_rate_cnt = 0, rates_flag;
130 u16 num_returned, num_remaining;
131 struct scmi_xfer *t;
132 struct scmi_msg_clock_describe_rates *clk_desc;
133 struct scmi_msg_resp_clock_describe_rates *rlist;
134
135 ret = scmi_one_xfer_init(handle, CLOCK_DESCRIBE_RATES,
136 SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
137 if (ret)
138 return ret;
139
140 clk_desc = t->tx.buf;
141 rlist = t->rx.buf;
142
143 do {
144 clk_desc->id = cpu_to_le32(clk_id);
145 /* Set the number of rates to be skipped/already read */
146 clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
147
148 ret = scmi_do_xfer(handle, t);
149 if (ret)
150 break;
151
152 rates_flag = le32_to_cpu(rlist->num_rates_flags);
153 num_remaining = NUM_REMAINING(rates_flag);
154 rate_discrete = RATE_DISCRETE(rates_flag);
155 num_returned = NUM_RETURNED(rates_flag);
156
157 if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
158 dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
159 break;
160 }
161
162 if (!rate_discrete) {
163 clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
164 clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
165 clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
166 dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
167 clk->range.min_rate, clk->range.max_rate,
168 clk->range.step_size);
169 break;
170 }
171
172 rate = &clk->list.rates[tot_rate_cnt];
173 for (cnt = 0; cnt < num_returned; cnt++, rate++) {
174 *rate = RATE_TO_U64(rlist->rate[cnt]);
175 dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
176 }
177
178 tot_rate_cnt += num_returned;
179 /*
180 * check for both returned and remaining to avoid infinite
181 * loop due to buggy firmware
182 */
183 } while (num_returned && num_remaining);
184
185 if (rate_discrete)
186 clk->list.num_rates = tot_rate_cnt;
187
188 scmi_one_xfer_put(handle, t);
189 return ret;
190}
191
192static int
193scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
194{
195 int ret;
196 struct scmi_xfer *t;
197
198 ret = scmi_one_xfer_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
199 sizeof(__le32), sizeof(u64), &t);
200 if (ret)
201 return ret;
202
203 *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
204
205 ret = scmi_do_xfer(handle, t);
206 if (!ret) {
207 __le32 *pval = t->rx.buf;
208
209 *value = le32_to_cpu(*pval);
210 *value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
211 }
212
213 scmi_one_xfer_put(handle, t);
214 return ret;
215}
216
217static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
218 u32 config, u64 rate)
219{
220 int ret;
221 struct scmi_xfer *t;
222 struct scmi_clock_set_rate *cfg;
223
224 ret = scmi_one_xfer_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
225 sizeof(*cfg), 0, &t);
226 if (ret)
227 return ret;
228
229 cfg = t->tx.buf;
230 cfg->flags = cpu_to_le32(config);
231 cfg->id = cpu_to_le32(clk_id);
232 cfg->value_low = cpu_to_le32(rate & 0xffffffff);
233 cfg->value_high = cpu_to_le32(rate >> 32);
234
235 ret = scmi_do_xfer(handle, t);
236
237 scmi_one_xfer_put(handle, t);
238 return ret;
239}
240
241static int
242scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
243{
244 int ret;
245 struct scmi_xfer *t;
246 struct scmi_clock_set_config *cfg;
247
248 ret = scmi_one_xfer_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
249 sizeof(*cfg), 0, &t);
250 if (ret)
251 return ret;
252
253 cfg = t->tx.buf;
254 cfg->id = cpu_to_le32(clk_id);
255 cfg->attributes = cpu_to_le32(config);
256
257 ret = scmi_do_xfer(handle, t);
258
259 scmi_one_xfer_put(handle, t);
260 return ret;
261}
262
263static int scmi_clock_enable(const struct scmi_handle *handle, u32 clk_id)
264{
265 return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
266}
267
268static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
269{
270 return scmi_clock_config_set(handle, clk_id, 0);
271}
272
273static int scmi_clock_count_get(const struct scmi_handle *handle)
274{
275 struct clock_info *ci = handle->clk_priv;
276
277 return ci->num_clocks;
278}
279
280static const struct scmi_clock_info *
281scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
282{
283 struct clock_info *ci = handle->clk_priv;
284 struct scmi_clock_info *clk = ci->clk + clk_id;
285
286 if (!clk->name || !clk->name[0])
287 return NULL;
288
289 return clk;
290}
291
292static struct scmi_clk_ops clk_ops = {
293 .count_get = scmi_clock_count_get,
294 .info_get = scmi_clock_info_get,
295 .rate_get = scmi_clock_rate_get,
296 .rate_set = scmi_clock_rate_set,
297 .enable = scmi_clock_enable,
298 .disable = scmi_clock_disable,
299};
300
301static int scmi_clock_protocol_init(struct scmi_handle *handle)
302{
303 u32 version;
304 int clkid, ret;
305 struct clock_info *cinfo;
306
307 scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
308
309 dev_dbg(handle->dev, "Clock Version %d.%d\n",
310 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
311
312 cinfo = devm_kzalloc(handle->dev, sizeof(*cinfo), GFP_KERNEL);
313 if (!cinfo)
314 return -ENOMEM;
315
316 scmi_clock_protocol_attributes_get(handle, cinfo);
317
318 cinfo->clk = devm_kcalloc(handle->dev, cinfo->num_clocks,
319 sizeof(*cinfo->clk), GFP_KERNEL);
320 if (!cinfo->clk)
321 return -ENOMEM;
322
323 for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
324 struct scmi_clock_info *clk = cinfo->clk + clkid;
325
326 ret = scmi_clock_attributes_get(handle, clkid, clk);
327 if (!ret)
328 scmi_clock_describe_rates_get(handle, clkid, clk);
329 }
330
331 handle->clk_ops = &clk_ops;
332 handle->clk_priv = cinfo;
333
334 return 0;
335}
336
337static int __init scmi_clock_init(void)
338{
339 return scmi_protocol_register(SCMI_PROTOCOL_CLOCK,
340 &scmi_clock_protocol_init);
341}
342subsys_initcall(scmi_clock_init);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 57d4b1c099e5..5a3092f05011 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -8,6 +8,7 @@
8#include <linux/types.h> 8#include <linux/types.h>
9 9
10#define SCMI_MAX_STR_SIZE 16 10#define SCMI_MAX_STR_SIZE 16
11#define SCMI_MAX_NUM_RATES 16
11 12
12/** 13/**
13 * struct scmi_revision_info - version information structure 14 * struct scmi_revision_info - version information structure
@@ -33,9 +34,49 @@ struct scmi_revision_info {
33 char sub_vendor_id[SCMI_MAX_STR_SIZE]; 34 char sub_vendor_id[SCMI_MAX_STR_SIZE];
34}; 35};
35 36
37struct scmi_clock_info {
38 char name[SCMI_MAX_STR_SIZE];
39 bool rate_discrete;
40 union {
41 struct {
42 int num_rates;
43 u64 rates[SCMI_MAX_NUM_RATES];
44 } list;
45 struct {
46 u64 min_rate;
47 u64 max_rate;
48 u64 step_size;
49 } range;
50 };
51};
52
36struct scmi_handle; 53struct scmi_handle;
37 54
38/** 55/**
56 * struct scmi_clk_ops - represents the various operations provided
57 * by SCMI Clock Protocol
58 *
59 * @count_get: get the count of clocks provided by SCMI
60 * @info_get: get the information of the specified clock
61 * @rate_get: request the current clock rate of a clock
62 * @rate_set: set the clock rate of a clock
63 * @enable: enables the specified clock
64 * @disable: disables the specified clock
65 */
66struct scmi_clk_ops {
67 int (*count_get)(const struct scmi_handle *handle);
68
69 const struct scmi_clock_info *(*info_get)
70 (const struct scmi_handle *handle, u32 clk_id);
71 int (*rate_get)(const struct scmi_handle *handle, u32 clk_id,
72 u64 *rate);
73 int (*rate_set)(const struct scmi_handle *handle, u32 clk_id,
74 u32 config, u64 rate);
75 int (*enable)(const struct scmi_handle *handle, u32 clk_id);
76 int (*disable)(const struct scmi_handle *handle, u32 clk_id);
77};
78
79/**
39 * struct scmi_perf_ops - represents the various operations provided 80 * struct scmi_perf_ops - represents the various operations provided
40 * by SCMI Performance Protocol 81 * by SCMI Performance Protocol
41 * 82 *
@@ -77,13 +118,16 @@ struct scmi_perf_ops {
77 * @dev: pointer to the SCMI device 118 * @dev: pointer to the SCMI device
78 * @version: pointer to the structure containing SCMI version information 119 * @version: pointer to the structure containing SCMI version information
79 * @perf_ops: pointer to set of performance protocol operations 120 * @perf_ops: pointer to set of performance protocol operations
121 * @clk_ops: pointer to set of clock protocol operations
80 */ 122 */
81struct scmi_handle { 123struct scmi_handle {
82 struct device *dev; 124 struct device *dev;
83 struct scmi_revision_info *version; 125 struct scmi_revision_info *version;
84 struct scmi_perf_ops *perf_ops; 126 struct scmi_perf_ops *perf_ops;
127 struct scmi_clk_ops *clk_ops;
85 /* for protocol internal use */ 128 /* for protocol internal use */
86 void *perf_priv; 129 void *perf_priv;
130 void *clk_priv;
87}; 131};
88 132
89enum scmi_std_protocol { 133enum scmi_std_protocol {