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 | 13 | #include <linux/export.h> |
f63bc788 AS |
14 | #include <linux/property.h> |
15 | ||
cf0936b0 HM |
16 | #include <linux/bcma/bcma.h> |
17 | ||
18 | #include "bcma_private.h" | |
19 | ||
057fcd42 RM |
20 | #define BCMA_GPIO_MAX_PINS 32 |
21 | ||
cf0936b0 HM |
22 | static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
23 | { | |
78d455a2 | 24 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
25 | |
26 | return !!bcma_chipco_gpio_in(cc, 1 << gpio); | |
27 | } | |
28 | ||
29 | static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio, | |
30 | int value) | |
31 | { | |
78d455a2 | 32 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
33 | |
34 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
35 | } | |
36 | ||
37 | static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | |
38 | { | |
78d455a2 | 39 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
40 | |
41 | bcma_chipco_gpio_outen(cc, 1 << gpio, 0); | |
42 | return 0; | |
43 | } | |
44 | ||
45 | static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, | |
46 | int value) | |
47 | { | |
78d455a2 | 48 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
49 | |
50 | bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio); | |
51 | bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); | |
52 | return 0; | |
53 | } | |
54 | ||
55 | static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio) | |
56 | { | |
78d455a2 | 57 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
58 | |
59 | bcma_chipco_gpio_control(cc, 1 << gpio, 0); | |
60 | /* clear pulldown */ | |
61 | bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0); | |
62 | /* Set pullup */ | |
63 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) | |
69 | { | |
78d455a2 | 70 | struct bcma_drv_cc *cc = gpiochip_get_data(chip); |
cf0936b0 HM |
71 | |
72 | /* clear pullup */ | |
73 | bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); | |
74 | } | |
75 | ||
0cbfc065 | 76 | #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X) |
8f1ca268 | 77 | |
2997609e RM |
78 | static void bcma_gpio_irq_unmask(struct irq_data *d) |
79 | { | |
74f4e0cc | 80 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
78d455a2 | 81 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
2997609e | 82 | int gpio = irqd_to_hwirq(d); |
978e55d2 | 83 | u32 val = bcma_chipco_gpio_in(cc, BIT(gpio)); |
2997609e | 84 | |
55549d6a | 85 | gpiochip_enable_irq(gc, gpio); |
978e55d2 | 86 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
2997609e RM |
87 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
88 | } | |
89 | ||
90 | static void bcma_gpio_irq_mask(struct irq_data *d) | |
91 | { | |
74f4e0cc | 92 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
78d455a2 | 93 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
2997609e RM |
94 | int gpio = irqd_to_hwirq(d); |
95 | ||
96 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); | |
55549d6a | 97 | gpiochip_disable_irq(gc, gpio); |
2997609e RM |
98 | } |
99 | ||
55549d6a | 100 | static const struct irq_chip bcma_gpio_irq_chip = { |
2997609e RM |
101 | .name = "BCMA-GPIO", |
102 | .irq_mask = bcma_gpio_irq_mask, | |
103 | .irq_unmask = bcma_gpio_irq_unmask, | |
55549d6a LW |
104 | .flags = IRQCHIP_IMMUTABLE, |
105 | GPIOCHIP_IRQ_RESOURCE_HELPERS, | |
2997609e RM |
106 | }; |
107 | ||
108 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) | |
109 | { | |
110 | struct bcma_drv_cc *cc = dev_id; | |
74f4e0cc | 111 | struct gpio_chip *gc = &cc->gpio; |
2997609e RM |
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); | |
d5ab2adb | 115 | unsigned long irqs = (val ^ pol) & mask; |
2997609e RM |
116 | int gpio; |
117 | ||
118 | if (!irqs) | |
119 | return IRQ_NONE; | |
120 | ||
74f4e0cc | 121 | for_each_set_bit(gpio, &irqs, gc->ngpio) |
94ec234a | 122 | generic_handle_domain_irq_safe(gc->irq.domain, gpio); |
2997609e RM |
123 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); |
124 | ||
125 | return IRQ_HANDLED; | |
126 | } | |
127 | ||
74f4e0cc | 128 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
129 | { |
130 | struct gpio_chip *chip = &cc->gpio; | |
a080ecb1 | 131 | struct gpio_irq_chip *girq = &chip->irq; |
74f4e0cc | 132 | int hwirq, err; |
2997609e RM |
133 | |
134 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
135 | return 0; | |
136 | ||
85eb92e8 | 137 | hwirq = bcma_core_irq(cc->core, 0); |
2997609e RM |
138 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", |
139 | cc); | |
140 | if (err) | |
74f4e0cc | 141 | return err; |
2997609e | 142 | |
978e55d2 | 143 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
2997609e RM |
144 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
145 | ||
55549d6a | 146 | gpio_irq_chip_set_chip(girq, &bcma_gpio_irq_chip); |
a080ecb1 LW |
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; | |
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; | |
5a1c18b7 | 190 | chip->parent = bus->dev; |
f63bc788 AS |
191 | chip->fwnode = dev_fwnode(&cc->core->dev); |
192 | ||
057fcd42 | 193 | switch (bus->chipinfo.id) { |
f022ea52 | 194 | case BCMA_CHIP_ID_BCM4707: |
0f8ca014 | 195 | case BCMA_CHIP_ID_BCM5357: |
f0db59e1 | 196 | case BCMA_CHIP_ID_BCM53572: |
459c3514 | 197 | case BCMA_CHIP_ID_BCM53573: |
61dba73c | 198 | case BCMA_CHIP_ID_BCM47094: |
0f8ca014 RM |
199 | chip->ngpio = 32; |
200 | break; | |
201 | default: | |
202 | chip->ngpio = 16; | |
203 | } | |
204 | ||
057fcd42 | 205 | /* |
2d57b712 FF |
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. | |
057fcd42 | 211 | */ |
2d57b712 FF |
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; | |
cf0936b0 | 217 | |
a080ecb1 | 218 | err = bcma_gpio_irq_init(cc); |
2997609e RM |
219 | if (err) |
220 | return err; | |
221 | ||
a080ecb1 | 222 | err = gpiochip_add_data(chip, cc); |
2997609e | 223 | if (err) { |
a080ecb1 | 224 | bcma_gpio_irq_exit(cc); |
2997609e RM |
225 | return err; |
226 | } | |
227 | ||
228 | return 0; | |
cf0936b0 | 229 | } |
c50ae947 HM |
230 | |
231 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) | |
232 | { | |
74f4e0cc | 233 | bcma_gpio_irq_exit(cc); |
88d5e520 | 234 | gpiochip_remove(&cc->gpio); |
235 | return 0; | |
c50ae947 | 236 | } |