1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * exynos-nocp.c - Exynos NoC (Network On Chip) Probe support 4 * 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 19 struct 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 */ 32 static 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 133 out: 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 143 static 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 175 out: 176 dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n"); 177 178 return ret; 179 } 180 181 static const struct devfreq_event_ops exynos_nocp_ops = { 182 .set_event = exynos_nocp_set_event, 183 .get_event = exynos_nocp_get_event, 184 }; 185 186 static const struct of_device_id exynos_nocp_id_match[] = { 187 { .compatible = "samsung,exynos5420-nocp", }, 188 { /* sentinel */ }, 189 }; 190 MODULE_DEVICE_TABLE(of, exynos_nocp_id_match); 191 192 static 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 199 static 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 233 static 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 278 static int 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 return 0; 285 } 286 287 static struct platform_driver exynos_nocp_driver = { 288 .probe = exynos_nocp_probe, 289 .remove = exynos_nocp_remove, 290 .driver = { 291 .name = "exynos-nocp", 292 .of_match_table = exynos_nocp_id_match, 293 }, 294 }; 295 module_platform_driver(exynos_nocp_driver); 296 297 MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver"); 298 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 299 MODULE_LICENSE("GPL"); 300