summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonghwa Lee <jonghwa3.lee@samsung.com>2014-05-15 18:42:34 +0900
committerJonghwa Lee <jonghwa3.lee@samsung.com>2014-05-15 18:59:12 +0900
commit203fe6a999c494a18cfba432d6b43465f790fffb (patch)
tree8637a476b1451c5c2d161a2c5c4132dea3b91127
parent1852bb82f00e2828ac0d713024f29d6bbcc88c31 (diff)
downloadlinux-3.10-203fe6a999c494a18cfba432d6b43465f790fffb.tar.gz
linux-3.10-203fe6a999c494a18cfba432d6b43465f790fffb.tar.bz2
linux-3.10-203fe6a999c494a18cfba432d6b43465f790fffb.zip
PM / devfreq : Make exynos4 busfreq driver to use PPMU not DMC internal one.sandbox/cosmichobo/tizen
This patch modifies exynos4 busfreq driver to use global PPMU not DMC block's internal PMU. The PPMU has different event set with DMC's and it'll be more adaquate to profile bus utilization. Change-Id: Ia5f6801cf6081edf5c76cad2af86978843bdf91a Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
-rw-r--r--arch/arm/boot/dts/exynos4x12.dtsi3
-rw-r--r--drivers/devfreq/exynos/Makefile2
-rw-r--r--drivers/devfreq/exynos/exynos4_bus.c198
-rw-r--r--drivers/devfreq/exynos/exynos_ppmu.h8
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;