diff options
Diffstat (limited to 'drivers/devfreq/event/exynos-ppmu.c')
| -rw-r--r-- | drivers/devfreq/event/exynos-ppmu.c | 145 |
1 files changed, 89 insertions, 56 deletions
diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index d96e3dc71cf8..88cd4dfe87e1 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support + * exynos_ppmu.c - Exynos PPMU (Platform Performance Monitoring Unit) support * * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd. * Author : Chanwoo Choi <cw00.choi@samsung.com> * - * 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. - * * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c */ @@ -15,14 +12,20 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_address.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/suspend.h> #include <linux/devfreq-event.h> #include "exynos-ppmu.h" +enum exynos_ppmu_type { + EXYNOS_TYPE_PPMU, + EXYNOS_TYPE_PPMU_V2, +}; + struct exynos_ppmu_data { struct clk *clk; }; @@ -36,6 +39,7 @@ struct exynos_ppmu { struct regmap *regmap; struct exynos_ppmu_data ppmu; + enum exynos_ppmu_type ppmu_type; }; #define PPMU_EVENT(name) \ @@ -89,19 +93,35 @@ static struct __exynos_ppmu_events { PPMU_EVENT(d1-cpu), PPMU_EVENT(d1-general), PPMU_EVENT(d1-rt), + + /* For Exynos5422 SoC, deprecated (backwards compatible) */ + PPMU_EVENT(dmc0_0), + PPMU_EVENT(dmc0_1), + PPMU_EVENT(dmc1_0), + PPMU_EVENT(dmc1_1), + /* For Exynos5422 SoC */ + PPMU_EVENT(dmc0-0), + PPMU_EVENT(dmc0-1), + PPMU_EVENT(dmc1-0), + PPMU_EVENT(dmc1-1), }; -static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +static int __exynos_ppmu_find_ppmu_id(const char *edev_name) { int i; for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) - if (!strcmp(edev->desc->name, ppmu_events[i].name)) + if (!strcmp(edev_name, ppmu_events[i].name)) return ppmu_events[i].id; return -EINVAL; } +static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +{ + return __exynos_ppmu_find_ppmu_id(edev->desc->name); +} + /* * The devfreq-event ops structure for PPMU v1.1 */ @@ -154,9 +174,9 @@ static int exynos_ppmu_set_event(struct devfreq_event_dev *edev) if (ret < 0) return ret; - /* Set the event of Read/Write data count */ + /* Set the event of proper data type monitoring */ ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id), - PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT); + edev->desc->event_type); if (ret < 0) return ret; @@ -368,23 +388,11 @@ static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev) if (ret < 0) return ret; - /* Set the event of Read/Write data count */ - switch (id) { - case PPMU_PMNCNT0: - case PPMU_PMNCNT1: - case PPMU_PMNCNT2: - ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id), - PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT); - if (ret < 0) - return ret; - break; - case PPMU_PMNCNT3: - ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id), - PPMU_V2_EVT3_RW_DATA_CNT); - if (ret < 0) - return ret; - break; - } + /* Set the event of proper data type monitoring */ + ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id), + edev->desc->event_type); + if (ret < 0) + return ret; /* Reset cycle counter/performance counter and enable PPMU */ ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc); @@ -483,31 +491,23 @@ static const struct devfreq_event_ops exynos_ppmu_v2_ops = { static const struct of_device_id exynos_ppmu_id_match[] = { { .compatible = "samsung,exynos-ppmu", - .data = (void *)&exynos_ppmu_ops, + .data = (void *)EXYNOS_TYPE_PPMU, }, { .compatible = "samsung,exynos-ppmu-v2", - .data = (void *)&exynos_ppmu_v2_ops, + .data = (void *)EXYNOS_TYPE_PPMU_V2, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match); -static struct devfreq_event_ops *exynos_bus_get_ops(struct device_node *np) -{ - const struct of_device_id *match; - - match = of_match_node(exynos_ppmu_id_match, np); - return (struct devfreq_event_ops *)match->data; -} - static int of_get_devfreq_events(struct device_node *np, struct exynos_ppmu *info) { struct devfreq_event_desc *desc; - struct devfreq_event_ops *event_ops; struct device *dev = info->dev; struct device_node *events_np, *node; int i, j, count; + int ret; events_np = of_get_child_by_name(np, "events"); if (!events_np) { @@ -515,35 +515,74 @@ static int of_get_devfreq_events(struct device_node *np, "failed to get child node of devfreq-event devices\n"); return -EINVAL; } - event_ops = exynos_bus_get_ops(np); count = of_get_child_count(events_np); - desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); - if (!desc) + desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL); + if (!desc) { + of_node_put(events_np); return -ENOMEM; + } info->num_events = count; + info->ppmu_type = (enum exynos_ppmu_type)device_get_match_data(dev); + j = 0; for_each_child_of_node(events_np, node) { for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { if (!ppmu_events[i].name) continue; - if (!of_node_cmp(node->name, ppmu_events[i].name)) + if (of_node_name_eq(node, ppmu_events[i].name)) break; } if (i == ARRAY_SIZE(ppmu_events)) { dev_warn(dev, - "don't know how to configure events : %s\n", - node->name); + "don't know how to configure events : %pOFn\n", + node); continue; } - desc[j].ops = event_ops; + switch (info->ppmu_type) { + case EXYNOS_TYPE_PPMU: + desc[j].ops = &exynos_ppmu_ops; + break; + case EXYNOS_TYPE_PPMU_V2: + desc[j].ops = &exynos_ppmu_v2_ops; + break; + } + desc[j].driver_data = info; of_property_read_string(node, "event-name", &desc[j].name); + ret = of_property_read_u32(node, "event-data-type", + &desc[j].event_type); + if (ret) { + /* Set the event of proper data type counting. + * Check if the data type has been defined in DT, + * use default if not. + */ + if (info->ppmu_type == EXYNOS_TYPE_PPMU_V2) { + /* Not all registers take the same value for + * read+write data count. + */ + switch (ppmu_events[i].id) { + case PPMU_PMNCNT0: + case PPMU_PMNCNT1: + case PPMU_PMNCNT2: + desc[j].event_type = PPMU_V2_RO_DATA_CNT + | PPMU_V2_WO_DATA_CNT; + break; + case PPMU_PMNCNT3: + desc[j].event_type = + PPMU_V2_EVT3_RW_DATA_CNT; + break; + } + } else { + desc[j].event_type = PPMU_RO_DATA_CNT | + PPMU_WO_DATA_CNT; + } + } j++; } @@ -575,8 +614,7 @@ static int exynos_ppmu_parse_dt(struct platform_device *pdev, } /* Maps the memory mapped IO to control PPMU register */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base); @@ -627,18 +665,15 @@ static int exynos_ppmu_probe(struct platform_device *pdev) size = sizeof(struct devfreq_event_dev *) * info->num_events; info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!info->edev) { - dev_err(&pdev->dev, - "failed to allocate memory devfreq-event devices\n"); + if (!info->edev) return -ENOMEM; - } + edev = info->edev; platform_set_drvdata(pdev, info); for (i = 0; i < info->num_events; i++) { edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); if (IS_ERR(edev[i])) { - ret = PTR_ERR(edev[i]); dev_err(&pdev->dev, "failed to add devfreq-event device\n"); return PTR_ERR(edev[i]); @@ -657,18 +692,16 @@ static int exynos_ppmu_probe(struct platform_device *pdev) return 0; } -static int exynos_ppmu_remove(struct platform_device *pdev) +static void exynos_ppmu_remove(struct platform_device *pdev) { struct exynos_ppmu *info = platform_get_drvdata(pdev); clk_disable_unprepare(info->ppmu.clk); - - return 0; } static struct platform_driver exynos_ppmu_driver = { .probe = exynos_ppmu_probe, - .remove = exynos_ppmu_remove, + .remove = exynos_ppmu_remove, .driver = { .name = "exynos-ppmu", .of_match_table = exynos_ppmu_id_match, |
