aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-handheld-4.0/locomo/0009-gpio-locomo-implement-per-pin-irq-handling.patch
blob: 7d07659dacfa841197d2cd53fcc23e9f349bafbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
From ec2485ae9fd5238d1def04e6377d213285ab3315 Mon Sep 17 00:00:00 2001
From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Date: Mon, 11 Nov 2013 04:27:27 +0400
Subject: [PATCH 09/20] gpio: locomo: implement per-pin irq handling

LoCoMo has a possibility to generate per-GPIO edge irqs. Support for
that was there in old locomo driver, got 'cleaned up' during old driver
IRQ cascading cleanup and is now reimplemented. It is expected that
SL-5500 (collie) will use locomo gpio irqs for mmc detection irq.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
 drivers/gpio/Kconfig       |   1 +
 drivers/gpio/gpio-locomo.c | 120 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9b1c2e6..b6ebcb6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -507,6 +507,7 @@ config GPIO_TB10X
 config GPIO_LOCOMO
 	bool "Sharp LoCoMo GPIO support"
 	depends on MFD_LOCOMO
+	select GPIOLIB_IRQCHIP
 	help
 	  Select this to support GPIO pins on Sharp LoCoMo Grid Array found
 	  in Sharp Zaurus collie and poodle models.
diff --git a/drivers/gpio/gpio-locomo.c b/drivers/gpio/gpio-locomo.c
index dd9a1ca..d8e5880 100644
--- a/drivers/gpio/gpio-locomo.c
+++ b/drivers/gpio/gpio-locomo.c
@@ -16,13 +16,15 @@
 #include <linux/slab.h>
 #include <linux/bitops.h>
 #include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/regmap.h>
 #include <linux/mfd/locomo.h>
 
 struct locomo_gpio {
 	struct regmap *regmap;
+	int irq;
 
 	struct gpio_chip gpio;
 
@@ -79,6 +81,99 @@ static int locomo_gpio_direction_output(struct gpio_chip *chip,
 	return 0;
 }
 
+static void
+locomo_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct gpio_chip *chip = irq_get_handler_data(irq);
+	struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
+	struct irq_chip *irqchip = irq_desc_get_chip(desc);
+	unsigned int gir;
+	unsigned int gpd;
+	unsigned int req;
+
+	chained_irq_enter(irqchip, desc);
+
+	while (1) {
+		regmap_read(lg->regmap, LOCOMO_GIR, &gir);
+		regmap_read(lg->regmap, LOCOMO_GPD, &gpd);
+		req = gir & gpd;
+
+		if (!req)
+			break;
+
+		generic_handle_irq(irq_find_mapping(lg->gpio.irqdomain,
+					ffs(req) - 1));
+	}
+
+	chained_irq_exit(irqchip, desc);
+}
+
+static void locomo_gpio_ack_irq(struct irq_data *d)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+	struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
+	unsigned int mask = BIT(d->hwirq);
+
+	regmap_update_bits(lg->regmap, LOCOMO_GWE, mask, mask);
+	regmap_update_bits(lg->regmap, LOCOMO_GIS, mask, 0);
+	regmap_update_bits(lg->regmap, LOCOMO_GWE, mask, 0);
+}
+
+static void locomo_gpio_mask_irq(struct irq_data *d)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+	struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
+	unsigned int mask = BIT(d->hwirq);
+
+	regmap_update_bits(lg->regmap, LOCOMO_GIE, mask, 0);
+}
+
+static void locomo_gpio_unmask_irq(struct irq_data *d)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+	struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
+	unsigned int mask = BIT(d->hwirq);
+
+	regmap_update_bits(lg->regmap, LOCOMO_GIE, mask, mask);
+}
+
+static int locomo_gpio_type_irq(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+	struct locomo_gpio *lg = container_of(chip, struct locomo_gpio, gpio);
+	unsigned int mask;
+
+	mask = BIT(d->hwirq);
+
+	if (type == IRQ_TYPE_PROBE) {
+		if ((lg->rising_edge | lg->falling_edge) & mask)
+			return 0;
+		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+	}
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		lg->rising_edge |= mask;
+	else
+		lg->rising_edge &= ~mask;
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		lg->falling_edge |= mask;
+	else
+		lg->falling_edge &= ~mask;
+
+	regmap_write(lg->regmap, LOCOMO_GRIE, lg->rising_edge);
+	regmap_write(lg->regmap, LOCOMO_GFIE, lg->falling_edge);
+
+	return 0;
+}
+
+static struct irq_chip locomo_gpio_irq_chip = {
+	.name		= "LOCOMO-g",
+	.irq_ack	= locomo_gpio_ack_irq,
+	.irq_mask	= locomo_gpio_mask_irq,
+	.irq_unmask	= locomo_gpio_unmask_irq,
+	.irq_set_type	= locomo_gpio_type_irq,
+};
+
 #ifdef CONFIG_PM_SLEEP
 static int locomo_gpio_suspend(struct device *dev)
 {
@@ -119,6 +214,10 @@ static int locomo_gpio_probe(struct platform_device *pdev)
 	if (!lg)
 		return -ENOMEM;
 
+	lg->irq = platform_get_irq(pdev, 0);
+	if (lg->irq < 0)
+		return -ENXIO;
+
 	lg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 	if (!lg->regmap)
 		return -EINVAL;
@@ -130,6 +229,7 @@ static int locomo_gpio_probe(struct platform_device *pdev)
 	regmap_write(lg->regmap, LOCOMO_GPD, 0x00);
 	regmap_write(lg->regmap, LOCOMO_GIE, 0x00);
 
+	lg->gpio.dev = &pdev->dev;
 	lg->gpio.base = pdata ? pdata->gpio_base : -1;
 	lg->gpio.label = "locomo-gpio";
 	lg->gpio.ngpio = 16;
@@ -142,7 +242,22 @@ static int locomo_gpio_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	ret = gpiochip_irqchip_add(&lg->gpio, &locomo_gpio_irq_chip, 0,
+				   handle_level_irq, IRQ_TYPE_NONE);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to add irq chip\n");
+		goto err_rm_gpiochip;
+	}
+
+	gpiochip_set_chained_irqchip(&lg->gpio, &locomo_gpio_irq_chip, lg->irq,
+				     locomo_gpio_irq_handler);
+
 	return 0;
+
+err_rm_gpiochip:
+	gpiochip_remove(&lg->gpio);
+
+	return ret;
 }
 
 static int locomo_gpio_remove(struct platform_device *pdev)
@@ -151,6 +266,9 @@ static int locomo_gpio_remove(struct platform_device *pdev)
 
 	gpiochip_remove(&lg->gpio);
 
+	irq_set_chained_handler(lg->irq, NULL);
+	irq_set_handler_data(lg->irq, NULL);
+
 	return 0;
 }
 
-- 
1.9.1