Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/arm/mach-sa1100/gpio.c |
| 3 | * |
| 4 | * Generic SA-1100 GPIO handling |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2 as |
| 8 | * published by the Free Software Foundation. |
| 9 | */ |
Russell King | 2f8163b | 2011-07-26 10:53:52 +0100 | [diff] [blame] | 10 | #include <linux/gpio.h> |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 11 | #include <linux/init.h> |
| 12 | #include <linux/module.h> |
Linus Walleij | 40ca061 | 2013-09-25 13:33:55 +0100 | [diff] [blame] | 13 | #include <linux/io.h> |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 14 | #include <linux/syscore_ops.h> |
Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 15 | #include <mach/hardware.h> |
Rob Herring | f314f33 | 2012-02-24 00:06:51 +0100 | [diff] [blame] | 16 | #include <mach/irqs.h> |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 17 | |
| 18 | static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) |
| 19 | { |
| 20 | return GPLR & GPIO_GPIO(offset); |
| 21 | } |
| 22 | |
| 23 | static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) |
| 24 | { |
| 25 | if (value) |
| 26 | GPSR = GPIO_GPIO(offset); |
| 27 | else |
| 28 | GPCR = GPIO_GPIO(offset); |
| 29 | } |
| 30 | |
| 31 | static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset) |
| 32 | { |
| 33 | unsigned long flags; |
| 34 | |
| 35 | local_irq_save(flags); |
| 36 | GPDR &= ~GPIO_GPIO(offset); |
| 37 | local_irq_restore(flags); |
| 38 | return 0; |
| 39 | } |
| 40 | |
| 41 | static int sa1100_direction_output(struct gpio_chip *chip, unsigned offset, int value) |
| 42 | { |
| 43 | unsigned long flags; |
| 44 | |
| 45 | local_irq_save(flags); |
| 46 | sa1100_gpio_set(chip, offset, value); |
| 47 | GPDR |= GPIO_GPIO(offset); |
| 48 | local_irq_restore(flags); |
| 49 | return 0; |
| 50 | } |
| 51 | |
Russell King | f408c98 | 2011-12-18 18:24:57 +0000 | [diff] [blame] | 52 | static int sa1100_to_irq(struct gpio_chip *chip, unsigned offset) |
| 53 | { |
Dmitry Eremin-Solenikov | 8350809 | 2015-01-15 02:29:16 +0100 | [diff] [blame] | 54 | return IRQ_GPIO0 + offset; |
Russell King | f408c98 | 2011-12-18 18:24:57 +0000 | [diff] [blame] | 55 | } |
| 56 | |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 57 | static struct gpio_chip sa1100_gpio_chip = { |
| 58 | .label = "gpio", |
| 59 | .direction_input = sa1100_direction_input, |
| 60 | .direction_output = sa1100_direction_output, |
| 61 | .set = sa1100_gpio_set, |
| 62 | .get = sa1100_gpio_get, |
Russell King | f408c98 | 2011-12-18 18:24:57 +0000 | [diff] [blame] | 63 | .to_irq = sa1100_to_irq, |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 64 | .base = 0, |
| 65 | .ngpio = GPIO_MAX + 1, |
| 66 | }; |
| 67 | |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 68 | /* |
| 69 | * SA1100 GPIO edge detection for IRQs: |
| 70 | * IRQs are generated on Falling-Edge, Rising-Edge, or both. |
| 71 | * Use this instead of directly setting GRER/GFER. |
| 72 | */ |
| 73 | static int GPIO_IRQ_rising_edge; |
| 74 | static int GPIO_IRQ_falling_edge; |
| 75 | static int GPIO_IRQ_mask; |
| 76 | |
| 77 | static int sa1100_gpio_type(struct irq_data *d, unsigned int type) |
| 78 | { |
| 79 | unsigned int mask; |
| 80 | |
| 81 | mask = BIT(d->hwirq); |
| 82 | |
| 83 | if (type == IRQ_TYPE_PROBE) { |
| 84 | if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask) |
| 85 | return 0; |
| 86 | type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; |
| 87 | } |
| 88 | |
| 89 | if (type & IRQ_TYPE_EDGE_RISING) |
| 90 | GPIO_IRQ_rising_edge |= mask; |
| 91 | else |
| 92 | GPIO_IRQ_rising_edge &= ~mask; |
| 93 | if (type & IRQ_TYPE_EDGE_FALLING) |
| 94 | GPIO_IRQ_falling_edge |= mask; |
| 95 | else |
| 96 | GPIO_IRQ_falling_edge &= ~mask; |
| 97 | |
| 98 | GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; |
| 99 | GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; |
| 100 | |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | /* |
| 105 | * GPIO IRQs must be acknowledged. |
| 106 | */ |
| 107 | static void sa1100_gpio_ack(struct irq_data *d) |
| 108 | { |
| 109 | GEDR = BIT(d->hwirq); |
| 110 | } |
| 111 | |
| 112 | static void sa1100_gpio_mask(struct irq_data *d) |
| 113 | { |
| 114 | unsigned int mask = BIT(d->hwirq); |
| 115 | |
| 116 | GPIO_IRQ_mask &= ~mask; |
| 117 | |
| 118 | GRER &= ~mask; |
| 119 | GFER &= ~mask; |
| 120 | } |
| 121 | |
| 122 | static void sa1100_gpio_unmask(struct irq_data *d) |
| 123 | { |
| 124 | unsigned int mask = BIT(d->hwirq); |
| 125 | |
| 126 | GPIO_IRQ_mask |= mask; |
| 127 | |
| 128 | GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; |
| 129 | GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; |
| 130 | } |
| 131 | |
| 132 | static int sa1100_gpio_wake(struct irq_data *d, unsigned int on) |
| 133 | { |
| 134 | if (on) |
| 135 | PWER |= BIT(d->hwirq); |
| 136 | else |
| 137 | PWER &= ~BIT(d->hwirq); |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | * This is for GPIO IRQs |
| 143 | */ |
| 144 | static struct irq_chip sa1100_gpio_irq_chip = { |
| 145 | .name = "GPIO", |
| 146 | .irq_ack = sa1100_gpio_ack, |
| 147 | .irq_mask = sa1100_gpio_mask, |
| 148 | .irq_unmask = sa1100_gpio_unmask, |
| 149 | .irq_set_type = sa1100_gpio_type, |
| 150 | .irq_set_wake = sa1100_gpio_wake, |
| 151 | }; |
| 152 | |
| 153 | static int sa1100_gpio_irqdomain_map(struct irq_domain *d, |
| 154 | unsigned int irq, irq_hw_number_t hwirq) |
| 155 | { |
| 156 | irq_set_chip_and_handler(irq, &sa1100_gpio_irq_chip, |
| 157 | handle_edge_irq); |
Rob Herring | 23393d4 | 2015-07-27 15:55:16 -0500 | [diff] [blame] | 158 | irq_set_noprobe(irq); |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 159 | |
| 160 | return 0; |
| 161 | } |
| 162 | |
Krzysztof Kozlowski | 0b354dc | 2015-04-27 21:54:07 +0900 | [diff] [blame] | 163 | static const struct irq_domain_ops sa1100_gpio_irqdomain_ops = { |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 164 | .map = sa1100_gpio_irqdomain_map, |
| 165 | .xlate = irq_domain_xlate_onetwocell, |
| 166 | }; |
| 167 | |
| 168 | static struct irq_domain *sa1100_gpio_irqdomain; |
| 169 | |
| 170 | /* |
| 171 | * IRQ 0-11 (GPIO) handler. We enter here with the |
| 172 | * irq_controller_lock held, and IRQs disabled. Decode the IRQ |
| 173 | * and call the handler. |
| 174 | */ |
Thomas Gleixner | bd0b9ac | 2015-09-14 10:42:37 +0200 | [diff] [blame] | 175 | static void sa1100_gpio_handler(struct irq_desc *desc) |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 176 | { |
Thomas Gleixner | 2951a79 | 2015-07-13 00:11:27 +0200 | [diff] [blame] | 177 | unsigned int irq, mask; |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 178 | |
| 179 | mask = GEDR; |
| 180 | do { |
| 181 | /* |
| 182 | * clear down all currently active IRQ sources. |
| 183 | * We will be processing them all. |
| 184 | */ |
| 185 | GEDR = mask; |
| 186 | |
| 187 | irq = IRQ_GPIO0; |
| 188 | do { |
| 189 | if (mask & 1) |
| 190 | generic_handle_irq(irq); |
| 191 | mask >>= 1; |
| 192 | irq++; |
| 193 | } while (mask); |
| 194 | |
| 195 | mask = GEDR; |
| 196 | } while (mask); |
| 197 | } |
| 198 | |
| 199 | static int sa1100_gpio_suspend(void) |
| 200 | { |
| 201 | /* |
| 202 | * Set the appropriate edges for wakeup. |
| 203 | */ |
| 204 | GRER = PWER & GPIO_IRQ_rising_edge; |
| 205 | GFER = PWER & GPIO_IRQ_falling_edge; |
| 206 | |
| 207 | /* |
| 208 | * Clear any pending GPIO interrupts. |
| 209 | */ |
| 210 | GEDR = GEDR; |
| 211 | |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | static void sa1100_gpio_resume(void) |
| 216 | { |
| 217 | GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; |
| 218 | GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; |
| 219 | } |
| 220 | |
| 221 | static struct syscore_ops sa1100_gpio_syscore_ops = { |
| 222 | .suspend = sa1100_gpio_suspend, |
| 223 | .resume = sa1100_gpio_resume, |
| 224 | }; |
| 225 | |
| 226 | static int __init sa1100_gpio_init_devicefs(void) |
| 227 | { |
| 228 | register_syscore_ops(&sa1100_gpio_syscore_ops); |
| 229 | return 0; |
| 230 | } |
| 231 | |
| 232 | device_initcall(sa1100_gpio_init_devicefs); |
| 233 | |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 234 | void __init sa1100_init_gpio(void) |
| 235 | { |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 236 | /* clear all GPIO edge detects */ |
| 237 | GFER = 0; |
| 238 | GRER = 0; |
| 239 | GEDR = -1; |
| 240 | |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 241 | gpiochip_add(&sa1100_gpio_chip); |
Dmitry Eremin-Solenikov | a0ea298d3 | 2015-01-15 02:32:26 +0100 | [diff] [blame] | 242 | |
| 243 | sa1100_gpio_irqdomain = irq_domain_add_simple(NULL, |
| 244 | 28, IRQ_GPIO0, |
| 245 | &sa1100_gpio_irqdomain_ops, NULL); |
| 246 | |
| 247 | /* |
| 248 | * Install handlers for GPIO 0-10 edge detect interrupts |
| 249 | */ |
| 250 | irq_set_chained_handler(IRQ_GPIO0_SC, sa1100_gpio_handler); |
| 251 | irq_set_chained_handler(IRQ_GPIO1_SC, sa1100_gpio_handler); |
| 252 | irq_set_chained_handler(IRQ_GPIO2_SC, sa1100_gpio_handler); |
| 253 | irq_set_chained_handler(IRQ_GPIO3_SC, sa1100_gpio_handler); |
| 254 | irq_set_chained_handler(IRQ_GPIO4_SC, sa1100_gpio_handler); |
| 255 | irq_set_chained_handler(IRQ_GPIO5_SC, sa1100_gpio_handler); |
| 256 | irq_set_chained_handler(IRQ_GPIO6_SC, sa1100_gpio_handler); |
| 257 | irq_set_chained_handler(IRQ_GPIO7_SC, sa1100_gpio_handler); |
| 258 | irq_set_chained_handler(IRQ_GPIO8_SC, sa1100_gpio_handler); |
| 259 | irq_set_chained_handler(IRQ_GPIO9_SC, sa1100_gpio_handler); |
| 260 | irq_set_chained_handler(IRQ_GPIO10_SC, sa1100_gpio_handler); |
| 261 | /* |
| 262 | * Install handler for GPIO 11-27 edge detect interrupts |
| 263 | */ |
| 264 | irq_set_chained_handler(IRQ_GPIO11_27, sa1100_gpio_handler); |
| 265 | |
Dmitry Baryshkov | 45528e3 | 2008-04-10 13:31:47 +0100 | [diff] [blame] | 266 | } |