Commit | Line | Data |
---|---|---|
cf0936b0 HM |
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.h> | |
2997609e RM |
12 | #include <linux/irq.h> |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/irqdomain.h> | |
cf0936b0 HM |
15 | #include <linux/export.h> |
16 | #include <linux/bcma/bcma.h> | |
17 | ||
18 | #include "bcma_private.h" | |
19 | ||
20 | static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip) | |
21 | { | |
22 | return container_of(chip, struct bcma_drv_cc, gpio); | |
23 | } | |
24 | ||
25 | static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio) | |
26 | { | |
27 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
28 | ||
29 | return !!bcma_chipco_gpio_in(cc, 1 << gpio); | |
30 | } | |
31 | ||
32 | static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio, | |
33 | int value) | |
34 | { | |
35 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
36 | ||
37 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
38 | } | |
39 | ||
40 | static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | |
41 | { | |
42 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
43 | ||
44 | bcma_chipco_gpio_outen(cc, 1 << gpio, 0); | |
45 | return 0; | |
46 | } | |
47 | ||
48 | static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, | |
49 | int value) | |
50 | { | |
51 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
52 | ||
53 | bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio); | |
54 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
55 | return 0; | |
56 | } | |
57 | ||
58 | static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio) | |
59 | { | |
60 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
61 | ||
62 | bcma_chipco_gpio_control(cc, 1 << gpio, 0); | |
63 | /* clear pulldown */ | |
64 | bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0); | |
65 | /* Set pullup */ | |
66 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio); | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) | |
72 | { | |
73 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
74 | ||
75 | /* clear pullup */ | |
76 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); | |
77 | } | |
78 | ||
2997609e | 79 | #if IS_BUILTIN(CONFIG_BCMA_HOST_SOC) |
8f1ca268 HM |
80 | static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) |
81 | { | |
82 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); | |
83 | ||
84 | if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
2997609e | 85 | return irq_find_mapping(cc->irq_domain, gpio); |
8f1ca268 HM |
86 | else |
87 | return -EINVAL; | |
88 | } | |
89 | ||
2997609e RM |
90 | static void bcma_gpio_irq_unmask(struct irq_data *d) |
91 | { | |
92 | struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d); | |
93 | int gpio = irqd_to_hwirq(d); | |
978e55d2 | 94 | u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); |
2997609e | 95 | |
978e55d2 | 96 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
2997609e RM |
97 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
98 | } | |
99 | ||
100 | static void bcma_gpio_irq_mask(struct irq_data *d) | |
101 | { | |
102 | struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d); | |
103 | int gpio = irqd_to_hwirq(d); | |
104 | ||
105 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); | |
106 | } | |
107 | ||
108 | static struct irq_chip bcma_gpio_irq_chip = { | |
109 | .name = "BCMA-GPIO", | |
110 | .irq_mask = bcma_gpio_irq_mask, | |
111 | .irq_unmask = bcma_gpio_irq_unmask, | |
112 | }; | |
113 | ||
114 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) | |
115 | { | |
116 | struct bcma_drv_cc *cc = dev_id; | |
117 | u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN); | |
118 | u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ); | |
119 | u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL); | |
d5ab2adb | 120 | unsigned long irqs = (val ^ pol) & mask; |
2997609e RM |
121 | int gpio; |
122 | ||
123 | if (!irqs) | |
124 | return IRQ_NONE; | |
125 | ||
d5ab2adb | 126 | for_each_set_bit(gpio, &irqs, cc->gpio.ngpio) |
2997609e RM |
127 | generic_handle_irq(bcma_gpio_to_irq(&cc->gpio, gpio)); |
128 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); | |
129 | ||
130 | return IRQ_HANDLED; | |
131 | } | |
132 | ||
133 | static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc) | |
134 | { | |
135 | struct gpio_chip *chip = &cc->gpio; | |
136 | int gpio, hwirq, err; | |
137 | ||
138 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
139 | return 0; | |
140 | ||
141 | cc->irq_domain = irq_domain_add_linear(NULL, chip->ngpio, | |
142 | &irq_domain_simple_ops, cc); | |
143 | if (!cc->irq_domain) { | |
144 | err = -ENODEV; | |
145 | goto err_irq_domain; | |
146 | } | |
147 | for (gpio = 0; gpio < chip->ngpio; gpio++) { | |
148 | int irq = irq_create_mapping(cc->irq_domain, gpio); | |
149 | ||
150 | irq_set_chip_data(irq, cc); | |
151 | irq_set_chip_and_handler(irq, &bcma_gpio_irq_chip, | |
152 | handle_simple_irq); | |
153 | } | |
154 | ||
155 | hwirq = bcma_core_irq(cc->core); | |
156 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", | |
157 | cc); | |
158 | if (err) | |
159 | goto err_req_irq; | |
160 | ||
978e55d2 | 161 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
2997609e RM |
162 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
163 | ||
164 | return 0; | |
165 | ||
166 | err_req_irq: | |
167 | for (gpio = 0; gpio < chip->ngpio; gpio++) { | |
168 | int irq = irq_find_mapping(cc->irq_domain, gpio); | |
169 | ||
170 | irq_dispose_mapping(irq); | |
171 | } | |
172 | irq_domain_remove(cc->irq_domain); | |
173 | err_irq_domain: | |
174 | return err; | |
175 | } | |
176 | ||
177 | static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc) | |
178 | { | |
179 | struct gpio_chip *chip = &cc->gpio; | |
180 | int gpio; | |
181 | ||
182 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
183 | return; | |
184 | ||
185 | bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); | |
186 | free_irq(bcma_core_irq(cc->core), cc); | |
187 | for (gpio = 0; gpio < chip->ngpio; gpio++) { | |
188 | int irq = irq_find_mapping(cc->irq_domain, gpio); | |
189 | ||
190 | irq_dispose_mapping(irq); | |
191 | } | |
192 | irq_domain_remove(cc->irq_domain); | |
193 | } | |
194 | #else | |
195 | static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc) | |
196 | { | |
197 | return 0; | |
198 | } | |
199 | ||
200 | static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc) | |
201 | { | |
202 | } | |
203 | #endif | |
204 | ||
cf0936b0 HM |
205 | int bcma_gpio_init(struct bcma_drv_cc *cc) |
206 | { | |
207 | struct gpio_chip *chip = &cc->gpio; | |
2997609e | 208 | int err; |
cf0936b0 HM |
209 | |
210 | chip->label = "bcma_gpio"; | |
211 | chip->owner = THIS_MODULE; | |
212 | chip->request = bcma_gpio_request; | |
213 | chip->free = bcma_gpio_free; | |
214 | chip->get = bcma_gpio_get_value; | |
215 | chip->set = bcma_gpio_set_value; | |
216 | chip->direction_input = bcma_gpio_direction_input; | |
217 | chip->direction_output = bcma_gpio_direction_output; | |
2997609e | 218 | #if IS_BUILTIN(CONFIG_BCMA_HOST_SOC) |
8f1ca268 | 219 | chip->to_irq = bcma_gpio_to_irq; |
2997609e | 220 | #endif |
0f8ca014 RM |
221 | switch (cc->core->bus->chipinfo.id) { |
222 | case BCMA_CHIP_ID_BCM5357: | |
223 | chip->ngpio = 32; | |
224 | break; | |
225 | default: | |
226 | chip->ngpio = 16; | |
227 | } | |
228 | ||
cf0936b0 HM |
229 | /* There is just one SoC in one device and its GPIO addresses should be |
230 | * deterministic to address them more easily. The other buses could get | |
231 | * a random base number. */ | |
232 | if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
233 | chip->base = 0; | |
234 | else | |
235 | chip->base = -1; | |
236 | ||
2997609e RM |
237 | err = bcma_gpio_irq_domain_init(cc); |
238 | if (err) | |
239 | return err; | |
240 | ||
241 | err = gpiochip_add(chip); | |
242 | if (err) { | |
243 | bcma_gpio_irq_domain_exit(cc); | |
244 | return err; | |
245 | } | |
246 | ||
247 | return 0; | |
cf0936b0 | 248 | } |
c50ae947 HM |
249 | |
250 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) | |
251 | { | |
2997609e | 252 | bcma_gpio_irq_domain_exit(cc); |
c50ae947 HM |
253 | return gpiochip_remove(&cc->gpio); |
254 | } |