1 | /* |
2 | * Broadcom specific AMBA |
3 | * GPIO driver |
4 | * |
5 | * Copyright 2011, Broadcom Corporation |
6 | * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de> |
7 | * |
8 | * Licensed under the GNU/GPL. See COPYING for details. |
9 | */ |
10 | |
11 | #include <linux/gpio/driver.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/export.h> |
14 | #include <linux/property.h> |
15 | |
16 | #include <linux/bcma/bcma.h> |
17 | |
18 | #include "bcma_private.h" |
19 | |
20 | #define BCMA_GPIO_MAX_PINS 32 |
21 | |
22 | static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
23 | { |
24 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
25 | |
26 | return !!bcma_chipco_gpio_in(cc, mask: 1 << gpio); |
27 | } |
28 | |
29 | static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio, |
30 | int value) |
31 | { |
32 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
33 | |
34 | bcma_chipco_gpio_out(cc, mask: 1 << gpio, value: value ? 1 << gpio : 0); |
35 | } |
36 | |
37 | static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) |
38 | { |
39 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
40 | |
41 | bcma_chipco_gpio_outen(cc, mask: 1 << gpio, value: 0); |
42 | return 0; |
43 | } |
44 | |
45 | static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, |
46 | int value) |
47 | { |
48 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
49 | |
50 | bcma_chipco_gpio_outen(cc, mask: 1 << gpio, value: 1 << gpio); |
51 | bcma_chipco_gpio_out(cc, mask: 1 << gpio, value: value ? 1 << gpio : 0); |
52 | return 0; |
53 | } |
54 | |
55 | static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio) |
56 | { |
57 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
58 | |
59 | bcma_chipco_gpio_control(cc, mask: 1 << gpio, value: 0); |
60 | /* clear pulldown */ |
61 | bcma_chipco_gpio_pulldown(cc, mask: 1 << gpio, value: 0); |
62 | /* Set pullup */ |
63 | bcma_chipco_gpio_pullup(cc, mask: 1 << gpio, value: 1 << gpio); |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) |
69 | { |
70 | struct bcma_drv_cc *cc = gpiochip_get_data(gc: chip); |
71 | |
72 | /* clear pullup */ |
73 | bcma_chipco_gpio_pullup(cc, mask: 1 << gpio, value: 0); |
74 | } |
75 | |
76 | #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X) |
77 | |
78 | static void bcma_gpio_irq_unmask(struct irq_data *d) |
79 | { |
80 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
81 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
82 | int gpio = irqd_to_hwirq(d); |
83 | u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); |
84 | |
85 | gpiochip_enable_irq(gc, gpio); |
86 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
87 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
88 | } |
89 | |
90 | static void bcma_gpio_irq_mask(struct irq_data *d) |
91 | { |
92 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
93 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
94 | int gpio = irqd_to_hwirq(d); |
95 | |
96 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); |
97 | gpiochip_disable_irq(gc, gpio); |
98 | } |
99 | |
100 | static const struct irq_chip bcma_gpio_irq_chip = { |
101 | .name = "BCMA-GPIO" , |
102 | .irq_mask = bcma_gpio_irq_mask, |
103 | .irq_unmask = bcma_gpio_irq_unmask, |
104 | .flags = IRQCHIP_IMMUTABLE, |
105 | GPIOCHIP_IRQ_RESOURCE_HELPERS, |
106 | }; |
107 | |
108 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) |
109 | { |
110 | struct bcma_drv_cc *cc = dev_id; |
111 | struct gpio_chip *gc = &cc->gpio; |
112 | u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN); |
113 | u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ); |
114 | u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL); |
115 | unsigned long irqs = (val ^ pol) & mask; |
116 | int gpio; |
117 | |
118 | if (!irqs) |
119 | return IRQ_NONE; |
120 | |
121 | for_each_set_bit(gpio, &irqs, gc->ngpio) |
122 | generic_handle_domain_irq_safe(gc->irq.domain, gpio); |
123 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); |
124 | |
125 | return IRQ_HANDLED; |
126 | } |
127 | |
128 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
129 | { |
130 | struct gpio_chip *chip = &cc->gpio; |
131 | struct gpio_irq_chip *girq = &chip->irq; |
132 | int hwirq, err; |
133 | |
134 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) |
135 | return 0; |
136 | |
137 | hwirq = bcma_core_irq(cc->core, 0); |
138 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio" , |
139 | cc); |
140 | if (err) |
141 | return err; |
142 | |
143 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
144 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
145 | |
146 | gpio_irq_chip_set_chip(girq, &bcma_gpio_irq_chip); |
147 | /* This will let us handle the parent IRQ in the driver */ |
148 | girq->parent_handler = NULL; |
149 | girq->num_parents = 0; |
150 | girq->parents = NULL; |
151 | girq->default_type = IRQ_TYPE_NONE; |
152 | girq->handler = handle_simple_irq; |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
158 | { |
159 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) |
160 | return; |
161 | |
162 | bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); |
163 | free_irq(bcma_core_irq(cc->core, 0), cc); |
164 | } |
165 | #else |
166 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
167 | { |
168 | return 0; |
169 | } |
170 | |
171 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
172 | { |
173 | } |
174 | #endif |
175 | |
176 | int bcma_gpio_init(struct bcma_drv_cc *cc) |
177 | { |
178 | struct bcma_bus *bus = cc->core->bus; |
179 | struct gpio_chip *chip = &cc->gpio; |
180 | int err; |
181 | |
182 | chip->label = "bcma_gpio" ; |
183 | chip->owner = THIS_MODULE; |
184 | chip->request = bcma_gpio_request; |
185 | chip->free = bcma_gpio_free; |
186 | chip->get = bcma_gpio_get_value; |
187 | chip->set = bcma_gpio_set_value; |
188 | chip->direction_input = bcma_gpio_direction_input; |
189 | chip->direction_output = bcma_gpio_direction_output; |
190 | chip->parent = bus->dev; |
191 | chip->fwnode = dev_fwnode(&cc->core->dev); |
192 | |
193 | switch (bus->chipinfo.id) { |
194 | case BCMA_CHIP_ID_BCM4707: |
195 | case BCMA_CHIP_ID_BCM5357: |
196 | case BCMA_CHIP_ID_BCM53572: |
197 | case BCMA_CHIP_ID_BCM53573: |
198 | case BCMA_CHIP_ID_BCM47094: |
199 | chip->ngpio = 32; |
200 | break; |
201 | default: |
202 | chip->ngpio = 16; |
203 | } |
204 | |
205 | /* |
206 | * Register SoC GPIO devices with absolute GPIO pin base. |
207 | * On MIPS, we don't have Device Tree and we can't use relative (per chip) |
208 | * GPIO numbers. |
209 | * On some ARM devices, user space may want to access some system GPIO |
210 | * pins directly, which is easier to do with a predictable GPIO base. |
211 | */ |
212 | if (IS_BUILTIN(CONFIG_BCM47XX) || |
213 | cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) |
214 | chip->base = bus->num * BCMA_GPIO_MAX_PINS; |
215 | else |
216 | chip->base = -1; |
217 | |
218 | err = bcma_gpio_irq_init(cc); |
219 | if (err) |
220 | return err; |
221 | |
222 | err = gpiochip_add_data(chip, cc); |
223 | if (err) { |
224 | bcma_gpio_irq_exit(cc); |
225 | return err; |
226 | } |
227 | |
228 | return 0; |
229 | } |
230 | |
231 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) |
232 | { |
233 | bcma_gpio_irq_exit(cc); |
234 | gpiochip_remove(gc: &cc->gpio); |
235 | return 0; |
236 | } |
237 | |