Commit | Line | Data |
---|---|---|
813e7d36 LW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // IXP4 GPIO driver | |
4 | // Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> | |
5 | // | |
6 | // based on previous work and know-how from: | |
7 | // Deepak Saxena <dsaxena@plexity.net> | |
8 | ||
9 | #include <linux/gpio/driver.h> | |
10 | #include <linux/io.h> | |
11 | #include <linux/irq.h> | |
12 | #include <linux/irqdomain.h> | |
13 | #include <linux/irqchip.h> | |
e4bfb0ff | 14 | #include <linux/of_irq.h> |
813e7d36 LW |
15 | #include <linux/platform_device.h> |
16 | #include <linux/bitops.h> | |
813e7d36 | 17 | |
813e7d36 LW |
18 | #define IXP4XX_REG_GPOUT 0x00 |
19 | #define IXP4XX_REG_GPOE 0x04 | |
20 | #define IXP4XX_REG_GPIN 0x08 | |
21 | #define IXP4XX_REG_GPIS 0x0C | |
22 | #define IXP4XX_REG_GPIT1 0x10 | |
23 | #define IXP4XX_REG_GPIT2 0x14 | |
24 | #define IXP4XX_REG_GPCLK 0x18 | |
25 | #define IXP4XX_REG_GPDBSEL 0x1C | |
26 | ||
27 | /* | |
28 | * The hardware uses 3 bits to indicate interrupt "style". | |
29 | * we clear and set these three bits accordingly. The lower 24 | |
30 | * bits in two registers (GPIT1 and GPIT2) are used to set up | |
31 | * the style for 8 lines each for a total of 16 GPIO lines. | |
32 | */ | |
33 | #define IXP4XX_GPIO_STYLE_ACTIVE_HIGH 0x0 | |
34 | #define IXP4XX_GPIO_STYLE_ACTIVE_LOW 0x1 | |
35 | #define IXP4XX_GPIO_STYLE_RISING_EDGE 0x2 | |
36 | #define IXP4XX_GPIO_STYLE_FALLING_EDGE 0x3 | |
37 | #define IXP4XX_GPIO_STYLE_TRANSITIONAL 0x4 | |
38 | #define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0) | |
39 | #define IXP4XX_GPIO_STYLE_SIZE 3 | |
40 | ||
41 | /** | |
42 | * struct ixp4xx_gpio - IXP4 GPIO state container | |
43 | * @dev: containing device for this instance | |
44 | * @fwnode: the fwnode for this GPIO chip | |
45 | * @gc: gpiochip for this instance | |
813e7d36 LW |
46 | * @base: remapped I/O-memory base |
47 | * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, | |
48 | * 0: level triggered | |
49 | */ | |
50 | struct ixp4xx_gpio { | |
51 | struct device *dev; | |
52 | struct fwnode_handle *fwnode; | |
53 | struct gpio_chip gc; | |
813e7d36 LW |
54 | void __iomem *base; |
55 | unsigned long long irq_edge; | |
56 | }; | |
57 | ||
813e7d36 LW |
58 | static void ixp4xx_gpio_irq_ack(struct irq_data *d) |
59 | { | |
aa7d618a LW |
60 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
61 | struct ixp4xx_gpio *g = gpiochip_get_data(gc); | |
813e7d36 LW |
62 | |
63 | __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); | |
64 | } | |
65 | ||
94e9bc73 LW |
66 | static void ixp4xx_gpio_mask_irq(struct irq_data *d) |
67 | { | |
68 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
69 | ||
70 | irq_chip_mask_parent(d); | |
71 | gpiochip_disable_irq(gc, d->hwirq); | |
72 | } | |
73 | ||
813e7d36 LW |
74 | static void ixp4xx_gpio_irq_unmask(struct irq_data *d) |
75 | { | |
aa7d618a LW |
76 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
77 | struct ixp4xx_gpio *g = gpiochip_get_data(gc); | |
813e7d36 LW |
78 | |
79 | /* ACK when unmasking if not edge-triggered */ | |
80 | if (!(g->irq_edge & BIT(d->hwirq))) | |
81 | ixp4xx_gpio_irq_ack(d); | |
82 | ||
94e9bc73 | 83 | gpiochip_enable_irq(gc, d->hwirq); |
813e7d36 LW |
84 | irq_chip_unmask_parent(d); |
85 | } | |
86 | ||
87 | static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) | |
88 | { | |
aa7d618a LW |
89 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
90 | struct ixp4xx_gpio *g = gpiochip_get_data(gc); | |
813e7d36 LW |
91 | int line = d->hwirq; |
92 | unsigned long flags; | |
93 | u32 int_style; | |
94 | u32 int_reg; | |
95 | u32 val; | |
96 | ||
97 | switch (type) { | |
98 | case IRQ_TYPE_EDGE_BOTH: | |
99 | irq_set_handler_locked(d, handle_edge_irq); | |
100 | int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL; | |
101 | g->irq_edge |= BIT(d->hwirq); | |
102 | break; | |
103 | case IRQ_TYPE_EDGE_RISING: | |
104 | irq_set_handler_locked(d, handle_edge_irq); | |
105 | int_style = IXP4XX_GPIO_STYLE_RISING_EDGE; | |
106 | g->irq_edge |= BIT(d->hwirq); | |
107 | break; | |
108 | case IRQ_TYPE_EDGE_FALLING: | |
109 | irq_set_handler_locked(d, handle_edge_irq); | |
110 | int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE; | |
111 | g->irq_edge |= BIT(d->hwirq); | |
112 | break; | |
113 | case IRQ_TYPE_LEVEL_HIGH: | |
114 | irq_set_handler_locked(d, handle_level_irq); | |
115 | int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH; | |
116 | g->irq_edge &= ~BIT(d->hwirq); | |
117 | break; | |
118 | case IRQ_TYPE_LEVEL_LOW: | |
119 | irq_set_handler_locked(d, handle_level_irq); | |
120 | int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW; | |
121 | g->irq_edge &= ~BIT(d->hwirq); | |
122 | break; | |
123 | default: | |
124 | return -EINVAL; | |
125 | } | |
126 | ||
127 | if (line >= 8) { | |
128 | /* pins 8-15 */ | |
129 | line -= 8; | |
130 | int_reg = IXP4XX_REG_GPIT2; | |
131 | } else { | |
132 | /* pins 0-7 */ | |
133 | int_reg = IXP4XX_REG_GPIT1; | |
134 | } | |
135 | ||
3c938cc5 | 136 | raw_spin_lock_irqsave(&g->gc.bgpio_lock, flags); |
813e7d36 LW |
137 | |
138 | /* Clear the style for the appropriate pin */ | |
139 | val = __raw_readl(g->base + int_reg); | |
140 | val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE)); | |
141 | __raw_writel(val, g->base + int_reg); | |
142 | ||
143 | __raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS); | |
144 | ||
145 | /* Set the new style */ | |
146 | val = __raw_readl(g->base + int_reg); | |
147 | val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); | |
148 | __raw_writel(val, g->base + int_reg); | |
149 | ||
150 | /* Force-configure this line as an input */ | |
151 | val = __raw_readl(g->base + IXP4XX_REG_GPOE); | |
152 | val |= BIT(d->hwirq); | |
153 | __raw_writel(val, g->base + IXP4XX_REG_GPOE); | |
154 | ||
3c938cc5 | 155 | raw_spin_unlock_irqrestore(&g->gc.bgpio_lock, flags); |
813e7d36 LW |
156 | |
157 | /* This parent only accept level high (asserted) */ | |
158 | return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); | |
159 | } | |
160 | ||
94e9bc73 | 161 | static const struct irq_chip ixp4xx_gpio_irqchip = { |
813e7d36 LW |
162 | .name = "IXP4GPIO", |
163 | .irq_ack = ixp4xx_gpio_irq_ack, | |
94e9bc73 | 164 | .irq_mask = ixp4xx_gpio_mask_irq, |
813e7d36 LW |
165 | .irq_unmask = ixp4xx_gpio_irq_unmask, |
166 | .irq_set_type = ixp4xx_gpio_irq_set_type, | |
94e9bc73 LW |
167 | .flags = IRQCHIP_IMMUTABLE, |
168 | GPIOCHIP_IRQ_RESOURCE_HELPERS, | |
813e7d36 LW |
169 | }; |
170 | ||
aa7d618a LW |
171 | static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, |
172 | unsigned int child, | |
173 | unsigned int child_type, | |
174 | unsigned int *parent, | |
175 | unsigned int *parent_type) | |
813e7d36 | 176 | { |
aa7d618a LW |
177 | /* All these interrupts are level high in the CPU */ |
178 | *parent_type = IRQ_TYPE_LEVEL_HIGH; | |
813e7d36 | 179 | |
aa7d618a LW |
180 | /* GPIO lines 0..12 have dedicated IRQs */ |
181 | if (child == 0) { | |
182 | *parent = 6; | |
183 | return 0; | |
813e7d36 | 184 | } |
aa7d618a LW |
185 | if (child == 1) { |
186 | *parent = 7; | |
813e7d36 LW |
187 | return 0; |
188 | } | |
aa7d618a LW |
189 | if (child >= 2 && child <= 12) { |
190 | *parent = child + 17; | |
191 | return 0; | |
813e7d36 | 192 | } |
aa7d618a | 193 | return -EINVAL; |
813e7d36 LW |
194 | } |
195 | ||
813e7d36 LW |
196 | static int ixp4xx_gpio_probe(struct platform_device *pdev) |
197 | { | |
198 | unsigned long flags; | |
199 | struct device *dev = &pdev->dev; | |
e4bfb0ff | 200 | struct device_node *np = dev->of_node; |
813e7d36 LW |
201 | struct irq_domain *parent; |
202 | struct resource *res; | |
203 | struct ixp4xx_gpio *g; | |
aa7d618a | 204 | struct gpio_irq_chip *girq; |
c83227a5 | 205 | struct device_node *irq_parent; |
813e7d36 | 206 | int ret; |
813e7d36 LW |
207 | |
208 | g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); | |
209 | if (!g) | |
210 | return -ENOMEM; | |
211 | g->dev = dev; | |
212 | ||
213 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
214 | g->base = devm_ioremap_resource(dev, res); | |
61059b70 | 215 | if (IS_ERR(g->base)) |
813e7d36 | 216 | return PTR_ERR(g->base); |
813e7d36 | 217 | |
c83227a5 LW |
218 | irq_parent = of_irq_find_parent(np); |
219 | if (!irq_parent) { | |
220 | dev_err(dev, "no IRQ parent node\n"); | |
221 | return -ENODEV; | |
222 | } | |
223 | parent = irq_find_host(irq_parent); | |
224 | if (!parent) { | |
225 | dev_err(dev, "no IRQ parent domain\n"); | |
226 | return -ENODEV; | |
aa7d618a | 227 | } |
c83227a5 | 228 | g->fwnode = of_node_to_fwnode(np); |
aa7d618a | 229 | |
813e7d36 LW |
230 | /* |
231 | * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on | |
232 | * specific machines. | |
233 | */ | |
4f3e79b3 LW |
234 | if (of_machine_is_compatible("dlink,dsm-g600-a") || |
235 | of_machine_is_compatible("iom,nas-100d")) | |
813e7d36 LW |
236 | __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK); |
237 | ||
238 | /* | |
239 | * This is a very special big-endian ARM issue: when the IXP4xx is | |
240 | * run in big endian mode, all registers in the machine are switched | |
241 | * around to the CPU-native endianness. As you see mostly in the | |
242 | * driver we use __raw_readl()/__raw_writel() to access the registers | |
243 | * in the appropriate order. With the GPIO library we need to specify | |
244 | * byte order explicitly, so this flag needs to be set when compiling | |
245 | * for big endian. | |
246 | */ | |
247 | #if defined(CONFIG_CPU_BIG_ENDIAN) | |
248 | flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; | |
249 | #else | |
250 | flags = 0; | |
251 | #endif | |
252 | ||
253 | /* Populate and register gpio chip */ | |
254 | ret = bgpio_init(&g->gc, dev, 4, | |
255 | g->base + IXP4XX_REG_GPIN, | |
256 | g->base + IXP4XX_REG_GPOUT, | |
257 | NULL, | |
258 | NULL, | |
259 | g->base + IXP4XX_REG_GPOE, | |
260 | flags); | |
261 | if (ret) { | |
262 | dev_err(dev, "unable to init generic GPIO\n"); | |
263 | return ret; | |
264 | } | |
813e7d36 LW |
265 | g->gc.ngpio = 16; |
266 | g->gc.label = "IXP4XX_GPIO_CHIP"; | |
267 | /* | |
268 | * TODO: when we have migrated to device tree and all GPIOs | |
269 | * are fetched using phandles, set this to -1 to get rid of | |
270 | * the fixed gpiochip base. | |
271 | */ | |
272 | g->gc.base = 0; | |
273 | g->gc.parent = &pdev->dev; | |
274 | g->gc.owner = THIS_MODULE; | |
275 | ||
aa7d618a | 276 | girq = &g->gc.irq; |
94e9bc73 | 277 | gpio_irq_chip_set_chip(girq, &ixp4xx_gpio_irqchip); |
aa7d618a LW |
278 | girq->fwnode = g->fwnode; |
279 | girq->parent_domain = parent; | |
280 | girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq; | |
281 | girq->handler = handle_bad_irq; | |
282 | girq->default_type = IRQ_TYPE_NONE; | |
283 | ||
813e7d36 LW |
284 | ret = devm_gpiochip_add_data(dev, &g->gc, g); |
285 | if (ret) { | |
286 | dev_err(dev, "failed to add SoC gpiochip\n"); | |
287 | return ret; | |
288 | } | |
289 | ||
813e7d36 | 290 | platform_set_drvdata(pdev, g); |
aa7d618a | 291 | dev_info(dev, "IXP4 GPIO registered\n"); |
813e7d36 LW |
292 | |
293 | return 0; | |
294 | } | |
295 | ||
e4bfb0ff LW |
296 | static const struct of_device_id ixp4xx_gpio_of_match[] = { |
297 | { | |
298 | .compatible = "intel,ixp4xx-gpio", | |
299 | }, | |
300 | {}, | |
301 | }; | |
302 | ||
303 | ||
813e7d36 LW |
304 | static struct platform_driver ixp4xx_gpio_driver = { |
305 | .driver = { | |
306 | .name = "ixp4xx-gpio", | |
e4bfb0ff | 307 | .of_match_table = of_match_ptr(ixp4xx_gpio_of_match), |
813e7d36 LW |
308 | }, |
309 | .probe = ixp4xx_gpio_probe, | |
310 | }; | |
311 | builtin_platform_driver(ixp4xx_gpio_driver); |