aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2015-11-03 05:04:16 -0500
committerMyungJoo Ham <myungjoo.ham@samsung.com>2016-05-02 22:20:05 -0400
commit0722249ac1f3dcc3af9e9d7ed89792a68f066660 (patch)
tree07ae8b17b2bcfdcacd1c14cd2f0b0104e5173fa7
parent04974df8049fc4240d22759a91e035082ccd18b4 (diff)
PM / devfreq: exynos: Add generic exynos bus frequency driver
This patch adds the generic exynos bus frequency driver for AMBA AXI bus of sub-blocks in exynos SoC with DEVFREQ framework. The Samsung Exynos SoC have the common architecture for bus between DRAM and sub-blocks in SoC. This driver can support the generic bus frequency driver for Exynos SoCs. In devicetree, Each bus block has a bus clock, regulator, operation-point and devfreq-event devices which measure the utilization of each bus block. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> [m.reichl and linux.amoon: Tested it on exynos4412-odroidu3 board] Tested-by: Markus Reichl <m.reichl@fivetechno.de> Tested-by: Anand Moon <linux.amoon@gmail.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Acked-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
-rw-r--r--drivers/devfreq/Kconfig15
-rw-r--r--drivers/devfreq/Makefile1
-rw-r--r--drivers/devfreq/exynos-bus.c443
3 files changed, 459 insertions, 0 deletions
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 4de78c552251..cedda8f1e5eb 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -66,6 +66,21 @@ config DEVFREQ_GOV_USERSPACE
66 66
67comment "DEVFREQ Drivers" 67comment "DEVFREQ Drivers"
68 68
69config ARM_EXYNOS_BUS_DEVFREQ
70 bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
71 depends on ARCH_EXYNOS
72 select DEVFREQ_GOV_SIMPLE_ONDEMAND
73 select DEVFREQ_EVENT_EXYNOS_PPMU
74 select PM_DEVFREQ_EVENT
75 select PM_OPP
76 help
77 This adds the common DEVFREQ driver for Exynos Memory bus. Exynos
78 Memory bus has one more group of memory bus (e.g, MIF and INT block).
79 Each memory bus group could contain many memoby bus block. It reads
80 PPMU counters of memory controllers by using DEVFREQ-event device
81 and adjusts the operating frequencies and voltages with OPP support.
82 This does not yet operate with optimal voltages.
83
69config ARM_EXYNOS4_BUS_DEVFREQ 84config ARM_EXYNOS4_BUS_DEVFREQ
70 bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" 85 bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver"
71 depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM 86 depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 5134f9ee983d..8af8aaf922a8 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
6obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o 6obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
7 7
8# DEVFREQ Drivers 8# DEVFREQ Drivers
9obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
9obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ 10obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/
10obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ 11obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/
11obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o 12obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
new file mode 100644
index 000000000000..137de6196af3
--- /dev/null
+++ b/drivers/devfreq/exynos-bus.c
@@ -0,0 +1,443 @@
1/*
2 * Generic Exynos Bus frequency driver with DEVFREQ Framework
3 *
4 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
5 * Author : Chanwoo Choi <cw00.choi@samsung.com>
6 *
7 * This driver support Exynos Bus frequency feature by using
8 * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/clk.h>
16#include <linux/devfreq.h>
17#include <linux/devfreq-event.h>
18#include <linux/device.h>
19#include <linux/export.h>
20#include <linux/module.h>
21#include <linux/of_device.h>
22#include <linux/pm_opp.h>
23#include <linux/platform_device.h>
24#include <linux/regulator/consumer.h>
25#include <linux/slab.h>
26
27#define DEFAULT_SATURATION_RATIO 40
28#define DEFAULT_VOLTAGE_TOLERANCE 2
29
30struct exynos_bus {
31 struct device *dev;
32
33 struct devfreq *devfreq;
34 struct devfreq_event_dev **edev;
35 unsigned int edev_count;
36 struct mutex lock;
37
38 struct dev_pm_opp *curr_opp;
39
40 struct regulator *regulator;
41 struct clk *clk;
42 unsigned int voltage_tolerance;
43 unsigned int ratio;
44};
45
46/*
47 * Control the devfreq-event device to get the current state of bus
48 */
49#define exynos_bus_ops_edev(ops) \
50static int exynos_bus_##ops(struct exynos_bus *bus) \
51{ \
52 int i, ret; \
53 \
54 for (i = 0; i < bus->edev_count; i++) { \
55 if (!bus->edev[i]) \
56 continue; \
57 ret = devfreq_event_##ops(bus->edev[i]); \
58 if (ret < 0) \
59 return ret; \
60 } \
61 \
62 return 0; \
63}
64exynos_bus_ops_edev(enable_edev);
65exynos_bus_ops_edev(disable_edev);
66exynos_bus_ops_edev(set_event);
67
68static int exynos_bus_get_event(struct exynos_bus *bus,
69 struct devfreq_event_data *edata)
70{
71 struct devfreq_event_data event_data;
72 unsigned long load_count = 0, total_count = 0;
73 int i, ret = 0;
74
75 for (i = 0; i < bus->edev_count; i++) {
76 if (!bus->edev[i])
77 continue;
78
79 ret = devfreq_event_get_event(bus->edev[i], &event_data);
80 if (ret < 0)
81 return ret;
82
83 if (i == 0 || event_data.load_count > load_count) {
84 load_count = event_data.load_count;
85 total_count = event_data.total_count;
86 }
87 }
88
89 edata->load_count = load_count;
90 edata->total_count = total_count;
91
92 return ret;
93}
94
95/*
96 * Must necessary function for devfreq governor
97 */
98static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
99{
100 struct exynos_bus *bus = dev_get_drvdata(dev);
101 struct dev_pm_opp *new_opp;
102 unsigned long old_freq, new_freq, old_volt, new_volt, tol;
103 int ret = 0;
104
105 /* Get new opp-bus instance according to new bus clock */
106 rcu_read_lock();
107 new_opp = devfreq_recommended_opp(dev, freq, flags);
108 if (IS_ERR(new_opp)) {
109 dev_err(dev, "failed to get recommended opp instance\n");
110 rcu_read_unlock();
111 return PTR_ERR(new_opp);
112 }
113
114 new_freq = dev_pm_opp_get_freq(new_opp);
115 new_volt = dev_pm_opp_get_voltage(new_opp);
116 old_freq = dev_pm_opp_get_freq(bus->curr_opp);
117 old_volt = dev_pm_opp_get_voltage(bus->curr_opp);
118 rcu_read_unlock();
119
120 if (old_freq == new_freq)
121 return 0;
122 tol = new_volt * bus->voltage_tolerance / 100;
123
124 /* Change voltage and frequency according to new OPP level */
125 mutex_lock(&bus->lock);
126
127 if (old_freq < new_freq) {
128 ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
129 if (ret < 0) {
130 dev_err(bus->dev, "failed to set voltage\n");
131 goto out;
132 }
133 }
134
135 ret = clk_set_rate(bus->clk, new_freq);
136 if (ret < 0) {
137 dev_err(dev, "failed to change clock of bus\n");
138 clk_set_rate(bus->clk, old_freq);
139 goto out;
140 }
141
142 if (old_freq > new_freq) {
143 ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
144 if (ret < 0) {
145 dev_err(bus->dev, "failed to set voltage\n");
146 goto out;
147 }
148 }
149 bus->curr_opp = new_opp;
150
151 dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n",
152 old_freq/1000, new_freq/1000);
153out:
154 mutex_unlock(&bus->lock);
155
156 return ret;
157}
158
159static int exynos_bus_get_dev_status(struct device *dev,
160 struct devfreq_dev_status *stat)
161{
162 struct exynos_bus *bus = dev_get_drvdata(dev);
163 struct devfreq_event_data edata;
164 int ret;
165
166 rcu_read_lock();
167 stat->current_frequency = dev_pm_opp_get_freq(bus->curr_opp);
168 rcu_read_unlock();
169
170 ret = exynos_bus_get_event(bus, &edata);
171 if (ret < 0) {
172 stat->total_time = stat->busy_time = 0;
173 goto err;
174 }
175
176 stat->busy_time = (edata.load_count * 100) / bus->ratio;
177 stat->total_time = edata.total_count;
178
179 dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time,
180 stat->total_time);
181
182err:
183 ret = exynos_bus_set_event(bus);
184 if (ret < 0) {
185 dev_err(dev, "failed to set event to devfreq-event devices\n");
186 return ret;
187 }
188
189 return ret;
190}
191
192static void exynos_bus_exit(struct device *dev)
193{
194 struct exynos_bus *bus = dev_get_drvdata(dev);
195 int ret;
196
197 ret = exynos_bus_disable_edev(bus);
198 if (ret < 0)
199 dev_warn(dev, "failed to disable the devfreq-event devices\n");
200
201 if (bus->regulator)
202 regulator_disable(bus->regulator);
203
204 dev_pm_opp_of_remove_table(dev);
205}
206
207static int exynos_bus_parse_of(struct device_node *np,
208 struct exynos_bus *bus)
209{
210 struct device *dev = bus->dev;
211 unsigned long rate;
212 int i, ret, count, size;
213
214 /* Get the clock to provide each bus with source clock */
215 bus->clk = devm_clk_get(dev, "bus");
216 if (IS_ERR(bus->clk)) {
217 dev_err(dev, "failed to get bus clock\n");
218 return PTR_ERR(bus->clk);
219 }
220
221 ret = clk_prepare_enable(bus->clk);
222 if (ret < 0) {
223 dev_err(dev, "failed to get enable clock\n");
224 return ret;
225 }
226
227 /* Get the freq/voltage OPP table to scale the bus frequency */
228 rcu_read_lock();
229 ret = dev_pm_opp_of_add_table(dev);
230 if (ret < 0) {
231 dev_err(dev, "failed to get OPP table\n");
232 rcu_read_unlock();
233 goto err_clk;
234 }
235
236 rate = clk_get_rate(bus->clk);
237 bus->curr_opp = dev_pm_opp_find_freq_ceil(dev, &rate);
238 if (IS_ERR(bus->curr_opp)) {
239 dev_err(dev, "failed to find dev_pm_opp\n");
240 rcu_read_unlock();
241 ret = PTR_ERR(bus->curr_opp);
242 goto err_opp;
243 }
244 rcu_read_unlock();
245
246 /* Get the regulator to provide each bus with the power */
247 bus->regulator = devm_regulator_get(dev, "vdd");
248 if (IS_ERR(bus->regulator)) {
249 dev_err(dev, "failed to get VDD regulator\n");
250 ret = PTR_ERR(bus->regulator);
251 goto err_opp;
252 }
253
254 ret = regulator_enable(bus->regulator);
255 if (ret < 0) {
256 dev_err(dev, "failed to enable VDD regulator\n");
257 goto err_opp;
258 }
259
260 /*
261 * Get the devfreq-event devices to get the current utilization of
262 * buses. This raw data will be used in devfreq ondemand governor.
263 */
264 count = devfreq_event_get_edev_count(dev);
265 if (count < 0) {
266 dev_err(dev, "failed to get the count of devfreq-event dev\n");
267 ret = count;
268 goto err_regulator;
269 }
270 bus->edev_count = count;
271
272 size = sizeof(*bus->edev) * count;
273 bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
274 if (!bus->edev) {
275 ret = -ENOMEM;
276 goto err_regulator;
277 }
278
279 for (i = 0; i < count; i++) {
280 bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i);
281 if (IS_ERR(bus->edev[i])) {
282 ret = -EPROBE_DEFER;
283 goto err_regulator;
284 }
285 }
286
287 /*
288 * Optionally, Get the saturation ratio according to Exynos SoC
289 * When measuring the utilization of each AXI bus with devfreq-event
290 * devices, the measured real cycle might be much lower than the
291 * total cycle of bus during sampling rate. In result, the devfreq
292 * simple-ondemand governor might not decide to change the current
293 * frequency due to too utilization (= real cycle/total cycle).
294 * So, this property is used to adjust the utilization when calculating
295 * the busy_time in exynos_bus_get_dev_status().
296 */
297 if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
298 bus->ratio = DEFAULT_SATURATION_RATIO;
299
300 if (of_property_read_u32(np, "exynos,voltage-tolerance",
301 &bus->voltage_tolerance))
302 bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;
303
304 return 0;
305
306err_regulator:
307 regulator_disable(bus->regulator);
308err_opp:
309 dev_pm_opp_of_remove_table(dev);
310err_clk:
311 clk_disable_unprepare(bus->clk);
312
313 return ret;
314}
315
316static int exynos_bus_probe(struct platform_device *pdev)
317{
318 struct device *dev = &pdev->dev;
319 struct device_node *np = dev->of_node;
320 struct devfreq_dev_profile *profile;
321 struct devfreq_simple_ondemand_data *ondemand_data;
322 struct exynos_bus *bus;
323 int ret;
324
325 if (!np) {
326 dev_err(dev, "failed to find devicetree node\n");
327 return -EINVAL;
328 }
329
330 bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
331 if (!bus)
332 return -ENOMEM;
333 mutex_init(&bus->lock);
334 bus->dev = &pdev->dev;
335 platform_set_drvdata(pdev, bus);
336
337 /* Parse the device-tree to get the resource information */
338 ret = exynos_bus_parse_of(np, bus);
339 if (ret < 0)
340 return ret;
341
342 /* Initalize the struct profile and governor data */
343 profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
344 if (!profile)
345 return -ENOMEM;
346 profile->polling_ms = 50;
347 profile->target = exynos_bus_target;
348 profile->get_dev_status = exynos_bus_get_dev_status;
349 profile->exit = exynos_bus_exit;
350
351 ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL);
352 if (!ondemand_data)
353 return -ENOMEM;
354 ondemand_data->upthreshold = 40;
355 ondemand_data->downdifferential = 5;
356
357 /* Add devfreq device to monitor and handle the exynos bus */
358 bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand",
359 ondemand_data);
360 if (IS_ERR(bus->devfreq)) {
361 dev_err(dev, "failed to add devfreq device\n");
362 return PTR_ERR(bus->devfreq);
363 }
364
365 /* Register opp_notifier to catch the change of OPP */
366 ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq);
367 if (ret < 0) {
368 dev_err(dev, "failed to register opp notifier\n");
369 return ret;
370 }
371
372 /*
373 * Enable devfreq-event to get raw data which is used to determine
374 * current bus load.
375 */
376 ret = exynos_bus_enable_edev(bus);
377 if (ret < 0) {
378 dev_err(dev, "failed to enable devfreq-event devices\n");
379 return ret;
380 }
381
382 ret = exynos_bus_set_event(bus);
383 if (ret < 0) {
384 dev_err(dev, "failed to set event to devfreq-event devices\n");
385 return ret;
386 }
387
388 return 0;
389}
390
391#ifdef CONFIG_PM_SLEEP
392static int exynos_bus_resume(struct device *dev)
393{
394 struct exynos_bus *bus = dev_get_drvdata(dev);
395 int ret;
396
397 ret = exynos_bus_enable_edev(bus);
398 if (ret < 0) {
399 dev_err(dev, "failed to enable the devfreq-event devices\n");
400 return ret;
401 }
402
403 return 0;
404}
405
406static int exynos_bus_suspend(struct device *dev)
407{
408 struct exynos_bus *bus = dev_get_drvdata(dev);
409 int ret;
410
411 ret = exynos_bus_disable_edev(bus);
412 if (ret < 0) {
413 dev_err(dev, "failed to disable the devfreq-event devices\n");
414 return ret;
415 }
416
417 return 0;
418}
419#endif
420
421static const struct dev_pm_ops exynos_bus_pm = {
422 SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume)
423};
424
425static const struct of_device_id exynos_bus_of_match[] = {
426 { .compatible = "samsung,exynos-bus", },
427 { /* sentinel */ },
428};
429MODULE_DEVICE_TABLE(of, exynos_bus_of_match);
430
431static struct platform_driver exynos_bus_platdrv = {
432 .probe = exynos_bus_probe,
433 .driver = {
434 .name = "exynos-bus",
435 .pm = &exynos_bus_pm,
436 .of_match_table = of_match_ptr(exynos_bus_of_match),
437 },
438};
439module_platform_driver(exynos_bus_platdrv);
440
441MODULE_DESCRIPTION("Generic Exynos Bus frequency driver");
442MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
443MODULE_LICENSE("GPL v2");