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 int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
21 | { | |
78d455a2 | 22 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
23 | |
24 | return !!bcma_chipco_gpio_in(cc, 1 << gpio); | |
25 | } | |
26 | ||
27 | static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio, | |
28 | int value) | |
29 | { | |
78d455a2 | 30 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
31 | |
32 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
33 | } | |
34 | ||
35 | static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | |
36 | { | |
78d455a2 | 37 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
38 | |
39 | bcma_chipco_gpio_outen(cc, 1 << gpio, 0); | |
40 | return 0; | |
41 | } | |
42 | ||
43 | static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, | |
44 | int value) | |
45 | { | |
78d455a2 | 46 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
47 | |
48 | bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio); | |
49 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
50 | return 0; | |
51 | } | |
52 | ||
53 | static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio) | |
54 | { | |
78d455a2 | 55 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
56 | |
57 | bcma_chipco_gpio_control(cc, 1 << gpio, 0); | |
58 | /* clear pulldown */ | |
59 | bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0); | |
60 | /* Set pullup */ | |
61 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio); | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) | |
67 | { | |
78d455a2 | 68 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
69 | |
70 | /* clear pullup */ | |
71 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); | |
72 | } | |
73 | ||
0cbfc065 | 74 | #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X) |
8f1ca268 | 75 | |
2997609e RM |
76 | static void bcma_gpio_irq_unmask(struct irq_data *d) |
77 | { | |
74f4e0cc | 78 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
78d455a2 | 79 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
2997609e | 80 | int gpio = irqd_to_hwirq(d); |
978e55d2 | 81 | u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); |
2997609e | 82 | |
978e55d2 | 83 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
2997609e RM |
84 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
85 | } | |
86 | ||
87 | static void bcma_gpio_irq_mask(struct irq_data *d) | |
88 | { | |
74f4e0cc | 89 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
78d455a2 | 90 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
2997609e RM |
91 | int gpio = irqd_to_hwirq(d); |
92 | ||
93 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); | |
94 | } | |
95 | ||
96 | static struct irq_chip bcma_gpio_irq_chip = { | |
97 | .name = "BCMA-GPIO", | |
98 | .irq_mask = bcma_gpio_irq_mask, | |
99 | .irq_unmask = bcma_gpio_irq_unmask, | |
100 | }; | |
101 | ||
102 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) | |
103 | { | |
104 | struct bcma_drv_cc *cc = dev_id; | |
74f4e0cc | 105 | struct gpio_chip *gc = &cc->gpio; |
2997609e RM |
106 | u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN); |
107 | u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ); | |
108 | u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL); | |
d5ab2adb | 109 | unsigned long irqs = (val ^ pol) & mask; |
2997609e RM |
110 | int gpio; |
111 | ||
112 | if (!irqs) | |
113 | return IRQ_NONE; | |
114 | ||
74f4e0cc LW |
115 | for_each_set_bit(gpio, &irqs, gc->ngpio) |
116 | generic_handle_irq(irq_find_mapping(gc->irqdomain, gpio)); | |
2997609e RM |
117 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); |
118 | ||
119 | return IRQ_HANDLED; | |
120 | } | |
121 | ||
74f4e0cc | 122 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
123 | { |
124 | struct gpio_chip *chip = &cc->gpio; | |
74f4e0cc | 125 | int hwirq, err; |
2997609e RM |
126 | |
127 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
128 | return 0; | |
129 | ||
85eb92e8 | 130 | hwirq = bcma_core_irq(cc->core, 0); |
2997609e RM |
131 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", |
132 | cc); | |
133 | if (err) | |
74f4e0cc | 134 | return err; |
2997609e | 135 | |
978e55d2 | 136 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
2997609e RM |
137 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
138 | ||
74f4e0cc LW |
139 | err = gpiochip_irqchip_add(chip, |
140 | &bcma_gpio_irq_chip, | |
141 | 0, | |
142 | handle_simple_irq, | |
143 | IRQ_TYPE_NONE); | |
144 | if (err) { | |
145 | free_irq(hwirq, cc); | |
146 | return err; | |
2997609e | 147 | } |
74f4e0cc LW |
148 | |
149 | return 0; | |
2997609e RM |
150 | } |
151 | ||
74f4e0cc | 152 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e | 153 | { |
2997609e RM |
154 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) |
155 | return; | |
156 | ||
157 | bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); | |
85eb92e8 | 158 | free_irq(bcma_core_irq(cc->core, 0), cc); |
2997609e RM |
159 | } |
160 | #else | |
74f4e0cc | 161 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
162 | { |
163 | return 0; | |
164 | } | |
165 | ||
74f4e0cc | 166 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e RM |
167 | { |
168 | } | |
169 | #endif | |
170 | ||
cf0936b0 HM |
171 | int bcma_gpio_init(struct bcma_drv_cc *cc) |
172 | { | |
057fcd42 | 173 | struct bcma_bus *bus = cc->core->bus; |
cf0936b0 | 174 | struct gpio_chip *chip = &cc->gpio; |
2997609e | 175 | int err; |
cf0936b0 HM |
176 | |
177 | chip->label = "bcma_gpio"; | |
178 | chip->owner = THIS_MODULE; | |
179 | chip->request = bcma_gpio_request; | |
180 | chip->free = bcma_gpio_free; | |
181 | chip->get = bcma_gpio_get_value; | |
182 | chip->set = bcma_gpio_set_value; | |
183 | chip->direction_input = bcma_gpio_direction_input; | |
184 | chip->direction_output = bcma_gpio_direction_output; | |
74f4e0cc | 185 | chip->owner = THIS_MODULE; |
58383c78 | 186 | chip->parent = bcma_bus_get_host_dev(bus); |
a0196d11 RM |
187 | #if IS_BUILTIN(CONFIG_OF) |
188 | if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
189 | chip->of_node = cc->core->dev.of_node; | |
2997609e | 190 | #endif |
057fcd42 | 191 | switch (bus->chipinfo.id) { |
f022ea52 | 192 | case BCMA_CHIP_ID_BCM4707: |
0f8ca014 | 193 | case BCMA_CHIP_ID_BCM5357: |
f0db59e1 | 194 | case BCMA_CHIP_ID_BCM53572: |
61dba73c | 195 | case BCMA_CHIP_ID_BCM47094: |
0f8ca014 RM |
196 | chip->ngpio = 32; |
197 | break; | |
198 | default: | |
199 | chip->ngpio = 16; | |
200 | } | |
201 | ||
057fcd42 | 202 | /* |
2d57b712 FF |
203 | * Register SoC GPIO devices with absolute GPIO pin base. |
204 | * On MIPS, we don't have Device Tree and we can't use relative (per chip) | |
205 | * GPIO numbers. | |
206 | * On some ARM devices, user space may want to access some system GPIO | |
207 | * pins directly, which is easier to do with a predictable GPIO base. | |
057fcd42 | 208 | */ |
2d57b712 FF |
209 | if (IS_BUILTIN(CONFIG_BCM47XX) || |
210 | cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
211 | chip->base = bus->num * BCMA_GPIO_MAX_PINS; | |
212 | else | |
213 | chip->base = -1; | |
cf0936b0 | 214 | |
78d455a2 | 215 | err = gpiochip_add_data(chip, cc); |
2997609e RM |
216 | if (err) |
217 | return err; | |
218 | ||
74f4e0cc | 219 | err = bcma_gpio_irq_init(cc); |
2997609e | 220 | if (err) { |
74f4e0cc | 221 | gpiochip_remove(chip); |
2997609e RM |
222 | return err; |
223 | } | |
224 | ||
225 | return 0; | |
cf0936b0 | 226 | } |
c50ae947 HM |
227 | |
228 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) | |
229 | { | |
74f4e0cc | 230 | bcma_gpio_irq_exit(cc); |
88d5e520 | 231 | gpiochip_remove(&cc->gpio); |
232 | return 0; | |
c50ae947 | 233 | } |