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 | ||
74f4e0cc | 11 | #include <linux/gpio/driver.h> |
2997609e | 12 | #include <linux/interrupt.h> |
cf0936b0 HM |
13 | #include <linux/export.h> |
14 | #include <linux/bcma/bcma.h> | |
15 | ||
16 | #include "bcma_private.h" | |
17 | ||
057fcd42 RM |
18 | #define BCMA_GPIO_MAX_PINS 32 |
19 | ||
cf0936b0 HM |
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 | ||
0cbfc065 | 79 | #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X) |
8f1ca268 | 80 | |
2997609e RM |
81 | static void bcma_gpio_irq_unmask(struct irq_data *d) |
82 | { | |
74f4e0cc LW |
83 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
84 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(gc); | |
2997609e | 85 | int gpio = irqd_to_hwirq(d); |
978e55d2 | 86 | u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); |
2997609e | 87 | |
978e55d2 | 88 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
2997609e RM |
89 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
90 | } | |
91 | ||
92 | static void bcma_gpio_irq_mask(struct irq_data *d) | |
93 | { | |
74f4e0cc LW |
94 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
95 | struct bcma_drv_cc *cc = bcma_gpio_get_cc(gc); | |
2997609e RM |
96 | int gpio = irqd_to_hwirq(d); |
97 | ||
98 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); | |
99 | } | |
100 | ||
101 | static struct irq_chip bcma_gpio_irq_chip = { | |
102 | .name = "BCMA-GPIO", | |
103 | .irq_mask = bcma_gpio_irq_mask, | |
104 | .irq_unmask = bcma_gpio_irq_unmask, | |
105 | }; | |
106 | ||
107 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) | |
108 | { | |
109 | struct bcma_drv_cc *cc = dev_id; | |
74f4e0cc | 110 | struct gpio_chip *gc = &cc->gpio; |
2997609e RM |
111 | u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN); |
112 | u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ); | |
113 | u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL); | |
d5ab2adb | 114 | unsigned long irqs = (val ^ pol) & mask; |
2997609e RM |
115 | int gpio; |
116 | ||
117 | if (!irqs) | |
118 | return IRQ_NONE; | |
119 | ||
74f4e0cc LW |
120 | for_each_set_bit(gpio, &irqs, gc->ngpio) |
121 | generic_handle_irq(irq_find_mapping(gc->irqdomain, gpio)); | |
2997609e RM |
122 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); |
123 | ||
124 | return IRQ_HANDLED; | |
125 | } | |
126 | ||
74f4e0cc | 127 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
128 | { |
129 | struct gpio_chip *chip = &cc->gpio; | |
74f4e0cc | 130 | int hwirq, err; |
2997609e RM |
131 | |
132 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
133 | return 0; | |
134 | ||
85eb92e8 | 135 | hwirq = bcma_core_irq(cc->core, 0); |
2997609e RM |
136 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", |
137 | cc); | |
138 | if (err) | |
74f4e0cc | 139 | return err; |
2997609e | 140 | |
978e55d2 | 141 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
2997609e RM |
142 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
143 | ||
74f4e0cc LW |
144 | err = gpiochip_irqchip_add(chip, |
145 | &bcma_gpio_irq_chip, | |
146 | 0, | |
147 | handle_simple_irq, | |
148 | IRQ_TYPE_NONE); | |
149 | if (err) { | |
150 | free_irq(hwirq, cc); | |
151 | return err; | |
2997609e | 152 | } |
74f4e0cc LW |
153 | |
154 | return 0; | |
2997609e RM |
155 | } |
156 | ||
74f4e0cc | 157 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e | 158 | { |
2997609e RM |
159 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) |
160 | return; | |
161 | ||
162 | bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); | |
85eb92e8 | 163 | free_irq(bcma_core_irq(cc->core, 0), cc); |
2997609e RM |
164 | } |
165 | #else | |
74f4e0cc | 166 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
167 | { |
168 | return 0; | |
169 | } | |
170 | ||
74f4e0cc | 171 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e RM |
172 | { |
173 | } | |
174 | #endif | |
175 | ||
cf0936b0 HM |
176 | int bcma_gpio_init(struct bcma_drv_cc *cc) |
177 | { | |
057fcd42 | 178 | struct bcma_bus *bus = cc->core->bus; |
cf0936b0 | 179 | struct gpio_chip *chip = &cc->gpio; |
2997609e | 180 | int err; |
cf0936b0 HM |
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; | |
74f4e0cc LW |
190 | chip->owner = THIS_MODULE; |
191 | chip->dev = bcma_bus_get_host_dev(bus); | |
a0196d11 RM |
192 | #if IS_BUILTIN(CONFIG_OF) |
193 | if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
194 | chip->of_node = cc->core->dev.of_node; | |
2997609e | 195 | #endif |
057fcd42 | 196 | switch (bus->chipinfo.id) { |
f022ea52 | 197 | case BCMA_CHIP_ID_BCM4707: |
0f8ca014 | 198 | case BCMA_CHIP_ID_BCM5357: |
f0db59e1 | 199 | case BCMA_CHIP_ID_BCM53572: |
0f8ca014 RM |
200 | chip->ngpio = 32; |
201 | break; | |
202 | default: | |
203 | chip->ngpio = 16; | |
204 | } | |
205 | ||
057fcd42 | 206 | /* |
2d57b712 FF |
207 | * Register SoC GPIO devices with absolute GPIO pin base. |
208 | * On MIPS, we don't have Device Tree and we can't use relative (per chip) | |
209 | * GPIO numbers. | |
210 | * On some ARM devices, user space may want to access some system GPIO | |
211 | * pins directly, which is easier to do with a predictable GPIO base. | |
057fcd42 | 212 | */ |
2d57b712 FF |
213 | if (IS_BUILTIN(CONFIG_BCM47XX) || |
214 | cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
215 | chip->base = bus->num * BCMA_GPIO_MAX_PINS; | |
216 | else | |
217 | chip->base = -1; | |
cf0936b0 | 218 | |
74f4e0cc | 219 | err = gpiochip_add(chip); |
2997609e RM |
220 | if (err) |
221 | return err; | |
222 | ||
74f4e0cc | 223 | err = bcma_gpio_irq_init(cc); |
2997609e | 224 | if (err) { |
74f4e0cc | 225 | gpiochip_remove(chip); |
2997609e RM |
226 | return err; |
227 | } | |
228 | ||
229 | return 0; | |
cf0936b0 | 230 | } |
c50ae947 HM |
231 | |
232 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) | |
233 | { | |
74f4e0cc | 234 | bcma_gpio_irq_exit(cc); |
88d5e520 | 235 | gpiochip_remove(&cc->gpio); |
236 | return 0; | |
c50ae947 | 237 | } |