/* * 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);