summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;