diff options
author | Mathias Nyman <mathias.nyman@linux.intel.com> | 2012-04-24 06:02:49 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2012-05-11 15:14:54 -0400 |
commit | 25cf25073a4e1e0563c288908481f10f98acb19a (patch) | |
tree | 7a3e22eb3898b7329150620db3dcd3c2e35f5ca2 | |
parent | fd454997d6873ef7ba668200f4278e006139187e (diff) |
gpio: add MSIC gpio driver
Add gpio support for Intel MSIC chips found in Intel Medfield platforms.
MSIC supports totally 24 GPIOs with 16 low voltage and 8 high voltage pins.
Driver uses MSIC mfd interface for MSIC access.
(Updated comment to indicate why locking is actually safe)
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r-- | drivers/gpio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-msic.c | 339 |
3 files changed, 348 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8ee6d29c3714..5169a99e9f61 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -520,4 +520,12 @@ config GPIO_TPS65910 | |||
520 | help | 520 | help |
521 | Select this option to enable GPIO driver for the TPS65910 | 521 | Select this option to enable GPIO driver for the TPS65910 |
522 | chip family. | 522 | chip family. |
523 | |||
524 | config GPIO_MSIC | ||
525 | bool "Intel MSIC mixed signal gpio support" | ||
526 | depends on MFD_INTEL_MSIC | ||
527 | help | ||
528 | Enable support for GPIO on intel MSIC controllers found in | ||
529 | intel MID devices | ||
530 | |||
523 | endif | 531 | endif |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1c2f6c0a835e..7862f49b4d05 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o | |||
33 | obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o | 33 | obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o |
34 | obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o | 34 | obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o |
35 | obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o | 35 | obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o |
36 | obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o | ||
36 | obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o | 37 | obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o |
37 | obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o | 38 | obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o |
38 | obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o | 39 | obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o |
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c new file mode 100644 index 000000000000..71a838f44501 --- /dev/null +++ b/drivers/gpio/gpio-msic.c | |||
@@ -0,0 +1,339 @@ | |||
1 | /* | ||
2 | * Intel Medfield MSIC GPIO driver> | ||
3 | * Copyright (c) 2011, Intel Corporation. | ||
4 | * | ||
5 | * Author: Mathias Nyman <mathias.nyman@linux.intel.com> | ||
6 | * Based on intel_pmic_gpio.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along with | ||
18 | * this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/mfd/intel_msic.h> | ||
31 | |||
32 | /* the offset for the mapping of global gpio pin to irq */ | ||
33 | #define MSIC_GPIO_IRQ_OFFSET 0x100 | ||
34 | |||
35 | #define MSIC_GPIO_DIR_IN 0 | ||
36 | #define MSIC_GPIO_DIR_OUT BIT(5) | ||
37 | #define MSIC_GPIO_TRIG_FALL BIT(1) | ||
38 | #define MSIC_GPIO_TRIG_RISE BIT(2) | ||
39 | |||
40 | /* masks for msic gpio output GPIOxxxxCTLO registers */ | ||
41 | #define MSIC_GPIO_DIR_MASK BIT(5) | ||
42 | #define MSIC_GPIO_DRV_MASK BIT(4) | ||
43 | #define MSIC_GPIO_REN_MASK BIT(3) | ||
44 | #define MSIC_GPIO_RVAL_MASK (BIT(2) | BIT(1)) | ||
45 | #define MSIC_GPIO_DOUT_MASK BIT(0) | ||
46 | |||
47 | /* masks for msic gpio input GPIOxxxxCTLI registers */ | ||
48 | #define MSIC_GPIO_GLBYP_MASK BIT(5) | ||
49 | #define MSIC_GPIO_DBNC_MASK (BIT(4) | BIT(3)) | ||
50 | #define MSIC_GPIO_INTCNT_MASK (BIT(2) | BIT(1)) | ||
51 | #define MSIC_GPIO_DIN_MASK BIT(0) | ||
52 | |||
53 | #define MSIC_NUM_GPIO 24 | ||
54 | |||
55 | struct msic_gpio { | ||
56 | struct platform_device *pdev; | ||
57 | struct mutex buslock; | ||
58 | struct gpio_chip chip; | ||
59 | int irq; | ||
60 | unsigned irq_base; | ||
61 | unsigned long trig_change_mask; | ||
62 | unsigned trig_type; | ||
63 | }; | ||
64 | |||
65 | /* | ||
66 | * MSIC has 24 gpios, 16 low voltage (1.2-1.8v) and 8 high voltage (3v). | ||
67 | * Both the high and low voltage gpios are divided in two banks. | ||
68 | * GPIOs are numbered with GPIO0LV0 as gpio_base in the following order: | ||
69 | * GPIO0LV0..GPIO0LV7: low voltage, bank 0, gpio_base | ||
70 | * GPIO1LV0..GPIO1LV7: low voltage, bank 1, gpio_base + 8 | ||
71 | * GPIO0HV0..GPIO0HV3: high voltage, bank 0, gpio_base + 16 | ||
72 | * GPIO1HV0..GPIO1HV3: high voltage, bank 1, gpio_base + 20 | ||
73 | */ | ||
74 | |||
75 | static int msic_gpio_to_ireg(unsigned offset) | ||
76 | { | ||
77 | if (offset >= MSIC_NUM_GPIO) | ||
78 | return -EINVAL; | ||
79 | |||
80 | if (offset < 8) | ||
81 | return INTEL_MSIC_GPIO0LV0CTLI - offset; | ||
82 | if (offset < 16) | ||
83 | return INTEL_MSIC_GPIO1LV0CTLI - offset + 8; | ||
84 | if (offset < 20) | ||
85 | return INTEL_MSIC_GPIO0HV0CTLI - offset + 16; | ||
86 | |||
87 | return INTEL_MSIC_GPIO1HV0CTLI - offset + 20; | ||
88 | } | ||
89 | |||
90 | static int msic_gpio_to_oreg(unsigned offset) | ||
91 | { | ||
92 | if (offset >= MSIC_NUM_GPIO) | ||
93 | return -EINVAL; | ||
94 | |||
95 | if (offset < 8) | ||
96 | return INTEL_MSIC_GPIO0LV0CTLO - offset; | ||
97 | if (offset < 16) | ||
98 | return INTEL_MSIC_GPIO1LV0CTLO - offset + 8; | ||
99 | if (offset < 20) | ||
100 | return INTEL_MSIC_GPIO0HV0CTLO - offset + 16; | ||
101 | |||
102 | return INTEL_MSIC_GPIO1HV0CTLO + offset + 20; | ||
103 | } | ||
104 | |||
105 | static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
106 | { | ||
107 | int reg; | ||
108 | |||
109 | reg = msic_gpio_to_oreg(offset); | ||
110 | if (reg < 0) | ||
111 | return reg; | ||
112 | |||
113 | return intel_msic_reg_update(reg, MSIC_GPIO_DIR_IN, MSIC_GPIO_DIR_MASK); | ||
114 | } | ||
115 | |||
116 | static int msic_gpio_direction_output(struct gpio_chip *chip, | ||
117 | unsigned offset, int value) | ||
118 | { | ||
119 | int reg; | ||
120 | unsigned mask; | ||
121 | |||
122 | value = (!!value) | MSIC_GPIO_DIR_OUT; | ||
123 | mask = MSIC_GPIO_DIR_MASK | MSIC_GPIO_DOUT_MASK; | ||
124 | |||
125 | reg = msic_gpio_to_oreg(offset); | ||
126 | if (reg < 0) | ||
127 | return reg; | ||
128 | |||
129 | return intel_msic_reg_update(reg, value, mask); | ||
130 | } | ||
131 | |||
132 | static int msic_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
133 | { | ||
134 | u8 r; | ||
135 | int ret; | ||
136 | int reg; | ||
137 | |||
138 | reg = msic_gpio_to_ireg(offset); | ||
139 | if (reg < 0) | ||
140 | return reg; | ||
141 | |||
142 | ret = intel_msic_reg_read(reg, &r); | ||
143 | if (ret < 0) | ||
144 | return ret; | ||
145 | |||
146 | return r & MSIC_GPIO_DIN_MASK; | ||
147 | } | ||
148 | |||
149 | static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
150 | { | ||
151 | int reg; | ||
152 | |||
153 | reg = msic_gpio_to_oreg(offset); | ||
154 | if (reg < 0) | ||
155 | return; | ||
156 | |||
157 | intel_msic_reg_update(reg, !!value , MSIC_GPIO_DOUT_MASK); | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * This is called from genirq with mg->buslock locked and | ||
162 | * irq_desc->lock held. We can not access the scu bus here, so we | ||
163 | * store the change and update in the bus_sync_unlock() function below | ||
164 | */ | ||
165 | static int msic_irq_type(struct irq_data *data, unsigned type) | ||
166 | { | ||
167 | struct msic_gpio *mg = irq_data_get_irq_chip_data(data); | ||
168 | u32 gpio = data->irq - mg->irq_base; | ||
169 | |||
170 | if (gpio >= mg->chip.ngpio) | ||
171 | return -EINVAL; | ||
172 | |||
173 | /* mark for which gpio the trigger changed, protected by buslock */ | ||
174 | mg->trig_change_mask |= (1 << gpio); | ||
175 | mg->trig_type = type; | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int msic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
181 | { | ||
182 | struct msic_gpio *mg = container_of(chip, struct msic_gpio, chip); | ||
183 | return mg->irq_base + offset; | ||
184 | } | ||
185 | |||
186 | static void msic_bus_lock(struct irq_data *data) | ||
187 | { | ||
188 | struct msic_gpio *mg = irq_data_get_irq_chip_data(data); | ||
189 | mutex_lock(&mg->buslock); | ||
190 | } | ||
191 | |||
192 | static void msic_bus_sync_unlock(struct irq_data *data) | ||
193 | { | ||
194 | struct msic_gpio *mg = irq_data_get_irq_chip_data(data); | ||
195 | int offset; | ||
196 | int reg; | ||
197 | u8 trig = 0; | ||
198 | |||
199 | /* We can only get one change at a time as the buslock covers the | ||
200 | entire transaction. The irq_desc->lock is dropped before we are | ||
201 | called but that is fine */ | ||
202 | if (mg->trig_change_mask) { | ||
203 | offset = __ffs(mg->trig_change_mask); | ||
204 | |||
205 | reg = msic_gpio_to_ireg(offset); | ||
206 | if (reg < 0) | ||
207 | goto out; | ||
208 | |||
209 | if (mg->trig_type & IRQ_TYPE_EDGE_RISING) | ||
210 | trig |= MSIC_GPIO_TRIG_RISE; | ||
211 | if (mg->trig_type & IRQ_TYPE_EDGE_FALLING) | ||
212 | trig |= MSIC_GPIO_TRIG_FALL; | ||
213 | |||
214 | intel_msic_reg_update(reg, trig, MSIC_GPIO_INTCNT_MASK); | ||
215 | mg->trig_change_mask = 0; | ||
216 | } | ||
217 | out: | ||
218 | mutex_unlock(&mg->buslock); | ||
219 | } | ||
220 | |||
221 | /* Firmware does all the masking and unmasking for us, no masking here. */ | ||
222 | static void msic_irq_unmask(struct irq_data *data) { } | ||
223 | |||
224 | static void msic_irq_mask(struct irq_data *data) { } | ||
225 | |||
226 | static struct irq_chip msic_irqchip = { | ||
227 | .name = "MSIC-GPIO", | ||
228 | .irq_mask = msic_irq_mask, | ||
229 | .irq_unmask = msic_irq_unmask, | ||
230 | .irq_set_type = msic_irq_type, | ||
231 | .irq_bus_lock = msic_bus_lock, | ||
232 | .irq_bus_sync_unlock = msic_bus_sync_unlock, | ||
233 | }; | ||
234 | |||
235 | static void msic_gpio_irq_handler(unsigned irq, struct irq_desc *desc) | ||
236 | { | ||
237 | struct irq_data *data = irq_desc_get_irq_data(desc); | ||
238 | struct msic_gpio *mg = irq_data_get_irq_handler_data(data); | ||
239 | struct irq_chip *chip = irq_data_get_irq_chip(data); | ||
240 | struct intel_msic *msic = pdev_to_intel_msic(mg->pdev); | ||
241 | int i; | ||
242 | int bitnr; | ||
243 | u8 pin; | ||
244 | unsigned long pending = 0; | ||
245 | |||
246 | for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) { | ||
247 | intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin); | ||
248 | pending = pin; | ||
249 | |||
250 | if (pending) { | ||
251 | for_each_set_bit(bitnr, &pending, BITS_PER_BYTE) | ||
252 | generic_handle_irq(mg->irq_base + | ||
253 | (i * BITS_PER_BYTE) + bitnr); | ||
254 | } | ||
255 | } | ||
256 | chip->irq_eoi(data); | ||
257 | } | ||
258 | |||
259 | static int __devinit platform_msic_gpio_probe(struct platform_device *pdev) | ||
260 | { | ||
261 | struct device *dev = &pdev->dev; | ||
262 | struct intel_msic_gpio_pdata *pdata = dev->platform_data; | ||
263 | struct msic_gpio *mg; | ||
264 | int irq = platform_get_irq(pdev, 0); | ||
265 | int retval; | ||
266 | int i; | ||
267 | |||
268 | if (irq < 0) { | ||
269 | dev_err(dev, "no IRQ line\n"); | ||
270 | return -EINVAL; | ||
271 | } | ||
272 | |||
273 | if (!pdata || !pdata->gpio_base) { | ||
274 | dev_err(dev, "incorrect or missing platform data\n"); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | |||
278 | mg = kzalloc(sizeof(*mg), GFP_KERNEL); | ||
279 | if (!mg) | ||
280 | return -ENOMEM; | ||
281 | |||
282 | dev_set_drvdata(dev, mg); | ||
283 | |||
284 | mg->pdev = pdev; | ||
285 | mg->irq = irq; | ||
286 | mg->irq_base = pdata->gpio_base + MSIC_GPIO_IRQ_OFFSET; | ||
287 | mg->chip.label = "msic_gpio"; | ||
288 | mg->chip.direction_input = msic_gpio_direction_input; | ||
289 | mg->chip.direction_output = msic_gpio_direction_output; | ||
290 | mg->chip.get = msic_gpio_get; | ||
291 | mg->chip.set = msic_gpio_set; | ||
292 | mg->chip.to_irq = msic_gpio_to_irq; | ||
293 | mg->chip.base = pdata->gpio_base; | ||
294 | mg->chip.ngpio = MSIC_NUM_GPIO; | ||
295 | mg->chip.can_sleep = 1; | ||
296 | mg->chip.dev = dev; | ||
297 | |||
298 | mutex_init(&mg->buslock); | ||
299 | |||
300 | retval = gpiochip_add(&mg->chip); | ||
301 | if (retval) { | ||
302 | dev_err(dev, "Adding MSIC gpio chip failed\n"); | ||
303 | goto err; | ||
304 | } | ||
305 | |||
306 | for (i = 0; i < mg->chip.ngpio; i++) { | ||
307 | irq_set_chip_data(i + mg->irq_base, mg); | ||
308 | irq_set_chip_and_handler_name(i + mg->irq_base, | ||
309 | &msic_irqchip, | ||
310 | handle_simple_irq, | ||
311 | "demux"); | ||
312 | } | ||
313 | irq_set_chained_handler(mg->irq, msic_gpio_irq_handler); | ||
314 | irq_set_handler_data(mg->irq, mg); | ||
315 | |||
316 | return 0; | ||
317 | err: | ||
318 | kfree(mg); | ||
319 | return retval; | ||
320 | } | ||
321 | |||
322 | static struct platform_driver platform_msic_gpio_driver = { | ||
323 | .driver = { | ||
324 | .name = "msic_gpio", | ||
325 | .owner = THIS_MODULE, | ||
326 | }, | ||
327 | .probe = platform_msic_gpio_probe, | ||
328 | }; | ||
329 | |||
330 | static int __init platform_msic_gpio_init(void) | ||
331 | { | ||
332 | return platform_driver_register(&platform_msic_gpio_driver); | ||
333 | } | ||
334 | |||
335 | subsys_initcall(platform_msic_gpio_init); | ||
336 | |||
337 | MODULE_AUTHOR("Mathias Nyman <mathias.nyman@linux.intel.com>"); | ||
338 | MODULE_DESCRIPTION("Intel Medfield MSIC GPIO driver"); | ||
339 | MODULE_LICENSE("GPL v2"); | ||