- Setup the INT clock ops to control/vary INT frequency
- Add PPMU support for Exynos5250
- Add mappings initially for the PPMU device

Signed-off-by: Abhilash Kesavan <[email protected]>
---
Changes since RFC v1:
* Fixed the unnecessary clock manipulations being done
* Moved the PPMU driver from drivers/devfreq to machine specific directory

 arch/arm/mach-exynos/Kconfig                     |   8 +
 arch/arm/mach-exynos/Makefile                    |   4 +
 arch/arm/mach-exynos/clock-exynos5.c             | 143 ++++++++
 arch/arm/mach-exynos/common.c                    |  25 ++
 arch/arm/mach-exynos/exynos5_ppmu.c              | 396 +++++++++++++++++++++++
 arch/arm/mach-exynos/exynos_ppmu.c               |  56 ++++
 arch/arm/mach-exynos/include/mach/exynos5_ppmu.h |  26 ++
 arch/arm/mach-exynos/include/mach/exynos_ppmu.h  |  79 +++++
 arch/arm/mach-exynos/include/mach/map.h          |   6 +
 arch/arm/mach-exynos/include/mach/regs-clock.h   |  37 +++
 arch/arm/plat-samsung/include/plat/map-s5p.h     |   6 +
 11 files changed, 786 insertions(+)
 create mode 100644 arch/arm/mach-exynos/exynos5_ppmu.c
 create mode 100644 arch/arm/mach-exynos/exynos_ppmu.c
 create mode 100644 arch/arm/mach-exynos/include/mach/exynos5_ppmu.h
 create mode 100644 arch/arm/mach-exynos/include/mach/exynos_ppmu.h

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 91d5b6f..38bde0e 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -179,6 +179,14 @@ config EXYNOS_SETUP_SPI
        help
          Common setup code for SPI GPIO configurations.
 
+config EXYNOS5250_PPMU
+       bool "Exynos5250 PPMU Driver"
+        depends on SOC_EXYNOS5250
+        help
+          This adds the Performance Profiling Monitoring Unit (PPMU) support
+         for Exynos5250. This code is used by the devfreq driver to read the
+         PPMU counters and vary the INT bus frequency/voltage.
+
 # machine support
 
 if ARCH_EXYNOS4
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 7e53a3a..b0b4cc9 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -74,3 +74,7 @@ obj-$(CONFIG_EXYNOS4_SETUP_KEYPAD)    += setup-keypad.o
 obj-$(CONFIG_EXYNOS4_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
 obj-$(CONFIG_EXYNOS4_SETUP_USB_PHY)    += setup-usb-phy.o
 obj-$(CONFIG_EXYNOS_SETUP_SPI)         += setup-spi.o
+
+# ppmu support
+
+obj-$(CONFIG_EXYNOS5250_PPMU)          += exynos_ppmu.o exynos5_ppmu.o
diff --git a/arch/arm/mach-exynos/clock-exynos5.c 
b/arch/arm/mach-exynos/clock-exynos5.c
index 0208c3a..050879c 100644
--- a/arch/arm/mach-exynos/clock-exynos5.c
+++ b/arch/arm/mach-exynos/clock-exynos5.c
@@ -108,6 +108,11 @@ static struct clk exynos5_clk_sclk_usbphy = {
        .rate           = 48000000,
 };
 
+/* Virtual Bus INT clock */
+static struct clk exynos5_int_clk = {
+       .name           = "int_clk",
+};
+
 static int exynos5_clksrc_mask_top_ctrl(struct clk *clk, int enable)
 {
        return s5p_gatectrl(EXYNOS5_CLKSRC_MASK_TOP, clk, enable);
@@ -1426,6 +1431,141 @@ static struct clk *exynos5_clks[] __initdata = {
        &clk_fout_cpll,
        &clk_fout_mpll_div2,
        &exynos5_clk_armclk,
+       &exynos5_int_clk,
+};
+
+#define INT_FREQ(f, a0, a1, a2, a3, a4, a5, b0, b1, b2, b3, \
+                       c0, c1, d0, e0) \
+       { \
+               .freq = (f) * 1000000, \
+               .clk_div_top0 = ((a0) << 0 | (a1) << 8 | (a2) << 12 | \
+                               (a3) << 16 | (a4) << 20 | (a5) << 28), \
+               .clk_div_top1 = ((b0) << 12 | (b1) << 16 | (b2) << 20 | \
+                               (b3) << 24), \
+               .clk_div_lex = ((c0) << 4 | (c1) << 8), \
+               .clk_div_r0x = ((d0) << 4), \
+               .clk_div_r1x = ((e0) << 4), \
+       }
+
+static struct {
+       unsigned long freq;
+       u32 clk_div_top0;
+       u32 clk_div_top1;
+       u32 clk_div_lex;
+       u32 clk_div_r0x;
+       u32 clk_div_r1x;
+} int_freq[] = {
+       /*
+        * values:
+        * freq
+        * clock divider for ACLK66, ACLK166, ACLK200, ACLK266,
+                       ACLK333, ACLK300_DISP1
+        * clock divider for ACLK300_GSCL, ACLK400_IOP, ACLK400_ISP, ACLK66_PRE
+        * clock divider for PCLK_LEX, ATCLK_LEX
+        * clock divider for ACLK_PR0X
+        * clock divider for ACLK_PR1X
+        */
+       INT_FREQ(266, 1, 1, 3, 2, 0, 0, 0, 1, 1, 5, 1, 0, 1, 1),
+       INT_FREQ(200, 1, 2, 4, 3, 1, 0, 0, 3, 2, 5, 1, 0, 1, 1),
+       INT_FREQ(160, 1, 3, 4, 4, 2, 0, 0, 3, 3, 5, 1, 0, 1, 1),
+       INT_FREQ(133, 1, 3, 5, 5, 2, 1, 1, 4, 4, 5, 1, 0, 1, 1),
+       INT_FREQ(100, 1, 7, 7, 7, 7, 3, 7, 7, 7, 5, 1, 0, 1, 1),
+};
+
+static unsigned long exynos5_clk_int_get_rate(struct clk *clk)
+{
+       return clk->rate;
+}
+
+static void exynos5_int_set_clkdiv(unsigned int div_index)
+{
+       unsigned int tmp;
+
+       /* Change Divider - TOP0 */
+       tmp = __raw_readl(EXYNOS5_CLKDIV_TOP0);
+
+       tmp &= ~(EXYNOS5_CLKDIV_TOP0_ACLK266_MASK |
+               EXYNOS5_CLKDIV_TOP0_ACLK200_MASK |
+               EXYNOS5_CLKDIV_TOP0_ACLK66_MASK |
+               EXYNOS5_CLKDIV_TOP0_ACLK333_MASK |
+               EXYNOS5_CLKDIV_TOP0_ACLK166_MASK |
+               EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_MASK);
+
+       tmp |= int_freq[div_index].clk_div_top0;
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_TOP0);
+
+       /* Wait for TOP0 divider to stabilize */
+       while (__raw_readl(EXYNOS5_CLKDIV_STAT_TOP0) & 0x151101)
+               cpu_relax();
+
+       /* Change Divider - TOP1 */
+       tmp = __raw_readl(EXYNOS5_CLKDIV_TOP1);
+
+       tmp &= ~(EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_MASK |
+               EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_MASK |
+               EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_MASK |
+               EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_MASK);
+
+       tmp |= int_freq[div_index].clk_div_top1;
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_TOP1);
+
+       /* Wait for TOP0 and TOP1 dividers to stabilize */
+       while ((__raw_readl(EXYNOS5_CLKDIV_STAT_TOP1) & 0x1110000) &&
+               (__raw_readl(EXYNOS5_CLKDIV_STAT_TOP0) & 0x80000))
+               cpu_relax();
+
+       /* Change Divider - LEX */
+       tmp = int_freq[div_index].clk_div_lex;
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_LEX);
+
+       /* Wait for LEX divider to stabilize */
+       while (__raw_readl(EXYNOS5_CLKDIV_STAT_LEX) & 0x110)
+               cpu_relax();
+
+       /* Change Divider - R0X */
+       tmp = int_freq[div_index].clk_div_r0x;
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_R0X);
+
+       /* Wait for R0X divider to stabilize */
+       while (__raw_readl(EXYNOS5_CLKDIV_STAT_R0X) & 0x10)
+               cpu_relax();
+
+       /* Change Divider - R1X */
+       tmp = int_freq[div_index].clk_div_r1x;
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_R1X);
+
+       /* Wait for R1X divider to stabilize */
+       while (__raw_readl(EXYNOS5_CLKDIV_STAT_R1X) & 0x10)
+               cpu_relax();
+}
+
+static int exynos5_clk_int_set_rate(struct clk *clk, unsigned long rate)
+{
+       int index;
+
+       for (index = 0; index < ARRAY_SIZE(int_freq); index++)
+               if (int_freq[index].freq == rate)
+                       break;
+
+       if (index == ARRAY_SIZE(int_freq))
+               return -EINVAL;
+
+       /* Change the system clock divider values */
+       exynos5_int_set_clkdiv(index);
+
+       clk->rate = rate;
+
+       return 0;
+}
+
+static struct clk_ops exynos5_clk_int_ops = {
+       .get_rate = exynos5_clk_int_get_rate,
+       .set_rate = exynos5_clk_int_set_rate
 };
 
 static u32 epll_div[][6] = {
@@ -1620,6 +1760,9 @@ void __init_or_cpufreq exynos5_setup_clocks(void)
 
        clk_fout_epll.ops = &exynos5_epll_ops;
 
+       exynos5_int_clk.ops = &exynos5_clk_int_ops;
+       exynos5_int_clk.rate = aclk_266;
+
        if (clk_set_parent(&exynos5_clk_mout_epll.clk, &clk_fout_epll))
                printk(KERN_ERR "Unable to set parent %s of clock %s.\n",
                                clk_fout_epll.name, 
exynos5_clk_mout_epll.clk.name);
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index 578a610..a285080 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -308,6 +308,31 @@ static struct map_desc exynos5_iodesc[] __initdata = {
                .pfn            = __phys_to_pfn(EXYNOS5_PA_UART),
                .length         = SZ_512K,
                .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_PPMU_CPU,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_PPMU_CPU),
+               .length         = SZ_8K,
+               .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_PPMU_DDR_C,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_PPMU_DDR_C),
+               .length         = SZ_8K,
+               .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_PPMU_DDR_R1,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_PPMU_DDR_R1),
+               .length         = SZ_8K,
+               .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_PPMU_DDR_L,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_PPMU_DDR_L),
+               .length         = SZ_8K,
+               .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_PPMU_RIGHT,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_PPMU_RIGHT),
+               .length         = SZ_8K,
+               .type           = MT_DEVICE,
        },
 };
 
diff --git a/arch/arm/mach-exynos/exynos5_ppmu.c 
b/arch/arm/mach-exynos/exynos5_ppmu.c
new file mode 100644
index 0000000..3fecef4
--- /dev/null
+++ b/arch/arm/mach-exynos/exynos5_ppmu.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS5 PPMU support
+ * Support for only EXYNOS5250 is present.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <mach/map.h>
+
+#include <mach/exynos_ppmu.h>
+#include <mach/exynos5_ppmu.h>
+
+#define FIXED_POINT_OFFSET 8
+#define FIXED_POINT_MASK ((1 << FIXED_POINT_OFFSET) - 1)
+
+enum exynos5_ppmu_list {
+       PPMU_DDR_C,
+       PPMU_DDR_R1,
+       PPMU_DDR_L,
+       PPMU_RIGHT,
+       PPMU_CPU,
+       PPMU_END,
+};
+
+struct exynos5_ppmu_handle {
+       struct list_head node;
+       struct exynos_ppmu ppmu[PPMU_END];
+};
+
+static DEFINE_SPINLOCK(exynos5_ppmu_lock);
+static LIST_HEAD(exynos5_ppmu_handle_list);
+static struct exynos5_ppmu_handle *exynos5_ppmu_trace_handle;
+
+static const char *exynos5_ppmu_name[PPMU_END] = {
+       [PPMU_DDR_C]    = "DDR_C",
+       [PPMU_DDR_R1]   = "DDR_R1",
+       [PPMU_DDR_L]    = "DDR_L",
+       [PPMU_RIGHT]    = "RIGHT",
+       [PPMU_CPU]      = "CPU",
+};
+
+static struct exynos_ppmu ppmu[PPMU_END] = {
+       [PPMU_DDR_C] = {
+               .hw_base = S5P_VA_PPMU_DDR_C,
+       },
+       [PPMU_DDR_R1] = {
+               .hw_base = S5P_VA_PPMU_DDR_R1,
+       },
+       [PPMU_DDR_L] = {
+               .hw_base = S5P_VA_PPMU_DDR_L,
+       },
+       [PPMU_RIGHT] = {
+               .hw_base = S5P_VA_PPMU_RIGHT,
+       },
+       [PPMU_CPU] = {
+               .hw_base = S5P_VA_PPMU_CPU,
+       },
+};
+
+static void exynos5_ppmu_reset(struct exynos_ppmu *ppmu)
+{
+       unsigned long flags;
+
+       void __iomem *ppmu_base = ppmu->hw_base;
+
+       /* Reset PPMU */
+       exynos_ppmu_reset(ppmu_base);
+
+       /* Set PPMU Event */
+       ppmu->event[PPMU_PMNCNT0] = RD_DATA_COUNT;
+       exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT0,
+                       ppmu->event[PPMU_PMNCNT0]);
+       ppmu->event[PPMU_PMCCNT1] = WR_DATA_COUNT;
+       exynos_ppmu_setevent(ppmu_base, PPMU_PMCCNT1,
+                       ppmu->event[PPMU_PMCCNT1]);
+       ppmu->event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
+       exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
+                       ppmu->event[PPMU_PMNCNT3]);
+
+       local_irq_save(flags);
+       ppmu->reset_time = ktime_get();
+       /* Start PPMU */
+       exynos_ppmu_start(ppmu_base);
+       local_irq_restore(flags);
+}
+
+static void exynos5_ppmu_read(struct exynos_ppmu *ppmu)
+{
+       int j;
+       unsigned long flags;
+       ktime_t read_time;
+       ktime_t t;
+       u32 reg;
+
+       void __iomem *ppmu_base = ppmu->hw_base;
+
+       local_irq_save(flags);
+       read_time = ktime_get();
+       /* Stop PPMU */
+       exynos_ppmu_stop(ppmu_base);
+       local_irq_restore(flags);
+
+       /* Update local data from PPMU */
+       ppmu->ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
+       reg = __raw_readl(ppmu_base + PPMU_FLAG);
+       ppmu->ccnt_overflow = reg & PPMU_CCNT_OVERFLOW;
+
+       for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
+               if (ppmu->event[j] == 0)
+                       ppmu->count[j] = 0;
+               else
+                       ppmu->count[j] = exynos_ppmu_read(ppmu_base, j);
+       }
+       t = ktime_sub(read_time, ppmu->reset_time);
+       ppmu->ns = ktime_to_ns(t);
+}
+
+static void exynos5_ppmu_add(struct exynos_ppmu *to, struct exynos_ppmu *from)
+{
+       int i;
+       int j;
+
+       for (i = 0; i < PPMU_END; i++) {
+               for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++)
+                       to[i].count[j] += from[i].count[j];
+
+               to[i].ccnt += from[i].ccnt;
+               if (to[i].ccnt < from[i].ccnt)
+                       to[i].ccnt_overflow = true;
+
+               to[i].ns += from[i].ns;
+
+               if (from[i].ccnt_overflow)
+                       to[i].ccnt_overflow = true;
+       }
+}
+
+static void exynos5_ppmu_handle_clear(struct exynos5_ppmu_handle *handle)
+{
+       memset(&handle->ppmu, 0, sizeof(struct exynos_ppmu) * PPMU_END);
+}
+
+static void exynos5_ppmu_update(void)
+{
+       int i;
+       struct exynos5_ppmu_handle *handle;
+
+       for (i = 0; i < PPMU_END; i++) {
+               exynos5_ppmu_read(&ppmu[i]);
+               exynos5_ppmu_reset(&ppmu[i]);
+       }
+
+       list_for_each_entry(handle, &exynos5_ppmu_handle_list, node)
+               exynos5_ppmu_add(handle->ppmu, ppmu);
+}
+
+static int exynos5_ppmu_get_filter(enum exynos5_ppmu_sets filter,
+       enum exynos5_ppmu_list *start, enum exynos5_ppmu_list *end)
+{
+       switch (filter) {
+       case PPMU_SET_DDR:
+               *start = PPMU_DDR_C;
+               *end = PPMU_DDR_L;
+               break;
+       case PPMU_SET_RIGHT:
+               *start = PPMU_RIGHT;
+               *end = PPMU_RIGHT;
+               break;
+       case PPMU_SET_CPU:
+               *start = PPMU_CPU;
+               *end = PPMU_CPU;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int exynos5_ppmu_get_busy(struct exynos5_ppmu_handle *handle,
+       enum exynos5_ppmu_sets filter)
+{
+       unsigned long flags;
+       int i;
+       int busy = 0;
+       int temp;
+       enum exynos5_ppmu_list start;
+       enum exynos5_ppmu_list end;
+       int ret;
+
+       ret = exynos5_ppmu_get_filter(filter, &start, &end);
+       if (ret < 0)
+               return ret;
+
+       spin_lock_irqsave(&exynos5_ppmu_lock, flags);
+
+       exynos5_ppmu_update();
+
+       for (i = start; i <= end; i++) {
+               if (handle->ppmu[i].ccnt_overflow) {
+                       busy = -EOVERFLOW;
+                       break;
+               }
+               temp = handle->ppmu[i].count[PPMU_PMNCNT3] * 100;
+               if (handle->ppmu[i].ccnt > 0)
+                       temp /= handle->ppmu[i].ccnt;
+               if (temp > busy)
+                       busy = temp;
+       }
+
+       exynos5_ppmu_handle_clear(handle);
+
+       spin_unlock_irqrestore(&exynos5_ppmu_lock, flags);
+
+       return busy;
+}
+
+static void exynos5_ppmu_put(struct exynos5_ppmu_handle *handle)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&exynos5_ppmu_lock, flags);
+
+       list_del(&handle->node);
+
+       spin_unlock_irqrestore(&exynos5_ppmu_lock, flags);
+
+       kfree(handle);
+}
+
+struct exynos5_ppmu_handle *exynos5_ppmu_get(void)
+{
+       struct exynos5_ppmu_handle *handle;
+       unsigned long flags;
+
+       handle = kzalloc(sizeof(struct exynos5_ppmu_handle), GFP_KERNEL);
+       if (!handle)
+               return NULL;
+
+       spin_lock_irqsave(&exynos5_ppmu_lock, flags);
+
+       exynos5_ppmu_update();
+       list_add_tail(&handle->node, &exynos5_ppmu_handle_list);
+
+       spin_unlock_irqrestore(&exynos5_ppmu_lock, flags);
+
+       return handle;
+}
+
+static int exynos5_ppmu_trace_init(void)
+{
+       exynos5_ppmu_trace_handle = exynos5_ppmu_get();
+       return 0;
+}
+late_initcall(exynos5_ppmu_trace_init);
+
+static void exynos5_ppmu_debug_compute(struct exynos_ppmu *ppmu,
+       enum ppmu_counter i, u32 *sat, u32 *freq, u32 *bw)
+{
+       u64 ns = ppmu->ns;
+       u32 busy = ppmu->count[i];
+       u32 total = ppmu->ccnt;
+
+       u64 s;
+       u64 f;
+       u64 b;
+
+       s = (u64)busy * 100 * (1 << FIXED_POINT_OFFSET);
+       s += total / 2;
+       do_div(s, total);
+
+       f = (u64)total * 1000 * (1 << FIXED_POINT_OFFSET);
+       f += ns / 2;
+       f = div64_u64(f, ns);
+
+       b = (u64)busy * (128 / 8) * 1000 * (1 << FIXED_POINT_OFFSET);
+       b += ns / 2;
+       b = div64_u64(b, ns);
+
+       *sat = s;
+       *freq = f;
+       *bw = b;
+}
+
+static void exynos5_ppmu_debug_show_one_counter(struct seq_file *s,
+       const char *name, const char *type, struct exynos_ppmu *ppmu,
+       enum ppmu_counter i, u32 *bw_total)
+{
+       u32 sat;
+       u32 freq;
+       u32 bw;
+
+       exynos5_ppmu_debug_compute(ppmu, i, &sat, &freq, &bw);
+
+       seq_printf(s, "%-10s %-10s %4u.%02u MBps %3u.%02u MHz %2u.%02u%%\n",
+               name, type,
+               bw >> FIXED_POINT_OFFSET,
+               (bw & FIXED_POINT_MASK) * 100 / (1 << FIXED_POINT_OFFSET),
+               freq >> FIXED_POINT_OFFSET,
+               (freq & FIXED_POINT_MASK) * 100 / (1 << FIXED_POINT_OFFSET),
+               sat >> FIXED_POINT_OFFSET,
+               (sat & FIXED_POINT_MASK) * 100 / (1 << FIXED_POINT_OFFSET));
+
+       *bw_total += bw;
+}
+
+static void exynos5_ppmu_debug_show_one(struct seq_file *s,
+       const char *name, struct exynos_ppmu *ppmu,
+       u32 *bw_total)
+{
+       exynos5_ppmu_debug_show_one_counter(s, name, "read+write",
+               ppmu, PPMU_PMNCNT3, &bw_total[PPMU_PMNCNT3]);
+       exynos5_ppmu_debug_show_one_counter(s, "", "read",
+               ppmu, PPMU_PMNCNT0, &bw_total[PPMU_PMNCNT0]);
+       exynos5_ppmu_debug_show_one_counter(s, "", "write",
+               ppmu, PPMU_PMCCNT1, &bw_total[PPMU_PMCCNT1]);
+
+}
+
+static int exynos5_ppmu_debug_show(struct seq_file *s, void *d)
+{
+       int i;
+       u32 bw_total[PPMU_PMNCNT_MAX];
+       struct exynos5_ppmu_handle *handle;
+       unsigned long flags;
+
+       memset(bw_total, 0, sizeof(bw_total));
+
+       handle = exynos5_ppmu_get();
+       msleep(100);
+
+       spin_lock_irqsave(&exynos5_ppmu_lock, flags);
+
+       exynos5_ppmu_update();
+
+       for (i = 0; i < PPMU_CPU; i++)
+               exynos5_ppmu_debug_show_one(s, exynos5_ppmu_name[i],
+                               &handle->ppmu[i], bw_total);
+
+       seq_printf(s, "%-10s %-10s %4u.%02u MBps\n", "total", "read+write",
+               bw_total[PPMU_PMNCNT3] >> FIXED_POINT_OFFSET,
+               (bw_total[PPMU_PMNCNT3] & FIXED_POINT_MASK) *
+                               100 / (1 << FIXED_POINT_OFFSET));
+       seq_printf(s, "%-10s %-10s %4u.%02u MBps\n", "", "read",
+               bw_total[PPMU_PMNCNT0] >> FIXED_POINT_OFFSET,
+               (bw_total[PPMU_PMNCNT0] & FIXED_POINT_MASK) *
+                               100 / (1 << FIXED_POINT_OFFSET));
+       seq_printf(s, "%-10s %-10s %4u.%02u MBps\n", "", "write",
+               bw_total[PPMU_PMCCNT1] >> FIXED_POINT_OFFSET,
+               (bw_total[PPMU_PMCCNT1] & FIXED_POINT_MASK) *
+                               100 / (1 << FIXED_POINT_OFFSET));
+
+       seq_printf(s, "\n");
+
+       exynos5_ppmu_debug_show_one(s, exynos5_ppmu_name[PPMU_CPU],
+                       &ppmu[PPMU_CPU], bw_total);
+
+       spin_unlock_irqrestore(&exynos5_ppmu_lock, flags);
+
+       exynos5_ppmu_put(handle);
+
+       return 0;
+}
+
+static int exynos5_ppmu_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, exynos5_ppmu_debug_show, inode->i_private);
+}
+
+static const struct file_operations exynos5_ppmu_debug_fops = {
+       .open           = exynos5_ppmu_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init exynos5_ppmu_debug_init(void)
+{
+       debugfs_create_file("exynos5_bus", S_IRUGO, NULL, NULL,
+               &exynos5_ppmu_debug_fops);
+       return 0;
+}
+late_initcall(exynos5_ppmu_debug_init);
diff --git a/arch/arm/mach-exynos/exynos_ppmu.c 
b/arch/arm/mach-exynos/exynos_ppmu.c
new file mode 100644
index 0000000..f627813
--- /dev/null
+++ b/arch/arm/mach-exynos/exynos_ppmu.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS - PPMU support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include <mach/exynos_ppmu.h>
+
+void exynos_ppmu_reset(void __iomem *ppmu_base)
+{
+       __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base);
+       __raw_writel(PPMU_ENABLE_CYCLE  |
+                    PPMU_ENABLE_COUNT0 |
+                    PPMU_ENABLE_COUNT1 |
+                    PPMU_ENABLE_COUNT2 |
+                    PPMU_ENABLE_COUNT3,
+                    ppmu_base + PPMU_CNTENS);
+}
+
+void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
+                       unsigned int evt)
+{
+       __raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch));
+}
+
+void exynos_ppmu_start(void __iomem *ppmu_base)
+{
+       __raw_writel(PPMU_ENABLE, ppmu_base);
+}
+
+void exynos_ppmu_stop(void __iomem *ppmu_base)
+{
+       __raw_writel(PPMU_DISABLE, ppmu_base);
+}
+
+unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch)
+{
+       unsigned int total;
+
+       if (ch == PPMU_PMNCNT3)
+               total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) |
+                         __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1)));
+       else
+               total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch));
+
+       return total;
+}
diff --git a/arch/arm/mach-exynos/include/mach/exynos5_ppmu.h 
b/arch/arm/mach-exynos/include/mach/exynos5_ppmu.h
new file mode 100644
index 0000000..9f492c1
--- /dev/null
+++ b/arch/arm/mach-exynos/include/mach/exynos5_ppmu.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS5 PPMU header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __DEVFREQ_EXYNOS5_PPMU_H
+#define __DEVFREQ_EXYNOS5_PPMU_H __FILE__
+
+enum exynos5_ppmu_sets {
+       PPMU_SET_DDR,
+       PPMU_SET_RIGHT,
+       PPMU_SET_CPU,
+};
+
+struct exynos5_ppmu_handle *exynos5_ppmu_get(void);
+extern int exynos5_ppmu_get_busy(struct exynos5_ppmu_handle *handle,
+       enum exynos5_ppmu_sets filter);
+
+#endif /* __DEVFREQ_EXYNOS5_PPMU_H */
+
diff --git a/arch/arm/mach-exynos/include/mach/exynos_ppmu.h 
b/arch/arm/mach-exynos/include/mach/exynos_ppmu.h
new file mode 100644
index 0000000..b46d31b
--- /dev/null
+++ b/arch/arm/mach-exynos/include/mach/exynos_ppmu.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS PPMU header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __DEVFREQ_EXYNOS_PPMU_H
+#define __DEVFREQ_EXYNOS_PPMU_H __FILE__
+
+#include <linux/ktime.h>
+
+/* For PPMU Control */
+#define PPMU_ENABLE             BIT(0)
+#define PPMU_DISABLE            0x0
+#define PPMU_CYCLE_RESET        BIT(1)
+#define PPMU_COUNTER_RESET      BIT(2)
+
+#define PPMU_ENABLE_COUNT0      BIT(0)
+#define PPMU_ENABLE_COUNT1      BIT(1)
+#define PPMU_ENABLE_COUNT2      BIT(2)
+#define PPMU_ENABLE_COUNT3      BIT(3)
+#define PPMU_ENABLE_CYCLE       BIT(31)
+
+#define PPMU_CNTENS            0x10
+#define PPMU_FLAG              0x50
+#define PPMU_CCNT_OVERFLOW     BIT(31)
+#define PPMU_CCNT              0x100
+
+#define PPMU_PMCNT0            0x110
+#define PPMU_PMCNT_OFFSET      0x10
+#define PMCNT_OFFSET(x)                (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x))
+
+#define PPMU_BEVT0SEL          0x1000
+#define PPMU_BEVTSEL_OFFSET    0x100
+#define PPMU_BEVTSEL(x)                (PPMU_BEVT0SEL + (ch * 
PPMU_BEVTSEL_OFFSET))
+
+/* For Event Selection */
+#define RD_DATA_COUNT          0x5
+#define WR_DATA_COUNT          0x6
+#define RDWR_DATA_COUNT                0x7
+
+enum ppmu_counter {
+       PPMU_PMNCNT0,
+       PPMU_PMCCNT1,
+       PPMU_PMNCNT2,
+       PPMU_PMNCNT3,
+       PPMU_PMNCNT_MAX,
+};
+
+struct bus_opp_table {
+       unsigned int idx;
+       unsigned long clk;
+       unsigned long volt;
+};
+
+struct exynos_ppmu {
+       void __iomem *hw_base;
+       unsigned int ccnt;
+       unsigned int event[PPMU_PMNCNT_MAX];
+       unsigned int count[PPMU_PMNCNT_MAX];
+       unsigned long long ns;
+       ktime_t reset_time;
+       bool ccnt_overflow;
+       bool count_overflow[PPMU_PMNCNT_MAX];
+};
+
+void exynos_ppmu_reset(void __iomem *ppmu_base);
+void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
+                       unsigned int evt);
+void exynos_ppmu_start(void __iomem *ppmu_base);
+void exynos_ppmu_stop(void __iomem *ppmu_base);
+unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch);
+#endif /* __DEVFREQ_EXYNOS_PPMU_H */
+
diff --git a/arch/arm/mach-exynos/include/mach/map.h 
b/arch/arm/mach-exynos/include/mach/map.h
index b8ea67e..9af6e06 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -229,6 +229,12 @@
 #define EXYNOS4_PA_SDRAM               0x40000000
 #define EXYNOS5_PA_SDRAM               0x40000000
 
+#define EXYNOS5_PA_PPMU_DDR_C          0x10C40000
+#define EXYNOS5_PA_PPMU_DDR_R1         0x10C50000
+#define EXYNOS5_PA_PPMU_CPU            0x10C60000
+#define EXYNOS5_PA_PPMU_DDR_L          0x10CB0000
+#define EXYNOS5_PA_PPMU_RIGHT          0x13660000
+
 /* Compatibiltiy Defines */
 
 #define S3C_PA_HSMMC0                  EXYNOS4_PA_HSMMC(0)
diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h 
b/arch/arm/mach-exynos/include/mach/regs-clock.h
index d36ad76..3d3cbc8 100644
--- a/arch/arm/mach-exynos/include/mach/regs-clock.h
+++ b/arch/arm/mach-exynos/include/mach/regs-clock.h
@@ -323,6 +323,9 @@
 #define EXYNOS5_CLKDIV_PERIC5                  EXYNOS_CLKREG(0x1056C)
 #define EXYNOS5_SCLK_DIV_ISP                   EXYNOS_CLKREG(0x10580)
 
+#define EXYNOS5_CLKDIV_STAT_TOP0               EXYNOS_CLKREG(0x10610)
+#define EXYNOS5_CLKDIV_STAT_TOP1               EXYNOS_CLKREG(0x10614)
+
 #define EXYNOS5_CLKGATE_IP_ACP                 EXYNOS_CLKREG(0x08800)
 #define EXYNOS5_CLKGATE_IP_ISP0                        EXYNOS_CLKREG(0x0C800)
 #define EXYNOS5_CLKGATE_IP_ISP1                        EXYNOS_CLKREG(0x0C804)
@@ -337,6 +340,18 @@
 #define EXYNOS5_CLKGATE_IP_PERIS               EXYNOS_CLKREG(0x10960)
 #define EXYNOS5_CLKGATE_BLOCK                  EXYNOS_CLKREG(0x10980)
 
+#define EXYNOS5_CLKGATE_BUS_SYSLFT             EXYNOS_CLKREG(0x08920)
+
+#define EXYNOS5_CLKOUT_CMU_TOP                 EXYNOS_CLKREG(0x10A00)
+
+#define EXYNOS5_CLKDIV_LEX                     EXYNOS_CLKREG(0x14500)
+#define EXYNOS5_CLKDIV_STAT_LEX                        EXYNOS_CLKREG(0x14600)
+
+#define EXYNOS5_CLKDIV_R0X                     EXYNOS_CLKREG(0x18500)
+#define EXYNOS5_CLKDIV_STAT_R0X                        EXYNOS_CLKREG(0x18600)
+
+#define EXYNOS5_CLKDIV_R1X                     EXYNOS_CLKREG(0x1C500)
+#define EXYNOS5_CLKDIV_STAT_R1X                        EXYNOS_CLKREG(0x1C600)
 #define EXYNOS5_BPLL_CON0                      EXYNOS_CLKREG(0x20110)
 #define EXYNOS5_CLKSRC_CDREX                   EXYNOS_CLKREG(0x20200)
 #define EXYNOS5_CLKDIV_CDREX                   EXYNOS_CLKREG(0x20500)
@@ -347,6 +362,28 @@
 
 #define EXYNOS5_EPLLCON0_LOCKED_SHIFT          (29)
 
+#define EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_SHIFT        (28)
+#define EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_MASK (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK333_SHIFT      (20)
+#define EXYNOS5_CLKDIV_TOP0_ACLK333_MASK       (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK333_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK266_SHIFT      (16)
+#define EXYNOS5_CLKDIV_TOP0_ACLK266_MASK       (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK266_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK200_SHIFT      (12)
+#define EXYNOS5_CLKDIV_TOP0_ACLK200_MASK       (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK200_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK166_SHIFT      (8)
+#define EXYNOS5_CLKDIV_TOP0_ACLK166_MASK       (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK166_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK66_SHIFT       (0)
+#define EXYNOS5_CLKDIV_TOP0_ACLK66_MASK                (0x7 << 
EXYNOS5_CLKDIV_TOP0_ACLK66_SHIFT)
+
+#define EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_SHIFT   (24)
+#define EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_MASK    (0x7 << 
EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_SHIFT  (20)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_MASK   (0x7 << 
EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_SHIFT  (16)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_MASK   (0x7 << 
EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_SHIFT (12)
+#define EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_MASK  (0x7 << 
EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_SHIFT)
+
 #define PWR_CTRL1_CORE2_DOWN_RATIO             (7 << 28)
 #define PWR_CTRL1_CORE1_DOWN_RATIO             (7 << 16)
 #define PWR_CTRL1_DIV2_DOWN_EN                 (1 << 9)
diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h 
b/arch/arm/plat-samsung/include/plat/map-s5p.h
index c186786..9399f2d 100644
--- a/arch/arm/plat-samsung/include/plat/map-s5p.h
+++ b/arch/arm/plat-samsung/include/plat/map-s5p.h
@@ -41,6 +41,12 @@
 #define S5P_VA_GIC_CPU         S3C_ADDR(0x02810000)
 #define S5P_VA_GIC_DIST                S3C_ADDR(0x02820000)
 
+#define S5P_VA_PPMU_CPU                S3C_ADDR(0x02830000)
+#define S5P_VA_PPMU_DDR_C      S3C_ADDR(0x02832000)
+#define S5P_VA_PPMU_DDR_R1     S3C_ADDR(0x02834000)
+#define S5P_VA_PPMU_DDR_L      S3C_ADDR(0x02836000)
+#define S5P_VA_PPMU_RIGHT      S3C_ADDR(0x02838000)
+
 #define VA_VIC(x)              (S3C_VA_IRQ + ((x) * 0x10000))
 #define VA_VIC0                        VA_VIC(0)
 #define VA_VIC1                        VA_VIC(1)
-- 
1.8.1-rc3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to