2 * Broadcom specific AMBA
5 * Copyright 2011, Broadcom Corporation
6 * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
8 * Licensed under the GNU/GPL. See COPYING for details.
11 #include <linux/gpio.h>
12 #include <linux/irq.h>
13 #include <linux/interrupt.h>
14 #include <linux/irqdomain.h>
15 #include <linux/export.h>
16 #include <linux/bcma/bcma.h>
18 #include "bcma_private.h"
20 #define BCMA_GPIO_MAX_PINS 32
22 static inline struct bcma_drv_cc
*bcma_gpio_get_cc(struct gpio_chip
*chip
)
24 return container_of(chip
, struct bcma_drv_cc
, gpio
);
27 static int bcma_gpio_get_value(struct gpio_chip
*chip
, unsigned gpio
)
29 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
31 return !!bcma_chipco_gpio_in(cc
, 1 << gpio
);
34 static void bcma_gpio_set_value(struct gpio_chip
*chip
, unsigned gpio
,
37 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
39 bcma_chipco_gpio_out(cc
, 1 << gpio
, value
? 1 << gpio
: 0);
42 static int bcma_gpio_direction_input(struct gpio_chip
*chip
, unsigned gpio
)
44 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
46 bcma_chipco_gpio_outen(cc
, 1 << gpio
, 0);
50 static int bcma_gpio_direction_output(struct gpio_chip
*chip
, unsigned gpio
,
53 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
55 bcma_chipco_gpio_outen(cc
, 1 << gpio
, 1 << gpio
);
56 bcma_chipco_gpio_out(cc
, 1 << gpio
, value
? 1 << gpio
: 0);
60 static int bcma_gpio_request(struct gpio_chip
*chip
, unsigned gpio
)
62 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
64 bcma_chipco_gpio_control(cc
, 1 << gpio
, 0);
66 bcma_chipco_gpio_pulldown(cc
, 1 << gpio
, 0);
68 bcma_chipco_gpio_pullup(cc
, 1 << gpio
, 1 << gpio
);
73 static void bcma_gpio_free(struct gpio_chip
*chip
, unsigned gpio
)
75 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
78 bcma_chipco_gpio_pullup(cc
, 1 << gpio
, 0);
81 #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
82 static int bcma_gpio_to_irq(struct gpio_chip
*chip
, unsigned gpio
)
84 struct bcma_drv_cc
*cc
= bcma_gpio_get_cc(chip
);
86 if (cc
->core
->bus
->hosttype
== BCMA_HOSTTYPE_SOC
)
87 return irq_find_mapping(cc
->irq_domain
, gpio
);
92 static void bcma_gpio_irq_unmask(struct irq_data
*d
)
94 struct bcma_drv_cc
*cc
= irq_data_get_irq_chip_data(d
);
95 int gpio
= irqd_to_hwirq(d
);
96 u32 val
= bcma_chipco_gpio_in(cc
, BIT(gpio
));
98 bcma_chipco_gpio_polarity(cc
, BIT(gpio
), val
);
99 bcma_chipco_gpio_intmask(cc
, BIT(gpio
), BIT(gpio
));
102 static void bcma_gpio_irq_mask(struct irq_data
*d
)
104 struct bcma_drv_cc
*cc
= irq_data_get_irq_chip_data(d
);
105 int gpio
= irqd_to_hwirq(d
);
107 bcma_chipco_gpio_intmask(cc
, BIT(gpio
), 0);
110 static struct irq_chip bcma_gpio_irq_chip
= {
112 .irq_mask
= bcma_gpio_irq_mask
,
113 .irq_unmask
= bcma_gpio_irq_unmask
,
116 static irqreturn_t
bcma_gpio_irq_handler(int irq
, void *dev_id
)
118 struct bcma_drv_cc
*cc
= dev_id
;
119 u32 val
= bcma_cc_read32(cc
, BCMA_CC_GPIOIN
);
120 u32 mask
= bcma_cc_read32(cc
, BCMA_CC_GPIOIRQ
);
121 u32 pol
= bcma_cc_read32(cc
, BCMA_CC_GPIOPOL
);
122 unsigned long irqs
= (val
^ pol
) & mask
;
128 for_each_set_bit(gpio
, &irqs
, cc
->gpio
.ngpio
)
129 generic_handle_irq(bcma_gpio_to_irq(&cc
->gpio
, gpio
));
130 bcma_chipco_gpio_polarity(cc
, irqs
, val
& irqs
);
135 static int bcma_gpio_irq_domain_init(struct bcma_drv_cc
*cc
)
137 struct gpio_chip
*chip
= &cc
->gpio
;
138 int gpio
, hwirq
, err
;
140 if (cc
->core
->bus
->hosttype
!= BCMA_HOSTTYPE_SOC
)
143 cc
->irq_domain
= irq_domain_add_linear(NULL
, chip
->ngpio
,
144 &irq_domain_simple_ops
, cc
);
145 if (!cc
->irq_domain
) {
149 for (gpio
= 0; gpio
< chip
->ngpio
; gpio
++) {
150 int irq
= irq_create_mapping(cc
->irq_domain
, gpio
);
152 irq_set_chip_data(irq
, cc
);
153 irq_set_chip_and_handler(irq
, &bcma_gpio_irq_chip
,
157 hwirq
= bcma_core_irq(cc
->core
, 0);
158 err
= request_irq(hwirq
, bcma_gpio_irq_handler
, IRQF_SHARED
, "gpio",
163 bcma_chipco_gpio_intmask(cc
, ~0, 0);
164 bcma_cc_set32(cc
, BCMA_CC_IRQMASK
, BCMA_CC_IRQ_GPIO
);
169 for (gpio
= 0; gpio
< chip
->ngpio
; gpio
++) {
170 int irq
= irq_find_mapping(cc
->irq_domain
, gpio
);
172 irq_dispose_mapping(irq
);
174 irq_domain_remove(cc
->irq_domain
);
179 static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc
*cc
)
181 struct gpio_chip
*chip
= &cc
->gpio
;
184 if (cc
->core
->bus
->hosttype
!= BCMA_HOSTTYPE_SOC
)
187 bcma_cc_mask32(cc
, BCMA_CC_IRQMASK
, ~BCMA_CC_IRQ_GPIO
);
188 free_irq(bcma_core_irq(cc
->core
, 0), cc
);
189 for (gpio
= 0; gpio
< chip
->ngpio
; gpio
++) {
190 int irq
= irq_find_mapping(cc
->irq_domain
, gpio
);
192 irq_dispose_mapping(irq
);
194 irq_domain_remove(cc
->irq_domain
);
197 static int bcma_gpio_irq_domain_init(struct bcma_drv_cc
*cc
)
202 static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc
*cc
)
207 int bcma_gpio_init(struct bcma_drv_cc
*cc
)
209 struct bcma_bus
*bus
= cc
->core
->bus
;
210 struct gpio_chip
*chip
= &cc
->gpio
;
213 chip
->label
= "bcma_gpio";
214 chip
->owner
= THIS_MODULE
;
215 chip
->request
= bcma_gpio_request
;
216 chip
->free
= bcma_gpio_free
;
217 chip
->get
= bcma_gpio_get_value
;
218 chip
->set
= bcma_gpio_set_value
;
219 chip
->direction_input
= bcma_gpio_direction_input
;
220 chip
->direction_output
= bcma_gpio_direction_output
;
221 #if IS_BUILTIN(CONFIG_BCM47XX) || IS_BUILTIN(CONFIG_ARCH_BCM_5301X)
222 chip
->to_irq
= bcma_gpio_to_irq
;
224 #if IS_BUILTIN(CONFIG_OF)
225 if (cc
->core
->bus
->hosttype
== BCMA_HOSTTYPE_SOC
)
226 chip
->of_node
= cc
->core
->dev
.of_node
;
228 switch (bus
->chipinfo
.id
) {
229 case BCMA_CHIP_ID_BCM5357
:
230 case BCMA_CHIP_ID_BCM53572
:
238 * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO
239 * pin numbers. We don't have Device Tree there and we can't really use
240 * relative (per chip) numbers.
241 * So let's use predictable base for BCM47XX and "random" for all other.
243 #if IS_BUILTIN(CONFIG_BCM47XX)
244 chip
->base
= bus
->num
* BCMA_GPIO_MAX_PINS
;
249 err
= bcma_gpio_irq_domain_init(cc
);
253 err
= gpiochip_add(chip
);
255 bcma_gpio_irq_domain_exit(cc
);
262 int bcma_gpio_unregister(struct bcma_drv_cc
*cc
)
264 bcma_gpio_irq_domain_exit(cc
);
265 gpiochip_remove(&cc
->gpio
);