1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright 2020 Michael Walle <michael@walle.cc>
8 #include <linux/device.h>
9 #include <linux/gpio/driver.h>
10 #include <linux/gpio/regmap.h>
11 #include <linux/interrupt.h>
12 #include <linux/kernel.h>
13 #include <linux/mod_devicetable.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
19 #define GPIO_REG_DIR 0x00
20 #define GPIO_REG_OUT 0x01
21 #define GPIO_REG_IN 0x02
22 #define GPIO_REG_IE 0x03
23 #define GPIO_REG_IP 0x04
25 /* input-only flavor */
26 #define GPI_REG_IN 0x00
28 /* output-only flavor */
29 #define GPO_REG_OUT 0x00
31 enum sl28cpld_gpio_type
{
37 static const struct regmap_irq sl28cpld_gpio_irqs
[] = {
38 REGMAP_IRQ_REG_LINE(0, 8),
39 REGMAP_IRQ_REG_LINE(1, 8),
40 REGMAP_IRQ_REG_LINE(2, 8),
41 REGMAP_IRQ_REG_LINE(3, 8),
42 REGMAP_IRQ_REG_LINE(4, 8),
43 REGMAP_IRQ_REG_LINE(5, 8),
44 REGMAP_IRQ_REG_LINE(6, 8),
45 REGMAP_IRQ_REG_LINE(7, 8),
48 static int sl28cpld_gpio_irq_init(struct platform_device
*pdev
,
50 struct gpio_regmap_config
*config
)
52 struct regmap_irq_chip_data
*irq_data
;
53 struct regmap_irq_chip
*irq_chip
;
54 struct device
*dev
= &pdev
->dev
;
57 if (!device_property_read_bool(dev
, "interrupt-controller"))
60 irq
= platform_get_irq(pdev
, 0);
64 irq_chip
= devm_kzalloc(dev
, sizeof(*irq_chip
), GFP_KERNEL
);
68 irq_chip
->name
= "sl28cpld-gpio-irq",
69 irq_chip
->irqs
= sl28cpld_gpio_irqs
;
70 irq_chip
->num_irqs
= ARRAY_SIZE(sl28cpld_gpio_irqs
);
71 irq_chip
->num_regs
= 1;
72 irq_chip
->status_base
= base
+ GPIO_REG_IP
;
73 irq_chip
->mask_base
= base
+ GPIO_REG_IE
;
74 irq_chip
->mask_invert
= true,
75 irq_chip
->ack_base
= base
+ GPIO_REG_IP
;
77 ret
= devm_regmap_add_irq_chip_fwnode(dev
, dev_fwnode(dev
),
79 IRQF_SHARED
| IRQF_ONESHOT
,
80 0, irq_chip
, &irq_data
);
84 config
->irq_domain
= regmap_irq_get_domain(irq_data
);
89 static int sl28cpld_gpio_probe(struct platform_device
*pdev
)
91 struct gpio_regmap_config config
= {0};
92 enum sl28cpld_gpio_type type
;
93 struct regmap
*regmap
;
97 if (!pdev
->dev
.parent
)
100 type
= (uintptr_t)device_get_match_data(&pdev
->dev
);
104 ret
= device_property_read_u32(&pdev
->dev
, "reg", &base
);
108 regmap
= dev_get_regmap(pdev
->dev
.parent
, NULL
);
112 config
.regmap
= regmap
;
113 config
.parent
= &pdev
->dev
;
118 config
.reg_dat_base
= base
+ GPIO_REG_IN
;
119 config
.reg_set_base
= base
+ GPIO_REG_OUT
;
120 /* reg_dir_out_base might be zero */
121 config
.reg_dir_out_base
= GPIO_REGMAP_ADDR(base
+ GPIO_REG_DIR
);
123 /* This type supports interrupts */
124 ret
= sl28cpld_gpio_irq_init(pdev
, base
, &config
);
129 config
.reg_set_base
= base
+ GPO_REG_OUT
;
132 config
.reg_dat_base
= base
+ GPI_REG_IN
;
135 dev_err(&pdev
->dev
, "unknown type %d\n", type
);
139 return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev
->dev
, &config
));
142 static const struct of_device_id sl28cpld_gpio_of_match
[] = {
143 { .compatible
= "kontron,sl28cpld-gpio", .data
= (void *)SL28CPLD_GPIO
},
144 { .compatible
= "kontron,sl28cpld-gpi", .data
= (void *)SL28CPLD_GPI
},
145 { .compatible
= "kontron,sl28cpld-gpo", .data
= (void *)SL28CPLD_GPO
},
148 MODULE_DEVICE_TABLE(of
, sl28cpld_gpio_of_match
);
150 static struct platform_driver sl28cpld_gpio_driver
= {
151 .probe
= sl28cpld_gpio_probe
,
153 .name
= "sl28cpld-gpio",
154 .of_match_table
= sl28cpld_gpio_of_match
,
157 module_platform_driver(sl28cpld_gpio_driver
);
159 MODULE_DESCRIPTION("sl28cpld GPIO Driver");
160 MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
161 MODULE_LICENSE("GPL");