aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel_pmic_gpio.c
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2010-07-13 05:56:25 -0400
committerMatthew Garrett <mjg@redhat.com>2010-08-03 09:49:09 -0400
commit8950778704cf8483cc5cc0140f557adf0d3f45a5 (patch)
treea08258cb55161df94f724ebfc4c031e45a7004b7 /drivers/platform/x86/intel_pmic_gpio.c
parentb52e04216fcd86968c01ad0cfdb249375f19134d (diff)
gpio: Add PMIC GPIO block support
Moorestown has PMIC chip which contains GPIO blocks. The PMIC chip is connected to Langwell by SPI interface. So this GPIO driver will be regarded as SPI GPIO expander though the actual GPIO access is through IPC and SRAM. The SPI master contoller will probe this device driver by parsing SPIB table. Cleaned up for new IPC, GPE removed and some printk and other tidying by Alan Cox. Fixes for points noted by Matthew Garrett Signed-off-by: Alek Du <alek.du@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform/x86/intel_pmic_gpio.c')
-rw-r--r--drivers/platform/x86/intel_pmic_gpio.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
new file mode 100644
index 000000000000..5cdcff653918
--- /dev/null
+++ b/drivers/platform/x86/intel_pmic_gpio.c
@@ -0,0 +1,340 @@
1/* Moorestown PMIC GPIO (access through IPC) driver
2 * Copyright (c) 2008 - 2009, Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18/* Supports:
19 * Moorestown platform PMIC chip
20 */
21
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/interrupt.h>
25#include <linux/delay.h>
26#include <linux/stddef.h>
27#include <linux/slab.h>
28#include <linux/ioport.h>
29#include <linux/init.h>
30#include <linux/io.h>
31#include <linux/gpio.h>
32#include <linux/interrupt.h>
33#include <asm/intel_scu_ipc.h>
34#include <linux/device.h>
35#include <linux/intel_pmic_gpio.h>
36#include <linux/platform_device.h>
37
38#define DRIVER_NAME "pmic_gpio"
39
40/* register offset that IPC driver should use
41 * 8 GPIO + 8 GPOSW (6 controllable) + 8GPO
42 */
43enum pmic_gpio_register {
44 GPIO0 = 0xE0,
45 GPIO7 = 0xE7,
46 GPIOINT = 0xE8,
47 GPOSWCTL0 = 0xEC,
48 GPOSWCTL5 = 0xF1,
49 GPO = 0xF4,
50};
51
52/* bits definition for GPIO & GPOSW */
53#define GPIO_DRV 0x01
54#define GPIO_DIR 0x02
55#define GPIO_DIN 0x04
56#define GPIO_DOU 0x08
57#define GPIO_INTCTL 0x30
58#define GPIO_DBC 0xc0
59
60#define GPOSW_DRV 0x01
61#define GPOSW_DOU 0x08
62#define GPOSW_RDRV 0x30
63
64
65#define NUM_GPIO 24
66
67struct pmic_gpio_irq {
68 spinlock_t lock;
69 u32 trigger[NUM_GPIO];
70 u32 dirty;
71 struct work_struct work;
72};
73
74
75struct pmic_gpio {
76 struct gpio_chip chip;
77 struct pmic_gpio_irq irqtypes;
78 void *gpiointr;
79 int irq;
80 unsigned irq_base;
81};
82
83static void pmic_program_irqtype(int gpio, int type)
84{
85 if (type & IRQ_TYPE_EDGE_RISING)
86 intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20);
87 else
88 intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20);
89
90 if (type & IRQ_TYPE_EDGE_FALLING)
91 intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10);
92 else
93 intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10);
94};
95
96static void pmic_irqtype_work(struct work_struct *work)
97{
98 struct pmic_gpio_irq *t =
99 container_of(work, struct pmic_gpio_irq, work);
100 unsigned long flags;
101 int i;
102 u16 type;
103
104 spin_lock_irqsave(&t->lock, flags);
105 /* As we drop the lock, we may need multiple scans if we race the
106 pmic_irq_type function */
107 while (t->dirty) {
108 /*
109 * For each pin that has the dirty bit set send an IPC
110 * message to configure the hardware via the PMIC
111 */
112 for (i = 0; i < NUM_GPIO; i++) {
113 if (!(t->dirty & (1 << i)))
114 continue;
115 t->dirty &= ~(1 << i);
116 /* We can't trust the array entry or dirty
117 once the lock is dropped */
118 type = t->trigger[i];
119 spin_unlock_irqrestore(&t->lock, flags);
120 pmic_program_irqtype(i, type);
121 spin_lock_irqsave(&t->lock, flags);
122 }
123 }
124 spin_unlock_irqrestore(&t->lock, flags);
125}
126
127static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
128{
129 if (offset > 8) {
130 printk(KERN_ERR
131 "%s: only pin 0-7 support input\n", __func__);
132 return -1;/* we only have 8 GPIO can use as input */
133 }
134 return intel_scu_ipc_update_register(GPIO0 + offset,
135 GPIO_DIR, GPIO_DIR);
136}
137
138static int pmic_gpio_direction_output(struct gpio_chip *chip,
139 unsigned offset, int value)
140{
141 int rc = 0;
142
143 if (offset < 8)/* it is GPIO */
144 rc = intel_scu_ipc_update_register(GPIO0 + offset,
145 GPIO_DRV | GPIO_DOU | GPIO_DIR,
146 GPIO_DRV | (value ? GPIO_DOU : 0));
147 else if (offset < 16)/* it is GPOSW */
148 rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
149 GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
150 GPOSW_DRV | (value ? GPOSW_DOU : 0));
151 else if (offset > 15 && offset < 24)/* it is GPO */
152 rc = intel_scu_ipc_update_register(GPO,
153 1 << (offset - 16),
154 value ? 1 << (offset - 16) : 0);
155 else {
156 printk(KERN_ERR
157 "%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
158 WARN_ON(1);
159 }
160
161 return rc;
162}
163
164static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)
165{
166 u8 r;
167 int ret;
168
169 /* we only have 8 GPIO pins we can use as input */
170 if (offset > 8)
171 return -EOPNOTSUPP;
172 ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);
173 if (ret < 0)
174 return ret;
175 return r & GPIO_DIN;
176}
177
178static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
179{
180 if (offset < 8)/* it is GPIO */
181 intel_scu_ipc_update_register(GPIO0 + offset,
182 GPIO_DRV | GPIO_DOU,
183 GPIO_DRV | (value ? GPIO_DOU : 0));
184 else if (offset < 16)/* it is GPOSW */
185 intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
186 GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
187 GPOSW_DRV | (value ? GPOSW_DOU : 0));
188 else if (offset > 15 && offset < 24) /* it is GPO */
189 intel_scu_ipc_update_register(GPO,
190 1 << (offset - 16),
191 value ? 1 << (offset - 16) : 0);
192}
193
194static int pmic_irq_type(unsigned irq, unsigned type)
195{
196 struct pmic_gpio *pg = get_irq_chip_data(irq);
197 u32 gpio = irq - pg->irq_base;
198 unsigned long flags;
199
200 if (gpio > pg->chip.ngpio)
201 return -EINVAL;
202
203 spin_lock_irqsave(&pg->irqtypes.lock, flags);
204 pg->irqtypes.trigger[gpio] = type;
205 pg->irqtypes.dirty |= (1 << gpio);
206 spin_unlock_irqrestore(&pg->irqtypes.lock, flags);
207 schedule_work(&pg->irqtypes.work);
208 return 0;
209}
210
211
212
213static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
214{
215 struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip);
216
217 return pg->irq_base + offset;
218}
219
220/* the gpiointr register is read-clear, so just do nothing. */
221static void pmic_irq_unmask(unsigned irq)
222{
223};
224
225static void pmic_irq_mask(unsigned irq)
226{
227};
228
229static struct irq_chip pmic_irqchip = {
230 .name = "PMIC-GPIO",
231 .mask = pmic_irq_mask,
232 .unmask = pmic_irq_unmask,
233 .set_type = pmic_irq_type,
234};
235
236static void pmic_irq_handler(unsigned irq, struct irq_desc *desc)
237{
238 struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq);
239 u8 intsts = *((u8 *)pg->gpiointr + 4);
240 int gpio;
241
242 for (gpio = 0; gpio < 8; gpio++) {
243 if (intsts & (1 << gpio)) {
244 pr_debug("pmic pin %d triggered\n", gpio);
245 generic_handle_irq(pg->irq_base + gpio);
246 }
247 }
248 desc->chip->eoi(irq);
249}
250
251static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
252{
253 struct device *dev = &pdev->dev;
254 int irq = platform_get_irq(pdev, 0);
255 struct intel_pmic_gpio_platform_data *pdata = dev->platform_data;
256
257 struct pmic_gpio *pg;
258 int retval;
259 int i;
260
261 if (irq < 0) {
262 dev_dbg(dev, "no IRQ line\n");
263 return -EINVAL;
264 }
265
266 if (!pdata || !pdata->gpio_base || !pdata->irq_base) {
267 dev_dbg(dev, "incorrect or missing platform data\n");
268 return -EINVAL;
269 }
270
271 pg = kzalloc(sizeof(*pg), GFP_KERNEL);
272 if (!pg)
273 return -ENOMEM;
274
275 dev_set_drvdata(dev, pg);
276
277 pg->irq = irq;
278 /* setting up SRAM mapping for GPIOINT register */
279 pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8);
280 if (!pg->gpiointr) {
281 printk(KERN_ERR "%s: Can not map GPIOINT.\n", __func__);
282 retval = -EINVAL;
283 goto err2;
284 }
285 pg->irq_base = pdata->irq_base;
286 pg->chip.label = "intel_pmic";
287 pg->chip.direction_input = pmic_gpio_direction_input;
288 pg->chip.direction_output = pmic_gpio_direction_output;
289 pg->chip.get = pmic_gpio_get;
290 pg->chip.set = pmic_gpio_set;
291 pg->chip.to_irq = pmic_gpio_to_irq;
292 pg->chip.base = pdata->gpio_base;
293 pg->chip.ngpio = NUM_GPIO;
294 pg->chip.can_sleep = 1;
295 pg->chip.dev = dev;
296
297 INIT_WORK(&pg->irqtypes.work, pmic_irqtype_work);
298 spin_lock_init(&pg->irqtypes.lock);
299
300 pg->chip.dev = dev;
301 retval = gpiochip_add(&pg->chip);
302 if (retval) {
303 printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__);
304 goto err;
305 }
306 set_irq_data(pg->irq, pg);
307 set_irq_chained_handler(pg->irq, pmic_irq_handler);
308 for (i = 0; i < 8; i++) {
309 set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip,
310 handle_simple_irq, "demux");
311 set_irq_chip_data(i + pg->irq_base, pg);
312 }
313 return 0;
314err:
315 iounmap(pg->gpiointr);
316err2:
317 kfree(pg);
318 return retval;
319}
320
321/* at the same time, register a platform driver
322 * this supports the sfi 0.81 fw */
323static struct platform_driver platform_pmic_gpio_driver = {
324 .driver = {
325 .name = DRIVER_NAME,
326 .owner = THIS_MODULE,
327 },
328 .probe = platform_pmic_gpio_probe,
329};
330
331static int __init platform_pmic_gpio_init(void)
332{
333 return platform_driver_register(&platform_pmic_gpio_driver);
334}
335
336subsys_initcall(platform_pmic_gpio_init);
337
338MODULE_AUTHOR("Alek Du <alek.du@intel.com>");
339MODULE_DESCRIPTION("Intel Moorestown PMIC GPIO driver");
340MODULE_LICENSE("GPL v2");