/arch/arm/mach-fsm/msm8660-gpio.c
C | 337 lines | 250 code | 52 blank | 35 comment | 12 complexity | 1b28e48651fbcb9564f249fc43905abd MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* Copyright (c) 2010, 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 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 *
17 */
18#include <linux/bitmap.h>
19#include <linux/gpio.h>
20#include <linux/init.h>
21#include <linux/irq.h>
22#include <linux/io.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/spinlock.h>
26#include <linux/pm_runtime.h>
27#include <mach/msm_iomap.h>
28#include "tlmm-msm8660.h"
29#include "gpiomux.h"
30
31/**
32 * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
33 *
34 * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
35 * keeping track of which gpios are unmasked as irq sources, we avoid
36 * having to do readl calls on hundreds of iomapped registers each time
37 * the summary interrupt fires in order to locate the active interrupts.
38 *
39 * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
40 * as wakeup sources. When the device is suspended, interrupts which are
41 * not wakeup sources are disabled.
42 */
43struct msm_gpio_dev {
44 struct gpio_chip gpio_chip;
45 spinlock_t lock;
46 DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
47 DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
48};
49
50static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
51{
52 return container_of(chip, struct msm_gpio_dev, gpio_chip);
53}
54
55static inline void set_gpio_bits(unsigned n, void __iomem *reg)
56{
57 writel(readl(reg) | n, reg);
58}
59
60static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
61{
62 writel(readl(reg) & ~n, reg);
63}
64
65static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
66{
67 return readl(GPIO_IN_OUT(offset)) & BIT(GPIO_IN_BIT);
68}
69
70static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
71{
72 writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(offset));
73}
74
75static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
76{
77 struct msm_gpio_dev *dev = to_msm_gpio_dev(chip);
78 unsigned long irq_flags;
79
80 spin_lock_irqsave(&dev->lock, irq_flags);
81 clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
82 spin_unlock_irqrestore(&dev->lock, irq_flags);
83 return 0;
84}
85
86static int msm_gpio_direction_output(struct gpio_chip *chip,
87 unsigned offset,
88 int val)
89{
90 struct msm_gpio_dev *dev = to_msm_gpio_dev(chip);
91 unsigned long irq_flags;
92
93 spin_lock_irqsave(&dev->lock, irq_flags);
94 msm_gpio_set(chip, offset, val);
95 set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(offset));
96 spin_unlock_irqrestore(&dev->lock, irq_flags);
97 return 0;
98}
99
100static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
101{
102 return MSM_GPIO_TO_INT(offset - chip->base);
103}
104
105static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
106{
107 return irq - MSM_GPIO_TO_INT(chip->base);
108}
109
110static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
111{
112 return msm_gpiomux_get(chip->base + offset);
113}
114
115static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
116{
117 msm_gpiomux_put(chip->base + offset);
118}
119
120static struct msm_gpio_dev msm_gpio = {
121 .gpio_chip = {
122 .base = 0,
123 .ngpio = NR_MSM_GPIOS,
124 .direction_input = msm_gpio_direction_input,
125 .direction_output = msm_gpio_direction_output,
126 .get = msm_gpio_get,
127 .set = msm_gpio_set,
128 .to_irq = msm_gpio_to_irq,
129 .request = msm_gpio_request,
130 .free = msm_gpio_free,
131 },
132};
133
134static void msm_gpio_irq_ack(unsigned int irq)
135{
136 writel(BIT(INTR_STATUS_BIT),
137 GPIO_INTR_STATUS(msm_irq_to_gpio(&msm_gpio.gpio_chip, irq)));
138}
139
140static void msm_gpio_irq_mask(unsigned int irq)
141{
142 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, irq);
143 unsigned long irq_flags;
144
145 spin_lock_irqsave(&msm_gpio.lock, irq_flags);
146 writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
147 clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
148 __clear_bit(gpio, msm_gpio.enabled_irqs);
149 spin_unlock_irqrestore(&msm_gpio.lock, irq_flags);
150}
151
152static void msm_gpio_irq_unmask(unsigned int irq)
153{
154 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, irq);
155 unsigned long irq_flags;
156
157 spin_lock_irqsave(&msm_gpio.lock, irq_flags);
158 __set_bit(gpio, msm_gpio.enabled_irqs);
159 set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE, GPIO_INTR_CFG(gpio));
160 writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
161 spin_unlock_irqrestore(&msm_gpio.lock, irq_flags);
162}
163
164static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
165{
166 void *addr = GPIO_INTR_CFG(msm_irq_to_gpio(&msm_gpio.gpio_chip, irq));
167 unsigned long irq_flags;
168 uint32_t bits;
169
170 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
171 return -EINVAL;
172
173 spin_lock_irqsave(&msm_gpio.lock, irq_flags);
174
175 bits = readl(addr);
176
177 if (flow_type & IRQ_TYPE_EDGE_BOTH) {
178 bits |= INTR_DECT_CTL_EDGE;
179 irq_desc[irq].handle_irq = handle_edge_irq;
180 } else {
181 bits &= ~INTR_DECT_CTL_EDGE;
182 irq_desc[irq].handle_irq = handle_level_irq;
183 }
184
185 if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
186 bits |= INTR_POL_CTL_HI;
187 else
188 bits &= ~INTR_POL_CTL_HI;
189
190 writel(bits, addr);
191
192 spin_unlock_irqrestore(&msm_gpio.lock, irq_flags);
193
194 return 0;
195}
196
197/*
198 * When the summary IRQ is raised, any number of GPIO lines may be high.
199 * It is the job of the summary handler to find all those GPIO lines
200 * which have been set as summary IRQ lines and which are triggered,
201 * and to call their interrupt handlers.
202 */
203static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc)
204{
205 unsigned long i;
206
207 for (i = find_first_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
208 i < NR_MSM_GPIOS;
209 i = find_next_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS, i + 1)) {
210 if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS_BIT))
211 generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
212 i));
213 }
214 desc->chip->ack(irq);
215}
216
217static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on)
218{
219 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, irq);
220
221 if (on)
222 set_bit(gpio, msm_gpio.wake_irqs);
223 else
224 clear_bit(gpio, msm_gpio.wake_irqs);
225
226 return 0;
227}
228
229static struct irq_chip msm_gpio_irq_chip = {
230 .name = "msm_gpio",
231 .mask = msm_gpio_irq_mask,
232 .unmask = msm_gpio_irq_unmask,
233 .ack = msm_gpio_irq_ack,
234 .set_type = msm_gpio_irq_set_type,
235 .set_wake = msm_gpio_irq_set_wake,
236};
237
238static int __devinit msm_gpio_probe(struct platform_device *dev)
239{
240 int i, irq, ret;
241
242 spin_lock_init(&msm_gpio.lock);
243 bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
244 bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
245 msm_gpio.gpio_chip.label = dev->name;
246 ret = gpiochip_add(&msm_gpio.gpio_chip);
247 if (ret < 0)
248 return ret;
249
250 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
251 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
252 set_irq_chip(irq, &msm_gpio_irq_chip);
253 set_irq_handler(irq, handle_level_irq);
254 set_irq_flags(irq, IRQF_VALID);
255 }
256 ret = pm_runtime_set_active(&dev->dev);
257 if (ret < 0)
258 printk(KERN_ERR "pm_runtime: fail to set active\n");
259
260 pm_runtime_enable(&dev->dev);
261 pm_runtime_get(&dev->dev);
262 set_irq_chained_handler(TLMM_SCSS_SUMMARY_IRQ,
263 msm_summary_irq_handler);
264 return 0;
265}
266
267static int __devexit msm_gpio_remove(struct platform_device *dev)
268{
269 int ret = gpiochip_remove(&msm_gpio.gpio_chip);
270
271 if (ret < 0)
272 return ret;
273
274 set_irq_handler(TLMM_SCSS_SUMMARY_IRQ, NULL);
275 pm_runtime_put(&dev->dev);
276 pm_runtime_disable(&dev->dev);
277
278
279 return 0;
280}
281
282#ifdef CONFIG_PM_RUNTIME
283static int msm_gpio_runtime_suspend(struct device *dev)
284{
285 dev_dbg(dev, "pm_runtime: suspending...\n");
286 return 0;
287}
288
289static int msm_gpio_runtime_resume(struct device *dev)
290{
291 dev_dbg(dev, "pm_runtime: resuming...\n");
292 return 0;
293}
294
295static int msm_gpio_runtime_idle(struct device *dev)
296{
297 dev_dbg(dev, "pm_runtime: idling...\n");
298 return 0;
299}
300#else
301#define msm_gpio_runtime_suspend NULL
302#define msm_gpio_runtime_resume NULL
303#define msm_gpio_runtime_idle NULL
304#endif
305
306static struct dev_pm_ops msm_gpio_dev_pm_ops = {
307 .runtime_suspend = msm_gpio_runtime_suspend,
308 .runtime_resume = msm_gpio_runtime_resume,
309 .runtime_idle = msm_gpio_runtime_idle,
310};
311
312static struct platform_driver msm_gpio_driver = {
313 .probe = msm_gpio_probe,
314 .remove = __devexit_p(msm_gpio_remove),
315 .driver = {
316 .name = "msm8660-gpio",
317 .owner = THIS_MODULE,
318 .pm = &msm_gpio_dev_pm_ops,
319 },
320};
321static int __init msm_gpio_init(void)
322{
323 return platform_driver_register(&msm_gpio_driver);
324}
325
326static void __exit msm_gpio_exit(void)
327{
328 platform_driver_unregister(&msm_gpio_driver);
329}
330
331postcore_initcall(msm_gpio_init);
332module_exit(msm_gpio_exit);
333
334MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
335MODULE_DESCRIPTION("Driver for Qualcomm MSM 8660-family SoC GPIOs");
336MODULE_LICENSE("GPL v2");
337MODULE_ALIAS("platform:msm8660-gpio");