blob: 3290a2a9929fec73244eab3e37f3ee0658b0c30f [file] [log] [blame]
Odelu Kukatla73820752017-10-30 13:27:16 +05301/*
2 *Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
3 *
4 *This program is free software; you can redistribute it and/or modify
5 *it under the terms of the GNU General Public License version 2 and
6 *only version 2 as published by the Free Software Foundation.
7 *
8 *This program is distributed in the hope that it will be useful,
9 *but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 *GNU General Public License for more details.
12 */
13#include <linux/clk.h>
14#include <linux/device.h>
15#include <linux/devfreq.h>
16#include <linux/init.h>
17#include <linux/ipc_logging.h>
18#include <linux/gfp.h>
19#include <linux/list.h>
20#include <linux/module.h>
21#include <linux/mutex.h>
22#include <linux/msm-bus.h>
23#include <linux/of.h>
24#include <linux/platform_device.h>
25
26#include "governor.h"
27#include "devfreq_spdm.h"
28
29static void *spdm_ipc_log_ctxt;
30#define DEVFREQ_SPDM_DEFAULT_WINDOW_MS 100
31#define SPDM_IPC_LOG_PAGES 5
32
33#define SPDM_IPC_LOG(x...) do { \
34 pr_debug(x); \
35 if (spdm_ipc_log_ctxt) \
36 ipc_log_string(spdm_ipc_log_ctxt, x); \
37} while (0)
38
39#define COPY_SIZE(x, y) ((x) <= (y) ? (x) : (y))
40
41static int change_bw(struct device *dev, unsigned long *freq, u32 flags)
42{
43 struct spdm_data *data = 0;
44 int i;
45 int next_idx;
46 int ret = 0;
47 struct spdm_args desc = { { 0 } };
48 int ext_status = 0;
49
50 if (!dev || !freq)
51 return -EINVAL;
52
53 data = dev_get_drvdata(dev);
54 if (!data)
55 return -EINVAL;
56
57 if (data->devfreq->previous_freq == *freq)
58 goto update_thresholds;
59
60 next_idx = data->cur_idx + 1;
61 next_idx = next_idx % 2;
62
63 for (i = 0; i < data->pdata->usecase[next_idx].num_paths; i++)
64 data->pdata->usecase[next_idx].vectors[i].ab = (*freq) << 6;
65
66 data->cur_idx = next_idx;
67 ret = msm_bus_scale_client_update_request(data->bus_scale_client_id,
68 data->cur_idx);
69
70update_thresholds:
71 desc.arg[0] = SPDM_CMD_ENABLE;
72 desc.arg[1] = data->spdm_client;
73 desc.arg[2] = (clk_get_rate(data->cci_clk)) / 1000;
74 ext_status = spdm_ext_call(&desc, 3);
75 if (ext_status)
76 pr_err("External command %u failed with error %u",
77 (int)desc.arg[0], ext_status);
78 return ret;
79}
80
81static int get_cur_bw(struct device *dev, unsigned long *freq)
82{
83 struct spdm_data *data = 0;
84
85 if (!dev || !freq)
86 return -EINVAL;
87
88 data = dev_get_drvdata(dev);
89 if (!data)
90 return -EINVAL;
91
92 *freq = data->pdata->usecase[data->cur_idx].vectors[0].ab >> 6;
93
94 return 0;
95}
96
97static int get_dev_status(struct device *dev, struct devfreq_dev_status *status)
98{
99 struct spdm_data *data = 0;
100 int ret;
101
102 if (!dev || !status)
103 return -EINVAL;
104
105 data = dev_get_drvdata(dev);
106 if (!data)
107 return -EINVAL;
108
109 /*
110 * determine if we want to go up or down based on the notification.
111 */
112 if (data->action == SPDM_UP)
113 status->busy_time = 255;
114 else
115 status->busy_time = 0;
116 status->total_time = 255;
117 ret = get_cur_bw(dev, &status->current_frequency);
118 if (ret)
119 return ret;
120
121 return 0;
122
123}
124
125static int populate_config_data(struct spdm_data *data,
126 struct platform_device *pdev)
127{
128 int ret = -EINVAL;
129 struct device_node *node = pdev->dev.of_node;
130 struct property *prop = 0;
131
132 ret = of_property_read_u32(node, "qcom,max-vote",
133 &data->config_data.max_vote);
134 if (ret)
135 return ret;
136
137 ret = of_property_read_u32(node, "qcom,bw-upstep",
138 &data->config_data.upstep);
139 if (ret)
140 return ret;
141
142 ret = of_property_read_u32(node, "qcom,bw-dwnstep",
143 &data->config_data.downstep);
144 if (ret)
145 return ret;
146
147 ret = of_property_read_u32(node, "qcom,alpha-up",
148 &data->config_data.aup);
149 if (ret)
150 return ret;
151
152 ret = of_property_read_u32(node, "qcom,alpha-down",
153 &data->config_data.adown);
154 if (ret)
155 return ret;
156
157 ret = of_property_read_u32(node, "qcom,bucket-size",
158 &data->config_data.bucket_size);
159 if (ret)
160 return ret;
161
162 ret = of_property_read_u32_array(node, "qcom,pl-freqs",
163 data->config_data.pl_freqs,
164 SPDM_PL_COUNT - 1);
165 if (ret)
166 return ret;
167
168 ret = of_property_read_u32_array(node, "qcom,reject-rate",
169 data->config_data.reject_rate,
170 SPDM_PL_COUNT * 2);
171 if (ret)
172 return ret;
173
174 ret = of_property_read_u32_array(node, "qcom,response-time-us",
175 data->config_data.response_time_us,
176 SPDM_PL_COUNT * 2);
177 if (ret)
178 return ret;
179
180 ret = of_property_read_u32_array(node, "qcom,cci-response-time-us",
181 data->config_data.cci_response_time_us,
182 SPDM_PL_COUNT * 2);
183 if (ret)
184 return ret;
185
186 ret = of_property_read_u32(node, "qcom,max-cci-freq",
187 &data->config_data.max_cci_freq);
188 if (ret)
189 return ret;
190 ret = of_property_read_u32(node, "qcom,up-step-multp",
191 &data->config_data.up_step_multp);
192 if (ret)
193 return ret;
194
195 prop = of_find_property(node, "qcom,ports", 0);
196 if (!prop)
197 return -EINVAL;
198 data->config_data.num_ports = prop->length / sizeof(u32);
199 data->config_data.ports =
200 devm_kzalloc(&pdev->dev, prop->length, GFP_KERNEL);
201 if (!data->config_data.ports)
202 return -ENOMEM;
203 ret = of_property_read_u32_array(node, "qcom,ports",
204 data->config_data.ports,
205 data->config_data.num_ports);
206 if (ret) {
207 devm_kfree(&pdev->dev, data->config_data.ports);
208 data->config_data.ports = NULL;
209 return ret;
210 }
211
212 return 0;
213}
214
215static int populate_spdm_data(struct spdm_data *data,
216 struct platform_device *pdev)
217{
218 int ret = -EINVAL;
219 struct device_node *node = pdev->dev.of_node;
220
221 ret = populate_config_data(data, pdev);
222 if (ret)
223 return ret;
224
225 ret =
226 of_property_read_u32(node, "qcom,spdm-client", &data->spdm_client);
227 if (ret)
228 goto no_client;
229
230 ret = of_property_read_u32(node, "qcom,spdm-interval", &data->window);
231 if (ret)
232 data->window = DEVFREQ_SPDM_DEFAULT_WINDOW_MS;
233
234 data->pdata = msm_bus_cl_get_pdata(pdev);
235 if (!data->pdata) {
236 ret = -EINVAL;
237 goto no_pdata;
238 }
239
240 return 0;
241
242no_client:
243no_pdata:
244 devm_kfree(&pdev->dev, data->config_data.ports);
245 data->config_data.ports = NULL;
246 return ret;
247}
248
249#ifdef CONFIG_MSM_HVC
250int __spdm_hyp_call(struct spdm_args *args, int num_args)
251{
252 struct hvc_desc desc = { { 0 } };
253 int status;
254
255 memcpy(desc.arg, args->arg,
256 COPY_SIZE(sizeof(desc.arg), sizeof(args->arg)));
257 SPDM_IPC_LOG("hvc call fn:0x%x, cmd:%llu, num_args:%d\n",
258 HVC_FN_SIP(SPDM_HYP_FNID), desc.arg[0], num_args);
259
260 status = hvc(HVC_FN_SIP(SPDM_HYP_FNID), &desc);
261
262 memcpy(args->ret, desc.ret,
263 COPY_SIZE(sizeof(args->ret), sizeof(desc.ret)));
264 SPDM_IPC_LOG("hvc return fn:0x%x cmd:%llu Ret[0]:%llu Ret[1]:%llu\n",
265 HVC_FN_SIP(SPDM_HYP_FNID), desc.arg[0],
266 desc.ret[0], desc.ret[1]);
267 return status;
268}
269#endif
270
271int __spdm_scm_call(struct spdm_args *args, int num_args)
272{
273 int status = 0;
274
275 SPDM_IPC_LOG("%s:svc_id:%d,cmd_id:%d,cmd:%llu,num_args:%d\n",
276 __func__, SPDM_SCM_SVC_ID, SPDM_SCM_CMD_ID,
277 args->arg[0], num_args);
278
279 if (!is_scm_armv8()) {
280 status = scm_call(SPDM_SCM_SVC_ID, SPDM_SCM_CMD_ID, args->arg,
281 sizeof(args->arg), args->ret,
282 sizeof(args->ret));
283 } else {
284 struct scm_desc desc = {0};
285 /*
286 * Need to hard code this, this is a requirement from TZ syscall
287 * interface.
288 */
289 desc.arginfo = SCM_ARGS(6);
290 memcpy(desc.args, args->arg,
291 COPY_SIZE(sizeof(desc.args), sizeof(args->arg)));
292
293 status = scm_call2(SCM_SIP_FNID(SPDM_SCM_SVC_ID,
294 SPDM_SCM_CMD_ID), &desc);
295
296 memcpy(args->ret, desc.ret,
297 COPY_SIZE(sizeof(args->ret), sizeof(desc.ret)));
298 }
299 SPDM_IPC_LOG("%s:svc_id:%d,cmd_id:%d,cmd:%llu,Ret[0]:%llu,Ret[1]:%llu\n"
300 , __func__, SPDM_SCM_SVC_ID, SPDM_SCM_CMD_ID, args->arg[0],
301 args->ret[0], args->ret[1]);
302 return status;
303}
304
305static int probe(struct platform_device *pdev)
306{
307 struct spdm_data *data = 0;
308 int ret = -EINVAL;
309 struct spdm_args desc = { { 0 } };
310 int ext_status = 0;
311
312 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
313 if (!data)
314 return -ENOMEM;
315
316 data->action = SPDM_DOWN;
317
318 platform_set_drvdata(pdev, data);
319
320 ret = populate_spdm_data(data, pdev);
321 if (ret)
322 goto bad_of;
323
324 desc.arg[0] = SPDM_CMD_GET_VERSION;
325 ext_status = spdm_ext_call(&desc, 1);
326 if (ext_status) {
327 pr_err("%s:External command %u failed with error %u\n",
328 __func__, (int)desc.arg[0], ext_status);
329 goto bad_of;
330 }
331
332 if (desc.ret[0] < SPDM_TZ_VERSION) {
333 pr_err("%s: Version mismatch expected 0x%x got 0x%x", __func__,
334 SPDM_TZ_VERSION, (int)desc.arg[0]);
335 goto bad_of;
336 }
337
338 data->bus_scale_client_id = msm_bus_scale_register_client(data->pdata);
339 if (!data->bus_scale_client_id) {
340 ret = -EINVAL;
341 goto no_bus_scaling;
342 }
343
344 data->cci_clk = clk_get(&pdev->dev, "cci_clk");
345 if (IS_ERR(data->cci_clk)) {
346 ret = PTR_ERR(data->cci_clk);
347 goto no_clock;
348 }
349
350 data->profile =
351 devm_kzalloc(&pdev->dev, sizeof(*(data->profile)), GFP_KERNEL);
352 if (!data->profile) {
353 ret = -ENOMEM;
354 goto no_profile;
355 }
356 data->profile->target = change_bw;
357 data->profile->get_dev_status = get_dev_status;
358 data->profile->get_cur_freq = get_cur_bw;
359 data->profile->polling_ms = data->window;
360
361 data->devfreq =
362 devfreq_add_device(&pdev->dev, data->profile, "spdm_bw_hyp", data);
363 if (IS_ERR(data->devfreq)) {
364 ret = PTR_ERR(data->devfreq);
365 goto no_spdm_device;
366 }
367
368 spdm_init_debugfs(&pdev->dev);
369 spdm_ipc_log_ctxt = ipc_log_context_create(SPDM_IPC_LOG_PAGES,
370 "devfreq_spdm", 0);
371
372 if (IS_ERR_OR_NULL(spdm_ipc_log_ctxt)) {
373 pr_err("%s: Failed to create IPC log context\n", __func__);
374 spdm_ipc_log_ctxt = NULL;
375 }
376
377
378 return 0;
379
380no_spdm_device:
381 devm_kfree(&pdev->dev, data->profile);
382no_profile:
383no_clock:
384 msm_bus_scale_unregister_client(data->bus_scale_client_id);
385no_bus_scaling:
386 devm_kfree(&pdev->dev, data->config_data.ports);
387bad_of:
388 devm_kfree(&pdev->dev, data);
389 platform_set_drvdata(pdev, NULL);
390 return ret;
391}
392
393static int remove(struct platform_device *pdev)
394{
395 struct spdm_data *data = 0;
396
397 data = platform_get_drvdata(pdev);
398
399 spdm_remove_debugfs(data);
400
401 if (data->devfreq)
402 devfreq_remove_device(data->devfreq);
403
404 if (data->profile)
405 devm_kfree(&pdev->dev, data->profile);
406
407 if (data->bus_scale_client_id)
408 msm_bus_scale_unregister_client(data->bus_scale_client_id);
409
410 if (data->config_data.ports)
411 devm_kfree(&pdev->dev, data->config_data.ports);
412
413 devm_kfree(&pdev->dev, data);
414 platform_set_drvdata(pdev, NULL);
415
416 if (spdm_ipc_log_ctxt)
417 ipc_log_context_destroy(spdm_ipc_log_ctxt);
418
419 return 0;
420}
421
422static const struct of_device_id devfreq_spdm_match[] = {
423 {.compatible = "qcom,devfreq_spdm"},
424 {}
425};
426
427static struct platform_driver devfreq_spdm_drvr = {
428 .driver = {
429 .name = "devfreq_spdm",
430 .owner = THIS_MODULE,
431 .of_match_table = devfreq_spdm_match,
432 },
433 .probe = probe,
434 .remove = remove,
435};
436
437static int __init devfreq_spdm_init(void)
438{
439 return platform_driver_register(&devfreq_spdm_drvr);
440}
441
442module_init(devfreq_spdm_init);
443
444MODULE_LICENSE("GPL v2");