1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20179a913SChanwoo Choi /* 30179a913SChanwoo Choi * exynos-nocp.c - EXYNOS NoC (Network On Chip) Probe support 40179a913SChanwoo Choi * 50179a913SChanwoo Choi * Copyright (c) 2016 Samsung Electronics Co., Ltd. 60179a913SChanwoo Choi * Author : Chanwoo Choi <cw00.choi@samsung.com> 70179a913SChanwoo Choi */ 80179a913SChanwoo Choi 90179a913SChanwoo Choi #include <linux/clk.h> 100179a913SChanwoo Choi #include <linux/module.h> 110179a913SChanwoo Choi #include <linux/devfreq-event.h> 120179a913SChanwoo Choi #include <linux/kernel.h> 130179a913SChanwoo Choi #include <linux/of_address.h> 140179a913SChanwoo Choi #include <linux/platform_device.h> 150179a913SChanwoo Choi #include <linux/regmap.h> 160179a913SChanwoo Choi 170179a913SChanwoo Choi #include "exynos-nocp.h" 180179a913SChanwoo Choi 190179a913SChanwoo Choi struct exynos_nocp { 200179a913SChanwoo Choi struct devfreq_event_dev *edev; 210179a913SChanwoo Choi struct devfreq_event_desc desc; 220179a913SChanwoo Choi 230179a913SChanwoo Choi struct device *dev; 240179a913SChanwoo Choi 250179a913SChanwoo Choi struct regmap *regmap; 260179a913SChanwoo Choi struct clk *clk; 270179a913SChanwoo Choi }; 280179a913SChanwoo Choi 290179a913SChanwoo Choi /* 300179a913SChanwoo Choi * The devfreq-event ops structure for nocp probe. 310179a913SChanwoo Choi */ 320179a913SChanwoo Choi static int exynos_nocp_set_event(struct devfreq_event_dev *edev) 330179a913SChanwoo Choi { 340179a913SChanwoo Choi struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); 350179a913SChanwoo Choi int ret; 360179a913SChanwoo Choi 370179a913SChanwoo Choi /* Disable NoC probe */ 380179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, 390179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK, 0); 400179a913SChanwoo Choi if (ret < 0) { 410179a913SChanwoo Choi dev_err(nocp->dev, "failed to disable the NoC probe device\n"); 420179a913SChanwoo Choi return ret; 430179a913SChanwoo Choi } 440179a913SChanwoo Choi 450179a913SChanwoo Choi /* Set a statistics dump period to 0 */ 460179a913SChanwoo Choi ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0); 470179a913SChanwoo Choi if (ret < 0) 480179a913SChanwoo Choi goto out; 490179a913SChanwoo Choi 500179a913SChanwoo Choi /* Set the IntEvent fields of *_SRC */ 510179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC, 520179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_MASK, 530179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_BYTE_MASK); 540179a913SChanwoo Choi if (ret < 0) 550179a913SChanwoo Choi goto out; 560179a913SChanwoo Choi 570179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC, 580179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_MASK, 590179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); 600179a913SChanwoo Choi if (ret < 0) 610179a913SChanwoo Choi goto out; 620179a913SChanwoo Choi 630179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC, 640179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_MASK, 650179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_CYCLE_MASK); 660179a913SChanwoo Choi if (ret < 0) 670179a913SChanwoo Choi goto out; 680179a913SChanwoo Choi 690179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC, 700179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_MASK, 710179a913SChanwoo Choi NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); 720179a913SChanwoo Choi if (ret < 0) 730179a913SChanwoo Choi goto out; 740179a913SChanwoo Choi 750179a913SChanwoo Choi 760179a913SChanwoo Choi /* Set an alarm with a max/min value of 0 to generate StatALARM */ 770179a913SChanwoo Choi ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0); 780179a913SChanwoo Choi if (ret < 0) 790179a913SChanwoo Choi goto out; 800179a913SChanwoo Choi 810179a913SChanwoo Choi ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0); 820179a913SChanwoo Choi if (ret < 0) 830179a913SChanwoo Choi goto out; 840179a913SChanwoo Choi 850179a913SChanwoo Choi /* Set AlarmMode */ 860179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE, 870179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MASK, 880179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); 890179a913SChanwoo Choi if (ret < 0) 900179a913SChanwoo Choi goto out; 910179a913SChanwoo Choi 920179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE, 930179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MASK, 940179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); 950179a913SChanwoo Choi if (ret < 0) 960179a913SChanwoo Choi goto out; 970179a913SChanwoo Choi 980179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE, 990179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MASK, 1000179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); 1010179a913SChanwoo Choi if (ret < 0) 1020179a913SChanwoo Choi goto out; 1030179a913SChanwoo Choi 1040179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE, 1050179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MASK, 1060179a913SChanwoo Choi NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); 1070179a913SChanwoo Choi if (ret < 0) 1080179a913SChanwoo Choi goto out; 1090179a913SChanwoo Choi 1100179a913SChanwoo Choi /* Enable the measurements by setting AlarmEn and StatEn */ 1110179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, 1120179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK, 1130179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK); 1140179a913SChanwoo Choi if (ret < 0) 1150179a913SChanwoo Choi goto out; 1160179a913SChanwoo Choi 1170179a913SChanwoo Choi /* Set GlobalEN */ 1180179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL, 1190179a913SChanwoo Choi NOCP_CFG_CTL_GLOBALEN_MASK, 1200179a913SChanwoo Choi NOCP_CFG_CTL_GLOBALEN_MASK); 1210179a913SChanwoo Choi if (ret < 0) 1220179a913SChanwoo Choi goto out; 1230179a913SChanwoo Choi 1240179a913SChanwoo Choi /* Enable NoC probe */ 1250179a913SChanwoo Choi ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, 1260179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK, 1270179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK); 1280179a913SChanwoo Choi if (ret < 0) 1290179a913SChanwoo Choi goto out; 1300179a913SChanwoo Choi 1310179a913SChanwoo Choi return 0; 1320179a913SChanwoo Choi 1330179a913SChanwoo Choi out: 1340179a913SChanwoo Choi /* Reset NoC probe */ 1350179a913SChanwoo Choi if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, 1360179a913SChanwoo Choi NOCP_MAIN_CTL_STATEN_MASK, 0)) { 1370179a913SChanwoo Choi dev_err(nocp->dev, "Failed to reset NoC probe device\n"); 1380179a913SChanwoo Choi } 1390179a913SChanwoo Choi 1400179a913SChanwoo Choi return ret; 1410179a913SChanwoo Choi } 1420179a913SChanwoo Choi 1430179a913SChanwoo Choi static int exynos_nocp_get_event(struct devfreq_event_dev *edev, 1440179a913SChanwoo Choi struct devfreq_event_data *edata) 1450179a913SChanwoo Choi { 1460179a913SChanwoo Choi struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); 1470179a913SChanwoo Choi unsigned int counter[4]; 1480179a913SChanwoo Choi int ret; 1490179a913SChanwoo Choi 1500179a913SChanwoo Choi /* Read cycle count */ 1510179a913SChanwoo Choi ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]); 1520179a913SChanwoo Choi if (ret < 0) 1530179a913SChanwoo Choi goto out; 1540179a913SChanwoo Choi 1550179a913SChanwoo Choi ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]); 1560179a913SChanwoo Choi if (ret < 0) 1570179a913SChanwoo Choi goto out; 1580179a913SChanwoo Choi 1590179a913SChanwoo Choi ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]); 1600179a913SChanwoo Choi if (ret < 0) 1610179a913SChanwoo Choi goto out; 1620179a913SChanwoo Choi 1630179a913SChanwoo Choi ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]); 1640179a913SChanwoo Choi if (ret < 0) 1650179a913SChanwoo Choi goto out; 1660179a913SChanwoo Choi 1670179a913SChanwoo Choi edata->load_count = ((counter[1] << 16) | counter[0]); 1680179a913SChanwoo Choi edata->total_count = ((counter[3] << 16) | counter[2]); 1690179a913SChanwoo Choi 1700179a913SChanwoo Choi dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, 1710179a913SChanwoo Choi edata->load_count, edata->total_count); 1720179a913SChanwoo Choi 1730179a913SChanwoo Choi return 0; 1740179a913SChanwoo Choi 1750179a913SChanwoo Choi out: 1760179a913SChanwoo Choi dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n"); 1770179a913SChanwoo Choi 1780179a913SChanwoo Choi return ret; 1790179a913SChanwoo Choi } 1800179a913SChanwoo Choi 1810179a913SChanwoo Choi static const struct devfreq_event_ops exynos_nocp_ops = { 1820179a913SChanwoo Choi .set_event = exynos_nocp_set_event, 1830179a913SChanwoo Choi .get_event = exynos_nocp_get_event, 1840179a913SChanwoo Choi }; 1850179a913SChanwoo Choi 1860179a913SChanwoo Choi static const struct of_device_id exynos_nocp_id_match[] = { 1870179a913SChanwoo Choi { .compatible = "samsung,exynos5420-nocp", }, 1880179a913SChanwoo Choi { /* sentinel */ }, 1890179a913SChanwoo Choi }; 190ca5c3b21SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, exynos_nocp_id_match); 1910179a913SChanwoo Choi 1920179a913SChanwoo Choi static struct regmap_config exynos_nocp_regmap_config = { 1930179a913SChanwoo Choi .reg_bits = 32, 1940179a913SChanwoo Choi .val_bits = 32, 1950179a913SChanwoo Choi .reg_stride = 4, 1960179a913SChanwoo Choi .max_register = NOCP_COUNTERS_3_VAL, 1970179a913SChanwoo Choi }; 1980179a913SChanwoo Choi 1990179a913SChanwoo Choi static int exynos_nocp_parse_dt(struct platform_device *pdev, 2000179a913SChanwoo Choi struct exynos_nocp *nocp) 2010179a913SChanwoo Choi { 2020179a913SChanwoo Choi struct device *dev = nocp->dev; 2030179a913SChanwoo Choi struct device_node *np = dev->of_node; 2040179a913SChanwoo Choi struct resource *res; 2050179a913SChanwoo Choi void __iomem *base; 2060179a913SChanwoo Choi 2070179a913SChanwoo Choi if (!np) { 2080179a913SChanwoo Choi dev_err(dev, "failed to find devicetree node\n"); 2090179a913SChanwoo Choi return -EINVAL; 2100179a913SChanwoo Choi } 2110179a913SChanwoo Choi 2120179a913SChanwoo Choi nocp->clk = devm_clk_get(dev, "nocp"); 2130179a913SChanwoo Choi if (IS_ERR(nocp->clk)) 2140179a913SChanwoo Choi nocp->clk = NULL; 2150179a913SChanwoo Choi 2160179a913SChanwoo Choi /* Maps the memory mapped IO to control nocp register */ 2170179a913SChanwoo Choi res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2180179a913SChanwoo Choi base = devm_ioremap_resource(dev, res); 2190179a913SChanwoo Choi if (IS_ERR(base)) 2200179a913SChanwoo Choi return PTR_ERR(base); 2210179a913SChanwoo Choi 2220179a913SChanwoo Choi exynos_nocp_regmap_config.max_register = resource_size(res) - 4; 2230179a913SChanwoo Choi 2240179a913SChanwoo Choi nocp->regmap = devm_regmap_init_mmio(dev, base, 2250179a913SChanwoo Choi &exynos_nocp_regmap_config); 2260179a913SChanwoo Choi if (IS_ERR(nocp->regmap)) { 2270179a913SChanwoo Choi dev_err(dev, "failed to initialize regmap\n"); 2280179a913SChanwoo Choi return PTR_ERR(nocp->regmap); 2290179a913SChanwoo Choi } 2300179a913SChanwoo Choi 2310179a913SChanwoo Choi return 0; 2320179a913SChanwoo Choi } 2330179a913SChanwoo Choi 2340179a913SChanwoo Choi static int exynos_nocp_probe(struct platform_device *pdev) 2350179a913SChanwoo Choi { 2360179a913SChanwoo Choi struct device *dev = &pdev->dev; 2370179a913SChanwoo Choi struct device_node *np = dev->of_node; 2380179a913SChanwoo Choi struct exynos_nocp *nocp; 2390179a913SChanwoo Choi int ret; 2400179a913SChanwoo Choi 2410179a913SChanwoo Choi nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); 2420179a913SChanwoo Choi if (!nocp) 2430179a913SChanwoo Choi return -ENOMEM; 2440179a913SChanwoo Choi 2450179a913SChanwoo Choi nocp->dev = &pdev->dev; 2460179a913SChanwoo Choi 2470179a913SChanwoo Choi /* Parse dt data to get resource */ 2480179a913SChanwoo Choi ret = exynos_nocp_parse_dt(pdev, nocp); 2490179a913SChanwoo Choi if (ret < 0) { 2500179a913SChanwoo Choi dev_err(&pdev->dev, 2510179a913SChanwoo Choi "failed to parse devicetree for resource\n"); 2520179a913SChanwoo Choi return ret; 2530179a913SChanwoo Choi } 2540179a913SChanwoo Choi 2550179a913SChanwoo Choi /* Add devfreq-event device to measure the bandwidth of NoC */ 2560179a913SChanwoo Choi nocp->desc.ops = &exynos_nocp_ops; 2570179a913SChanwoo Choi nocp->desc.driver_data = nocp; 2580179a913SChanwoo Choi nocp->desc.name = np->full_name; 2590179a913SChanwoo Choi nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc); 2600179a913SChanwoo Choi if (IS_ERR(nocp->edev)) { 2610179a913SChanwoo Choi dev_err(&pdev->dev, 2620179a913SChanwoo Choi "failed to add devfreq-event device\n"); 2630179a913SChanwoo Choi return PTR_ERR(nocp->edev); 2640179a913SChanwoo Choi } 2650179a913SChanwoo Choi platform_set_drvdata(pdev, nocp); 2660179a913SChanwoo Choi 267973a27c7SArvind Yadav ret = clk_prepare_enable(nocp->clk); 268973a27c7SArvind Yadav if (ret) { 269973a27c7SArvind Yadav dev_err(&pdev->dev, "failed to prepare ppmu clock\n"); 270973a27c7SArvind Yadav return ret; 271973a27c7SArvind Yadav } 2720179a913SChanwoo Choi 2730179a913SChanwoo Choi pr_info("exynos-nocp: new NoC Probe device registered: %s\n", 2740179a913SChanwoo Choi dev_name(dev)); 2750179a913SChanwoo Choi 2760179a913SChanwoo Choi return 0; 2770179a913SChanwoo Choi } 2780179a913SChanwoo Choi 2790179a913SChanwoo Choi static int exynos_nocp_remove(struct platform_device *pdev) 2800179a913SChanwoo Choi { 2810179a913SChanwoo Choi struct exynos_nocp *nocp = platform_get_drvdata(pdev); 2820179a913SChanwoo Choi 2830179a913SChanwoo Choi clk_disable_unprepare(nocp->clk); 2840179a913SChanwoo Choi 2850179a913SChanwoo Choi return 0; 2860179a913SChanwoo Choi } 2870179a913SChanwoo Choi 2880179a913SChanwoo Choi static struct platform_driver exynos_nocp_driver = { 2890179a913SChanwoo Choi .probe = exynos_nocp_probe, 2900179a913SChanwoo Choi .remove = exynos_nocp_remove, 2910179a913SChanwoo Choi .driver = { 2920179a913SChanwoo Choi .name = "exynos-nocp", 2930179a913SChanwoo Choi .of_match_table = exynos_nocp_id_match, 2940179a913SChanwoo Choi }, 2950179a913SChanwoo Choi }; 2960179a913SChanwoo Choi module_platform_driver(exynos_nocp_driver); 2970179a913SChanwoo Choi 2980179a913SChanwoo Choi MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver"); 2990179a913SChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 3000179a913SChanwoo Choi MODULE_LICENSE("GPL"); 301