Commit ba778b37 authored by Chanwoo Choi's avatar Chanwoo Choi Committed by MyungJoo Ham

PM / devfreq: exynos4: use common PPMU code

This patch converts exynos4_bus driver to use common PPMU code
(exynos_ppmu.c) instead of individual functions related to PPC
because PPMU is integrated module with both PPC and Bus event
generator.  When using PPMU to get bus performance read/write
event exynos4_bus driver deson't need to consider memory type.
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
[bzolnier: splitted out changes from the bigger patch]
Signed-off-by: default avatarBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: default avatarMyungJoo Ham <myungjoo.ham@samsung.com>
parent ae29fa1d
# Exynos DEVFREQ Drivers # 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 obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o
...@@ -25,15 +25,16 @@ ...@@ -25,15 +25,16 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/module.h> #include <linux/module.h>
#include <mach/map.h>
#include "exynos_ppmu.h"
#include "exynos4_bus.h"
/* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ /* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */
#ifdef CONFIG_EXYNOS_ASV #ifdef CONFIG_EXYNOS_ASV
extern unsigned int exynos_result_of_asv; extern unsigned int exynos_result_of_asv;
#endif #endif
#include <mach/map.h>
#include "exynos4_bus.h"
#define MAX_SAFEVOLT 1200000 /* 1.2V */ #define MAX_SAFEVOLT 1200000 /* 1.2V */
enum exynos4_busf_type { enum exynos4_busf_type {
...@@ -44,22 +45,6 @@ enum exynos4_busf_type { ...@@ -44,22 +45,6 @@ enum exynos4_busf_type {
/* Assume that the bus is saturated if the utilization is 40% */ /* Assume that the bus is saturated if the utilization is 40% */
#define BUS_SATURATION_RATIO 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 { enum busclk_level_idx {
LV_0 = 0, LV_0 = 0,
LV_1, LV_1,
...@@ -68,6 +53,13 @@ enum busclk_level_idx { ...@@ -68,6 +53,13 @@ enum busclk_level_idx {
LV_4, LV_4,
_LV_END _LV_END
}; };
enum exynos_ppmu_idx {
PPMU_DMC0,
PPMU_DMC1,
PPMU_END,
};
#define EX4210_LV_MAX LV_2 #define EX4210_LV_MAX LV_2
#define EX4x12_LV_MAX LV_4 #define EX4x12_LV_MAX LV_4
#define EX4210_LV_NUM (LV_2 + 1) #define EX4210_LV_NUM (LV_2 + 1)
...@@ -91,7 +83,7 @@ struct busfreq_data { ...@@ -91,7 +83,7 @@ struct busfreq_data {
struct regulator *vdd_int; struct regulator *vdd_int;
struct regulator *vdd_mif; /* Exynos4412/4212 only */ struct regulator *vdd_mif; /* Exynos4412/4212 only */
struct busfreq_opp_info curr_oppinfo; struct busfreq_opp_info curr_oppinfo;
struct exynos4_ppmu dmc[2]; struct exynos_ppmu ppmu[PPMU_END];
struct notifier_block pm_notifier; struct notifier_block pm_notifier;
struct mutex lock; struct mutex lock;
...@@ -101,12 +93,6 @@ struct busfreq_data { ...@@ -101,12 +93,6 @@ struct busfreq_data {
unsigned int top_divtable[_LV_END]; 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 */ /* 4210 controls clock of mif and voltage of int */
static struct bus_opp_table exynos4210_busclk_table[] = { static struct bus_opp_table exynos4210_busclk_table[] = {
{LV_0, 400000, 1150000}, {LV_0, 400000, 1150000},
...@@ -524,27 +510,22 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, ...@@ -524,27 +510,22 @@ static int exynos4x12_set_busclk(struct busfreq_data *data,
return 0; return 0;
} }
static void busfreq_mon_reset(struct busfreq_data *data) static void busfreq_mon_reset(struct busfreq_data *data)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < 2; i++) { for (i = 0; i < PPMU_END; i++) {
void __iomem *ppmu_base = data->dmc[i].hw_base; void __iomem *ppmu_base = data->ppmu[i].hw_base;
/* Reset PPMU */ /* Reset the performance and cycle counters */
__raw_writel(0x8000000f, ppmu_base + 0xf010); exynos_ppmu_reset(ppmu_base);
__raw_writel(0x8000000f, ppmu_base + 0xf050);
__raw_writel(0x6, ppmu_base + 0xf000);
__raw_writel(0x0, ppmu_base + 0xf100);
/* Set PPMU Event */ /* Setup count registers to monitor read/write transactions */
data->dmc[i].event = 0x6; data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
__raw_writel(((data->dmc[i].event << 12) | 0x1), exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
ppmu_base + 0xfc); data->ppmu[i].event[PPMU_PMNCNT3]);
/* Start PPMU */ exynos_ppmu_start(ppmu_base);
__raw_writel(0x1, ppmu_base + 0xf000);
} }
} }
...@@ -552,23 +533,20 @@ static void exynos4_read_ppmu(struct busfreq_data *data) ...@@ -552,23 +533,20 @@ static void exynos4_read_ppmu(struct busfreq_data *data)
{ {
int i, j; int i, j;
for (i = 0; i < 2; i++) { for (i = 0; i < PPMU_END; i++) {
void __iomem *ppmu_base = data->dmc[i].hw_base; void __iomem *ppmu_base = data->ppmu[i].hw_base;
u32 overflow;
/* Stop PPMU */ exynos_ppmu_stop(ppmu_base);
__raw_writel(0x0, ppmu_base + 0xf000);
/* Update local data from PPMU */ /* Update local data from PPMU */
overflow = __raw_readl(ppmu_base + 0xf050); data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100); for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
data->dmc[i].ccnt_overflow = overflow & (1 << 31); if (data->ppmu[i].event[j] == 0)
data->ppmu[i].count[j] = 0;
for (j = 0; j < PPMU_PMNCNT_MAX; j++) { else
data->dmc[i].count[j] = __raw_readl( data->ppmu[i].count[j] =
ppmu_base + (0xf110 + (0x10 * j))); exynos_ppmu_read(ppmu_base, j);
data->dmc[i].count_overflow[j] = overflow & (1 << j);
} }
} }
...@@ -698,66 +676,42 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, ...@@ -698,66 +676,42 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
return err; return err;
} }
static int exynos4_get_busier_dmc(struct busfreq_data *data) static int exynos4_get_busier_ppmu(struct busfreq_data *data)
{ {
u64 p0 = data->dmc[0].count[0]; int i, j;
u64 p1 = data->dmc[1].count[0]; int busy = 0;
unsigned int temp = 0;
p0 *= data->dmc[1].ccnt;
p1 *= data->dmc[0].ccnt; for (i = 0; i < PPMU_END; i++) {
for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
if (data->dmc[1].ccnt == 0) if (data->ppmu[i].count[j] > temp) {
return 0; temp = data->ppmu[i].count[j];
busy = i;
}
}
}
if (p0 > p1) return busy;
return 0;
return 1;
} }
static int exynos4_bus_get_dev_status(struct device *dev, static int exynos4_bus_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat) struct devfreq_dev_status *stat)
{ {
struct busfreq_data *data = dev_get_drvdata(dev); struct busfreq_data *data = dev_get_drvdata(dev);
int busier_dmc; int busier;
int cycles_x2 = 2; /* 2 x cycles */
void __iomem *addr;
u32 timing;
u32 memctrl;
exynos4_read_ppmu(data); exynos4_read_ppmu(data);
busier_dmc = exynos4_get_busier_dmc(data); busier = exynos4_get_busier_ppmu(data);
stat->current_frequency = data->curr_oppinfo.rate; 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;
}
/* Number of cycles spent on memory access */ /* Number of cycles spent on memory access */
stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2); stat->busy_time = data->ppmu[busier].count[PPMU_PMNCNT3];
stat->busy_time *= 100 / BUS_SATURATION_RATIO; stat->busy_time *= 100 / BUS_SATURATION_RATIO;
stat->total_time = data->dmc[busier_dmc].ccnt; stat->total_time = data->ppmu[busier].ccnt;
/* If the counters have overflown, retry */ /* If the counters have overflown, retry */
if (data->dmc[busier_dmc].ccnt_overflow || if (data->ppmu[busier].ccnt_overflow ||
data->dmc[busier_dmc].count_overflow[0]) data->ppmu[busier].count_overflow[0])
return -EAGAIN; return -EAGAIN;
return 0; return 0;
...@@ -1023,8 +977,8 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) ...@@ -1023,8 +977,8 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
} }
data->type = pdev->id_entry->driver_data; data->type = pdev->id_entry->driver_data;
data->dmc[0].hw_base = S5P_VA_DMC0; data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0;
data->dmc[1].hw_base = S5P_VA_DMC1; data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1;
data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;
data->dev = dev; data->dev = dev;
mutex_init(&data->lock); mutex_init(&data->lock);
...@@ -1074,13 +1028,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) ...@@ -1074,13 +1028,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
busfreq_mon_reset(data);
data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
"simple_ondemand", NULL); "simple_ondemand", NULL);
if (IS_ERR(data->devfreq)) if (IS_ERR(data->devfreq))
return PTR_ERR(data->devfreq); return PTR_ERR(data->devfreq);
/*
* Start PPMU (Performance Profiling Monitoring Unit) to check
* utilization of each IP in the Exynos4 SoC.
*/
busfreq_mon_reset(data);
/* Register opp_notifier for Exynos4 busfreq */ /* Register opp_notifier for Exynos4 busfreq */
err = devfreq_register_opp_notifier(dev, data->devfreq); err = devfreq_register_opp_notifier(dev, data->devfreq);
if (err < 0) { if (err < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment