1f262f28cSChanwoo Choi /* 2f262f28cSChanwoo Choi * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support 3f262f28cSChanwoo Choi * 4f262f28cSChanwoo Choi * Copyright (c) 2014 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), 85f262f28cSChanwoo Choi { /* sentinel */ }, 86f262f28cSChanwoo Choi }; 87f262f28cSChanwoo Choi 88f262f28cSChanwoo Choi static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) 89f262f28cSChanwoo Choi { 90f262f28cSChanwoo Choi int i; 91f262f28cSChanwoo Choi 92f262f28cSChanwoo Choi for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) 93f262f28cSChanwoo Choi if (!strcmp(edev->desc->name, ppmu_events[i].name)) 94f262f28cSChanwoo Choi return ppmu_events[i].id; 95f262f28cSChanwoo Choi 96f262f28cSChanwoo Choi return -EINVAL; 97f262f28cSChanwoo Choi } 98f262f28cSChanwoo Choi 99f262f28cSChanwoo Choi static int exynos_ppmu_disable(struct devfreq_event_dev *edev) 100f262f28cSChanwoo Choi { 101f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 102f262f28cSChanwoo Choi u32 pmnc; 103f262f28cSChanwoo Choi 104f262f28cSChanwoo Choi /* Disable all counters */ 105f262f28cSChanwoo Choi __raw_writel(PPMU_CCNT_MASK | 106f262f28cSChanwoo Choi PPMU_PMCNT0_MASK | 107f262f28cSChanwoo Choi PPMU_PMCNT1_MASK | 108f262f28cSChanwoo Choi PPMU_PMCNT2_MASK | 109f262f28cSChanwoo Choi PPMU_PMCNT3_MASK, 110f262f28cSChanwoo Choi info->ppmu.base + PPMU_CNTENC); 111f262f28cSChanwoo Choi 112f262f28cSChanwoo Choi /* Disable PPMU */ 113f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 114f262f28cSChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 115f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 116f262f28cSChanwoo Choi 117f262f28cSChanwoo Choi return 0; 118f262f28cSChanwoo Choi } 119f262f28cSChanwoo Choi 120f262f28cSChanwoo Choi static int exynos_ppmu_set_event(struct devfreq_event_dev *edev) 121f262f28cSChanwoo Choi { 122f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 123f262f28cSChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 124f262f28cSChanwoo Choi u32 pmnc, cntens; 125f262f28cSChanwoo Choi 126f262f28cSChanwoo Choi if (id < 0) 127f262f28cSChanwoo Choi return id; 128f262f28cSChanwoo Choi 129f262f28cSChanwoo Choi /* Enable specific counter */ 130f262f28cSChanwoo Choi cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS); 131f262f28cSChanwoo Choi cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 132f262f28cSChanwoo Choi __raw_writel(cntens, info->ppmu.base + PPMU_CNTENS); 133f262f28cSChanwoo Choi 134f262f28cSChanwoo Choi /* Set the event of Read/Write data count */ 135f262f28cSChanwoo Choi __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT, 136f262f28cSChanwoo Choi info->ppmu.base + PPMU_BEVTxSEL(id)); 137f262f28cSChanwoo Choi 138f262f28cSChanwoo Choi /* Reset cycle counter/performance counter and enable PPMU */ 139f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 140f262f28cSChanwoo Choi pmnc &= ~(PPMU_PMNC_ENABLE_MASK 141f262f28cSChanwoo Choi | PPMU_PMNC_COUNTER_RESET_MASK 142f262f28cSChanwoo Choi | PPMU_PMNC_CC_RESET_MASK); 143f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT); 144f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); 145f262f28cSChanwoo Choi pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); 146f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 147f262f28cSChanwoo Choi 148f262f28cSChanwoo Choi return 0; 149f262f28cSChanwoo Choi } 150f262f28cSChanwoo Choi 151f262f28cSChanwoo Choi static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, 152f262f28cSChanwoo Choi struct devfreq_event_data *edata) 153f262f28cSChanwoo Choi { 154f262f28cSChanwoo Choi struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); 155f262f28cSChanwoo Choi int id = exynos_ppmu_find_ppmu_id(edev); 156f262f28cSChanwoo Choi u32 pmnc, cntenc; 157f262f28cSChanwoo Choi 158f262f28cSChanwoo Choi if (id < 0) 159f262f28cSChanwoo Choi return -EINVAL; 160f262f28cSChanwoo Choi 161f262f28cSChanwoo Choi /* Disable PPMU */ 162f262f28cSChanwoo Choi pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); 163f262f28cSChanwoo Choi pmnc &= ~PPMU_PMNC_ENABLE_MASK; 164f262f28cSChanwoo Choi __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); 165f262f28cSChanwoo Choi 166f262f28cSChanwoo Choi /* Read cycle count */ 167f262f28cSChanwoo Choi edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT); 168f262f28cSChanwoo Choi 169f262f28cSChanwoo Choi /* Read performance count */ 170f262f28cSChanwoo Choi switch (id) { 171f262f28cSChanwoo Choi case PPMU_PMNCNT0: 172f262f28cSChanwoo Choi case PPMU_PMNCNT1: 173f262f28cSChanwoo Choi case PPMU_PMNCNT2: 174f262f28cSChanwoo Choi edata->load_count 175f262f28cSChanwoo Choi = __raw_readl(info->ppmu.base + PPMU_PMNCT(id)); 176f262f28cSChanwoo Choi break; 177f262f28cSChanwoo Choi case PPMU_PMNCNT3: 178f262f28cSChanwoo Choi edata->load_count = 179f262f28cSChanwoo Choi ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8) 180f262f28cSChanwoo Choi | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW)); 181f262f28cSChanwoo Choi break; 182f262f28cSChanwoo Choi default: 183f262f28cSChanwoo Choi return -EINVAL; 184f262f28cSChanwoo Choi } 185f262f28cSChanwoo Choi 186f262f28cSChanwoo Choi /* Disable specific counter */ 187f262f28cSChanwoo Choi cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC); 188f262f28cSChanwoo Choi cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); 189f262f28cSChanwoo Choi __raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC); 190f262f28cSChanwoo Choi 191f262f28cSChanwoo Choi dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, 192f262f28cSChanwoo Choi edata->load_count, edata->total_count); 193f262f28cSChanwoo Choi 194f262f28cSChanwoo Choi return 0; 195f262f28cSChanwoo Choi } 196f262f28cSChanwoo Choi 1976f240fbcSChanwoo Choi static const struct devfreq_event_ops exynos_ppmu_ops = { 198f262f28cSChanwoo Choi .disable = exynos_ppmu_disable, 199f262f28cSChanwoo Choi .set_event = exynos_ppmu_set_event, 200f262f28cSChanwoo Choi .get_event = exynos_ppmu_get_event, 201f262f28cSChanwoo Choi }; 202f262f28cSChanwoo Choi 203f262f28cSChanwoo Choi static int of_get_devfreq_events(struct device_node *np, 204f262f28cSChanwoo Choi struct exynos_ppmu *info) 205f262f28cSChanwoo Choi { 206f262f28cSChanwoo Choi struct devfreq_event_desc *desc; 207f262f28cSChanwoo Choi struct device *dev = info->dev; 208f262f28cSChanwoo Choi struct device_node *events_np, *node; 209f262f28cSChanwoo Choi int i, j, count; 210f262f28cSChanwoo Choi 211f262f28cSChanwoo Choi events_np = of_get_child_by_name(np, "events"); 212f262f28cSChanwoo Choi if (!events_np) { 213f262f28cSChanwoo Choi dev_err(dev, 214f262f28cSChanwoo Choi "failed to get child node of devfreq-event devices\n"); 215f262f28cSChanwoo Choi return -EINVAL; 216f262f28cSChanwoo Choi } 217f262f28cSChanwoo Choi 218f262f28cSChanwoo Choi count = of_get_child_count(events_np); 219f262f28cSChanwoo Choi desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); 220f262f28cSChanwoo Choi if (!desc) 221f262f28cSChanwoo Choi return -ENOMEM; 222f262f28cSChanwoo Choi info->num_events = count; 223f262f28cSChanwoo Choi 224f262f28cSChanwoo Choi j = 0; 225f262f28cSChanwoo Choi for_each_child_of_node(events_np, node) { 226f262f28cSChanwoo Choi for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { 227f262f28cSChanwoo Choi if (!ppmu_events[i].name) 228f262f28cSChanwoo Choi continue; 229f262f28cSChanwoo Choi 230f262f28cSChanwoo Choi if (!of_node_cmp(node->name, ppmu_events[i].name)) 231f262f28cSChanwoo Choi break; 232f262f28cSChanwoo Choi } 233f262f28cSChanwoo Choi 234f262f28cSChanwoo Choi if (i == ARRAY_SIZE(ppmu_events)) { 235f262f28cSChanwoo Choi dev_warn(dev, 236f262f28cSChanwoo Choi "don't know how to configure events : %s\n", 237f262f28cSChanwoo Choi node->name); 238f262f28cSChanwoo Choi continue; 239f262f28cSChanwoo Choi } 240f262f28cSChanwoo Choi 241f262f28cSChanwoo Choi desc[j].ops = &exynos_ppmu_ops; 242f262f28cSChanwoo Choi desc[j].driver_data = info; 243f262f28cSChanwoo Choi 244f262f28cSChanwoo Choi of_property_read_string(node, "event-name", &desc[j].name); 245f262f28cSChanwoo Choi 246f262f28cSChanwoo Choi j++; 247f262f28cSChanwoo Choi 248f262f28cSChanwoo Choi of_node_put(node); 249f262f28cSChanwoo Choi } 250f262f28cSChanwoo Choi info->desc = desc; 251f262f28cSChanwoo Choi 252f262f28cSChanwoo Choi of_node_put(events_np); 253f262f28cSChanwoo Choi 254f262f28cSChanwoo Choi return 0; 255f262f28cSChanwoo Choi } 256f262f28cSChanwoo Choi 257f262f28cSChanwoo Choi static int exynos_ppmu_parse_dt(struct exynos_ppmu *info) 258f262f28cSChanwoo Choi { 259f262f28cSChanwoo Choi struct device *dev = info->dev; 260f262f28cSChanwoo Choi struct device_node *np = dev->of_node; 261f262f28cSChanwoo Choi int ret = 0; 262f262f28cSChanwoo Choi 263f262f28cSChanwoo Choi if (!np) { 264f262f28cSChanwoo Choi dev_err(dev, "failed to find devicetree node\n"); 265f262f28cSChanwoo Choi return -EINVAL; 266f262f28cSChanwoo Choi } 267f262f28cSChanwoo Choi 268f262f28cSChanwoo Choi /* Maps the memory mapped IO to control PPMU register */ 269f262f28cSChanwoo Choi info->ppmu.base = of_iomap(np, 0); 270f262f28cSChanwoo Choi if (IS_ERR_OR_NULL(info->ppmu.base)) { 271f262f28cSChanwoo Choi dev_err(dev, "failed to map memory region\n"); 272f262f28cSChanwoo Choi return -ENOMEM; 273f262f28cSChanwoo Choi } 274f262f28cSChanwoo Choi 275f262f28cSChanwoo Choi info->ppmu.clk = devm_clk_get(dev, "ppmu"); 276f262f28cSChanwoo Choi if (IS_ERR(info->ppmu.clk)) { 277f262f28cSChanwoo Choi info->ppmu.clk = NULL; 278f262f28cSChanwoo Choi dev_warn(dev, "cannot get PPMU clock\n"); 279f262f28cSChanwoo Choi } 280f262f28cSChanwoo Choi 281f262f28cSChanwoo Choi ret = of_get_devfreq_events(np, info); 282f262f28cSChanwoo Choi if (ret < 0) { 283f262f28cSChanwoo Choi dev_err(dev, "failed to parse exynos ppmu dt node\n"); 284f262f28cSChanwoo Choi goto err; 285f262f28cSChanwoo Choi } 286f262f28cSChanwoo Choi 287f262f28cSChanwoo Choi return 0; 288f262f28cSChanwoo Choi 289f262f28cSChanwoo Choi err: 290f262f28cSChanwoo Choi iounmap(info->ppmu.base); 291f262f28cSChanwoo Choi 292f262f28cSChanwoo Choi return ret; 293f262f28cSChanwoo Choi } 294f262f28cSChanwoo Choi 295f262f28cSChanwoo Choi static int exynos_ppmu_probe(struct platform_device *pdev) 296f262f28cSChanwoo Choi { 297f262f28cSChanwoo Choi struct exynos_ppmu *info; 298f262f28cSChanwoo Choi struct devfreq_event_dev **edev; 299f262f28cSChanwoo Choi struct devfreq_event_desc *desc; 300f262f28cSChanwoo Choi int i, ret = 0, size; 301f262f28cSChanwoo Choi 302f262f28cSChanwoo Choi info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 303f262f28cSChanwoo Choi if (!info) 304f262f28cSChanwoo Choi return -ENOMEM; 305f262f28cSChanwoo Choi 306f262f28cSChanwoo Choi mutex_init(&info->lock); 307f262f28cSChanwoo Choi info->dev = &pdev->dev; 308f262f28cSChanwoo Choi 309f262f28cSChanwoo Choi /* Parse dt data to get resource */ 310f262f28cSChanwoo Choi ret = exynos_ppmu_parse_dt(info); 311f262f28cSChanwoo Choi if (ret < 0) { 312f262f28cSChanwoo Choi dev_err(&pdev->dev, 313f262f28cSChanwoo Choi "failed to parse devicetree for resource\n"); 314f262f28cSChanwoo Choi return ret; 315f262f28cSChanwoo Choi } 316f262f28cSChanwoo Choi desc = info->desc; 317f262f28cSChanwoo Choi 318f262f28cSChanwoo Choi size = sizeof(struct devfreq_event_dev *) * info->num_events; 319f262f28cSChanwoo Choi info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); 320f262f28cSChanwoo Choi if (!info->edev) { 321f262f28cSChanwoo Choi dev_err(&pdev->dev, 322f262f28cSChanwoo Choi "failed to allocate memory devfreq-event devices\n"); 323f262f28cSChanwoo Choi return -ENOMEM; 324f262f28cSChanwoo Choi } 325f262f28cSChanwoo Choi edev = info->edev; 326f262f28cSChanwoo Choi platform_set_drvdata(pdev, info); 327f262f28cSChanwoo Choi 328f262f28cSChanwoo Choi for (i = 0; i < info->num_events; i++) { 329f262f28cSChanwoo Choi edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); 33004a695edSDan Carpenter if (IS_ERR(edev[i])) { 33104a695edSDan Carpenter ret = PTR_ERR(edev[i]); 332f262f28cSChanwoo Choi dev_err(&pdev->dev, 333f262f28cSChanwoo Choi "failed to add devfreq-event device\n"); 334f262f28cSChanwoo Choi goto err; 335f262f28cSChanwoo Choi } 336f262f28cSChanwoo Choi } 337f262f28cSChanwoo Choi 338f262f28cSChanwoo Choi clk_prepare_enable(info->ppmu.clk); 339f262f28cSChanwoo Choi 340f262f28cSChanwoo Choi return 0; 341f262f28cSChanwoo Choi err: 342f262f28cSChanwoo Choi iounmap(info->ppmu.base); 343f262f28cSChanwoo Choi 344f262f28cSChanwoo Choi return ret; 345f262f28cSChanwoo Choi } 346f262f28cSChanwoo Choi 347f262f28cSChanwoo Choi static int exynos_ppmu_remove(struct platform_device *pdev) 348f262f28cSChanwoo Choi { 349f262f28cSChanwoo Choi struct exynos_ppmu *info = platform_get_drvdata(pdev); 350f262f28cSChanwoo Choi 351f262f28cSChanwoo Choi clk_disable_unprepare(info->ppmu.clk); 352f262f28cSChanwoo Choi iounmap(info->ppmu.base); 353f262f28cSChanwoo Choi 354f262f28cSChanwoo Choi return 0; 355f262f28cSChanwoo Choi } 356f262f28cSChanwoo Choi 357f262f28cSChanwoo Choi static struct of_device_id exynos_ppmu_id_match[] = { 358f262f28cSChanwoo Choi { .compatible = "samsung,exynos-ppmu", }, 359f262f28cSChanwoo Choi { /* sentinel */ }, 360f262f28cSChanwoo Choi }; 361f262f28cSChanwoo Choi 362f262f28cSChanwoo Choi static struct platform_driver exynos_ppmu_driver = { 363f262f28cSChanwoo Choi .probe = exynos_ppmu_probe, 364f262f28cSChanwoo Choi .remove = exynos_ppmu_remove, 365f262f28cSChanwoo Choi .driver = { 366f262f28cSChanwoo Choi .name = "exynos-ppmu", 367f262f28cSChanwoo Choi .of_match_table = exynos_ppmu_id_match, 368f262f28cSChanwoo Choi }, 369f262f28cSChanwoo Choi }; 370f262f28cSChanwoo Choi module_platform_driver(exynos_ppmu_driver); 371f262f28cSChanwoo Choi 372f262f28cSChanwoo Choi MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); 373f262f28cSChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 374f262f28cSChanwoo Choi MODULE_LICENSE("GPL"); 375