Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bus: Add Baikal-T1 APB-bus driver

Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs.
IO requests are routed to this bus by means of the DW AMBA 3 AXI
Interconnect. In case if an attempted APB transaction stays with no
response for a pre-defined time an interrupt occurs and the bus gets
freed for a next operation. This driver provides the interrupt handler
to detect the erroneous address, prints an error message about the
address fault, updates an errors counter. The counter and the APB-bus
operations timeout can be accessed via corresponding sysfs nodes.
A dedicated sysfs-node can be also used to artificially cause the
bus errors described above.

[arnd: fix build warnings for missing includes and wrong return types]

Link: https://lore.kernel.org/r/20200526125928.17096-6-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: soc@kernel.org
Cc: devicetree@vger.kernel.org
Reported-by: kbuild test robot <lkp@intel.com>
Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

authored by

Serge Semin and committed by
Arnd Bergmann
8f93662d 63cb7713

+438
+15
drivers/bus/Kconfig
··· 29 29 arbiter. This driver provides timeout and target abort error handling 30 30 and internal bus master decoding. 31 31 32 + config BT1_APB 33 + tristate "Baikal-T1 APB-bus driver" 34 + depends on MIPS_BAIKAL_T1 || COMPILE_TEST 35 + select REGMAP_MMIO 36 + help 37 + Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs. 38 + IO requests are routed to this bus by means of the DW AMBA 3 AXI 39 + Interconnect. In case of any APB protocol collisions, slave device 40 + not responding on timeout an IRQ is raised with an erroneous address 41 + reported to the APB terminator (APB Errors Handler Block). This 42 + driver provides the interrupt handler to detect the erroneous 43 + address, prints an error message about the address fault, updates an 44 + errors counter. The counter and the APB-bus operations timeout can be 45 + accessed via corresponding sysfs nodes. 46 + 32 47 config BT1_AXI 33 48 tristate "Baikal-T1 AXI-bus driver" 34 49 depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+1
drivers/bus/Makefile
··· 13 13 # DPAA2 fsl-mc bus 14 14 obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ 15 15 16 + obj-$(CONFIG_BT1_APB) += bt1-apb.o 16 17 obj-$(CONFIG_BT1_AXI) += bt1-axi.o 17 18 obj-$(CONFIG_IMX_WEIM) += imx-weim.o 18 19 obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
+422
drivers/bus/bt1-apb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4 + * 5 + * Authors: 6 + * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7 + * 8 + * Baikal-T1 APB-bus driver 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/types.h> 14 + #include <linux/device.h> 15 + #include <linux/atomic.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/interrupt.h> 18 + #include <linux/nmi.h> 19 + #include <linux/of.h> 20 + #include <linux/regmap.h> 21 + #include <linux/clk.h> 22 + #include <linux/reset.h> 23 + #include <linux/time64.h> 24 + #include <linux/clk.h> 25 + #include <linux/sysfs.h> 26 + 27 + #define APB_EHB_ISR 0x00 28 + #define APB_EHB_ISR_PENDING BIT(0) 29 + #define APB_EHB_ISR_MASK BIT(1) 30 + #define APB_EHB_ADDR 0x04 31 + #define APB_EHB_TIMEOUT 0x08 32 + 33 + #define APB_EHB_TIMEOUT_MIN 0x000003FFU 34 + #define APB_EHB_TIMEOUT_MAX 0xFFFFFFFFU 35 + 36 + /* 37 + * struct bt1_apb - Baikal-T1 APB EHB private data 38 + * @dev: Pointer to the device structure. 39 + * @regs: APB EHB registers map. 40 + * @res: No-device error injection memory region. 41 + * @irq: Errors IRQ number. 42 + * @rate: APB-bus reference clock rate. 43 + * @pclk: APB-reference clock. 44 + * @prst: APB domain reset line. 45 + * @count: Number of errors detected. 46 + */ 47 + struct bt1_apb { 48 + struct device *dev; 49 + 50 + struct regmap *regs; 51 + void __iomem *res; 52 + int irq; 53 + 54 + unsigned long rate; 55 + struct clk *pclk; 56 + 57 + struct reset_control *prst; 58 + 59 + atomic_t count; 60 + }; 61 + 62 + static const struct regmap_config bt1_apb_regmap_cfg = { 63 + .reg_bits = 32, 64 + .val_bits = 32, 65 + .reg_stride = 4, 66 + .max_register = APB_EHB_TIMEOUT, 67 + .fast_io = true 68 + }; 69 + 70 + static inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n) 71 + { 72 + u64 timeout = (u64)n * USEC_PER_SEC; 73 + 74 + do_div(timeout, apb->rate); 75 + 76 + return timeout; 77 + 78 + } 79 + 80 + static inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb, 81 + unsigned long timeout) 82 + { 83 + u64 n = (u64)timeout * apb->rate; 84 + 85 + do_div(n, USEC_PER_SEC); 86 + 87 + return n; 88 + 89 + } 90 + 91 + static irqreturn_t bt1_apb_isr(int irq, void *data) 92 + { 93 + struct bt1_apb *apb = data; 94 + u32 addr = 0; 95 + 96 + regmap_read(apb->regs, APB_EHB_ADDR, &addr); 97 + 98 + dev_crit_ratelimited(apb->dev, 99 + "APB-bus fault %d: Slave access timeout at 0x%08x\n", 100 + atomic_inc_return(&apb->count), 101 + addr); 102 + 103 + /* 104 + * Print backtrace on each CPU. This might be pointless if the fault 105 + * has happened on the same CPU as the IRQ handler is executed or 106 + * the other core proceeded further execution despite the error. 107 + * But if it's not, by looking at the trace we would get straight to 108 + * the cause of the problem. 109 + */ 110 + trigger_all_cpu_backtrace(); 111 + 112 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0); 113 + 114 + return IRQ_HANDLED; 115 + } 116 + 117 + static void bt1_apb_clear_data(void *data) 118 + { 119 + struct bt1_apb *apb = data; 120 + struct platform_device *pdev = to_platform_device(apb->dev); 121 + 122 + platform_set_drvdata(pdev, NULL); 123 + } 124 + 125 + static struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev) 126 + { 127 + struct device *dev = &pdev->dev; 128 + struct bt1_apb *apb; 129 + int ret; 130 + 131 + apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL); 132 + if (!apb) 133 + return ERR_PTR(-ENOMEM); 134 + 135 + ret = devm_add_action(dev, bt1_apb_clear_data, apb); 136 + if (ret) { 137 + dev_err(dev, "Can't add APB EHB data clear action\n"); 138 + return ERR_PTR(ret); 139 + } 140 + 141 + apb->dev = dev; 142 + atomic_set(&apb->count, 0); 143 + platform_set_drvdata(pdev, apb); 144 + 145 + return apb; 146 + } 147 + 148 + static int bt1_apb_request_regs(struct bt1_apb *apb) 149 + { 150 + struct platform_device *pdev = to_platform_device(apb->dev); 151 + void __iomem *regs; 152 + 153 + regs = devm_platform_ioremap_resource_byname(pdev, "ehb"); 154 + if (IS_ERR(regs)) { 155 + dev_err(apb->dev, "Couldn't map APB EHB registers\n"); 156 + return PTR_ERR(regs); 157 + } 158 + 159 + apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg); 160 + if (IS_ERR(apb->regs)) { 161 + dev_err(apb->dev, "Couldn't create APB EHB regmap\n"); 162 + return PTR_ERR(apb->regs); 163 + } 164 + 165 + apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev"); 166 + if (IS_ERR(apb->res)) { 167 + dev_err(apb->dev, "Couldn't map reserved region\n"); 168 + return PTR_ERR(apb->res); 169 + } 170 + 171 + return 0; 172 + } 173 + 174 + static int bt1_apb_request_rst(struct bt1_apb *apb) 175 + { 176 + int ret; 177 + 178 + apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst"); 179 + if (IS_ERR(apb->prst)) { 180 + dev_warn(apb->dev, "Couldn't get reset control line\n"); 181 + return PTR_ERR(apb->prst); 182 + } 183 + 184 + ret = reset_control_deassert(apb->prst); 185 + if (ret) 186 + dev_err(apb->dev, "Failed to deassert the reset line\n"); 187 + 188 + return ret; 189 + } 190 + 191 + static void bt1_apb_disable_clk(void *data) 192 + { 193 + struct bt1_apb *apb = data; 194 + 195 + clk_disable_unprepare(apb->pclk); 196 + } 197 + 198 + static int bt1_apb_request_clk(struct bt1_apb *apb) 199 + { 200 + int ret; 201 + 202 + apb->pclk = devm_clk_get(apb->dev, "pclk"); 203 + if (IS_ERR(apb->pclk)) { 204 + dev_err(apb->dev, "Couldn't get APB clock descriptor\n"); 205 + return PTR_ERR(apb->pclk); 206 + } 207 + 208 + ret = clk_prepare_enable(apb->pclk); 209 + if (ret) { 210 + dev_err(apb->dev, "Couldn't enable the APB clock\n"); 211 + return ret; 212 + } 213 + 214 + ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb); 215 + if (ret) { 216 + dev_err(apb->dev, "Can't add APB EHB clocks disable action\n"); 217 + return ret; 218 + } 219 + 220 + apb->rate = clk_get_rate(apb->pclk); 221 + if (!apb->rate) { 222 + dev_err(apb->dev, "Invalid clock rate\n"); 223 + return -EINVAL; 224 + } 225 + 226 + return 0; 227 + } 228 + 229 + static void bt1_apb_clear_irq(void *data) 230 + { 231 + struct bt1_apb *apb = data; 232 + 233 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0); 234 + } 235 + 236 + static int bt1_apb_request_irq(struct bt1_apb *apb) 237 + { 238 + struct platform_device *pdev = to_platform_device(apb->dev); 239 + int ret; 240 + 241 + apb->irq = platform_get_irq(pdev, 0); 242 + if (apb->irq < 0) 243 + return apb->irq; 244 + 245 + ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED, 246 + "bt1-apb", apb); 247 + if (ret) { 248 + dev_err(apb->dev, "Couldn't request APB EHB IRQ\n"); 249 + return ret; 250 + } 251 + 252 + ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb); 253 + if (ret) { 254 + dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n"); 255 + return ret; 256 + } 257 + 258 + /* Unmask IRQ and clear it' pending flag. */ 259 + regmap_update_bits(apb->regs, APB_EHB_ISR, 260 + APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK, 261 + APB_EHB_ISR_MASK); 262 + 263 + return 0; 264 + } 265 + 266 + static ssize_t count_show(struct device *dev, struct device_attribute *attr, 267 + char *buf) 268 + { 269 + struct bt1_apb *apb = dev_get_drvdata(dev); 270 + 271 + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count)); 272 + } 273 + static DEVICE_ATTR_RO(count); 274 + 275 + static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, 276 + char *buf) 277 + { 278 + struct bt1_apb *apb = dev_get_drvdata(dev); 279 + unsigned long timeout; 280 + int ret; 281 + u32 n; 282 + 283 + ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n); 284 + if (ret) 285 + return ret; 286 + 287 + timeout = bt1_apb_n_to_timeout_us(apb, n); 288 + 289 + return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout); 290 + } 291 + 292 + static ssize_t timeout_store(struct device *dev, 293 + struct device_attribute *attr, 294 + const char *buf, size_t count) 295 + { 296 + struct bt1_apb *apb = dev_get_drvdata(dev); 297 + unsigned long timeout; 298 + int ret; 299 + u32 n; 300 + 301 + if (kstrtoul(buf, 0, &timeout) < 0) 302 + return -EINVAL; 303 + 304 + n = bt1_apb_timeout_to_n_us(apb, timeout); 305 + n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX); 306 + 307 + ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n); 308 + 309 + return ret ?: count; 310 + } 311 + static DEVICE_ATTR_RW(timeout); 312 + 313 + static ssize_t inject_error_show(struct device *dev, struct device_attribute *attr, 314 + char *buf) 315 + { 316 + return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n"); 317 + } 318 + 319 + static ssize_t inject_error_store(struct device *dev, 320 + struct device_attribute *attr, 321 + const char *data, size_t count) 322 + { 323 + struct bt1_apb *apb = dev_get_drvdata(dev); 324 + 325 + /* 326 + * Either dummy read from the unmapped address in the APB IO area 327 + * or manually set the IRQ status. 328 + */ 329 + if (!strncmp(data, "nodev", 5)) 330 + readl(apb->res); 331 + else if (!strncmp(data, "irq", 3)) 332 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 333 + APB_EHB_ISR_PENDING); 334 + else 335 + return -EINVAL; 336 + 337 + return count; 338 + } 339 + static DEVICE_ATTR_RW(inject_error); 340 + 341 + static struct attribute *bt1_apb_sysfs_attrs[] = { 342 + &dev_attr_count.attr, 343 + &dev_attr_timeout.attr, 344 + &dev_attr_inject_error.attr, 345 + NULL 346 + }; 347 + ATTRIBUTE_GROUPS(bt1_apb_sysfs); 348 + 349 + static void bt1_apb_remove_sysfs(void *data) 350 + { 351 + struct bt1_apb *apb = data; 352 + 353 + device_remove_groups(apb->dev, bt1_apb_sysfs_groups); 354 + } 355 + 356 + static int bt1_apb_init_sysfs(struct bt1_apb *apb) 357 + { 358 + int ret; 359 + 360 + ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups); 361 + if (ret) { 362 + dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n"); 363 + return ret; 364 + } 365 + 366 + ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb); 367 + if (ret) 368 + dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n"); 369 + 370 + return ret; 371 + } 372 + 373 + static int bt1_apb_probe(struct platform_device *pdev) 374 + { 375 + struct bt1_apb *apb; 376 + int ret; 377 + 378 + apb = bt1_apb_create_data(pdev); 379 + if (IS_ERR(apb)) 380 + return PTR_ERR(apb); 381 + 382 + ret = bt1_apb_request_regs(apb); 383 + if (ret) 384 + return ret; 385 + 386 + ret = bt1_apb_request_rst(apb); 387 + if (ret) 388 + return ret; 389 + 390 + ret = bt1_apb_request_clk(apb); 391 + if (ret) 392 + return ret; 393 + 394 + ret = bt1_apb_request_irq(apb); 395 + if (ret) 396 + return ret; 397 + 398 + ret = bt1_apb_init_sysfs(apb); 399 + if (ret) 400 + return ret; 401 + 402 + return 0; 403 + } 404 + 405 + static const struct of_device_id bt1_apb_of_match[] = { 406 + { .compatible = "baikal,bt1-apb" }, 407 + { } 408 + }; 409 + MODULE_DEVICE_TABLE(of, bt1_apb_of_match); 410 + 411 + static struct platform_driver bt1_apb_driver = { 412 + .probe = bt1_apb_probe, 413 + .driver = { 414 + .name = "bt1-apb", 415 + .of_match_table = bt1_apb_of_match 416 + } 417 + }; 418 + module_platform_driver(bt1_apb_driver); 419 + 420 + MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 421 + MODULE_DESCRIPTION("Baikal-T1 APB-bus driver"); 422 + MODULE_LICENSE("GPL v2");