1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Generic Exynos Bus frequency driver with DEVFREQ Framework |
4 | * |
5 | * Copyright (c) 2016 Samsung Electronics Co., Ltd. |
6 | * Author : Chanwoo Choi <cw00.choi@samsung.com> |
7 | * |
8 | * This driver support Exynos Bus frequency feature by using |
9 | * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c. |
10 | */ |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/devfreq.h> |
14 | #include <linux/devfreq-event.h> |
15 | #include <linux/device.h> |
16 | #include <linux/export.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | #include <linux/pm_opp.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/regulator/consumer.h> |
22 | |
23 | #define DEFAULT_SATURATION_RATIO 40 |
24 | |
25 | struct exynos_bus { |
26 | struct device *dev; |
27 | struct platform_device *icc_pdev; |
28 | |
29 | struct devfreq *devfreq; |
30 | struct devfreq_event_dev **edev; |
31 | unsigned int edev_count; |
32 | struct mutex lock; |
33 | |
34 | unsigned long curr_freq; |
35 | |
36 | int opp_token; |
37 | struct clk *clk; |
38 | unsigned int ratio; |
39 | }; |
40 | |
41 | /* |
42 | * Control the devfreq-event device to get the current state of bus |
43 | */ |
44 | #define exynos_bus_ops_edev(ops) \ |
45 | static int exynos_bus_##ops(struct exynos_bus *bus) \ |
46 | { \ |
47 | int i, ret; \ |
48 | \ |
49 | for (i = 0; i < bus->edev_count; i++) { \ |
50 | if (!bus->edev[i]) \ |
51 | continue; \ |
52 | ret = devfreq_event_##ops(bus->edev[i]); \ |
53 | if (ret < 0) \ |
54 | return ret; \ |
55 | } \ |
56 | \ |
57 | return 0; \ |
58 | } |
59 | exynos_bus_ops_edev(enable_edev); |
60 | exynos_bus_ops_edev(disable_edev); |
61 | exynos_bus_ops_edev(set_event); |
62 | |
63 | static int exynos_bus_get_event(struct exynos_bus *bus, |
64 | struct devfreq_event_data *edata) |
65 | { |
66 | struct devfreq_event_data event_data; |
67 | unsigned long load_count = 0, total_count = 0; |
68 | int i, ret = 0; |
69 | |
70 | for (i = 0; i < bus->edev_count; i++) { |
71 | if (!bus->edev[i]) |
72 | continue; |
73 | |
74 | ret = devfreq_event_get_event(edev: bus->edev[i], edata: &event_data); |
75 | if (ret < 0) |
76 | return ret; |
77 | |
78 | if (i == 0 || event_data.load_count > load_count) { |
79 | load_count = event_data.load_count; |
80 | total_count = event_data.total_count; |
81 | } |
82 | } |
83 | |
84 | edata->load_count = load_count; |
85 | edata->total_count = total_count; |
86 | |
87 | return ret; |
88 | } |
89 | |
90 | /* |
91 | * devfreq function for both simple-ondemand and passive governor |
92 | */ |
93 | static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) |
94 | { |
95 | struct exynos_bus *bus = dev_get_drvdata(dev); |
96 | struct dev_pm_opp *new_opp; |
97 | int ret = 0; |
98 | |
99 | /* Get correct frequency for bus. */ |
100 | new_opp = devfreq_recommended_opp(dev, freq, flags); |
101 | if (IS_ERR(ptr: new_opp)) { |
102 | dev_err(dev, "failed to get recommended opp instance\n" ); |
103 | return PTR_ERR(ptr: new_opp); |
104 | } |
105 | |
106 | dev_pm_opp_put(opp: new_opp); |
107 | |
108 | /* Change voltage and frequency according to new OPP level */ |
109 | mutex_lock(&bus->lock); |
110 | ret = dev_pm_opp_set_rate(dev, target_freq: *freq); |
111 | if (!ret) |
112 | bus->curr_freq = *freq; |
113 | |
114 | mutex_unlock(lock: &bus->lock); |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static int exynos_bus_get_dev_status(struct device *dev, |
120 | struct devfreq_dev_status *stat) |
121 | { |
122 | struct exynos_bus *bus = dev_get_drvdata(dev); |
123 | struct devfreq_event_data edata; |
124 | int ret; |
125 | |
126 | stat->current_frequency = bus->curr_freq; |
127 | |
128 | ret = exynos_bus_get_event(bus, edata: &edata); |
129 | if (ret < 0) { |
130 | dev_err(dev, "failed to get event from devfreq-event devices\n" ); |
131 | stat->total_time = stat->busy_time = 0; |
132 | goto err; |
133 | } |
134 | |
135 | stat->busy_time = (edata.load_count * 100) / bus->ratio; |
136 | stat->total_time = edata.total_count; |
137 | |
138 | dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n" , stat->busy_time, |
139 | stat->total_time); |
140 | |
141 | err: |
142 | ret = exynos_bus_set_event(bus); |
143 | if (ret < 0) { |
144 | dev_err(dev, "failed to set event to devfreq-event devices\n" ); |
145 | return ret; |
146 | } |
147 | |
148 | return ret; |
149 | } |
150 | |
151 | static void exynos_bus_exit(struct device *dev) |
152 | { |
153 | struct exynos_bus *bus = dev_get_drvdata(dev); |
154 | int ret; |
155 | |
156 | ret = exynos_bus_disable_edev(bus); |
157 | if (ret < 0) |
158 | dev_warn(dev, "failed to disable the devfreq-event devices\n" ); |
159 | |
160 | platform_device_unregister(bus->icc_pdev); |
161 | |
162 | dev_pm_opp_of_remove_table(dev); |
163 | clk_disable_unprepare(clk: bus->clk); |
164 | dev_pm_opp_put_regulators(token: bus->opp_token); |
165 | } |
166 | |
167 | static void exynos_bus_passive_exit(struct device *dev) |
168 | { |
169 | struct exynos_bus *bus = dev_get_drvdata(dev); |
170 | |
171 | platform_device_unregister(bus->icc_pdev); |
172 | |
173 | dev_pm_opp_of_remove_table(dev); |
174 | clk_disable_unprepare(clk: bus->clk); |
175 | } |
176 | |
177 | static int exynos_bus_parent_parse_of(struct device_node *np, |
178 | struct exynos_bus *bus) |
179 | { |
180 | struct device *dev = bus->dev; |
181 | const char *supplies[] = { "vdd" , NULL }; |
182 | int i, ret, count, size; |
183 | |
184 | ret = dev_pm_opp_set_regulators(dev, names: supplies); |
185 | if (ret < 0) { |
186 | dev_err(dev, "failed to set regulators %d\n" , ret); |
187 | return ret; |
188 | } |
189 | |
190 | bus->opp_token = ret; |
191 | |
192 | /* |
193 | * Get the devfreq-event devices to get the current utilization of |
194 | * buses. This raw data will be used in devfreq ondemand governor. |
195 | */ |
196 | count = devfreq_event_get_edev_count(dev, phandle_name: "devfreq-events" ); |
197 | if (count < 0) { |
198 | dev_err(dev, "failed to get the count of devfreq-event dev\n" ); |
199 | ret = count; |
200 | goto err_regulator; |
201 | } |
202 | bus->edev_count = count; |
203 | |
204 | size = sizeof(*bus->edev) * count; |
205 | bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); |
206 | if (!bus->edev) { |
207 | ret = -ENOMEM; |
208 | goto err_regulator; |
209 | } |
210 | |
211 | for (i = 0; i < count; i++) { |
212 | bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, |
213 | phandle_name: "devfreq-events" , index: i); |
214 | if (IS_ERR(ptr: bus->edev[i])) { |
215 | ret = -EPROBE_DEFER; |
216 | goto err_regulator; |
217 | } |
218 | } |
219 | |
220 | /* |
221 | * Optionally, Get the saturation ratio according to Exynos SoC |
222 | * When measuring the utilization of each AXI bus with devfreq-event |
223 | * devices, the measured real cycle might be much lower than the |
224 | * total cycle of bus during sampling rate. In result, the devfreq |
225 | * simple-ondemand governor might not decide to change the current |
226 | * frequency due to too utilization (= real cycle/total cycle). |
227 | * So, this property is used to adjust the utilization when calculating |
228 | * the busy_time in exynos_bus_get_dev_status(). |
229 | */ |
230 | if (of_property_read_u32(np, propname: "exynos,saturation-ratio" , out_value: &bus->ratio)) |
231 | bus->ratio = DEFAULT_SATURATION_RATIO; |
232 | |
233 | return 0; |
234 | |
235 | err_regulator: |
236 | dev_pm_opp_put_regulators(token: bus->opp_token); |
237 | |
238 | return ret; |
239 | } |
240 | |
241 | static int exynos_bus_parse_of(struct device_node *np, |
242 | struct exynos_bus *bus) |
243 | { |
244 | struct device *dev = bus->dev; |
245 | struct dev_pm_opp *opp; |
246 | unsigned long rate; |
247 | int ret; |
248 | |
249 | /* Get the clock to provide each bus with source clock */ |
250 | bus->clk = devm_clk_get(dev, id: "bus" ); |
251 | if (IS_ERR(ptr: bus->clk)) { |
252 | dev_err(dev, "failed to get bus clock\n" ); |
253 | return PTR_ERR(ptr: bus->clk); |
254 | } |
255 | |
256 | ret = clk_prepare_enable(clk: bus->clk); |
257 | if (ret < 0) { |
258 | dev_err(dev, "failed to get enable clock\n" ); |
259 | return ret; |
260 | } |
261 | |
262 | /* Get the freq and voltage from OPP table to scale the bus freq */ |
263 | ret = dev_pm_opp_of_add_table(dev); |
264 | if (ret < 0) { |
265 | dev_err(dev, "failed to get OPP table\n" ); |
266 | goto err_clk; |
267 | } |
268 | |
269 | rate = clk_get_rate(clk: bus->clk); |
270 | |
271 | opp = devfreq_recommended_opp(dev, freq: &rate, flags: 0); |
272 | if (IS_ERR(ptr: opp)) { |
273 | dev_err(dev, "failed to find dev_pm_opp\n" ); |
274 | ret = PTR_ERR(ptr: opp); |
275 | goto err_opp; |
276 | } |
277 | bus->curr_freq = dev_pm_opp_get_freq(opp); |
278 | dev_pm_opp_put(opp); |
279 | |
280 | return 0; |
281 | |
282 | err_opp: |
283 | dev_pm_opp_of_remove_table(dev); |
284 | err_clk: |
285 | clk_disable_unprepare(clk: bus->clk); |
286 | |
287 | return ret; |
288 | } |
289 | |
290 | static int exynos_bus_profile_init(struct exynos_bus *bus, |
291 | struct devfreq_dev_profile *profile) |
292 | { |
293 | struct device *dev = bus->dev; |
294 | struct devfreq_simple_ondemand_data *ondemand_data; |
295 | int ret; |
296 | |
297 | /* Initialize the struct profile and governor data for parent device */ |
298 | profile->polling_ms = 50; |
299 | profile->target = exynos_bus_target; |
300 | profile->get_dev_status = exynos_bus_get_dev_status; |
301 | profile->exit = exynos_bus_exit; |
302 | |
303 | ondemand_data = devm_kzalloc(dev, size: sizeof(*ondemand_data), GFP_KERNEL); |
304 | if (!ondemand_data) |
305 | return -ENOMEM; |
306 | |
307 | ondemand_data->upthreshold = 40; |
308 | ondemand_data->downdifferential = 5; |
309 | |
310 | /* Add devfreq device to monitor and handle the exynos bus */ |
311 | bus->devfreq = devm_devfreq_add_device(dev, profile, |
312 | DEVFREQ_GOV_SIMPLE_ONDEMAND, |
313 | data: ondemand_data); |
314 | if (IS_ERR(ptr: bus->devfreq)) { |
315 | dev_err(dev, "failed to add devfreq device\n" ); |
316 | return PTR_ERR(ptr: bus->devfreq); |
317 | } |
318 | |
319 | /* Register opp_notifier to catch the change of OPP */ |
320 | ret = devm_devfreq_register_opp_notifier(dev, devfreq: bus->devfreq); |
321 | if (ret < 0) { |
322 | dev_err(dev, "failed to register opp notifier\n" ); |
323 | return ret; |
324 | } |
325 | |
326 | /* |
327 | * Enable devfreq-event to get raw data which is used to determine |
328 | * current bus load. |
329 | */ |
330 | ret = exynos_bus_enable_edev(bus); |
331 | if (ret < 0) { |
332 | dev_err(dev, "failed to enable devfreq-event devices\n" ); |
333 | return ret; |
334 | } |
335 | |
336 | ret = exynos_bus_set_event(bus); |
337 | if (ret < 0) { |
338 | dev_err(dev, "failed to set event to devfreq-event devices\n" ); |
339 | goto err_edev; |
340 | } |
341 | |
342 | return 0; |
343 | |
344 | err_edev: |
345 | if (exynos_bus_disable_edev(bus)) |
346 | dev_warn(dev, "failed to disable the devfreq-event devices\n" ); |
347 | |
348 | return ret; |
349 | } |
350 | |
351 | static int exynos_bus_profile_init_passive(struct exynos_bus *bus, |
352 | struct devfreq_dev_profile *profile) |
353 | { |
354 | struct device *dev = bus->dev; |
355 | struct devfreq_passive_data *passive_data; |
356 | struct devfreq *parent_devfreq; |
357 | |
358 | /* Initialize the struct profile and governor data for passive device */ |
359 | profile->target = exynos_bus_target; |
360 | profile->exit = exynos_bus_passive_exit; |
361 | |
362 | /* Get the instance of parent devfreq device */ |
363 | parent_devfreq = devfreq_get_devfreq_by_phandle(dev, phandle_name: "devfreq" , index: 0); |
364 | if (IS_ERR(ptr: parent_devfreq)) |
365 | return -EPROBE_DEFER; |
366 | |
367 | passive_data = devm_kzalloc(dev, size: sizeof(*passive_data), GFP_KERNEL); |
368 | if (!passive_data) |
369 | return -ENOMEM; |
370 | |
371 | passive_data->parent = parent_devfreq; |
372 | |
373 | /* Add devfreq device for exynos bus with passive governor */ |
374 | bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE, |
375 | data: passive_data); |
376 | if (IS_ERR(ptr: bus->devfreq)) { |
377 | dev_err(dev, |
378 | "failed to add devfreq dev with passive governor\n" ); |
379 | return PTR_ERR(ptr: bus->devfreq); |
380 | } |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static int exynos_bus_probe(struct platform_device *pdev) |
386 | { |
387 | struct device *dev = &pdev->dev; |
388 | struct device_node *np = dev->of_node, *node; |
389 | struct devfreq_dev_profile *profile; |
390 | struct exynos_bus *bus; |
391 | int ret, max_state; |
392 | unsigned long min_freq, max_freq; |
393 | bool passive = false; |
394 | |
395 | if (!np) { |
396 | dev_err(dev, "failed to find devicetree node\n" ); |
397 | return -EINVAL; |
398 | } |
399 | |
400 | bus = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bus), GFP_KERNEL); |
401 | if (!bus) |
402 | return -ENOMEM; |
403 | mutex_init(&bus->lock); |
404 | bus->dev = &pdev->dev; |
405 | platform_set_drvdata(pdev, data: bus); |
406 | |
407 | profile = devm_kzalloc(dev, size: sizeof(*profile), GFP_KERNEL); |
408 | if (!profile) |
409 | return -ENOMEM; |
410 | |
411 | node = of_parse_phandle(np: dev->of_node, phandle_name: "devfreq" , index: 0); |
412 | if (node) { |
413 | of_node_put(node); |
414 | passive = true; |
415 | } else { |
416 | ret = exynos_bus_parent_parse_of(np, bus); |
417 | if (ret < 0) |
418 | return ret; |
419 | } |
420 | |
421 | /* Parse the device-tree to get the resource information */ |
422 | ret = exynos_bus_parse_of(np, bus); |
423 | if (ret < 0) |
424 | goto err_reg; |
425 | |
426 | if (passive) |
427 | ret = exynos_bus_profile_init_passive(bus, profile); |
428 | else |
429 | ret = exynos_bus_profile_init(bus, profile); |
430 | |
431 | if (ret < 0) |
432 | goto err; |
433 | |
434 | /* Create child platform device for the interconnect provider */ |
435 | if (of_property_present(np: dev->of_node, propname: "#interconnect-cells" )) { |
436 | bus->icc_pdev = platform_device_register_data( |
437 | parent: dev, name: "exynos-generic-icc" , |
438 | PLATFORM_DEVID_AUTO, NULL, size: 0); |
439 | |
440 | if (IS_ERR(ptr: bus->icc_pdev)) { |
441 | ret = PTR_ERR(ptr: bus->icc_pdev); |
442 | goto err; |
443 | } |
444 | } |
445 | |
446 | max_state = bus->devfreq->max_state; |
447 | min_freq = (bus->devfreq->freq_table[0] / 1000); |
448 | max_freq = (bus->devfreq->freq_table[max_state - 1] / 1000); |
449 | pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n" , |
450 | dev_name(dev), min_freq, max_freq); |
451 | |
452 | return 0; |
453 | |
454 | err: |
455 | dev_pm_opp_of_remove_table(dev); |
456 | clk_disable_unprepare(clk: bus->clk); |
457 | err_reg: |
458 | dev_pm_opp_put_regulators(token: bus->opp_token); |
459 | |
460 | return ret; |
461 | } |
462 | |
463 | static void exynos_bus_shutdown(struct platform_device *pdev) |
464 | { |
465 | struct exynos_bus *bus = dev_get_drvdata(dev: &pdev->dev); |
466 | |
467 | devfreq_suspend_device(devfreq: bus->devfreq); |
468 | } |
469 | |
470 | #ifdef CONFIG_PM_SLEEP |
471 | static int exynos_bus_resume(struct device *dev) |
472 | { |
473 | struct exynos_bus *bus = dev_get_drvdata(dev); |
474 | int ret; |
475 | |
476 | ret = exynos_bus_enable_edev(bus); |
477 | if (ret < 0) { |
478 | dev_err(dev, "failed to enable the devfreq-event devices\n" ); |
479 | return ret; |
480 | } |
481 | |
482 | return 0; |
483 | } |
484 | |
485 | static int exynos_bus_suspend(struct device *dev) |
486 | { |
487 | struct exynos_bus *bus = dev_get_drvdata(dev); |
488 | int ret; |
489 | |
490 | ret = exynos_bus_disable_edev(bus); |
491 | if (ret < 0) { |
492 | dev_err(dev, "failed to disable the devfreq-event devices\n" ); |
493 | return ret; |
494 | } |
495 | |
496 | return 0; |
497 | } |
498 | #endif |
499 | |
500 | static const struct dev_pm_ops exynos_bus_pm = { |
501 | SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) |
502 | }; |
503 | |
504 | static const struct of_device_id exynos_bus_of_match[] = { |
505 | { .compatible = "samsung,exynos-bus" , }, |
506 | { /* sentinel */ }, |
507 | }; |
508 | MODULE_DEVICE_TABLE(of, exynos_bus_of_match); |
509 | |
510 | static struct platform_driver exynos_bus_platdrv = { |
511 | .probe = exynos_bus_probe, |
512 | .shutdown = exynos_bus_shutdown, |
513 | .driver = { |
514 | .name = "exynos-bus" , |
515 | .pm = &exynos_bus_pm, |
516 | .of_match_table = exynos_bus_of_match, |
517 | }, |
518 | }; |
519 | module_platform_driver(exynos_bus_platdrv); |
520 | |
521 | MODULE_SOFTDEP("pre: exynos_ppmu" ); |
522 | MODULE_DESCRIPTION("Generic Exynos Bus frequency driver" ); |
523 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>" ); |
524 | MODULE_LICENSE("GPL v2" ); |
525 | |