blob: 3332fc5b159f37e22bb2347b2024417ef432ec5e [file] [log] [blame]
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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 *
12 */
13#include <linux/bitmap.h>
14#include <linux/bitops.h>
15#include <linux/gpio.h>
16#include <linux/init.h>
17#include <linux/interrupt.h>
18#include <linux/irq.h>
19#include <linux/io.h>
20#include <linux/module.h>
21#include <linux/spinlock.h>
22#include <linux/syscore_ops.h>
23#include <linux/irqdomain.h>
24#include <linux/of.h>
25#include <linux/err.h>
Rohit Vaswanib1cc4932012-07-23 21:30:11 -070026#include <linux/platform_device.h>
Rohit Vaswani341c2032012-11-08 18:49:29 -080027#include <linux/slab.h>
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070028
29#include <asm/mach/irq.h>
30
31#include <mach/msm_iomap.h>
32#include <mach/gpiomux.h>
33#include <mach/mpm.h>
34#include "gpio-msm-common.h"
35
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053036#ifdef CONFIG_GPIO_MSM_V3
37enum msm_tlmm_register {
38 SDC4_HDRV_PULL_CTL = 0x0, /* NOT USED */
39 SDC3_HDRV_PULL_CTL = 0x0, /* NOT USED */
40 SDC2_HDRV_PULL_CTL = 0x2048,
41 SDC1_HDRV_PULL_CTL = 0x2044,
42};
43#else
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070044enum msm_tlmm_register {
45 SDC4_HDRV_PULL_CTL = 0x20a0,
46 SDC3_HDRV_PULL_CTL = 0x20a4,
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053047 SDC2_HDRV_PULL_CTL = 0x0, /* NOT USED */
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070048 SDC1_HDRV_PULL_CTL = 0x20a0,
49};
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053050#endif
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070051
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -080052static int tlmm_msm_summary_irq, nr_direct_connect_irqs;
Rohit Vaswanid2001522012-12-05 19:23:44 -080053
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070054struct tlmm_field_cfg {
55 enum msm_tlmm_register reg;
56 u8 off;
57};
58
59static const struct tlmm_field_cfg tlmm_hdrv_cfgs[] = {
60 {SDC4_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC4_CLK */
61 {SDC4_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC4_CMD */
62 {SDC4_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC4_DATA */
63 {SDC3_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC3_CLK */
64 {SDC3_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC3_CMD */
65 {SDC3_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC3_DATA */
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053066 {SDC2_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC2_CLK */
67 {SDC2_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC2_CMD */
68 {SDC2_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC2_DATA */
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070069 {SDC1_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC1_CLK */
70 {SDC1_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC1_CMD */
71 {SDC1_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC1_DATA */
72};
73
74static const struct tlmm_field_cfg tlmm_pull_cfgs[] = {
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053075 {SDC4_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC4_CLK */
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070076 {SDC4_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC4_CMD */
77 {SDC4_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC4_DATA */
78 {SDC3_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC3_CLK */
79 {SDC3_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC3_CMD */
80 {SDC3_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC3_DATA */
Sujit Reddy Thumma39306c22012-06-26 15:39:26 +053081 {SDC2_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC2_CLK */
82 {SDC2_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC2_CMD */
83 {SDC2_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC2_DATA */
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070084 {SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK */
85 {SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD */
86 {SDC1_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC1_DATA */
87};
88
89/*
90 * Supported arch specific irq extension.
91 * Default make them NULL.
92 */
93struct irq_chip msm_gpio_irq_extn = {
94 .irq_eoi = NULL,
95 .irq_mask = NULL,
96 .irq_unmask = NULL,
97 .irq_retrigger = NULL,
98 .irq_set_type = NULL,
99 .irq_set_wake = NULL,
100 .irq_disable = NULL,
101};
102
103/**
104 * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
105 *
106 * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
107 * keeping track of which gpios are unmasked as irq sources, we avoid
108 * having to do __raw_readl calls on hundreds of iomapped registers each time
109 * the summary interrupt fires in order to locate the active interrupts.
110 *
111 * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
112 * as wakeup sources. When the device is suspended, interrupts which are
113 * not wakeup sources are disabled.
114 *
115 * @dual_edge_irqs: a bitmap used to track which irqs are configured
116 * as dual-edge, as this is not supported by the hardware and requires
117 * some special handling in the driver.
118 */
119struct msm_gpio_dev {
120 struct gpio_chip gpio_chip;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800121 unsigned long *enabled_irqs;
122 unsigned long *wake_irqs;
123 unsigned long *dual_edge_irqs;
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700124 struct irq_domain *domain;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700125};
126
127static DEFINE_SPINLOCK(tlmm_lock);
128
129static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
130{
131 return container_of(chip, struct msm_gpio_dev, gpio_chip);
132}
133
134static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
135{
136 int rc;
137 rc = __msm_gpio_get_inout(offset);
138 mb();
139 return rc;
140}
141
142static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
143{
144 __msm_gpio_set_inout(offset, val);
145 mb();
146}
147
148static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
149{
150 unsigned long irq_flags;
151
152 spin_lock_irqsave(&tlmm_lock, irq_flags);
153 __msm_gpio_set_config_direction(offset, 1, 0);
154 mb();
155 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
156 return 0;
157}
158
159static int msm_gpio_direction_output(struct gpio_chip *chip,
160 unsigned offset,
161 int val)
162{
163 unsigned long irq_flags;
164
165 spin_lock_irqsave(&tlmm_lock, irq_flags);
166 __msm_gpio_set_config_direction(offset, 0, val);
167 mb();
168 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
169 return 0;
170}
171
172#ifdef CONFIG_OF
173static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
174{
175 struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700176 struct irq_domain *domain = g_dev->domain;
Michael Bohan27e7b942012-07-06 10:25:30 -0700177 return irq_linear_revmap(domain, offset);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700178}
179
180static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
181{
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700182 struct irq_data *irq_data = irq_get_irq_data(irq);
183 return irq_data->hwirq;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700184}
185#else
186static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
187{
188 return MSM_GPIO_TO_INT(offset - chip->base);
189}
190
191static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
192{
193 return irq - MSM_GPIO_TO_INT(chip->base);
194}
195#endif
196
197static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
198{
199 return msm_gpiomux_get(chip->base + offset);
200}
201
202static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
203{
204 msm_gpiomux_put(chip->base + offset);
205}
206
207static struct msm_gpio_dev msm_gpio = {
208 .gpio_chip = {
209 .label = "msmgpio",
210 .base = 0,
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700211 .direction_input = msm_gpio_direction_input,
212 .direction_output = msm_gpio_direction_output,
213 .get = msm_gpio_get,
214 .set = msm_gpio_set,
215 .to_irq = msm_gpio_to_irq,
216 .request = msm_gpio_request,
217 .free = msm_gpio_free,
218 },
219};
220
221static void switch_mpm_config(struct irq_data *d, unsigned val)
222{
223 /* switch the configuration in the mpm as well */
224 if (!msm_gpio_irq_extn.irq_set_type)
225 return;
226
227 if (val)
228 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
229 else
230 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
231}
232
233/* For dual-edge interrupts in software, since the hardware has no
234 * such support:
235 *
236 * At appropriate moments, this function may be called to flip the polarity
237 * settings of both-edge irq lines to try and catch the next edge.
238 *
239 * The attempt is considered successful if:
240 * - the status bit goes high, indicating that an edge was caught, or
241 * - the input value of the gpio doesn't change during the attempt.
242 * If the value changes twice during the process, that would cause the first
243 * test to fail but would force the second, as two opposite
244 * transitions would cause a detection no matter the polarity setting.
245 *
246 * The do-loop tries to sledge-hammer closed the timing hole between
247 * the initial value-read and the polarity-write - if the line value changes
248 * during that window, an interrupt is lost, the new polarity setting is
249 * incorrect, and the first success test will fail, causing a retry.
250 *
251 * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
252 */
253static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
254{
255 int loop_limit = 100;
256 unsigned val, val2, intstat;
257
258 do {
259 val = __msm_gpio_get_inout(gpio);
260 __msm_gpio_set_polarity(gpio, val);
261 val2 = __msm_gpio_get_inout(gpio);
262 intstat = __msm_gpio_get_intr_status(gpio);
263 if (intstat || val == val2) {
264 switch_mpm_config(d, val);
265 return;
266 }
267 } while (loop_limit-- > 0);
268 pr_err("%s: dual-edge irq failed to stabilize, %#08x != %#08x\n",
269 __func__, val, val2);
270}
271
272static void msm_gpio_irq_ack(struct irq_data *d)
273{
274 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
275
276 __msm_gpio_set_intr_status(gpio);
277 if (test_bit(gpio, msm_gpio.dual_edge_irqs))
278 msm_gpio_update_dual_edge_pos(d, gpio);
279 mb();
280}
281
282static void msm_gpio_irq_mask(struct irq_data *d)
283{
284 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
285 unsigned long irq_flags;
286
287 spin_lock_irqsave(&tlmm_lock, irq_flags);
288 __msm_gpio_set_intr_cfg_enable(gpio, 0);
289 __clear_bit(gpio, msm_gpio.enabled_irqs);
290 mb();
291 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
292
293 if (msm_gpio_irq_extn.irq_mask)
294 msm_gpio_irq_extn.irq_mask(d);
295
296}
297
298static void msm_gpio_irq_unmask(struct irq_data *d)
299{
300 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
301 unsigned long irq_flags;
302
303 spin_lock_irqsave(&tlmm_lock, irq_flags);
304 __set_bit(gpio, msm_gpio.enabled_irqs);
Rohit Vaswanif4f36582012-08-28 11:49:11 -0700305 if (!__msm_gpio_get_intr_cfg_enable(gpio)) {
306 __msm_gpio_set_intr_status(gpio);
307 __msm_gpio_set_intr_cfg_enable(gpio, 1);
308 mb();
309 }
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700310 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
311
312 if (msm_gpio_irq_extn.irq_mask)
313 msm_gpio_irq_extn.irq_unmask(d);
314}
315
316static void msm_gpio_irq_disable(struct irq_data *d)
317{
318 if (msm_gpio_irq_extn.irq_disable)
319 msm_gpio_irq_extn.irq_disable(d);
320}
321
322static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
323{
324 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
325 unsigned long irq_flags;
326
327 spin_lock_irqsave(&tlmm_lock, irq_flags);
328
329 if (flow_type & IRQ_TYPE_EDGE_BOTH) {
330 __irq_set_handler_locked(d->irq, handle_edge_irq);
331 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
332 __set_bit(gpio, msm_gpio.dual_edge_irqs);
333 else
334 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
335 } else {
336 __irq_set_handler_locked(d->irq, handle_level_irq);
337 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
338 }
339
340 __msm_gpio_set_intr_cfg_type(gpio, flow_type);
341
342 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
343 msm_gpio_update_dual_edge_pos(d, gpio);
344
345 mb();
346 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
347
348 if (msm_gpio_irq_extn.irq_set_type)
349 msm_gpio_irq_extn.irq_set_type(d, flow_type);
350
351 return 0;
352}
353
354/*
355 * When the summary IRQ is raised, any number of GPIO lines may be high.
356 * It is the job of the summary handler to find all those GPIO lines
357 * which have been set as summary IRQ lines and which are triggered,
358 * and to call their interrupt handlers.
359 */
360static irqreturn_t msm_summary_irq_handler(int irq, void *data)
361{
362 unsigned long i;
363 struct irq_desc *desc = irq_to_desc(irq);
364 struct irq_chip *chip = irq_desc_get_chip(desc);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800365 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700366
367 chained_irq_enter(chip, desc);
368
Rohit Vaswani341c2032012-11-08 18:49:29 -0800369 for (i = find_first_bit(msm_gpio.enabled_irqs, ngpio);
370 i < ngpio;
371 i = find_next_bit(msm_gpio.enabled_irqs, ngpio, i + 1)) {
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700372 if (__msm_gpio_get_intr_status(i))
373 generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
374 i));
375 }
376
377 chained_irq_exit(chip, desc);
378 return IRQ_HANDLED;
379}
380
381static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
382{
383 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800384 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700385
386 if (on) {
Rohit Vaswani341c2032012-11-08 18:49:29 -0800387 if (bitmap_empty(msm_gpio.wake_irqs, ngpio))
Rohit Vaswanid2001522012-12-05 19:23:44 -0800388 irq_set_irq_wake(tlmm_msm_summary_irq, 1);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700389 set_bit(gpio, msm_gpio.wake_irqs);
390 } else {
391 clear_bit(gpio, msm_gpio.wake_irqs);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800392 if (bitmap_empty(msm_gpio.wake_irqs, ngpio))
Rohit Vaswanid2001522012-12-05 19:23:44 -0800393 irq_set_irq_wake(tlmm_msm_summary_irq, 0);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700394 }
395
396 if (msm_gpio_irq_extn.irq_set_wake)
397 msm_gpio_irq_extn.irq_set_wake(d, on);
398
399 return 0;
400}
401
402static struct irq_chip msm_gpio_irq_chip = {
403 .name = "msmgpio",
404 .irq_mask = msm_gpio_irq_mask,
405 .irq_unmask = msm_gpio_irq_unmask,
406 .irq_ack = msm_gpio_irq_ack,
407 .irq_set_type = msm_gpio_irq_set_type,
408 .irq_set_wake = msm_gpio_irq_set_wake,
409 .irq_disable = msm_gpio_irq_disable,
410};
411
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700412#ifdef CONFIG_PM
413static int msm_gpio_suspend(void)
414{
415 unsigned long irq_flags;
416 unsigned long i;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800417 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700418
419 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800420 for_each_set_bit(i, msm_gpio.enabled_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700421 __msm_gpio_set_intr_cfg_enable(i, 0);
422
Rohit Vaswani341c2032012-11-08 18:49:29 -0800423 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700424 __msm_gpio_set_intr_cfg_enable(i, 1);
425 mb();
426 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
427 return 0;
428}
429
430void msm_gpio_show_resume_irq(void)
431{
432 unsigned long irq_flags;
433 int i, irq, intstat;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800434 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700435
436 if (!msm_show_resume_irq_mask)
437 return;
438
439 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800440 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio) {
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700441 intstat = __msm_gpio_get_intr_status(i);
442 if (intstat) {
443 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
444 pr_warning("%s: %d triggered\n",
445 __func__, irq);
446 }
447 }
448 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
449}
450
451static void msm_gpio_resume(void)
452{
453 unsigned long irq_flags;
454 unsigned long i;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800455 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700456
457 msm_gpio_show_resume_irq();
458
459 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800460 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700461 __msm_gpio_set_intr_cfg_enable(i, 0);
462
Rohit Vaswani341c2032012-11-08 18:49:29 -0800463 for_each_set_bit(i, msm_gpio.enabled_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700464 __msm_gpio_set_intr_cfg_enable(i, 1);
465 mb();
466 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
467}
468#else
469#define msm_gpio_suspend NULL
470#define msm_gpio_resume NULL
471#endif
472
473static struct syscore_ops msm_gpio_syscore_ops = {
474 .suspend = msm_gpio_suspend,
475 .resume = msm_gpio_resume,
476};
477
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700478static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
479 unsigned id, unsigned width, unsigned val)
480{
481 unsigned long irqflags;
482 u32 mask = (1 << width) - 1;
483 u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
484 u32 reg_val;
485
486 spin_lock_irqsave(&tlmm_lock, irqflags);
487 reg_val = __raw_readl(reg);
488 reg_val &= ~(mask << configs[id].off);
489 reg_val |= (val & mask) << configs[id].off;
490 __raw_writel(reg_val, reg);
491 mb();
492 spin_unlock_irqrestore(&tlmm_lock, irqflags);
493}
494
495void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
496{
497 msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
498}
499EXPORT_SYMBOL(msm_tlmm_set_hdrive);
500
501void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
502{
503 msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
504}
505EXPORT_SYMBOL(msm_tlmm_set_pull);
506
507int gpio_tlmm_config(unsigned config, unsigned disable)
508{
509 unsigned gpio = GPIO_PIN(config);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800510 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700511
Rohit Vaswani341c2032012-11-08 18:49:29 -0800512 if (gpio > ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700513 return -EINVAL;
514
515 __gpio_tlmm_config(config);
516 mb();
517
518 return 0;
519}
520EXPORT_SYMBOL(gpio_tlmm_config);
521
522int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
523 unsigned int input_polarity)
524{
525 unsigned long irq_flags;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800526 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700527
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800528 if (gpio >= ngpio || irq >= nr_direct_connect_irqs)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700529 return -EINVAL;
530
531 spin_lock_irqsave(&tlmm_lock, irq_flags);
532 __msm_gpio_install_direct_irq(gpio, irq, input_polarity);
533 mb();
534 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
535
536 return 0;
537}
538EXPORT_SYMBOL(msm_gpio_install_direct_irq);
539
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700540/*
541 * This lock class tells lockdep that GPIO irqs are in a different
542 * category than their parent, so it won't report false recursion.
543 */
544static struct lock_class_key msm_gpio_lock_class;
545
Rohit Vaswani341c2032012-11-08 18:49:29 -0800546static inline void msm_gpio_set_irq_handler(struct device *dev)
547{
548 int irq, i;
549
550 if (!dev->of_node) {
551 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
552 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
553 irq_set_lockdep_class(irq, &msm_gpio_lock_class);
554 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
555 handle_level_irq);
556 set_irq_flags(irq, IRQF_VALID);
557 }
558 }
559}
560
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700561static int __devinit msm_gpio_probe(struct platform_device *pdev)
562{
Rohit Vaswani341c2032012-11-08 18:49:29 -0800563 int ret, ngpio = 0;
564 struct msm_gpio_pdata *pdata = pdev->dev.platform_data;
565
566 if (pdev->dev.of_node) {
567 ret = of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio);
568 if (ret) {
569 pr_err("%s: Failed to find ngpio property\n", __func__);
570 return ret;
571 }
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800572 ret = of_property_read_u32(pdev->dev.of_node,
573 "qcom,direct-connect-irqs",
574 &nr_direct_connect_irqs);
575 if (ret) {
576 pr_err("%s: Failed to find qcom,direct-connect-irqs property\n"
577 , __func__);
578 return ret;
579 }
Rohit Vaswani341c2032012-11-08 18:49:29 -0800580 } else {
581 ngpio = pdata->ngpio;
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800582 nr_direct_connect_irqs = pdata->direct_connect_irqs;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800583 }
584
Rohit Vaswanid2001522012-12-05 19:23:44 -0800585 tlmm_msm_summary_irq = platform_get_irq(pdev, 0);
586 if (tlmm_msm_summary_irq < 0) {
587 pr_err("%s: No interrupt defined for msmgpio\n", __func__);
588 return -ENXIO;
589 }
Rohit Vaswani341c2032012-11-08 18:49:29 -0800590
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700591 msm_gpio.gpio_chip.dev = &pdev->dev;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800592 msm_gpio.gpio_chip.ngpio = ngpio;
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700593 spin_lock_init(&tlmm_lock);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800594 msm_gpio.enabled_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
595 * BITS_TO_LONGS(ngpio), GFP_KERNEL);
596 if (!msm_gpio.enabled_irqs) {
597 dev_err(&pdev->dev, "%s failed to allocated enabled_irqs bitmap\n"
598 , __func__);
599 return -ENOMEM;
600 }
601
602 msm_gpio.wake_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long) *
603 BITS_TO_LONGS(ngpio), GFP_KERNEL);
604 if (!msm_gpio.wake_irqs) {
605 dev_err(&pdev->dev, "%s failed to allocated wake_irqs bitmap\n"
606 , __func__);
607 return -ENOMEM;
608 }
609 msm_gpio.dual_edge_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
610 * BITS_TO_LONGS(ngpio), GFP_KERNEL);
611 if (!msm_gpio.dual_edge_irqs) {
612 dev_err(&pdev->dev, "%s failed to allocated dual_edge_irqs bitmap\n"
613 , __func__);
614 return -ENOMEM;
615 }
616
617 bitmap_zero(msm_gpio.enabled_irqs, ngpio);
618 bitmap_zero(msm_gpio.wake_irqs, ngpio);
619 bitmap_zero(msm_gpio.dual_edge_irqs, ngpio);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700620 ret = gpiochip_add(&msm_gpio.gpio_chip);
621 if (ret < 0)
622 return ret;
623
Rohit Vaswani341c2032012-11-08 18:49:29 -0800624 msm_gpio_set_irq_handler(&pdev->dev);
625
626 ret = devm_request_irq(&pdev->dev, tlmm_msm_summary_irq,
627 msm_summary_irq_handler, IRQF_TRIGGER_HIGH,
628 "msmgpio", NULL);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700629 if (ret) {
Rohit Vaswanid2001522012-12-05 19:23:44 -0800630 pr_err("Request_irq failed for tlmm_msm_summary_irq - %d\n",
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700631 ret);
632 return ret;
633 }
634 register_syscore_ops(&msm_gpio_syscore_ops);
635 return 0;
636}
637
638#ifdef CONFIG_OF
639static struct of_device_id msm_gpio_of_match[] __devinitdata = {
640 {.compatible = "qcom,msm-gpio", },
641 { },
642};
643#endif
644
645static int __devexit msm_gpio_remove(struct platform_device *pdev)
646{
647 int ret;
648
649 unregister_syscore_ops(&msm_gpio_syscore_ops);
650 ret = gpiochip_remove(&msm_gpio.gpio_chip);
651 if (ret < 0)
652 return ret;
Rohit Vaswanid2001522012-12-05 19:23:44 -0800653 irq_set_handler(tlmm_msm_summary_irq, NULL);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700654
655 return 0;
656}
657
658static struct platform_driver msm_gpio_driver = {
659 .probe = msm_gpio_probe,
660 .remove = __devexit_p(msm_gpio_remove),
661 .driver = {
662 .name = "msmgpio",
663 .owner = THIS_MODULE,
664 .of_match_table = of_match_ptr(msm_gpio_of_match),
665 },
666};
667
668static void __exit msm_gpio_exit(void)
669{
670 platform_driver_unregister(&msm_gpio_driver);
671}
672module_exit(msm_gpio_exit);
673
674static int __init msm_gpio_init(void)
675{
676 return platform_driver_register(&msm_gpio_driver);
677}
678postcore_initcall(msm_gpio_init);
679
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700680#ifdef CONFIG_OF
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700681static int msm_gpio_irq_domain_xlate(struct irq_domain *d,
682 struct device_node *controller,
683 const u32 *intspec,
684 unsigned int intsize,
685 unsigned long *out_hwirq,
686 unsigned int *out_type)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700687{
688 if (d->of_node != controller)
689 return -EINVAL;
690 if (intsize != 2)
691 return -EINVAL;
692
693 /* hwirq value */
694 *out_hwirq = intspec[0];
695
696 /* irq flags */
697 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
698 return 0;
699}
700
Michael Bohan27e7b942012-07-06 10:25:30 -0700701static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
702 irq_hw_number_t hwirq)
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700703{
Michael Bohan27e7b942012-07-06 10:25:30 -0700704 irq_set_lockdep_class(irq, &msm_gpio_lock_class);
705 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
706 handle_level_irq);
707 set_irq_flags(irq, IRQF_VALID);
708
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700709 return 0;
710}
711
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700712static struct irq_domain_ops msm_gpio_irq_domain_ops = {
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700713 .xlate = msm_gpio_irq_domain_xlate,
714 .map = msm_gpio_irq_domain_map,
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700715};
716
717int __init msm_gpio_of_init(struct device_node *node,
718 struct device_node *parent)
719{
Rohit Vaswani341c2032012-11-08 18:49:29 -0800720 int ngpio, ret;
721
722 ret = of_property_read_u32(node, "ngpio", &ngpio);
723 if (ret) {
724 WARN(1, "Cannot get numgpios from device tree\n");
725 return ret;
726 }
727 msm_gpio.domain = irq_domain_add_linear(node, ngpio,
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700728 &msm_gpio_irq_domain_ops, &msm_gpio);
729 if (!msm_gpio.domain) {
730 WARN(1, "Cannot allocate irq_domain\n");
731 return -ENOMEM;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700732 }
733
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700734 return 0;
735}
736#endif
737
738MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
739MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
740MODULE_LICENSE("GPL v2");
741MODULE_ALIAS("sysdev:msmgpio");