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 | |
978e55d2 | 85 | bcma_chipco_gpio_polarity(cc, BIT(gpio), val); |
2997609e RM |
86 | bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); |
87 | } | |
88 | ||
89 | static void bcma_gpio_irq_mask(struct irq_data *d) | |
90 | { | |
74f4e0cc | 91 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
78d455a2 | 92 | struct bcma_drv_cc *cc = gpiochip_get_data(gc); |
2997609e RM |
93 | int gpio = irqd_to_hwirq(d); |
94 | ||
95 | bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); | |
96 | } | |
97 | ||
98 | static struct irq_chip bcma_gpio_irq_chip = { | |
99 | .name = "BCMA-GPIO", | |
100 | .irq_mask = bcma_gpio_irq_mask, | |
101 | .irq_unmask = bcma_gpio_irq_unmask, | |
102 | }; | |
103 | ||
104 | static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) | |
105 | { | |
106 | struct bcma_drv_cc *cc = dev_id; | |
74f4e0cc | 107 | struct gpio_chip *gc = &cc->gpio; |
2997609e RM |
108 | u32 val = bcma_cc_read32(cc, BCMA_CC_GPIOIN); |
109 | u32 mask = bcma_cc_read32(cc, BCMA_CC_GPIOIRQ); | |
110 | u32 pol = bcma_cc_read32(cc, BCMA_CC_GPIOPOL); | |
d5ab2adb | 111 | unsigned long irqs = (val ^ pol) & mask; |
2997609e RM |
112 | int gpio; |
113 | ||
114 | if (!irqs) | |
115 | return IRQ_NONE; | |
116 | ||
74f4e0cc | 117 | for_each_set_bit(gpio, &irqs, gc->ngpio) |
94ec234a | 118 | generic_handle_domain_irq_safe(gc->irq.domain, gpio); |
2997609e RM |
119 | bcma_chipco_gpio_polarity(cc, irqs, val & irqs); |
120 | ||
121 | return IRQ_HANDLED; | |
122 | } | |
123 | ||
74f4e0cc | 124 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
125 | { |
126 | struct gpio_chip *chip = &cc->gpio; | |
a080ecb1 | 127 | struct gpio_irq_chip *girq = &chip->irq; |
74f4e0cc | 128 | int hwirq, err; |
2997609e RM |
129 | |
130 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) | |
131 | return 0; | |
132 | ||
85eb92e8 | 133 | hwirq = bcma_core_irq(cc->core, 0); |
2997609e RM |
134 | err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", |
135 | cc); | |
136 | if (err) | |
74f4e0cc | 137 | return err; |
2997609e | 138 | |
978e55d2 | 139 | bcma_chipco_gpio_intmask(cc, ~0, 0); |
2997609e RM |
140 | bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); |
141 | ||
a080ecb1 LW |
142 | girq->chip = &bcma_gpio_irq_chip; |
143 | /* This will let us handle the parent IRQ in the driver */ | |
144 | girq->parent_handler = NULL; | |
145 | girq->num_parents = 0; | |
146 | girq->parents = NULL; | |
147 | girq->default_type = IRQ_TYPE_NONE; | |
148 | girq->handler = handle_simple_irq; | |
74f4e0cc LW |
149 | |
150 | return 0; | |
2997609e RM |
151 | } |
152 | ||
74f4e0cc | 153 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e | 154 | { |
2997609e RM |
155 | if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) |
156 | return; | |
157 | ||
158 | bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); | |
85eb92e8 | 159 | free_irq(bcma_core_irq(cc->core, 0), cc); |
2997609e RM |
160 | } |
161 | #else | |
74f4e0cc | 162 | static int bcma_gpio_irq_init(struct bcma_drv_cc *cc) |
2997609e RM |
163 | { |
164 | return 0; | |
165 | } | |
166 | ||
74f4e0cc | 167 | static void bcma_gpio_irq_exit(struct bcma_drv_cc *cc) |
2997609e RM |
168 | { |
169 | } | |
170 | #endif | |
171 | ||
cf0936b0 HM |
172 | int bcma_gpio_init(struct bcma_drv_cc *cc) |
173 | { | |
057fcd42 | 174 | struct bcma_bus *bus = cc->core->bus; |
cf0936b0 | 175 | struct gpio_chip *chip = &cc->gpio; |
2997609e | 176 | int err; |
cf0936b0 HM |
177 | |
178 | chip->label = "bcma_gpio"; | |
179 | chip->owner = THIS_MODULE; | |
180 | chip->request = bcma_gpio_request; | |
181 | chip->free = bcma_gpio_free; | |
182 | chip->get = bcma_gpio_get_value; | |
183 | chip->set = bcma_gpio_set_value; | |
184 | chip->direction_input = bcma_gpio_direction_input; | |
185 | chip->direction_output = bcma_gpio_direction_output; | |
5a1c18b7 | 186 | chip->parent = bus->dev; |
f63bc788 AS |
187 | chip->fwnode = dev_fwnode(&cc->core->dev); |
188 | ||
057fcd42 | 189 | switch (bus->chipinfo.id) { |
f022ea52 | 190 | case BCMA_CHIP_ID_BCM4707: |
0f8ca014 | 191 | case BCMA_CHIP_ID_BCM5357: |
f0db59e1 | 192 | case BCMA_CHIP_ID_BCM53572: |
459c3514 | 193 | case BCMA_CHIP_ID_BCM53573: |
61dba73c | 194 | case BCMA_CHIP_ID_BCM47094: |
0f8ca014 RM |
195 | chip->ngpio = 32; |
196 | break; | |
197 | default: | |
198 | chip->ngpio = 16; | |
199 | } | |
200 | ||
057fcd42 | 201 | /* |
2d57b712 FF |
202 | * Register SoC GPIO devices with absolute GPIO pin base. |
203 | * On MIPS, we don't have Device Tree and we can't use relative (per chip) | |
204 | * GPIO numbers. | |
205 | * On some ARM devices, user space may want to access some system GPIO | |
206 | * pins directly, which is easier to do with a predictable GPIO base. | |
057fcd42 | 207 | */ |
2d57b712 FF |
208 | if (IS_BUILTIN(CONFIG_BCM47XX) || |
209 | cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) | |
210 | chip->base = bus->num * BCMA_GPIO_MAX_PINS; | |
211 | else | |
212 | chip->base = -1; | |
cf0936b0 | 213 | |
a080ecb1 | 214 | err = bcma_gpio_irq_init(cc); |
2997609e RM |
215 | if (err) |
216 | return err; | |
217 | ||
a080ecb1 | 218 | err = gpiochip_add_data(chip, cc); |
2997609e | 219 | if (err) { |
a080ecb1 | 220 | bcma_gpio_irq_exit(cc); |
2997609e RM |
221 | return err; |
222 | } | |
223 | ||
224 | return 0; | |
cf0936b0 | 225 | } |
c50ae947 HM |
226 | |
227 | int bcma_gpio_unregister(struct bcma_drv_cc *cc) | |
228 | { | |
74f4e0cc | 229 | bcma_gpio_irq_exit(cc); |
88d5e520 | 230 | gpiochip_remove(&cc->gpio); |
231 | return 0; | |
c50ae947 | 232 | } |