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 */ |
