1 /* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals
3 Copyright (C) 2014 Linutronix GmbH
4 Author: John Ogness <john.ogness@linutronix.de>
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).
10 Here is an example of the device tree entries:
13 ranges = <0x2 0x0 0x0 0xff810000 0x10000>;
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>;
22 device_type = "netx5152";
23 uio_name = "netx_custom";
24 netx5152,init-win0-offset = <0x0>;
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.
34 The netx5152 type has been tested to work with the netX 51/52 hardware
35 from Hilscher using the Hilscher userspace netX stack.
37 The netx5152 type should serve as a model to add new type-specific
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>
50 #include <asm/fsl_lbc.h>
54 struct fsl_elbc_gpcm
{
56 struct fsl_lbc_regs __iomem
*lbc
;
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
);
65 static ssize_t
reg_show(struct device
*dev
, struct device_attribute
*attr
,
67 static ssize_t
reg_store(struct device
*dev
, struct device_attribute
*attr
,
68 const char *buf
, size_t count
);
70 DEVICE_ATTR(reg_br
, S_IRUGO
|S_IWUSR
|S_IWGRP
, reg_show
, reg_store
);
71 DEVICE_ATTR(reg_or
, S_IRUGO
|S_IWUSR
|S_IWGRP
, reg_show
, reg_store
);
73 static ssize_t
reg_show(struct device
*dev
, struct device_attribute
*attr
,
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
];
81 if (attr
== &dev_attr_reg_br
) {
82 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
85 } else if (attr
== &dev_attr_reg_or
) {
86 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
93 static ssize_t
reg_store(struct device
*dev
, struct device_attribute
*attr
,
94 const char *buf
, size_t count
)
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
];
105 /* parse use input */
106 if (kstrtoul(buf
, 0, &val
) != 0)
110 /* read current values */
111 reg_br_cur
= in_be32(&bank
->br
);
112 reg_or_cur
= in_be32(&bank
->or);
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
)) {
121 /* not allowed to change mode */
122 if ((reg_new
& BR_MSEL
) != BR_MS_GPCM
)
125 /* write new value (force valid) */
126 out_be32(&bank
->br
, reg_new
| BR_V
);
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
))
133 /* write new value */
134 out_be32(&bank
->or, reg_new
);
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
150 static irqreturn_t
netx5152_irq_handler(int irq
, struct uio_info
*info
)
152 void __iomem
*reg_int_en
= info
->mem
[0].internal_addr
+
153 DPM_HOST_WIN0_OFFSET
+
155 void __iomem
*reg_int_stat
= info
->mem
[0].internal_addr
+
156 DPM_HOST_WIN0_OFFSET
+
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) {
165 /* disable interrupts */
166 iowrite32(ioread32(reg_int_en
) & ~DPM_HOST_INT_GLOBAL_EN
, reg_int_en
);
171 static void netx5152_init(struct uio_info
*info
)
173 unsigned long win0_offset
= DPM_HOST_WIN0_OFFSET
;
174 struct fsl_elbc_gpcm
*priv
= info
->priv
;
177 /* get an optional initial win0 offset */
178 prop
= of_get_property(priv
->dev
->of_node
,
179 "netx5152,init-win0-offset", NULL
);
181 win0_offset
= of_read_ulong(prop
, 1);
183 /* disable interrupts */
184 iowrite32(0, info
->mem
[0].internal_addr
+ win0_offset
+
188 static void netx5152_shutdown(struct uio_info
*info
, bool init_err
)
193 /* disable interrupts */
194 iowrite32(0, info
->mem
[0].internal_addr
+ DPM_HOST_WIN0_OFFSET
+
199 static void setup_periph(struct fsl_elbc_gpcm
*priv
,
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";
213 static int check_of_data(struct fsl_elbc_gpcm
*priv
,
214 struct resource
*res
,
215 u32 reg_br
, u32 reg_or
)
217 /* check specified bank */
218 if (priv
->bank
>= MAX_BANKS
) {
219 dev_err(priv
->dev
, "invalid bank\n");
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");
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");
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");
244 static 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
)
252 /* get the memory resource */
253 ret
= of_address_to_resource(node
, 0, res
);
255 dev_err(priv
->dev
, "failed to get resource\n");
259 /* get the bank number */
260 ret
= of_property_read_u32(node
, "reg", &priv
->bank
);
262 dev_err(priv
->dev
, "failed to get bank number\n");
266 /* get BR value to set */
267 ret
= of_property_read_u32(node
, "elbc-gpcm-br", reg_br
);
269 dev_err(priv
->dev
, "missing elbc-gpcm-br value\n");
273 /* get OR value to set */
274 ret
= of_property_read_u32(node
, "elbc-gpcm-or", reg_or
);
276 dev_err(priv
->dev
, "missing elbc-gpcm-or value\n");
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
);
285 /* get optional irq value */
286 *irq
= irq_of_parse_and_map(node
, 0);
288 /* sanity check device tree data */
289 ret
= check_of_data(priv
, res
, *reg_br
, *reg_or
);
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
);
303 static int uio_fsl_elbc_gpcm_probe(struct platform_device
*pdev
)
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
;
317 if (!fsl_lbc_ctrl_dev
|| !fsl_lbc_ctrl_dev
->regs
)
320 /* allocate private data */
321 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
324 priv
->dev
= &pdev
->dev
;
325 priv
->lbc
= fsl_lbc_ctrl_dev
->regs
;
327 /* get device tree data */
328 ret
= get_of_data(priv
, node
, &res
, ®_br_new
, ®_or_new
,
333 /* allocate UIO structure */
334 info
= kzalloc(sizeof(*info
), GFP_KERNEL
);
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);
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
)) {
350 "bank in use by another peripheral\n");
355 /* warn if behavior settings changing */
356 if ((reg_br_cur
& ~(BR_BA
| BR_V
)) !=
357 (reg_br_new
& ~(BR_BA
| BR_V
))) {
359 "modifying BR settings: 0x%08x -> 0x%08x",
360 reg_br_cur
, reg_br_new
);
362 if ((reg_or_cur
& ~OR_GPCM_AM
) != (reg_or_new
& ~OR_GPCM_AM
)) {
364 "modifying OR settings: 0x%08x -> 0x%08x",
365 reg_or_cur
, reg_or_new
);
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
);
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");
383 /* set all UIO data */
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
;
390 info
->name
= uio_name
;
391 info
->version
= "0.0.1";
393 if (priv
->irq_handler
) {
395 info
->irq_flags
= IRQF_SHARED
;
396 info
->handler
= priv
->irq_handler
;
399 dev_warn(priv
->dev
, "ignoring irq, no handler\n");
406 /* register UIO device */
407 if (uio_register_device(priv
->dev
, info
) != 0) {
408 dev_err(priv
->dev
, "UIO registration failed\n");
413 /* store private data */
414 platform_set_drvdata(pdev
, info
);
416 /* create sysfs files */
417 ret
= device_create_file(priv
->dev
, &dev_attr_reg_br
);
420 ret
= device_create_file(priv
->dev
, &dev_attr_reg_or
);
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);
431 device_remove_file(priv
->dev
, &dev_attr_reg_br
);
433 platform_set_drvdata(pdev
, NULL
);
434 uio_unregister_device(info
);
437 priv
->shutdown(info
, true);
438 iounmap(info
->mem
[0].internal_addr
);
440 kfree(info
->mem
[0].name
);
448 static int uio_fsl_elbc_gpcm_remove(struct platform_device
*pdev
)
450 struct uio_info
*info
= platform_get_drvdata(pdev
);
451 struct fsl_elbc_gpcm
*priv
= info
->priv
;
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
);
458 priv
->shutdown(info
, false);
459 iounmap(info
->mem
[0].internal_addr
);
460 kfree(info
->mem
[0].name
);
469 static const struct of_device_id uio_fsl_elbc_gpcm_match
[] = {
470 { .compatible
= "fsl,elbc-gpcm-uio", },
473 MODULE_DEVICE_TABLE(of
, uio_fsl_elbc_gpcm_match
);
475 static struct platform_driver uio_fsl_elbc_gpcm_driver
= {
477 .name
= "fsl,elbc-gpcm-uio",
478 .owner
= THIS_MODULE
,
479 .of_match_table
= uio_fsl_elbc_gpcm_match
,
481 .probe
= uio_fsl_elbc_gpcm_probe
,
482 .remove
= uio_fsl_elbc_gpcm_remove
,
484 module_platform_driver(uio_fsl_elbc_gpcm_driver
);
486 MODULE_LICENSE("GPL");
487 MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
488 MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");