summaryrefslogtreecommitdiff
path: root/drivers/devfreq
diff options
context:
space:
mode:
authorJonghwa Lee <jonghwa3.lee@samsung.com>2014-05-15 18:42:34 +0900
committerChanho Park <chanho61.park@samsung.com>2014-11-18 11:47:52 +0900
commit10d929a26aa3846901d4c15bee8ee823d930a4f2 (patch)
tree6d98c18d1aafdb1200d023b63191bc3c16e2ac18 /drivers/devfreq
parent173fb994bc2d234f1b6dafcf62855c94ecb5c398 (diff)
downloadlinux-3.10-10d929a26aa3846901d4c15bee8ee823d930a4f2.tar.gz
linux-3.10-10d929a26aa3846901d4c15bee8ee823d930a4f2.tar.bz2
linux-3.10-10d929a26aa3846901d4c15bee8ee823d930a4f2.zip
PM / devfreq : Make exynos4 busfreq driver to use PPMU not DMC internal one.
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>
Diffstat (limited to 'drivers/devfreq')
-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
3 files changed, 104 insertions, 104 deletions
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;