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