1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20722249aSChanwoo Choi /*
30722249aSChanwoo Choi * Generic Exynos Bus frequency driver with DEVFREQ Framework
40722249aSChanwoo Choi *
5403e0689SChanwoo Choi * Copyright (c) 2016 Samsung Electronics Co., Ltd.
60722249aSChanwoo Choi * Author : Chanwoo Choi <cw00.choi@samsung.com>
70722249aSChanwoo Choi *
80722249aSChanwoo Choi * This driver support Exynos Bus frequency feature by using
90722249aSChanwoo Choi * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c.
100722249aSChanwoo Choi */
110722249aSChanwoo Choi
120722249aSChanwoo Choi #include <linux/clk.h>
130722249aSChanwoo Choi #include <linux/devfreq.h>
140722249aSChanwoo Choi #include <linux/devfreq-event.h>
150722249aSChanwoo Choi #include <linux/device.h>
160722249aSChanwoo Choi #include <linux/export.h>
170722249aSChanwoo Choi #include <linux/module.h>
18a4408921SArtur Świgoń #include <linux/of.h>
190722249aSChanwoo Choi #include <linux/pm_opp.h>
200722249aSChanwoo Choi #include <linux/platform_device.h>
210722249aSChanwoo Choi #include <linux/regulator/consumer.h>
220722249aSChanwoo Choi
230722249aSChanwoo Choi #define DEFAULT_SATURATION_RATIO 40
240722249aSChanwoo Choi
250722249aSChanwoo Choi struct exynos_bus {
260722249aSChanwoo Choi struct device *dev;
27404d59c5SSylwester Nawrocki struct platform_device *icc_pdev;
280722249aSChanwoo Choi
290722249aSChanwoo Choi struct devfreq *devfreq;
300722249aSChanwoo Choi struct devfreq_event_dev **edev;
310722249aSChanwoo Choi unsigned int edev_count;
320722249aSChanwoo Choi struct mutex lock;
330722249aSChanwoo Choi
34c8ce82b9SViresh Kumar unsigned long curr_freq;
350722249aSChanwoo Choi
36b0ec0942SViresh Kumar int opp_token;
370722249aSChanwoo Choi struct clk *clk;
380722249aSChanwoo Choi unsigned int ratio;
390722249aSChanwoo Choi };
400722249aSChanwoo Choi
410722249aSChanwoo Choi /*
420722249aSChanwoo Choi * Control the devfreq-event device to get the current state of bus
430722249aSChanwoo Choi */
440722249aSChanwoo Choi #define exynos_bus_ops_edev(ops) \
450722249aSChanwoo Choi static int exynos_bus_##ops(struct exynos_bus *bus) \
460722249aSChanwoo Choi { \
470722249aSChanwoo Choi int i, ret; \
480722249aSChanwoo Choi \
490722249aSChanwoo Choi for (i = 0; i < bus->edev_count; i++) { \
500722249aSChanwoo Choi if (!bus->edev[i]) \
510722249aSChanwoo Choi continue; \
520722249aSChanwoo Choi ret = devfreq_event_##ops(bus->edev[i]); \
530722249aSChanwoo Choi if (ret < 0) \
540722249aSChanwoo Choi return ret; \
550722249aSChanwoo Choi } \
560722249aSChanwoo Choi \
570722249aSChanwoo Choi return 0; \
580722249aSChanwoo Choi }
590722249aSChanwoo Choi exynos_bus_ops_edev(enable_edev);
600722249aSChanwoo Choi exynos_bus_ops_edev(disable_edev);
610722249aSChanwoo Choi exynos_bus_ops_edev(set_event);
620722249aSChanwoo Choi
exynos_bus_get_event(struct exynos_bus * bus,struct devfreq_event_data * edata)630722249aSChanwoo Choi static int exynos_bus_get_event(struct exynos_bus *bus,
640722249aSChanwoo Choi struct devfreq_event_data *edata)
650722249aSChanwoo Choi {
660722249aSChanwoo Choi struct devfreq_event_data event_data;
670722249aSChanwoo Choi unsigned long load_count = 0, total_count = 0;
680722249aSChanwoo Choi int i, ret = 0;
690722249aSChanwoo Choi
700722249aSChanwoo Choi for (i = 0; i < bus->edev_count; i++) {
710722249aSChanwoo Choi if (!bus->edev[i])
720722249aSChanwoo Choi continue;
730722249aSChanwoo Choi
740722249aSChanwoo Choi ret = devfreq_event_get_event(bus->edev[i], &event_data);
750722249aSChanwoo Choi if (ret < 0)
760722249aSChanwoo Choi return ret;
770722249aSChanwoo Choi
780722249aSChanwoo Choi if (i == 0 || event_data.load_count > load_count) {
790722249aSChanwoo Choi load_count = event_data.load_count;
800722249aSChanwoo Choi total_count = event_data.total_count;
810722249aSChanwoo Choi }
820722249aSChanwoo Choi }
830722249aSChanwoo Choi
840722249aSChanwoo Choi edata->load_count = load_count;
850722249aSChanwoo Choi edata->total_count = total_count;
860722249aSChanwoo Choi
870722249aSChanwoo Choi return ret;
880722249aSChanwoo Choi }
890722249aSChanwoo Choi
900722249aSChanwoo Choi /*
914294a779SKamil Konieczny * devfreq function for both simple-ondemand and passive governor
920722249aSChanwoo Choi */
exynos_bus_target(struct device * dev,unsigned long * freq,u32 flags)930722249aSChanwoo Choi static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
940722249aSChanwoo Choi {
950722249aSChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
960722249aSChanwoo Choi struct dev_pm_opp *new_opp;
970722249aSChanwoo Choi int ret = 0;
980722249aSChanwoo Choi
994294a779SKamil Konieczny /* Get correct frequency for bus. */
1000722249aSChanwoo Choi new_opp = devfreq_recommended_opp(dev, freq, flags);
1010722249aSChanwoo Choi if (IS_ERR(new_opp)) {
1020722249aSChanwoo Choi dev_err(dev, "failed to get recommended opp instance\n");
1030722249aSChanwoo Choi return PTR_ERR(new_opp);
1040722249aSChanwoo Choi }
1050722249aSChanwoo Choi
1068a31d9d9SViresh Kumar dev_pm_opp_put(new_opp);
1078a31d9d9SViresh Kumar
1080722249aSChanwoo Choi /* Change voltage and frequency according to new OPP level */
1090722249aSChanwoo Choi mutex_lock(&bus->lock);
1104294a779SKamil Konieczny ret = dev_pm_opp_set_rate(dev, *freq);
1114294a779SKamil Konieczny if (!ret)
1124294a779SKamil Konieczny bus->curr_freq = *freq;
1130722249aSChanwoo Choi
1140722249aSChanwoo Choi mutex_unlock(&bus->lock);
1150722249aSChanwoo Choi
1160722249aSChanwoo Choi return ret;
1170722249aSChanwoo Choi }
1180722249aSChanwoo Choi
exynos_bus_get_dev_status(struct device * dev,struct devfreq_dev_status * stat)1190722249aSChanwoo Choi static int exynos_bus_get_dev_status(struct device *dev,
1200722249aSChanwoo Choi struct devfreq_dev_status *stat)
1210722249aSChanwoo Choi {
1220722249aSChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
1230722249aSChanwoo Choi struct devfreq_event_data edata;
1240722249aSChanwoo Choi int ret;
1250722249aSChanwoo Choi
126c8ce82b9SViresh Kumar stat->current_frequency = bus->curr_freq;
1270722249aSChanwoo Choi
1280722249aSChanwoo Choi ret = exynos_bus_get_event(bus, &edata);
1290722249aSChanwoo Choi if (ret < 0) {
13028135762SYangtao Li dev_err(dev, "failed to get event from devfreq-event devices\n");
1310722249aSChanwoo Choi stat->total_time = stat->busy_time = 0;
1320722249aSChanwoo Choi goto err;
1330722249aSChanwoo Choi }
1340722249aSChanwoo Choi
1350722249aSChanwoo Choi stat->busy_time = (edata.load_count * 100) / bus->ratio;
1360722249aSChanwoo Choi stat->total_time = edata.total_count;
1370722249aSChanwoo Choi
1380722249aSChanwoo Choi dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time,
1390722249aSChanwoo Choi stat->total_time);
1400722249aSChanwoo Choi
1410722249aSChanwoo Choi err:
1420722249aSChanwoo Choi ret = exynos_bus_set_event(bus);
1430722249aSChanwoo Choi if (ret < 0) {
1440722249aSChanwoo Choi dev_err(dev, "failed to set event to devfreq-event devices\n");
1450722249aSChanwoo Choi return ret;
1460722249aSChanwoo Choi }
1470722249aSChanwoo Choi
1480722249aSChanwoo Choi return ret;
1490722249aSChanwoo Choi }
1500722249aSChanwoo Choi
exynos_bus_exit(struct device * dev)1510722249aSChanwoo Choi static void exynos_bus_exit(struct device *dev)
1520722249aSChanwoo Choi {
1530722249aSChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
1540722249aSChanwoo Choi int ret;
1550722249aSChanwoo Choi
1560722249aSChanwoo Choi ret = exynos_bus_disable_edev(bus);
1570722249aSChanwoo Choi if (ret < 0)
1580722249aSChanwoo Choi dev_warn(dev, "failed to disable the devfreq-event devices\n");
1590722249aSChanwoo Choi
160404d59c5SSylwester Nawrocki platform_device_unregister(bus->icc_pdev);
161404d59c5SSylwester Nawrocki
1620722249aSChanwoo Choi dev_pm_opp_of_remove_table(dev);
163403e0689SChanwoo Choi clk_disable_unprepare(bus->clk);
164b0ec0942SViresh Kumar dev_pm_opp_put_regulators(bus->opp_token);
1650722249aSChanwoo Choi }
1660722249aSChanwoo Choi
exynos_bus_passive_exit(struct device * dev)167403e0689SChanwoo Choi static void exynos_bus_passive_exit(struct device *dev)
168403e0689SChanwoo Choi {
169403e0689SChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
170403e0689SChanwoo Choi
171404d59c5SSylwester Nawrocki platform_device_unregister(bus->icc_pdev);
172404d59c5SSylwester Nawrocki
173403e0689SChanwoo Choi dev_pm_opp_of_remove_table(dev);
174403e0689SChanwoo Choi clk_disable_unprepare(bus->clk);
1750722249aSChanwoo Choi }
1760722249aSChanwoo Choi
exynos_bus_parent_parse_of(struct device_node * np,struct exynos_bus * bus)177403e0689SChanwoo Choi static int exynos_bus_parent_parse_of(struct device_node *np,
178403e0689SChanwoo Choi struct exynos_bus *bus)
179403e0689SChanwoo Choi {
180403e0689SChanwoo Choi struct device *dev = bus->dev;
18187686cc8SViresh Kumar const char *supplies[] = { "vdd", NULL };
182403e0689SChanwoo Choi int i, ret, count, size;
1830722249aSChanwoo Choi
184b0ec0942SViresh Kumar ret = dev_pm_opp_set_regulators(dev, supplies);
185b0ec0942SViresh Kumar if (ret < 0) {
1864294a779SKamil Konieczny dev_err(dev, "failed to set regulators %d\n", ret);
187403e0689SChanwoo Choi return ret;
1880722249aSChanwoo Choi }
1890722249aSChanwoo Choi
190b0ec0942SViresh Kumar bus->opp_token = ret;
1914294a779SKamil Konieczny
1920722249aSChanwoo Choi /*
1930722249aSChanwoo Choi * Get the devfreq-event devices to get the current utilization of
1940722249aSChanwoo Choi * buses. This raw data will be used in devfreq ondemand governor.
1950722249aSChanwoo Choi */
19602bdbf7dSChanwoo Choi count = devfreq_event_get_edev_count(dev, "devfreq-events");
1970722249aSChanwoo Choi if (count < 0) {
1980722249aSChanwoo Choi dev_err(dev, "failed to get the count of devfreq-event dev\n");
1990722249aSChanwoo Choi ret = count;
2000722249aSChanwoo Choi goto err_regulator;
2010722249aSChanwoo Choi }
2020722249aSChanwoo Choi bus->edev_count = count;
2030722249aSChanwoo Choi
2040722249aSChanwoo Choi size = sizeof(*bus->edev) * count;
2050722249aSChanwoo Choi bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
2060722249aSChanwoo Choi if (!bus->edev) {
2070722249aSChanwoo Choi ret = -ENOMEM;
2080722249aSChanwoo Choi goto err_regulator;
2090722249aSChanwoo Choi }
2100722249aSChanwoo Choi
2110722249aSChanwoo Choi for (i = 0; i < count; i++) {
21202bdbf7dSChanwoo Choi bus->edev[i] = devfreq_event_get_edev_by_phandle(dev,
21302bdbf7dSChanwoo Choi "devfreq-events", i);
2140722249aSChanwoo Choi if (IS_ERR(bus->edev[i])) {
2150722249aSChanwoo Choi ret = -EPROBE_DEFER;
2160722249aSChanwoo Choi goto err_regulator;
2170722249aSChanwoo Choi }
2180722249aSChanwoo Choi }
2190722249aSChanwoo Choi
2200722249aSChanwoo Choi /*
2210722249aSChanwoo Choi * Optionally, Get the saturation ratio according to Exynos SoC
2220722249aSChanwoo Choi * When measuring the utilization of each AXI bus with devfreq-event
2230722249aSChanwoo Choi * devices, the measured real cycle might be much lower than the
2240722249aSChanwoo Choi * total cycle of bus during sampling rate. In result, the devfreq
2250722249aSChanwoo Choi * simple-ondemand governor might not decide to change the current
2260722249aSChanwoo Choi * frequency due to too utilization (= real cycle/total cycle).
2270722249aSChanwoo Choi * So, this property is used to adjust the utilization when calculating
2280722249aSChanwoo Choi * the busy_time in exynos_bus_get_dev_status().
2290722249aSChanwoo Choi */
2300722249aSChanwoo Choi if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
2310722249aSChanwoo Choi bus->ratio = DEFAULT_SATURATION_RATIO;
2320722249aSChanwoo Choi
2330722249aSChanwoo Choi return 0;
2340722249aSChanwoo Choi
2350722249aSChanwoo Choi err_regulator:
236b0ec0942SViresh Kumar dev_pm_opp_put_regulators(bus->opp_token);
237403e0689SChanwoo Choi
238403e0689SChanwoo Choi return ret;
239403e0689SChanwoo Choi }
240403e0689SChanwoo Choi
exynos_bus_parse_of(struct device_node * np,struct exynos_bus * bus)241403e0689SChanwoo Choi static int exynos_bus_parse_of(struct device_node *np,
242403e0689SChanwoo Choi struct exynos_bus *bus)
243403e0689SChanwoo Choi {
244403e0689SChanwoo Choi struct device *dev = bus->dev;
245c8ce82b9SViresh Kumar struct dev_pm_opp *opp;
246403e0689SChanwoo Choi unsigned long rate;
247403e0689SChanwoo Choi int ret;
248403e0689SChanwoo Choi
249403e0689SChanwoo Choi /* Get the clock to provide each bus with source clock */
250403e0689SChanwoo Choi bus->clk = devm_clk_get(dev, "bus");
251403e0689SChanwoo Choi if (IS_ERR(bus->clk)) {
252403e0689SChanwoo Choi dev_err(dev, "failed to get bus clock\n");
253403e0689SChanwoo Choi return PTR_ERR(bus->clk);
254403e0689SChanwoo Choi }
255403e0689SChanwoo Choi
256403e0689SChanwoo Choi ret = clk_prepare_enable(bus->clk);
257403e0689SChanwoo Choi if (ret < 0) {
258403e0689SChanwoo Choi dev_err(dev, "failed to get enable clock\n");
259403e0689SChanwoo Choi return ret;
260403e0689SChanwoo Choi }
261403e0689SChanwoo Choi
262403e0689SChanwoo Choi /* Get the freq and voltage from OPP table to scale the bus freq */
263403e0689SChanwoo Choi ret = dev_pm_opp_of_add_table(dev);
264403e0689SChanwoo Choi if (ret < 0) {
265403e0689SChanwoo Choi dev_err(dev, "failed to get OPP table\n");
266403e0689SChanwoo Choi goto err_clk;
267403e0689SChanwoo Choi }
268403e0689SChanwoo Choi
269403e0689SChanwoo Choi rate = clk_get_rate(bus->clk);
270c8ce82b9SViresh Kumar
271c8ce82b9SViresh Kumar opp = devfreq_recommended_opp(dev, &rate, 0);
272c8ce82b9SViresh Kumar if (IS_ERR(opp)) {
273403e0689SChanwoo Choi dev_err(dev, "failed to find dev_pm_opp\n");
274c8ce82b9SViresh Kumar ret = PTR_ERR(opp);
275403e0689SChanwoo Choi goto err_opp;
276403e0689SChanwoo Choi }
277c8ce82b9SViresh Kumar bus->curr_freq = dev_pm_opp_get_freq(opp);
2788a31d9d9SViresh Kumar dev_pm_opp_put(opp);
279403e0689SChanwoo Choi
280403e0689SChanwoo Choi return 0;
281403e0689SChanwoo Choi
2820722249aSChanwoo Choi err_opp:
2830722249aSChanwoo Choi dev_pm_opp_of_remove_table(dev);
2840722249aSChanwoo Choi err_clk:
2850722249aSChanwoo Choi clk_disable_unprepare(bus->clk);
2860722249aSChanwoo Choi
2870722249aSChanwoo Choi return ret;
2880722249aSChanwoo Choi }
2890722249aSChanwoo Choi
exynos_bus_profile_init(struct exynos_bus * bus,struct devfreq_dev_profile * profile)290a47a97ecSArtur Świgoń static int exynos_bus_profile_init(struct exynos_bus *bus,
291a47a97ecSArtur Świgoń struct devfreq_dev_profile *profile)
2920722249aSChanwoo Choi {
293a47a97ecSArtur Świgoń struct device *dev = bus->dev;
2940722249aSChanwoo Choi struct devfreq_simple_ondemand_data *ondemand_data;
295a47a97ecSArtur Świgoń int ret;
296403e0689SChanwoo Choi
29783cb0e4dSMyungJoo Ham /* Initialize the struct profile and governor data for parent device */
2980722249aSChanwoo Choi profile->polling_ms = 50;
2990722249aSChanwoo Choi profile->target = exynos_bus_target;
3000722249aSChanwoo Choi profile->get_dev_status = exynos_bus_get_dev_status;
3010722249aSChanwoo Choi profile->exit = exynos_bus_exit;
3020722249aSChanwoo Choi
3030722249aSChanwoo Choi ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL);
304a4408921SArtur Świgoń if (!ondemand_data)
305a4408921SArtur Świgoń return -ENOMEM;
306a4408921SArtur Świgoń
3070722249aSChanwoo Choi ondemand_data->upthreshold = 40;
3080722249aSChanwoo Choi ondemand_data->downdifferential = 5;
3090722249aSChanwoo Choi
3100722249aSChanwoo Choi /* Add devfreq device to monitor and handle the exynos bus */
311aa7c352fSChanwoo Choi bus->devfreq = devm_devfreq_add_device(dev, profile,
312aa7c352fSChanwoo Choi DEVFREQ_GOV_SIMPLE_ONDEMAND,
3130722249aSChanwoo Choi ondemand_data);
3140722249aSChanwoo Choi if (IS_ERR(bus->devfreq)) {
3150722249aSChanwoo Choi dev_err(dev, "failed to add devfreq device\n");
316a4408921SArtur Świgoń return PTR_ERR(bus->devfreq);
3170722249aSChanwoo Choi }
3180722249aSChanwoo Choi
3190722249aSChanwoo Choi /* Register opp_notifier to catch the change of OPP */
3200722249aSChanwoo Choi ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq);
3210722249aSChanwoo Choi if (ret < 0) {
3220722249aSChanwoo Choi dev_err(dev, "failed to register opp notifier\n");
323a4408921SArtur Świgoń return ret;
3240722249aSChanwoo Choi }
3250722249aSChanwoo Choi
3260722249aSChanwoo Choi /*
3270722249aSChanwoo Choi * Enable devfreq-event to get raw data which is used to determine
3280722249aSChanwoo Choi * current bus load.
3290722249aSChanwoo Choi */
3300722249aSChanwoo Choi ret = exynos_bus_enable_edev(bus);
3310722249aSChanwoo Choi if (ret < 0) {
3320722249aSChanwoo Choi dev_err(dev, "failed to enable devfreq-event devices\n");
333a4408921SArtur Świgoń return ret;
3340722249aSChanwoo Choi }
3350722249aSChanwoo Choi
3360722249aSChanwoo Choi ret = exynos_bus_set_event(bus);
3370722249aSChanwoo Choi if (ret < 0) {
3380722249aSChanwoo Choi dev_err(dev, "failed to set event to devfreq-event devices\n");
3396c315d8fSYangtao Li goto err_edev;
3400722249aSChanwoo Choi }
3410722249aSChanwoo Choi
342a4408921SArtur Świgoń return 0;
3436c315d8fSYangtao Li
3446c315d8fSYangtao Li err_edev:
3456c315d8fSYangtao Li if (exynos_bus_disable_edev(bus))
3466c315d8fSYangtao Li dev_warn(dev, "failed to disable the devfreq-event devices\n");
3476c315d8fSYangtao Li
3486c315d8fSYangtao Li return ret;
349a47a97ecSArtur Świgoń }
350a47a97ecSArtur Świgoń
exynos_bus_profile_init_passive(struct exynos_bus * bus,struct devfreq_dev_profile * profile)351a05bb963SArtur Świgoń static int exynos_bus_profile_init_passive(struct exynos_bus *bus,
352a05bb963SArtur Świgoń struct devfreq_dev_profile *profile)
353a05bb963SArtur Świgoń {
354a05bb963SArtur Świgoń struct device *dev = bus->dev;
355a05bb963SArtur Świgoń struct devfreq_passive_data *passive_data;
356a05bb963SArtur Świgoń struct devfreq *parent_devfreq;
357a05bb963SArtur Świgoń
358a05bb963SArtur Świgoń /* Initialize the struct profile and governor data for passive device */
359a05bb963SArtur Świgoń profile->target = exynos_bus_target;
360a05bb963SArtur Świgoń profile->exit = exynos_bus_passive_exit;
361a05bb963SArtur Świgoń
362a05bb963SArtur Świgoń /* Get the instance of parent devfreq device */
36386d90fd9SChanwoo Choi parent_devfreq = devfreq_get_devfreq_by_phandle(dev, "devfreq", 0);
364a4408921SArtur Świgoń if (IS_ERR(parent_devfreq))
365a4408921SArtur Świgoń return -EPROBE_DEFER;
366a05bb963SArtur Świgoń
367a05bb963SArtur Świgoń passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
368a4408921SArtur Świgoń if (!passive_data)
369a4408921SArtur Świgoń return -ENOMEM;
370a4408921SArtur Świgoń
371a05bb963SArtur Świgoń passive_data->parent = parent_devfreq;
372a05bb963SArtur Świgoń
373a05bb963SArtur Świgoń /* Add devfreq device for exynos bus with passive governor */
374a05bb963SArtur Świgoń bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE,
375a05bb963SArtur Świgoń passive_data);
376a05bb963SArtur Świgoń if (IS_ERR(bus->devfreq)) {
377a05bb963SArtur Świgoń dev_err(dev,
378a05bb963SArtur Świgoń "failed to add devfreq dev with passive governor\n");
379a4408921SArtur Świgoń return PTR_ERR(bus->devfreq);
380a05bb963SArtur Świgoń }
381a05bb963SArtur Świgoń
382a4408921SArtur Świgoń return 0;
383a05bb963SArtur Świgoń }
384a05bb963SArtur Świgoń
exynos_bus_probe(struct platform_device * pdev)385a47a97ecSArtur Świgoń static int exynos_bus_probe(struct platform_device *pdev)
386a47a97ecSArtur Świgoń {
387a47a97ecSArtur Świgoń struct device *dev = &pdev->dev;
388a47a97ecSArtur Świgoń struct device_node *np = dev->of_node, *node;
389a47a97ecSArtur Świgoń struct devfreq_dev_profile *profile;
390a47a97ecSArtur Świgoń struct exynos_bus *bus;
391a47a97ecSArtur Świgoń int ret, max_state;
392a47a97ecSArtur Świgoń unsigned long min_freq, max_freq;
393a47a97ecSArtur Świgoń bool passive = false;
394a47a97ecSArtur Świgoń
395a47a97ecSArtur Świgoń if (!np) {
396a47a97ecSArtur Świgoń dev_err(dev, "failed to find devicetree node\n");
397a47a97ecSArtur Świgoń return -EINVAL;
398a47a97ecSArtur Świgoń }
399a47a97ecSArtur Świgoń
400a47a97ecSArtur Świgoń bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
401a47a97ecSArtur Świgoń if (!bus)
402a47a97ecSArtur Świgoń return -ENOMEM;
403a47a97ecSArtur Świgoń mutex_init(&bus->lock);
404a47a97ecSArtur Świgoń bus->dev = &pdev->dev;
405a47a97ecSArtur Świgoń platform_set_drvdata(pdev, bus);
406a47a97ecSArtur Świgoń
407a47a97ecSArtur Świgoń profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
408a47a97ecSArtur Świgoń if (!profile)
409a47a97ecSArtur Świgoń return -ENOMEM;
410a47a97ecSArtur Świgoń
411a47a97ecSArtur Świgoń node = of_parse_phandle(dev->of_node, "devfreq", 0);
412a47a97ecSArtur Świgoń if (node) {
413a47a97ecSArtur Świgoń of_node_put(node);
414a47a97ecSArtur Świgoń passive = true;
415a47a97ecSArtur Świgoń } else {
416a47a97ecSArtur Świgoń ret = exynos_bus_parent_parse_of(np, bus);
417a47a97ecSArtur Świgoń if (ret < 0)
418a47a97ecSArtur Świgoń return ret;
419a47a97ecSArtur Świgoń }
420a47a97ecSArtur Świgoń
421a47a97ecSArtur Świgoń /* Parse the device-tree to get the resource information */
422a47a97ecSArtur Świgoń ret = exynos_bus_parse_of(np, bus);
423a47a97ecSArtur Świgoń if (ret < 0)
424a47a97ecSArtur Świgoń goto err_reg;
425a47a97ecSArtur Świgoń
426a47a97ecSArtur Świgoń if (passive)
427a05bb963SArtur Świgoń ret = exynos_bus_profile_init_passive(bus, profile);
428a4408921SArtur Świgoń else
429a4408921SArtur Świgoń ret = exynos_bus_profile_init(bus, profile);
430a4408921SArtur Świgoń
431a05bb963SArtur Świgoń if (ret < 0)
432403e0689SChanwoo Choi goto err;
433403e0689SChanwoo Choi
434404d59c5SSylwester Nawrocki /* Create child platform device for the interconnect provider */
435*b7405e3fSRob Herring if (of_property_present(dev->of_node, "#interconnect-cells")) {
436404d59c5SSylwester Nawrocki bus->icc_pdev = platform_device_register_data(
437404d59c5SSylwester Nawrocki dev, "exynos-generic-icc",
438404d59c5SSylwester Nawrocki PLATFORM_DEVID_AUTO, NULL, 0);
439404d59c5SSylwester Nawrocki
440404d59c5SSylwester Nawrocki if (IS_ERR(bus->icc_pdev)) {
441404d59c5SSylwester Nawrocki ret = PTR_ERR(bus->icc_pdev);
442404d59c5SSylwester Nawrocki goto err;
443404d59c5SSylwester Nawrocki }
444404d59c5SSylwester Nawrocki }
445404d59c5SSylwester Nawrocki
446c8934e4eSChristian Marangi max_state = bus->devfreq->max_state;
447c8934e4eSChristian Marangi min_freq = (bus->devfreq->freq_table[0] / 1000);
448c8934e4eSChristian Marangi max_freq = (bus->devfreq->freq_table[max_state - 1] / 1000);
449403e0689SChanwoo Choi pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
450403e0689SChanwoo Choi dev_name(dev), min_freq, max_freq);
451403e0689SChanwoo Choi
4520722249aSChanwoo Choi return 0;
453403e0689SChanwoo Choi
454403e0689SChanwoo Choi err:
455403e0689SChanwoo Choi dev_pm_opp_of_remove_table(dev);
456403e0689SChanwoo Choi clk_disable_unprepare(bus->clk);
4572c2b20e0SKamil Konieczny err_reg:
458b0ec0942SViresh Kumar dev_pm_opp_put_regulators(bus->opp_token);
459403e0689SChanwoo Choi
460403e0689SChanwoo Choi return ret;
4610722249aSChanwoo Choi }
4620722249aSChanwoo Choi
exynos_bus_shutdown(struct platform_device * pdev)463fbb9c3c9SMarek Szyprowski static void exynos_bus_shutdown(struct platform_device *pdev)
464fbb9c3c9SMarek Szyprowski {
465fbb9c3c9SMarek Szyprowski struct exynos_bus *bus = dev_get_drvdata(&pdev->dev);
466fbb9c3c9SMarek Szyprowski
467fbb9c3c9SMarek Szyprowski devfreq_suspend_device(bus->devfreq);
468fbb9c3c9SMarek Szyprowski }
469fbb9c3c9SMarek Szyprowski
4700722249aSChanwoo Choi #ifdef CONFIG_PM_SLEEP
exynos_bus_resume(struct device * dev)4710722249aSChanwoo Choi static int exynos_bus_resume(struct device *dev)
4720722249aSChanwoo Choi {
4730722249aSChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
4740722249aSChanwoo Choi int ret;
4750722249aSChanwoo Choi
4760722249aSChanwoo Choi ret = exynos_bus_enable_edev(bus);
4770722249aSChanwoo Choi if (ret < 0) {
4780722249aSChanwoo Choi dev_err(dev, "failed to enable the devfreq-event devices\n");
4790722249aSChanwoo Choi return ret;
4800722249aSChanwoo Choi }
4810722249aSChanwoo Choi
4820722249aSChanwoo Choi return 0;
4830722249aSChanwoo Choi }
4840722249aSChanwoo Choi
exynos_bus_suspend(struct device * dev)4850722249aSChanwoo Choi static int exynos_bus_suspend(struct device *dev)
4860722249aSChanwoo Choi {
4870722249aSChanwoo Choi struct exynos_bus *bus = dev_get_drvdata(dev);
4880722249aSChanwoo Choi int ret;
4890722249aSChanwoo Choi
4900722249aSChanwoo Choi ret = exynos_bus_disable_edev(bus);
4910722249aSChanwoo Choi if (ret < 0) {
4920722249aSChanwoo Choi dev_err(dev, "failed to disable the devfreq-event devices\n");
4930722249aSChanwoo Choi return ret;
4940722249aSChanwoo Choi }
4950722249aSChanwoo Choi
4960722249aSChanwoo Choi return 0;
4970722249aSChanwoo Choi }
4980722249aSChanwoo Choi #endif
4990722249aSChanwoo Choi
5000722249aSChanwoo Choi static const struct dev_pm_ops exynos_bus_pm = {
5010722249aSChanwoo Choi SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume)
5020722249aSChanwoo Choi };
5030722249aSChanwoo Choi
5040722249aSChanwoo Choi static const struct of_device_id exynos_bus_of_match[] = {
5050722249aSChanwoo Choi { .compatible = "samsung,exynos-bus", },
5060722249aSChanwoo Choi { /* sentinel */ },
5070722249aSChanwoo Choi };
5080722249aSChanwoo Choi MODULE_DEVICE_TABLE(of, exynos_bus_of_match);
5090722249aSChanwoo Choi
5100722249aSChanwoo Choi static struct platform_driver exynos_bus_platdrv = {
5110722249aSChanwoo Choi .probe = exynos_bus_probe,
512fbb9c3c9SMarek Szyprowski .shutdown = exynos_bus_shutdown,
5130722249aSChanwoo Choi .driver = {
5140722249aSChanwoo Choi .name = "exynos-bus",
5150722249aSChanwoo Choi .pm = &exynos_bus_pm,
516f9d0393aSKrzysztof Kozlowski .of_match_table = exynos_bus_of_match,
5170722249aSChanwoo Choi },
5180722249aSChanwoo Choi };
5190722249aSChanwoo Choi module_platform_driver(exynos_bus_platdrv);
5200722249aSChanwoo Choi
5210722249aSChanwoo Choi MODULE_SOFTDEP("pre: exynos_ppmu");
5220722249aSChanwoo Choi MODULE_DESCRIPTION("Generic Exynos Bus frequency driver");
5230722249aSChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
524 MODULE_LICENSE("GPL v2");
525