2 * Ingenic JZ47xx GPIO driver
4 * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
6 * License terms: GNU General Public License (GPL) version 2
9 #include <linux/gpio/driver.h>
10 #include <linux/interrupt.h>
12 #include <linux/module.h>
13 #include <linux/of_address.h>
14 #include <linux/of_device.h>
15 #include <linux/of_irq.h>
16 #include <linux/pinctrl/consumer.h>
17 #include <linux/regmap.h>
22 #define JZ4740_GPIO_DATA 0x10
23 #define JZ4740_GPIO_SELECT 0x50
24 #define JZ4740_GPIO_DIR 0x60
25 #define JZ4740_GPIO_TRIG 0x70
26 #define JZ4740_GPIO_FLAG 0x80
28 #define JZ4770_GPIO_INT 0x10
29 #define JZ4770_GPIO_PAT1 0x30
30 #define JZ4770_GPIO_PAT0 0x40
31 #define JZ4770_GPIO_FLAG 0x50
33 #define REG_SET(x) ((x) + 0x4)
34 #define REG_CLEAR(x) ((x) + 0x8)
42 struct ingenic_gpio_chip
{
45 struct irq_chip irq_chip
;
46 unsigned int irq
, reg_base
;
47 enum jz_version version
;
50 static u32
gpio_ingenic_read_reg(struct ingenic_gpio_chip
*jzgc
, u8 reg
)
54 regmap_read(jzgc
->map
, jzgc
->reg_base
+ reg
, &val
);
59 static void gpio_ingenic_set_bit(struct ingenic_gpio_chip
*jzgc
,
60 u8 reg
, u8 offset
, bool set
)
67 regmap_write(jzgc
->map
, jzgc
->reg_base
+ reg
, BIT(offset
));
70 static inline bool gpio_get_value(struct ingenic_gpio_chip
*jzgc
, u8 offset
)
72 unsigned int val
= gpio_ingenic_read_reg(jzgc
, GPIO_PIN
);
74 return !!(val
& BIT(offset
));
77 static void gpio_set_value(struct ingenic_gpio_chip
*jzgc
, u8 offset
, int value
)
79 if (jzgc
->version
>= ID_JZ4770
)
80 gpio_ingenic_set_bit(jzgc
, JZ4770_GPIO_PAT0
, offset
, !!value
);
82 gpio_ingenic_set_bit(jzgc
, JZ4740_GPIO_DATA
, offset
, !!value
);
85 static void irq_set_type(struct ingenic_gpio_chip
*jzgc
,
86 u8 offset
, unsigned int type
)
90 if (jzgc
->version
>= ID_JZ4770
) {
91 reg1
= JZ4770_GPIO_PAT1
;
92 reg2
= JZ4770_GPIO_PAT0
;
94 reg1
= JZ4740_GPIO_TRIG
;
95 reg2
= JZ4740_GPIO_DIR
;
99 case IRQ_TYPE_EDGE_RISING
:
100 gpio_ingenic_set_bit(jzgc
, reg2
, offset
, true);
101 gpio_ingenic_set_bit(jzgc
, reg1
, offset
, true);
103 case IRQ_TYPE_EDGE_FALLING
:
104 gpio_ingenic_set_bit(jzgc
, reg2
, offset
, false);
105 gpio_ingenic_set_bit(jzgc
, reg1
, offset
, true);
107 case IRQ_TYPE_LEVEL_HIGH
:
108 gpio_ingenic_set_bit(jzgc
, reg2
, offset
, true);
109 gpio_ingenic_set_bit(jzgc
, reg1
, offset
, false);
111 case IRQ_TYPE_LEVEL_LOW
:
113 gpio_ingenic_set_bit(jzgc
, reg2
, offset
, false);
114 gpio_ingenic_set_bit(jzgc
, reg1
, offset
, false);
119 static void ingenic_gpio_irq_mask(struct irq_data
*irqd
)
121 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
122 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
124 gpio_ingenic_set_bit(jzgc
, GPIO_MSK
, irqd
->hwirq
, true);
127 static void ingenic_gpio_irq_unmask(struct irq_data
*irqd
)
129 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
130 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
132 gpio_ingenic_set_bit(jzgc
, GPIO_MSK
, irqd
->hwirq
, false);
135 static void ingenic_gpio_irq_enable(struct irq_data
*irqd
)
137 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
138 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
139 int irq
= irqd
->hwirq
;
141 if (jzgc
->version
>= ID_JZ4770
)
142 gpio_ingenic_set_bit(jzgc
, JZ4770_GPIO_INT
, irq
, true);
144 gpio_ingenic_set_bit(jzgc
, JZ4740_GPIO_SELECT
, irq
, true);
146 ingenic_gpio_irq_unmask(irqd
);
149 static void ingenic_gpio_irq_disable(struct irq_data
*irqd
)
151 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
152 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
153 int irq
= irqd
->hwirq
;
155 ingenic_gpio_irq_mask(irqd
);
157 if (jzgc
->version
>= ID_JZ4770
)
158 gpio_ingenic_set_bit(jzgc
, JZ4770_GPIO_INT
, irq
, false);
160 gpio_ingenic_set_bit(jzgc
, JZ4740_GPIO_SELECT
, irq
, false);
163 static void ingenic_gpio_irq_ack(struct irq_data
*irqd
)
165 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
166 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
167 int irq
= irqd
->hwirq
;
170 if (irqd_get_trigger_type(irqd
) == IRQ_TYPE_EDGE_BOTH
) {
172 * Switch to an interrupt for the opposite edge to the one that
173 * triggered the interrupt being ACKed.
175 high
= gpio_get_value(jzgc
, irq
);
177 irq_set_type(jzgc
, irq
, IRQ_TYPE_EDGE_FALLING
);
179 irq_set_type(jzgc
, irq
, IRQ_TYPE_EDGE_RISING
);
182 if (jzgc
->version
>= ID_JZ4770
)
183 gpio_ingenic_set_bit(jzgc
, JZ4770_GPIO_FLAG
, irq
, false);
185 gpio_ingenic_set_bit(jzgc
, JZ4740_GPIO_DATA
, irq
, true);
188 static int ingenic_gpio_irq_set_type(struct irq_data
*irqd
, unsigned int type
)
190 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
191 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
194 case IRQ_TYPE_EDGE_BOTH
:
195 case IRQ_TYPE_EDGE_RISING
:
196 case IRQ_TYPE_EDGE_FALLING
:
197 irq_set_handler_locked(irqd
, handle_edge_irq
);
199 case IRQ_TYPE_LEVEL_HIGH
:
200 case IRQ_TYPE_LEVEL_LOW
:
201 irq_set_handler_locked(irqd
, handle_level_irq
);
204 irq_set_handler_locked(irqd
, handle_bad_irq
);
207 if (type
== IRQ_TYPE_EDGE_BOTH
) {
209 * The hardware does not support interrupts on both edges. The
210 * best we can do is to set up a single-edge interrupt and then
211 * switch to the opposing edge when ACKing the interrupt.
213 bool high
= gpio_get_value(jzgc
, irqd
->hwirq
);
215 type
= high
? IRQ_TYPE_EDGE_FALLING
: IRQ_TYPE_EDGE_RISING
;
218 irq_set_type(jzgc
, irqd
->hwirq
, type
);
222 static int ingenic_gpio_irq_set_wake(struct irq_data
*irqd
, unsigned int on
)
224 struct gpio_chip
*gc
= irq_data_get_irq_chip_data(irqd
);
225 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
227 return irq_set_irq_wake(jzgc
->irq
, on
);
230 static void ingenic_gpio_irq_handler(struct irq_desc
*desc
)
232 struct gpio_chip
*gc
= irq_desc_get_handler_data(desc
);
233 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
234 struct irq_chip
*irq_chip
= irq_data_get_irq_chip(&desc
->irq_data
);
235 unsigned long flag
, i
;
237 chained_irq_enter(irq_chip
, desc
);
239 if (jzgc
->version
>= ID_JZ4770
)
240 flag
= gpio_ingenic_read_reg(jzgc
, JZ4770_GPIO_FLAG
);
242 flag
= gpio_ingenic_read_reg(jzgc
, JZ4740_GPIO_FLAG
);
244 for_each_set_bit(i
, &flag
, 32)
245 generic_handle_irq(irq_linear_revmap(gc
->irq
.domain
, i
));
246 chained_irq_exit(irq_chip
, desc
);
249 static void ingenic_gpio_set(struct gpio_chip
*gc
,
250 unsigned int offset
, int value
)
252 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
254 gpio_set_value(jzgc
, offset
, value
);
257 static int ingenic_gpio_get(struct gpio_chip
*gc
, unsigned int offset
)
259 struct ingenic_gpio_chip
*jzgc
= gpiochip_get_data(gc
);
261 return (int) gpio_get_value(jzgc
, offset
);
264 static int ingenic_gpio_direction_input(struct gpio_chip
*gc
,
267 return pinctrl_gpio_direction_input(gc
->base
+ offset
);
270 static int ingenic_gpio_direction_output(struct gpio_chip
*gc
,
271 unsigned int offset
, int value
)
273 ingenic_gpio_set(gc
, offset
, value
);
274 return pinctrl_gpio_direction_output(gc
->base
+ offset
);
277 static const struct of_device_id ingenic_gpio_of_match
[] = {
278 { .compatible
= "ingenic,jz4740-gpio", .data
= (void *)ID_JZ4740
},
279 { .compatible
= "ingenic,jz4770-gpio", .data
= (void *)ID_JZ4770
},
280 { .compatible
= "ingenic,jz4780-gpio", .data
= (void *)ID_JZ4780
},
283 MODULE_DEVICE_TABLE(of
, ingenic_gpio_of_match
);
285 static int ingenic_gpio_probe(struct platform_device
*pdev
)
287 struct device
*dev
= &pdev
->dev
;
288 struct ingenic_gpio_chip
*jzgc
;
292 jzgc
= devm_kzalloc(dev
, sizeof(*jzgc
), GFP_KERNEL
);
296 jzgc
->map
= dev_get_drvdata(dev
->parent
);
298 dev_err(dev
, "Cannot get parent regmap\n");
302 err
= of_property_read_u32(dev
->of_node
, "reg", &bank
);
304 dev_err(dev
, "Cannot read \"reg\" property: %i\n", err
);
308 jzgc
->reg_base
= bank
* 0x100;
310 jzgc
->gc
.label
= devm_kasprintf(dev
, GFP_KERNEL
, "GPIO%c", 'A' + bank
);
314 /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY
315 * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN
316 * <linux/gpio/consumer.h> INSTEAD.
318 jzgc
->gc
.base
= bank
* 32;
321 jzgc
->gc
.parent
= dev
;
322 jzgc
->gc
.of_node
= dev
->of_node
;
323 jzgc
->gc
.owner
= THIS_MODULE
;
324 jzgc
->version
= (enum jz_version
)of_device_get_match_data(dev
);
326 jzgc
->gc
.set
= ingenic_gpio_set
;
327 jzgc
->gc
.get
= ingenic_gpio_get
;
328 jzgc
->gc
.direction_input
= ingenic_gpio_direction_input
;
329 jzgc
->gc
.direction_output
= ingenic_gpio_direction_output
;
331 if (of_property_read_bool(dev
->of_node
, "gpio-ranges")) {
332 jzgc
->gc
.request
= gpiochip_generic_request
;
333 jzgc
->gc
.free
= gpiochip_generic_free
;
336 err
= devm_gpiochip_add_data(dev
, &jzgc
->gc
, jzgc
);
340 jzgc
->irq
= irq_of_parse_and_map(dev
->of_node
, 0);
344 jzgc
->irq_chip
.name
= jzgc
->gc
.label
;
345 jzgc
->irq_chip
.irq_enable
= ingenic_gpio_irq_enable
;
346 jzgc
->irq_chip
.irq_disable
= ingenic_gpio_irq_disable
;
347 jzgc
->irq_chip
.irq_unmask
= ingenic_gpio_irq_unmask
;
348 jzgc
->irq_chip
.irq_mask
= ingenic_gpio_irq_mask
;
349 jzgc
->irq_chip
.irq_ack
= ingenic_gpio_irq_ack
;
350 jzgc
->irq_chip
.irq_set_type
= ingenic_gpio_irq_set_type
;
351 jzgc
->irq_chip
.irq_set_wake
= ingenic_gpio_irq_set_wake
;
352 jzgc
->irq_chip
.flags
= IRQCHIP_MASK_ON_SUSPEND
;
354 err
= gpiochip_irqchip_add(&jzgc
->gc
, &jzgc
->irq_chip
, 0,
355 handle_level_irq
, IRQ_TYPE_NONE
);
359 gpiochip_set_chained_irqchip(&jzgc
->gc
, &jzgc
->irq_chip
,
360 jzgc
->irq
, ingenic_gpio_irq_handler
);
364 static int ingenic_gpio_remove(struct platform_device
*pdev
)
369 static struct platform_driver ingenic_gpio_driver
= {
371 .name
= "gpio-ingenic",
372 .of_match_table
= of_match_ptr(ingenic_gpio_of_match
),
374 .probe
= ingenic_gpio_probe
,
375 .remove
= ingenic_gpio_remove
,
378 static int __init
ingenic_gpio_drv_register(void)
380 return platform_driver_register(&ingenic_gpio_driver
);
382 subsys_initcall(ingenic_gpio_drv_register
);
384 static void __exit
ingenic_gpio_drv_unregister(void)
386 platform_driver_unregister(&ingenic_gpio_driver
);
388 module_exit(ingenic_gpio_drv_unregister
);
390 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
391 MODULE_DESCRIPTION("Ingenic JZ47xx GPIO driver");
392 MODULE_LICENSE("GPL");