linux/drivers/devfreq/event/exynos-nocp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * exynos-nocp.c - Exynos NoC (Network On Chip) Probe support
   4 *
   5 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
   6 * Author : Chanwoo Choi <cw00.choi@samsung.com>
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/module.h>
  11#include <linux/devfreq-event.h>
  12#include <linux/kernel.h>
  13#include <linux/of_address.h>
  14#include <linux/platform_device.h>
  15#include <linux/regmap.h>
  16
  17#include "exynos-nocp.h"
  18
  19struct exynos_nocp {
  20        struct devfreq_event_dev *edev;
  21        struct devfreq_event_desc desc;
  22
  23        struct device *dev;
  24
  25        struct regmap *regmap;
  26        struct clk *clk;
  27};
  28
  29/*
  30 * The devfreq-event ops structure for nocp probe.
  31 */
  32static int exynos_nocp_set_event(struct devfreq_event_dev *edev)
  33{
  34        struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
  35        int ret;
  36
  37        /* Disable NoC probe */
  38        ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
  39                                NOCP_MAIN_CTL_STATEN_MASK, 0);
  40        if (ret < 0) {
  41                dev_err(nocp->dev, "failed to disable the NoC probe device\n");
  42                return ret;
  43        }
  44
  45        /* Set a statistics dump period to 0 */
  46        ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0);
  47        if (ret < 0)
  48                goto out;
  49
  50        /* Set the IntEvent fields of *_SRC */
  51        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC,
  52                                NOCP_CNT_SRC_INTEVENT_MASK,
  53                                NOCP_CNT_SRC_INTEVENT_BYTE_MASK);
  54        if (ret < 0)
  55                goto out;
  56
  57        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC,
  58                                NOCP_CNT_SRC_INTEVENT_MASK,
  59                                NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
  60        if (ret < 0)
  61                goto out;
  62
  63        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC,
  64                                NOCP_CNT_SRC_INTEVENT_MASK,
  65                                NOCP_CNT_SRC_INTEVENT_CYCLE_MASK);
  66        if (ret < 0)
  67                goto out;
  68
  69        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC,
  70                                NOCP_CNT_SRC_INTEVENT_MASK,
  71                                NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
  72        if (ret < 0)
  73                goto out;
  74
  75
  76        /* Set an alarm with a max/min value of 0 to generate StatALARM */
  77        ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0);
  78        if (ret < 0)
  79                goto out;
  80
  81        ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0);
  82        if (ret < 0)
  83                goto out;
  84
  85        /* Set AlarmMode */
  86        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE,
  87                                NOCP_CNT_ALARM_MODE_MASK,
  88                                NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
  89        if (ret < 0)
  90                goto out;
  91
  92        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE,
  93                                NOCP_CNT_ALARM_MODE_MASK,
  94                                NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
  95        if (ret < 0)
  96                goto out;
  97
  98        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE,
  99                                NOCP_CNT_ALARM_MODE_MASK,
 100                                NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
 101        if (ret < 0)
 102                goto out;
 103
 104        ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE,
 105                                NOCP_CNT_ALARM_MODE_MASK,
 106                                NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
 107        if (ret < 0)
 108                goto out;
 109
 110        /* Enable the measurements by setting AlarmEn and StatEn */
 111        ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
 112                        NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK,
 113                        NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK);
 114        if (ret < 0)
 115                goto out;
 116
 117        /* Set GlobalEN */
 118        ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL,
 119                                NOCP_CFG_CTL_GLOBALEN_MASK,
 120                                NOCP_CFG_CTL_GLOBALEN_MASK);
 121        if (ret < 0)
 122                goto out;
 123
 124        /* Enable NoC probe */
 125        ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
 126                                NOCP_MAIN_CTL_STATEN_MASK,
 127                                NOCP_MAIN_CTL_STATEN_MASK);
 128        if (ret < 0)
 129                goto out;
 130
 131        return 0;
 132
 133out:
 134        /* Reset NoC probe */
 135        if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
 136                                NOCP_MAIN_CTL_STATEN_MASK, 0)) {
 137                dev_err(nocp->dev, "Failed to reset NoC probe device\n");
 138        }
 139
 140        return ret;
 141}
 142
 143static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
 144                                struct devfreq_event_data *edata)
 145{
 146        struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
 147        unsigned int counter[4];
 148        int ret;
 149
 150        /* Read cycle count */
 151        ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]);
 152        if (ret < 0)
 153                goto out;
 154
 155        ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]);
 156        if (ret < 0)
 157                goto out;
 158
 159        ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]);
 160        if (ret < 0)
 161                goto out;
 162
 163        ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]);
 164        if (ret < 0)
 165                goto out;
 166
 167        edata->load_count = ((counter[1] << 16) | counter[0]);
 168        edata->total_count = ((counter[3] << 16) | counter[2]);
 169
 170        dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
 171                                        edata->load_count, edata->total_count);
 172
 173        return 0;
 174
 175out:
 176        dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
 177
 178        return ret;
 179}
 180
 181static const struct devfreq_event_ops exynos_nocp_ops = {
 182        .set_event = exynos_nocp_set_event,
 183        .get_event = exynos_nocp_get_event,
 184};
 185
 186static const struct of_device_id exynos_nocp_id_match[] = {
 187        { .compatible = "samsung,exynos5420-nocp", },
 188        { /* sentinel */ },
 189};
 190MODULE_DEVICE_TABLE(of, exynos_nocp_id_match);
 191
 192static struct regmap_config exynos_nocp_regmap_config = {
 193        .reg_bits = 32,
 194        .val_bits = 32,
 195        .reg_stride = 4,
 196        .max_register = NOCP_COUNTERS_3_VAL,
 197};
 198
 199static int exynos_nocp_parse_dt(struct platform_device *pdev,
 200                                struct exynos_nocp *nocp)
 201{
 202        struct device *dev = nocp->dev;
 203        struct device_node *np = dev->of_node;
 204        struct resource *res;
 205        void __iomem *base;
 206
 207        if (!np) {
 208                dev_err(dev, "failed to find devicetree node\n");
 209                return -EINVAL;
 210        }
 211
 212        nocp->clk = devm_clk_get(dev, "nocp");
 213        if (IS_ERR(nocp->clk))
 214                nocp->clk = NULL;
 215
 216        /* Maps the memory mapped IO to control nocp register */
 217        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 218        base = devm_ioremap_resource(dev, res);
 219        if (IS_ERR(base))
 220                return PTR_ERR(base);
 221
 222        exynos_nocp_regmap_config.max_register = resource_size(res) - 4;
 223
 224        nocp->regmap = devm_regmap_init_mmio(dev, base,
 225                                        &exynos_nocp_regmap_config);
 226        if (IS_ERR(nocp->regmap)) {
 227                dev_err(dev, "failed to initialize regmap\n");
 228                return PTR_ERR(nocp->regmap);
 229        }
 230
 231        return 0;
 232}
 233
 234static int exynos_nocp_probe(struct platform_device *pdev)
 235{
 236        struct device *dev = &pdev->dev;
 237        struct device_node *np = dev->of_node;
 238        struct exynos_nocp *nocp;
 239        int ret;
 240
 241        nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);
 242        if (!nocp)
 243                return -ENOMEM;
 244
 245        nocp->dev = &pdev->dev;
 246
 247        /* Parse dt data to get resource */
 248        ret = exynos_nocp_parse_dt(pdev, nocp);
 249        if (ret < 0) {
 250                dev_err(&pdev->dev,
 251                        "failed to parse devicetree for resource\n");
 252                return ret;
 253        }
 254
 255        /* Add devfreq-event device to measure the bandwidth of NoC */
 256        nocp->desc.ops = &exynos_nocp_ops;
 257        nocp->desc.driver_data = nocp;
 258        nocp->desc.name = np->full_name;
 259        nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);
 260        if (IS_ERR(nocp->edev)) {
 261                dev_err(&pdev->dev,
 262                        "failed to add devfreq-event device\n");
 263                return PTR_ERR(nocp->edev);
 264        }
 265        platform_set_drvdata(pdev, nocp);
 266
 267        ret = clk_prepare_enable(nocp->clk);
 268        if (ret) {
 269                dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
 270                return ret;
 271        }
 272
 273        pr_info("exynos-nocp: new NoC Probe device registered: %s\n",
 274                        dev_name(dev));
 275
 276        return 0;
 277}
 278
 279static int exynos_nocp_remove(struct platform_device *pdev)
 280{
 281        struct exynos_nocp *nocp = platform_get_drvdata(pdev);
 282
 283        clk_disable_unprepare(nocp->clk);
 284
 285        return 0;
 286}
 287
 288static struct platform_driver exynos_nocp_driver = {
 289        .probe  = exynos_nocp_probe,
 290        .remove = exynos_nocp_remove,
 291        .driver = {
 292                .name   = "exynos-nocp",
 293                .of_match_table = exynos_nocp_id_match,
 294        },
 295};
 296module_platform_driver(exynos_nocp_driver);
 297
 298MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");
 299MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
 300MODULE_LICENSE("GPL");
 301