| /* | |
| * 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 "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; | |
| } | |
| void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < ppmu_data->ppmu_end; i++) { | |
| void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | |
| /* Reset the performance and cycle counters */ | |
| exynos_ppmu_reset(ppmu_base); | |
| /* Setup count registers to monitor read/write transactions */ | |
| ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; | |
| exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, | |
| ppmu_data->ppmu[i].event[PPMU_PMNCNT3]); | |
| exynos_ppmu_start(ppmu_base); | |
| } | |
| } | |
| EXPORT_SYMBOL(busfreq_mon_reset); | |
| void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data) | |
| { | |
| int i, j; | |
| for (i = 0; i < ppmu_data->ppmu_end; i++) { | |
| void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | |
| exynos_ppmu_stop(ppmu_base); | |
| /* Update local data from PPMU */ | |
| ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); | |
| for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | |
| if (ppmu_data->ppmu[i].event[j] == 0) | |
| ppmu_data->ppmu[i].count[j] = 0; | |
| else | |
| ppmu_data->ppmu[i].count[j] = | |
| exynos_ppmu_read(ppmu_base, j); | |
| } | |
| } | |
| busfreq_mon_reset(ppmu_data); | |
| } | |
| EXPORT_SYMBOL(exynos_read_ppmu); | |
| int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data) | |
| { | |
| unsigned int count = 0; | |
| int i, j, busy = 0; | |
| for (i = 0; i < ppmu_data->ppmu_end; i++) { | |
| for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | |
| if (ppmu_data->ppmu[i].count[j] > count) { | |
| count = ppmu_data->ppmu[i].count[j]; | |
| busy = i; | |
| } | |
| } | |
| } | |
| return busy; | |
| } | |
| EXPORT_SYMBOL(exynos_get_busier_ppmu); |