xref: /openbmc/linux/drivers/devfreq/exynos-bus.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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