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