| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 1 | /* | 
| Eric Miao | 38f539a | 2009-01-20 12:09:06 +0800 | [diff] [blame] | 2 | *  linux/arch/arm/plat-pxa/gpio.c | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 3 | * | 
|  | 4 | *  Generic PXA GPIO handling | 
|  | 5 | * | 
|  | 6 | *  Author:	Nicolas Pitre | 
|  | 7 | *  Created:	Jun 15, 2001 | 
|  | 8 | *  Copyright:	MontaVista Software Inc. | 
|  | 9 | * | 
|  | 10 | *  This program is free software; you can redistribute it and/or modify | 
|  | 11 | *  it under the terms of the GNU General Public License version 2 as | 
|  | 12 | *  published by the Free Software Foundation. | 
|  | 13 | */ | 
| Haojian Zhuang | 389eda1 | 2011-10-17 21:26:55 +0800 | [diff] [blame] | 14 | #include <linux/clk.h> | 
|  | 15 | #include <linux/err.h> | 
| Russell King | 2f8163b | 2011-07-26 10:53:52 +0100 | [diff] [blame] | 16 | #include <linux/gpio.h> | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 17 | #include <linux/gpio-pxa.h> | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 18 | #include <linux/init.h> | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 19 | #include <linux/irq.h> | 
| Russell King | fced80c | 2008-09-06 12:10:45 +0100 | [diff] [blame] | 20 | #include <linux/io.h> | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 21 | #include <linux/platform_device.h> | 
| Rafael J. Wysocki | 2eaa03b | 2011-04-22 22:03:11 +0200 | [diff] [blame] | 22 | #include <linux/syscore_ops.h> | 
| Daniel Mack | 4aa7826 | 2009-06-19 22:56:09 +0200 | [diff] [blame] | 23 | #include <linux/slab.h> | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 24 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 25 | /* | 
|  | 26 | * We handle the GPIOs by banks, each bank covers up to 32 GPIOs with | 
|  | 27 | * one set of registers. The register offsets are organized below: | 
|  | 28 | * | 
|  | 29 | *           GPLR    GPDR    GPSR    GPCR    GRER    GFER    GEDR | 
|  | 30 | * BANK 0 - 0x0000  0x000C  0x0018  0x0024  0x0030  0x003C  0x0048 | 
|  | 31 | * BANK 1 - 0x0004  0x0010  0x001C  0x0028  0x0034  0x0040  0x004C | 
|  | 32 | * BANK 2 - 0x0008  0x0014  0x0020  0x002C  0x0038  0x0044  0x0050 | 
|  | 33 | * | 
|  | 34 | * BANK 3 - 0x0100  0x010C  0x0118  0x0124  0x0130  0x013C  0x0148 | 
|  | 35 | * BANK 4 - 0x0104  0x0110  0x011C  0x0128  0x0134  0x0140  0x014C | 
|  | 36 | * BANK 5 - 0x0108  0x0114  0x0120  0x012C  0x0138  0x0144  0x0150 | 
|  | 37 | * | 
|  | 38 | * NOTE: | 
|  | 39 | *   BANK 3 is only available on PXA27x and later processors. | 
|  | 40 | *   BANK 4 and 5 are only available on PXA935 | 
|  | 41 | */ | 
|  | 42 |  | 
|  | 43 | #define GPLR_OFFSET	0x00 | 
|  | 44 | #define GPDR_OFFSET	0x0C | 
|  | 45 | #define GPSR_OFFSET	0x18 | 
|  | 46 | #define GPCR_OFFSET	0x24 | 
|  | 47 | #define GRER_OFFSET	0x30 | 
|  | 48 | #define GFER_OFFSET	0x3C | 
|  | 49 | #define GEDR_OFFSET	0x48 | 
|  | 50 | #define GAFR_OFFSET	0x54 | 
| Haojian Zhuang | be24168 | 2011-10-17 21:07:15 +0800 | [diff] [blame] | 51 | #define ED_MASK_OFFSET	0x9C	/* GPIO edge detection for AP side */ | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 52 |  | 
|  | 53 | #define BANK_OFF(n)	(((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 54 |  | 
| Eric Miao | 3b8e285 | 2009-01-07 11:30:49 +0800 | [diff] [blame] | 55 | int pxa_last_gpio; | 
|  | 56 |  | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 57 | struct pxa_gpio_chip { | 
|  | 58 | struct gpio_chip chip; | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 59 | void __iomem	*regbase; | 
|  | 60 | char label[10]; | 
|  | 61 |  | 
|  | 62 | unsigned long	irq_mask; | 
|  | 63 | unsigned long	irq_edge_rise; | 
|  | 64 | unsigned long	irq_edge_fall; | 
|  | 65 |  | 
|  | 66 | #ifdef CONFIG_PM | 
|  | 67 | unsigned long	saved_gplr; | 
|  | 68 | unsigned long	saved_gpdr; | 
|  | 69 | unsigned long	saved_grer; | 
|  | 70 | unsigned long	saved_gfer; | 
|  | 71 | #endif | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 72 | }; | 
|  | 73 |  | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 74 | enum { | 
|  | 75 | PXA25X_GPIO = 0, | 
|  | 76 | PXA26X_GPIO, | 
|  | 77 | PXA27X_GPIO, | 
|  | 78 | PXA3XX_GPIO, | 
|  | 79 | PXA93X_GPIO, | 
|  | 80 | MMP_GPIO = 0x10, | 
|  | 81 | MMP2_GPIO, | 
|  | 82 | }; | 
|  | 83 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 84 | static DEFINE_SPINLOCK(gpio_lock); | 
|  | 85 | static struct pxa_gpio_chip *pxa_gpio_chips; | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 86 | static int gpio_type; | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 87 | static void __iomem *gpio_reg_base; | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 88 |  | 
|  | 89 | #define for_each_gpio_chip(i, c)			\ | 
|  | 90 | for (i = 0, c = &pxa_gpio_chips[0]; i <= pxa_last_gpio; i += 32, c++) | 
|  | 91 |  | 
|  | 92 | static inline void __iomem *gpio_chip_base(struct gpio_chip *c) | 
|  | 93 | { | 
|  | 94 | return container_of(c, struct pxa_gpio_chip, chip)->regbase; | 
|  | 95 | } | 
|  | 96 |  | 
| Linus Walleij | a065685 | 2011-06-13 10:42:19 +0200 | [diff] [blame] | 97 | static inline struct pxa_gpio_chip *gpio_to_pxachip(unsigned gpio) | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 98 | { | 
|  | 99 | return &pxa_gpio_chips[gpio_to_bank(gpio)]; | 
|  | 100 | } | 
|  | 101 |  | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 102 | static inline int gpio_is_pxa_type(int type) | 
|  | 103 | { | 
|  | 104 | return (type & MMP_GPIO) == 0; | 
|  | 105 | } | 
|  | 106 |  | 
|  | 107 | static inline int gpio_is_mmp_type(int type) | 
|  | 108 | { | 
|  | 109 | return (type & MMP_GPIO) != 0; | 
|  | 110 | } | 
|  | 111 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 112 | /* GPIO86/87/88/89 on PXA26x have their direction bits in PXA_GPDR(2 inverted, | 
|  | 113 | * as well as their Alternate Function value being '1' for GPIO in GAFRx. | 
|  | 114 | */ | 
|  | 115 | static inline int __gpio_is_inverted(int gpio) | 
|  | 116 | { | 
|  | 117 | if ((gpio_type == PXA26X_GPIO) && (gpio > 85)) | 
|  | 118 | return 1; | 
|  | 119 | return 0; | 
|  | 120 | } | 
|  | 121 |  | 
|  | 122 | /* | 
|  | 123 | * On PXA25x and PXA27x, GAFRx and GPDRx together decide the alternate | 
|  | 124 | * function of a GPIO, and GPDRx cannot be altered once configured. It | 
|  | 125 | * is attributed as "occupied" here (I know this terminology isn't | 
|  | 126 | * accurate, you are welcome to propose a better one :-) | 
|  | 127 | */ | 
|  | 128 | static inline int __gpio_is_occupied(unsigned gpio) | 
|  | 129 | { | 
|  | 130 | struct pxa_gpio_chip *pxachip; | 
|  | 131 | void __iomem *base; | 
|  | 132 | unsigned long gafr = 0, gpdr = 0; | 
|  | 133 | int ret, af = 0, dir = 0; | 
|  | 134 |  | 
|  | 135 | pxachip = gpio_to_pxachip(gpio); | 
|  | 136 | base = gpio_chip_base(&pxachip->chip); | 
|  | 137 | gpdr = readl_relaxed(base + GPDR_OFFSET); | 
|  | 138 |  | 
|  | 139 | switch (gpio_type) { | 
|  | 140 | case PXA25X_GPIO: | 
|  | 141 | case PXA26X_GPIO: | 
|  | 142 | case PXA27X_GPIO: | 
|  | 143 | gafr = readl_relaxed(base + GAFR_OFFSET); | 
|  | 144 | af = (gafr >> ((gpio & 0xf) * 2)) & 0x3; | 
|  | 145 | dir = gpdr & GPIO_bit(gpio); | 
|  | 146 |  | 
|  | 147 | if (__gpio_is_inverted(gpio)) | 
|  | 148 | ret = (af != 1) || (dir == 0); | 
|  | 149 | else | 
|  | 150 | ret = (af != 0) || (dir != 0); | 
|  | 151 | break; | 
|  | 152 | default: | 
|  | 153 | ret = gpdr & GPIO_bit(gpio); | 
|  | 154 | break; | 
|  | 155 | } | 
|  | 156 | return ret; | 
|  | 157 | } | 
|  | 158 |  | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 159 | #ifdef CONFIG_ARCH_PXA | 
|  | 160 | static inline int __pxa_gpio_to_irq(int gpio) | 
|  | 161 | { | 
|  | 162 | if (gpio_is_pxa_type(gpio_type)) | 
|  | 163 | return PXA_GPIO_TO_IRQ(gpio); | 
|  | 164 | return -1; | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | static inline int __pxa_irq_to_gpio(int irq) | 
|  | 168 | { | 
|  | 169 | if (gpio_is_pxa_type(gpio_type)) | 
|  | 170 | return irq - PXA_GPIO_TO_IRQ(0); | 
|  | 171 | return -1; | 
|  | 172 | } | 
|  | 173 | #else | 
|  | 174 | static inline int __pxa_gpio_to_irq(int gpio) { return -1; } | 
|  | 175 | static inline int __pxa_irq_to_gpio(int irq) { return -1; } | 
|  | 176 | #endif | 
|  | 177 |  | 
|  | 178 | #ifdef CONFIG_ARCH_MMP | 
|  | 179 | static inline int __mmp_gpio_to_irq(int gpio) | 
|  | 180 | { | 
|  | 181 | if (gpio_is_mmp_type(gpio_type)) | 
|  | 182 | return MMP_GPIO_TO_IRQ(gpio); | 
|  | 183 | return -1; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | static inline int __mmp_irq_to_gpio(int irq) | 
|  | 187 | { | 
|  | 188 | if (gpio_is_mmp_type(gpio_type)) | 
|  | 189 | return irq - MMP_GPIO_TO_IRQ(0); | 
|  | 190 | return -1; | 
|  | 191 | } | 
|  | 192 | #else | 
|  | 193 | static inline int __mmp_gpio_to_irq(int gpio) { return -1; } | 
|  | 194 | static inline int __mmp_irq_to_gpio(int irq) { return -1; } | 
|  | 195 | #endif | 
|  | 196 |  | 
|  | 197 | static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | 
|  | 198 | { | 
|  | 199 | int gpio, ret; | 
|  | 200 |  | 
|  | 201 | gpio = chip->base + offset; | 
|  | 202 | ret = __pxa_gpio_to_irq(gpio); | 
|  | 203 | if (ret >= 0) | 
|  | 204 | return ret; | 
|  | 205 | return __mmp_gpio_to_irq(gpio); | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | int pxa_irq_to_gpio(int irq) | 
|  | 209 | { | 
|  | 210 | int ret; | 
|  | 211 |  | 
|  | 212 | ret = __pxa_irq_to_gpio(irq); | 
|  | 213 | if (ret >= 0) | 
|  | 214 | return ret; | 
|  | 215 | return __mmp_irq_to_gpio(irq); | 
|  | 216 | } | 
|  | 217 |  | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 218 | static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | 
|  | 219 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 220 | void __iomem *base = gpio_chip_base(chip); | 
|  | 221 | uint32_t value, mask = 1 << offset; | 
|  | 222 | unsigned long flags; | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 223 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 224 | spin_lock_irqsave(&gpio_lock, flags); | 
|  | 225 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 226 | value = readl_relaxed(base + GPDR_OFFSET); | 
| Eric Miao | 067455a | 2008-11-26 18:12:04 +0800 | [diff] [blame] | 227 | if (__gpio_is_inverted(chip->base + offset)) | 
|  | 228 | value |= mask; | 
|  | 229 | else | 
|  | 230 | value &= ~mask; | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 231 | writel_relaxed(value, base + GPDR_OFFSET); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 232 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 233 | spin_unlock_irqrestore(&gpio_lock, flags); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 234 | return 0; | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | static int pxa_gpio_direction_output(struct gpio_chip *chip, | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 238 | unsigned offset, int value) | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 239 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 240 | void __iomem *base = gpio_chip_base(chip); | 
|  | 241 | uint32_t tmp, mask = 1 << offset; | 
|  | 242 | unsigned long flags; | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 243 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 244 | writel_relaxed(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET)); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 245 |  | 
|  | 246 | spin_lock_irqsave(&gpio_lock, flags); | 
|  | 247 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 248 | tmp = readl_relaxed(base + GPDR_OFFSET); | 
| Eric Miao | 067455a | 2008-11-26 18:12:04 +0800 | [diff] [blame] | 249 | if (__gpio_is_inverted(chip->base + offset)) | 
|  | 250 | tmp &= ~mask; | 
|  | 251 | else | 
|  | 252 | tmp |= mask; | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 253 | writel_relaxed(tmp, base + GPDR_OFFSET); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 254 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 255 | spin_unlock_irqrestore(&gpio_lock, flags); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 256 | return 0; | 
|  | 257 | } | 
|  | 258 |  | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 259 | static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset) | 
|  | 260 | { | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 261 | return readl_relaxed(gpio_chip_base(chip) + GPLR_OFFSET) & (1 << offset); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 262 | } | 
|  | 263 |  | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 264 | static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | 
|  | 265 | { | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 266 | writel_relaxed(1 << offset, gpio_chip_base(chip) + | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 267 | (value ? GPSR_OFFSET : GPCR_OFFSET)); | 
| Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 268 | } | 
|  | 269 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 270 | static int __devinit pxa_init_gpio_chip(int gpio_end) | 
| Eric Miao | a58fbcd | 2009-01-06 17:37:37 +0800 | [diff] [blame] | 271 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 272 | int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1; | 
|  | 273 | struct pxa_gpio_chip *chips; | 
| Eric Miao | a58fbcd | 2009-01-06 17:37:37 +0800 | [diff] [blame] | 274 |  | 
| Daniel Mack | 4aa7826 | 2009-06-19 22:56:09 +0200 | [diff] [blame] | 275 | chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 276 | if (chips == NULL) { | 
|  | 277 | pr_err("%s: failed to allocate GPIO chips\n", __func__); | 
|  | 278 | return -ENOMEM; | 
| Eric Miao | a58fbcd | 2009-01-06 17:37:37 +0800 | [diff] [blame] | 279 | } | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 280 |  | 
|  | 281 | for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32) { | 
|  | 282 | struct gpio_chip *c = &chips[i].chip; | 
|  | 283 |  | 
|  | 284 | sprintf(chips[i].label, "gpio-%d", i); | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 285 | chips[i].regbase = gpio_reg_base + BANK_OFF(i); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 286 |  | 
|  | 287 | c->base  = gpio; | 
|  | 288 | c->label = chips[i].label; | 
|  | 289 |  | 
|  | 290 | c->direction_input  = pxa_gpio_direction_input; | 
|  | 291 | c->direction_output = pxa_gpio_direction_output; | 
|  | 292 | c->get = pxa_gpio_get; | 
|  | 293 | c->set = pxa_gpio_set; | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 294 | c->to_irq = pxa_gpio_to_irq; | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 295 |  | 
|  | 296 | /* number of GPIOs on last bank may be less than 32 */ | 
|  | 297 | c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32; | 
|  | 298 | gpiochip_add(c); | 
|  | 299 | } | 
|  | 300 | pxa_gpio_chips = chips; | 
|  | 301 | return 0; | 
| Eric Miao | a58fbcd | 2009-01-06 17:37:37 +0800 | [diff] [blame] | 302 | } | 
|  | 303 |  | 
| Eric Miao | a8f6fae | 2009-04-21 14:39:07 +0800 | [diff] [blame] | 304 | /* Update only those GRERx and GFERx edge detection register bits if those | 
|  | 305 | * bits are set in c->irq_mask | 
|  | 306 | */ | 
|  | 307 | static inline void update_edge_detect(struct pxa_gpio_chip *c) | 
|  | 308 | { | 
|  | 309 | uint32_t grer, gfer; | 
|  | 310 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 311 | grer = readl_relaxed(c->regbase + GRER_OFFSET) & ~c->irq_mask; | 
|  | 312 | gfer = readl_relaxed(c->regbase + GFER_OFFSET) & ~c->irq_mask; | 
| Eric Miao | a8f6fae | 2009-04-21 14:39:07 +0800 | [diff] [blame] | 313 | grer |= c->irq_edge_rise & c->irq_mask; | 
|  | 314 | gfer |= c->irq_edge_fall & c->irq_mask; | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 315 | writel_relaxed(grer, c->regbase + GRER_OFFSET); | 
|  | 316 | writel_relaxed(gfer, c->regbase + GFER_OFFSET); | 
| Eric Miao | a8f6fae | 2009-04-21 14:39:07 +0800 | [diff] [blame] | 317 | } | 
|  | 318 |  | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 319 | static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 320 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 321 | struct pxa_gpio_chip *c; | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 322 | int gpio = pxa_irq_to_gpio(d->irq); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 323 | unsigned long gpdr, mask = GPIO_bit(gpio); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 324 |  | 
| Linus Walleij | a065685 | 2011-06-13 10:42:19 +0200 | [diff] [blame] | 325 | c = gpio_to_pxachip(gpio); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 326 |  | 
|  | 327 | if (type == IRQ_TYPE_PROBE) { | 
|  | 328 | /* Don't mess with enabled GPIOs using preconfigured edges or | 
|  | 329 | * GPIOs set to alternate function or to output during probe | 
|  | 330 | */ | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 331 | if ((c->irq_edge_rise | c->irq_edge_fall) & GPIO_bit(gpio)) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 332 | return 0; | 
| eric miao | 689c04a | 2008-03-04 17:18:38 +0800 | [diff] [blame] | 333 |  | 
|  | 334 | if (__gpio_is_occupied(gpio)) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 335 | return 0; | 
| eric miao | 689c04a | 2008-03-04 17:18:38 +0800 | [diff] [blame] | 336 |  | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 337 | type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; | 
|  | 338 | } | 
|  | 339 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 340 | gpdr = readl_relaxed(c->regbase + GPDR_OFFSET); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 341 |  | 
| Eric Miao | 067455a | 2008-11-26 18:12:04 +0800 | [diff] [blame] | 342 | if (__gpio_is_inverted(gpio)) | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 343 | writel_relaxed(gpdr | mask,  c->regbase + GPDR_OFFSET); | 
| Eric Miao | 067455a | 2008-11-26 18:12:04 +0800 | [diff] [blame] | 344 | else | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 345 | writel_relaxed(gpdr & ~mask, c->regbase + GPDR_OFFSET); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 346 |  | 
|  | 347 | if (type & IRQ_TYPE_EDGE_RISING) | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 348 | c->irq_edge_rise |= mask; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 349 | else | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 350 | c->irq_edge_rise &= ~mask; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 351 |  | 
|  | 352 | if (type & IRQ_TYPE_EDGE_FALLING) | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 353 | c->irq_edge_fall |= mask; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 354 | else | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 355 | c->irq_edge_fall &= ~mask; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 356 |  | 
| Eric Miao | a8f6fae | 2009-04-21 14:39:07 +0800 | [diff] [blame] | 357 | update_edge_detect(c); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 358 |  | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 359 | pr_debug("%s: IRQ%d (GPIO%d) - edge%s%s\n", __func__, d->irq, gpio, | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 360 | ((type & IRQ_TYPE_EDGE_RISING)  ? " rising"  : ""), | 
|  | 361 | ((type & IRQ_TYPE_EDGE_FALLING) ? " falling" : "")); | 
|  | 362 | return 0; | 
|  | 363 | } | 
|  | 364 |  | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 365 | static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) | 
|  | 366 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 367 | struct pxa_gpio_chip *c; | 
|  | 368 | int loop, gpio, gpio_base, n; | 
|  | 369 | unsigned long gedr; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 370 |  | 
|  | 371 | do { | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 372 | loop = 0; | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 373 | for_each_gpio_chip(gpio, c) { | 
|  | 374 | gpio_base = c->chip.base; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 375 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 376 | gedr = readl_relaxed(c->regbase + GEDR_OFFSET); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 377 | gedr = gedr & c->irq_mask; | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 378 | writel_relaxed(gedr, c->regbase + GEDR_OFFSET); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 379 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 380 | n = find_first_bit(&gedr, BITS_PER_LONG); | 
|  | 381 | while (n < BITS_PER_LONG) { | 
|  | 382 | loop = 1; | 
|  | 383 |  | 
|  | 384 | generic_handle_irq(gpio_to_irq(gpio_base + n)); | 
|  | 385 | n = find_next_bit(&gedr, BITS_PER_LONG, n + 1); | 
|  | 386 | } | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 387 | } | 
|  | 388 | } while (loop); | 
|  | 389 | } | 
|  | 390 |  | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 391 | static void pxa_ack_muxed_gpio(struct irq_data *d) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 392 | { | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 393 | int gpio = pxa_irq_to_gpio(d->irq); | 
| Linus Walleij | a065685 | 2011-06-13 10:42:19 +0200 | [diff] [blame] | 394 | struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 395 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 396 | writel_relaxed(GPIO_bit(gpio), c->regbase + GEDR_OFFSET); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 397 | } | 
|  | 398 |  | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 399 | static void pxa_mask_muxed_gpio(struct irq_data *d) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 400 | { | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 401 | int gpio = pxa_irq_to_gpio(d->irq); | 
| Linus Walleij | a065685 | 2011-06-13 10:42:19 +0200 | [diff] [blame] | 402 | struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 403 | uint32_t grer, gfer; | 
|  | 404 |  | 
|  | 405 | c->irq_mask &= ~GPIO_bit(gpio); | 
|  | 406 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 407 | grer = readl_relaxed(c->regbase + GRER_OFFSET) & ~GPIO_bit(gpio); | 
|  | 408 | gfer = readl_relaxed(c->regbase + GFER_OFFSET) & ~GPIO_bit(gpio); | 
|  | 409 | writel_relaxed(grer, c->regbase + GRER_OFFSET); | 
|  | 410 | writel_relaxed(gfer, c->regbase + GFER_OFFSET); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 411 | } | 
|  | 412 |  | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 413 | static void pxa_unmask_muxed_gpio(struct irq_data *d) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 414 | { | 
| Haojian Zhuang | 4929f5a | 2011-10-10 16:03:51 +0800 | [diff] [blame] | 415 | int gpio = pxa_irq_to_gpio(d->irq); | 
| Linus Walleij | a065685 | 2011-06-13 10:42:19 +0200 | [diff] [blame] | 416 | struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 417 |  | 
|  | 418 | c->irq_mask |= GPIO_bit(gpio); | 
| Eric Miao | a8f6fae | 2009-04-21 14:39:07 +0800 | [diff] [blame] | 419 | update_edge_detect(c); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 420 | } | 
|  | 421 |  | 
|  | 422 | static struct irq_chip pxa_muxed_gpio_chip = { | 
|  | 423 | .name		= "GPIO", | 
| Lennert Buytenhek | a3f4c92 | 2010-11-29 11:18:26 +0100 | [diff] [blame] | 424 | .irq_ack	= pxa_ack_muxed_gpio, | 
|  | 425 | .irq_mask	= pxa_mask_muxed_gpio, | 
|  | 426 | .irq_unmask	= pxa_unmask_muxed_gpio, | 
|  | 427 | .irq_set_type	= pxa_gpio_irq_type, | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 428 | }; | 
|  | 429 |  | 
| Haojian Zhuang | 478e223 | 2011-10-14 16:44:07 +0800 | [diff] [blame] | 430 | static int pxa_gpio_nums(void) | 
|  | 431 | { | 
|  | 432 | int count = 0; | 
|  | 433 |  | 
|  | 434 | #ifdef CONFIG_ARCH_PXA | 
|  | 435 | if (cpu_is_pxa25x()) { | 
|  | 436 | #ifdef CONFIG_CPU_PXA26x | 
|  | 437 | count = 89; | 
|  | 438 | gpio_type = PXA26X_GPIO; | 
|  | 439 | #elif defined(CONFIG_PXA25x) | 
|  | 440 | count = 84; | 
|  | 441 | gpio_type = PXA26X_GPIO; | 
|  | 442 | #endif /* CONFIG_CPU_PXA26x */ | 
|  | 443 | } else if (cpu_is_pxa27x()) { | 
|  | 444 | count = 120; | 
|  | 445 | gpio_type = PXA27X_GPIO; | 
|  | 446 | } else if (cpu_is_pxa93x() || cpu_is_pxa95x()) { | 
|  | 447 | count = 191; | 
|  | 448 | gpio_type = PXA93X_GPIO; | 
|  | 449 | } else if (cpu_is_pxa3xx()) { | 
|  | 450 | count = 127; | 
|  | 451 | gpio_type = PXA3XX_GPIO; | 
|  | 452 | } | 
|  | 453 | #endif /* CONFIG_ARCH_PXA */ | 
|  | 454 |  | 
|  | 455 | #ifdef CONFIG_ARCH_MMP | 
|  | 456 | if (cpu_is_pxa168() || cpu_is_pxa910()) { | 
|  | 457 | count = 127; | 
|  | 458 | gpio_type = MMP_GPIO; | 
|  | 459 | } else if (cpu_is_mmp2()) { | 
|  | 460 | count = 191; | 
|  | 461 | gpio_type = MMP2_GPIO; | 
|  | 462 | } | 
|  | 463 | #endif /* CONFIG_ARCH_MMP */ | 
|  | 464 | return count; | 
|  | 465 | } | 
|  | 466 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 467 | static int __devinit pxa_gpio_probe(struct platform_device *pdev) | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 468 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 469 | struct pxa_gpio_chip *c; | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 470 | struct resource *res; | 
| Haojian Zhuang | 389eda1 | 2011-10-17 21:26:55 +0800 | [diff] [blame] | 471 | struct clk *clk; | 
|  | 472 | int gpio, irq, ret; | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 473 | int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 474 |  | 
| Haojian Zhuang | 478e223 | 2011-10-14 16:44:07 +0800 | [diff] [blame] | 475 | pxa_last_gpio = pxa_gpio_nums(); | 
|  | 476 | if (!pxa_last_gpio) | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 477 | return -EINVAL; | 
|  | 478 |  | 
|  | 479 | irq0 = platform_get_irq_byname(pdev, "gpio0"); | 
|  | 480 | irq1 = platform_get_irq_byname(pdev, "gpio1"); | 
|  | 481 | irq_mux = platform_get_irq_byname(pdev, "gpio_mux"); | 
|  | 482 | if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0) | 
|  | 483 | || (irq_mux <= 0)) | 
|  | 484 | return -EINVAL; | 
|  | 485 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 486 | if (!res) | 
|  | 487 | return -EINVAL; | 
|  | 488 | gpio_reg_base = ioremap(res->start, resource_size(res)); | 
|  | 489 | if (!gpio_reg_base) | 
|  | 490 | return -EINVAL; | 
|  | 491 |  | 
|  | 492 | if (irq0 > 0) | 
|  | 493 | gpio_offset = 2; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 494 |  | 
| Haojian Zhuang | 389eda1 | 2011-10-17 21:26:55 +0800 | [diff] [blame] | 495 | clk = clk_get(&pdev->dev, NULL); | 
|  | 496 | if (IS_ERR(clk)) { | 
|  | 497 | dev_err(&pdev->dev, "Error %ld to get gpio clock\n", | 
|  | 498 | PTR_ERR(clk)); | 
|  | 499 | iounmap(gpio_reg_base); | 
|  | 500 | return PTR_ERR(clk); | 
|  | 501 | } | 
|  | 502 | ret = clk_prepare(clk); | 
|  | 503 | if (ret) { | 
|  | 504 | clk_put(clk); | 
|  | 505 | iounmap(gpio_reg_base); | 
|  | 506 | return ret; | 
|  | 507 | } | 
|  | 508 | ret = clk_enable(clk); | 
|  | 509 | if (ret) { | 
|  | 510 | clk_unprepare(clk); | 
|  | 511 | clk_put(clk); | 
|  | 512 | iounmap(gpio_reg_base); | 
|  | 513 | return ret; | 
|  | 514 | } | 
|  | 515 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 516 | /* Initialize GPIO chips */ | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 517 | pxa_init_gpio_chip(pxa_last_gpio); | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 518 |  | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 519 | /* clear all GPIO edge detects */ | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 520 | for_each_gpio_chip(gpio, c) { | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 521 | writel_relaxed(0, c->regbase + GFER_OFFSET); | 
|  | 522 | writel_relaxed(0, c->regbase + GRER_OFFSET); | 
|  | 523 | writel_relaxed(~0,c->regbase + GEDR_OFFSET); | 
| Haojian Zhuang | be24168 | 2011-10-17 21:07:15 +0800 | [diff] [blame] | 524 | /* unmask GPIO edge detect for AP side */ | 
|  | 525 | if (gpio_is_mmp_type(gpio_type)) | 
|  | 526 | writel_relaxed(~0, c->regbase + ED_MASK_OFFSET); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 527 | } | 
|  | 528 |  | 
| Haojian Zhuang | 87c49e2 | 2011-10-10 14:38:46 +0800 | [diff] [blame] | 529 | #ifdef CONFIG_ARCH_PXA | 
|  | 530 | irq = gpio_to_irq(0); | 
|  | 531 | irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip, | 
|  | 532 | handle_edge_irq); | 
|  | 533 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 
|  | 534 | irq_set_chained_handler(IRQ_GPIO0, pxa_gpio_demux_handler); | 
|  | 535 |  | 
|  | 536 | irq = gpio_to_irq(1); | 
|  | 537 | irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip, | 
|  | 538 | handle_edge_irq); | 
|  | 539 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 
|  | 540 | irq_set_chained_handler(IRQ_GPIO1, pxa_gpio_demux_handler); | 
|  | 541 | #endif | 
|  | 542 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 543 | for (irq  = gpio_to_irq(gpio_offset); | 
|  | 544 | irq <= gpio_to_irq(pxa_last_gpio); irq++) { | 
| Thomas Gleixner | f38c02f | 2011-03-24 13:35:09 +0100 | [diff] [blame] | 545 | irq_set_chip_and_handler(irq, &pxa_muxed_gpio_chip, | 
|  | 546 | handle_edge_irq); | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 547 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 
|  | 548 | } | 
|  | 549 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 550 | irq_set_chained_handler(irq_mux, pxa_gpio_demux_handler); | 
|  | 551 | return 0; | 
| eric miao | e3630db | 2008-03-04 11:42:26 +0800 | [diff] [blame] | 552 | } | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 553 |  | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 554 | static struct platform_driver pxa_gpio_driver = { | 
|  | 555 | .probe		= pxa_gpio_probe, | 
|  | 556 | .driver		= { | 
|  | 557 | .name	= "pxa-gpio", | 
|  | 558 | }, | 
|  | 559 | }; | 
|  | 560 |  | 
|  | 561 | static int __init pxa_gpio_init(void) | 
|  | 562 | { | 
|  | 563 | return platform_driver_register(&pxa_gpio_driver); | 
|  | 564 | } | 
|  | 565 | postcore_initcall(pxa_gpio_init); | 
|  | 566 |  | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 567 | #ifdef CONFIG_PM | 
| Rafael J. Wysocki | 2eaa03b | 2011-04-22 22:03:11 +0200 | [diff] [blame] | 568 | static int pxa_gpio_suspend(void) | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 569 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 570 | struct pxa_gpio_chip *c; | 
|  | 571 | int gpio; | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 572 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 573 | for_each_gpio_chip(gpio, c) { | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 574 | c->saved_gplr = readl_relaxed(c->regbase + GPLR_OFFSET); | 
|  | 575 | c->saved_gpdr = readl_relaxed(c->regbase + GPDR_OFFSET); | 
|  | 576 | c->saved_grer = readl_relaxed(c->regbase + GRER_OFFSET); | 
|  | 577 | c->saved_gfer = readl_relaxed(c->regbase + GFER_OFFSET); | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 578 |  | 
|  | 579 | /* Clear GPIO transition detect bits */ | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 580 | writel_relaxed(0xffffffff, c->regbase + GEDR_OFFSET); | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 581 | } | 
|  | 582 | return 0; | 
|  | 583 | } | 
|  | 584 |  | 
| Rafael J. Wysocki | 2eaa03b | 2011-04-22 22:03:11 +0200 | [diff] [blame] | 585 | static void pxa_gpio_resume(void) | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 586 | { | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 587 | struct pxa_gpio_chip *c; | 
|  | 588 | int gpio; | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 589 |  | 
| Eric Miao | 0807da5 | 2009-01-07 18:01:51 +0800 | [diff] [blame] | 590 | for_each_gpio_chip(gpio, c) { | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 591 | /* restore level with set/clear */ | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 592 | writel_relaxed( c->saved_gplr, c->regbase + GPSR_OFFSET); | 
|  | 593 | writel_relaxed(~c->saved_gplr, c->regbase + GPCR_OFFSET); | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 594 |  | 
| Haojian Zhuang | df664d2 | 2011-10-14 17:24:03 +0800 | [diff] [blame] | 595 | writel_relaxed(c->saved_grer, c->regbase + GRER_OFFSET); | 
|  | 596 | writel_relaxed(c->saved_gfer, c->regbase + GFER_OFFSET); | 
|  | 597 | writel_relaxed(c->saved_gpdr, c->regbase + GPDR_OFFSET); | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 598 | } | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 599 | } | 
|  | 600 | #else | 
|  | 601 | #define pxa_gpio_suspend	NULL | 
|  | 602 | #define pxa_gpio_resume		NULL | 
|  | 603 | #endif | 
|  | 604 |  | 
| Rafael J. Wysocki | 2eaa03b | 2011-04-22 22:03:11 +0200 | [diff] [blame] | 605 | struct syscore_ops pxa_gpio_syscore_ops = { | 
| eric miao | 663707c | 2008-03-04 16:13:58 +0800 | [diff] [blame] | 606 | .suspend	= pxa_gpio_suspend, | 
|  | 607 | .resume		= pxa_gpio_resume, | 
|  | 608 | }; | 
| Haojian Zhuang | 157d264 | 2011-10-17 20:37:52 +0800 | [diff] [blame] | 609 |  | 
|  | 610 | static int __init pxa_gpio_sysinit(void) | 
|  | 611 | { | 
|  | 612 | register_syscore_ops(&pxa_gpio_syscore_ops); | 
|  | 613 | return 0; | 
|  | 614 | } | 
|  | 615 | postcore_initcall(pxa_gpio_sysinit); |