blob: 3d188096d7644bb1225f47c7dfc1a3685c7b0c12 [file] [log] [blame]
Rohit Vaswani10379f82013-03-09 21:51:11 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -07002 *
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 */
Venkat Gopalakrishnan4a814ab2013-07-19 19:56:54 -070087 {SDC1_HDRV_PULL_CTL, 15}, /* TLMM_PULL_SDC1_RCLK */
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -070088};
89
90/*
91 * Supported arch specific irq extension.
92 * Default make them NULL.
93 */
94struct irq_chip msm_gpio_irq_extn = {
95 .irq_eoi = NULL,
96 .irq_mask = NULL,
97 .irq_unmask = NULL,
98 .irq_retrigger = NULL,
99 .irq_set_type = NULL,
100 .irq_set_wake = NULL,
101 .irq_disable = NULL,
102};
103
104/**
105 * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
106 *
107 * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
108 * keeping track of which gpios are unmasked as irq sources, we avoid
109 * having to do __raw_readl calls on hundreds of iomapped registers each time
110 * the summary interrupt fires in order to locate the active interrupts.
111 *
112 * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
113 * as wakeup sources. When the device is suspended, interrupts which are
114 * not wakeup sources are disabled.
115 *
116 * @dual_edge_irqs: a bitmap used to track which irqs are configured
117 * as dual-edge, as this is not supported by the hardware and requires
118 * some special handling in the driver.
119 */
120struct msm_gpio_dev {
121 struct gpio_chip gpio_chip;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800122 unsigned long *enabled_irqs;
123 unsigned long *wake_irqs;
124 unsigned long *dual_edge_irqs;
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700125 struct irq_domain *domain;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700126};
127
128static DEFINE_SPINLOCK(tlmm_lock);
129
130static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
131{
132 return container_of(chip, struct msm_gpio_dev, gpio_chip);
133}
134
135static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
136{
137 int rc;
138 rc = __msm_gpio_get_inout(offset);
139 mb();
140 return rc;
141}
142
143static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
144{
145 __msm_gpio_set_inout(offset, val);
146 mb();
147}
148
149static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
150{
151 unsigned long irq_flags;
152
153 spin_lock_irqsave(&tlmm_lock, irq_flags);
154 __msm_gpio_set_config_direction(offset, 1, 0);
155 mb();
156 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
157 return 0;
158}
159
160static int msm_gpio_direction_output(struct gpio_chip *chip,
161 unsigned offset,
162 int val)
163{
164 unsigned long irq_flags;
165
166 spin_lock_irqsave(&tlmm_lock, irq_flags);
167 __msm_gpio_set_config_direction(offset, 0, val);
168 mb();
169 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
170 return 0;
171}
172
173#ifdef CONFIG_OF
174static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
175{
176 struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700177 struct irq_domain *domain = g_dev->domain;
Rohit Vaswani10379f82013-03-09 21:51:11 -0800178 return irq_create_mapping(domain, offset);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700179}
180
181static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
182{
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700183 struct irq_data *irq_data = irq_get_irq_data(irq);
184 return irq_data->hwirq;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700185}
186#else
187static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
188{
189 return MSM_GPIO_TO_INT(offset - chip->base);
190}
191
192static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
193{
194 return irq - MSM_GPIO_TO_INT(chip->base);
195}
196#endif
197
198static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
199{
200 return msm_gpiomux_get(chip->base + offset);
201}
202
203static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
204{
205 msm_gpiomux_put(chip->base + offset);
206}
207
208static struct msm_gpio_dev msm_gpio = {
209 .gpio_chip = {
210 .label = "msmgpio",
211 .base = 0,
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700212 .direction_input = msm_gpio_direction_input,
213 .direction_output = msm_gpio_direction_output,
214 .get = msm_gpio_get,
215 .set = msm_gpio_set,
216 .to_irq = msm_gpio_to_irq,
217 .request = msm_gpio_request,
218 .free = msm_gpio_free,
219 },
220};
221
222static void switch_mpm_config(struct irq_data *d, unsigned val)
223{
224 /* switch the configuration in the mpm as well */
225 if (!msm_gpio_irq_extn.irq_set_type)
226 return;
227
228 if (val)
229 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
230 else
231 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
232}
233
234/* For dual-edge interrupts in software, since the hardware has no
235 * such support:
236 *
237 * At appropriate moments, this function may be called to flip the polarity
238 * settings of both-edge irq lines to try and catch the next edge.
239 *
240 * The attempt is considered successful if:
241 * - the status bit goes high, indicating that an edge was caught, or
242 * - the input value of the gpio doesn't change during the attempt.
243 * If the value changes twice during the process, that would cause the first
244 * test to fail but would force the second, as two opposite
245 * transitions would cause a detection no matter the polarity setting.
246 *
247 * The do-loop tries to sledge-hammer closed the timing hole between
248 * the initial value-read and the polarity-write - if the line value changes
249 * during that window, an interrupt is lost, the new polarity setting is
250 * incorrect, and the first success test will fail, causing a retry.
251 *
252 * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
253 */
254static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
255{
256 int loop_limit = 100;
257 unsigned val, val2, intstat;
258
259 do {
260 val = __msm_gpio_get_inout(gpio);
261 __msm_gpio_set_polarity(gpio, val);
262 val2 = __msm_gpio_get_inout(gpio);
263 intstat = __msm_gpio_get_intr_status(gpio);
264 if (intstat || val == val2) {
265 switch_mpm_config(d, val);
266 return;
267 }
268 } while (loop_limit-- > 0);
269 pr_err("%s: dual-edge irq failed to stabilize, %#08x != %#08x\n",
270 __func__, val, val2);
271}
272
273static void msm_gpio_irq_ack(struct irq_data *d)
274{
275 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
276
277 __msm_gpio_set_intr_status(gpio);
278 if (test_bit(gpio, msm_gpio.dual_edge_irqs))
279 msm_gpio_update_dual_edge_pos(d, gpio);
280 mb();
281}
282
283static void msm_gpio_irq_mask(struct irq_data *d)
284{
285 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
286 unsigned long irq_flags;
287
288 spin_lock_irqsave(&tlmm_lock, irq_flags);
289 __msm_gpio_set_intr_cfg_enable(gpio, 0);
290 __clear_bit(gpio, msm_gpio.enabled_irqs);
291 mb();
292 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
293
294 if (msm_gpio_irq_extn.irq_mask)
295 msm_gpio_irq_extn.irq_mask(d);
296
297}
298
299static void msm_gpio_irq_unmask(struct irq_data *d)
300{
301 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
302 unsigned long irq_flags;
303
304 spin_lock_irqsave(&tlmm_lock, irq_flags);
305 __set_bit(gpio, msm_gpio.enabled_irqs);
Rohit Vaswanif4f36582012-08-28 11:49:11 -0700306 if (!__msm_gpio_get_intr_cfg_enable(gpio)) {
307 __msm_gpio_set_intr_status(gpio);
308 __msm_gpio_set_intr_cfg_enable(gpio, 1);
309 mb();
310 }
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700311 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
312
313 if (msm_gpio_irq_extn.irq_mask)
314 msm_gpio_irq_extn.irq_unmask(d);
315}
316
317static void msm_gpio_irq_disable(struct irq_data *d)
318{
319 if (msm_gpio_irq_extn.irq_disable)
320 msm_gpio_irq_extn.irq_disable(d);
321}
322
323static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
324{
325 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
326 unsigned long irq_flags;
327
328 spin_lock_irqsave(&tlmm_lock, irq_flags);
329
330 if (flow_type & IRQ_TYPE_EDGE_BOTH) {
331 __irq_set_handler_locked(d->irq, handle_edge_irq);
332 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
333 __set_bit(gpio, msm_gpio.dual_edge_irqs);
334 else
335 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
336 } else {
337 __irq_set_handler_locked(d->irq, handle_level_irq);
338 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
339 }
340
341 __msm_gpio_set_intr_cfg_type(gpio, flow_type);
342
343 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
344 msm_gpio_update_dual_edge_pos(d, gpio);
345
346 mb();
347 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
348
Taniya Dase794d212013-01-22 11:01:55 +0530349 if ((flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) {
350 if (msm_gpio_irq_extn.irq_set_type)
351 msm_gpio_irq_extn.irq_set_type(d, flow_type);
352 }
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700353
354 return 0;
355}
356
357/*
358 * When the summary IRQ is raised, any number of GPIO lines may be high.
359 * It is the job of the summary handler to find all those GPIO lines
360 * which have been set as summary IRQ lines and which are triggered,
361 * and to call their interrupt handlers.
362 */
363static irqreturn_t msm_summary_irq_handler(int irq, void *data)
364{
365 unsigned long i;
366 struct irq_desc *desc = irq_to_desc(irq);
367 struct irq_chip *chip = irq_desc_get_chip(desc);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800368 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700369
370 chained_irq_enter(chip, desc);
371
Rohit Vaswani341c2032012-11-08 18:49:29 -0800372 for (i = find_first_bit(msm_gpio.enabled_irqs, ngpio);
373 i < ngpio;
374 i = find_next_bit(msm_gpio.enabled_irqs, ngpio, i + 1)) {
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700375 if (__msm_gpio_get_intr_status(i))
376 generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
377 i));
378 }
379
380 chained_irq_exit(chip, desc);
381 return IRQ_HANDLED;
382}
383
384static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
385{
386 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800387 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700388
389 if (on) {
Rohit Vaswani341c2032012-11-08 18:49:29 -0800390 if (bitmap_empty(msm_gpio.wake_irqs, ngpio))
Rohit Vaswanid2001522012-12-05 19:23:44 -0800391 irq_set_irq_wake(tlmm_msm_summary_irq, 1);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700392 set_bit(gpio, msm_gpio.wake_irqs);
393 } else {
394 clear_bit(gpio, msm_gpio.wake_irqs);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800395 if (bitmap_empty(msm_gpio.wake_irqs, ngpio))
Rohit Vaswanid2001522012-12-05 19:23:44 -0800396 irq_set_irq_wake(tlmm_msm_summary_irq, 0);
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700397 }
398
399 if (msm_gpio_irq_extn.irq_set_wake)
400 msm_gpio_irq_extn.irq_set_wake(d, on);
401
402 return 0;
403}
404
405static struct irq_chip msm_gpio_irq_chip = {
406 .name = "msmgpio",
407 .irq_mask = msm_gpio_irq_mask,
408 .irq_unmask = msm_gpio_irq_unmask,
409 .irq_ack = msm_gpio_irq_ack,
410 .irq_set_type = msm_gpio_irq_set_type,
411 .irq_set_wake = msm_gpio_irq_set_wake,
412 .irq_disable = msm_gpio_irq_disable,
413};
414
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700415#ifdef CONFIG_PM
416static int msm_gpio_suspend(void)
417{
418 unsigned long irq_flags;
419 unsigned long i;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800420 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700421
422 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800423 for_each_set_bit(i, msm_gpio.enabled_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700424 __msm_gpio_set_intr_cfg_enable(i, 0);
425
Rohit Vaswani341c2032012-11-08 18:49:29 -0800426 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700427 __msm_gpio_set_intr_cfg_enable(i, 1);
428 mb();
429 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
430 return 0;
431}
432
433void msm_gpio_show_resume_irq(void)
434{
435 unsigned long irq_flags;
436 int i, irq, intstat;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800437 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700438
439 if (!msm_show_resume_irq_mask)
440 return;
441
442 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800443 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio) {
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700444 intstat = __msm_gpio_get_intr_status(i);
445 if (intstat) {
446 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
447 pr_warning("%s: %d triggered\n",
448 __func__, irq);
449 }
450 }
451 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
452}
453
454static void msm_gpio_resume(void)
455{
456 unsigned long irq_flags;
457 unsigned long i;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800458 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700459
460 msm_gpio_show_resume_irq();
461
462 spin_lock_irqsave(&tlmm_lock, irq_flags);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800463 for_each_set_bit(i, msm_gpio.wake_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700464 __msm_gpio_set_intr_cfg_enable(i, 0);
465
Rohit Vaswani341c2032012-11-08 18:49:29 -0800466 for_each_set_bit(i, msm_gpio.enabled_irqs, ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700467 __msm_gpio_set_intr_cfg_enable(i, 1);
468 mb();
469 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
470}
471#else
472#define msm_gpio_suspend NULL
473#define msm_gpio_resume NULL
474#endif
475
476static struct syscore_ops msm_gpio_syscore_ops = {
477 .suspend = msm_gpio_suspend,
478 .resume = msm_gpio_resume,
479};
480
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700481static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
482 unsigned id, unsigned width, unsigned val)
483{
484 unsigned long irqflags;
485 u32 mask = (1 << width) - 1;
486 u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
487 u32 reg_val;
488
489 spin_lock_irqsave(&tlmm_lock, irqflags);
490 reg_val = __raw_readl(reg);
491 reg_val &= ~(mask << configs[id].off);
492 reg_val |= (val & mask) << configs[id].off;
493 __raw_writel(reg_val, reg);
494 mb();
495 spin_unlock_irqrestore(&tlmm_lock, irqflags);
496}
497
498void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
499{
500 msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
501}
502EXPORT_SYMBOL(msm_tlmm_set_hdrive);
503
504void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
505{
506 msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
507}
508EXPORT_SYMBOL(msm_tlmm_set_pull);
509
510int gpio_tlmm_config(unsigned config, unsigned disable)
511{
512 unsigned gpio = GPIO_PIN(config);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800513 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700514
Rohit Vaswani341c2032012-11-08 18:49:29 -0800515 if (gpio > ngpio)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700516 return -EINVAL;
517
518 __gpio_tlmm_config(config);
519 mb();
520
521 return 0;
522}
523EXPORT_SYMBOL(gpio_tlmm_config);
524
525int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
526 unsigned int input_polarity)
527{
528 unsigned long irq_flags;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800529 int ngpio = msm_gpio.gpio_chip.ngpio;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700530
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800531 if (gpio >= ngpio || irq >= nr_direct_connect_irqs)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700532 return -EINVAL;
533
534 spin_lock_irqsave(&tlmm_lock, irq_flags);
535 __msm_gpio_install_direct_irq(gpio, irq, input_polarity);
536 mb();
537 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
538
539 return 0;
540}
541EXPORT_SYMBOL(msm_gpio_install_direct_irq);
542
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700543/*
544 * This lock class tells lockdep that GPIO irqs are in a different
545 * category than their parent, so it won't report false recursion.
546 */
547static struct lock_class_key msm_gpio_lock_class;
548
Rohit Vaswani341c2032012-11-08 18:49:29 -0800549static inline void msm_gpio_set_irq_handler(struct device *dev)
550{
551 int irq, i;
552
553 if (!dev->of_node) {
554 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
555 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
556 irq_set_lockdep_class(irq, &msm_gpio_lock_class);
557 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
558 handle_level_irq);
559 set_irq_flags(irq, IRQF_VALID);
560 }
561 }
562}
563
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700564static int __devinit msm_gpio_probe(struct platform_device *pdev)
565{
Rohit Vaswani341c2032012-11-08 18:49:29 -0800566 int ret, ngpio = 0;
567 struct msm_gpio_pdata *pdata = pdev->dev.platform_data;
568
569 if (pdev->dev.of_node) {
570 ret = of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio);
571 if (ret) {
572 pr_err("%s: Failed to find ngpio property\n", __func__);
573 return ret;
574 }
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800575 ret = of_property_read_u32(pdev->dev.of_node,
576 "qcom,direct-connect-irqs",
577 &nr_direct_connect_irqs);
578 if (ret) {
579 pr_err("%s: Failed to find qcom,direct-connect-irqs property\n"
580 , __func__);
581 return ret;
582 }
Rohit Vaswani341c2032012-11-08 18:49:29 -0800583 } else {
584 ngpio = pdata->ngpio;
Rohit Vaswanied0a4ef2012-12-11 15:14:42 -0800585 nr_direct_connect_irqs = pdata->direct_connect_irqs;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800586 }
587
Rohit Vaswanid2001522012-12-05 19:23:44 -0800588 tlmm_msm_summary_irq = platform_get_irq(pdev, 0);
589 if (tlmm_msm_summary_irq < 0) {
590 pr_err("%s: No interrupt defined for msmgpio\n", __func__);
591 return -ENXIO;
592 }
Rohit Vaswani341c2032012-11-08 18:49:29 -0800593
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700594 msm_gpio.gpio_chip.dev = &pdev->dev;
Rohit Vaswani341c2032012-11-08 18:49:29 -0800595 msm_gpio.gpio_chip.ngpio = ngpio;
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700596 spin_lock_init(&tlmm_lock);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800597 msm_gpio.enabled_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
598 * BITS_TO_LONGS(ngpio), GFP_KERNEL);
599 if (!msm_gpio.enabled_irqs) {
600 dev_err(&pdev->dev, "%s failed to allocated enabled_irqs bitmap\n"
601 , __func__);
602 return -ENOMEM;
603 }
604
605 msm_gpio.wake_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long) *
606 BITS_TO_LONGS(ngpio), GFP_KERNEL);
607 if (!msm_gpio.wake_irqs) {
608 dev_err(&pdev->dev, "%s failed to allocated wake_irqs bitmap\n"
609 , __func__);
610 return -ENOMEM;
611 }
612 msm_gpio.dual_edge_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
613 * BITS_TO_LONGS(ngpio), GFP_KERNEL);
614 if (!msm_gpio.dual_edge_irqs) {
615 dev_err(&pdev->dev, "%s failed to allocated dual_edge_irqs bitmap\n"
616 , __func__);
617 return -ENOMEM;
618 }
619
620 bitmap_zero(msm_gpio.enabled_irqs, ngpio);
621 bitmap_zero(msm_gpio.wake_irqs, ngpio);
622 bitmap_zero(msm_gpio.dual_edge_irqs, ngpio);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700623 ret = gpiochip_add(&msm_gpio.gpio_chip);
624 if (ret < 0)
625 return ret;
626
Rohit Vaswani341c2032012-11-08 18:49:29 -0800627 msm_gpio_set_irq_handler(&pdev->dev);
628
629 ret = devm_request_irq(&pdev->dev, tlmm_msm_summary_irq,
630 msm_summary_irq_handler, IRQF_TRIGGER_HIGH,
631 "msmgpio", NULL);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700632 if (ret) {
Rohit Vaswanid2001522012-12-05 19:23:44 -0800633 pr_err("Request_irq failed for tlmm_msm_summary_irq - %d\n",
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700634 ret);
635 return ret;
636 }
637 register_syscore_ops(&msm_gpio_syscore_ops);
638 return 0;
639}
640
641#ifdef CONFIG_OF
642static struct of_device_id msm_gpio_of_match[] __devinitdata = {
643 {.compatible = "qcom,msm-gpio", },
644 { },
645};
646#endif
647
648static int __devexit msm_gpio_remove(struct platform_device *pdev)
649{
650 int ret;
651
652 unregister_syscore_ops(&msm_gpio_syscore_ops);
653 ret = gpiochip_remove(&msm_gpio.gpio_chip);
654 if (ret < 0)
655 return ret;
Rohit Vaswanid2001522012-12-05 19:23:44 -0800656 irq_set_handler(tlmm_msm_summary_irq, NULL);
Rohit Vaswanib1cc4932012-07-23 21:30:11 -0700657
658 return 0;
659}
660
661static struct platform_driver msm_gpio_driver = {
662 .probe = msm_gpio_probe,
663 .remove = __devexit_p(msm_gpio_remove),
664 .driver = {
665 .name = "msmgpio",
666 .owner = THIS_MODULE,
667 .of_match_table = of_match_ptr(msm_gpio_of_match),
668 },
669};
670
671static void __exit msm_gpio_exit(void)
672{
673 platform_driver_unregister(&msm_gpio_driver);
674}
675module_exit(msm_gpio_exit);
676
677static int __init msm_gpio_init(void)
678{
679 return platform_driver_register(&msm_gpio_driver);
680}
681postcore_initcall(msm_gpio_init);
682
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700683#ifdef CONFIG_OF
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700684static int msm_gpio_irq_domain_xlate(struct irq_domain *d,
685 struct device_node *controller,
686 const u32 *intspec,
687 unsigned int intsize,
688 unsigned long *out_hwirq,
689 unsigned int *out_type)
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700690{
691 if (d->of_node != controller)
692 return -EINVAL;
693 if (intsize != 2)
694 return -EINVAL;
695
696 /* hwirq value */
697 *out_hwirq = intspec[0];
698
699 /* irq flags */
700 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
701 return 0;
702}
703
Michael Bohan27e7b942012-07-06 10:25:30 -0700704static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
705 irq_hw_number_t hwirq)
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700706{
Michael Bohan27e7b942012-07-06 10:25:30 -0700707 irq_set_lockdep_class(irq, &msm_gpio_lock_class);
708 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
709 handle_level_irq);
710 set_irq_flags(irq, IRQF_VALID);
711
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700712 return 0;
713}
714
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700715static struct irq_domain_ops msm_gpio_irq_domain_ops = {
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700716 .xlate = msm_gpio_irq_domain_xlate,
717 .map = msm_gpio_irq_domain_map,
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700718};
719
720int __init msm_gpio_of_init(struct device_node *node,
721 struct device_node *parent)
722{
Rohit Vaswani341c2032012-11-08 18:49:29 -0800723 int ngpio, ret;
724
725 ret = of_property_read_u32(node, "ngpio", &ngpio);
726 if (ret) {
727 WARN(1, "Cannot get numgpios from device tree\n");
728 return ret;
729 }
730 msm_gpio.domain = irq_domain_add_linear(node, ngpio,
Michael Bohanbb6b30f2012-06-01 13:33:51 -0700731 &msm_gpio_irq_domain_ops, &msm_gpio);
732 if (!msm_gpio.domain) {
733 WARN(1, "Cannot allocate irq_domain\n");
734 return -ENOMEM;
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700735 }
736
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -0700737 return 0;
738}
739#endif
740
741MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
742MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
743MODULE_LICENSE("GPL v2");
744MODULE_ALIAS("sysdev:msmgpio");