1 // SPDX-License-Identifier: GPL-2.0 2 /* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals 3 4 Copyright (C) 2014 Linutronix GmbH 5 Author: John Ogness <john.ogness@linutronix.de> 6 7 This driver provides UIO access to memory of a peripheral connected 8 to the Freescale enhanced local bus controller (eLBC) interface 9 using the general purpose chip-select mode (GPCM). 10 11 Here is an example of the device tree entries: 12 13 localbus@ffe05000 { 14 ranges = <0x2 0x0 0x0 0xff810000 0x10000>; 15 16 dpm@2,0 { 17 compatible = "fsl,elbc-gpcm-uio"; 18 reg = <0x2 0x0 0x10000>; 19 elbc-gpcm-br = <0xff810800>; 20 elbc-gpcm-or = <0xffff09f7>; 21 interrupt-parent = <&mpic>; 22 interrupts = <4 1>; 23 device_type = "netx5152"; 24 uio_name = "netx_custom"; 25 netx5152,init-win0-offset = <0x0>; 26 }; 27 }; 28 29 Only the entries reg (to identify bank) and elbc-gpcm-* (initial BR/OR 30 values) are required. The entries interrupt*, device_type, and uio_name 31 are optional (as well as any type-specific options such as 32 netx5152,init-win0-offset). As long as no interrupt handler is needed, 33 this driver can be used without any type-specific implementation. 34 35 The netx5152 type has been tested to work with the netX 51/52 hardware 36 from Hilscher using the Hilscher userspace netX stack. 37 38 The netx5152 type should serve as a model to add new type-specific 39 devices as needed. 40 */ 41 42 #include <linux/module.h> 43 #include <linux/device.h> 44 #include <linux/string.h> 45 #include <linux/slab.h> 46 #include <linux/platform_device.h> 47 #include <linux/uio_driver.h> 48 #include <linux/of_address.h> 49 #include <linux/of_irq.h> 50 51 #include <asm/fsl_lbc.h> 52 53 #define MAX_BANKS 8 54 55 struct fsl_elbc_gpcm { 56 struct device *dev; 57 struct fsl_lbc_regs __iomem *lbc; 58 u32 bank; 59 const char *name; 60 61 void (*init)(struct uio_info *info); 62 void (*shutdown)(struct uio_info *info, bool init_err); 63 irqreturn_t (*irq_handler)(int irq, struct uio_info *info); 64 }; 65 66 static ssize_t reg_show(struct device *dev, struct device_attribute *attr, 67 char *buf); 68 static ssize_t reg_store(struct device *dev, struct device_attribute *attr, 69 const char *buf, size_t count); 70 71 DEVICE_ATTR(reg_br, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store); 72 DEVICE_ATTR(reg_or, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store); 73 74 static ssize_t reg_show(struct device *dev, struct device_attribute *attr, 75 char *buf) 76 { 77 struct platform_device *pdev = to_platform_device(dev); 78 struct uio_info *info = platform_get_drvdata(pdev); 79 struct fsl_elbc_gpcm *priv = info->priv; 80 struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank]; 81 82 if (attr == &dev_attr_reg_br) { 83 return scnprintf(buf, PAGE_SIZE, "0x%08x\n", 84 in_be32(&bank->br)); 85 86 } else if (attr == &dev_attr_reg_or) { 87 return scnprintf(buf, PAGE_SIZE, "0x%08x\n", 88 in_be32(&bank->or)); 89 } 90 91 return 0; 92 } 93 94 static ssize_t reg_store(struct device *dev, struct device_attribute *attr, 95 const char *buf, size_t count) 96 { 97 struct platform_device *pdev = to_platform_device(dev); 98 struct uio_info *info = platform_get_drvdata(pdev); 99 struct fsl_elbc_gpcm *priv = info->priv; 100 struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank]; 101 unsigned long val; 102 u32 reg_br_cur; 103 u32 reg_or_cur; 104 u32 reg_new; 105 106 /* parse use input */ 107 if (kstrtoul(buf, 0, &val) != 0) 108 return -EINVAL; 109 reg_new = (u32)val; 110 111 /* read current values */ 112 reg_br_cur = in_be32(&bank->br); 113 reg_or_cur = in_be32(&bank->or); 114 115 if (attr == &dev_attr_reg_br) { 116 /* not allowed to change effective base address */ 117 if ((reg_br_cur & reg_or_cur & BR_BA) != 118 (reg_new & reg_or_cur & BR_BA)) { 119 return -EINVAL; 120 } 121 122 /* not allowed to change mode */ 123 if ((reg_new & BR_MSEL) != BR_MS_GPCM) 124 return -EINVAL; 125 126 /* write new value (force valid) */ 127 out_be32(&bank->br, reg_new | BR_V); 128 129 } else if (attr == &dev_attr_reg_or) { 130 /* not allowed to change access mask */ 131 if ((reg_or_cur & OR_GPCM_AM) != (reg_new & OR_GPCM_AM)) 132 return -EINVAL; 133 134 /* write new value */ 135 out_be32(&bank->or, reg_new); 136 137 } else { 138 return -EINVAL; 139 } 140 141 return count; 142 } 143 144 #ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152 145 #define DPM_HOST_WIN0_OFFSET 0xff00 146 #define DPM_HOST_INT_STAT0 0xe0 147 #define DPM_HOST_INT_EN0 0xf0 148 #define DPM_HOST_INT_MASK 0xe600ffff 149 #define DPM_HOST_INT_GLOBAL_EN 0x80000000 150 151 static irqreturn_t netx5152_irq_handler(int irq, struct uio_info *info) 152 { 153 void __iomem *reg_int_en = info->mem[0].internal_addr + 154 DPM_HOST_WIN0_OFFSET + 155 DPM_HOST_INT_EN0; 156 void __iomem *reg_int_stat = info->mem[0].internal_addr + 157 DPM_HOST_WIN0_OFFSET + 158 DPM_HOST_INT_STAT0; 159 160 /* check if an interrupt is enabled and active */ 161 if ((ioread32(reg_int_en) & ioread32(reg_int_stat) & 162 DPM_HOST_INT_MASK) == 0) { 163 return IRQ_NONE; 164 } 165 166 /* disable interrupts */ 167 iowrite32(ioread32(reg_int_en) & ~DPM_HOST_INT_GLOBAL_EN, reg_int_en); 168 169 return IRQ_HANDLED; 170 } 171 172 static void netx5152_init(struct uio_info *info) 173 { 174 unsigned long win0_offset = DPM_HOST_WIN0_OFFSET; 175 struct fsl_elbc_gpcm *priv = info->priv; 176 const void *prop; 177 178 /* get an optional initial win0 offset */ 179 prop = of_get_property(priv->dev->of_node, 180 "netx5152,init-win0-offset", NULL); 181 if (prop) 182 win0_offset = of_read_ulong(prop, 1); 183 184 /* disable interrupts */ 185 iowrite32(0, info->mem[0].internal_addr + win0_offset + 186 DPM_HOST_INT_EN0); 187 } 188 189 static void netx5152_shutdown(struct uio_info *info, bool init_err) 190 { 191 if (init_err) 192 return; 193 194 /* disable interrupts */ 195 iowrite32(0, info->mem[0].internal_addr + DPM_HOST_WIN0_OFFSET + 196 DPM_HOST_INT_EN0); 197 } 198 #endif 199 200 static void setup_periph(struct fsl_elbc_gpcm *priv, 201 const char *type) 202 { 203 #ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152 204 if (strcmp(type, "netx5152") == 0) { 205 priv->irq_handler = netx5152_irq_handler; 206 priv->init = netx5152_init; 207 priv->shutdown = netx5152_shutdown; 208 priv->name = "netX 51/52"; 209 return; 210 } 211 #endif 212 } 213 214 static int check_of_data(struct fsl_elbc_gpcm *priv, 215 struct resource *res, 216 u32 reg_br, u32 reg_or) 217 { 218 /* check specified bank */ 219 if (priv->bank >= MAX_BANKS) { 220 dev_err(priv->dev, "invalid bank\n"); 221 return -ENODEV; 222 } 223 224 /* check specified mode (BR_MS_GPCM is 0) */ 225 if ((reg_br & BR_MSEL) != BR_MS_GPCM) { 226 dev_err(priv->dev, "unsupported mode\n"); 227 return -ENODEV; 228 } 229 230 /* check specified mask vs. resource size */ 231 if ((~(reg_or & OR_GPCM_AM) + 1) != resource_size(res)) { 232 dev_err(priv->dev, "address mask / size mismatch\n"); 233 return -ENODEV; 234 } 235 236 /* check specified address */ 237 if ((reg_br & reg_or & BR_BA) != fsl_lbc_addr(res->start)) { 238 dev_err(priv->dev, "base address mismatch\n"); 239 return -ENODEV; 240 } 241 242 return 0; 243 } 244 245 static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node, 246 struct resource *res, u32 *reg_br, 247 u32 *reg_or, unsigned int *irq, char **name) 248 { 249 const char *dt_name; 250 const char *type; 251 int ret; 252 253 /* get the memory resource */ 254 ret = of_address_to_resource(node, 0, res); 255 if (ret) { 256 dev_err(priv->dev, "failed to get resource\n"); 257 return ret; 258 } 259 260 /* get the bank number */ 261 ret = of_property_read_u32(node, "reg", &priv->bank); 262 if (ret) { 263 dev_err(priv->dev, "failed to get bank number\n"); 264 return ret; 265 } 266 267 /* get BR value to set */ 268 ret = of_property_read_u32(node, "elbc-gpcm-br", reg_br); 269 if (ret) { 270 dev_err(priv->dev, "missing elbc-gpcm-br value\n"); 271 return ret; 272 } 273 274 /* get OR value to set */ 275 ret = of_property_read_u32(node, "elbc-gpcm-or", reg_or); 276 if (ret) { 277 dev_err(priv->dev, "missing elbc-gpcm-or value\n"); 278 return ret; 279 } 280 281 /* get optional peripheral type */ 282 priv->name = "generic"; 283 if (of_property_read_string(node, "device_type", &type) == 0) 284 setup_periph(priv, type); 285 286 /* get optional irq value */ 287 *irq = irq_of_parse_and_map(node, 0); 288 289 /* sanity check device tree data */ 290 ret = check_of_data(priv, res, *reg_br, *reg_or); 291 if (ret) 292 return ret; 293 294 /* get optional uio name */ 295 if (of_property_read_string(node, "uio_name", &dt_name) != 0) 296 dt_name = "eLBC_GPCM"; 297 *name = kstrdup(dt_name, GFP_KERNEL); 298 if (!*name) 299 return -ENOMEM; 300 301 return 0; 302 } 303 304 static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) 305 { 306 struct device_node *node = pdev->dev.of_node; 307 struct fsl_elbc_gpcm *priv; 308 struct uio_info *info; 309 char *uio_name = NULL; 310 struct resource res; 311 unsigned int irq; 312 u32 reg_br_cur; 313 u32 reg_or_cur; 314 u32 reg_br_new; 315 u32 reg_or_new; 316 int ret; 317 318 if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) 319 return -ENODEV; 320 321 /* allocate private data */ 322 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 323 if (!priv) 324 return -ENOMEM; 325 priv->dev = &pdev->dev; 326 priv->lbc = fsl_lbc_ctrl_dev->regs; 327 328 /* get device tree data */ 329 ret = get_of_data(priv, node, &res, ®_br_new, ®_or_new, 330 &irq, &uio_name); 331 if (ret) 332 goto out_err0; 333 334 /* allocate UIO structure */ 335 info = kzalloc(sizeof(*info), GFP_KERNEL); 336 if (!info) { 337 ret = -ENOMEM; 338 goto out_err0; 339 } 340 341 /* get current BR/OR values */ 342 reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br); 343 reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or); 344 345 /* if bank already configured, make sure it matches */ 346 if ((reg_br_cur & BR_V)) { 347 if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM || 348 (reg_br_cur & reg_or_cur & BR_BA) 349 != fsl_lbc_addr(res.start)) { 350 dev_err(priv->dev, 351 "bank in use by another peripheral\n"); 352 ret = -ENODEV; 353 goto out_err1; 354 } 355 356 /* warn if behavior settings changing */ 357 if ((reg_br_cur & ~(BR_BA | BR_V)) != 358 (reg_br_new & ~(BR_BA | BR_V))) { 359 dev_warn(priv->dev, 360 "modifying BR settings: 0x%08x -> 0x%08x", 361 reg_br_cur, reg_br_new); 362 } 363 if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) { 364 dev_warn(priv->dev, 365 "modifying OR settings: 0x%08x -> 0x%08x", 366 reg_or_cur, reg_or_new); 367 } 368 } 369 370 /* configure the bank (force base address and GPCM) */ 371 reg_br_new &= ~(BR_BA | BR_MSEL); 372 reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V; 373 out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new); 374 out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new); 375 376 /* map the memory resource */ 377 info->mem[0].internal_addr = ioremap(res.start, resource_size(&res)); 378 if (!info->mem[0].internal_addr) { 379 dev_err(priv->dev, "failed to map chip region\n"); 380 ret = -ENODEV; 381 goto out_err1; 382 } 383 384 /* set all UIO data */ 385 if (node->name) 386 info->mem[0].name = kstrdup(node->name, GFP_KERNEL); 387 info->mem[0].addr = res.start; 388 info->mem[0].size = resource_size(&res); 389 info->mem[0].memtype = UIO_MEM_PHYS; 390 info->priv = priv; 391 info->name = uio_name; 392 info->version = "0.0.1"; 393 if (irq != NO_IRQ) { 394 if (priv->irq_handler) { 395 info->irq = irq; 396 info->irq_flags = IRQF_SHARED; 397 info->handler = priv->irq_handler; 398 } else { 399 irq = NO_IRQ; 400 dev_warn(priv->dev, "ignoring irq, no handler\n"); 401 } 402 } 403 404 if (priv->init) 405 priv->init(info); 406 407 /* register UIO device */ 408 if (uio_register_device(priv->dev, info) != 0) { 409 dev_err(priv->dev, "UIO registration failed\n"); 410 ret = -ENODEV; 411 goto out_err2; 412 } 413 414 /* store private data */ 415 platform_set_drvdata(pdev, info); 416 417 /* create sysfs files */ 418 ret = device_create_file(priv->dev, &dev_attr_reg_br); 419 if (ret) 420 goto out_err3; 421 ret = device_create_file(priv->dev, &dev_attr_reg_or); 422 if (ret) 423 goto out_err4; 424 425 dev_info(priv->dev, 426 "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n", 427 priv->name, (unsigned long long)res.start, priv->bank, 428 irq != NO_IRQ ? irq : -1); 429 430 return 0; 431 out_err4: 432 device_remove_file(priv->dev, &dev_attr_reg_br); 433 out_err3: 434 platform_set_drvdata(pdev, NULL); 435 uio_unregister_device(info); 436 out_err2: 437 if (priv->shutdown) 438 priv->shutdown(info, true); 439 iounmap(info->mem[0].internal_addr); 440 out_err1: 441 kfree(info->mem[0].name); 442 kfree(info); 443 out_err0: 444 kfree(uio_name); 445 kfree(priv); 446 return ret; 447 } 448 449 static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev) 450 { 451 struct uio_info *info = platform_get_drvdata(pdev); 452 struct fsl_elbc_gpcm *priv = info->priv; 453 454 device_remove_file(priv->dev, &dev_attr_reg_or); 455 device_remove_file(priv->dev, &dev_attr_reg_br); 456 platform_set_drvdata(pdev, NULL); 457 uio_unregister_device(info); 458 if (priv->shutdown) 459 priv->shutdown(info, false); 460 iounmap(info->mem[0].internal_addr); 461 kfree(info->mem[0].name); 462 kfree(info->name); 463 kfree(info); 464 kfree(priv); 465 466 return 0; 467 468 } 469 470 static const struct of_device_id uio_fsl_elbc_gpcm_match[] = { 471 { .compatible = "fsl,elbc-gpcm-uio", }, 472 {} 473 }; 474 MODULE_DEVICE_TABLE(of, uio_fsl_elbc_gpcm_match); 475 476 static struct platform_driver uio_fsl_elbc_gpcm_driver = { 477 .driver = { 478 .name = "fsl,elbc-gpcm-uio", 479 .of_match_table = uio_fsl_elbc_gpcm_match, 480 }, 481 .probe = uio_fsl_elbc_gpcm_probe, 482 .remove = uio_fsl_elbc_gpcm_remove, 483 }; 484 module_platform_driver(uio_fsl_elbc_gpcm_driver); 485 486 MODULE_LICENSE("GPL"); 487 MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); 488 MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver"); 489