Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bcma: gpio: add own IRQ domain

Input GPIO changes can generate interrupts, but we need kind of ACK for
them by changing IRQ polarity. This is required to stop hardware from
keep generating interrupts and generate another one on the next GPIO
state change.
This code allows using GPIOs with standard interrupts and add for
example GPIO buttons support.

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Acked-by: Hauke Mehrtens <hauke@hauke-m.de>
Signed-off-by: John Crispin <blogic@openwrt.org>
Patchwork: http://patchwork.linux-mips.org/patch/6216/

authored by

Rafał Miłecki and committed by
Ralf Baechle
2997609e 87c99203

+134 -2
+1
drivers/bcma/Kconfig
··· 75 75 config BCMA_DRIVER_GPIO 76 76 bool "BCMA GPIO driver" 77 77 depends on BCMA && GPIOLIB 78 + select IRQ_DOMAIN if BCMA_HOST_SOC 78 79 help 79 80 Driver to provide access to the GPIO pins of the bcma bus. 80 81
+132 -2
drivers/bcma/driver_gpio.c
··· 9 9 */ 10 10 11 11 #include <linux/gpio.h> 12 + #include <linux/irq.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/irqdomain.h> 12 15 #include <linux/export.h> 13 16 #include <linux/bcma/bcma.h> 14 17 ··· 76 73 bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); 77 74 } 78 75 76 + #if IS_BUILTIN(CONFIG_BCMA_HOST_SOC) 79 77 static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) 80 78 { 81 79 struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); 82 80 83 81 if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) 84 - return bcma_core_irq(cc->core); 82 + return irq_find_mapping(cc->irq_domain, gpio); 85 83 else 86 84 return -EINVAL; 87 85 } 88 86 87 + static void bcma_gpio_irq_unmask(struct irq_data *d) 88 + { 89 + struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d); 90 + int gpio = irqd_to_hwirq(d); 91 + 92 + bcma_chipco_gpio_intmask(cc, BIT(gpio), BIT(gpio)); 93 + } 94 + 95 + static void bcma_gpio_irq_mask(struct irq_data *d) 96 + { 97 + struct bcma_drv_cc *cc = irq_data_get_irq_chip_data(d); 98 + int gpio = irqd_to_hwirq(d); 99 + 100 + bcma_chipco_gpio_intmask(cc, BIT(gpio), 0); 101 + } 102 + 103 + static struct irq_chip bcma_gpio_irq_chip = { 104 + .name = "BCMA-GPIO", 105 + .irq_mask = bcma_gpio_irq_mask, 106 + .irq_unmask = bcma_gpio_irq_unmask, 107 + }; 108 + 109 + static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) 110 + { 111 + struct bcma_drv_cc *cc = dev_id; 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); 115 + u32 irqs = (val ^ pol) & mask; 116 + int gpio; 117 + 118 + if (!irqs) 119 + return IRQ_NONE; 120 + 121 + for_each_set_bit(gpio, (unsigned long *)&irqs, cc->gpio.ngpio) 122 + generic_handle_irq(bcma_gpio_to_irq(&cc->gpio, gpio)); 123 + bcma_chipco_gpio_polarity(cc, irqs, val & irqs); 124 + 125 + return IRQ_HANDLED; 126 + } 127 + 128 + static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc) 129 + { 130 + struct gpio_chip *chip = &cc->gpio; 131 + int gpio, hwirq, err; 132 + 133 + if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) 134 + return 0; 135 + 136 + cc->irq_domain = irq_domain_add_linear(NULL, chip->ngpio, 137 + &irq_domain_simple_ops, cc); 138 + if (!cc->irq_domain) { 139 + err = -ENODEV; 140 + goto err_irq_domain; 141 + } 142 + for (gpio = 0; gpio < chip->ngpio; gpio++) { 143 + int irq = irq_create_mapping(cc->irq_domain, gpio); 144 + 145 + irq_set_chip_data(irq, cc); 146 + irq_set_chip_and_handler(irq, &bcma_gpio_irq_chip, 147 + handle_simple_irq); 148 + } 149 + 150 + hwirq = bcma_core_irq(cc->core); 151 + err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio", 152 + cc); 153 + if (err) 154 + goto err_req_irq; 155 + 156 + bcma_cc_set32(cc, BCMA_CC_IRQMASK, BCMA_CC_IRQ_GPIO); 157 + 158 + return 0; 159 + 160 + err_req_irq: 161 + for (gpio = 0; gpio < chip->ngpio; gpio++) { 162 + int irq = irq_find_mapping(cc->irq_domain, gpio); 163 + 164 + irq_dispose_mapping(irq); 165 + } 166 + irq_domain_remove(cc->irq_domain); 167 + err_irq_domain: 168 + return err; 169 + } 170 + 171 + static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc) 172 + { 173 + struct gpio_chip *chip = &cc->gpio; 174 + int gpio; 175 + 176 + if (cc->core->bus->hosttype != BCMA_HOSTTYPE_SOC) 177 + return; 178 + 179 + bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO); 180 + free_irq(bcma_core_irq(cc->core), cc); 181 + for (gpio = 0; gpio < chip->ngpio; gpio++) { 182 + int irq = irq_find_mapping(cc->irq_domain, gpio); 183 + 184 + irq_dispose_mapping(irq); 185 + } 186 + irq_domain_remove(cc->irq_domain); 187 + } 188 + #else 189 + static int bcma_gpio_irq_domain_init(struct bcma_drv_cc *cc) 190 + { 191 + return 0; 192 + } 193 + 194 + static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc) 195 + { 196 + } 197 + #endif 198 + 89 199 int bcma_gpio_init(struct bcma_drv_cc *cc) 90 200 { 91 201 struct gpio_chip *chip = &cc->gpio; 202 + int err; 92 203 93 204 chip->label = "bcma_gpio"; 94 205 chip->owner = THIS_MODULE; ··· 212 95 chip->set = bcma_gpio_set_value; 213 96 chip->direction_input = bcma_gpio_direction_input; 214 97 chip->direction_output = bcma_gpio_direction_output; 98 + #if IS_BUILTIN(CONFIG_BCMA_HOST_SOC) 215 99 chip->to_irq = bcma_gpio_to_irq; 100 + #endif 216 101 chip->ngpio = 16; 217 102 /* There is just one SoC in one device and its GPIO addresses should be 218 103 * deterministic to address them more easily. The other buses could get ··· 224 105 else 225 106 chip->base = -1; 226 107 227 - return gpiochip_add(chip); 108 + err = bcma_gpio_irq_domain_init(cc); 109 + if (err) 110 + return err; 111 + 112 + err = gpiochip_add(chip); 113 + if (err) { 114 + bcma_gpio_irq_domain_exit(cc); 115 + return err; 116 + } 117 + 118 + return 0; 228 119 } 229 120 230 121 int bcma_gpio_unregister(struct bcma_drv_cc *cc) 231 122 { 123 + bcma_gpio_irq_domain_exit(cc); 232 124 return gpiochip_remove(&cc->gpio); 233 125 }
+1
include/linux/bcma/bcma_driver_chipcommon.h
··· 640 640 spinlock_t gpio_lock; 641 641 #ifdef CONFIG_BCMA_DRIVER_GPIO 642 642 struct gpio_chip gpio; 643 + struct irq_domain *irq_domain; 643 644 #endif 644 645 }; 645 646