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 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
);
74 static ssize_t
reg_show(struct device
*dev
, struct device_attribute
*attr
,
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
];
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 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
];
104 /* parse use input */
105 if (kstrtoul(buf
, 0, &val
) != 0)
109 /* read current values */
110 reg_br_cur
= in_be32(&bank
->br
);
111 reg_or_cur
= in_be32(&bank
->or);
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
)) {
120 /* not allowed to change mode */
121 if ((reg_new
& BR_MSEL
) != BR_MS_GPCM
)
124 /* write new value (force valid) */
125 out_be32(&bank
->br
, reg_new
| BR_V
);
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
))
132 /* write new value */
133 out_be32(&bank
->or, reg_new
);
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
149 static irqreturn_t
netx5152_irq_handler(int irq
, struct uio_info
*info
)
151 void __iomem
*reg_int_en
= info
->mem
[0].internal_addr
+
152 DPM_HOST_WIN0_OFFSET
+
154 void __iomem
*reg_int_stat
= info
->mem
[0].internal_addr
+
155 DPM_HOST_WIN0_OFFSET
+
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) {
164 /* disable interrupts */
165 iowrite32(ioread32(reg_int_en
) & ~DPM_HOST_INT_GLOBAL_EN
, reg_int_en
);
170 static void netx5152_init(struct uio_info
*info
)
172 unsigned long win0_offset
= DPM_HOST_WIN0_OFFSET
;
173 struct fsl_elbc_gpcm
*priv
= info
->priv
;
176 /* get an optional initial win0 offset */
177 prop
= of_get_property(priv
->dev
->of_node
,
178 "netx5152,init-win0-offset", NULL
);
180 win0_offset
= of_read_ulong(prop
, 1);
182 /* disable interrupts */
183 iowrite32(0, info
->mem
[0].internal_addr
+ win0_offset
+
187 static void netx5152_shutdown(struct uio_info
*info
, bool init_err
)
192 /* disable interrupts */
193 iowrite32(0, info
->mem
[0].internal_addr
+ DPM_HOST_WIN0_OFFSET
+
198 static void setup_periph(struct fsl_elbc_gpcm
*priv
,
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";
212 static int check_of_data(struct fsl_elbc_gpcm
*priv
,
213 struct resource
*res
,
214 u32 reg_br
, u32 reg_or
)
216 /* check specified bank */
217 if (priv
->bank
>= MAX_BANKS
) {
218 dev_err(priv
->dev
, "invalid bank\n");
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");
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");
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");
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
)
251 /* get the memory resource */
252 ret
= of_address_to_resource(node
, 0, res
);
254 dev_err(priv
->dev
, "failed to get resource\n");
258 /* get the bank number */
259 ret
= of_property_read_u32(node
, "reg", &priv
->bank
);
261 dev_err(priv
->dev
, "failed to get bank number\n");
265 /* get BR value to set */
266 ret
= of_property_read_u32(node
, "elbc-gpcm-br", reg_br
);
268 dev_err(priv
->dev
, "missing elbc-gpcm-br value\n");
272 /* get OR value to set */
273 ret
= of_property_read_u32(node
, "elbc-gpcm-or", reg_or
);
275 dev_err(priv
->dev
, "missing elbc-gpcm-or value\n");
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
);
284 /* get optional irq value */
285 *irq
= irq_of_parse_and_map(node
, 0);
287 /* sanity check device tree data */
288 ret
= check_of_data(priv
, res
, *reg_br
, *reg_or
);
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
);
302 static int uio_fsl_elbc_gpcm_probe(struct platform_device
*pdev
)
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
;
316 if (!fsl_lbc_ctrl_dev
|| !fsl_lbc_ctrl_dev
->regs
)
319 /* allocate private data */
320 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
323 priv
->dev
= &pdev
->dev
;
324 priv
->lbc
= fsl_lbc_ctrl_dev
->regs
;
326 /* get device tree data */
327 ret
= get_of_data(priv
, node
, &res
, ®_br_new
, ®_or_new
,
332 /* allocate UIO structure */
333 info
= kzalloc(sizeof(*info
), GFP_KERNEL
);
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);
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
)) {
349 "bank in use by another peripheral\n");
354 /* warn if behavior settings changing */
355 if ((reg_br_cur
& ~(BR_BA
| BR_V
)) !=
356 (reg_br_new
& ~(BR_BA
| BR_V
))) {
358 "modifying BR settings: 0x%08x -> 0x%08x",
359 reg_br_cur
, reg_br_new
);
361 if ((reg_or_cur
& ~OR_GPCM_AM
) != (reg_or_new
& ~OR_GPCM_AM
)) {
363 "modifying OR settings: 0x%08x -> 0x%08x",
364 reg_or_cur
, reg_or_new
);
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
);
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");
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
;
388 info
->name
= uio_name
;
389 info
->version
= "0.0.1";
391 if (priv
->irq_handler
) {
393 info
->irq_flags
= IRQF_SHARED
;
394 info
->handler
= priv
->irq_handler
;
397 dev_warn(priv
->dev
, "ignoring irq, no handler\n");
404 /* register UIO device */
405 if (uio_register_device(priv
->dev
, info
) != 0) {
406 dev_err(priv
->dev
, "UIO registration failed\n");
411 /* store private data */
412 platform_set_drvdata(pdev
, info
);
414 /* create sysfs files */
415 ret
= device_create_file(priv
->dev
, &dev_attr_reg_br
);
418 ret
= device_create_file(priv
->dev
, &dev_attr_reg_or
);
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);
429 device_remove_file(priv
->dev
, &dev_attr_reg_br
);
431 platform_set_drvdata(pdev
, NULL
);
432 uio_unregister_device(info
);
435 priv
->shutdown(info
, true);
436 iounmap(info
->mem
[0].internal_addr
);
438 kfree(info
->mem
[0].name
);
446 static int uio_fsl_elbc_gpcm_remove(struct platform_device
*pdev
)
448 struct uio_info
*info
= platform_get_drvdata(pdev
);
449 struct fsl_elbc_gpcm
*priv
= info
->priv
;
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
);
456 priv
->shutdown(info
, false);
457 iounmap(info
->mem
[0].internal_addr
);
458 kfree(info
->mem
[0].name
);
467 static const struct of_device_id uio_fsl_elbc_gpcm_match
[] = {
468 { .compatible
= "fsl,elbc-gpcm-uio", },
471 MODULE_DEVICE_TABLE(of
, uio_fsl_elbc_gpcm_match
);
473 static struct platform_driver uio_fsl_elbc_gpcm_driver
= {
475 .name
= "fsl,elbc-gpcm-uio",
476 .of_match_table
= uio_fsl_elbc_gpcm_match
,
478 .probe
= uio_fsl_elbc_gpcm_probe
,
479 .remove
= uio_fsl_elbc_gpcm_remove
,
481 module_platform_driver(uio_fsl_elbc_gpcm_driver
);
483 MODULE_LICENSE("GPL");
484 MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
485 MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");