diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /arch/arm/mach-exynos/ppmu.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_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.c | 194 |
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 +}; |