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