715 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support4 *5 * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.6 * Author : Chanwoo Choi <cw00.choi@samsung.com>7 *8 * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c9 */10 11#include <linux/clk.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/property.h>18#include <linux/regmap.h>19#include <linux/suspend.h>20#include <linux/devfreq-event.h>21 22#include "exynos-ppmu.h"23 24enum exynos_ppmu_type {25 EXYNOS_TYPE_PPMU,26 EXYNOS_TYPE_PPMU_V2,27};28 29struct exynos_ppmu_data {30 struct clk *clk;31};32 33struct exynos_ppmu {34 struct devfreq_event_dev **edev;35 struct devfreq_event_desc *desc;36 unsigned int num_events;37 38 struct device *dev;39 struct regmap *regmap;40 41 struct exynos_ppmu_data ppmu;42 enum exynos_ppmu_type ppmu_type;43};44 45#define PPMU_EVENT(name) \46 { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \47 { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \48 { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \49 { "ppmu-event3-"#name, PPMU_PMNCNT3 }50 51static struct __exynos_ppmu_events {52 char *name;53 int id;54} ppmu_events[] = {55 /* For Exynos3250, Exynos4 and Exynos5260 */56 PPMU_EVENT(g3d),57 PPMU_EVENT(fsys),58 59 /* For Exynos4 SoCs and Exynos3250 */60 PPMU_EVENT(dmc0),61 PPMU_EVENT(dmc1),62 PPMU_EVENT(cpu),63 PPMU_EVENT(rightbus),64 PPMU_EVENT(leftbus),65 PPMU_EVENT(lcd0),66 PPMU_EVENT(camif),67 68 /* Only for Exynos3250 and Exynos5260 */69 PPMU_EVENT(mfc),70 71 /* Only for Exynos4 SoCs */72 PPMU_EVENT(mfc-left),73 PPMU_EVENT(mfc-right),74 75 /* Only for Exynos5260 SoCs */76 PPMU_EVENT(drex0-s0),77 PPMU_EVENT(drex0-s1),78 PPMU_EVENT(drex1-s0),79 PPMU_EVENT(drex1-s1),80 PPMU_EVENT(eagle),81 PPMU_EVENT(kfc),82 PPMU_EVENT(isp),83 PPMU_EVENT(fimc),84 PPMU_EVENT(gscl),85 PPMU_EVENT(mscl),86 PPMU_EVENT(fimd0x),87 PPMU_EVENT(fimd1x),88 89 /* Only for Exynos5433 SoCs */90 PPMU_EVENT(d0-cpu),91 PPMU_EVENT(d0-general),92 PPMU_EVENT(d0-rt),93 PPMU_EVENT(d1-cpu),94 PPMU_EVENT(d1-general),95 PPMU_EVENT(d1-rt),96 97 /* For Exynos5422 SoC, deprecated (backwards compatible) */98 PPMU_EVENT(dmc0_0),99 PPMU_EVENT(dmc0_1),100 PPMU_EVENT(dmc1_0),101 PPMU_EVENT(dmc1_1),102 /* For Exynos5422 SoC */103 PPMU_EVENT(dmc0-0),104 PPMU_EVENT(dmc0-1),105 PPMU_EVENT(dmc1-0),106 PPMU_EVENT(dmc1-1),107};108 109static int __exynos_ppmu_find_ppmu_id(const char *edev_name)110{111 int i;112 113 for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)114 if (!strcmp(edev_name, ppmu_events[i].name))115 return ppmu_events[i].id;116 117 return -EINVAL;118}119 120static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)121{122 return __exynos_ppmu_find_ppmu_id(edev->desc->name);123}124 125/*126 * The devfreq-event ops structure for PPMU v1.1127 */128static int exynos_ppmu_disable(struct devfreq_event_dev *edev)129{130 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);131 int ret;132 u32 pmnc;133 134 /* Disable all counters */135 ret = regmap_write(info->regmap, PPMU_CNTENC,136 PPMU_CCNT_MASK |137 PPMU_PMCNT0_MASK |138 PPMU_PMCNT1_MASK |139 PPMU_PMCNT2_MASK |140 PPMU_PMCNT3_MASK);141 if (ret < 0)142 return ret;143 144 /* Disable PPMU */145 ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);146 if (ret < 0)147 return ret;148 149 pmnc &= ~PPMU_PMNC_ENABLE_MASK;150 ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);151 if (ret < 0)152 return ret;153 154 return 0;155}156 157static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)158{159 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);160 int id = exynos_ppmu_find_ppmu_id(edev);161 int ret;162 u32 pmnc, cntens;163 164 if (id < 0)165 return id;166 167 /* Enable specific counter */168 ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);169 if (ret < 0)170 return ret;171 172 cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));173 ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);174 if (ret < 0)175 return ret;176 177 /* Set the event of proper data type monitoring */178 ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),179 edev->desc->event_type);180 if (ret < 0)181 return ret;182 183 /* Reset cycle counter/performance counter and enable PPMU */184 ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);185 if (ret < 0)186 return ret;187 188 pmnc &= ~(PPMU_PMNC_ENABLE_MASK189 | PPMU_PMNC_COUNTER_RESET_MASK190 | PPMU_PMNC_CC_RESET_MASK);191 pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);192 pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);193 pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);194 ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);195 if (ret < 0)196 return ret;197 198 return 0;199}200 201static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,202 struct devfreq_event_data *edata)203{204 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);205 int id = exynos_ppmu_find_ppmu_id(edev);206 unsigned int total_count, load_count;207 unsigned int pmcnt3_high, pmcnt3_low;208 unsigned int pmnc, cntenc;209 int ret;210 211 if (id < 0)212 return -EINVAL;213 214 /* Disable PPMU */215 ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);216 if (ret < 0)217 return ret;218 219 pmnc &= ~PPMU_PMNC_ENABLE_MASK;220 ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);221 if (ret < 0)222 return ret;223 224 /* Read cycle count */225 ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);226 if (ret < 0)227 return ret;228 edata->total_count = total_count;229 230 /* Read performance count */231 switch (id) {232 case PPMU_PMNCNT0:233 case PPMU_PMNCNT1:234 case PPMU_PMNCNT2:235 ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);236 if (ret < 0)237 return ret;238 edata->load_count = load_count;239 break;240 case PPMU_PMNCNT3:241 ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);242 if (ret < 0)243 return ret;244 245 ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);246 if (ret < 0)247 return ret;248 249 edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);250 break;251 default:252 return -EINVAL;253 }254 255 /* Disable specific counter */256 ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);257 if (ret < 0)258 return ret;259 260 cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));261 ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);262 if (ret < 0)263 return ret;264 265 dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,266 edata->load_count, edata->total_count);267 268 return 0;269}270 271static const struct devfreq_event_ops exynos_ppmu_ops = {272 .disable = exynos_ppmu_disable,273 .set_event = exynos_ppmu_set_event,274 .get_event = exynos_ppmu_get_event,275};276 277/*278 * The devfreq-event ops structure for PPMU v2.0279 */280static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)281{282 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);283 int ret;284 u32 pmnc, clear;285 286 /* Disable all counters */287 clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK288 | PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);289 ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);290 if (ret < 0)291 return ret;292 293 ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);294 if (ret < 0)295 return ret;296 297 ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);298 if (ret < 0)299 return ret;300 301 ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);302 if (ret < 0)303 return ret;304 305 ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);306 if (ret < 0)307 return ret;308 309 ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);310 if (ret < 0)311 return ret;312 313 ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);314 if (ret < 0)315 return ret;316 317 ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);318 if (ret < 0)319 return ret;320 321 ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);322 if (ret < 0)323 return ret;324 325 ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);326 if (ret < 0)327 return ret;328 329 ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);330 if (ret < 0)331 return ret;332 333 ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);334 if (ret < 0)335 return ret;336 337 ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);338 if (ret < 0)339 return ret;340 341 ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);342 if (ret < 0)343 return ret;344 345 ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);346 if (ret < 0)347 return ret;348 349 ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);350 if (ret < 0)351 return ret;352 353 ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);354 if (ret < 0)355 return ret;356 357 ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);358 if (ret < 0)359 return ret;360 361 /* Disable PPMU */362 ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);363 if (ret < 0)364 return ret;365 366 pmnc &= ~PPMU_PMNC_ENABLE_MASK;367 ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);368 if (ret < 0)369 return ret;370 371 return 0;372}373 374static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)375{376 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);377 unsigned int pmnc, cntens;378 int id = exynos_ppmu_find_ppmu_id(edev);379 int ret;380 381 /* Enable all counters */382 ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);383 if (ret < 0)384 return ret;385 386 cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));387 ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);388 if (ret < 0)389 return ret;390 391 /* Set the event of proper data type monitoring */392 ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),393 edev->desc->event_type);394 if (ret < 0)395 return ret;396 397 /* Reset cycle counter/performance counter and enable PPMU */398 ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);399 if (ret < 0)400 return ret;401 402 pmnc &= ~(PPMU_PMNC_ENABLE_MASK403 | PPMU_PMNC_COUNTER_RESET_MASK404 | PPMU_PMNC_CC_RESET_MASK405 | PPMU_PMNC_CC_DIVIDER_MASK406 | PPMU_V2_PMNC_START_MODE_MASK);407 pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);408 pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);409 pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);410 pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);411 412 ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);413 if (ret < 0)414 return ret;415 416 return 0;417}418 419static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,420 struct devfreq_event_data *edata)421{422 struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);423 int id = exynos_ppmu_find_ppmu_id(edev);424 int ret;425 unsigned int pmnc, cntenc;426 unsigned int pmcnt_high, pmcnt_low;427 unsigned int total_count, count;428 unsigned long load_count = 0;429 430 /* Disable PPMU */431 ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);432 if (ret < 0)433 return ret;434 435 pmnc &= ~PPMU_PMNC_ENABLE_MASK;436 ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);437 if (ret < 0)438 return ret;439 440 /* Read cycle count and performance count */441 ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);442 if (ret < 0)443 return ret;444 edata->total_count = total_count;445 446 switch (id) {447 case PPMU_PMNCNT0:448 case PPMU_PMNCNT1:449 case PPMU_PMNCNT2:450 ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);451 if (ret < 0)452 return ret;453 load_count = count;454 break;455 case PPMU_PMNCNT3:456 ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,457 &pmcnt_high);458 if (ret < 0)459 return ret;460 461 ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);462 if (ret < 0)463 return ret;464 465 load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;466 break;467 }468 edata->load_count = load_count;469 470 /* Disable all counters */471 ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);472 if (ret < 0)473 return 0;474 475 cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));476 ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);477 if (ret < 0)478 return ret;479 480 dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,481 edata->load_count, edata->total_count);482 return 0;483}484 485static const struct devfreq_event_ops exynos_ppmu_v2_ops = {486 .disable = exynos_ppmu_v2_disable,487 .set_event = exynos_ppmu_v2_set_event,488 .get_event = exynos_ppmu_v2_get_event,489};490 491static const struct of_device_id exynos_ppmu_id_match[] = {492 {493 .compatible = "samsung,exynos-ppmu",494 .data = (void *)EXYNOS_TYPE_PPMU,495 }, {496 .compatible = "samsung,exynos-ppmu-v2",497 .data = (void *)EXYNOS_TYPE_PPMU_V2,498 },499 { /* sentinel */ },500};501MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);502 503static int of_get_devfreq_events(struct device_node *np,504 struct exynos_ppmu *info)505{506 struct devfreq_event_desc *desc;507 struct device *dev = info->dev;508 struct device_node *events_np, *node;509 int i, j, count;510 int ret;511 512 events_np = of_get_child_by_name(np, "events");513 if (!events_np) {514 dev_err(dev,515 "failed to get child node of devfreq-event devices\n");516 return -EINVAL;517 }518 519 count = of_get_child_count(events_np);520 desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);521 if (!desc) {522 of_node_put(events_np);523 return -ENOMEM;524 }525 info->num_events = count;526 527 info->ppmu_type = (enum exynos_ppmu_type)device_get_match_data(dev);528 529 j = 0;530 for_each_child_of_node(events_np, node) {531 for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {532 if (!ppmu_events[i].name)533 continue;534 535 if (of_node_name_eq(node, ppmu_events[i].name))536 break;537 }538 539 if (i == ARRAY_SIZE(ppmu_events)) {540 dev_warn(dev,541 "don't know how to configure events : %pOFn\n",542 node);543 continue;544 }545 546 switch (info->ppmu_type) {547 case EXYNOS_TYPE_PPMU:548 desc[j].ops = &exynos_ppmu_ops;549 break;550 case EXYNOS_TYPE_PPMU_V2:551 desc[j].ops = &exynos_ppmu_v2_ops;552 break;553 }554 555 desc[j].driver_data = info;556 557 of_property_read_string(node, "event-name", &desc[j].name);558 ret = of_property_read_u32(node, "event-data-type",559 &desc[j].event_type);560 if (ret) {561 /* Set the event of proper data type counting.562 * Check if the data type has been defined in DT,563 * use default if not.564 */565 if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) {566 /* Not all registers take the same value for567 * read+write data count.568 */569 switch (ppmu_events[i].id) {570 case PPMU_PMNCNT0:571 case PPMU_PMNCNT1:572 case PPMU_PMNCNT2:573 desc[j].event_type = PPMU_V2_RO_DATA_CNT574 | PPMU_V2_WO_DATA_CNT;575 break;576 case PPMU_PMNCNT3:577 desc[j].event_type =578 PPMU_V2_EVT3_RW_DATA_CNT;579 break;580 }581 } else {582 desc[j].event_type = PPMU_RO_DATA_CNT |583 PPMU_WO_DATA_CNT;584 }585 }586 587 j++;588 }589 info->desc = desc;590 591 of_node_put(events_np);592 593 return 0;594}595 596static struct regmap_config exynos_ppmu_regmap_config = {597 .reg_bits = 32,598 .val_bits = 32,599 .reg_stride = 4,600};601 602static int exynos_ppmu_parse_dt(struct platform_device *pdev,603 struct exynos_ppmu *info)604{605 struct device *dev = info->dev;606 struct device_node *np = dev->of_node;607 struct resource *res;608 void __iomem *base;609 int ret = 0;610 611 if (!np) {612 dev_err(dev, "failed to find devicetree node\n");613 return -EINVAL;614 }615 616 /* Maps the memory mapped IO to control PPMU register */617 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);618 if (IS_ERR(base))619 return PTR_ERR(base);620 621 exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;622 info->regmap = devm_regmap_init_mmio(dev, base,623 &exynos_ppmu_regmap_config);624 if (IS_ERR(info->regmap)) {625 dev_err(dev, "failed to initialize regmap\n");626 return PTR_ERR(info->regmap);627 }628 629 info->ppmu.clk = devm_clk_get(dev, "ppmu");630 if (IS_ERR(info->ppmu.clk)) {631 info->ppmu.clk = NULL;632 dev_warn(dev, "cannot get PPMU clock\n");633 }634 635 ret = of_get_devfreq_events(np, info);636 if (ret < 0) {637 dev_err(dev, "failed to parse exynos ppmu dt node\n");638 return ret;639 }640 641 return 0;642}643 644static int exynos_ppmu_probe(struct platform_device *pdev)645{646 struct exynos_ppmu *info;647 struct devfreq_event_dev **edev;648 struct devfreq_event_desc *desc;649 int i, ret = 0, size;650 651 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);652 if (!info)653 return -ENOMEM;654 655 info->dev = &pdev->dev;656 657 /* Parse dt data to get resource */658 ret = exynos_ppmu_parse_dt(pdev, info);659 if (ret < 0) {660 dev_err(&pdev->dev,661 "failed to parse devicetree for resource\n");662 return ret;663 }664 desc = info->desc;665 666 size = sizeof(struct devfreq_event_dev *) * info->num_events;667 info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);668 if (!info->edev)669 return -ENOMEM;670 671 edev = info->edev;672 platform_set_drvdata(pdev, info);673 674 for (i = 0; i < info->num_events; i++) {675 edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);676 if (IS_ERR(edev[i])) {677 dev_err(&pdev->dev,678 "failed to add devfreq-event device\n");679 return PTR_ERR(edev[i]);680 }681 682 pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",683 dev_name(&pdev->dev), desc[i].name);684 }685 686 ret = clk_prepare_enable(info->ppmu.clk);687 if (ret) {688 dev_err(&pdev->dev, "failed to prepare ppmu clock\n");689 return ret;690 }691 692 return 0;693}694 695static void exynos_ppmu_remove(struct platform_device *pdev)696{697 struct exynos_ppmu *info = platform_get_drvdata(pdev);698 699 clk_disable_unprepare(info->ppmu.clk);700}701 702static struct platform_driver exynos_ppmu_driver = {703 .probe = exynos_ppmu_probe,704 .remove_new = exynos_ppmu_remove,705 .driver = {706 .name = "exynos-ppmu",707 .of_match_table = exynos_ppmu_id_match,708 },709};710module_platform_driver(exynos_ppmu_driver);711 712MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");713MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");714MODULE_LICENSE("GPL");715