Commit | Line | Data |
---|---|---|
18a1e013 JN |
1 | /* |
2 | * Artec-3 general port I/O device | |
3 | * | |
4 | * Copyright (c) 2007 Axis Communications AB | |
5 | * | |
6 | * Authors: Bjorn Wesen (initial version) | |
7 | * Ola Knutsson (LED handling) | |
8 | * Johan Adolfsson (read/set directions, write, port G, | |
9 | * port to ETRAX FS. | |
10 | * Ricard Wanderlof (PWM for Artpec-3) | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/sched.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/ioport.h> | |
18 | #include <linux/errno.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/fs.h> | |
21 | #include <linux/string.h> | |
22 | #include <linux/poll.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/spinlock.h> | |
0c401df3 | 26 | #include <linux/smp_lock.h> |
18a1e013 JN |
27 | |
28 | #include <asm/etraxgpio.h> | |
29 | #include <hwregs/reg_map.h> | |
30 | #include <hwregs/reg_rdwr.h> | |
31 | #include <hwregs/gio_defs.h> | |
32 | #include <hwregs/intr_vect_defs.h> | |
33 | #include <asm/io.h> | |
34 | #include <asm/system.h> | |
35 | #include <asm/irq.h> | |
556dcee7 | 36 | #include <mach/pinmux.h> |
18a1e013 JN |
37 | |
38 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
39 | #include "../i2c.h" | |
40 | ||
41 | #define VIRT_I2C_ADDR 0x40 | |
42 | #endif | |
43 | ||
44 | /* The following gio ports on ARTPEC-3 is available: | |
45 | * pa 32 bits | |
46 | * pb 32 bits | |
47 | * pc 16 bits | |
48 | * each port has a rw_px_dout, r_px_din and rw_px_oe register. | |
49 | */ | |
50 | ||
51 | #define GPIO_MAJOR 120 /* experimental MAJOR number */ | |
52 | ||
53 | #define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */ | |
54 | ||
55 | #define D(x) | |
56 | ||
57 | #if 0 | |
58 | static int dp_cnt; | |
59 | #define DP(x) \ | |
60 | do { \ | |
61 | dp_cnt++; \ | |
62 | if (dp_cnt % 1000 == 0) \ | |
63 | x; \ | |
64 | } while (0) | |
65 | #else | |
66 | #define DP(x) | |
67 | #endif | |
68 | ||
69 | static char gpio_name[] = "etrax gpio"; | |
70 | ||
71 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
72 | static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, | |
73 | unsigned long arg); | |
74 | #endif | |
90276a1a | 75 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); |
a34d2442 JN |
76 | static ssize_t gpio_write(struct file *file, const char __user *buf, |
77 | size_t count, loff_t *off); | |
18a1e013 JN |
78 | static int gpio_open(struct inode *inode, struct file *filp); |
79 | static int gpio_release(struct inode *inode, struct file *filp); | |
80 | static unsigned int gpio_poll(struct file *filp, | |
a34d2442 | 81 | struct poll_table_struct *wait); |
18a1e013 JN |
82 | |
83 | /* private data per open() of this driver */ | |
84 | ||
85 | struct gpio_private { | |
86 | struct gpio_private *next; | |
87 | /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */ | |
88 | unsigned char clk_mask; | |
89 | unsigned char data_mask; | |
90 | unsigned char write_msb; | |
91 | unsigned char pad1; | |
92 | /* These fields are generic */ | |
93 | unsigned long highalarm, lowalarm; | |
94 | wait_queue_head_t alarm_wq; | |
95 | int minor; | |
96 | }; | |
97 | ||
98 | static void gpio_set_alarm(struct gpio_private *priv); | |
a34d2442 JN |
99 | static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg); |
100 | static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, | |
101 | unsigned long arg); | |
102 | ||
18a1e013 JN |
103 | |
104 | /* linked list of alarms to check for */ | |
105 | ||
106 | static struct gpio_private *alarmlist; | |
107 | ||
108 | static int wanted_interrupts; | |
109 | ||
a34d2442 | 110 | static DEFINE_SPINLOCK(gpio_lock); |
18a1e013 JN |
111 | |
112 | #define NUM_PORTS (GPIO_MINOR_LAST+1) | |
113 | #define GIO_REG_RD_ADDR(reg) \ | |
a34d2442 | 114 | (unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) |
18a1e013 | 115 | #define GIO_REG_WR_ADDR(reg) \ |
a34d2442 JN |
116 | (unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg) |
117 | static unsigned long led_dummy; | |
118 | static unsigned long port_d_dummy; /* Only input on Artpec-3 */ | |
18a1e013 | 119 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO |
a34d2442 | 120 | static unsigned long port_e_dummy; /* Non existent on Artpec-3 */ |
18a1e013 JN |
121 | static unsigned long virtual_dummy; |
122 | static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; | |
123 | static unsigned short cached_virtual_gpio_read; | |
124 | #endif | |
125 | ||
a34d2442 | 126 | static unsigned long *data_out[NUM_PORTS] = { |
18a1e013 JN |
127 | GIO_REG_WR_ADDR(rw_pa_dout), |
128 | GIO_REG_WR_ADDR(rw_pb_dout), | |
129 | &led_dummy, | |
130 | GIO_REG_WR_ADDR(rw_pc_dout), | |
131 | &port_d_dummy, | |
132 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
133 | &port_e_dummy, | |
134 | &virtual_dummy, | |
135 | #endif | |
136 | }; | |
137 | ||
a34d2442 | 138 | static unsigned long *data_in[NUM_PORTS] = { |
18a1e013 JN |
139 | GIO_REG_RD_ADDR(r_pa_din), |
140 | GIO_REG_RD_ADDR(r_pb_din), | |
141 | &led_dummy, | |
142 | GIO_REG_RD_ADDR(r_pc_din), | |
143 | GIO_REG_RD_ADDR(r_pd_din), | |
144 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
145 | &port_e_dummy, | |
146 | &virtual_dummy, | |
147 | #endif | |
148 | }; | |
149 | ||
150 | static unsigned long changeable_dir[NUM_PORTS] = { | |
151 | CONFIG_ETRAX_PA_CHANGEABLE_DIR, | |
152 | CONFIG_ETRAX_PB_CHANGEABLE_DIR, | |
153 | 0, | |
154 | CONFIG_ETRAX_PC_CHANGEABLE_DIR, | |
155 | 0, | |
156 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
157 | 0, | |
158 | CONFIG_ETRAX_PV_CHANGEABLE_DIR, | |
159 | #endif | |
160 | }; | |
161 | ||
162 | static unsigned long changeable_bits[NUM_PORTS] = { | |
163 | CONFIG_ETRAX_PA_CHANGEABLE_BITS, | |
164 | CONFIG_ETRAX_PB_CHANGEABLE_BITS, | |
165 | 0, | |
166 | CONFIG_ETRAX_PC_CHANGEABLE_BITS, | |
167 | 0, | |
168 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
169 | 0, | |
170 | CONFIG_ETRAX_PV_CHANGEABLE_BITS, | |
171 | #endif | |
172 | }; | |
173 | ||
a34d2442 | 174 | static unsigned long *dir_oe[NUM_PORTS] = { |
18a1e013 JN |
175 | GIO_REG_WR_ADDR(rw_pa_oe), |
176 | GIO_REG_WR_ADDR(rw_pb_oe), | |
177 | &led_dummy, | |
178 | GIO_REG_WR_ADDR(rw_pc_oe), | |
179 | &port_d_dummy, | |
180 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
181 | &port_e_dummy, | |
182 | &virtual_rw_pv_oe, | |
183 | #endif | |
184 | }; | |
185 | ||
a34d2442 | 186 | static void gpio_set_alarm(struct gpio_private *priv) |
18a1e013 JN |
187 | { |
188 | int bit; | |
189 | int intr_cfg; | |
190 | int mask; | |
191 | int pins; | |
192 | unsigned long flags; | |
193 | ||
a34d2442 | 194 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 JN |
195 | intr_cfg = REG_RD_INT(gio, regi_gio, rw_intr_cfg); |
196 | pins = REG_RD_INT(gio, regi_gio, rw_intr_pins); | |
197 | mask = REG_RD_INT(gio, regi_gio, rw_intr_mask) & I2C_INTERRUPT_BITS; | |
198 | ||
199 | for (bit = 0; bit < 32; bit++) { | |
200 | int intr = bit % 8; | |
201 | int pin = bit / 8; | |
202 | if (priv->minor < GPIO_MINOR_LEDS) | |
203 | pin += priv->minor * 4; | |
204 | else | |
205 | pin += (priv->minor - 1) * 4; | |
206 | ||
207 | if (priv->highalarm & (1<<bit)) { | |
208 | intr_cfg |= (regk_gio_hi << (intr * 3)); | |
209 | mask |= 1 << intr; | |
210 | wanted_interrupts = mask & 0xff; | |
211 | pins |= pin << (intr * 4); | |
212 | } else if (priv->lowalarm & (1<<bit)) { | |
213 | intr_cfg |= (regk_gio_lo << (intr * 3)); | |
214 | mask |= 1 << intr; | |
215 | wanted_interrupts = mask & 0xff; | |
216 | pins |= pin << (intr * 4); | |
217 | } | |
218 | } | |
219 | ||
220 | REG_WR_INT(gio, regi_gio, rw_intr_cfg, intr_cfg); | |
221 | REG_WR_INT(gio, regi_gio, rw_intr_pins, pins); | |
222 | REG_WR_INT(gio, regi_gio, rw_intr_mask, mask); | |
223 | ||
a34d2442 | 224 | spin_unlock_irqrestore(&gpio_lock, flags); |
18a1e013 JN |
225 | } |
226 | ||
a34d2442 | 227 | static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) |
18a1e013 JN |
228 | { |
229 | unsigned int mask = 0; | |
a34d2442 | 230 | struct gpio_private *priv = file->private_data; |
18a1e013 JN |
231 | unsigned long data; |
232 | unsigned long tmp; | |
233 | ||
234 | if (priv->minor >= GPIO_MINOR_PWM0 && | |
235 | priv->minor <= GPIO_MINOR_LAST_PWM) | |
236 | return 0; | |
237 | ||
238 | poll_wait(file, &priv->alarm_wq, wait); | |
239 | if (priv->minor <= GPIO_MINOR_D) { | |
a34d2442 | 240 | data = readl(data_in[priv->minor]); |
18a1e013 JN |
241 | REG_WR_INT(gio, regi_gio, rw_ack_intr, wanted_interrupts); |
242 | tmp = REG_RD_INT(gio, regi_gio, rw_intr_mask); | |
243 | tmp &= I2C_INTERRUPT_BITS; | |
244 | tmp |= wanted_interrupts; | |
245 | REG_WR_INT(gio, regi_gio, rw_intr_mask, tmp); | |
246 | } else | |
247 | return 0; | |
248 | ||
249 | if ((data & priv->highalarm) || (~data & priv->lowalarm)) | |
250 | mask = POLLIN|POLLRDNORM; | |
251 | ||
252 | DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); | |
253 | return mask; | |
254 | } | |
255 | ||
a34d2442 | 256 | static irqreturn_t gpio_interrupt(int irq, void *dev_id) |
18a1e013 JN |
257 | { |
258 | reg_gio_rw_intr_mask intr_mask; | |
259 | reg_gio_r_masked_intr masked_intr; | |
260 | reg_gio_rw_ack_intr ack_intr; | |
a34d2442 | 261 | unsigned long flags; |
18a1e013 JN |
262 | unsigned long tmp; |
263 | unsigned long tmp2; | |
264 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
265 | unsigned char enable_gpiov_ack = 0; | |
266 | #endif | |
267 | ||
268 | /* Find what PA interrupts are active */ | |
269 | masked_intr = REG_RD(gio, regi_gio, r_masked_intr); | |
270 | tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); | |
271 | ||
272 | /* Find those that we have enabled */ | |
a34d2442 | 273 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 | 274 | tmp &= wanted_interrupts; |
a34d2442 | 275 | spin_unlock_irqrestore(&gpio_lock, flags); |
18a1e013 JN |
276 | |
277 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
278 | /* Something changed on virtual GPIO. Interrupt is acked by | |
279 | * reading the device. | |
280 | */ | |
281 | if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { | |
282 | i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, | |
283 | sizeof(cached_virtual_gpio_read)); | |
284 | enable_gpiov_ack = 1; | |
285 | } | |
286 | #endif | |
287 | ||
288 | /* Ack them */ | |
289 | ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); | |
290 | REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); | |
291 | ||
292 | /* Disable those interrupts.. */ | |
293 | intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); | |
294 | tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); | |
295 | tmp2 &= ~tmp; | |
296 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
297 | /* Do not disable interrupt on virtual GPIO. Changes on virtual | |
298 | * pins are only noticed by an interrupt. | |
299 | */ | |
300 | if (enable_gpiov_ack) | |
301 | tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); | |
302 | #endif | |
303 | intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); | |
304 | REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); | |
305 | ||
306 | return IRQ_RETVAL(tmp); | |
307 | } | |
308 | ||
a34d2442 JN |
309 | static void gpio_write_bit(unsigned long *port, unsigned char data, int bit, |
310 | unsigned char clk_mask, unsigned char data_mask) | |
311 | { | |
312 | unsigned long shadow = readl(port) & ~clk_mask; | |
313 | writel(shadow, port); | |
314 | if (data & 1 << bit) | |
315 | shadow |= data_mask; | |
316 | else | |
317 | shadow &= ~data_mask; | |
318 | writel(shadow, port); | |
319 | /* For FPGA: min 5.0ns (DCC) before CCLK high */ | |
320 | shadow |= clk_mask; | |
321 | writel(shadow, port); | |
322 | } | |
323 | ||
324 | static void gpio_write_byte(struct gpio_private *priv, unsigned long *port, | |
325 | unsigned char data) | |
326 | { | |
327 | int i; | |
328 | ||
329 | if (priv->write_msb) | |
330 | for (i = 7; i >= 0; i--) | |
331 | gpio_write_bit(port, data, i, priv->clk_mask, | |
332 | priv->data_mask); | |
333 | else | |
334 | for (i = 0; i <= 7; i++) | |
335 | gpio_write_bit(port, data, i, priv->clk_mask, | |
336 | priv->data_mask); | |
337 | } | |
338 | ||
18a1e013 | 339 | |
a34d2442 JN |
340 | static ssize_t gpio_write(struct file *file, const char __user *buf, |
341 | size_t count, loff_t *off) | |
18a1e013 | 342 | { |
a34d2442 | 343 | struct gpio_private *priv = file->private_data; |
18a1e013 | 344 | unsigned long flags; |
18a1e013 JN |
345 | ssize_t retval = count; |
346 | /* Only bits 0-7 may be used for write operations but allow all | |
347 | devices except leds... */ | |
348 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
349 | if (priv->minor == GPIO_MINOR_V) | |
350 | return -EFAULT; | |
351 | #endif | |
352 | if (priv->minor == GPIO_MINOR_LEDS) | |
353 | return -EFAULT; | |
354 | ||
355 | if (priv->minor >= GPIO_MINOR_PWM0 && | |
356 | priv->minor <= GPIO_MINOR_LAST_PWM) | |
357 | return -EFAULT; | |
358 | ||
359 | if (!access_ok(VERIFY_READ, buf, count)) | |
360 | return -EFAULT; | |
361 | ||
18a1e013 JN |
362 | /* It must have been configured using the IO_CFG_WRITE_MODE */ |
363 | /* Perhaps a better error code? */ | |
a34d2442 | 364 | if (priv->clk_mask == 0 || priv->data_mask == 0) |
18a1e013 JN |
365 | return -EPERM; |
366 | ||
18a1e013 JN |
367 | D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " |
368 | "msb: %i\n", | |
a34d2442 JN |
369 | count, priv->data_mask, priv->clk_mask, priv->write_msb)); |
370 | ||
371 | spin_lock_irqsave(&gpio_lock, flags); | |
372 | ||
373 | while (count--) | |
374 | gpio_write_byte(priv, data_out[priv->minor], *buf++); | |
375 | ||
376 | spin_unlock_irqrestore(&gpio_lock, flags); | |
18a1e013 JN |
377 | return retval; |
378 | } | |
379 | ||
a34d2442 | 380 | static int gpio_open(struct inode *inode, struct file *filp) |
18a1e013 JN |
381 | { |
382 | struct gpio_private *priv; | |
383 | int p = iminor(inode); | |
384 | ||
385 | if (p > GPIO_MINOR_LAST_PWM || | |
386 | (p > GPIO_MINOR_LAST && p < GPIO_MINOR_PWM0)) | |
387 | return -EINVAL; | |
388 | ||
389 | priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); | |
390 | ||
391 | if (!priv) | |
392 | return -ENOMEM; | |
0c401df3 JC |
393 | |
394 | lock_kernel(); | |
18a1e013 JN |
395 | memset(priv, 0, sizeof(*priv)); |
396 | ||
397 | priv->minor = p; | |
a34d2442 | 398 | filp->private_data = priv; |
18a1e013 JN |
399 | |
400 | /* initialize the io/alarm struct, not for PWM ports though */ | |
401 | if (p <= GPIO_MINOR_LAST) { | |
402 | ||
403 | priv->clk_mask = 0; | |
404 | priv->data_mask = 0; | |
405 | priv->highalarm = 0; | |
406 | priv->lowalarm = 0; | |
407 | ||
408 | init_waitqueue_head(&priv->alarm_wq); | |
409 | ||
410 | /* link it into our alarmlist */ | |
a34d2442 | 411 | spin_lock_irq(&gpio_lock); |
18a1e013 JN |
412 | priv->next = alarmlist; |
413 | alarmlist = priv; | |
a34d2442 | 414 | spin_unlock_irq(&gpio_lock); |
18a1e013 JN |
415 | } |
416 | ||
0c401df3 | 417 | unlock_kernel(); |
18a1e013 JN |
418 | return 0; |
419 | } | |
420 | ||
a34d2442 | 421 | static int gpio_release(struct inode *inode, struct file *filp) |
18a1e013 JN |
422 | { |
423 | struct gpio_private *p; | |
424 | struct gpio_private *todel; | |
425 | /* local copies while updating them: */ | |
426 | unsigned long a_high, a_low; | |
427 | ||
428 | /* prepare to free private structure */ | |
a34d2442 | 429 | todel = filp->private_data; |
18a1e013 JN |
430 | |
431 | /* unlink from alarmlist - only for non-PWM ports though */ | |
432 | if (todel->minor <= GPIO_MINOR_LAST) { | |
a34d2442 | 433 | spin_lock_irq(&gpio_lock); |
18a1e013 JN |
434 | p = alarmlist; |
435 | ||
436 | if (p == todel) | |
437 | alarmlist = todel->next; | |
438 | else { | |
439 | while (p->next != todel) | |
440 | p = p->next; | |
441 | p->next = todel->next; | |
442 | } | |
443 | ||
444 | /* Check if there are still any alarms set */ | |
445 | p = alarmlist; | |
446 | a_high = 0; | |
447 | a_low = 0; | |
448 | while (p) { | |
449 | if (p->minor == GPIO_MINOR_A) { | |
450 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
451 | p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); | |
452 | #endif | |
453 | a_high |= p->highalarm; | |
454 | a_low |= p->lowalarm; | |
455 | } | |
456 | ||
457 | p = p->next; | |
458 | } | |
459 | ||
460 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
461 | /* Variable 'a_low' needs to be set here again | |
462 | * to ensure that interrupt for virtual GPIO is handled. | |
463 | */ | |
464 | a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); | |
465 | #endif | |
466 | ||
a34d2442 | 467 | spin_unlock_irq(&gpio_lock); |
18a1e013 JN |
468 | } |
469 | kfree(todel); | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | /* Main device API. ioctl's to read/set/clear bits, as well as to | |
475 | * set alarms to wait for using a subsequent select(). | |
476 | */ | |
477 | ||
478 | inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) | |
479 | { | |
480 | /* Set direction 0=unchanged 1=input, | |
481 | * return mask with 1=input | |
482 | */ | |
483 | unsigned long flags; | |
484 | unsigned long dir_shadow; | |
485 | ||
a34d2442 JN |
486 | spin_lock_irqsave(&gpio_lock, flags); |
487 | ||
488 | dir_shadow = readl(dir_oe[priv->minor]) & | |
489 | ~(arg & changeable_dir[priv->minor]); | |
490 | writel(dir_shadow, dir_oe[priv->minor]); | |
491 | ||
492 | spin_unlock_irqrestore(&gpio_lock, flags); | |
18a1e013 JN |
493 | |
494 | if (priv->minor == GPIO_MINOR_C) | |
495 | dir_shadow ^= 0xFFFF; /* Only 16 bits */ | |
496 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
497 | else if (priv->minor == GPIO_MINOR_V) | |
498 | dir_shadow ^= 0xFFFF; /* Only 16 bits */ | |
499 | #endif | |
500 | else | |
501 | dir_shadow ^= 0xFFFFFFFF; /* PA, PB and PD 32 bits */ | |
502 | ||
503 | return dir_shadow; | |
504 | ||
505 | } /* setget_input */ | |
506 | ||
a34d2442 JN |
507 | static inline unsigned long setget_output(struct gpio_private *priv, |
508 | unsigned long arg) | |
18a1e013 JN |
509 | { |
510 | unsigned long flags; | |
511 | unsigned long dir_shadow; | |
512 | ||
a34d2442 | 513 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 | 514 | |
a34d2442 JN |
515 | dir_shadow = readl(dir_oe[priv->minor]) | |
516 | (arg & changeable_dir[priv->minor]); | |
517 | writel(dir_shadow, dir_oe[priv->minor]); | |
18a1e013 | 518 | |
a34d2442 JN |
519 | spin_unlock_irqrestore(&gpio_lock, flags); |
520 | return dir_shadow; | |
521 | } /* setget_output */ | |
18a1e013 | 522 | |
90276a1a | 523 | static long gpio_ioctl_unlocked(struct file *file, |
a34d2442 | 524 | unsigned int cmd, unsigned long arg) |
18a1e013 JN |
525 | { |
526 | unsigned long flags; | |
527 | unsigned long val; | |
528 | unsigned long shadow; | |
a34d2442 | 529 | struct gpio_private *priv = file->private_data; |
18a1e013 JN |
530 | |
531 | if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) | |
a34d2442 | 532 | return -ENOTTY; |
18a1e013 JN |
533 | |
534 | /* Check for special ioctl handlers first */ | |
535 | ||
536 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
537 | if (priv->minor == GPIO_MINOR_V) | |
538 | return virtual_gpio_ioctl(file, cmd, arg); | |
539 | #endif | |
540 | ||
541 | if (priv->minor == GPIO_MINOR_LEDS) | |
542 | return gpio_leds_ioctl(cmd, arg); | |
543 | ||
544 | if (priv->minor >= GPIO_MINOR_PWM0 && | |
545 | priv->minor <= GPIO_MINOR_LAST_PWM) | |
546 | return gpio_pwm_ioctl(priv, cmd, arg); | |
547 | ||
548 | switch (_IOC_NR(cmd)) { | |
549 | case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ | |
550 | /* Read the port. */ | |
a34d2442 | 551 | return readl(data_in[priv->minor]); |
18a1e013 | 552 | case IO_SETBITS: |
a34d2442 | 553 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 | 554 | /* Set changeable bits with a 1 in arg. */ |
a34d2442 JN |
555 | shadow = readl(data_out[priv->minor]) | |
556 | (arg & changeable_bits[priv->minor]); | |
557 | writel(shadow, data_out[priv->minor]); | |
558 | spin_unlock_irqrestore(&gpio_lock, flags); | |
18a1e013 JN |
559 | break; |
560 | case IO_CLRBITS: | |
a34d2442 | 561 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 | 562 | /* Clear changeable bits with a 1 in arg. */ |
a34d2442 JN |
563 | shadow = readl(data_out[priv->minor]) & |
564 | ~(arg & changeable_bits[priv->minor]); | |
565 | writel(shadow, data_out[priv->minor]); | |
566 | spin_unlock_irqrestore(&gpio_lock, flags); | |
18a1e013 JN |
567 | break; |
568 | case IO_HIGHALARM: | |
569 | /* Set alarm when bits with 1 in arg go high. */ | |
570 | priv->highalarm |= arg; | |
571 | gpio_set_alarm(priv); | |
572 | break; | |
573 | case IO_LOWALARM: | |
574 | /* Set alarm when bits with 1 in arg go low. */ | |
575 | priv->lowalarm |= arg; | |
576 | gpio_set_alarm(priv); | |
577 | break; | |
578 | case IO_CLRALARM: | |
579 | /* Clear alarm for bits with 1 in arg. */ | |
580 | priv->highalarm &= ~arg; | |
581 | priv->lowalarm &= ~arg; | |
582 | gpio_set_alarm(priv); | |
583 | break; | |
584 | case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ | |
585 | /* Read direction 0=input 1=output */ | |
a34d2442 JN |
586 | return readl(dir_oe[priv->minor]); |
587 | ||
18a1e013 JN |
588 | case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ |
589 | /* Set direction 0=unchanged 1=input, | |
590 | * return mask with 1=input | |
591 | */ | |
592 | return setget_input(priv, arg); | |
a34d2442 | 593 | |
18a1e013 JN |
594 | case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ |
595 | /* Set direction 0=unchanged 1=output, | |
596 | * return mask with 1=output | |
597 | */ | |
598 | return setget_output(priv, arg); | |
599 | ||
600 | case IO_CFG_WRITE_MODE: | |
601 | { | |
a34d2442 JN |
602 | int res = -EPERM; |
603 | unsigned long dir_shadow, clk_mask, data_mask, write_msb; | |
604 | ||
605 | clk_mask = arg & 0xFF; | |
606 | data_mask = (arg >> 8) & 0xFF; | |
607 | write_msb = (arg >> 16) & 0x01; | |
18a1e013 | 608 | |
18a1e013 JN |
609 | /* Check if we're allowed to change the bits and |
610 | * the direction is correct | |
611 | */ | |
a34d2442 JN |
612 | spin_lock_irqsave(&gpio_lock, flags); |
613 | dir_shadow = readl(dir_oe[priv->minor]); | |
614 | if ((clk_mask & changeable_bits[priv->minor]) && | |
615 | (data_mask & changeable_bits[priv->minor]) && | |
616 | (clk_mask & dir_shadow) && | |
617 | (data_mask & dir_shadow)) { | |
618 | priv->clk_mask = clk_mask; | |
619 | priv->data_mask = data_mask; | |
620 | priv->write_msb = write_msb; | |
621 | res = 0; | |
18a1e013 | 622 | } |
a34d2442 JN |
623 | spin_unlock_irqrestore(&gpio_lock, flags); |
624 | ||
625 | return res; | |
18a1e013 JN |
626 | } |
627 | case IO_READ_INBITS: | |
628 | /* *arg is result of reading the input pins */ | |
a34d2442 JN |
629 | val = readl(data_in[priv->minor]); |
630 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
18a1e013 JN |
631 | return -EFAULT; |
632 | return 0; | |
18a1e013 JN |
633 | case IO_READ_OUTBITS: |
634 | /* *arg is result of reading the output shadow */ | |
635 | val = *data_out[priv->minor]; | |
a34d2442 | 636 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
18a1e013 JN |
637 | return -EFAULT; |
638 | break; | |
639 | case IO_SETGET_INPUT: | |
640 | /* bits set in *arg is set to input, | |
641 | * *arg updated with current input pins. | |
642 | */ | |
a34d2442 | 643 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) |
18a1e013 JN |
644 | return -EFAULT; |
645 | val = setget_input(priv, val); | |
a34d2442 | 646 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
18a1e013 JN |
647 | return -EFAULT; |
648 | break; | |
649 | case IO_SETGET_OUTPUT: | |
650 | /* bits set in *arg is set to output, | |
651 | * *arg updated with current output pins. | |
652 | */ | |
a34d2442 | 653 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) |
18a1e013 JN |
654 | return -EFAULT; |
655 | val = setget_output(priv, val); | |
a34d2442 | 656 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
18a1e013 JN |
657 | return -EFAULT; |
658 | break; | |
659 | default: | |
660 | return -EINVAL; | |
661 | } /* switch */ | |
662 | ||
663 | return 0; | |
664 | } | |
665 | ||
90276a1a JN |
666 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
667 | { | |
668 | long ret; | |
669 | ||
670 | lock_kernel(); | |
671 | ret = gpio_ioctl_unlocked(file, cmd, arg); | |
672 | unlock_kernel(); | |
673 | ||
674 | return ret; | |
675 | } | |
676 | ||
18a1e013 | 677 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO |
a34d2442 JN |
678 | static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, |
679 | unsigned long arg) | |
18a1e013 JN |
680 | { |
681 | unsigned long flags; | |
682 | unsigned short val; | |
683 | unsigned short shadow; | |
a34d2442 | 684 | struct gpio_private *priv = file->private_data; |
18a1e013 JN |
685 | |
686 | switch (_IOC_NR(cmd)) { | |
687 | case IO_SETBITS: | |
a34d2442 | 688 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 JN |
689 | /* Set changeable bits with a 1 in arg. */ |
690 | i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); | |
a34d2442 JN |
691 | shadow |= ~readl(dir_oe[priv->minor]) | |
692 | (arg & changeable_bits[priv->minor]); | |
18a1e013 | 693 | i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); |
999fb23e | 694 | spin_unlock_irqrestore(&gpio_lock, flags); |
18a1e013 JN |
695 | break; |
696 | case IO_CLRBITS: | |
a34d2442 | 697 | spin_lock_irqsave(&gpio_lock, flags); |
18a1e013 JN |
698 | /* Clear changeable bits with a 1 in arg. */ |
699 | i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); | |
a34d2442 JN |
700 | shadow |= ~readl(dir_oe[priv->minor]) & |
701 | ~(arg & changeable_bits[priv->minor]); | |
18a1e013 | 702 | i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); |
999fb23e | 703 | spin_unlock_irqrestore(&gpio_lock, flags); |
18a1e013 JN |
704 | break; |
705 | case IO_HIGHALARM: | |
706 | /* Set alarm when bits with 1 in arg go high. */ | |
707 | priv->highalarm |= arg; | |
708 | break; | |
709 | case IO_LOWALARM: | |
710 | /* Set alarm when bits with 1 in arg go low. */ | |
711 | priv->lowalarm |= arg; | |
712 | break; | |
713 | case IO_CLRALARM: | |
714 | /* Clear alarm for bits with 1 in arg. */ | |
715 | priv->highalarm &= ~arg; | |
716 | priv->lowalarm &= ~arg; | |
717 | break; | |
718 | case IO_CFG_WRITE_MODE: | |
719 | { | |
720 | unsigned long dir_shadow; | |
a34d2442 | 721 | dir_shadow = readl(dir_oe[priv->minor]); |
18a1e013 JN |
722 | |
723 | priv->clk_mask = arg & 0xFF; | |
724 | priv->data_mask = (arg >> 8) & 0xFF; | |
725 | priv->write_msb = (arg >> 16) & 0x01; | |
726 | /* Check if we're allowed to change the bits and | |
727 | * the direction is correct | |
728 | */ | |
729 | if (!((priv->clk_mask & changeable_bits[priv->minor]) && | |
730 | (priv->data_mask & changeable_bits[priv->minor]) && | |
731 | (priv->clk_mask & dir_shadow) && | |
732 | (priv->data_mask & dir_shadow))) { | |
733 | priv->clk_mask = 0; | |
734 | priv->data_mask = 0; | |
735 | return -EPERM; | |
736 | } | |
737 | break; | |
738 | } | |
739 | case IO_READ_INBITS: | |
740 | /* *arg is result of reading the input pins */ | |
a34d2442 JN |
741 | val = cached_virtual_gpio_read & ~readl(dir_oe[priv->minor]); |
742 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
18a1e013 JN |
743 | return -EFAULT; |
744 | return 0; | |
a34d2442 | 745 | |
18a1e013 JN |
746 | case IO_READ_OUTBITS: |
747 | /* *arg is result of reading the output shadow */ | |
748 | i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); | |
a34d2442 JN |
749 | val &= readl(dir_oe[priv->minor]); |
750 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
18a1e013 JN |
751 | return -EFAULT; |
752 | break; | |
753 | case IO_SETGET_INPUT: | |
754 | { | |
755 | /* bits set in *arg is set to input, | |
756 | * *arg updated with current input pins. | |
757 | */ | |
a34d2442 JN |
758 | unsigned short input_mask = ~readl(dir_oe[priv->minor]); |
759 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) | |
18a1e013 JN |
760 | return -EFAULT; |
761 | val = setget_input(priv, val); | |
a34d2442 | 762 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
18a1e013 JN |
763 | return -EFAULT; |
764 | if ((input_mask & val) != input_mask) { | |
765 | /* Input pins changed. All ports desired as input | |
766 | * should be set to logic 1. | |
767 | */ | |
768 | unsigned short change = input_mask ^ val; | |
769 | i2c_read(VIRT_I2C_ADDR, (void *)&shadow, | |
770 | sizeof(shadow)); | |
771 | shadow &= ~change; | |
772 | shadow |= val; | |
773 | i2c_write(VIRT_I2C_ADDR, (void *)&shadow, | |
774 | sizeof(shadow)); | |
775 | } | |
776 | break; | |
777 | } | |
778 | case IO_SETGET_OUTPUT: | |
779 | /* bits set in *arg is set to output, | |
780 | * *arg updated with current output pins. | |
781 | */ | |
a34d2442 | 782 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) |
18a1e013 JN |
783 | return -EFAULT; |
784 | val = setget_output(priv, val); | |
a34d2442 | 785 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
18a1e013 JN |
786 | return -EFAULT; |
787 | break; | |
788 | default: | |
789 | return -EINVAL; | |
790 | } /* switch */ | |
791 | return 0; | |
792 | } | |
793 | #endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ | |
794 | ||
a34d2442 | 795 | static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) |
18a1e013 JN |
796 | { |
797 | unsigned char green; | |
798 | unsigned char red; | |
799 | ||
800 | switch (_IOC_NR(cmd)) { | |
801 | case IO_LEDACTIVE_SET: | |
802 | green = ((unsigned char) arg) & 1; | |
803 | red = (((unsigned char) arg) >> 1) & 1; | |
cacc0cc8 JN |
804 | CRIS_LED_ACTIVE_SET_G(green); |
805 | CRIS_LED_ACTIVE_SET_R(red); | |
18a1e013 JN |
806 | break; |
807 | ||
808 | default: | |
809 | return -EINVAL; | |
810 | } /* switch */ | |
811 | ||
812 | return 0; | |
813 | } | |
814 | ||
815 | static int gpio_pwm_set_mode(unsigned long arg, int pwm_port) | |
816 | { | |
817 | int pinmux_pwm = pinmux_pwm0 + pwm_port; | |
818 | int mode; | |
819 | reg_gio_rw_pwm0_ctrl rw_pwm_ctrl = { | |
820 | .ccd_val = 0, | |
821 | .ccd_override = regk_gio_no, | |
822 | .mode = regk_gio_no | |
823 | }; | |
824 | int allocstatus; | |
825 | ||
826 | if (get_user(mode, &((struct io_pwm_set_mode *) arg)->mode)) | |
827 | return -EFAULT; | |
828 | rw_pwm_ctrl.mode = mode; | |
829 | if (mode != PWM_OFF) | |
830 | allocstatus = crisv32_pinmux_alloc_fixed(pinmux_pwm); | |
831 | else | |
832 | allocstatus = crisv32_pinmux_dealloc_fixed(pinmux_pwm); | |
833 | if (allocstatus) | |
834 | return allocstatus; | |
835 | REG_WRITE(reg_gio_rw_pwm0_ctrl, REG_ADDR(gio, regi_gio, rw_pwm0_ctrl) + | |
836 | 12 * pwm_port, rw_pwm_ctrl); | |
837 | return 0; | |
838 | } | |
839 | ||
840 | static int gpio_pwm_set_period(unsigned long arg, int pwm_port) | |
841 | { | |
842 | struct io_pwm_set_period periods; | |
843 | reg_gio_rw_pwm0_var rw_pwm_widths; | |
844 | ||
a34d2442 | 845 | if (copy_from_user(&periods, (void __user *)arg, sizeof(periods))) |
18a1e013 JN |
846 | return -EFAULT; |
847 | if (periods.lo > 8191 || periods.hi > 8191) | |
848 | return -EINVAL; | |
849 | rw_pwm_widths.lo = periods.lo; | |
850 | rw_pwm_widths.hi = periods.hi; | |
851 | REG_WRITE(reg_gio_rw_pwm0_var, REG_ADDR(gio, regi_gio, rw_pwm0_var) + | |
852 | 12 * pwm_port, rw_pwm_widths); | |
853 | return 0; | |
854 | } | |
855 | ||
856 | static int gpio_pwm_set_duty(unsigned long arg, int pwm_port) | |
857 | { | |
858 | unsigned int duty; | |
859 | reg_gio_rw_pwm0_data rw_pwm_duty; | |
860 | ||
861 | if (get_user(duty, &((struct io_pwm_set_duty *) arg)->duty)) | |
862 | return -EFAULT; | |
863 | if (duty > 255) | |
864 | return -EINVAL; | |
865 | rw_pwm_duty.data = duty; | |
866 | REG_WRITE(reg_gio_rw_pwm0_data, REG_ADDR(gio, regi_gio, rw_pwm0_data) + | |
867 | 12 * pwm_port, rw_pwm_duty); | |
868 | return 0; | |
869 | } | |
870 | ||
a34d2442 JN |
871 | static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, |
872 | unsigned long arg) | |
18a1e013 JN |
873 | { |
874 | int pwm_port = priv->minor - GPIO_MINOR_PWM0; | |
875 | ||
876 | switch (_IOC_NR(cmd)) { | |
877 | case IO_PWM_SET_MODE: | |
878 | return gpio_pwm_set_mode(arg, pwm_port); | |
879 | case IO_PWM_SET_PERIOD: | |
880 | return gpio_pwm_set_period(arg, pwm_port); | |
881 | case IO_PWM_SET_DUTY: | |
882 | return gpio_pwm_set_duty(arg, pwm_port); | |
883 | default: | |
884 | return -EINVAL; | |
885 | } | |
886 | return 0; | |
887 | } | |
888 | ||
a34d2442 | 889 | static const struct file_operations gpio_fops = { |
90276a1a JN |
890 | .owner = THIS_MODULE, |
891 | .poll = gpio_poll, | |
892 | .unlocked_ioctl = gpio_ioctl, | |
893 | .write = gpio_write, | |
894 | .open = gpio_open, | |
895 | .release = gpio_release, | |
6038f373 | 896 | .llseek = noop_llseek, |
18a1e013 JN |
897 | }; |
898 | ||
899 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
a34d2442 | 900 | static void __init virtual_gpio_init(void) |
18a1e013 JN |
901 | { |
902 | reg_gio_rw_intr_cfg intr_cfg; | |
903 | reg_gio_rw_intr_mask intr_mask; | |
904 | unsigned short shadow; | |
905 | ||
906 | shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ | |
907 | shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; | |
908 | i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); | |
909 | ||
910 | /* Set interrupt mask and on what state the interrupt shall trigger. | |
911 | * For virtual gpio the interrupt shall trigger on logic '0'. | |
912 | */ | |
913 | intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); | |
914 | intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); | |
915 | ||
916 | switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { | |
917 | case 0: | |
918 | intr_cfg.pa0 = regk_gio_lo; | |
919 | intr_mask.pa0 = regk_gio_yes; | |
920 | break; | |
921 | case 1: | |
922 | intr_cfg.pa1 = regk_gio_lo; | |
923 | intr_mask.pa1 = regk_gio_yes; | |
924 | break; | |
925 | case 2: | |
926 | intr_cfg.pa2 = regk_gio_lo; | |
927 | intr_mask.pa2 = regk_gio_yes; | |
928 | break; | |
929 | case 3: | |
930 | intr_cfg.pa3 = regk_gio_lo; | |
931 | intr_mask.pa3 = regk_gio_yes; | |
932 | break; | |
933 | case 4: | |
934 | intr_cfg.pa4 = regk_gio_lo; | |
935 | intr_mask.pa4 = regk_gio_yes; | |
936 | break; | |
937 | case 5: | |
938 | intr_cfg.pa5 = regk_gio_lo; | |
939 | intr_mask.pa5 = regk_gio_yes; | |
940 | break; | |
941 | case 6: | |
942 | intr_cfg.pa6 = regk_gio_lo; | |
943 | intr_mask.pa6 = regk_gio_yes; | |
944 | break; | |
945 | case 7: | |
946 | intr_cfg.pa7 = regk_gio_lo; | |
947 | intr_mask.pa7 = regk_gio_yes; | |
948 | break; | |
949 | } | |
950 | ||
951 | REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); | |
952 | REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); | |
953 | } | |
954 | #endif | |
955 | ||
956 | /* main driver initialization routine, called from mem.c */ | |
957 | ||
a34d2442 | 958 | static int __init gpio_init(void) |
18a1e013 JN |
959 | { |
960 | int res; | |
961 | ||
a34d2442 JN |
962 | printk(KERN_INFO "ETRAX FS GPIO driver v2.7, (c) 2003-2008 " |
963 | "Axis Communications AB\n"); | |
964 | ||
18a1e013 JN |
965 | /* do the formalities */ |
966 | ||
967 | res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); | |
968 | if (res < 0) { | |
969 | printk(KERN_ERR "gpio: couldn't get a major number.\n"); | |
970 | return res; | |
971 | } | |
972 | ||
973 | /* Clear all leds */ | |
cacc0cc8 JN |
974 | CRIS_LED_NETWORK_GRP0_SET(0); |
975 | CRIS_LED_NETWORK_GRP1_SET(0); | |
976 | CRIS_LED_ACTIVE_SET(0); | |
977 | CRIS_LED_DISK_READ(0); | |
978 | CRIS_LED_DISK_WRITE(0); | |
18a1e013 | 979 | |
a34d2442 JN |
980 | int res2 = request_irq(GIO_INTR_VECT, gpio_interrupt, |
981 | IRQF_SHARED | IRQF_DISABLED, "gpio", &alarmlist); | |
982 | if (res2) { | |
18a1e013 | 983 | printk(KERN_ERR "err: irq for gpio\n"); |
a34d2442 JN |
984 | return res2; |
985 | } | |
18a1e013 JN |
986 | |
987 | /* No IRQs by default. */ | |
988 | REG_WR_INT(gio, regi_gio, rw_intr_pins, 0); | |
989 | ||
990 | #ifdef CONFIG_ETRAX_VIRTUAL_GPIO | |
991 | virtual_gpio_init(); | |
992 | #endif | |
993 | ||
994 | return res; | |
995 | } | |
996 | ||
997 | /* this makes sure that gpio_init is called during kernel boot */ | |
998 | ||
999 | module_init(gpio_init); |