Commit | Line | Data |
---|---|---|
e400bae9 YY |
1 | /* |
2 | * Driver for NEC VR4100 series General-purpose I/O Unit. | |
3 | * | |
4 | * Copyright (C) 2002 MontaVista Software Inc. | |
5 | * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> | |
364ca8a8 | 6 | * Copyright (C) 2003-2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> |
e400bae9 YY |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | */ | |
e400bae9 YY |
22 | #include <linux/errno.h> |
23 | #include <linux/fs.h> | |
24 | #include <linux/init.h> | |
e400bae9 | 25 | #include <linux/interrupt.h> |
44173fb2 | 26 | #include <linux/irq.h> |
e400bae9 YY |
27 | #include <linux/kernel.h> |
28 | #include <linux/module.h> | |
44173fb2 | 29 | #include <linux/platform_device.h> |
e400bae9 YY |
30 | #include <linux/spinlock.h> |
31 | #include <linux/types.h> | |
32 | ||
e400bae9 YY |
33 | #include <asm/io.h> |
34 | #include <asm/vr41xx/giu.h> | |
66151bbd | 35 | #include <asm/vr41xx/irq.h> |
e400bae9 YY |
36 | #include <asm/vr41xx/vr41xx.h> |
37 | ||
29ce2c76 | 38 | MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>"); |
e400bae9 YY |
39 | MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); |
40 | MODULE_LICENSE("GPL"); | |
41 | ||
42 | static int major; /* default is dynamic major device number */ | |
43 | module_param(major, int, 0); | |
44 | MODULE_PARM_DESC(major, "Major device number"); | |
45 | ||
e400bae9 YY |
46 | #define GIUIOSELL 0x00 |
47 | #define GIUIOSELH 0x02 | |
48 | #define GIUPIODL 0x04 | |
49 | #define GIUPIODH 0x06 | |
50 | #define GIUINTSTATL 0x08 | |
51 | #define GIUINTSTATH 0x0a | |
52 | #define GIUINTENL 0x0c | |
53 | #define GIUINTENH 0x0e | |
54 | #define GIUINTTYPL 0x10 | |
55 | #define GIUINTTYPH 0x12 | |
56 | #define GIUINTALSELL 0x14 | |
57 | #define GIUINTALSELH 0x16 | |
58 | #define GIUINTHTSELL 0x18 | |
59 | #define GIUINTHTSELH 0x1a | |
60 | #define GIUPODATL 0x1c | |
61 | #define GIUPODATEN 0x1c | |
62 | #define GIUPODATH 0x1e | |
63 | #define PIOEN0 0x0100 | |
64 | #define PIOEN1 0x0200 | |
65 | #define GIUPODAT 0x1e | |
66 | #define GIUFEDGEINHL 0x20 | |
67 | #define GIUFEDGEINHH 0x22 | |
68 | #define GIUREDGEINHL 0x24 | |
69 | #define GIUREDGEINHH 0x26 | |
70 | ||
71 | #define GIUUSEUPDN 0x1e0 | |
72 | #define GIUTERMUPDN 0x1e2 | |
73 | ||
74 | #define GPIO_HAS_PULLUPDOWN_IO 0x0001 | |
75 | #define GPIO_HAS_OUTPUT_ENABLE 0x0002 | |
76 | #define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 | |
77 | ||
78 | static spinlock_t giu_lock; | |
e400bae9 YY |
79 | static unsigned long giu_flags; |
80 | static unsigned int giu_nr_pins; | |
81 | ||
82 | static void __iomem *giu_base; | |
83 | ||
84 | #define giu_read(offset) readw(giu_base + (offset)) | |
85 | #define giu_write(offset, value) writew((value), giu_base + (offset)) | |
86 | ||
87 | #define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) | |
88 | #define GIUINT_HIGH_OFFSET 16 | |
89 | #define GIUINT_HIGH_MAX 32 | |
90 | ||
91 | static inline uint16_t giu_set(uint16_t offset, uint16_t set) | |
92 | { | |
93 | uint16_t data; | |
94 | ||
95 | data = giu_read(offset); | |
96 | data |= set; | |
97 | giu_write(offset, data); | |
98 | ||
99 | return data; | |
100 | } | |
101 | ||
102 | static inline uint16_t giu_clear(uint16_t offset, uint16_t clear) | |
103 | { | |
104 | uint16_t data; | |
105 | ||
106 | data = giu_read(offset); | |
107 | data &= ~clear; | |
108 | giu_write(offset, data); | |
109 | ||
110 | return data; | |
111 | } | |
112 | ||
364ca8a8 | 113 | static void ack_giuint_low(unsigned int irq) |
e400bae9 | 114 | { |
364ca8a8 | 115 | giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq)); |
e400bae9 YY |
116 | } |
117 | ||
364ca8a8 | 118 | static void mask_giuint_low(unsigned int irq) |
e400bae9 YY |
119 | { |
120 | giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq)); | |
121 | } | |
122 | ||
364ca8a8 | 123 | static void mask_ack_giuint_low(unsigned int irq) |
e400bae9 YY |
124 | { |
125 | unsigned int pin; | |
126 | ||
127 | pin = GPIO_PIN_OF_IRQ(irq); | |
128 | giu_clear(GIUINTENL, 1 << pin); | |
129 | giu_write(GIUINTSTATL, 1 << pin); | |
130 | } | |
131 | ||
364ca8a8 | 132 | static void unmask_giuint_low(unsigned int irq) |
e400bae9 | 133 | { |
364ca8a8 | 134 | giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq)); |
e400bae9 YY |
135 | } |
136 | ||
364ca8a8 YY |
137 | static struct irq_chip giuint_low_irq_chip = { |
138 | .name = "GIUINTL", | |
139 | .ack = ack_giuint_low, | |
140 | .mask = mask_giuint_low, | |
141 | .mask_ack = mask_ack_giuint_low, | |
142 | .unmask = unmask_giuint_low, | |
e400bae9 YY |
143 | }; |
144 | ||
364ca8a8 | 145 | static void ack_giuint_high(unsigned int irq) |
e400bae9 | 146 | { |
364ca8a8 | 147 | giu_write(GIUINTSTATH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); |
e400bae9 YY |
148 | } |
149 | ||
364ca8a8 | 150 | static void mask_giuint_high(unsigned int irq) |
e400bae9 YY |
151 | { |
152 | giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); | |
153 | } | |
154 | ||
364ca8a8 | 155 | static void mask_ack_giuint_high(unsigned int irq) |
e400bae9 YY |
156 | { |
157 | unsigned int pin; | |
158 | ||
159 | pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET; | |
160 | giu_clear(GIUINTENH, 1 << pin); | |
161 | giu_write(GIUINTSTATH, 1 << pin); | |
162 | } | |
163 | ||
364ca8a8 | 164 | static void unmask_giuint_high(unsigned int irq) |
e400bae9 | 165 | { |
364ca8a8 | 166 | giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); |
e400bae9 YY |
167 | } |
168 | ||
364ca8a8 YY |
169 | static struct irq_chip giuint_high_irq_chip = { |
170 | .name = "GIUINTH", | |
171 | .ack = ack_giuint_high, | |
172 | .mask = mask_giuint_high, | |
173 | .mask_ack = mask_ack_giuint_high, | |
174 | .unmask = unmask_giuint_high, | |
e400bae9 YY |
175 | }; |
176 | ||
7d12e780 | 177 | static int giu_get_irq(unsigned int irq) |
e400bae9 YY |
178 | { |
179 | uint16_t pendl, pendh, maskl, maskh; | |
180 | int i; | |
181 | ||
182 | pendl = giu_read(GIUINTSTATL); | |
183 | pendh = giu_read(GIUINTSTATH); | |
184 | maskl = giu_read(GIUINTENL); | |
185 | maskh = giu_read(GIUINTENH); | |
186 | ||
187 | maskl &= pendl; | |
188 | maskh &= pendh; | |
189 | ||
190 | if (maskl) { | |
191 | for (i = 0; i < 16; i++) { | |
192 | if (maskl & (1 << i)) | |
193 | return GIU_IRQ(i); | |
194 | } | |
195 | } else if (maskh) { | |
196 | for (i = 0; i < 16; i++) { | |
197 | if (maskh & (1 << i)) | |
198 | return GIU_IRQ(i + GIUINT_HIGH_OFFSET); | |
199 | } | |
200 | } | |
201 | ||
202 | printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", | |
203 | maskl, pendl, maskh, pendh); | |
204 | ||
205 | atomic_inc(&irq_err_count); | |
206 | ||
207 | return -EINVAL; | |
208 | } | |
209 | ||
210 | void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_t signal) | |
211 | { | |
212 | uint16_t mask; | |
213 | ||
214 | if (pin < GIUINT_HIGH_OFFSET) { | |
215 | mask = 1 << pin; | |
216 | if (trigger != IRQ_TRIGGER_LEVEL) { | |
217 | giu_set(GIUINTTYPL, mask); | |
218 | if (signal == IRQ_SIGNAL_HOLD) | |
219 | giu_set(GIUINTHTSELL, mask); | |
220 | else | |
221 | giu_clear(GIUINTHTSELL, mask); | |
44173fb2 | 222 | if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { |
e400bae9 YY |
223 | switch (trigger) { |
224 | case IRQ_TRIGGER_EDGE_FALLING: | |
225 | giu_set(GIUFEDGEINHL, mask); | |
226 | giu_clear(GIUREDGEINHL, mask); | |
227 | break; | |
228 | case IRQ_TRIGGER_EDGE_RISING: | |
229 | giu_clear(GIUFEDGEINHL, mask); | |
230 | giu_set(GIUREDGEINHL, mask); | |
231 | break; | |
232 | default: | |
233 | giu_set(GIUFEDGEINHL, mask); | |
234 | giu_set(GIUREDGEINHL, mask); | |
235 | break; | |
236 | } | |
237 | } | |
364ca8a8 YY |
238 | set_irq_chip_and_handler(GIU_IRQ(pin), |
239 | &giuint_low_irq_chip, | |
240 | handle_edge_irq); | |
e400bae9 YY |
241 | } else { |
242 | giu_clear(GIUINTTYPL, mask); | |
243 | giu_clear(GIUINTHTSELL, mask); | |
364ca8a8 YY |
244 | set_irq_chip_and_handler(GIU_IRQ(pin), |
245 | &giuint_low_irq_chip, | |
246 | handle_level_irq); | |
e400bae9 YY |
247 | } |
248 | giu_write(GIUINTSTATL, mask); | |
249 | } else if (pin < GIUINT_HIGH_MAX) { | |
250 | mask = 1 << (pin - GIUINT_HIGH_OFFSET); | |
251 | if (trigger != IRQ_TRIGGER_LEVEL) { | |
252 | giu_set(GIUINTTYPH, mask); | |
253 | if (signal == IRQ_SIGNAL_HOLD) | |
254 | giu_set(GIUINTHTSELH, mask); | |
255 | else | |
256 | giu_clear(GIUINTHTSELH, mask); | |
44173fb2 | 257 | if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { |
e400bae9 YY |
258 | switch (trigger) { |
259 | case IRQ_TRIGGER_EDGE_FALLING: | |
260 | giu_set(GIUFEDGEINHH, mask); | |
261 | giu_clear(GIUREDGEINHH, mask); | |
262 | break; | |
263 | case IRQ_TRIGGER_EDGE_RISING: | |
264 | giu_clear(GIUFEDGEINHH, mask); | |
265 | giu_set(GIUREDGEINHH, mask); | |
266 | break; | |
267 | default: | |
268 | giu_set(GIUFEDGEINHH, mask); | |
269 | giu_set(GIUREDGEINHH, mask); | |
270 | break; | |
271 | } | |
272 | } | |
364ca8a8 YY |
273 | set_irq_chip_and_handler(GIU_IRQ(pin), |
274 | &giuint_high_irq_chip, | |
275 | handle_edge_irq); | |
e400bae9 YY |
276 | } else { |
277 | giu_clear(GIUINTTYPH, mask); | |
278 | giu_clear(GIUINTHTSELH, mask); | |
364ca8a8 YY |
279 | set_irq_chip_and_handler(GIU_IRQ(pin), |
280 | &giuint_high_irq_chip, | |
281 | handle_level_irq); | |
e400bae9 YY |
282 | } |
283 | giu_write(GIUINTSTATH, mask); | |
284 | } | |
285 | } | |
e400bae9 YY |
286 | EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); |
287 | ||
288 | void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) | |
289 | { | |
290 | uint16_t mask; | |
291 | ||
292 | if (pin < GIUINT_HIGH_OFFSET) { | |
293 | mask = 1 << pin; | |
294 | if (level == IRQ_LEVEL_HIGH) | |
295 | giu_set(GIUINTALSELL, mask); | |
296 | else | |
297 | giu_clear(GIUINTALSELL, mask); | |
298 | giu_write(GIUINTSTATL, mask); | |
299 | } else if (pin < GIUINT_HIGH_MAX) { | |
300 | mask = 1 << (pin - GIUINT_HIGH_OFFSET); | |
301 | if (level == IRQ_LEVEL_HIGH) | |
302 | giu_set(GIUINTALSELH, mask); | |
303 | else | |
304 | giu_clear(GIUINTALSELH, mask); | |
305 | giu_write(GIUINTSTATH, mask); | |
306 | } | |
307 | } | |
e400bae9 YY |
308 | EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); |
309 | ||
310 | gpio_data_t vr41xx_gpio_get_pin(unsigned int pin) | |
311 | { | |
312 | uint16_t reg, mask; | |
313 | ||
314 | if (pin >= giu_nr_pins) | |
315 | return GPIO_DATA_INVAL; | |
316 | ||
317 | if (pin < 16) { | |
318 | reg = giu_read(GIUPIODL); | |
319 | mask = (uint16_t)1 << pin; | |
320 | } else if (pin < 32) { | |
321 | reg = giu_read(GIUPIODH); | |
322 | mask = (uint16_t)1 << (pin - 16); | |
323 | } else if (pin < 48) { | |
324 | reg = giu_read(GIUPODATL); | |
325 | mask = (uint16_t)1 << (pin - 32); | |
326 | } else { | |
327 | reg = giu_read(GIUPODATH); | |
328 | mask = (uint16_t)1 << (pin - 48); | |
329 | } | |
330 | ||
331 | if (reg & mask) | |
332 | return GPIO_DATA_HIGH; | |
333 | ||
334 | return GPIO_DATA_LOW; | |
335 | } | |
e400bae9 YY |
336 | EXPORT_SYMBOL_GPL(vr41xx_gpio_get_pin); |
337 | ||
338 | int vr41xx_gpio_set_pin(unsigned int pin, gpio_data_t data) | |
339 | { | |
340 | uint16_t offset, mask, reg; | |
341 | unsigned long flags; | |
342 | ||
343 | if (pin >= giu_nr_pins) | |
344 | return -EINVAL; | |
345 | ||
346 | if (pin < 16) { | |
347 | offset = GIUPIODL; | |
348 | mask = (uint16_t)1 << pin; | |
349 | } else if (pin < 32) { | |
350 | offset = GIUPIODH; | |
351 | mask = (uint16_t)1 << (pin - 16); | |
352 | } else if (pin < 48) { | |
353 | offset = GIUPODATL; | |
354 | mask = (uint16_t)1 << (pin - 32); | |
355 | } else { | |
356 | offset = GIUPODATH; | |
357 | mask = (uint16_t)1 << (pin - 48); | |
358 | } | |
359 | ||
360 | spin_lock_irqsave(&giu_lock, flags); | |
361 | ||
362 | reg = giu_read(offset); | |
363 | if (data == GPIO_DATA_HIGH) | |
364 | reg |= mask; | |
365 | else | |
366 | reg &= ~mask; | |
367 | giu_write(offset, reg); | |
368 | ||
369 | spin_unlock_irqrestore(&giu_lock, flags); | |
370 | ||
371 | return 0; | |
372 | } | |
e400bae9 YY |
373 | EXPORT_SYMBOL_GPL(vr41xx_gpio_set_pin); |
374 | ||
375 | int vr41xx_gpio_set_direction(unsigned int pin, gpio_direction_t dir) | |
376 | { | |
377 | uint16_t offset, mask, reg; | |
378 | unsigned long flags; | |
379 | ||
380 | if (pin >= giu_nr_pins) | |
381 | return -EINVAL; | |
382 | ||
383 | if (pin < 16) { | |
384 | offset = GIUIOSELL; | |
385 | mask = (uint16_t)1 << pin; | |
386 | } else if (pin < 32) { | |
387 | offset = GIUIOSELH; | |
388 | mask = (uint16_t)1 << (pin - 16); | |
389 | } else { | |
390 | if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { | |
391 | offset = GIUPODATEN; | |
392 | mask = (uint16_t)1 << (pin - 32); | |
393 | } else { | |
394 | switch (pin) { | |
395 | case 48: | |
396 | offset = GIUPODATH; | |
397 | mask = PIOEN0; | |
398 | break; | |
399 | case 49: | |
400 | offset = GIUPODATH; | |
401 | mask = PIOEN1; | |
402 | break; | |
403 | default: | |
404 | return -EINVAL; | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | spin_lock_irqsave(&giu_lock, flags); | |
410 | ||
411 | reg = giu_read(offset); | |
412 | if (dir == GPIO_OUTPUT) | |
413 | reg |= mask; | |
414 | else | |
415 | reg &= ~mask; | |
416 | giu_write(offset, reg); | |
417 | ||
418 | spin_unlock_irqrestore(&giu_lock, flags); | |
419 | ||
420 | return 0; | |
421 | } | |
e400bae9 YY |
422 | EXPORT_SYMBOL_GPL(vr41xx_gpio_set_direction); |
423 | ||
424 | int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) | |
425 | { | |
426 | uint16_t reg, mask; | |
427 | unsigned long flags; | |
428 | ||
429 | if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) | |
430 | return -EPERM; | |
431 | ||
432 | if (pin >= 15) | |
433 | return -EINVAL; | |
434 | ||
435 | mask = (uint16_t)1 << pin; | |
436 | ||
437 | spin_lock_irqsave(&giu_lock, flags); | |
438 | ||
439 | if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { | |
440 | reg = giu_read(GIUTERMUPDN); | |
441 | if (pull == GPIO_PULL_UP) | |
442 | reg |= mask; | |
443 | else | |
444 | reg &= ~mask; | |
445 | giu_write(GIUTERMUPDN, reg); | |
446 | ||
447 | reg = giu_read(GIUUSEUPDN); | |
448 | reg |= mask; | |
449 | giu_write(GIUUSEUPDN, reg); | |
450 | } else { | |
451 | reg = giu_read(GIUUSEUPDN); | |
452 | reg &= ~mask; | |
453 | giu_write(GIUUSEUPDN, reg); | |
454 | } | |
455 | ||
456 | spin_unlock_irqrestore(&giu_lock, flags); | |
457 | ||
458 | return 0; | |
459 | } | |
e400bae9 YY |
460 | EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown); |
461 | ||
462 | static ssize_t gpio_read(struct file *file, char __user *buf, size_t len, | |
463 | loff_t *ppos) | |
464 | { | |
465 | unsigned int pin; | |
466 | char value = '0'; | |
467 | ||
a7113a96 | 468 | pin = iminor(file->f_path.dentry->d_inode); |
e400bae9 YY |
469 | if (pin >= giu_nr_pins) |
470 | return -EBADF; | |
471 | ||
472 | if (vr41xx_gpio_get_pin(pin) == GPIO_DATA_HIGH) | |
473 | value = '1'; | |
474 | ||
475 | if (len <= 0) | |
476 | return -EFAULT; | |
477 | ||
478 | if (put_user(value, buf)) | |
479 | return -EFAULT; | |
480 | ||
481 | return 1; | |
482 | } | |
483 | ||
484 | static ssize_t gpio_write(struct file *file, const char __user *data, | |
485 | size_t len, loff_t *ppos) | |
486 | { | |
487 | unsigned int pin; | |
488 | size_t i; | |
489 | char c; | |
490 | int retval = 0; | |
491 | ||
a7113a96 | 492 | pin = iminor(file->f_path.dentry->d_inode); |
e400bae9 YY |
493 | if (pin >= giu_nr_pins) |
494 | return -EBADF; | |
495 | ||
496 | for (i = 0; i < len; i++) { | |
497 | if (get_user(c, data + i)) | |
498 | return -EFAULT; | |
499 | ||
500 | switch (c) { | |
501 | case '0': | |
502 | retval = vr41xx_gpio_set_pin(pin, GPIO_DATA_LOW); | |
503 | break; | |
504 | case '1': | |
505 | retval = vr41xx_gpio_set_pin(pin, GPIO_DATA_HIGH); | |
506 | break; | |
507 | case 'D': | |
508 | printk(KERN_INFO "GPIO%d: pull down\n", pin); | |
509 | retval = vr41xx_gpio_pullupdown(pin, GPIO_PULL_DOWN); | |
510 | break; | |
511 | case 'd': | |
512 | printk(KERN_INFO "GPIO%d: pull up/down disable\n", pin); | |
513 | retval = vr41xx_gpio_pullupdown(pin, GPIO_PULL_DISABLE); | |
514 | break; | |
515 | case 'I': | |
516 | printk(KERN_INFO "GPIO%d: input\n", pin); | |
517 | retval = vr41xx_gpio_set_direction(pin, GPIO_INPUT); | |
518 | break; | |
519 | case 'O': | |
520 | printk(KERN_INFO "GPIO%d: output\n", pin); | |
521 | retval = vr41xx_gpio_set_direction(pin, GPIO_OUTPUT); | |
522 | break; | |
523 | case 'o': | |
524 | printk(KERN_INFO "GPIO%d: output disable\n", pin); | |
525 | retval = vr41xx_gpio_set_direction(pin, GPIO_OUTPUT_DISABLE); | |
526 | break; | |
527 | case 'P': | |
528 | printk(KERN_INFO "GPIO%d: pull up\n", pin); | |
529 | retval = vr41xx_gpio_pullupdown(pin, GPIO_PULL_UP); | |
530 | break; | |
531 | case 'p': | |
532 | printk(KERN_INFO "GPIO%d: pull up/down disable\n", pin); | |
533 | retval = vr41xx_gpio_pullupdown(pin, GPIO_PULL_DISABLE); | |
534 | break; | |
535 | default: | |
536 | break; | |
537 | } | |
538 | ||
539 | if (retval < 0) | |
540 | break; | |
541 | } | |
542 | ||
543 | return i; | |
544 | } | |
545 | ||
546 | static int gpio_open(struct inode *inode, struct file *file) | |
547 | { | |
548 | unsigned int pin; | |
549 | ||
550 | pin = iminor(inode); | |
551 | if (pin >= giu_nr_pins) | |
552 | return -EBADF; | |
553 | ||
554 | return nonseekable_open(inode, file); | |
555 | } | |
556 | ||
557 | static int gpio_release(struct inode *inode, struct file *file) | |
558 | { | |
559 | unsigned int pin; | |
560 | ||
561 | pin = iminor(inode); | |
562 | if (pin >= giu_nr_pins) | |
563 | return -EBADF; | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
62322d25 | 568 | static const struct file_operations gpio_fops = { |
e400bae9 YY |
569 | .owner = THIS_MODULE, |
570 | .read = gpio_read, | |
571 | .write = gpio_write, | |
572 | .open = gpio_open, | |
573 | .release = gpio_release, | |
574 | }; | |
575 | ||
d39b6cfe | 576 | static int __devinit giu_probe(struct platform_device *dev) |
e400bae9 | 577 | { |
44173fb2 YY |
578 | struct resource *res; |
579 | unsigned int trigger, i, pin; | |
364ca8a8 | 580 | struct irq_chip *chip; |
44173fb2 YY |
581 | int irq, retval; |
582 | ||
583 | switch (dev->id) { | |
584 | case GPIO_50PINS_PULLUPDOWN: | |
585 | giu_flags = GPIO_HAS_PULLUPDOWN_IO; | |
586 | giu_nr_pins = 50; | |
e400bae9 | 587 | break; |
44173fb2 YY |
588 | case GPIO_36PINS: |
589 | giu_nr_pins = 36; | |
e400bae9 | 590 | break; |
44173fb2 YY |
591 | case GPIO_48PINS_EDGE_SELECT: |
592 | giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; | |
593 | giu_nr_pins = 48; | |
e400bae9 YY |
594 | break; |
595 | default: | |
44173fb2 | 596 | printk(KERN_ERR "GIU: unknown ID %d\n", dev->id); |
e400bae9 YY |
597 | return -ENODEV; |
598 | } | |
599 | ||
44173fb2 YY |
600 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); |
601 | if (!res) | |
e400bae9 YY |
602 | return -EBUSY; |
603 | ||
44173fb2 YY |
604 | giu_base = ioremap(res->start, res->end - res->start + 1); |
605 | if (!giu_base) | |
e400bae9 | 606 | return -ENOMEM; |
e400bae9 YY |
607 | |
608 | retval = register_chrdev(major, "GIU", &gpio_fops); | |
609 | if (retval < 0) { | |
44173fb2 YY |
610 | iounmap(giu_base); |
611 | giu_base = NULL; | |
e400bae9 YY |
612 | return retval; |
613 | } | |
614 | ||
615 | if (major == 0) { | |
616 | major = retval; | |
617 | printk(KERN_INFO "GIU: major number %d\n", major); | |
618 | } | |
619 | ||
620 | spin_lock_init(&giu_lock); | |
e400bae9 YY |
621 | |
622 | giu_write(GIUINTENL, 0); | |
623 | giu_write(GIUINTENH, 0); | |
624 | ||
364ca8a8 YY |
625 | trigger = giu_read(GIUINTTYPH) << 16; |
626 | trigger |= giu_read(GIUINTTYPL); | |
e400bae9 | 627 | for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { |
364ca8a8 YY |
628 | pin = GPIO_PIN_OF_IRQ(i); |
629 | if (pin < GIUINT_HIGH_OFFSET) | |
630 | chip = &giuint_low_irq_chip; | |
e400bae9 | 631 | else |
364ca8a8 YY |
632 | chip = &giuint_high_irq_chip; |
633 | ||
634 | if (trigger & (1 << pin)) | |
635 | set_irq_chip_and_handler(i, chip, handle_edge_irq); | |
636 | else | |
637 | set_irq_chip_and_handler(i, chip, handle_level_irq); | |
638 | ||
e400bae9 YY |
639 | } |
640 | ||
44173fb2 YY |
641 | irq = platform_get_irq(dev, 0); |
642 | if (irq < 0 || irq >= NR_IRQS) | |
643 | return -EBUSY; | |
644 | ||
645 | return cascade_irq(irq, giu_get_irq); | |
e400bae9 YY |
646 | } |
647 | ||
d39b6cfe | 648 | static int __devexit giu_remove(struct platform_device *dev) |
e400bae9 | 649 | { |
44173fb2 YY |
650 | if (giu_base) { |
651 | iounmap(giu_base); | |
652 | giu_base = NULL; | |
653 | } | |
e400bae9 YY |
654 | |
655 | return 0; | |
656 | } | |
657 | ||
3ae5eaec | 658 | static struct platform_driver giu_device_driver = { |
e400bae9 | 659 | .probe = giu_probe, |
d39b6cfe | 660 | .remove = __devexit_p(giu_remove), |
3ae5eaec RK |
661 | .driver = { |
662 | .name = "GIU", | |
d39b6cfe | 663 | .owner = THIS_MODULE, |
3ae5eaec | 664 | }, |
e400bae9 YY |
665 | }; |
666 | ||
e701e85b | 667 | static int __init vr41xx_giu_init(void) |
e400bae9 | 668 | { |
44173fb2 | 669 | return platform_driver_register(&giu_device_driver); |
e400bae9 YY |
670 | } |
671 | ||
e701e85b | 672 | static void __exit vr41xx_giu_exit(void) |
e400bae9 | 673 | { |
3ae5eaec | 674 | platform_driver_unregister(&giu_device_driver); |
e400bae9 YY |
675 | } |
676 | ||
677 | module_init(vr41xx_giu_init); | |
678 | module_exit(vr41xx_giu_exit); |