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