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 22 struct 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 */ 35 static 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 136 out: 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 146 static 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 178 out: 179 edata->load_count = 0; 180 edata->total_count = 0; 181 182 dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n"); 183 184 return ret; 185 } 186 187 static const struct devfreq_event_ops exynos_nocp_ops = { 188 .set_event = exynos_nocp_set_event, 189 .get_event = exynos_nocp_get_event, 190 }; 191 192 static const struct of_device_id exynos_nocp_id_match[] = { 193 { .compatible = "samsung,exynos5420-nocp", }, 194 { /* sentinel */ }, 195 }; 196 197 static struct regmap_config exynos_nocp_regmap_config = { 198 .reg_bits = 32, 199 .val_bits = 32, 200 .reg_stride = 4, 201 .max_register = NOCP_COUNTERS_3_VAL, 202 }; 203 204 static int exynos_nocp_parse_dt(struct platform_device *pdev, 205 struct exynos_nocp *nocp) 206 { 207 struct device *dev = nocp->dev; 208 struct device_node *np = dev->of_node; 209 struct resource *res; 210 void __iomem *base; 211 212 if (!np) { 213 dev_err(dev, "failed to find devicetree node\n"); 214 return -EINVAL; 215 } 216 217 nocp->clk = devm_clk_get(dev, "nocp"); 218 if (IS_ERR(nocp->clk)) 219 nocp->clk = NULL; 220 221 /* Maps the memory mapped IO to control nocp register */ 222 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 223 base = devm_ioremap_resource(dev, res); 224 if (IS_ERR(base)) 225 return PTR_ERR(base); 226 227 exynos_nocp_regmap_config.max_register = resource_size(res) - 4; 228 229 nocp->regmap = devm_regmap_init_mmio(dev, base, 230 &exynos_nocp_regmap_config); 231 if (IS_ERR(nocp->regmap)) { 232 dev_err(dev, "failed to initialize regmap\n"); 233 return PTR_ERR(nocp->regmap); 234 } 235 236 return 0; 237 } 238 239 static int exynos_nocp_probe(struct platform_device *pdev) 240 { 241 struct device *dev = &pdev->dev; 242 struct device_node *np = dev->of_node; 243 struct exynos_nocp *nocp; 244 int ret; 245 246 nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); 247 if (!nocp) 248 return -ENOMEM; 249 250 nocp->dev = &pdev->dev; 251 252 /* Parse dt data to get resource */ 253 ret = exynos_nocp_parse_dt(pdev, nocp); 254 if (ret < 0) { 255 dev_err(&pdev->dev, 256 "failed to parse devicetree for resource\n"); 257 return ret; 258 } 259 260 /* Add devfreq-event device to measure the bandwidth of NoC */ 261 nocp->desc.ops = &exynos_nocp_ops; 262 nocp->desc.driver_data = nocp; 263 nocp->desc.name = np->full_name; 264 nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc); 265 if (IS_ERR(nocp->edev)) { 266 dev_err(&pdev->dev, 267 "failed to add devfreq-event device\n"); 268 return PTR_ERR(nocp->edev); 269 } 270 platform_set_drvdata(pdev, nocp); 271 272 clk_prepare_enable(nocp->clk); 273 274 pr_info("exynos-nocp: new NoC Probe device registered: %s\n", 275 dev_name(dev)); 276 277 return 0; 278 } 279 280 static int exynos_nocp_remove(struct platform_device *pdev) 281 { 282 struct exynos_nocp *nocp = platform_get_drvdata(pdev); 283 284 clk_disable_unprepare(nocp->clk); 285 286 return 0; 287 } 288 289 static struct platform_driver exynos_nocp_driver = { 290 .probe = exynos_nocp_probe, 291 .remove = exynos_nocp_remove, 292 .driver = { 293 .name = "exynos-nocp", 294 .of_match_table = exynos_nocp_id_match, 295 }, 296 }; 297 module_platform_driver(exynos_nocp_driver); 298 299 MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver"); 300 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 301 MODULE_LICENSE("GPL"); 302