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 platform_device
*pdev
= to_platform_device(dev
);
78 struct uio_info
*info
= platform_get_drvdata(pdev
);
79 struct fsl_elbc_gpcm
*priv
= info
->priv
;
80 struct fsl_lbc_bank
*bank
= &priv
->lbc
->bank
[priv
->bank
];
82 if (attr
== &dev_attr_reg_br
) {
83 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
86 } else if (attr
== &dev_attr_reg_or
) {
87 return scnprintf(buf
, PAGE_SIZE
, "0x%08x\n",
94 static ssize_t
reg_store(struct device
*dev
, struct device_attribute
*attr
,
95 const char *buf
, size_t count
)
97 struct platform_device
*pdev
= to_platform_device(dev
);
98 struct uio_info
*info
= platform_get_drvdata(pdev
);
99 struct fsl_elbc_gpcm
*priv
= info
->priv
;
100 struct fsl_lbc_bank
*bank
= &priv
->lbc
->bank
[priv
->bank
];
106 /* parse use input */
107 if (kstrtoul(buf
, 0, &val
) != 0)
111 /* read current values */
112 reg_br_cur
= in_be32(&bank
->br
);
113 reg_or_cur
= in_be32(&bank
->or);
115 if (attr
== &dev_attr_reg_br
) {
116 /* not allowed to change effective base address */
117 if ((reg_br_cur
& reg_or_cur
& BR_BA
) !=
118 (reg_new
& reg_or_cur
& BR_BA
)) {
122 /* not allowed to change mode */
123 if ((reg_new
& BR_MSEL
) != BR_MS_GPCM
)
126 /* write new value (force valid) */
127 out_be32(&bank
->br
, reg_new
| BR_V
);
129 } else if (attr
== &dev_attr_reg_or
) {
130 /* not allowed to change access mask */
131 if ((reg_or_cur
& OR_GPCM_AM
) != (reg_new
& OR_GPCM_AM
))
134 /* write new value */
135 out_be32(&bank
->or, reg_new
);
144 #ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
145 #define DPM_HOST_WIN0_OFFSET 0xff00
146 #define DPM_HOST_INT_STAT0 0xe0
147 #define DPM_HOST_INT_EN0 0xf0
148 #define DPM_HOST_INT_MASK 0xe600ffff
149 #define DPM_HOST_INT_GLOBAL_EN 0x80000000
151 static irqreturn_t
netx5152_irq_handler(int irq
, struct uio_info
*info
)
153 void __iomem
*reg_int_en
= info
->mem
[0].internal_addr
+
154 DPM_HOST_WIN0_OFFSET
+
156 void __iomem
*reg_int_stat
= info
->mem
[0].internal_addr
+
157 DPM_HOST_WIN0_OFFSET
+
160 /* check if an interrupt is enabled and active */
161 if ((ioread32(reg_int_en
) & ioread32(reg_int_stat
) &
162 DPM_HOST_INT_MASK
) == 0) {
166 /* disable interrupts */
167 iowrite32(ioread32(reg_int_en
) & ~DPM_HOST_INT_GLOBAL_EN
, reg_int_en
);
172 static void netx5152_init(struct uio_info
*info
)
174 unsigned long win0_offset
= DPM_HOST_WIN0_OFFSET
;
175 struct fsl_elbc_gpcm
*priv
= info
->priv
;
178 /* get an optional initial win0 offset */
179 prop
= of_get_property(priv
->dev
->of_node
,
180 "netx5152,init-win0-offset", NULL
);
182 win0_offset
= of_read_ulong(prop
, 1);
184 /* disable interrupts */
185 iowrite32(0, info
->mem
[0].internal_addr
+ win0_offset
+
189 static void netx5152_shutdown(struct uio_info
*info
, bool init_err
)
194 /* disable interrupts */
195 iowrite32(0, info
->mem
[0].internal_addr
+ DPM_HOST_WIN0_OFFSET
+
200 static void setup_periph(struct fsl_elbc_gpcm
*priv
,
203 #ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
204 if (strcmp(type
, "netx5152") == 0) {
205 priv
->irq_handler
= netx5152_irq_handler
;
206 priv
->init
= netx5152_init
;
207 priv
->shutdown
= netx5152_shutdown
;
208 priv
->name
= "netX 51/52";
214 static int check_of_data(struct fsl_elbc_gpcm
*priv
,
215 struct resource
*res
,
216 u32 reg_br
, u32 reg_or
)
218 /* check specified bank */
219 if (priv
->bank
>= MAX_BANKS
) {
220 dev_err(priv
->dev
, "invalid bank\n");
224 /* check specified mode (BR_MS_GPCM is 0) */
225 if ((reg_br
& BR_MSEL
) != BR_MS_GPCM
) {
226 dev_err(priv
->dev
, "unsupported mode\n");
230 /* check specified mask vs. resource size */
231 if ((~(reg_or
& OR_GPCM_AM
) + 1) != resource_size(res
)) {
232 dev_err(priv
->dev
, "address mask / size mismatch\n");
236 /* check specified address */
237 if ((reg_br
& reg_or
& BR_BA
) != fsl_lbc_addr(res
->start
)) {
238 dev_err(priv
->dev
, "base address mismatch\n");
245 static int get_of_data(struct fsl_elbc_gpcm
*priv
, struct device_node
*node
,
246 struct resource
*res
, u32
*reg_br
,
247 u32
*reg_or
, unsigned int *irq
, char **name
)
253 /* get the memory resource */
254 ret
= of_address_to_resource(node
, 0, res
);
256 dev_err(priv
->dev
, "failed to get resource\n");
260 /* get the bank number */
261 ret
= of_property_read_u32(node
, "reg", &priv
->bank
);
263 dev_err(priv
->dev
, "failed to get bank number\n");
267 /* get BR value to set */
268 ret
= of_property_read_u32(node
, "elbc-gpcm-br", reg_br
);
270 dev_err(priv
->dev
, "missing elbc-gpcm-br value\n");
274 /* get OR value to set */
275 ret
= of_property_read_u32(node
, "elbc-gpcm-or", reg_or
);
277 dev_err(priv
->dev
, "missing elbc-gpcm-or value\n");
281 /* get optional peripheral type */
282 priv
->name
= "generic";
283 if (of_property_read_string(node
, "device_type", &type
) == 0)
284 setup_periph(priv
, type
);
286 /* get optional irq value */
287 *irq
= irq_of_parse_and_map(node
, 0);
289 /* sanity check device tree data */
290 ret
= check_of_data(priv
, res
, *reg_br
, *reg_or
);
294 /* get optional uio name */
295 if (of_property_read_string(node
, "uio_name", &dt_name
) != 0)
296 dt_name
= "eLBC_GPCM";
297 *name
= kstrdup(dt_name
, GFP_KERNEL
);
304 static int uio_fsl_elbc_gpcm_probe(struct platform_device
*pdev
)
306 struct device_node
*node
= pdev
->dev
.of_node
;
307 struct fsl_elbc_gpcm
*priv
;
308 struct uio_info
*info
;
309 char *uio_name
= NULL
;
318 if (!fsl_lbc_ctrl_dev
|| !fsl_lbc_ctrl_dev
->regs
)
321 /* allocate private data */
322 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
325 priv
->dev
= &pdev
->dev
;
326 priv
->lbc
= fsl_lbc_ctrl_dev
->regs
;
328 /* get device tree data */
329 ret
= get_of_data(priv
, node
, &res
, ®_br_new
, ®_or_new
,
334 /* allocate UIO structure */
335 info
= kzalloc(sizeof(*info
), GFP_KERNEL
);
341 /* get current BR/OR values */
342 reg_br_cur
= in_be32(&priv
->lbc
->bank
[priv
->bank
].br
);
343 reg_or_cur
= in_be32(&priv
->lbc
->bank
[priv
->bank
].or);
345 /* if bank already configured, make sure it matches */
346 if ((reg_br_cur
& BR_V
)) {
347 if ((reg_br_cur
& BR_MSEL
) != BR_MS_GPCM
||
348 (reg_br_cur
& reg_or_cur
& BR_BA
)
349 != fsl_lbc_addr(res
.start
)) {
351 "bank in use by another peripheral\n");
356 /* warn if behavior settings changing */
357 if ((reg_br_cur
& ~(BR_BA
| BR_V
)) !=
358 (reg_br_new
& ~(BR_BA
| BR_V
))) {
360 "modifying BR settings: 0x%08x -> 0x%08x",
361 reg_br_cur
, reg_br_new
);
363 if ((reg_or_cur
& ~OR_GPCM_AM
) != (reg_or_new
& ~OR_GPCM_AM
)) {
365 "modifying OR settings: 0x%08x -> 0x%08x",
366 reg_or_cur
, reg_or_new
);
370 /* configure the bank (force base address and GPCM) */
371 reg_br_new
&= ~(BR_BA
| BR_MSEL
);
372 reg_br_new
|= fsl_lbc_addr(res
.start
) | BR_MS_GPCM
| BR_V
;
373 out_be32(&priv
->lbc
->bank
[priv
->bank
].or, reg_or_new
);
374 out_be32(&priv
->lbc
->bank
[priv
->bank
].br
, reg_br_new
);
376 /* map the memory resource */
377 info
->mem
[0].internal_addr
= ioremap(res
.start
, resource_size(&res
));
378 if (!info
->mem
[0].internal_addr
) {
379 dev_err(priv
->dev
, "failed to map chip region\n");
384 /* set all UIO data */
386 info
->mem
[0].name
= kstrdup(node
->name
, GFP_KERNEL
);
387 info
->mem
[0].addr
= res
.start
;
388 info
->mem
[0].size
= resource_size(&res
);
389 info
->mem
[0].memtype
= UIO_MEM_PHYS
;
391 info
->name
= uio_name
;
392 info
->version
= "0.0.1";
394 if (priv
->irq_handler
) {
396 info
->irq_flags
= IRQF_SHARED
;
397 info
->handler
= priv
->irq_handler
;
400 dev_warn(priv
->dev
, "ignoring irq, no handler\n");
407 /* register UIO device */
408 if (uio_register_device(priv
->dev
, info
) != 0) {
409 dev_err(priv
->dev
, "UIO registration failed\n");
414 /* store private data */
415 platform_set_drvdata(pdev
, info
);
417 /* create sysfs files */
418 ret
= device_create_file(priv
->dev
, &dev_attr_reg_br
);
421 ret
= device_create_file(priv
->dev
, &dev_attr_reg_or
);
426 "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
427 priv
->name
, (unsigned long long)res
.start
, priv
->bank
,
428 irq
!= NO_IRQ
? irq
: -1);
432 device_remove_file(priv
->dev
, &dev_attr_reg_br
);
434 platform_set_drvdata(pdev
, NULL
);
435 uio_unregister_device(info
);
438 priv
->shutdown(info
, true);
439 iounmap(info
->mem
[0].internal_addr
);
441 kfree(info
->mem
[0].name
);
449 static int uio_fsl_elbc_gpcm_remove(struct platform_device
*pdev
)
451 struct uio_info
*info
= platform_get_drvdata(pdev
);
452 struct fsl_elbc_gpcm
*priv
= info
->priv
;
454 device_remove_file(priv
->dev
, &dev_attr_reg_or
);
455 device_remove_file(priv
->dev
, &dev_attr_reg_br
);
456 platform_set_drvdata(pdev
, NULL
);
457 uio_unregister_device(info
);
459 priv
->shutdown(info
, false);
460 iounmap(info
->mem
[0].internal_addr
);
461 kfree(info
->mem
[0].name
);
470 static const struct of_device_id uio_fsl_elbc_gpcm_match
[] = {
471 { .compatible
= "fsl,elbc-gpcm-uio", },
474 MODULE_DEVICE_TABLE(of
, uio_fsl_elbc_gpcm_match
);
476 static struct platform_driver uio_fsl_elbc_gpcm_driver
= {
478 .name
= "fsl,elbc-gpcm-uio",
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");