PM / devfreq : Make exynos4 busfreq driver to use PPMU not DMC internal one.
authorJonghwa Lee <jonghwa3.lee@samsung.com>
Thu, 15 May 2014 09:42:34 +0000 (18:42 +0900)
committerChanho Park <chanho61.park@samsung.com>
Tue, 18 Nov 2014 02:47:52 +0000 (11:47 +0900)
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>
arch/arm/boot/dts/exynos4x12.dtsi
drivers/devfreq/exynos/Makefile
drivers/devfreq/exynos/exynos4_bus.c
drivers/devfreq/exynos/exynos_ppmu.h

index b3c105b..190dc0f 100644 (file)
 
        busfreq@0 {
                compatible = "samsung,exynos4x12-busfreq";
+               reg = <0x106A0000 0x2000>, <0x106B0000 0x2000>;
+               clocks = <&clock 414>, <&clock 415>;
+               clock-names = "ppmudmc0", "ppmudmc1";
                status = "disabled";
        };
 };
index bfaaf5b..49bc917 100644 (file)
@@ -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
index d3e9d9a..2e7bce2 100644 (file)
@@ -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);
index 7dfb221..16c81ad 100644 (file)
 
 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;