298 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * exynos-nocp.c - Exynos NoC (Network On Chip) Probe support4 *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 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);218 if (IS_ERR(base))219 return PTR_ERR(base);220 221 exynos_nocp_regmap_config.max_register = resource_size(res) - 4;222 223 nocp->regmap = devm_regmap_init_mmio(dev, base,224 &exynos_nocp_regmap_config);225 if (IS_ERR(nocp->regmap)) {226 dev_err(dev, "failed to initialize regmap\n");227 return PTR_ERR(nocp->regmap);228 }229 230 return 0;231}232 233static int exynos_nocp_probe(struct platform_device *pdev)234{235 struct device *dev = &pdev->dev;236 struct device_node *np = dev->of_node;237 struct exynos_nocp *nocp;238 int ret;239 240 nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);241 if (!nocp)242 return -ENOMEM;243 244 nocp->dev = &pdev->dev;245 246 /* Parse dt data to get resource */247 ret = exynos_nocp_parse_dt(pdev, nocp);248 if (ret < 0) {249 dev_err(&pdev->dev,250 "failed to parse devicetree for resource\n");251 return ret;252 }253 254 /* Add devfreq-event device to measure the bandwidth of NoC */255 nocp->desc.ops = &exynos_nocp_ops;256 nocp->desc.driver_data = nocp;257 nocp->desc.name = np->full_name;258 nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);259 if (IS_ERR(nocp->edev)) {260 dev_err(&pdev->dev,261 "failed to add devfreq-event device\n");262 return PTR_ERR(nocp->edev);263 }264 platform_set_drvdata(pdev, nocp);265 266 ret = clk_prepare_enable(nocp->clk);267 if (ret) {268 dev_err(&pdev->dev, "failed to prepare ppmu clock\n");269 return ret;270 }271 272 pr_info("exynos-nocp: new NoC Probe device registered: %s\n",273 dev_name(dev));274 275 return 0;276}277 278static void exynos_nocp_remove(struct platform_device *pdev)279{280 struct exynos_nocp *nocp = platform_get_drvdata(pdev);281 282 clk_disable_unprepare(nocp->clk);283}284 285static struct platform_driver exynos_nocp_driver = {286 .probe = exynos_nocp_probe,287 .remove_new = exynos_nocp_remove,288 .driver = {289 .name = "exynos-nocp",290 .of_match_table = exynos_nocp_id_match,291 },292};293module_platform_driver(exynos_nocp_driver);294 295MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");296MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");297MODULE_LICENSE("GPL");298