PageRenderTime 30ms CodeModel.GetById 16ms app.highlight 12ms RepoModel.GetById 0ms app.codeStats 0ms

/arch/arm/mach-fsm/msm8660-gpio.c

https://bitbucket.org/sammyz/iscream_thunderc-2.6.35-rebase
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");