aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/ppmu.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /arch/arm/mach-exynos/ppmu.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'arch/arm/mach-exynos/ppmu.c')
-rw-r--r--arch/arm/mach-exynos/ppmu.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/ppmu.c b/arch/arm/mach-exynos/ppmu.c
new file mode 100644
index 0000000..44d606c
--- /dev/null
+++ b/arch/arm/mach-exynos/ppmu.c
@@ -0,0 +1,194 @@
+/* linux/arch/arm/mach-exynos/ppmu.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS4 - CPU 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/types.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/device.h>
+
+#include <plat/cpu.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/ppmu.h>
+
+static LIST_HEAD(ppmu_list);
+
+unsigned long long ppmu_load[PPMU_END];
+unsigned long long ppmu_load_detail[2][PPMU_END];
+
+void exynos4_ppmu_reset(struct exynos4_ppmu_hw *ppmu)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ int i;
+
+ __raw_writel(0x3 << 1, ppmu_base);
+ __raw_writel(0x8000000f, ppmu_base + PPMU_CNTENS);
+
+ if (soc_is_exynos4210())
+ for (i = 0; i < NUMBER_OF_COUNTER; i++) {
+ __raw_writel(0x0, ppmu_base + DEVT0_ID + (i * DEVT_ID_OFFSET));
+ __raw_writel(0x0, ppmu_base + DEVT0_IDMSK + (i * DEVT_ID_OFFSET));
+ }
+}
+
+void exynos4_ppmu_setevent(struct exynos4_ppmu_hw *ppmu,
+ unsigned int evt_num)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ __raw_writel(ppmu->event[evt_num], ppmu_base + PPMU_BEVT0SEL + (evt_num * PPMU_BEVTSEL_OFFSET));
+}
+
+void exynos4_ppmu_start(struct exynos4_ppmu_hw *ppmu)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ __raw_writel(0x1, ppmu_base);
+}
+
+void exynos4_ppmu_stop(struct exynos4_ppmu_hw *ppmu)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ __raw_writel(0x0, ppmu_base);
+}
+
+unsigned long long exynos4_ppmu_update(struct exynos4_ppmu_hw *ppmu, int ch)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ unsigned long long total = 0;
+
+ ppmu->ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
+
+ if (ppmu->ccnt == 0)
+ ppmu->ccnt = MAX_CCNT;
+
+ if (ch >= NUMBER_OF_COUNTER || ppmu->event[ch] == 0)
+ return 0;
+
+ if (ch == 3 && !soc_is_exynos4210())
+ total = (((u64)__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));
+
+ if (total > ppmu->ccnt)
+ total = ppmu->ccnt;
+
+ ppmu_load_detail[0][ppmu->id] = total * ppmu->weight;
+ ppmu_load_detail[1][ppmu->id] = ppmu->ccnt;
+ return div64_u64((total * ppmu->weight * 100), ppmu->ccnt);
+}
+
+void ppmu_start(struct device *dev)
+{
+ struct exynos4_ppmu_hw *ppmu;
+
+ list_for_each_entry(ppmu, &ppmu_list, node)
+ if (ppmu->dev == dev)
+ exynos4_ppmu_start(ppmu);
+}
+
+void ppmu_update(struct device *dev, int ch)
+{
+ struct exynos4_ppmu_hw *ppmu;
+
+ list_for_each_entry(ppmu, &ppmu_list, node)
+ if (ppmu->dev == dev) {
+ exynos4_ppmu_stop(ppmu);
+ ppmu_load[ppmu->id] = exynos4_ppmu_update(ppmu, ch);
+ exynos4_ppmu_reset(ppmu);
+ }
+}
+
+void ppmu_reset(struct device *dev)
+{
+ struct exynos4_ppmu_hw *ppmu;
+ int i;
+
+ list_for_each_entry(ppmu, &ppmu_list, node) {
+ if (ppmu->dev == dev) {
+ exynos4_ppmu_stop(ppmu);
+ for (i = 0; i < NUMBER_OF_COUNTER; i++)
+ if (ppmu->event[i] != 0)
+ exynos4_ppmu_setevent(ppmu, i);
+ exynos4_ppmu_reset(ppmu);
+ }
+ }
+}
+
+void ppmu_init(struct exynos4_ppmu_hw *ppmu, struct device *dev)
+{
+ void __iomem *ppmu_base = ppmu->hw_base;
+ int i;
+
+ ppmu->dev = dev;
+ list_add(&ppmu->node, &ppmu_list);
+
+ if (soc_is_exynos4210())
+ for (i = 0; i < NUMBER_OF_COUNTER; i++) {
+ __raw_writel(0x0, ppmu_base + DEVT0_ID + (i * DEVT_ID_OFFSET));
+ __raw_writel(0x0, ppmu_base + DEVT0_IDMSK + (i * DEVT_ID_OFFSET));
+ }
+
+ for (i = 0; i < NUMBER_OF_COUNTER; i++)
+ if (ppmu->event[i] != 0)
+ exynos4_ppmu_setevent(ppmu, i);
+}
+
+struct exynos4_ppmu_hw exynos_ppmu[] = {
+ [PPMU_DMC0] = {
+ .id = PPMU_DMC0,
+ .hw_base = S5P_VA_PPMU_DMC0,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DMC1] = {
+ .id = PPMU_DMC1,
+ .hw_base = S5P_VA_PPMU_DMC1,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_CPU] = {
+ .id = PPMU_CPU,
+ .hw_base = S5P_VA_PPMU_CPU,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+#ifdef CONFIG_ARCH_EXYNOS5
+ [PPMU_DDR_C] = {
+ .id = PPMU_DDR_C,
+ .hw_base = S5P_VA_PPMU_DDR_C,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DDR_R1] = {
+ .id = PPMU_DDR_R1,
+ .hw_base = S5P_VA_PPMU_DDR_R1,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DDR_L] = {
+ .id = PPMU_DDR_L,
+ .hw_base = S5P_VA_PPMU_DDR_L,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_RIGHT0_BUS] = {
+ .id = PPMU_RIGHT0_BUS,
+ .hw_base = S5P_VA_PPMU_RIGHT0_BUS,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+#endif
+};