1f262f28cSChanwoo Choi /* 2f262f28cSChanwoo Choi * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support 3f262f28cSChanwoo Choi * 477fe46a3SChanwoo Choi * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd. 5f262f28cSChanwoo Choi * Author : Chanwoo Choi <cw00.choi@samsung.com> 6f262f28cSChanwoo Choi * 7f262f28cSChanwoo Choi * This program is free software; you can redistribute it and/or modify 8f262f28cSChanwoo Choi * it under the terms of the GNU General Public License version 2 as 9f262f28cSChanwoo Choi * published by the Free Software Foundation. 10f262f28cSChanwoo Choi * 11f262f28cSChanwoo Choi * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c 12f262f28cSChanwoo Choi */ 13f262f28cSChanwoo Choi 14f262f28cSChanwoo Choi #include <linux/clk.h> 15f262f28cSChanwoo Choi #include <linux/io.h> 16f262f28cSChanwoo Choi #include <linux/kernel.h> 17f262f28cSChanwoo Choi #include <linux/module.h> 18f262f28cSChanwoo Choi #include <linux/mutex.h> 19f262f28cSChanwoo Choi #include <linux/of_address.h> 20f262f28cSChanwoo Choi #include <linux/platform_device.h> 21f262f28cSChanwoo Choi #include <linux/suspend.h> 22f262f28cSChanwoo Choi #include <linux/devfreq-event.h> 23f262f28cSChanwoo Choi 24f262f28cSChanwoo Choi #include "exynos-ppmu.h" 25f262f28cSChanwoo Choi 26f262f28cSChanwoo Choi struct exynos_ppmu_data { 27f262f28cSChanwoo Choi void __iomem *base; 28f262f28cSChanwoo Choi struct clk *clk; 29f262f28cSChanwoo Choi }; 30f262f28cSChanwoo Choi 31f262f28cSChanwoo Choi struct exynos_ppmu { 32f262f28cSChanwoo Choi struct devfreq_event_dev **edev; 33f262f28cSChanwoo Choi struct devfreq_event_desc *desc; 34f262f28cSChanwoo Choi unsigned int num_events; 35f262f28cSChanwoo Choi 36f262f28cSChanwoo Choi struct device *dev; 37f262f28cSChanwoo Choi struct mutex lock; 38f262f28cSChanwoo Choi 39f262f28cSChanwoo Choi struct exynos_ppmu_data ppmu; 40f262f28cSChanwoo Choi }; 41f262f28cSChanwoo Choi 42f262f28cSChanwoo Choi #define PPMU_EVENT(name) \ 43f262f28cSChanwoo Choi { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \ 44f262f28cSChanwoo Choi { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \ 45f262f28cSChanwoo Choi { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \ 46f262f28cSChanwoo Choi { "ppmu-event3-"#name, PPMU_PMNCNT3 } 47f262f28cSChanwoo Choi 48f262f28cSChanwoo Choi struct __exynos_ppmu_events { 49f262f28cSChanwoo Choi char *name; 50f262f28cSChanwoo Choi int id; 51f262f28cSChanwoo Choi } ppmu_events[] = { 52f262f28cSChanwoo Choi /* For Exynos3250, Exynos4 and Exynos5260 */ 53f262f28cSChanwoo Choi PPMU_EVENT(g3d), 54f262f28cSChanwoo Choi PPMU_EVENT(fsys), 55f262f28cSChanwoo Choi 56f262f28cSChanwoo Choi /* For Exynos4 SoCs and Exynos3250 */ 57f262f28cSChanwoo Choi PPMU_EVENT(dmc0), 58f262f28cSChanwoo Choi PPMU_EVENT(dmc1), 59f262f28cSChanwoo Choi PPMU_EVENT(cpu), 60f262f28cSChanwoo Choi PPMU_EVENT(rightbus), 61f262f28cSChanwoo Choi PPMU_EVENT(leftbus), 62f262f28cSChanwoo Choi PPMU_EVENT(lcd0), 63f262f28cSChanwoo Choi PPMU_EVENT(camif), 64f262f28cSChanwoo Choi 65f262f28cSChanwoo Choi /* Only for Exynos3250 and Exynos5260 */ 66f262f28cSChanwoo Choi PPMU_EVENT(mfc), 67f262f28cSChanwoo Choi 68f262f28cSChanwoo Choi /* Only for Exynos4 SoCs */ 69f262f28cSChanwoo Choi PPMU_EVENT(mfc-left), 70f262f28cSChanwoo Choi PPMU_EVENT(mfc-right), 71f262f28cSChanwoo Choi 72f262f28cSChanwoo Choi /* Only for Exynos5260 SoCs */ 73f262f28cSChanwoo Choi PPMU_EVENT(drex0-s0), 74f262f28cSChanwoo Choi PPMU_EVENT(drex0-s1), 75f262f28cSChanwoo Choi PPMU_EVENT(drex1-s0), 76f262f28cSChanwoo Choi PPMU_EVENT(drex1-s1), 77f262f28cSChanwoo Choi PPMU_EVENT(eagle), 78f262f28cSChanwoo Choi PPMU_EVENT(kfc), 79f262f28cSChanwoo Choi PPMU_EVENT(isp), 80f262f28cSChanwoo Choi PPMU_EVENT(fimc), 81f262f28cSChanwoo Choi PPMU_EVENT(gscl), 82f262f28cSChanwoo Choi PPMU_EVENT(mscl), 83f262f28cSChanwoo Choi PPMU_EVENT(fimd0x), 84f262f28cSChanwoo Choi PPMU_EVENT(fimd1x), 8577fe46a3SChanwoo Choi 8677fe46a3SChanwoo Choi /* Only for Exynos5433 SoCs */ 8777fe46a3SChanwoo Choi PPMU_EVENT(d0-cpu), 8877fe46a3SChanwoo Choi PPMU_EVENT(d0-general), 8977fe46a3SChanwoo Choi PPMU_EVENT(d0-rt), 9077fe46a3SChanwoo Choi PPMU_EVENT(d1-cpu), 9177fe46a3SChanwoo Choi PPMU_EVENT(d1-general), 9277fe46a3SChanwoo Choi PPMU_EVENT(d1-rt), 9377fe46a3SChanwoo Choi 94f262f28cSChanwoo Choi { /* sentinel */ }, 95f262f28cSChanwoo Choi }; 96f262f28cSChanwoo Choi 97f262f28cSChanwoo Choi static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) 98f262f28cSChanwoo Choi { 99f262f28cSChanwoo Choi int i; 100f262f28cSChanwoo Choi 101f262f28cSChanwoo Choi for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) 102f262f28cSChanwoo Choi if (!strcmp(edev->desc->name, ppmu_events[i].name)) 103f262f28cSChanwoo Choi return ppmu_events[i].id; 104f262f28cSChanwoo Choi 105f262f28cSChanwoo Choi return -EINVAL; 106f262f28cSChanwoo Choi } 107f262f28cSChanwoo Choi 10877fe46a3SChanwoo Choi /* 10977fe46a3SChanwoo Choi * The devfreq-event ops structure for PPMU v1.1 11077fe46a3SChanwoo Choi */ 111f262f28cSChanwoo Choi static int exynos_ppmu_disable(struct devfreq_event_dev *edev) 112f262f28cSChanwoo Choi { 113f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 114f262f28cSChanwoo Choi u32 pmnc; 115f262f28cSChanwoo Choi 116f262f28cSChanwoo Choi /* Disable all counters */ 117f262f28cSChanwoo Choi __raw_writel(PPMU_CCNT_MASK | 118f262f28cSChanwoo Choi PPMU_PMCNT0_MASK | 119f262f28cSChanwoo Choi PPMU_PMCNT1_MASK | 120f262f28cSChanwoo Choi PPMU_PMCNT2_MASK | 121f262f28cSChanwoo Choi PPMU_PMCNT3_MASK, 122f262f28cSChanwoo Choi info->ppmu.base + PPMU_CNTENC); 123f262f28cSChanwoo Choi 124f262f28cSChanwoo Choi /* Disable PPMU */ 125f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 126f262f28cSChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 127f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 128f262f28cSChanwoo Choi 129f262f28cSChanwoo Choi return 0; 130f262f28cSChanwoo Choi } 131f262f28cSChanwoo Choi 132f262f28cSChanwoo Choi static int exynos_ppmu_set_event(struct devfreq_event_dev *edev) 133f262f28cSChanwoo Choi { 134f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 135f262f28cSChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 136f262f28cSChanwoo Choi u32 pmnc, cntens; 137f262f28cSChanwoo Choi 138f262f28cSChanwoo Choi if (id < 0) 139f262f28cSChanwoo Choi return id; 140f262f28cSChanwoo Choi 141f262f28cSChanwoo Choi /* Enable specific counter */ 142f262f28cSChanwoo Choi cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS); 143f262f28cSChanwoo Choi cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 144f262f28cSChanwoo Choi __raw_writel(cntens, info->ppmu.base + PPMU_CNTENS); 145f262f28cSChanwoo Choi 146f262f28cSChanwoo Choi /* Set the event of Read/Write data count */ 147f262f28cSChanwoo Choi __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT, 148f262f28cSChanwoo Choi info->ppmu.base + PPMU_BEVTxSEL(id)); 149f262f28cSChanwoo Choi 150f262f28cSChanwoo Choi /* Reset cycle counter/performance counter and enable PPMU */ 151f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 152f262f28cSChanwoo Choi pmnc &= ~(PPMU_PMNC_ENABLE_MASK 153f262f28cSChanwoo Choi | PPMU_PMNC_COUNTER_RESET_MASK 154f262f28cSChanwoo Choi | PPMU_PMNC_CC_RESET_MASK); 155f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT); 156f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); 157f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); 158f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 159f262f28cSChanwoo Choi 160f262f28cSChanwoo Choi return 0; 161f262f28cSChanwoo Choi } 162f262f28cSChanwoo Choi 163f262f28cSChanwoo Choi static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, 164f262f28cSChanwoo Choi struct devfreq_event_data *edata) 165f262f28cSChanwoo Choi { 166f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 167f262f28cSChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 168f262f28cSChanwoo Choi u32 pmnc, cntenc; 169f262f28cSChanwoo Choi 170f262f28cSChanwoo Choi if (id < 0) 171f262f28cSChanwoo Choi return -EINVAL; 172f262f28cSChanwoo Choi 173f262f28cSChanwoo Choi /* Disable PPMU */ 174f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 175f262f28cSChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 176f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 177f262f28cSChanwoo Choi 178f262f28cSChanwoo Choi /* Read cycle count */ 179f262f28cSChanwoo Choi edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT); 180f262f28cSChanwoo Choi 181f262f28cSChanwoo Choi /* Read performance count */ 182f262f28cSChanwoo Choi switch (id) { 183f262f28cSChanwoo Choi case PPMU_PMNCNT0: 184f262f28cSChanwoo Choi case PPMU_PMNCNT1: 185f262f28cSChanwoo Choi case PPMU_PMNCNT2: 186f262f28cSChanwoo Choi edata->load_count 187f262f28cSChanwoo Choi = __raw_readl(info->ppmu.base + PPMU_PMNCT(id)); 188f262f28cSChanwoo Choi break; 189f262f28cSChanwoo Choi case PPMU_PMNCNT3: 190f262f28cSChanwoo Choi edata->load_count = 191f262f28cSChanwoo Choi ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8) 192f262f28cSChanwoo Choi | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW)); 193f262f28cSChanwoo Choi break; 194f262f28cSChanwoo Choi default: 195f262f28cSChanwoo Choi return -EINVAL; 196f262f28cSChanwoo Choi } 197f262f28cSChanwoo Choi 198f262f28cSChanwoo Choi /* Disable specific counter */ 199f262f28cSChanwoo Choi cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC); 200f262f28cSChanwoo Choi cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 201f262f28cSChanwoo Choi __raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC); 202f262f28cSChanwoo Choi 203f262f28cSChanwoo Choi dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, 204f262f28cSChanwoo Choi edata->load_count, edata->total_count); 205f262f28cSChanwoo Choi 206f262f28cSChanwoo Choi return 0; 207f262f28cSChanwoo Choi } 208f262f28cSChanwoo Choi 2096f240fbcSChanwoo Choi static const struct devfreq_event_ops exynos_ppmu_ops = { 210f262f28cSChanwoo Choi .disable = exynos_ppmu_disable, 211f262f28cSChanwoo Choi .set_event = exynos_ppmu_set_event, 212f262f28cSChanwoo Choi .get_event = exynos_ppmu_get_event, 213f262f28cSChanwoo Choi }; 214f262f28cSChanwoo Choi 21577fe46a3SChanwoo Choi /* 21677fe46a3SChanwoo Choi * The devfreq-event ops structure for PPMU v2.0 21777fe46a3SChanwoo Choi */ 21877fe46a3SChanwoo Choi static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev) 21977fe46a3SChanwoo Choi { 22077fe46a3SChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 22177fe46a3SChanwoo Choi u32 pmnc, clear; 22277fe46a3SChanwoo Choi 22377fe46a3SChanwoo Choi /* Disable all counters */ 22477fe46a3SChanwoo Choi clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK 22577fe46a3SChanwoo Choi | PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK); 22677fe46a3SChanwoo Choi 22777fe46a3SChanwoo Choi __raw_writel(clear, info->ppmu.base + PPMU_V2_FLAG); 22877fe46a3SChanwoo Choi __raw_writel(clear, info->ppmu.base + PPMU_V2_INTENC); 22977fe46a3SChanwoo Choi __raw_writel(clear, info->ppmu.base + PPMU_V2_CNTENC); 23077fe46a3SChanwoo Choi __raw_writel(clear, info->ppmu.base + PPMU_V2_CNT_RESET); 23177fe46a3SChanwoo Choi 23277fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG0); 23377fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG1); 23477fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_CFG2); 23577fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CIG_RESULT); 23677fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CNT_AUTO); 23777fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV0_TYPE); 23877fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV1_TYPE); 23977fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV2_TYPE); 24077fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_CH_EV3_TYPE); 24177fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_V); 24277fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_ID_A); 24377fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_V); 24477fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_SM_OTHERS_A); 24577fe46a3SChanwoo Choi __raw_writel(0x0, info->ppmu.base + PPMU_V2_INTERRUPT_RESET); 24677fe46a3SChanwoo Choi 24777fe46a3SChanwoo Choi /* Disable PPMU */ 24877fe46a3SChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); 24977fe46a3SChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 25077fe46a3SChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC); 25177fe46a3SChanwoo Choi 25277fe46a3SChanwoo Choi return 0; 25377fe46a3SChanwoo Choi } 25477fe46a3SChanwoo Choi 25577fe46a3SChanwoo Choi static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev) 25677fe46a3SChanwoo Choi { 25777fe46a3SChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 25877fe46a3SChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 25977fe46a3SChanwoo Choi u32 pmnc, cntens; 26077fe46a3SChanwoo Choi 26177fe46a3SChanwoo Choi /* Enable all counters */ 26277fe46a3SChanwoo Choi cntens = __raw_readl(info->ppmu.base + PPMU_V2_CNTENS); 26377fe46a3SChanwoo Choi cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 26477fe46a3SChanwoo Choi __raw_writel(cntens, info->ppmu.base + PPMU_V2_CNTENS); 26577fe46a3SChanwoo Choi 26677fe46a3SChanwoo Choi /* Set the event of Read/Write data count */ 26777fe46a3SChanwoo Choi switch (id) { 26877fe46a3SChanwoo Choi case PPMU_PMNCNT0: 26977fe46a3SChanwoo Choi case PPMU_PMNCNT1: 27077fe46a3SChanwoo Choi case PPMU_PMNCNT2: 27177fe46a3SChanwoo Choi __raw_writel(PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT, 27277fe46a3SChanwoo Choi info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id)); 27377fe46a3SChanwoo Choi break; 27477fe46a3SChanwoo Choi case PPMU_PMNCNT3: 27577fe46a3SChanwoo Choi __raw_writel(PPMU_V2_EVT3_RW_DATA_CNT, 27677fe46a3SChanwoo Choi info->ppmu.base + PPMU_V2_CH_EVx_TYPE(id)); 27777fe46a3SChanwoo Choi break; 27877fe46a3SChanwoo Choi } 27977fe46a3SChanwoo Choi 28077fe46a3SChanwoo Choi /* Reset cycle counter/performance counter and enable PPMU */ 28177fe46a3SChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); 28277fe46a3SChanwoo Choi pmnc &= ~(PPMU_PMNC_ENABLE_MASK 28377fe46a3SChanwoo Choi | PPMU_PMNC_COUNTER_RESET_MASK 28477fe46a3SChanwoo Choi | PPMU_PMNC_CC_RESET_MASK 28577fe46a3SChanwoo Choi | PPMU_PMNC_CC_DIVIDER_MASK 28677fe46a3SChanwoo Choi | PPMU_V2_PMNC_START_MODE_MASK); 28777fe46a3SChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT); 28877fe46a3SChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); 28977fe46a3SChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); 29077fe46a3SChanwoo Choi pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT); 29177fe46a3SChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC); 29277fe46a3SChanwoo Choi 29377fe46a3SChanwoo Choi return 0; 29477fe46a3SChanwoo Choi } 29577fe46a3SChanwoo Choi 29677fe46a3SChanwoo Choi static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev, 29777fe46a3SChanwoo Choi struct devfreq_event_data *edata) 29877fe46a3SChanwoo Choi { 29977fe46a3SChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 30077fe46a3SChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 30177fe46a3SChanwoo Choi u32 pmnc, cntenc; 30277fe46a3SChanwoo Choi u32 pmcnt_high, pmcnt_low; 30377fe46a3SChanwoo Choi u64 load_count = 0; 30477fe46a3SChanwoo Choi 30577fe46a3SChanwoo Choi /* Disable PPMU */ 30677fe46a3SChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_V2_PMNC); 30777fe46a3SChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 30877fe46a3SChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_V2_PMNC); 30977fe46a3SChanwoo Choi 31077fe46a3SChanwoo Choi /* Read cycle count and performance count */ 31177fe46a3SChanwoo Choi edata->total_count = __raw_readl(info->ppmu.base + PPMU_V2_CCNT); 31277fe46a3SChanwoo Choi 31377fe46a3SChanwoo Choi switch (id) { 31477fe46a3SChanwoo Choi case PPMU_PMNCNT0: 31577fe46a3SChanwoo Choi case PPMU_PMNCNT1: 31677fe46a3SChanwoo Choi case PPMU_PMNCNT2: 31777fe46a3SChanwoo Choi load_count = __raw_readl(info->ppmu.base + PPMU_V2_PMNCT(id)); 31877fe46a3SChanwoo Choi break; 31977fe46a3SChanwoo Choi case PPMU_PMNCNT3: 32077fe46a3SChanwoo Choi pmcnt_high = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_HIGH); 32177fe46a3SChanwoo Choi pmcnt_low = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_LOW); 32277fe46a3SChanwoo Choi load_count = (u64)((pmcnt_high & 0xff) << 32) + (u64)pmcnt_low; 32377fe46a3SChanwoo Choi break; 32477fe46a3SChanwoo Choi } 32577fe46a3SChanwoo Choi edata->load_count = load_count; 32677fe46a3SChanwoo Choi 32777fe46a3SChanwoo Choi /* Disable all counters */ 32877fe46a3SChanwoo Choi cntenc = __raw_readl(info->ppmu.base + PPMU_V2_CNTENC); 32977fe46a3SChanwoo Choi cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 33077fe46a3SChanwoo Choi __raw_writel(cntenc, info->ppmu.base + PPMU_V2_CNTENC); 33177fe46a3SChanwoo Choi 33277fe46a3SChanwoo Choi dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name, 33377fe46a3SChanwoo Choi edata->load_count, edata->total_count); 33477fe46a3SChanwoo Choi return 0; 33577fe46a3SChanwoo Choi } 33677fe46a3SChanwoo Choi 33777fe46a3SChanwoo Choi static const struct devfreq_event_ops exynos_ppmu_v2_ops = { 33877fe46a3SChanwoo Choi .disable = exynos_ppmu_v2_disable, 33977fe46a3SChanwoo Choi .set_event = exynos_ppmu_v2_set_event, 34077fe46a3SChanwoo Choi .get_event = exynos_ppmu_v2_get_event, 34177fe46a3SChanwoo Choi }; 34277fe46a3SChanwoo Choi 34377fe46a3SChanwoo Choi static const struct of_device_id exynos_ppmu_id_match[] = { 34477fe46a3SChanwoo Choi { 34577fe46a3SChanwoo Choi .compatible = "samsung,exynos-ppmu", 34677fe46a3SChanwoo Choi .data = (void *)&exynos_ppmu_ops, 34777fe46a3SChanwoo Choi }, { 34877fe46a3SChanwoo Choi .compatible = "samsung,exynos-ppmu-v2", 34977fe46a3SChanwoo Choi .data = (void *)&exynos_ppmu_v2_ops, 35077fe46a3SChanwoo Choi }, 35177fe46a3SChanwoo Choi { /* sentinel */ }, 35277fe46a3SChanwoo Choi }; 35377fe46a3SChanwoo Choi 35477fe46a3SChanwoo Choi static struct devfreq_event_ops *exynos_bus_get_ops(struct device_node *np) 35577fe46a3SChanwoo Choi { 35677fe46a3SChanwoo Choi const struct of_device_id *match; 35777fe46a3SChanwoo Choi 35877fe46a3SChanwoo Choi match = of_match_node(exynos_ppmu_id_match, np); 35977fe46a3SChanwoo Choi return (struct devfreq_event_ops *)match->data; 36077fe46a3SChanwoo Choi } 36177fe46a3SChanwoo Choi 362f262f28cSChanwoo Choi static int of_get_devfreq_events(struct device_node *np, 363f262f28cSChanwoo Choi struct exynos_ppmu *info) 364f262f28cSChanwoo Choi { 365f262f28cSChanwoo Choi struct devfreq_event_desc *desc; 36677fe46a3SChanwoo Choi struct devfreq_event_ops *event_ops; 367f262f28cSChanwoo Choi struct device *dev = info->dev; 368f262f28cSChanwoo Choi struct device_node *events_np, *node; 369f262f28cSChanwoo Choi int i, j, count; 370f262f28cSChanwoo Choi 371f262f28cSChanwoo Choi events_np = of_get_child_by_name(np, "events"); 372f262f28cSChanwoo Choi if (!events_np) { 373f262f28cSChanwoo Choi dev_err(dev, 374f262f28cSChanwoo Choi "failed to get child node of devfreq-event devices\n"); 375f262f28cSChanwoo Choi return -EINVAL; 376f262f28cSChanwoo Choi } 37777fe46a3SChanwoo Choi event_ops = exynos_bus_get_ops(np); 378f262f28cSChanwoo Choi 379f262f28cSChanwoo Choi count = of_get_child_count(events_np); 380f262f28cSChanwoo Choi desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); 381f262f28cSChanwoo Choi if (!desc) 382f262f28cSChanwoo Choi return -ENOMEM; 383f262f28cSChanwoo Choi info->num_events = count; 384f262f28cSChanwoo Choi 385f262f28cSChanwoo Choi j = 0; 386f262f28cSChanwoo Choi for_each_child_of_node(events_np, node) { 387f262f28cSChanwoo Choi for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { 388f262f28cSChanwoo Choi if (!ppmu_events[i].name) 389f262f28cSChanwoo Choi continue; 390f262f28cSChanwoo Choi 391f262f28cSChanwoo Choi if (!of_node_cmp(node->name, ppmu_events[i].name)) 392f262f28cSChanwoo Choi break; 393f262f28cSChanwoo Choi } 394f262f28cSChanwoo Choi 395f262f28cSChanwoo Choi if (i == ARRAY_SIZE(ppmu_events)) { 396f262f28cSChanwoo Choi dev_warn(dev, 397f262f28cSChanwoo Choi "don't know how to configure events : %s\n", 398f262f28cSChanwoo Choi node->name); 399f262f28cSChanwoo Choi continue; 400f262f28cSChanwoo Choi } 401f262f28cSChanwoo Choi 40277fe46a3SChanwoo Choi desc[j].ops = event_ops; 403f262f28cSChanwoo Choi desc[j].driver_data = info; 404f262f28cSChanwoo Choi 405f262f28cSChanwoo Choi of_property_read_string(node, "event-name", &desc[j].name); 406f262f28cSChanwoo Choi 407f262f28cSChanwoo Choi j++; 408f262f28cSChanwoo Choi 409f262f28cSChanwoo Choi of_node_put(node); 410f262f28cSChanwoo Choi } 411f262f28cSChanwoo Choi info->desc = desc; 412f262f28cSChanwoo Choi 413f262f28cSChanwoo Choi of_node_put(events_np); 414f262f28cSChanwoo Choi 415f262f28cSChanwoo Choi return 0; 416f262f28cSChanwoo Choi } 417f262f28cSChanwoo Choi 418f262f28cSChanwoo Choi static int exynos_ppmu_parse_dt(struct exynos_ppmu *info) 419f262f28cSChanwoo Choi { 420f262f28cSChanwoo Choi struct device *dev = info->dev; 421f262f28cSChanwoo Choi struct device_node *np = dev->of_node; 422f262f28cSChanwoo Choi int ret = 0; 423f262f28cSChanwoo Choi 424f262f28cSChanwoo Choi if (!np) { 425f262f28cSChanwoo Choi dev_err(dev, "failed to find devicetree node\n"); 426f262f28cSChanwoo Choi return -EINVAL; 427f262f28cSChanwoo Choi } 428f262f28cSChanwoo Choi 429f262f28cSChanwoo Choi /* Maps the memory mapped IO to control PPMU register */ 430f262f28cSChanwoo Choi info->ppmu.base = of_iomap(np, 0); 431f262f28cSChanwoo Choi if (IS_ERR_OR_NULL(info->ppmu.base)) { 432f262f28cSChanwoo Choi dev_err(dev, "failed to map memory region\n"); 433f262f28cSChanwoo Choi return -ENOMEM; 434f262f28cSChanwoo Choi } 435f262f28cSChanwoo Choi 436f262f28cSChanwoo Choi info->ppmu.clk = devm_clk_get(dev, "ppmu"); 437f262f28cSChanwoo Choi if (IS_ERR(info->ppmu.clk)) { 438f262f28cSChanwoo Choi info->ppmu.clk = NULL; 439f262f28cSChanwoo Choi dev_warn(dev, "cannot get PPMU clock\n"); 440f262f28cSChanwoo Choi } 441f262f28cSChanwoo Choi 442f262f28cSChanwoo Choi ret = of_get_devfreq_events(np, info); 443f262f28cSChanwoo Choi if (ret < 0) { 444f262f28cSChanwoo Choi dev_err(dev, "failed to parse exynos ppmu dt node\n"); 445f262f28cSChanwoo Choi goto err; 446f262f28cSChanwoo Choi } 447f262f28cSChanwoo Choi 448f262f28cSChanwoo Choi return 0; 449f262f28cSChanwoo Choi 450f262f28cSChanwoo Choi err: 451f262f28cSChanwoo Choi iounmap(info->ppmu.base); 452f262f28cSChanwoo Choi 453f262f28cSChanwoo Choi return ret; 454f262f28cSChanwoo Choi } 455f262f28cSChanwoo Choi 456f262f28cSChanwoo Choi static int exynos_ppmu_probe(struct platform_device *pdev) 457f262f28cSChanwoo Choi { 458f262f28cSChanwoo Choi struct exynos_ppmu *info; 459f262f28cSChanwoo Choi struct devfreq_event_dev **edev; 460f262f28cSChanwoo Choi struct devfreq_event_desc *desc; 461f262f28cSChanwoo Choi int i, ret = 0, size; 462f262f28cSChanwoo Choi 463f262f28cSChanwoo Choi info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 464f262f28cSChanwoo Choi if (!info) 465f262f28cSChanwoo Choi return -ENOMEM; 466f262f28cSChanwoo Choi 467f262f28cSChanwoo Choi mutex_init(&info->lock); 468f262f28cSChanwoo Choi info->dev = &pdev->dev; 469f262f28cSChanwoo Choi 470f262f28cSChanwoo Choi /* Parse dt data to get resource */ 471f262f28cSChanwoo Choi ret = exynos_ppmu_parse_dt(info); 472f262f28cSChanwoo Choi if (ret < 0) { 473f262f28cSChanwoo Choi dev_err(&pdev->dev, 474f262f28cSChanwoo Choi "failed to parse devicetree for resource\n"); 475f262f28cSChanwoo Choi return ret; 476f262f28cSChanwoo Choi } 477f262f28cSChanwoo Choi desc = info->desc; 478f262f28cSChanwoo Choi 479f262f28cSChanwoo Choi size = sizeof(struct devfreq_event_dev *) * info->num_events; 480f262f28cSChanwoo Choi info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); 481f262f28cSChanwoo Choi if (!info->edev) { 482f262f28cSChanwoo Choi dev_err(&pdev->dev, 483f262f28cSChanwoo Choi "failed to allocate memory devfreq-event devices\n"); 484f262f28cSChanwoo Choi return -ENOMEM; 485f262f28cSChanwoo Choi } 486f262f28cSChanwoo Choi edev = info->edev; 487f262f28cSChanwoo Choi platform_set_drvdata(pdev, info); 488f262f28cSChanwoo Choi 489f262f28cSChanwoo Choi for (i = 0; i < info->num_events; i++) { 490f262f28cSChanwoo Choi edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); 49104a695edSDan Carpenter if (IS_ERR(edev[i])) { 49204a695edSDan Carpenter ret = PTR_ERR(edev[i]); 493f262f28cSChanwoo Choi dev_err(&pdev->dev, 494f262f28cSChanwoo Choi "failed to add devfreq-event device\n"); 495f262f28cSChanwoo Choi goto err; 496f262f28cSChanwoo Choi } 497f262f28cSChanwoo Choi } 498f262f28cSChanwoo Choi 499f262f28cSChanwoo Choi clk_prepare_enable(info->ppmu.clk); 500f262f28cSChanwoo Choi 501f262f28cSChanwoo Choi return 0; 502f262f28cSChanwoo Choi err: 503f262f28cSChanwoo Choi iounmap(info->ppmu.base); 504f262f28cSChanwoo Choi 505f262f28cSChanwoo Choi return ret; 506f262f28cSChanwoo Choi } 507f262f28cSChanwoo Choi 508f262f28cSChanwoo Choi static int exynos_ppmu_remove(struct platform_device *pdev) 509f262f28cSChanwoo Choi { 510f262f28cSChanwoo Choi struct exynos_ppmu *info = platform_get_drvdata(pdev); 511f262f28cSChanwoo Choi 512f262f28cSChanwoo Choi clk_disable_unprepare(info->ppmu.clk); 513f262f28cSChanwoo Choi iounmap(info->ppmu.base); 514f262f28cSChanwoo Choi 515f262f28cSChanwoo Choi return 0; 516f262f28cSChanwoo Choi } 517f262f28cSChanwoo Choi 518f262f28cSChanwoo Choi static struct platform_driver exynos_ppmu_driver = { 519f262f28cSChanwoo Choi .probe = exynos_ppmu_probe, 520f262f28cSChanwoo Choi .remove = exynos_ppmu_remove, 521f262f28cSChanwoo Choi .driver = { 522f262f28cSChanwoo Choi .name = "exynos-ppmu", 523f262f28cSChanwoo Choi .of_match_table = exynos_ppmu_id_match, 524f262f28cSChanwoo Choi }, 525f262f28cSChanwoo Choi }; 526f262f28cSChanwoo Choi module_platform_driver(exynos_ppmu_driver); 527f262f28cSChanwoo Choi 528f262f28cSChanwoo Choi MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); 529f262f28cSChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 530f262f28cSChanwoo Choi MODULE_LICENSE("GPL"); 531