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