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