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