brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 5edc522 Raw
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