Blame view

drivers/gpio/gpio-timberdale.c 7.52 KB
35570ac60   Richard Röjfors   gpio: add GPIO dr...
1
  /*
c103de240   Grant Likely   gpio: reorganize ...
2
   * Timberdale FPGA GPIO driver
52ad90531   Paul Gortmaker   gpio: timberdale:...
3
   * Author: Mocean Laboratories
35570ac60   Richard Röjfors   gpio: add GPIO dr...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   * Copyright (c) 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:
   * Timberdale FPGA GPIO
   */
52ad90531   Paul Gortmaker   gpio: timberdale:...
23
  #include <linux/init.h>
35570ac60   Richard Röjfors   gpio: add GPIO dr...
24
25
  #include <linux/gpio.h>
  #include <linux/platform_device.h>
e3cb91ce1   David Miller   timbgpio: fix build
26
  #include <linux/irq.h>
35570ac60   Richard Röjfors   gpio: add GPIO dr...
27
28
29
  #include <linux/io.h>
  #include <linux/timb_gpio.h>
  #include <linux/interrupt.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
30
  #include <linux/slab.h>
35570ac60   Richard Röjfors   gpio: add GPIO dr...
31
32
33
34
35
36
37
38
39
40
41
  
  #define DRIVER_NAME "timb-gpio"
  
  #define TGPIOVAL	0x00
  #define TGPIODIR	0x04
  #define TGPIO_IER	0x08
  #define TGPIO_ISR	0x0c
  #define TGPIO_IPR	0x10
  #define TGPIO_ICR	0x14
  #define TGPIO_FLR	0x18
  #define TGPIO_LVR	0x1c
8c35c89aa   Richard Röjfors   timbgpio: add sup...
42
43
  #define TGPIO_VER	0x20
  #define TGPIO_BFLR	0x24
35570ac60   Richard Röjfors   gpio: add GPIO dr...
44
45
46
47
48
49
  
  struct timbgpio {
  	void __iomem		*membase;
  	spinlock_t		lock; /* mutual exclusion */
  	struct gpio_chip	gpio;
  	int			irq_base;
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
50
  	unsigned long		last_ier;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
51
52
53
54
55
  };
  
  static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
  	unsigned offset, bool enabled)
  {
92a41e2f7   Linus Walleij   gpio: timberdale:...
56
  	struct timbgpio *tgpio = gpiochip_get_data(gpio);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  	u32 reg;
  
  	spin_lock(&tgpio->lock);
  	reg = ioread32(tgpio->membase + offset);
  
  	if (enabled)
  		reg |= (1 << index);
  	else
  		reg &= ~(1 << index);
  
  	iowrite32(reg, tgpio->membase + offset);
  	spin_unlock(&tgpio->lock);
  
  	return 0;
  }
  
  static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
  {
  	return timbgpio_update_bit(gpio, nr, TGPIODIR, true);
  }
  
  static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
  {
92a41e2f7   Linus Walleij   gpio: timberdale:...
80
  	struct timbgpio *tgpio = gpiochip_get_data(gpio);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  	u32 value;
  
  	value = ioread32(tgpio->membase + TGPIOVAL);
  	return (value & (1 << nr)) ? 1 : 0;
  }
  
  static int timbgpio_gpio_direction_output(struct gpio_chip *gpio,
  						unsigned nr, int val)
  {
  	return timbgpio_update_bit(gpio, nr, TGPIODIR, false);
  }
  
  static void timbgpio_gpio_set(struct gpio_chip *gpio,
  				unsigned nr, int val)
  {
  	timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0);
  }
  
  static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset)
  {
92a41e2f7   Linus Walleij   gpio: timberdale:...
101
  	struct timbgpio *tgpio = gpiochip_get_data(gpio);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
102
103
104
105
106
107
108
109
110
111
  
  	if (tgpio->irq_base <= 0)
  		return -EINVAL;
  
  	return tgpio->irq_base + offset;
  }
  
  /*
   * GPIO IRQ
   */
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
112
  static void timbgpio_irq_disable(struct irq_data *d)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
113
  {
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
114
115
  	struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
  	int offset = d->irq - tgpio->irq_base;
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
116
  	unsigned long flags;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
117

76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
118
  	spin_lock_irqsave(&tgpio->lock, flags);
d79550a7b   Dan Carpenter   gpio-timberdale: ...
119
  	tgpio->last_ier &= ~(1UL << offset);
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
120
121
  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
  	spin_unlock_irqrestore(&tgpio->lock, flags);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
122
  }
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
123
  static void timbgpio_irq_enable(struct irq_data *d)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
124
  {
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
125
126
  	struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
  	int offset = d->irq - tgpio->irq_base;
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
127
  	unsigned long flags;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
128

76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
129
  	spin_lock_irqsave(&tgpio->lock, flags);
d79550a7b   Dan Carpenter   gpio-timberdale: ...
130
  	tgpio->last_ier |= 1UL << offset;
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
131
132
  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
  	spin_unlock_irqrestore(&tgpio->lock, flags);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
133
  }
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
134
  static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
135
  {
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
136
137
  	struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
  	int offset = d->irq - tgpio->irq_base;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
138
  	unsigned long flags;
8c35c89aa   Richard Röjfors   timbgpio: add sup...
139
140
  	u32 lvr, flr, bflr = 0;
  	u32 ver;
2a481800c   Julia Lawall   drivers/gpio/timb...
141
  	int ret = 0;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
142
143
144
  
  	if (offset < 0 || offset > tgpio->gpio.ngpio)
  		return -EINVAL;
8c35c89aa   Richard Röjfors   timbgpio: add sup...
145
  	ver = ioread32(tgpio->membase + TGPIO_VER);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
146
147
148
149
  	spin_lock_irqsave(&tgpio->lock, flags);
  
  	lvr = ioread32(tgpio->membase + TGPIO_LVR);
  	flr = ioread32(tgpio->membase + TGPIO_FLR);
8c35c89aa   Richard Röjfors   timbgpio: add sup...
150
151
  	if (ver > 2)
  		bflr = ioread32(tgpio->membase + TGPIO_BFLR);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
152
153
  
  	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
8c35c89aa   Richard Röjfors   timbgpio: add sup...
154
  		bflr &= ~(1 << offset);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
155
156
157
158
159
160
  		flr &= ~(1 << offset);
  		if (trigger & IRQ_TYPE_LEVEL_HIGH)
  			lvr |= 1 << offset;
  		else
  			lvr &= ~(1 << offset);
  	}
8c35c89aa   Richard Röjfors   timbgpio: add sup...
161
  	if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
2a481800c   Julia Lawall   drivers/gpio/timb...
162
163
164
  		if (ver < 3) {
  			ret = -EINVAL;
  			goto out;
8a29a4096   Laurent Navet   gpio: gpio-timber...
165
  		} else {
8c35c89aa   Richard Röjfors   timbgpio: add sup...
166
167
168
169
170
  			flr |= 1 << offset;
  			bflr |= 1 << offset;
  		}
  	} else {
  		bflr &= ~(1 << offset);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
171
  		flr |= 1 << offset;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
172
  		if (trigger & IRQ_TYPE_EDGE_FALLING)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
173
  			lvr &= ~(1 << offset);
8c35c89aa   Richard Röjfors   timbgpio: add sup...
174
175
  		else
  			lvr |= 1 << offset;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
176
177
178
179
  	}
  
  	iowrite32(lvr, tgpio->membase + TGPIO_LVR);
  	iowrite32(flr, tgpio->membase + TGPIO_FLR);
8c35c89aa   Richard Röjfors   timbgpio: add sup...
180
181
  	if (ver > 2)
  		iowrite32(bflr, tgpio->membase + TGPIO_BFLR);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
182
  	iowrite32(1 << offset, tgpio->membase + TGPIO_ICR);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
183

2a481800c   Julia Lawall   drivers/gpio/timb...
184
185
186
  out:
  	spin_unlock_irqrestore(&tgpio->lock, flags);
  	return ret;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
187
  }
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
188
  static void timbgpio_irq(struct irq_desc *desc)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
189
  {
476f8b4c9   Jiang Liu   gpio: Use irq_des...
190
191
  	struct timbgpio *tgpio = irq_desc_get_handler_data(desc);
  	struct irq_data *data = irq_desc_get_irq_data(desc);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
192
193
  	unsigned long ipr;
  	int offset;
476f8b4c9   Jiang Liu   gpio: Use irq_des...
194
  	data->chip->irq_ack(data);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
195
196
  	ipr = ioread32(tgpio->membase + TGPIO_IPR);
  	iowrite32(ipr, tgpio->membase + TGPIO_ICR);
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
197
198
199
200
201
  	/*
  	 * Some versions of the hardware trash the IER register if more than
  	 * one interrupt is received simultaneously.
  	 */
  	iowrite32(0, tgpio->membase + TGPIO_IER);
984b3f574   Akinobu Mita   bitops: rename fo...
202
  	for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
203
  		generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
76d800a5b   Tomas Hallenberg   gpio: timbgpio: u...
204
205
  
  	iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
206
207
208
209
  }
  
  static struct irq_chip timbgpio_irqchip = {
  	.name		= "GPIO",
a1f5f22ad   Lennert Buytenhek   gpio: timbgpio: i...
210
211
212
  	.irq_enable	= timbgpio_irq_enable,
  	.irq_disable	= timbgpio_irq_disable,
  	.irq_set_type	= timbgpio_irq_type,
35570ac60   Richard Röjfors   gpio: add GPIO dr...
213
  };
3836309d9   Bill Pemberton   gpio: remove use ...
214
  static int timbgpio_probe(struct platform_device *pdev)
35570ac60   Richard Röjfors   gpio: add GPIO dr...
215
216
  {
  	int err, i;
0ed3398ed   abdoulaye berthe   gpio: timberdale:...
217
  	struct device *dev = &pdev->dev;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
218
219
220
  	struct gpio_chip *gc;
  	struct timbgpio *tgpio;
  	struct resource *iomem;
e56aee189   Jingoo Han   gpio: use dev_get...
221
  	struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
222
223
224
  	int irq = platform_get_irq(pdev, 0);
  
  	if (!pdata || pdata->nr_pins > 32) {
0ed3398ed   abdoulaye berthe   gpio: timberdale:...
225
226
227
  		dev_err(dev, "Invalid platform data
  ");
  		return -EINVAL;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
228
  	}
0ed3398ed   abdoulaye berthe   gpio: timberdale:...
229
  	tgpio = devm_kzalloc(dev, sizeof(struct timbgpio), GFP_KERNEL);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
230
  	if (!tgpio) {
0ed3398ed   abdoulaye berthe   gpio: timberdale:...
231
232
233
  		dev_err(dev, "Memory alloc failed
  ");
  		return -EINVAL;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
234
235
236
237
  	}
  	tgpio->irq_base = pdata->irq_base;
  
  	spin_lock_init(&tgpio->lock);
fa283db76   Amitoj Kaur Chawla   gpio: timberdale:...
238
239
240
241
  	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	tgpio->membase = devm_ioremap_resource(dev, iomem);
  	if (IS_ERR(tgpio->membase))
  		return PTR_ERR(tgpio->membase);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
242
243
244
245
246
  
  	gc = &tgpio->gpio;
  
  	gc->label = dev_name(&pdev->dev);
  	gc->owner = THIS_MODULE;
58383c784   Linus Walleij   gpio: change memb...
247
  	gc->parent = &pdev->dev;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
248
249
250
251
252
253
254
255
  	gc->direction_input = timbgpio_gpio_direction_input;
  	gc->get = timbgpio_gpio_get;
  	gc->direction_output = timbgpio_gpio_direction_output;
  	gc->set = timbgpio_gpio_set;
  	gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
  	gc->dbg_show = NULL;
  	gc->base = pdata->gpio_base;
  	gc->ngpio = pdata->nr_pins;
9fb1f39eb   Linus Walleij   gpio/pinctrl: mak...
256
  	gc->can_sleep = false;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
257

43fad8322   Laxman Dewangan   gpio: timberdale ...
258
  	err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
259
  	if (err)
0ed3398ed   abdoulaye berthe   gpio: timberdale:...
260
  		return err;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
261
262
263
264
265
266
267
268
269
270
  
  	platform_set_drvdata(pdev, tgpio);
  
  	/* make sure to disable interrupts */
  	iowrite32(0x0, tgpio->membase + TGPIO_IER);
  
  	if (irq < 0 || tgpio->irq_base <= 0)
  		return 0;
  
  	for (i = 0; i < pdata->nr_pins; i++) {
e5428a682   Linus Walleij   gpio: drop users ...
271
272
  		irq_set_chip_and_handler(tgpio->irq_base + i,
  			&timbgpio_irqchip, handle_simple_irq);
b51804bcf   Thomas Gleixner   gpio: Cleanup gen...
273
  		irq_set_chip_data(tgpio->irq_base + i, tgpio);
23393d49f   Rob Herring   gpio: kill off se...
274
  		irq_clear_status_flags(tgpio->irq_base + i, IRQ_NOREQUEST | IRQ_NOPROBE);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
275
  	}
8a52211ad   Thomas Gleixner   gpio/timberdale: ...
276
  	irq_set_chained_handler_and_data(irq, timbgpio_irq, tgpio);
35570ac60   Richard Röjfors   gpio: add GPIO dr...
277
278
  
  	return 0;
35570ac60   Richard Röjfors   gpio: add GPIO dr...
279
  }
35570ac60   Richard Röjfors   gpio: add GPIO dr...
280
281
  static struct platform_driver timbgpio_platform_driver = {
  	.driver = {
52ad90531   Paul Gortmaker   gpio: timberdale:...
282
283
  		.name			= DRIVER_NAME,
  		.suppress_bind_attrs	= true,
35570ac60   Richard Röjfors   gpio: add GPIO dr...
284
285
  	},
  	.probe		= timbgpio_probe,
35570ac60   Richard Röjfors   gpio: add GPIO dr...
286
287
288
  };
  
  /*--------------------------------------------------------------------------*/
52ad90531   Paul Gortmaker   gpio: timberdale:...
289
  builtin_platform_driver(timbgpio_platform_driver);