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