Blame view

drivers/gpio/gpio-langwell.c 11 KB
c103de240   Grant Likely   gpio: reorganize ...
1
2
3
  /*
   * Moorestown platform Langwell chip GPIO driver
   *
8bf026177   Alek Du   gpio: add Intel M...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   * Copyright (c) 2008 - 2009,  Intel Corporation.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  /* Supports:
   * Moorestown platform Langwell chip.
8081c84c9   Alek Du   gpio: add Penwell...
22
   * Medfield platform Penwell chip.
72b4379e9   Alan Cox   langwell_gpio: ad...
23
   * Whitney point.
8bf026177   Alek Du   gpio: add Intel M...
24
25
26
27
   */
  
  #include <linux/module.h>
  #include <linux/pci.h>
72b4379e9   Alan Cox   langwell_gpio: ad...
28
  #include <linux/platform_device.h>
8bf026177   Alek Du   gpio: add Intel M...
29
30
31
32
33
34
35
36
  #include <linux/kernel.h>
  #include <linux/delay.h>
  #include <linux/stddef.h>
  #include <linux/interrupt.h>
  #include <linux/init.h>
  #include <linux/irq.h>
  #include <linux/io.h>
  #include <linux/gpio.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
37
  #include <linux/slab.h>
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
38
  #include <linux/pm_runtime.h>
8bf026177   Alek Du   gpio: add Intel M...
39

8081c84c9   Alek Du   gpio: add Penwell...
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  /*
   * Langwell chip has 64 pins and thus there are 2 32bit registers to control
   * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit
   * registers to control them, so we only define the order here instead of a
   * structure, to get a bit offset for a pin (use GPDR as an example):
   *
   * nreg = ngpio / 32;
   * reg = offset / 32;
   * bit = offset % 32;
   * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4;
   *
   * so the bit of reg_addr is to control pin offset's GPDR feature
  */
  
  enum GPIO_REG {
  	GPLR = 0,	/* pin level read-only */
  	GPDR,		/* pin direction */
  	GPSR,		/* pin set */
  	GPCR,		/* pin clear */
  	GRER,		/* rising edge detect */
  	GFER,		/* falling edge detect */
  	GEDR,		/* edge detect result */
8bf026177   Alek Du   gpio: add Intel M...
62
63
64
65
  };
  
  struct lnw_gpio {
  	struct gpio_chip		chip;
8081c84c9   Alek Du   gpio: add Penwell...
66
  	void				*reg_base;
8bf026177   Alek Du   gpio: add Intel M...
67
68
  	spinlock_t			lock;
  	unsigned			irq_base;
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
69
  	struct pci_dev			*pdev;
8bf026177   Alek Du   gpio: add Intel M...
70
  };
8081c84c9   Alek Du   gpio: add Penwell...
71
72
  static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
  			enum GPIO_REG reg_type)
8bf026177   Alek Du   gpio: add Intel M...
73
74
  {
  	struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
8081c84c9   Alek Du   gpio: add Penwell...
75
  	unsigned nreg = chip->ngpio / 32;
8bf026177   Alek Du   gpio: add Intel M...
76
  	u8 reg = offset / 32;
8081c84c9   Alek Du   gpio: add Penwell...
77
78
79
80
81
82
83
84
85
  	void __iomem *ptr;
  
  	ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
  	return ptr;
  }
  
  static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
  	void __iomem *gplr = gpio_reg(chip, offset, GPLR);
8bf026177   Alek Du   gpio: add Intel M...
86

8bf026177   Alek Du   gpio: add Intel M...
87
88
89
90
91
  	return readl(gplr) & BIT(offset % 32);
  }
  
  static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  {
8bf026177   Alek Du   gpio: add Intel M...
92
93
94
  	void __iomem *gpsr, *gpcr;
  
  	if (value) {
8081c84c9   Alek Du   gpio: add Penwell...
95
  		gpsr = gpio_reg(chip, offset, GPSR);
8bf026177   Alek Du   gpio: add Intel M...
96
97
  		writel(BIT(offset % 32), gpsr);
  	} else {
8081c84c9   Alek Du   gpio: add Penwell...
98
  		gpcr = gpio_reg(chip, offset, GPCR);
8bf026177   Alek Du   gpio: add Intel M...
99
100
101
102
103
104
105
  		writel(BIT(offset % 32), gpcr);
  	}
  }
  
  static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
  {
  	struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
8081c84c9   Alek Du   gpio: add Penwell...
106
  	void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
8bf026177   Alek Du   gpio: add Intel M...
107
108
  	u32 value;
  	unsigned long flags;
8bf026177   Alek Du   gpio: add Intel M...
109

7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
110
111
  	if (lnw->pdev)
  		pm_runtime_get(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
112
113
114
115
116
  	spin_lock_irqsave(&lnw->lock, flags);
  	value = readl(gpdr);
  	value &= ~BIT(offset % 32);
  	writel(value, gpdr);
  	spin_unlock_irqrestore(&lnw->lock, flags);
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
117
118
119
  
  	if (lnw->pdev)
  		pm_runtime_put(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
120
121
122
123
124
125
126
  	return 0;
  }
  
  static int lnw_gpio_direction_output(struct gpio_chip *chip,
  			unsigned offset, int value)
  {
  	struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
8081c84c9   Alek Du   gpio: add Penwell...
127
  	void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
8bf026177   Alek Du   gpio: add Intel M...
128
  	unsigned long flags;
8bf026177   Alek Du   gpio: add Intel M...
129
130
  
  	lnw_gpio_set(chip, offset, value);
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
131
132
133
  
  	if (lnw->pdev)
  		pm_runtime_get(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
134
135
  	spin_lock_irqsave(&lnw->lock, flags);
  	value = readl(gpdr);
6eab04a87   Justin P. Mattock   treewide: remove ...
136
  	value |= BIT(offset % 32);
8bf026177   Alek Du   gpio: add Intel M...
137
138
  	writel(value, gpdr);
  	spin_unlock_irqrestore(&lnw->lock, flags);
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
139
140
141
  
  	if (lnw->pdev)
  		pm_runtime_put(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
142
143
144
145
146
147
148
149
  	return 0;
  }
  
  static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
  {
  	struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip);
  	return lnw->irq_base + offset;
  }
5ffd72c67   Lennert Buytenhek   gpio: langwell_gp...
150
  static int lnw_irq_type(struct irq_data *d, unsigned type)
8bf026177   Alek Du   gpio: add Intel M...
151
  {
5ffd72c67   Lennert Buytenhek   gpio: langwell_gp...
152
153
  	struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d);
  	u32 gpio = d->irq - lnw->irq_base;
8bf026177   Alek Du   gpio: add Intel M...
154
155
  	unsigned long flags;
  	u32 value;
8081c84c9   Alek Du   gpio: add Penwell...
156
157
  	void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER);
  	void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER);
8bf026177   Alek Du   gpio: add Intel M...
158

4efec6272   Roel Kluin   gpio: fix test on...
159
  	if (gpio >= lnw->chip.ngpio)
8bf026177   Alek Du   gpio: add Intel M...
160
  		return -EINVAL;
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
161
162
163
  
  	if (lnw->pdev)
  		pm_runtime_get(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
164
165
166
167
168
169
170
171
172
173
174
175
176
  	spin_lock_irqsave(&lnw->lock, flags);
  	if (type & IRQ_TYPE_EDGE_RISING)
  		value = readl(grer) | BIT(gpio % 32);
  	else
  		value = readl(grer) & (~BIT(gpio % 32));
  	writel(value, grer);
  
  	if (type & IRQ_TYPE_EDGE_FALLING)
  		value = readl(gfer) | BIT(gpio % 32);
  	else
  		value = readl(gfer) & (~BIT(gpio % 32));
  	writel(value, gfer);
  	spin_unlock_irqrestore(&lnw->lock, flags);
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
177
178
  	if (lnw->pdev)
  		pm_runtime_put(&lnw->pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
179
  	return 0;
fd0574cb5   Andrew Morton   drivers/gpio/lang...
180
  }
8bf026177   Alek Du   gpio: add Intel M...
181

5ffd72c67   Lennert Buytenhek   gpio: langwell_gp...
182
  static void lnw_irq_unmask(struct irq_data *d)
8bf026177   Alek Du   gpio: add Intel M...
183
  {
fd0574cb5   Andrew Morton   drivers/gpio/lang...
184
  }
8bf026177   Alek Du   gpio: add Intel M...
185

5ffd72c67   Lennert Buytenhek   gpio: langwell_gp...
186
  static void lnw_irq_mask(struct irq_data *d)
8bf026177   Alek Du   gpio: add Intel M...
187
  {
fd0574cb5   Andrew Morton   drivers/gpio/lang...
188
  }
8bf026177   Alek Du   gpio: add Intel M...
189
190
191
  
  static struct irq_chip lnw_irqchip = {
  	.name		= "LNW-GPIO",
5ffd72c67   Lennert Buytenhek   gpio: langwell_gp...
192
193
194
  	.irq_mask	= lnw_irq_mask,
  	.irq_unmask	= lnw_irq_unmask,
  	.irq_set_type	= lnw_irq_type,
8bf026177   Alek Du   gpio: add Intel M...
195
  };
8081c84c9   Alek Du   gpio: add Penwell...
196
197
198
199
  static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = {   /* pin number */
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 },
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 },
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 },
8bf026177   Alek Du   gpio: add Intel M...
200
201
202
203
204
205
  	{ 0, }
  };
  MODULE_DEVICE_TABLE(pci, lnw_gpio_ids);
  
  static void lnw_irq_handler(unsigned irq, struct irq_desc *desc)
  {
20e2aa916   Thomas Gleixner   gpio/langwell: Fi...
206
207
208
  	struct irq_data *data = irq_desc_get_irq_data(desc);
  	struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data);
  	struct irq_chip *chip = irq_data_get_irq_chip(data);
84bead6c3   Thomas Gleixner   gpio/langwell: Cl...
209
  	u32 base, gpio, mask;
732063b92   Thomas Gleixner   gpio/langwell: Si...
210
  	unsigned long pending;
8bf026177   Alek Du   gpio: add Intel M...
211
  	void __iomem *gedr;
8bf026177   Alek Du   gpio: add Intel M...
212
213
  
  	/* check GPIO controller to check which pin triggered the interrupt */
8081c84c9   Alek Du   gpio: add Penwell...
214
215
  	for (base = 0; base < lnw->chip.ngpio; base += 32) {
  		gedr = gpio_reg(&lnw->chip, base, GEDR);
84bead6c3   Thomas Gleixner   gpio/langwell: Cl...
216
  		pending = readl(gedr);
732063b92   Thomas Gleixner   gpio/langwell: Si...
217
  		while (pending) {
2345b20fd   Mathias Nyman   gpio/langwell_gpi...
218
  			gpio = __ffs(pending);
84bead6c3   Thomas Gleixner   gpio/langwell: Cl...
219
220
221
222
  			mask = BIT(gpio);
  			pending &= ~mask;
  			/* Clear before handling so we can't lose an edge */
  			writel(mask, gedr);
732063b92   Thomas Gleixner   gpio/langwell: Si...
223
224
  			generic_handle_irq(lnw->irq_base + base + gpio);
  		}
8bf026177   Alek Du   gpio: add Intel M...
225
  	}
0766d20fd   Feng Tang   langwell_gpio: mo...
226

20e2aa916   Thomas Gleixner   gpio/langwell: Fi...
227
  	chip->irq_eoi(data);
8bf026177   Alek Du   gpio: add Intel M...
228
  }
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  #ifdef CONFIG_PM
  static int lnw_gpio_runtime_resume(struct device *dev)
  {
  	return 0;
  }
  
  static int lnw_gpio_runtime_suspend(struct device *dev)
  {
  	return 0;
  }
  
  static int lnw_gpio_runtime_idle(struct device *dev)
  {
  	int err = pm_schedule_suspend(dev, 500);
  
  	if (!err)
  		return 0;
  
  	return -EBUSY;
  }
  
  #else
  #define lnw_gpio_runtime_suspend	NULL
  #define lnw_gpio_runtime_resume		NULL
  #define lnw_gpio_runtime_idle		NULL
  #endif
  
  static const struct dev_pm_ops lnw_gpio_pm_ops = {
  	.runtime_suspend = lnw_gpio_runtime_suspend,
  	.runtime_resume = lnw_gpio_runtime_resume,
  	.runtime_idle = lnw_gpio_runtime_idle,
  };
8bf026177   Alek Du   gpio: add Intel M...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
  			const struct pci_device_id *id)
  {
  	void *base;
  	int i;
  	resource_size_t start, len;
  	struct lnw_gpio *lnw;
  	u32 irq_base;
  	u32 gpio_base;
  	int retval = 0;
  
  	retval = pci_enable_device(pdev);
  	if (retval)
  		goto done;
  
  	retval = pci_request_regions(pdev, "langwell_gpio");
  	if (retval) {
  		dev_err(&pdev->dev, "error requesting resources
  ");
  		goto err2;
  	}
  	/* get the irq_base from bar1 */
  	start = pci_resource_start(pdev, 1);
  	len = pci_resource_len(pdev, 1);
  	base = ioremap_nocache(start, len);
  	if (!base) {
  		dev_err(&pdev->dev, "error mapping bar1
  ");
  		goto err3;
  	}
  	irq_base = *(u32 *)base;
  	gpio_base = *((u32 *)base + 1);
  	/* release the IO mapping, since we already get the info from bar1 */
  	iounmap(base);
  	/* get the register base from bar0 */
  	start = pci_resource_start(pdev, 0);
  	len = pci_resource_len(pdev, 0);
  	base = ioremap_nocache(start, len);
  	if (!base) {
  		dev_err(&pdev->dev, "error mapping bar0
  ");
  		retval = -EFAULT;
  		goto err3;
  	}
  
  	lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL);
  	if (!lnw) {
  		dev_err(&pdev->dev, "can't allocate langwell_gpio chip data
  ");
  		retval = -ENOMEM;
  		goto err4;
  	}
  	lnw->reg_base = base;
  	lnw->irq_base = irq_base;
  	lnw->chip.label = dev_name(&pdev->dev);
  	lnw->chip.direction_input = lnw_gpio_direction_input;
  	lnw->chip.direction_output = lnw_gpio_direction_output;
  	lnw->chip.get = lnw_gpio_get;
  	lnw->chip.set = lnw_gpio_set;
  	lnw->chip.to_irq = lnw_gpio_to_irq;
  	lnw->chip.base = gpio_base;
8081c84c9   Alek Du   gpio: add Penwell...
322
  	lnw->chip.ngpio = id->driver_data;
8bf026177   Alek Du   gpio: add Intel M...
323
  	lnw->chip.can_sleep = 0;
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
324
  	lnw->pdev = pdev;
8bf026177   Alek Du   gpio: add Intel M...
325
326
327
328
329
330
331
  	pci_set_drvdata(pdev, lnw);
  	retval = gpiochip_add(&lnw->chip);
  	if (retval) {
  		dev_err(&pdev->dev, "langwell gpiochip_add error %d
  ", retval);
  		goto err5;
  	}
674db9069   Thomas Gleixner   gpio/langwell: Co...
332
333
  	irq_set_handler_data(pdev->irq, lnw);
  	irq_set_chained_handler(pdev->irq, lnw_irq_handler);
8bf026177   Alek Du   gpio: add Intel M...
334
  	for (i = 0; i < lnw->chip.ngpio; i++) {
674db9069   Thomas Gleixner   gpio/langwell: Co...
335
336
337
  		irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip,
  					      handle_simple_irq, "demux");
  		irq_set_chip_data(i + lnw->irq_base, lnw);
8bf026177   Alek Du   gpio: add Intel M...
338
339
340
  	}
  
  	spin_lock_init(&lnw->lock);
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
341
342
343
  
  	pm_runtime_put_noidle(&pdev->dev);
  	pm_runtime_allow(&pdev->dev);
8bf026177   Alek Du   gpio: add Intel M...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  	goto done;
  err5:
  	kfree(lnw);
  err4:
  	iounmap(base);
  err3:
  	pci_release_regions(pdev);
  err2:
  	pci_disable_device(pdev);
  done:
  	return retval;
  }
  
  static struct pci_driver lnw_gpio_driver = {
  	.name		= "langwell_gpio",
  	.id_table	= lnw_gpio_ids,
  	.probe		= lnw_gpio_probe,
7812803a3   Kristen Carlson Accardi   langwell_gpio: ad...
361
362
363
  	.driver		= {
  		.pm	= &lnw_gpio_pm_ops,
  	},
8bf026177   Alek Du   gpio: add Intel M...
364
  };
72b4379e9   Alan Cox   langwell_gpio: ad...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  
  static int __devinit wp_gpio_probe(struct platform_device *pdev)
  {
  	struct lnw_gpio *lnw;
  	struct gpio_chip *gc;
  	struct resource *rc;
  	int retval = 0;
  
  	rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!rc)
  		return -EINVAL;
  
  	lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL);
  	if (!lnw) {
  		dev_err(&pdev->dev,
  			"can't allocate whitneypoint_gpio chip data
  ");
  		return -ENOMEM;
  	}
  	lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc));
  	if (lnw->reg_base == NULL) {
  		retval = -EINVAL;
  		goto err_kmalloc;
  	}
  	spin_lock_init(&lnw->lock);
  	gc = &lnw->chip;
  	gc->label = dev_name(&pdev->dev);
  	gc->owner = THIS_MODULE;
  	gc->direction_input = lnw_gpio_direction_input;
  	gc->direction_output = lnw_gpio_direction_output;
  	gc->get = lnw_gpio_get;
  	gc->set = lnw_gpio_set;
  	gc->to_irq = NULL;
  	gc->base = 0;
  	gc->ngpio = 64;
  	gc->can_sleep = 0;
  	retval = gpiochip_add(gc);
  	if (retval) {
  		dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d
  ",
  								retval);
  		goto err_ioremap;
  	}
  	platform_set_drvdata(pdev, lnw);
  	return 0;
  err_ioremap:
  	iounmap(lnw->reg_base);
  err_kmalloc:
  	kfree(lnw);
  	return retval;
  }
  
  static int __devexit wp_gpio_remove(struct platform_device *pdev)
  {
  	struct lnw_gpio *lnw = platform_get_drvdata(pdev);
  	int err;
  	err = gpiochip_remove(&lnw->chip);
  	if (err)
  		dev_err(&pdev->dev, "failed to remove gpio_chip.
  ");
  	iounmap(lnw->reg_base);
  	kfree(lnw);
  	platform_set_drvdata(pdev, NULL);
  	return 0;
  }
  
  static struct platform_driver wp_gpio_driver = {
  	.probe		= wp_gpio_probe,
  	.remove		= __devexit_p(wp_gpio_remove),
  	.driver		= {
  		.name	= "wp_gpio",
  		.owner	= THIS_MODULE,
  	},
  };
8bf026177   Alek Du   gpio: add Intel M...
439
440
  static int __init lnw_gpio_init(void)
  {
72b4379e9   Alan Cox   langwell_gpio: ad...
441
442
443
444
445
446
447
448
  	int ret;
  	ret =  pci_register_driver(&lnw_gpio_driver);
  	if (ret < 0)
  		return ret;
  	ret = platform_driver_register(&wp_gpio_driver);
  	if (ret < 0)
  		pci_unregister_driver(&lnw_gpio_driver);
  	return ret;
8bf026177   Alek Du   gpio: add Intel M...
449
450
451
  }
  
  device_initcall(lnw_gpio_init);