blob: 7ada69a10898124fee9e84a4b42a27f81fecbd3b [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
Gregory Bean0cc2fc12010-11-24 11:53:51 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
Gregory Bean0cc2fc12010-11-24 11:53:51 -080012 */
Gregory Bean70cc2c02010-11-24 11:53:52 -080013#include <linux/bitmap.h>
14#include <linux/bitops.h>
Gregory Bean0cc2fc12010-11-24 11:53:51 -080015#include <linux/gpio.h>
Gregory Bean70cc2c02010-11-24 11:53:52 -080016#include <linux/init.h>
17#include <linux/interrupt.h>
Gregory Bean0cc2fc12010-11-24 11:53:51 -080018#include <linux/irq.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <linux/io.h>
Gregory Bean0cc2fc12010-11-24 11:53:51 -080020#include <linux/module.h>
Gregory Bean0cc2fc12010-11-24 11:53:51 -080021#include <linux/spinlock.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/syscore_ops.h>
Will Deacon03dd7652011-02-21 14:54:57 +000023
24#include <asm/mach/irq.h>
25
Gregory Bean0cc2fc12010-11-24 11:53:51 -080026#include <mach/msm_iomap.h>
Rohit Vaswania513aa8d2011-07-18 15:14:28 -070027#include <mach/gpiomux.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028#include "mpm.h"
Gregory Bean0cc2fc12010-11-24 11:53:51 -080029
30/* Bits of interest in the GPIO_IN_OUT register.
31 */
32enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033 GPIO_IN_BIT = 0,
34 GPIO_OUT_BIT = 1
Gregory Bean70cc2c02010-11-24 11:53:52 -080035};
36
37/* Bits of interest in the GPIO_INTR_STATUS register.
38 */
39enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040 INTR_STATUS_BIT = 0,
Gregory Bean0cc2fc12010-11-24 11:53:51 -080041};
42
43/* Bits of interest in the GPIO_CFG register.
44 */
45enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046 GPIO_OE_BIT = 9,
Gregory Bean0cc2fc12010-11-24 11:53:51 -080047};
48
Gregory Bean70cc2c02010-11-24 11:53:52 -080049/* Bits of interest in the GPIO_INTR_CFG register.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050 */
51enum {
52 INTR_ENABLE_BIT = 0,
53 INTR_POL_CTL_BIT = 1,
54 INTR_DECT_CTL_BIT = 2,
55 INTR_RAW_STATUS_EN_BIT = 3,
56};
57
58/* Codes of interest in GPIO_INTR_CFG_SU.
59 */
60enum {
61 TARGET_PROC_SCORPION = 4,
62 TARGET_PROC_NONE = 7,
63};
64
65/*
66 * There is no 'DC_POLARITY_LO' because the GIC is incapable
67 * of asserting on falling edge or level-low conditions. Even though
68 * the registers allow for low-polarity inputs, the case can never arise.
69 */
70enum {
71 DC_POLARITY_HI = BIT(11),
72 DC_IRQ_ENABLE = BIT(3),
73};
74
75enum msm_tlmm_register {
76 SDC4_HDRV_PULL_CTL = 0x20a0,
77 SDC3_HDRV_PULL_CTL = 0x20a4,
78 SDC1_HDRV_PULL_CTL = 0x20a0,
79};
80
81struct tlmm_field_cfg {
82 enum msm_tlmm_register reg;
83 u8 off;
84};
85
86static const struct tlmm_field_cfg tlmm_hdrv_cfgs[] = {
87 {SDC4_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC4_CLK */
88 {SDC4_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC4_CMD */
89 {SDC4_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC4_DATA */
90 {SDC3_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC3_CLK */
91 {SDC3_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC3_CMD */
92 {SDC3_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC3_DATA */
93 {SDC1_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC1_CLK */
94 {SDC1_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC1_CMD */
95 {SDC1_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC1_DATA */
96};
97
98static const struct tlmm_field_cfg tlmm_pull_cfgs[] = {
99 {SDC4_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC4_CMD */
100 {SDC4_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC4_DATA */
101 {SDC3_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC3_CLK */
102 {SDC3_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC3_CMD */
103 {SDC3_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC3_DATA */
104 {SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK */
105 {SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD */
106 {SDC1_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC1_DATA */
107};
108
109/*
110 * Supported arch specific irq extension.
111 * Default make them NULL.
112 */
113struct irq_chip msm_gpio_irq_extn = {
114 .irq_eoi = NULL,
115 .irq_mask = NULL,
116 .irq_unmask = NULL,
117 .irq_retrigger = NULL,
118 .irq_set_type = NULL,
119 .irq_set_wake = NULL,
120 .irq_disable = NULL,
121};
122
123/*
Gregory Bean70cc2c02010-11-24 11:53:52 -0800124 * When a GPIO triggers, two separate decisions are made, controlled
125 * by two separate flags.
126 *
127 * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
128 * register for that GPIO will be updated to reflect the triggering of that
129 * gpio. If this bit is 0, this register will not be updated.
130 * - Second, INTR_ENABLE controls whether an interrupt is triggered.
131 *
132 * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
133 * can be triggered but the status register will not reflect it.
134 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT)
136#define INTR_ENABLE BIT(INTR_ENABLE_BIT)
137#define INTR_DECT_CTL_EDGE BIT(INTR_DECT_CTL_BIT)
138#define INTR_POL_CTL_HI BIT(INTR_POL_CTL_BIT)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800139
140#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141#define DIR_CONN_INTR_CFG_SU(irq) (MSM_TLMM_BASE + 0x0700 + (0x04 * (irq)))
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800142#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
143#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
Gregory Bean70cc2c02010-11-24 11:53:52 -0800144#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
145#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio)))
146
147/**
148 * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
149 *
150 * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
151 * keeping track of which gpios are unmasked as irq sources, we avoid
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 * having to do __raw_readl calls on hundreds of iomapped registers each time
Gregory Bean70cc2c02010-11-24 11:53:52 -0800153 * the summary interrupt fires in order to locate the active interrupts.
154 *
155 * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
156 * as wakeup sources. When the device is suspended, interrupts which are
157 * not wakeup sources are disabled.
158 *
159 * @dual_edge_irqs: a bitmap used to track which irqs are configured
160 * as dual-edge, as this is not supported by the hardware and requires
161 * some special handling in the driver.
162 */
163struct msm_gpio_dev {
164 struct gpio_chip gpio_chip;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
166 DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
167 DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800168};
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800169
170static DEFINE_SPINLOCK(tlmm_lock);
171
Gregory Bean70cc2c02010-11-24 11:53:52 -0800172static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
173{
174 return container_of(chip, struct msm_gpio_dev, gpio_chip);
175}
176
177static inline void set_gpio_bits(unsigned n, void __iomem *reg)
178{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179 __raw_writel(__raw_readl(reg) | n, reg);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800180}
181
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800183{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 __raw_writel(__raw_readl(reg) & ~n, reg);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800185}
186
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800187static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
188{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 int rc;
190 rc = __raw_readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN_BIT);
191 mb();
192 return rc;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800193}
194
195static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
196{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 __raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(offset));
198 mb();
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800199}
200
201static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
202{
203 unsigned long irq_flags;
204
205 spin_lock_irqsave(&tlmm_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
207 mb();
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800208 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
209 return 0;
210}
211
212static int msm_gpio_direction_output(struct gpio_chip *chip,
213 unsigned offset,
214 int val)
215{
216 unsigned long irq_flags;
217
218 spin_lock_irqsave(&tlmm_lock, irq_flags);
219 msm_gpio_set(chip, offset, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220 set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
221 mb();
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800222 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
223 return 0;
224}
225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
227{
228 return MSM_GPIO_TO_INT(offset - chip->base);
229}
230
231static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
232{
233 return irq - MSM_GPIO_TO_INT(chip->base);
234}
235
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800236static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
237{
238 return msm_gpiomux_get(chip->base + offset);
239}
240
241static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
242{
243 msm_gpiomux_put(chip->base + offset);
244}
245
Gregory Bean70cc2c02010-11-24 11:53:52 -0800246static struct msm_gpio_dev msm_gpio = {
247 .gpio_chip = {
Michael Bohanfedb2432011-10-07 16:11:38 -0700248 .label = "msmgpio",
Gregory Bean70cc2c02010-11-24 11:53:52 -0800249 .base = 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 .ngpio = NR_MSM_GPIOS,
Gregory Bean70cc2c02010-11-24 11:53:52 -0800251 .direction_input = msm_gpio_direction_input,
252 .direction_output = msm_gpio_direction_output,
253 .get = msm_gpio_get,
254 .set = msm_gpio_set,
255 .to_irq = msm_gpio_to_irq,
256 .request = msm_gpio_request,
257 .free = msm_gpio_free,
258 },
259};
260
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261static void switch_mpm_config(struct irq_data *d, unsigned val)
262{
263 /* switch the configuration in the mpm as well */
264 if (!msm_gpio_irq_extn.irq_set_type)
265 return;
266
267 if (val)
268 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
269 else
270 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
271}
272
Gregory Bean70cc2c02010-11-24 11:53:52 -0800273/* For dual-edge interrupts in software, since the hardware has no
274 * such support:
275 *
276 * At appropriate moments, this function may be called to flip the polarity
277 * settings of both-edge irq lines to try and catch the next edge.
278 *
279 * The attempt is considered successful if:
280 * - the status bit goes high, indicating that an edge was caught, or
281 * - the input value of the gpio doesn't change during the attempt.
282 * If the value changes twice during the process, that would cause the first
283 * test to fail but would force the second, as two opposite
284 * transitions would cause a detection no matter the polarity setting.
285 *
286 * The do-loop tries to sledge-hammer closed the timing hole between
287 * the initial value-read and the polarity-write - if the line value changes
288 * during that window, an interrupt is lost, the new polarity setting is
289 * incorrect, and the first success test will fail, causing a retry.
290 *
291 * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
292 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800294{
295 int loop_limit = 100;
296 unsigned val, val2, intstat;
297
298 do {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 val = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800300 if (val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800302 else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303 set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
304 val2 = __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
305 intstat = __raw_readl(GPIO_INTR_STATUS(gpio)) &
306 BIT(INTR_STATUS_BIT);
307 if (intstat || val == val2) {
308 switch_mpm_config(d, val);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800309 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 }
Gregory Bean70cc2c02010-11-24 11:53:52 -0800311 } while (loop_limit-- > 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312 pr_err("%s: dual-edge irq failed to stabilize, "
Gregory Bean70cc2c02010-11-24 11:53:52 -0800313 "interrupts dropped. %#08x != %#08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 __func__, val, val2);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800315}
316
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100317static void msm_gpio_irq_ack(struct irq_data *d)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800318{
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100319 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 __raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800322 if (test_bit(gpio, msm_gpio.dual_edge_irqs))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 msm_gpio_update_dual_edge_pos(d, gpio);
324 mb();
325}
326
327static void __msm_gpio_irq_mask(unsigned int gpio)
328{
329 __raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
330 clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800331}
332
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100333static void msm_gpio_irq_mask(struct irq_data *d)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800334{
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100335 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800336 unsigned long irq_flags;
337
338 spin_lock_irqsave(&tlmm_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 __msm_gpio_irq_mask(gpio);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800340 __clear_bit(gpio, msm_gpio.enabled_irqs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341 mb();
Gregory Bean70cc2c02010-11-24 11:53:52 -0800342 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343
344 if (msm_gpio_irq_extn.irq_mask)
345 msm_gpio_irq_extn.irq_mask(d);
346
347}
348
349static void __msm_gpio_irq_unmask(unsigned int gpio)
350{
351 set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
352 __raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800353}
354
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100355static void msm_gpio_irq_unmask(struct irq_data *d)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800356{
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100357 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800358 unsigned long irq_flags;
359
360 spin_lock_irqsave(&tlmm_lock, irq_flags);
361 __set_bit(gpio, msm_gpio.enabled_irqs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 __msm_gpio_irq_unmask(gpio);
363 mb();
Gregory Bean70cc2c02010-11-24 11:53:52 -0800364 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365
366 if (msm_gpio_irq_extn.irq_mask)
367 msm_gpio_irq_extn.irq_unmask(d);
368}
369
370static void msm_gpio_irq_disable(struct irq_data *d)
371{
372 if (msm_gpio_irq_extn.irq_disable)
373 msm_gpio_irq_extn.irq_disable(d);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800374}
375
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100376static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800377{
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100378 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800379 unsigned long irq_flags;
380 uint32_t bits;
381
382 spin_lock_irqsave(&tlmm_lock, irq_flags);
383
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 bits = __raw_readl(GPIO_INTR_CFG(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800385
386 if (flow_type & IRQ_TYPE_EDGE_BOTH) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 bits |= INTR_DECT_CTL_EDGE;
Thomas Gleixner70c4fa22011-03-24 12:41:27 +0100388 __irq_set_handler_locked(d->irq, handle_edge_irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800389 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
390 __set_bit(gpio, msm_gpio.dual_edge_irqs);
391 else
392 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
393 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 bits &= ~INTR_DECT_CTL_EDGE;
Thomas Gleixner70c4fa22011-03-24 12:41:27 +0100395 __irq_set_handler_locked(d->irq, handle_level_irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800396 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
397 }
398
399 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 bits |= INTR_POL_CTL_HI;
Gregory Bean70cc2c02010-11-24 11:53:52 -0800401 else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 bits &= ~INTR_POL_CTL_HI;
Gregory Bean70cc2c02010-11-24 11:53:52 -0800403
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 __raw_writel(bits, GPIO_INTR_CFG(gpio));
Gregory Bean70cc2c02010-11-24 11:53:52 -0800405
406 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 msm_gpio_update_dual_edge_pos(d, gpio);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800408
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 mb();
Gregory Bean70cc2c02010-11-24 11:53:52 -0800410 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
411
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412 if (msm_gpio_irq_extn.irq_set_type)
413 msm_gpio_irq_extn.irq_set_type(d, flow_type);
414
Gregory Bean70cc2c02010-11-24 11:53:52 -0800415 return 0;
416}
417
418/*
419 * When the summary IRQ is raised, any number of GPIO lines may be high.
420 * It is the job of the summary handler to find all those GPIO lines
421 * which have been set as summary IRQ lines and which are triggered,
422 * and to call their interrupt handlers.
423 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424static irqreturn_t msm_summary_irq_handler(int irq, void *data)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800425{
426 unsigned long i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427 struct irq_desc *desc = irq_to_desc(irq);
Will Deacon03dd7652011-02-21 14:54:57 +0000428 struct irq_chip *chip = irq_desc_get_chip(desc);
429
430 chained_irq_enter(chip, desc);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800431
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432 for (i = find_first_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
433 i < NR_MSM_GPIOS;
434 i = find_next_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS, i + 1)) {
435 if (__raw_readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS_BIT))
Gregory Bean70cc2c02010-11-24 11:53:52 -0800436 generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
437 i));
438 }
Will Deacon03dd7652011-02-21 14:54:57 +0000439
440 chained_irq_exit(chip, desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441 return IRQ_HANDLED;
Gregory Bean70cc2c02010-11-24 11:53:52 -0800442}
443
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100444static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
Gregory Bean70cc2c02010-11-24 11:53:52 -0800445{
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100446 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800447
448 if (on) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
450 irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 1);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800451 set_bit(gpio, msm_gpio.wake_irqs);
452 } else {
453 clear_bit(gpio, msm_gpio.wake_irqs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454 if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
455 irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 0);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800456 }
457
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 if (msm_gpio_irq_extn.irq_set_wake)
459 msm_gpio_irq_extn.irq_set_wake(d, on);
460
Gregory Bean70cc2c02010-11-24 11:53:52 -0800461 return 0;
462}
463
464static struct irq_chip msm_gpio_irq_chip = {
465 .name = "msmgpio",
Thomas Gleixnercf8d1582011-03-24 11:58:31 +0100466 .irq_mask = msm_gpio_irq_mask,
467 .irq_unmask = msm_gpio_irq_unmask,
468 .irq_ack = msm_gpio_irq_ack,
469 .irq_set_type = msm_gpio_irq_set_type,
470 .irq_set_wake = msm_gpio_irq_set_wake,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471 .irq_disable = msm_gpio_irq_disable,
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800472};
473
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700474static int __devinit msm_gpio_probe(void)
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800475{
Gregory Bean70cc2c02010-11-24 11:53:52 -0800476 int i, irq, ret;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800477
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478 spin_lock_init(&tlmm_lock);
479 bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
480 bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
481 bitmap_zero(msm_gpio.dual_edge_irqs, NR_MSM_GPIOS);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800482 ret = gpiochip_add(&msm_gpio.gpio_chip);
483 if (ret < 0)
484 return ret;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800485
Gregory Bean70cc2c02010-11-24 11:53:52 -0800486 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
487 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
Thomas Gleixnerf38c02f2011-03-24 13:35:09 +0100488 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
489 handle_level_irq);
Gregory Bean70cc2c02010-11-24 11:53:52 -0800490 set_irq_flags(irq, IRQF_VALID);
491 }
492
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493 ret = request_irq(TLMM_MSM_SUMMARY_IRQ, msm_summary_irq_handler,
494 IRQF_TRIGGER_HIGH, "msmgpio", NULL);
495 if (ret) {
496 pr_err("Request_irq failed for TLMM_MSM_SUMMARY_IRQ - %d\n",
497 ret);
498 return ret;
499 }
Gregory Bean70cc2c02010-11-24 11:53:52 -0800500 return 0;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800501}
502
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503static int __devexit msm_gpio_remove(void)
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800504{
Gregory Bean70cc2c02010-11-24 11:53:52 -0800505 int ret = gpiochip_remove(&msm_gpio.gpio_chip);
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800506
507 if (ret < 0)
508 return ret;
509
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 irq_set_handler(TLMM_MSM_SUMMARY_IRQ, NULL);
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800511
512 return 0;
513}
514
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515#ifdef CONFIG_PM
516static int msm_gpio_suspend(void)
517{
518 unsigned long irq_flags;
519 unsigned long i;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800520
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 spin_lock_irqsave(&tlmm_lock, irq_flags);
522 for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
523 __msm_gpio_irq_mask(i);
524
525 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
526 __msm_gpio_irq_unmask(i);
527 mb();
528 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
529 return 0;
530}
531
532extern int msm_show_resume_irq_mask;
533
534void msm_gpio_show_resume_irq(void)
535{
536 unsigned long irq_flags;
537 int i, irq, intstat;
538
539 if (!msm_show_resume_irq_mask)
540 return;
541
542 spin_lock_irqsave(&tlmm_lock, irq_flags);
543 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS) {
544 intstat = __raw_readl(GPIO_INTR_STATUS(i)) &
545 BIT(INTR_STATUS_BIT);
546 if (intstat) {
547 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
548 pr_warning("%s: %d triggered\n",
549 __func__, irq);
550 }
551 }
552 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
553}
554
555static void msm_gpio_resume(void)
556{
557 unsigned long irq_flags;
558 unsigned long i;
559
560 msm_gpio_show_resume_irq();
561
562 spin_lock_irqsave(&tlmm_lock, irq_flags);
563 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
564 __msm_gpio_irq_mask(i);
565
566 for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
567 __msm_gpio_irq_unmask(i);
568 mb();
569 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
570}
571#else
572#define msm_gpio_suspend NULL
573#define msm_gpio_resume NULL
574#endif
575
576static struct syscore_ops msm_gpio_syscore_ops = {
577 .suspend = msm_gpio_suspend,
578 .resume = msm_gpio_resume,
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800579};
580
581static int __init msm_gpio_init(void)
582{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 msm_gpio_probe();
584 register_syscore_ops(&msm_gpio_syscore_ops);
585 return 0;
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800586}
587
588static void __exit msm_gpio_exit(void)
589{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590 unregister_syscore_ops(&msm_gpio_syscore_ops);
591 msm_gpio_remove();
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800592}
593
594postcore_initcall(msm_gpio_init);
595module_exit(msm_gpio_exit);
596
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
598 unsigned id, unsigned width, unsigned val)
599{
600 unsigned long irqflags;
601 u32 mask = (1 << width) - 1;
602 u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
603 u32 reg_val;
604
605 spin_lock_irqsave(&tlmm_lock, irqflags);
606 reg_val = __raw_readl(reg);
607 reg_val &= ~(mask << configs[id].off);
608 reg_val |= (val & mask) << configs[id].off;
609 __raw_writel(reg_val, reg);
610 mb();
611 spin_unlock_irqrestore(&tlmm_lock, irqflags);
612}
613
614void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
615{
616 msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
617}
618EXPORT_SYMBOL(msm_tlmm_set_hdrive);
619
620void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
621{
622 msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
623}
624EXPORT_SYMBOL(msm_tlmm_set_pull);
625
626int gpio_tlmm_config(unsigned config, unsigned disable)
627{
628 uint32_t flags;
629 unsigned gpio = GPIO_PIN(config);
630
631 if (gpio > NR_MSM_GPIOS)
632 return -EINVAL;
633
634 flags = ((GPIO_DIR(config) << 9) & (0x1 << 9)) |
635 ((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
636 ((GPIO_FUNC(config) << 2) & (0xf << 2)) |
637 ((GPIO_PULL(config) & 0x3));
638 __raw_writel(flags, GPIO_CONFIG(gpio));
639 mb();
640
641 return 0;
642}
643EXPORT_SYMBOL(gpio_tlmm_config);
644
645int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
646 unsigned int input_polarity)
647{
648 unsigned long irq_flags;
649 uint32_t bits;
650
651 if (gpio >= NR_MSM_GPIOS || irq >= NR_TLMM_MSM_DIR_CONN_IRQ)
652 return -EINVAL;
653
654 spin_lock_irqsave(&tlmm_lock, irq_flags);
655
656 __raw_writel(__raw_readl(GPIO_CONFIG(gpio)) | BIT(GPIO_OE_BIT),
657 GPIO_CONFIG(gpio));
658 __raw_writel(__raw_readl(GPIO_INTR_CFG(gpio)) &
659 ~(INTR_RAW_STATUS_EN | INTR_ENABLE),
660 GPIO_INTR_CFG(gpio));
661 __raw_writel(DC_IRQ_ENABLE | TARGET_PROC_NONE,
662 GPIO_INTR_CFG_SU(gpio));
663
664 bits = TARGET_PROC_SCORPION | (gpio << 3);
665 if (input_polarity)
666 bits |= DC_POLARITY_HI;
667 __raw_writel(bits, DIR_CONN_INTR_CFG_SU(irq));
668
669 mb();
670 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
671
672 return 0;
673}
674EXPORT_SYMBOL(msm_gpio_install_direct_irq);
675
Gregory Bean0cc2fc12010-11-24 11:53:51 -0800676MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
677MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
678MODULE_LICENSE("GPL v2");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679MODULE_ALIAS("sysdev:msmgpio");