diff options
-rw-r--r-- | arch/arm/boot/dts/exynos4x12.dtsi | 3 | ||||
-rw-r--r-- | drivers/devfreq/exynos/Makefile | 2 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.c | 198 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.h | 8 |
4 files changed, 107 insertions, 104 deletions
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index b3c105b1f74..474a9fca111 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -359,6 +359,9 @@ busfreq@0 { compatible = "samsung,exynos4x12-busfreq"; + reg = <0x106A0000 0x2000>, <0x106B0000 0x2000>, + clocks = <&clock 414>, <&clock 415>; + clock-names = "ppmudmc0", "ppmudmc1"; status = "disabled"; }; }; 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 d3e9d9ab005..2e7bce22412 100644 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ b/drivers/devfreq/exynos/exynos4_bus.c @@ -25,6 +25,9 @@ #include <linux/regulator/consumer.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/clk.h> + +#include "exynos_ppmu.h" /* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ #ifdef CONFIG_EXYNOS_ASV @@ -33,8 +36,6 @@ extern unsigned int exynos_result_of_asv; #include <mach/regs-clock.h> -#include <plat/map-s5p.h> - #define MAX_SAFEVOLT 1200000 /* 1.2V */ enum exynos4_busf_type { @@ -45,22 +46,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, @@ -92,7 +77,8 @@ 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 exynos_ppmu ppmu[PPMU_CNT]; + struct clk *clk_ppmu[PPMU_CNT]; struct notifier_block pm_notifier; struct mutex lock; @@ -102,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}, @@ -528,24 +508,25 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, static void busfreq_mon_reset(struct busfreq_data *data) { - unsigned int i; + int i, j; - for (i = 0; i < 2; i++) { - void __iomem *ppmu_base = data->dmc[i].hw_base; + for (i = 0; i < PPMU_CNT; i++) { + void __iomem *ppmu_base = data->ppmu[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); + exynos_ppmu_reset(ppmu_base); /* Set PPMU Event */ - data->dmc[i].event = 0x6; - __raw_writel(((data->dmc[i].event << 12) | 0x1), - ppmu_base + 0xfc); + for (j = 0; j < PPMU_PMNCNT_MAX; j++) { + if (data->ppmu[i].event[j] < 0) + continue; + + exynos_ppmu_setevent(ppmu_base, j, + data->ppmu[i].event[j]); + } /* Start PPMU */ - __raw_writel(0x1, ppmu_base + 0xf000); + exynos_ppmu_start(ppmu_base); } } @@ -553,23 +534,23 @@ 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; + for (i = 0; i < PPMU_CNT; i++) { + void __iomem *ppmu_base = data->ppmu[i].hw_base; u32 overflow; - /* Stop PPMU */ - __raw_writel(0x0, ppmu_base + 0xf000); + exynos_ppmu_stop(ppmu_base); /* Update local data from PPMU */ - overflow = __raw_readl(ppmu_base + 0xf050); + overflow = __raw_readl(ppmu_base + PPMU_FLAG); - data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100); - data->dmc[i].ccnt_overflow = overflow & (1 << 31); + data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); + data->ppmu[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); + if (data->ppmu[i].event[j] < 0) + data->ppmu[i].count[j] = 0; + data->ppmu[i].count[j] = exynos_ppmu_read(ppmu_base, j); + data->ppmu[i].count_overflow[j] = overflow & (1 << j); } } @@ -699,67 +680,31 @@ 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; + int i, j, _busy, _total; exynos4_read_ppmu(data); - busier_dmc = exynos4_get_busier_dmc(data); - stat->current_frequency = data->curr_oppinfo.rate; - 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; + for (i = 0, _busy = 0, _total = 0; i < PPMU_CNT; i++) { + for (j = 0; j < PPMU_PMNCNT_MAX; j++) { + if (data->ppmu[i].count[j] > _busy) { + /* If the counters have overflown, retry */ + if (data->ppmu[i].ccnt_overflow) + return -EAGAIN; + + _busy = data->ppmu[i].count[j]; + _total = data->ppmu[i].ccnt; + } + } } - /* Number of cycles spent on memory access */ - stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2); + stat->busy_time = _busy; stat->busy_time *= 100 / BUS_SATURATION_RATIO; - stat->total_time = data->dmc[busier_dmc].ccnt; - - /* If the counters have overflown, retry */ - if (data->dmc[busier_dmc].ccnt_overflow || - data->dmc[busier_dmc].count_overflow[0]) - return -EAGAIN; + stat->total_time = _total; + stat->current_frequency = data->curr_oppinfo.rate; return 0; } @@ -1039,9 +984,9 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) struct busfreq_data *data; struct opp *opp; struct device *dev = &pdev->dev; - int type, err = 0; + int i, type, err = 0; - data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct busfreq_data), GFP_KERNEL); if (data == NULL) { dev_err(dev, "Cannot allocate memory.\n"); return -ENOMEM; @@ -1054,10 +999,33 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) } else { type = pdev->id_entry->driver_data; } - data->type = type; - data->dmc[0].hw_base = S5P_VA_DMC0; - data->dmc[1].hw_base = S5P_VA_DMC1; + + /* Initialize PPMU data */ + for (i = 0; i < PPMU_CNT; i++) { + struct resource *res; + + /* Allocating iomap for PPMU SFRs */ + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(dev, "No mem resource for ppmu[%d]\n", i); + return -ENOENT; + } + data->ppmu[i].hw_base = devm_ioremap_resource(dev, res); + if (IS_ERR(data->ppmu[i].hw_base)) + return PTR_ERR(data->ppmu[i].hw_base); + + /* + * TODO: + * PPMU's event would be set through board specific data. + * Now, only use 4th PPC to check read/write data count. + */ + data->ppmu[i].event[PPMU_PMNCNT0] = -1; + data->ppmu[i].event[PPMU_PMNCNT1] = -1; + data->ppmu[i].event[PPMU_PMNCNT2] = -1; + data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; + } + data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; data->dev = dev; @@ -1090,6 +1058,28 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) } } + data->clk_ppmu[PPMU_DMC0] = devm_clk_get(dev, "ppmudmc0"); + if (IS_ERR(data->clk_ppmu[PPMU_DMC0])) { + dev_err(dev, "Cannot get ppmudmc0 clock\n"); + return PTR_ERR(data->clk_ppmu[PPMU_DMC0]); + } + err = clk_prepare_enable(data->clk_ppmu[PPMU_DMC0]); + if (err) { + dev_err(dev, "Cannot enable ppmudmc0 clock\n"); + return err; + } + + data->clk_ppmu[PPMU_DMC1] = devm_clk_get(dev, "ppmudmc1"); + if (IS_ERR(data->clk_ppmu[PPMU_DMC1])) { + dev_err(dev, "Cannot get ppmudmc1 clock\n"); + return PTR_ERR(data->clk_ppmu[PPMU_DMC1]); + } + err = clk_prepare_enable(data->clk_ppmu[PPMU_DMC1]); + if (err) { + dev_err(dev, "Cannot enable ppmudmc0 clock\n"); + return err; + } + rcu_read_lock(); opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); if (IS_ERR(opp)) { @@ -1126,6 +1116,10 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) static int exynos4_busfreq_remove(struct platform_device *pdev) { struct busfreq_data *data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < PPMU_CNT; i++) + clk_disable_unprepare(data->clk_ppmu[i]); unregister_pm_notifier(&data->pm_notifier); devfreq_remove_device(data->devfreq); diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h index 7dfb221eacc..16c81ad572f 100644 --- a/drivers/devfreq/exynos/exynos_ppmu.h +++ b/drivers/devfreq/exynos/exynos_ppmu.h @@ -46,12 +46,18 @@ enum ppmu_counter { PPMU_PMNCNT0, - PPMU_PMCCNT1, + PPMU_PMNCNT1, PPMU_PMNCNT2, PPMU_PMNCNT3, PPMU_PMNCNT_MAX, }; +enum ppmu_type { + PPMU_DMC0, + PPMU_DMC1, + PPMU_CNT, +}; + struct bus_opp_table { unsigned int idx; unsigned long clk; |