1 // SPDX-License-Identifier: GPL-2.0
2 /* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals
4 Copyright (C) 2014 Linutronix GmbH
5 Author: John Ogness <john.ogness@linutronix.de>
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).
11 Here is an example of the device tree entries:
14 ranges = <0x2 0x0 0x0 0xff810000 0x10000>;
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>;
23 device_type = "netx5152";
24 uio_name = "netx_custom";
25 netx5152,init-win0-offset = <0x0>;
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.
35 The netx5152 type has been tested to work with the netX 51/52 hardware
36 from Hilscher using the Hilscher userspace netX stack.
38 The netx5152 type should serve as a model to add new type-specific
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>
51 #include <asm/fsl_lbc.h>
55 struct fsl_elbc_gpcm
{
57 struct fsl_lbc_regs __iomem
*lbc
;
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
);
66 static ssize_t
reg_show(struct device
*dev
, struct device_attribute
*attr
,
68 static ssize_t
reg_store(struct device
*dev
, struct device_attribute
*attr
,
69 const char *buf
, size_t count
);
71 static DEVICE_ATTR(reg_br
, 0664, reg_show
, reg_store
);
72 static DEVICE_ATTR(reg_or
, 0664, reg_show
, reg_store
);
74 static struct attribute
*uio_fsl_elbc_gpcm_attrs
[] = {
75 &dev_attr_reg_br
.attr
,
76 &dev_attr_reg_or
.attr
,
79 ATTRIBUTE_GROUPS(uio_fsl_elbc_gpcm
);
81 static ssize_t
reg_show(struct device
*dev
, struct device_attribute
*attr
,
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
];
88 if (attr
== &dev_attr_reg_br
) {
89 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
92 } else if (attr
== &dev_attr_reg_or
) {
93 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
100 static ssize_t
reg_store(struct device
*dev
, struct device_attribute
*attr
,
101 const char *buf
, size_t count
)
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
];
111 /* parse use input */
112 if (kstrtoul(buf
, 0, &val
) != 0)
116 /* read current values */
117 reg_br_cur
= in_be32(&bank
->br
);
118 reg_or_cur
= in_be32(&bank
->or);
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
)) {
127 /* not allowed to change mode */
128 if ((reg_new
& BR_MSEL
) != BR_MS_GPCM
)
131 /* write new value (force valid) */
132 out_be32(&bank
->br
, reg_new
| BR_V
);
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
))
139 /* write new value */
140 out_be32(&bank
->or, reg_new
);
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
156 static irqreturn_t
netx5152_irq_handler(int irq
, struct uio_info
*info
)
158 void __iomem
*reg_int_en
= info
->mem
[0].internal_addr
+
159 DPM_HOST_WIN0_OFFSET
+
161 void __iomem
*reg_int_stat
= info
->mem
[0].internal_addr
+
162 DPM_HOST_WIN0_OFFSET
+
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) {
171 /* disable interrupts */
172 iowrite32(ioread32(reg_int_en
) & ~DPM_HOST_INT_GLOBAL_EN
, reg_int_en
);
177 static void netx5152_init(struct uio_info
*info
)
179 unsigned long win0_offset
= DPM_HOST_WIN0_OFFSET
;
180 struct fsl_elbc_gpcm
*priv
= info
->priv
;
183 /* get an optional initial win0 offset */
184 prop
= of_get_property(priv
->dev
->of_node
,
185 "netx5152,init-win0-offset", NULL
);
187 win0_offset
= of_read_ulong(prop
, 1);
189 /* disable interrupts */
190 iowrite32(0, info
->mem
[0].internal_addr
+ win0_offset
+
194 static void netx5152_shutdown(struct uio_info
*info
, bool init_err
)
199 /* disable interrupts */
200 iowrite32(0, info
->mem
[0].internal_addr
+ DPM_HOST_WIN0_OFFSET
+
205 static void setup_periph(struct fsl_elbc_gpcm
*priv
,
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";
219 static int check_of_data(struct fsl_elbc_gpcm
*priv
,
220 struct resource
*res
,
221 u32 reg_br
, u32 reg_or
)
223 /* check specified bank */
224 if (priv
->bank
>= MAX_BANKS
) {
225 dev_err(priv
->dev
, "invalid bank\n");
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");
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");
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");
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
)
258 /* get the memory resource */
259 ret
= of_address_to_resource(node
, 0, res
);
261 dev_err(priv
->dev
, "failed to get resource\n");
265 /* get the bank number */
266 ret
= of_property_read_u32(node
, "reg", &priv
->bank
);
268 dev_err(priv
->dev
, "failed to get bank number\n");
272 /* get BR value to set */
273 ret
= of_property_read_u32(node
, "elbc-gpcm-br", reg_br
);
275 dev_err(priv
->dev
, "missing elbc-gpcm-br value\n");
279 /* get OR value to set */
280 ret
= of_property_read_u32(node
, "elbc-gpcm-or", reg_or
);
282 dev_err(priv
->dev
, "missing elbc-gpcm-or value\n");
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
);
291 /* get optional irq value */
292 *irq
= irq_of_parse_and_map(node
, 0);
294 /* sanity check device tree data */
295 ret
= check_of_data(priv
, res
, *reg_br
, *reg_or
);
299 /* get optional uio name */
300 if (of_property_read_string(node
, "uio_name", &dt_name
) != 0)
301 dt_name
= "eLBC_GPCM";
302 *name
= kstrdup(dt_name
, GFP_KERNEL
);
309 static int uio_fsl_elbc_gpcm_probe(struct platform_device
*pdev
)
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
;
323 if (!fsl_lbc_ctrl_dev
|| !fsl_lbc_ctrl_dev
->regs
)
326 /* allocate private data */
327 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
330 priv
->dev
= &pdev
->dev
;
331 priv
->lbc
= fsl_lbc_ctrl_dev
->regs
;
333 /* get device tree data */
334 ret
= get_of_data(priv
, node
, &res
, ®_br_new
, ®_or_new
,
339 /* allocate UIO structure */
340 info
= kzalloc(sizeof(*info
), GFP_KERNEL
);
346 /* get current BR/OR values */
347 reg_br_cur
= in_be32(&priv
->lbc
->bank
[priv
->bank
].br
);
348 reg_or_cur
= in_be32(&priv
->lbc
->bank
[priv
->bank
].or);
350 /* if bank already configured, make sure it matches */
351 if ((reg_br_cur
& BR_V
)) {
352 if ((reg_br_cur
& BR_MSEL
) != BR_MS_GPCM
||
353 (reg_br_cur
& reg_or_cur
& BR_BA
)
354 != fsl_lbc_addr(res
.start
)) {
356 "bank in use by another peripheral\n");
361 /* warn if behavior settings changing */
362 if ((reg_br_cur
& ~(BR_BA
| BR_V
)) !=
363 (reg_br_new
& ~(BR_BA
| BR_V
))) {
365 "modifying BR settings: 0x%08x -> 0x%08x",
366 reg_br_cur
, reg_br_new
);
368 if ((reg_or_cur
& ~OR_GPCM_AM
) != (reg_or_new
& ~OR_GPCM_AM
)) {
370 "modifying OR settings: 0x%08x -> 0x%08x",
371 reg_or_cur
, reg_or_new
);
375 /* configure the bank (force base address and GPCM) */
376 reg_br_new
&= ~(BR_BA
| BR_MSEL
);
377 reg_br_new
|= fsl_lbc_addr(res
.start
) | BR_MS_GPCM
| BR_V
;
378 out_be32(&priv
->lbc
->bank
[priv
->bank
].or, reg_or_new
);
379 out_be32(&priv
->lbc
->bank
[priv
->bank
].br
, reg_br_new
);
381 /* map the memory resource */
382 info
->mem
[0].internal_addr
= ioremap(res
.start
, resource_size(&res
));
383 if (!info
->mem
[0].internal_addr
) {
384 dev_err(priv
->dev
, "failed to map chip region\n");
389 /* set all UIO data */
390 info
->mem
[0].name
= kasprintf(GFP_KERNEL
, "%pOFn", node
);
391 info
->mem
[0].addr
= res
.start
;
392 info
->mem
[0].size
= resource_size(&res
);
393 info
->mem
[0].memtype
= UIO_MEM_PHYS
;
395 info
->name
= uio_name
;
396 info
->version
= "0.0.1";
398 if (priv
->irq_handler
) {
400 info
->irq_flags
= IRQF_SHARED
;
401 info
->handler
= priv
->irq_handler
;
404 dev_warn(priv
->dev
, "ignoring irq, no handler\n");
411 /* register UIO device */
412 if (uio_register_device(priv
->dev
, info
) != 0) {
413 dev_err(priv
->dev
, "UIO registration failed\n");
418 /* store private data */
419 platform_set_drvdata(pdev
, info
);
422 "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
423 priv
->name
, (unsigned long long)res
.start
, priv
->bank
,
424 irq
!= NO_IRQ
? irq
: -1);
429 priv
->shutdown(info
, true);
430 iounmap(info
->mem
[0].internal_addr
);
432 kfree(info
->mem
[0].name
);
440 static int uio_fsl_elbc_gpcm_remove(struct platform_device
*pdev
)
442 struct uio_info
*info
= platform_get_drvdata(pdev
);
443 struct fsl_elbc_gpcm
*priv
= info
->priv
;
445 platform_set_drvdata(pdev
, NULL
);
446 uio_unregister_device(info
);
448 priv
->shutdown(info
, false);
449 iounmap(info
->mem
[0].internal_addr
);
450 kfree(info
->mem
[0].name
);
459 static const struct of_device_id uio_fsl_elbc_gpcm_match
[] = {
460 { .compatible
= "fsl,elbc-gpcm-uio", },
463 MODULE_DEVICE_TABLE(of
, uio_fsl_elbc_gpcm_match
);
465 static struct platform_driver uio_fsl_elbc_gpcm_driver
= {
467 .name
= "fsl,elbc-gpcm-uio",
468 .of_match_table
= uio_fsl_elbc_gpcm_match
,
469 .dev_groups
= uio_fsl_elbc_gpcm_groups
,
471 .probe
= uio_fsl_elbc_gpcm_probe
,
472 .remove
= uio_fsl_elbc_gpcm_remove
,
474 module_platform_driver(uio_fsl_elbc_gpcm_driver
);
476 MODULE_LICENSE("GPL");
477 MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
478 MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");