diff options
Diffstat (limited to 'drivers/devfreq')
| -rw-r--r-- | drivers/devfreq/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/devfreq/devfreq.c | 185 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/Makefile | 2 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.c | 254 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.h | 110 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/exynos5_bus.c | 189 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.c | 60 | ||||
| -rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.h | 8 | 
8 files changed, 470 insertions, 345 deletions
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 31f3adba4cf..49e74c1fc63 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -67,22 +67,23 @@ comment "DEVFREQ Drivers"  config ARM_EXYNOS4_BUS_DEVFREQ  	bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" -	depends on CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 +	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM  	select ARCH_HAS_OPP  	select DEVFREQ_GOV_SIMPLE_ONDEMAND +	select PM_OPP  	help  	  This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)  	  and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int).  	  It reads PPMU counters of memory controllers and adjusts  	  the operating frequencies and voltages with OPP support. -	  To operate with optimal voltages, ASV support is required -	  (CONFIG_EXYNOS_ASV). +	  This does not yet operate with optimal voltages.  config ARM_EXYNOS5_BUS_DEVFREQ  	bool "ARM Exynos5250 Bus DEVFREQ Driver"  	depends on SOC_EXYNOS5250  	select ARCH_HAS_OPP  	select DEVFREQ_GOV_SIMPLE_ONDEMAND +	select PM_OPP  	help  	  This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).  	  It reads PPMU counters of memory controllers and adjusts the diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c99c00d35d3..9f90369dd6b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -18,7 +18,7 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/stat.h> -#include <linux/opp.h> +#include <linux/pm_opp.h>  #include <linux/devfreq.h>  #include <linux/workqueue.h>  #include <linux/platform_device.h> @@ -91,26 +91,35 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)   */  static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)  { -	int lev, prev_lev; +	int lev, prev_lev, ret = 0;  	unsigned long cur_time; -	lev = devfreq_get_freq_level(devfreq, freq); -	if (lev < 0) -		return lev; -  	cur_time = jiffies; -	devfreq->time_in_state[lev] += + +	prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq); +	if (prev_lev < 0) { +		ret = prev_lev; +		goto out; +	} + +	devfreq->time_in_state[prev_lev] +=  			 cur_time - devfreq->last_stat_updated; -	if (freq != devfreq->previous_freq) { -		prev_lev = devfreq_get_freq_level(devfreq, -						devfreq->previous_freq); + +	lev = devfreq_get_freq_level(devfreq, freq); +	if (lev < 0) { +		ret = lev; +		goto out; +	} + +	if (lev != prev_lev) {  		devfreq->trans_table[(prev_lev *  				devfreq->profile->max_state) + lev]++;  		devfreq->total_trans++;  	} -	devfreq->last_stat_updated = cur_time; -	return 0; +out: +	devfreq->last_stat_updated = cur_time; +	return ret;  }  /** @@ -385,7 +394,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,   * @devfreq:	the devfreq struct   * @skip:	skip calling device_unregister().   */ -static void _remove_devfreq(struct devfreq *devfreq, bool skip) +static void _remove_devfreq(struct devfreq *devfreq)  {  	mutex_lock(&devfreq_list_lock);  	if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { @@ -403,11 +412,6 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)  	if (devfreq->profile->exit)  		devfreq->profile->exit(devfreq->dev.parent); -	if (!skip && get_device(&devfreq->dev)) { -		device_unregister(&devfreq->dev); -		put_device(&devfreq->dev); -	} -  	mutex_destroy(&devfreq->lock);  	kfree(devfreq);  } @@ -417,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)   * @dev:	the devfreq device   *   * This calls _remove_devfreq() if _remove_devfreq() is not called. - * Note that devfreq_dev_release() could be called by _remove_devfreq() as - * well as by others unregistering the device.   */  static void devfreq_dev_release(struct device *dev)  {  	struct devfreq *devfreq = to_devfreq(dev); -	_remove_devfreq(devfreq, true); +	_remove_devfreq(devfreq);  }  /** @@ -535,12 +537,76 @@ int devfreq_remove_device(struct devfreq *devfreq)  	if (!devfreq)  		return -EINVAL; -	_remove_devfreq(devfreq, false); +	device_unregister(&devfreq->dev); +	put_device(&devfreq->dev);  	return 0;  }  EXPORT_SYMBOL(devfreq_remove_device); +static int devm_devfreq_dev_match(struct device *dev, void *res, void *data) +{ +	struct devfreq **r = res; + +	if (WARN_ON(!r || !*r)) +		return 0; + +	return *r == data; +} + +static void devm_devfreq_dev_release(struct device *dev, void *res) +{ +	devfreq_remove_device(*(struct devfreq **)res); +} + +/** + * devm_devfreq_add_device() - Resource-managed devfreq_add_device() + * @dev:	the device to add devfreq feature. + * @profile:	device-specific profile to run devfreq. + * @governor_name:	name of the policy to choose frequency. + * @data:	private data for the governor. The devfreq framework does not + *		touch this value. + * + * This function manages automatically the memory of devfreq device using device + * resource management and simplify the free operation for memory of devfreq + * device. + */ +struct devfreq *devm_devfreq_add_device(struct device *dev, +					struct devfreq_dev_profile *profile, +					const char *governor_name, +					void *data) +{ +	struct devfreq **ptr, *devfreq; + +	ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return ERR_PTR(-ENOMEM); + +	devfreq = devfreq_add_device(dev, profile, governor_name, data); +	if (IS_ERR(devfreq)) { +		devres_free(ptr); +		return ERR_PTR(-ENOMEM); +	} + +	*ptr = devfreq; +	devres_add(dev, ptr); + +	return devfreq; +} +EXPORT_SYMBOL(devm_devfreq_add_device); + +/** + * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() + * @dev:	the device to add devfreq feature. + * @devfreq:	the devfreq instance to be removed + */ +void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) +{ +	WARN_ON(devres_release(dev, devm_devfreq_dev_release, +			       devm_devfreq_dev_match, devfreq)); +} +EXPORT_SYMBOL(devm_devfreq_remove_device); +  /**   * devfreq_suspend_device() - Suspend devfreq of a device.   * @devfreq: the devfreq instance to be suspended @@ -902,13 +968,13 @@ static ssize_t available_frequencies_show(struct device *d,  {  	struct devfreq *df = to_devfreq(d);  	struct device *dev = df->dev.parent; -	struct opp *opp; +	struct dev_pm_opp *opp;  	ssize_t count = 0;  	unsigned long freq = 0;  	rcu_read_lock();  	do { -		opp = opp_find_freq_ceil(dev, &freq); +		opp = dev_pm_opp_find_freq_ceil(dev, &freq);  		if (IS_ERR(opp))  			break; @@ -993,10 +1059,10 @@ static int __init devfreq_init(void)  	}  	devfreq_wq = create_freezable_workqueue("devfreq_wq"); -	if (IS_ERR(devfreq_wq)) { +	if (!devfreq_wq) {  		class_destroy(devfreq_class);  		pr_err("%s: couldn't create workqueue\n", __FILE__); -		return PTR_ERR(devfreq_wq); +		return -ENOMEM;  	}  	devfreq_class->dev_groups = devfreq_groups; @@ -1029,25 +1095,26 @@ module_exit(devfreq_exit);   * under the locked area. The pointer returned must be used prior to unlocking   * with rcu_read_unlock() to maintain the integrity of the pointer.   */ -struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, -				    u32 flags) +struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, +					   unsigned long *freq, +					   u32 flags)  { -	struct opp *opp; +	struct dev_pm_opp *opp;  	if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) {  		/* The freq is an upper bound. opp should be lower */ -		opp = opp_find_freq_floor(dev, freq); +		opp = dev_pm_opp_find_freq_floor(dev, freq);  		/* If not available, use the closest opp */  		if (opp == ERR_PTR(-ERANGE)) -			opp = opp_find_freq_ceil(dev, freq); +			opp = dev_pm_opp_find_freq_ceil(dev, freq);  	} else {  		/* The freq is an lower bound. opp should be higher */ -		opp = opp_find_freq_ceil(dev, freq); +		opp = dev_pm_opp_find_freq_ceil(dev, freq);  		/* If not available, use the closest opp */  		if (opp == ERR_PTR(-ERANGE)) -			opp = opp_find_freq_floor(dev, freq); +			opp = dev_pm_opp_find_freq_floor(dev, freq);  	}  	return opp; @@ -1066,7 +1133,7 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)  	int ret = 0;  	rcu_read_lock(); -	nh = opp_get_notifier(dev); +	nh = dev_pm_opp_get_notifier(dev);  	if (IS_ERR(nh))  		ret = PTR_ERR(nh);  	rcu_read_unlock(); @@ -1092,7 +1159,7 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)  	int ret = 0;  	rcu_read_lock(); -	nh = opp_get_notifier(dev); +	nh = dev_pm_opp_get_notifier(dev);  	if (IS_ERR(nh))  		ret = PTR_ERR(nh);  	rcu_read_unlock(); @@ -1102,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)  	return ret;  } +static void devm_devfreq_opp_release(struct device *dev, void *res) +{ +	devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); +} + +/** + * devm_ devfreq_register_opp_notifier() + *		- Resource-managed devfreq_register_opp_notifier() + * @dev:	The devfreq user device. (parent of devfreq) + * @devfreq:	The devfreq object. + */ +int devm_devfreq_register_opp_notifier(struct device *dev, +				       struct devfreq *devfreq) +{ +	struct devfreq **ptr; +	int ret; + +	ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return -ENOMEM; + +	ret = devfreq_register_opp_notifier(dev, devfreq); +	if (ret) { +		devres_free(ptr); +		return ret; +	} + +	*ptr = devfreq; +	devres_add(dev, ptr); + +	return 0; +} +EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); + +/** + * devm_devfreq_unregister_opp_notifier() + *		- Resource-managed devfreq_unregister_opp_notifier() + * @dev:	The devfreq user device. (parent of devfreq) + * @devfreq:	The devfreq object. + */ +void devm_devfreq_unregister_opp_notifier(struct device *dev, +					 struct devfreq *devfreq) +{ +	WARN_ON(devres_release(dev, devm_devfreq_opp_release, +			       devm_devfreq_dev_match, devfreq)); +} +EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); +  MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");  MODULE_DESCRIPTION("devfreq class support");  MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile index bfaaf5b0d61..49bc9175f92 100644 --- a/drivers/devfreq/exynos/Makefile +++ b/drivers/devfreq/exynos/Makefile @@ -1,3 +1,3 @@  # Exynos DEVFREQ Drivers -obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)	+= exynos4_bus.o +obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)	+= exynos_ppmu.o exynos4_bus.o  obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ)	+= exynos_ppmu.o exynos5_bus.o diff --git a/drivers/devfreq/exynos/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c index c5f86d8caca..d9b08d3b683 100644 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ b/drivers/devfreq/exynos/exynos4_bus.c @@ -19,20 +19,16 @@  #include <linux/slab.h>  #include <linux/mutex.h>  #include <linux/suspend.h> -#include <linux/opp.h> +#include <linux/pm_opp.h>  #include <linux/devfreq.h>  #include <linux/platform_device.h>  #include <linux/regulator/consumer.h>  #include <linux/module.h> -/* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ -#ifdef CONFIG_EXYNOS_ASV -extern unsigned int exynos_result_of_asv; -#endif - -#include <mach/regs-clock.h> +#include <mach/map.h> -#include <plat/map-s5p.h> +#include "exynos_ppmu.h" +#include "exynos4_bus.h"  #define MAX_SAFEVOLT	1200000 /* 1.2V */ @@ -44,22 +40,6 @@ enum exynos4_busf_type {  /* Assume that the bus is saturated if the utilization is 40% */  #define BUS_SATURATION_RATIO	40 -enum ppmu_counter { -	PPMU_PMNCNT0 = 0, -	PPMU_PMCCNT1, -	PPMU_PMNCNT2, -	PPMU_PMNCNT3, -	PPMU_PMNCNT_MAX, -}; -struct exynos4_ppmu { -	void __iomem *hw_base; -	unsigned int ccnt; -	unsigned int event; -	unsigned int count[PPMU_PMNCNT_MAX]; -	bool ccnt_overflow; -	bool count_overflow[PPMU_PMNCNT_MAX]; -}; -  enum busclk_level_idx {  	LV_0 = 0,  	LV_1, @@ -68,6 +48,13 @@ enum busclk_level_idx {  	LV_4,  	_LV_END  }; + +enum exynos_ppmu_idx { +	PPMU_DMC0, +	PPMU_DMC1, +	PPMU_END, +}; +  #define EX4210_LV_MAX	LV_2  #define EX4x12_LV_MAX	LV_4  #define EX4210_LV_NUM	(LV_2 + 1) @@ -91,7 +78,7 @@ struct busfreq_data {  	struct regulator *vdd_int;  	struct regulator *vdd_mif; /* Exynos4412/4212 only */  	struct busfreq_opp_info curr_oppinfo; -	struct exynos4_ppmu dmc[2]; +	struct busfreq_ppmu_data ppmu_data;  	struct notifier_block pm_notifier;  	struct mutex lock; @@ -101,12 +88,6 @@ struct busfreq_data {  	unsigned int top_divtable[_LV_END];  }; -struct bus_opp_table { -	unsigned int idx; -	unsigned long clk; -	unsigned long volt; -}; -  /* 4210 controls clock of mif and voltage of int */  static struct bus_opp_table exynos4210_busclk_table[] = {  	{LV_0, 400000, 1150000}, @@ -116,7 +97,7 @@ static struct bus_opp_table exynos4210_busclk_table[] = {  };  /* - * MIF is the main control knob clock for exynox4x12 MIF/INT + * MIF is the main control knob clock for Exynos4x12 MIF/INT   * clock and voltage of both mif/int are controlled.   */  static struct bus_opp_table exynos4x12_mifclk_table[] = { @@ -524,57 +505,6 @@ static int exynos4x12_set_busclk(struct busfreq_data *data,  	return 0;  } - -static void busfreq_mon_reset(struct busfreq_data *data) -{ -	unsigned int i; - -	for (i = 0; i < 2; i++) { -		void __iomem *ppmu_base = data->dmc[i].hw_base; - -		/* Reset PPMU */ -		__raw_writel(0x8000000f, ppmu_base + 0xf010); -		__raw_writel(0x8000000f, ppmu_base + 0xf050); -		__raw_writel(0x6, ppmu_base + 0xf000); -		__raw_writel(0x0, ppmu_base + 0xf100); - -		/* Set PPMU Event */ -		data->dmc[i].event = 0x6; -		__raw_writel(((data->dmc[i].event << 12) | 0x1), -			     ppmu_base + 0xfc); - -		/* Start PPMU */ -		__raw_writel(0x1, ppmu_base + 0xf000); -	} -} - -static void exynos4_read_ppmu(struct busfreq_data *data) -{ -	int i, j; - -	for (i = 0; i < 2; i++) { -		void __iomem *ppmu_base = data->dmc[i].hw_base; -		u32 overflow; - -		/* Stop PPMU */ -		__raw_writel(0x0, ppmu_base + 0xf000); - -		/* Update local data from PPMU */ -		overflow = __raw_readl(ppmu_base + 0xf050); - -		data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100); -		data->dmc[i].ccnt_overflow = overflow & (1 << 31); - -		for (j = 0; j < PPMU_PMNCNT_MAX; j++) { -			data->dmc[i].count[j] = __raw_readl( -					ppmu_base + (0xf110 + (0x10 * j))); -			data->dmc[i].count_overflow[j] = overflow & (1 << j); -		} -	} - -	busfreq_mon_reset(data); -} -  static int exynos4x12_get_intspec(unsigned long mifclk)  {  	int i = 0; @@ -639,7 +569,7 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,  	struct platform_device *pdev = container_of(dev, struct platform_device,  						    dev);  	struct busfreq_data *data = platform_get_drvdata(pdev); -	struct opp *opp; +	struct dev_pm_opp *opp;  	unsigned long freq;  	unsigned long old_freq = data->curr_oppinfo.rate;  	struct busfreq_opp_info	new_oppinfo; @@ -650,8 +580,8 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,  		rcu_read_unlock();  		return PTR_ERR(opp);  	} -	new_oppinfo.rate = opp_get_freq(opp); -	new_oppinfo.volt = opp_get_voltage(opp); +	new_oppinfo.rate = dev_pm_opp_get_freq(opp); +	new_oppinfo.volt = dev_pm_opp_get_voltage(opp);  	rcu_read_unlock();  	freq = new_oppinfo.rate; @@ -698,84 +628,35 @@ out:  	return err;  } -static int exynos4_get_busier_dmc(struct busfreq_data *data) -{ -	u64 p0 = data->dmc[0].count[0]; -	u64 p1 = data->dmc[1].count[0]; - -	p0 *= data->dmc[1].ccnt; -	p1 *= data->dmc[0].ccnt; - -	if (data->dmc[1].ccnt == 0) -		return 0; - -	if (p0 > p1) -		return 0; -	return 1; -} -  static int exynos4_bus_get_dev_status(struct device *dev,  				      struct devfreq_dev_status *stat)  {  	struct busfreq_data *data = dev_get_drvdata(dev); -	int busier_dmc; -	int cycles_x2 = 2; /* 2 x cycles */ -	void __iomem *addr; -	u32 timing; -	u32 memctrl; - -	exynos4_read_ppmu(data); -	busier_dmc = exynos4_get_busier_dmc(data); -	stat->current_frequency = data->curr_oppinfo.rate; +	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; +	int busier; -	if (busier_dmc) -		addr = S5P_VA_DMC1; -	else -		addr = S5P_VA_DMC0; - -	memctrl = __raw_readl(addr + 0x04); /* one of DDR2/3/LPDDR2 */ -	timing = __raw_readl(addr + 0x38); /* CL or WL/RL values */ - -	switch ((memctrl >> 8) & 0xf) { -	case 0x4: /* DDR2 */ -		cycles_x2 = ((timing >> 16) & 0xf) * 2; -		break; -	case 0x5: /* LPDDR2 */ -	case 0x6: /* DDR3 */ -		cycles_x2 = ((timing >> 8) & 0xf) + ((timing >> 0) & 0xf); -		break; -	default: -		pr_err("%s: Unknown Memory Type(%d).\n", __func__, -		       (memctrl >> 8) & 0xf); -		return -EINVAL; -	} +	exynos_read_ppmu(ppmu_data); +	busier = exynos_get_busier_ppmu(ppmu_data); +	stat->current_frequency = data->curr_oppinfo.rate;  	/* Number of cycles spent on memory access */ -	stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2); +	stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3];  	stat->busy_time *= 100 / BUS_SATURATION_RATIO; -	stat->total_time = data->dmc[busier_dmc].ccnt; +	stat->total_time = ppmu_data->ppmu[busier].ccnt;  	/* If the counters have overflown, retry */ -	if (data->dmc[busier_dmc].ccnt_overflow || -	    data->dmc[busier_dmc].count_overflow[0]) +	if (ppmu_data->ppmu[busier].ccnt_overflow || +	    ppmu_data->ppmu[busier].count_overflow[0])  		return -EAGAIN;  	return 0;  } -static void exynos4_bus_exit(struct device *dev) -{ -	struct busfreq_data *data = dev_get_drvdata(dev); - -	devfreq_unregister_opp_notifier(dev, data->devfreq); -} -  static struct devfreq_dev_profile exynos4_devfreq_profile = {  	.initial_freq	= 400000,  	.polling_ms	= 50,  	.target		= exynos4_bus_target,  	.get_dev_status	= exynos4_bus_get_dev_status, -	.exit		= exynos4_bus_exit,  };  static int exynos4210_init_tables(struct busfreq_data *data) @@ -837,11 +718,11 @@ static int exynos4210_init_tables(struct busfreq_data *data)  		data->top_divtable[i] = tmp;  	} -#ifdef CONFIG_EXYNOS_ASV -	tmp = exynos4_result_of_asv; -#else +	/* +	 * TODO: init tmp based on busfreq_data +	 * (device-tree or platform-data) +	 */  	tmp = 0; /* Max voltages for the reliability of the unknown */ -#endif  	pr_debug("ASV Group of Exynos4 is %d\n", tmp);  	/* Use merged grouping for voltage */ @@ -873,7 +754,7 @@ static int exynos4210_init_tables(struct busfreq_data *data)  		exynos4210_busclk_table[i].volt = exynos4210_asv_volt[mgrp][i];  	for (i = LV_0; i < EX4210_LV_NUM; i++) { -		err = opp_add(data->dev, exynos4210_busclk_table[i].clk, +		err = dev_pm_opp_add(data->dev, exynos4210_busclk_table[i].clk,  			      exynos4210_busclk_table[i].volt);  		if (err) {  			dev_err(data->dev, "Cannot add opp entries.\n"); @@ -922,11 +803,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data)  		data->dmc_divtable[i] = tmp;  	} -#ifdef CONFIG_EXYNOS_ASV -	tmp = exynos4_result_of_asv; -#else  	tmp = 0; /* Max voltages for the reliability of the unknown */ -#endif  	if (tmp > 8)  		tmp = 0; @@ -940,7 +817,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data)  	}  	for (i = 0; i < EX4x12_LV_NUM; i++) { -		ret = opp_add(data->dev, exynos4x12_mifclk_table[i].clk, +		ret = dev_pm_opp_add(data->dev, exynos4x12_mifclk_table[i].clk,  			      exynos4x12_mifclk_table[i].volt);  		if (ret) {  			dev_err(data->dev, "Fail to add opp entries.\n"); @@ -956,7 +833,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,  {  	struct busfreq_data *data = container_of(this, struct busfreq_data,  						 pm_notifier); -	struct opp *opp; +	struct dev_pm_opp *opp;  	struct busfreq_opp_info	new_oppinfo;  	unsigned long maxfreq = ULONG_MAX;  	int err = 0; @@ -969,7 +846,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,  		data->disabled = true;  		rcu_read_lock(); -		opp = opp_find_freq_floor(data->dev, &maxfreq); +		opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq);  		if (IS_ERR(opp)) {  			rcu_read_unlock();  			dev_err(data->dev, "%s: unable to find a min freq\n", @@ -977,8 +854,8 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,  			mutex_unlock(&data->lock);  			return PTR_ERR(opp);  		} -		new_oppinfo.rate = opp_get_freq(opp); -		new_oppinfo.volt = opp_get_voltage(opp); +		new_oppinfo.rate = dev_pm_opp_get_freq(opp); +		new_oppinfo.volt = dev_pm_opp_get_voltage(opp);  		rcu_read_unlock();  		err = exynos4_bus_setvolt(data, &new_oppinfo, @@ -1020,7 +897,8 @@ unlock:  static int exynos4_busfreq_probe(struct platform_device *pdev)  {  	struct busfreq_data *data; -	struct opp *opp; +	struct busfreq_ppmu_data *ppmu_data; +	struct dev_pm_opp *opp;  	struct device *dev = &pdev->dev;  	int err = 0; @@ -1030,9 +908,19 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)  		return -ENOMEM;  	} +	ppmu_data = &data->ppmu_data; +	ppmu_data->ppmu_end = PPMU_END; +	ppmu_data->ppmu = devm_kzalloc(dev, +				       sizeof(struct exynos_ppmu) * PPMU_END, +				       GFP_KERNEL); +	if (!ppmu_data->ppmu) { +		dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); +		return -ENOMEM; +	} +  	data->type = pdev->id_entry->driver_data; -	data->dmc[0].hw_base = S5P_VA_DMC0; -	data->dmc[1].hw_base = S5P_VA_DMC1; +	ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; +	ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1;  	data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;  	data->dev = dev;  	mutex_init(&data->lock); @@ -1048,8 +936,11 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)  		dev_err(dev, "Cannot determine the device id %d\n", data->type);  		err = -EINVAL;  	} -	if (err) +	if (err) { +		dev_err(dev, "Cannot initialize busfreq table %d\n", +			     data->type);  		return err; +	}  	data->vdd_int = devm_regulator_get(dev, "vdd_int");  	if (IS_ERR(data->vdd_int)) { @@ -1065,32 +956,42 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)  	}  	rcu_read_lock(); -	opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); +	opp = dev_pm_opp_find_freq_floor(dev, +					 &exynos4_devfreq_profile.initial_freq);  	if (IS_ERR(opp)) {  		rcu_read_unlock();  		dev_err(dev, "Invalid initial frequency %lu kHz.\n",  			exynos4_devfreq_profile.initial_freq);  		return PTR_ERR(opp);  	} -	data->curr_oppinfo.rate = opp_get_freq(opp); -	data->curr_oppinfo.volt = opp_get_voltage(opp); +	data->curr_oppinfo.rate = dev_pm_opp_get_freq(opp); +	data->curr_oppinfo.volt = dev_pm_opp_get_voltage(opp);  	rcu_read_unlock();  	platform_set_drvdata(pdev, data); -	busfreq_mon_reset(data); - -	data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, +	data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile,  					   "simple_ondemand", NULL);  	if (IS_ERR(data->devfreq))  		return PTR_ERR(data->devfreq); -	devfreq_register_opp_notifier(dev, data->devfreq); +	/* +	 * Start PPMU (Performance Profiling Monitoring Unit) to check +	 * utilization of each IP in the Exynos4 SoC. +	 */ +	busfreq_mon_reset(ppmu_data); +	/* Register opp_notifier for Exynos4 busfreq */ +	err = devm_devfreq_register_opp_notifier(dev, data->devfreq); +	if (err < 0) { +		dev_err(dev, "Failed to register opp notifier\n"); +		return err; +	} + +	/* Register pm_notifier for Exynos4 busfreq */  	err = register_pm_notifier(&data->pm_notifier);  	if (err) {  		dev_err(dev, "Failed to setup pm notifier\n"); -		devfreq_remove_device(data->devfreq);  		return err;  	} @@ -1101,23 +1002,24 @@ static int exynos4_busfreq_remove(struct platform_device *pdev)  {  	struct busfreq_data *data = platform_get_drvdata(pdev); +	/* Unregister all of notifier chain */  	unregister_pm_notifier(&data->pm_notifier); -	devfreq_remove_device(data->devfreq);  	return 0;  } +#ifdef CONFIG_PM_SLEEP  static int exynos4_busfreq_resume(struct device *dev)  {  	struct busfreq_data *data = dev_get_drvdata(dev); +	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; -	busfreq_mon_reset(data); +	busfreq_mon_reset(ppmu_data);  	return 0;  } +#endif -static const struct dev_pm_ops exynos4_busfreq_pm = { -	.resume	= exynos4_busfreq_resume, -}; +static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume);  static const struct platform_device_id exynos4_busfreq_id[] = {  	{ "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, @@ -1133,7 +1035,7 @@ static struct platform_driver exynos4_busfreq_driver = {  	.driver = {  		.name	= "exynos4-busfreq",  		.owner	= THIS_MODULE, -		.pm	= &exynos4_busfreq_pm, +		.pm	= &exynos4_busfreq_pm_ops,  	},  }; diff --git a/drivers/devfreq/exynos/exynos4_bus.h b/drivers/devfreq/exynos/exynos4_bus.h new file mode 100644 index 00000000000..94c73c18d28 --- /dev/null +++ b/drivers/devfreq/exynos/exynos4_bus.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + *		http://www.samsung.com/ + * + * EXYNOS4 BUS header + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __DEVFREQ_EXYNOS4_BUS_H +#define __DEVFREQ_EXYNOS4_BUS_H __FILE__ + +#include <mach/map.h> + +#define EXYNOS4_CLKDIV_LEFTBUS			(S5P_VA_CMU + 0x04500) +#define EXYNOS4_CLKDIV_STAT_LEFTBUS		(S5P_VA_CMU + 0x04600) + +#define EXYNOS4_CLKDIV_RIGHTBUS			(S5P_VA_CMU + 0x08500) +#define EXYNOS4_CLKDIV_STAT_RIGHTBUS		(S5P_VA_CMU + 0x08600) + +#define EXYNOS4_CLKDIV_TOP			(S5P_VA_CMU + 0x0C510) +#define EXYNOS4_CLKDIV_CAM			(S5P_VA_CMU + 0x0C520) +#define EXYNOS4_CLKDIV_MFC			(S5P_VA_CMU + 0x0C528) + +#define EXYNOS4_CLKDIV_STAT_TOP			(S5P_VA_CMU + 0x0C610) +#define EXYNOS4_CLKDIV_STAT_MFC			(S5P_VA_CMU + 0x0C628) + +#define EXYNOS4210_CLKGATE_IP_IMAGE		(S5P_VA_CMU + 0x0C930) +#define EXYNOS4212_CLKGATE_IP_IMAGE		(S5P_VA_CMU + 0x04930) + +#define EXYNOS4_CLKDIV_DMC0			(S5P_VA_CMU + 0x10500) +#define EXYNOS4_CLKDIV_DMC1			(S5P_VA_CMU + 0x10504) +#define EXYNOS4_CLKDIV_STAT_DMC0		(S5P_VA_CMU + 0x10600) +#define EXYNOS4_CLKDIV_STAT_DMC1		(S5P_VA_CMU + 0x10604) + +#define EXYNOS4_DMC_PAUSE_CTRL			(S5P_VA_CMU + 0x11094) +#define EXYNOS4_DMC_PAUSE_ENABLE		(1 << 0) + +#define EXYNOS4_CLKDIV_DMC0_ACP_SHIFT		(0) +#define EXYNOS4_CLKDIV_DMC0_ACP_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT	(4) +#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK	(0x7 << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT		(8) +#define EXYNOS4_CLKDIV_DMC0_DPHY_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_DMC_SHIFT		(12) +#define EXYNOS4_CLKDIV_DMC0_DMC_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT		(16) +#define EXYNOS4_CLKDIV_DMC0_DMCD_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT		(20) +#define EXYNOS4_CLKDIV_DMC0_DMCP_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT		(24) +#define EXYNOS4_CLKDIV_DMC0_COPY2_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) +#define EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT	(28) +#define EXYNOS4_CLKDIV_DMC0_CORETI_MASK		(0x7 << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT) + +#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT	(0) +#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK	(0xf << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) +#define EXYNOS4_CLKDIV_DMC1_C2C_SHIFT		(4) +#define EXYNOS4_CLKDIV_DMC1_C2C_MASK		(0x7 << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) +#define EXYNOS4_CLKDIV_DMC1_PWI_SHIFT		(8) +#define EXYNOS4_CLKDIV_DMC1_PWI_MASK		(0xf << EXYNOS4_CLKDIV_DMC1_PWI_SHIFT) +#define EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT	(12) +#define EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK	(0x7 << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT) +#define EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT		(16) +#define EXYNOS4_CLKDIV_DMC1_DVSEM_MASK		(0x7f << EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT) +#define EXYNOS4_CLKDIV_DMC1_DPM_SHIFT		(24) +#define EXYNOS4_CLKDIV_DMC1_DPM_MASK		(0x7f << EXYNOS4_CLKDIV_DMC1_DPM_SHIFT) + +#define EXYNOS4_CLKDIV_MFC_SHIFT		(0) +#define EXYNOS4_CLKDIV_MFC_MASK			(0x7 << EXYNOS4_CLKDIV_MFC_SHIFT) + +#define EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT	(0) +#define EXYNOS4_CLKDIV_TOP_ACLK200_MASK		(0x7 << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT	(4) +#define EXYNOS4_CLKDIV_TOP_ACLK100_MASK		(0xF << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT	(8) +#define EXYNOS4_CLKDIV_TOP_ACLK160_MASK		(0x7 << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT	(12) +#define EXYNOS4_CLKDIV_TOP_ACLK133_MASK		(0x7 << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT	(16) +#define EXYNOS4_CLKDIV_TOP_ONENAND_MASK		(0x7 << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT	(20) +#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK	(0x7 << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) +#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT	(24) +#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_MASK	(0x7 << EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT) + +#define EXYNOS4_CLKDIV_BUS_GDLR_SHIFT		(0) +#define EXYNOS4_CLKDIV_BUS_GDLR_MASK		(0x7 << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) +#define EXYNOS4_CLKDIV_BUS_GPLR_SHIFT		(4) +#define EXYNOS4_CLKDIV_BUS_GPLR_MASK		(0x7 << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT) + +#define EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT		(0) +#define EXYNOS4_CLKDIV_CAM_FIMC0_MASK		(0xf << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) +#define EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT		(4) +#define EXYNOS4_CLKDIV_CAM_FIMC1_MASK		(0xf << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) +#define EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT		(8) +#define EXYNOS4_CLKDIV_CAM_FIMC2_MASK		(0xf << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) +#define EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT		(12) +#define EXYNOS4_CLKDIV_CAM_FIMC3_MASK		(0xf << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT) + +#define EXYNOS4_CLKDIV_CAM1			(S5P_VA_CMU + 0x0C568) + +#define EXYNOS4_CLKDIV_STAT_CAM1		(S5P_VA_CMU + 0x0C668) + +#define EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT		(0) +#define EXYNOS4_CLKDIV_CAM1_JPEG_MASK		(0xf << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT) + +#endif /* __DEVFREQ_EXYNOS4_BUS_H */ diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c index 574b16b59be..6cd0392e279 100644 --- a/drivers/devfreq/exynos/exynos5_bus.c +++ b/drivers/devfreq/exynos/exynos5_bus.c @@ -15,10 +15,9 @@  #include <linux/module.h>  #include <linux/devfreq.h>  #include <linux/io.h> -#include <linux/opp.h> +#include <linux/pm_opp.h>  #include <linux/slab.h>  #include <linux/suspend.h> -#include <linux/opp.h>  #include <linux/clk.h>  #include <linux/delay.h>  #include <linux/platform_device.h> @@ -51,7 +50,7 @@ struct busfreq_data_int {  	struct device *dev;  	struct devfreq *devfreq;  	struct regulator *vdd_int; -	struct exynos_ppmu ppmu[PPMU_END]; +	struct busfreq_ppmu_data ppmu_data;  	unsigned long curr_freq;  	bool disabled; @@ -76,49 +75,6 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {  	{0, 0, 0},  }; -static void busfreq_mon_reset(struct busfreq_data_int *data) -{ -	unsigned int i; - -	for (i = PPMU_RIGHT; i < PPMU_END; i++) { -		void __iomem *ppmu_base = data->ppmu[i].hw_base; - -		/* Reset the performance and cycle counters */ -		exynos_ppmu_reset(ppmu_base); - -		/* Setup count registers to monitor read/write transactions */ -		data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; -		exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, -					data->ppmu[i].event[PPMU_PMNCNT3]); - -		exynos_ppmu_start(ppmu_base); -	} -} - -static void exynos5_read_ppmu(struct busfreq_data_int *data) -{ -	int i, j; - -	for (i = PPMU_RIGHT; i < PPMU_END; i++) { -		void __iomem *ppmu_base = data->ppmu[i].hw_base; - -		exynos_ppmu_stop(ppmu_base); - -		/* Update local data from PPMU */ -		data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); - -		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { -			if (data->ppmu[i].event[j] == 0) -				data->ppmu[i].count[j] = 0; -			else -				data->ppmu[i].count[j] = -					exynos_ppmu_read(ppmu_base, j); -		} -	} - -	busfreq_mon_reset(data); -} -  static int exynos5_int_setvolt(struct busfreq_data_int *data,  				unsigned long volt)  { @@ -132,7 +88,7 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,  	struct platform_device *pdev = container_of(dev, struct platform_device,  						    dev);  	struct busfreq_data_int *data = platform_get_drvdata(pdev); -	struct opp *opp; +	struct dev_pm_opp *opp;  	unsigned long old_freq, freq;  	unsigned long volt; @@ -144,8 +100,8 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,  		return PTR_ERR(opp);  	} -	freq = opp_get_freq(opp); -	volt = opp_get_voltage(opp); +	freq = dev_pm_opp_get_freq(opp); +	volt = dev_pm_opp_get_voltage(opp);  	rcu_read_unlock();  	old_freq = data->curr_freq; @@ -153,7 +109,7 @@ static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq,  	if (old_freq == freq)  		return 0; -	dev_dbg(dev, "targetting %lukHz %luuV\n", freq, volt); +	dev_dbg(dev, "targeting %lukHz %luuV\n", freq, volt);  	mutex_lock(&data->lock); @@ -186,59 +142,33 @@ out:  	return err;  } -static int exynos5_get_busier_dmc(struct busfreq_data_int *data) -{ -	int i, j; -	int busy = 0; -	unsigned int temp = 0; - -	for (i = PPMU_RIGHT; i < PPMU_END; i++) { -		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { -			if (data->ppmu[i].count[j] > temp) { -				temp = data->ppmu[i].count[j]; -				busy = i; -			} -		} -	} - -	return busy; -} -  static int exynos5_int_get_dev_status(struct device *dev,  				      struct devfreq_dev_status *stat)  {  	struct platform_device *pdev = container_of(dev, struct platform_device,  						    dev);  	struct busfreq_data_int *data = platform_get_drvdata(pdev); +	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;  	int busier_dmc; -	exynos5_read_ppmu(data); -	busier_dmc = exynos5_get_busier_dmc(data); +	exynos_read_ppmu(ppmu_data); +	busier_dmc = exynos_get_busier_ppmu(ppmu_data);  	stat->current_frequency = data->curr_freq;  	/* Number of cycles spent on memory access */ -	stat->busy_time = data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; +	stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3];  	stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; -	stat->total_time = data->ppmu[busier_dmc].ccnt; +	stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt;  	return 0;  } -static void exynos5_int_exit(struct device *dev) -{ -	struct platform_device *pdev = container_of(dev, struct platform_device, -						    dev); -	struct busfreq_data_int *data = platform_get_drvdata(pdev); - -	devfreq_unregister_opp_notifier(dev, data->devfreq); -}  static struct devfreq_dev_profile exynos5_devfreq_int_profile = {  	.initial_freq		= 160000,  	.polling_ms		= 100,  	.target			= exynos5_busfreq_int_target,  	.get_dev_status		= exynos5_int_get_dev_status, -	.exit			= exynos5_int_exit,  };  static int exynos5250_init_int_tables(struct busfreq_data_int *data) @@ -246,7 +176,7 @@ static int exynos5250_init_int_tables(struct busfreq_data_int *data)  	int i, err = 0;  	for (i = LV_0; i < _LV_END; i++) { -		err = opp_add(data->dev, exynos5_int_opp_table[i].clk, +		err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk,  				exynos5_int_opp_table[i].volt);  		if (err) {  			dev_err(data->dev, "Cannot add opp entries.\n"); @@ -262,7 +192,7 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,  {  	struct busfreq_data_int *data = container_of(this,  					struct busfreq_data_int, pm_notifier); -	struct opp *opp; +	struct dev_pm_opp *opp;  	unsigned long maxfreq = ULONG_MAX;  	unsigned long freq;  	unsigned long volt; @@ -276,14 +206,14 @@ static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this,  		data->disabled = true;  		rcu_read_lock(); -		opp = opp_find_freq_floor(data->dev, &maxfreq); +		opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq);  		if (IS_ERR(opp)) {  			rcu_read_unlock();  			err = PTR_ERR(opp);  			goto unlock;  		} -		freq = opp_get_freq(opp); -		volt = opp_get_voltage(opp); +		freq = dev_pm_opp_get_freq(opp); +		volt = dev_pm_opp_get_voltage(opp);  		rcu_read_unlock();  		err = exynos5_int_setvolt(data, volt); @@ -316,7 +246,8 @@ unlock:  static int exynos5_busfreq_int_probe(struct platform_device *pdev)  {  	struct busfreq_data_int *data; -	struct opp *opp; +	struct busfreq_ppmu_data *ppmu_data; +	struct dev_pm_opp *opp;  	struct device *dev = &pdev->dev;  	struct device_node *np;  	unsigned long initial_freq; @@ -331,16 +262,26 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)  		return -ENOMEM;  	} +	ppmu_data = &data->ppmu_data; +	ppmu_data->ppmu_end = PPMU_END; +	ppmu_data->ppmu = devm_kzalloc(dev, +				       sizeof(struct exynos_ppmu) * PPMU_END, +				       GFP_KERNEL); +	if (!ppmu_data->ppmu) { +		dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); +		return -ENOMEM; +	} +  	np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");  	if (np == NULL) {  		pr_err("Unable to find PPMU node\n");  		return -ENOENT;  	} -	for (i = PPMU_RIGHT; i < PPMU_END; i++) { +	for (i = 0; i < ppmu_data->ppmu_end; i++) {  		/* map PPMU memory region */ -		data->ppmu[i].hw_base = of_iomap(np, i); -		if (data->ppmu[i].hw_base == NULL) { +		ppmu_data->ppmu[i].hw_base = of_iomap(np, i); +		if (ppmu_data->ppmu[i].hw_base == NULL) {  			dev_err(&pdev->dev, "failed to map memory region\n");  			return -ENOMEM;  		} @@ -351,81 +292,69 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)  	err = exynos5250_init_int_tables(data);  	if (err) -		goto err_regulator; +		return err; -	data->vdd_int = regulator_get(dev, "vdd_int"); +	data->vdd_int = devm_regulator_get(dev, "vdd_int");  	if (IS_ERR(data->vdd_int)) {  		dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); -		err = PTR_ERR(data->vdd_int); -		goto err_regulator; +		return PTR_ERR(data->vdd_int);  	} -	data->int_clk = clk_get(dev, "int_clk"); +	data->int_clk = devm_clk_get(dev, "int_clk");  	if (IS_ERR(data->int_clk)) {  		dev_err(dev, "Cannot get clock \"int_clk\"\n"); -		err = PTR_ERR(data->int_clk); -		goto err_clock; +		return PTR_ERR(data->int_clk);  	}  	rcu_read_lock(); -	opp = opp_find_freq_floor(dev, +	opp = dev_pm_opp_find_freq_floor(dev,  			&exynos5_devfreq_int_profile.initial_freq);  	if (IS_ERR(opp)) {  		rcu_read_unlock();  		dev_err(dev, "Invalid initial frequency %lu kHz.\n",  		       exynos5_devfreq_int_profile.initial_freq); -		err = PTR_ERR(opp); -		goto err_opp_add; +		return PTR_ERR(opp);  	} -	initial_freq = opp_get_freq(opp); -	initial_volt = opp_get_voltage(opp); +	initial_freq = dev_pm_opp_get_freq(opp); +	initial_volt = dev_pm_opp_get_voltage(opp);  	rcu_read_unlock();  	data->curr_freq = initial_freq;  	err = clk_set_rate(data->int_clk, initial_freq * 1000);  	if (err) {  		dev_err(dev, "Failed to set initial frequency\n"); -		goto err_opp_add; +		return err;  	}  	err = exynos5_int_setvolt(data, initial_volt);  	if (err) -		goto err_opp_add; +		return err;  	platform_set_drvdata(pdev, data); -	busfreq_mon_reset(data); +	busfreq_mon_reset(ppmu_data); -	data->devfreq = devfreq_add_device(dev, &exynos5_devfreq_int_profile, +	data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,  					   "simple_ondemand", NULL); +	if (IS_ERR(data->devfreq)) +		return PTR_ERR(data->devfreq); -	if (IS_ERR(data->devfreq)) { -		err = PTR_ERR(data->devfreq); -		goto err_devfreq_add; +	err = devm_devfreq_register_opp_notifier(dev, data->devfreq); +	if (err < 0) { +		dev_err(dev, "Failed to register opp notifier\n"); +		return err;  	} -	devfreq_register_opp_notifier(dev, data->devfreq); -  	err = register_pm_notifier(&data->pm_notifier);  	if (err) {  		dev_err(dev, "Failed to setup pm notifier\n"); -		goto err_devfreq_add; +		return err;  	}  	/* TODO: Add a new QOS class for int/mif bus */  	pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);  	return 0; - -err_devfreq_add: -	devfreq_remove_device(data->devfreq); -	platform_set_drvdata(pdev, NULL); -err_opp_add: -	clk_put(data->int_clk); -err_clock: -	regulator_put(data->vdd_int); -err_regulator: -	return err;  }  static int exynos5_busfreq_int_remove(struct platform_device *pdev) @@ -434,27 +363,27 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev)  	pm_qos_remove_request(&data->int_req);  	unregister_pm_notifier(&data->pm_notifier); -	devfreq_remove_device(data->devfreq); -	regulator_put(data->vdd_int); -	clk_put(data->int_clk); -	platform_set_drvdata(pdev, NULL);  	return 0;  } +#ifdef CONFIG_PM_SLEEP  static int exynos5_busfreq_int_resume(struct device *dev)  {  	struct platform_device *pdev = container_of(dev, struct platform_device,  						    dev);  	struct busfreq_data_int *data = platform_get_drvdata(pdev); +	struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; -	busfreq_mon_reset(data); +	busfreq_mon_reset(ppmu_data);  	return 0;  } -  static const struct dev_pm_ops exynos5_busfreq_int_pm = {  	.resume	= exynos5_busfreq_int_resume,  }; +#endif +static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL, +			 exynos5_busfreq_int_resume);  /* platform device pointer for exynos5 devfreq device. */  static struct platform_device *exynos5_devfreq_pdev; @@ -465,7 +394,7 @@ static struct platform_driver exynos5_busfreq_int_driver = {  	.driver		= {  		.name		= "exynos5-bus-int",  		.owner		= THIS_MODULE, -		.pm		= &exynos5_busfreq_int_pm, +		.pm		= &exynos5_busfreq_int_pm_ops,  	},  }; @@ -479,7 +408,7 @@ static int __init exynos5_busfreq_int_init(void)  	exynos5_devfreq_pdev =  		platform_device_register_simple("exynos5-bus-int", -1, NULL, 0); -	if (IS_ERR_OR_NULL(exynos5_devfreq_pdev)) { +	if (IS_ERR(exynos5_devfreq_pdev)) {  		ret = PTR_ERR(exynos5_devfreq_pdev);  		goto out1;  	} diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c index 85fc5ac1036..75fcc5140ff 100644 --- a/drivers/devfreq/exynos/exynos_ppmu.c +++ b/drivers/devfreq/exynos/exynos_ppmu.c @@ -54,3 +54,63 @@ unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch)  	return total;  } + +void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data) +{ +	unsigned int i; + +	for (i = 0; i < ppmu_data->ppmu_end; i++) { +		void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; + +		/* Reset the performance and cycle counters */ +		exynos_ppmu_reset(ppmu_base); + +		/* Setup count registers to monitor read/write transactions */ +		ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; +		exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, +					ppmu_data->ppmu[i].event[PPMU_PMNCNT3]); + +		exynos_ppmu_start(ppmu_base); +	} +} + +void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data) +{ +	int i, j; + +	for (i = 0; i < ppmu_data->ppmu_end; i++) { +		void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; + +		exynos_ppmu_stop(ppmu_base); + +		/* Update local data from PPMU */ +		ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); + +		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { +			if (ppmu_data->ppmu[i].event[j] == 0) +				ppmu_data->ppmu[i].count[j] = 0; +			else +				ppmu_data->ppmu[i].count[j] = +					exynos_ppmu_read(ppmu_base, j); +		} +	} + +	busfreq_mon_reset(ppmu_data); +} + +int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data) +{ +	unsigned int count = 0; +	int i, j, busy = 0; + +	for (i = 0; i < ppmu_data->ppmu_end; i++) { +		for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { +			if (ppmu_data->ppmu[i].count[j] > count) { +				count = ppmu_data->ppmu[i].count[j]; +				busy = i; +			} +		} +	} + +	return busy; +} diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h index 7dfb221eacc..71f17ba3563 100644 --- a/drivers/devfreq/exynos/exynos_ppmu.h +++ b/drivers/devfreq/exynos/exynos_ppmu.h @@ -69,10 +69,18 @@ struct exynos_ppmu {  	bool count_overflow[PPMU_PMNCNT_MAX];  }; +struct busfreq_ppmu_data { +	struct exynos_ppmu *ppmu; +	int ppmu_end; +}; +  void exynos_ppmu_reset(void __iomem *ppmu_base);  void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,  			unsigned int evt);  void exynos_ppmu_start(void __iomem *ppmu_base);  void exynos_ppmu_stop(void __iomem *ppmu_base);  unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); +void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data); +void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data); +int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data);  #endif /* __DEVFREQ_EXYNOS_PPMU_H */  | 
