]>
Commit | Line | Data |
---|---|---|
45a4127c | 1 | /* |
1da177e4 LT |
2 | * Etrax general port I/O device |
3 | * | |
45a4127c | 4 | * Copyright (c) 1999-2007 Axis Communications AB |
1da177e4 LT |
5 | * |
6 | * Authors: Bjorn Wesen (initial version) | |
7 | * Ola Knutsson (LED handling) | |
8 | * Johan Adolfsson (read/set directions, write, port G) | |
1da177e4 LT |
9 | */ |
10 | ||
1da177e4 LT |
11 | |
12 | #include <linux/module.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/ioport.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/fs.h> | |
0c401df3 | 19 | #include <linux/smp_lock.h> |
1da177e4 LT |
20 | #include <linux/string.h> |
21 | #include <linux/poll.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/interrupt.h> | |
24 | ||
25 | #include <asm/etraxgpio.h> | |
556dcee7 | 26 | #include <arch/svinto.h> |
1da177e4 LT |
27 | #include <asm/io.h> |
28 | #include <asm/system.h> | |
29 | #include <asm/irq.h> | |
556dcee7 | 30 | #include <arch/io_interface_mux.h> |
1da177e4 LT |
31 | |
32 | #define GPIO_MAJOR 120 /* experimental MAJOR number */ | |
33 | ||
34 | #define D(x) | |
35 | ||
36 | #if 0 | |
37 | static int dp_cnt; | |
38 | #define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) | |
39 | #else | |
40 | #define DP(x) | |
41 | #endif | |
45a4127c | 42 | |
1da177e4 LT |
43 | static char gpio_name[] = "etrax gpio"; |
44 | ||
45 | #if 0 | |
46 | static wait_queue_head_t *gpio_wq; | |
47 | #endif | |
48 | ||
90276a1a | 49 | static int gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); |
ad433f23 JN |
50 | static ssize_t gpio_write(struct file *file, const char __user *buf, |
51 | size_t count, loff_t *off); | |
1da177e4 LT |
52 | static int gpio_open(struct inode *inode, struct file *filp); |
53 | static int gpio_release(struct inode *inode, struct file *filp); | |
54 | static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); | |
55 | ||
56 | /* private data per open() of this driver */ | |
57 | ||
58 | struct gpio_private { | |
59 | struct gpio_private *next; | |
60 | /* These fields are for PA and PB only */ | |
61 | volatile unsigned char *port, *shadow; | |
62 | volatile unsigned char *dir, *dir_shadow; | |
63 | unsigned char changeable_dir; | |
64 | unsigned char changeable_bits; | |
65 | unsigned char clk_mask; | |
66 | unsigned char data_mask; | |
67 | unsigned char write_msb; | |
68 | unsigned char pad1, pad2, pad3; | |
69 | /* These fields are generic */ | |
70 | unsigned long highalarm, lowalarm; | |
71 | wait_queue_head_t alarm_wq; | |
72 | int minor; | |
73 | }; | |
74 | ||
75 | /* linked list of alarms to check for */ | |
76 | ||
ad433f23 | 77 | static struct gpio_private *alarmlist; |
1da177e4 | 78 | |
ad433f23 JN |
79 | static int gpio_some_alarms; /* Set if someone uses alarm */ |
80 | static unsigned long gpio_pa_irq_enabled_mask; | |
1da177e4 | 81 | |
7e920426 MS |
82 | static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ |
83 | ||
1da177e4 LT |
84 | /* Port A and B use 8 bit access, but Port G is 32 bit */ |
85 | #define NUM_PORTS (GPIO_MINOR_B+1) | |
86 | ||
45a4127c JN |
87 | static volatile unsigned char *ports[NUM_PORTS] = { |
88 | R_PORT_PA_DATA, | |
1da177e4 LT |
89 | R_PORT_PB_DATA, |
90 | }; | |
91 | static volatile unsigned char *shads[NUM_PORTS] = { | |
45a4127c | 92 | &port_pa_data_shadow, |
1da177e4 LT |
93 | &port_pb_data_shadow |
94 | }; | |
95 | ||
96 | /* What direction bits that are user changeable 1=changeable*/ | |
97 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR | |
98 | #define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00 | |
99 | #endif | |
100 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR | |
101 | #define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00 | |
102 | #endif | |
103 | ||
104 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS | |
105 | #define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF | |
106 | #endif | |
107 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS | |
108 | #define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF | |
109 | #endif | |
110 | ||
111 | ||
45a4127c | 112 | static unsigned char changeable_dir[NUM_PORTS] = { |
1da177e4 | 113 | CONFIG_ETRAX_PA_CHANGEABLE_DIR, |
45a4127c | 114 | CONFIG_ETRAX_PB_CHANGEABLE_DIR |
1da177e4 | 115 | }; |
45a4127c | 116 | static unsigned char changeable_bits[NUM_PORTS] = { |
1da177e4 | 117 | CONFIG_ETRAX_PA_CHANGEABLE_BITS, |
45a4127c | 118 | CONFIG_ETRAX_PB_CHANGEABLE_BITS |
1da177e4 LT |
119 | }; |
120 | ||
45a4127c JN |
121 | static volatile unsigned char *dir[NUM_PORTS] = { |
122 | R_PORT_PA_DIR, | |
123 | R_PORT_PB_DIR | |
1da177e4 LT |
124 | }; |
125 | ||
126 | static volatile unsigned char *dir_shadow[NUM_PORTS] = { | |
45a4127c JN |
127 | &port_pa_dir_shadow, |
128 | &port_pb_dir_shadow | |
1da177e4 LT |
129 | }; |
130 | ||
7e920426 MS |
131 | /* All bits in port g that can change dir. */ |
132 | static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; | |
133 | ||
45a4127c | 134 | /* Port G is 32 bit, handle it special, some bits are both inputs |
1da177e4 LT |
135 | and outputs at the same time, only some of the bits can change direction |
136 | and some of them in groups of 8 bit. */ | |
137 | static unsigned long changeable_dir_g; | |
138 | static unsigned long dir_g_in_bits; | |
139 | static unsigned long dir_g_out_bits; | |
140 | static unsigned long dir_g_shadow; /* 1=output */ | |
141 | ||
142 | #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) | |
143 | ||
144 | ||
45a4127c | 145 | static unsigned int gpio_poll(struct file *file, poll_table *wait) |
1da177e4 LT |
146 | { |
147 | unsigned int mask = 0; | |
ad433f23 | 148 | struct gpio_private *priv = file->private_data; |
1da177e4 | 149 | unsigned long data; |
45a4127c JN |
150 | unsigned long flags; |
151 | ||
152 | spin_lock_irqsave(&gpio_lock, flags); | |
153 | ||
1da177e4 LT |
154 | poll_wait(file, &priv->alarm_wq, wait); |
155 | if (priv->minor == GPIO_MINOR_A) { | |
1da177e4 LT |
156 | unsigned long tmp; |
157 | data = *R_PORT_PA_DATA; | |
158 | /* PA has support for high level interrupt - | |
159 | * lets activate for those low and with highalarm set | |
160 | */ | |
161 | tmp = ~data & priv->highalarm & 0xFF; | |
162 | tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); | |
45a4127c | 163 | |
1da177e4 LT |
164 | gpio_pa_irq_enabled_mask |= tmp; |
165 | *R_IRQ_MASK1_SET = tmp; | |
1da177e4 LT |
166 | } else if (priv->minor == GPIO_MINOR_B) |
167 | data = *R_PORT_PB_DATA; | |
168 | else if (priv->minor == GPIO_MINOR_G) | |
169 | data = *R_PORT_G_DATA; | |
5c6ff79d | 170 | else { |
45a4127c JN |
171 | mask = 0; |
172 | goto out; | |
5c6ff79d | 173 | } |
45a4127c | 174 | |
1da177e4 LT |
175 | if ((data & priv->highalarm) || |
176 | (~data & priv->lowalarm)) { | |
177 | mask = POLLIN|POLLRDNORM; | |
178 | } | |
7e920426 | 179 | |
45a4127c JN |
180 | out: |
181 | spin_unlock_irqrestore(&gpio_lock, flags); | |
1da177e4 | 182 | DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); |
7e920426 | 183 | |
1da177e4 LT |
184 | return mask; |
185 | } | |
186 | ||
187 | int etrax_gpio_wake_up_check(void) | |
188 | { | |
45a4127c | 189 | struct gpio_private *priv; |
1da177e4 LT |
190 | unsigned long data = 0; |
191 | int ret = 0; | |
45a4127c JN |
192 | unsigned long flags; |
193 | ||
194 | spin_lock_irqsave(&gpio_lock, flags); | |
195 | priv = alarmlist; | |
1da177e4 | 196 | while (priv) { |
45a4127c | 197 | if (USE_PORTS(priv)) |
1da177e4 | 198 | data = *priv->port; |
45a4127c | 199 | else if (priv->minor == GPIO_MINOR_G) |
1da177e4 | 200 | data = *R_PORT_G_DATA; |
45a4127c | 201 | |
1da177e4 LT |
202 | if ((data & priv->highalarm) || |
203 | (~data & priv->lowalarm)) { | |
204 | DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); | |
205 | wake_up_interruptible(&priv->alarm_wq); | |
206 | ret = 1; | |
207 | } | |
208 | priv = priv->next; | |
209 | } | |
45a4127c | 210 | spin_unlock_irqrestore(&gpio_lock, flags); |
1da177e4 LT |
211 | return ret; |
212 | } | |
213 | ||
214 | static irqreturn_t | |
45a4127c | 215 | gpio_poll_timer_interrupt(int irq, void *dev_id) |
1da177e4 LT |
216 | { |
217 | if (gpio_some_alarms) { | |
218 | etrax_gpio_wake_up_check(); | |
219 | return IRQ_HANDLED; | |
220 | } | |
221 | return IRQ_NONE; | |
222 | } | |
223 | ||
224 | static irqreturn_t | |
ad433f23 | 225 | gpio_interrupt(int irq, void *dev_id) |
1da177e4 LT |
226 | { |
227 | unsigned long tmp; | |
45a4127c JN |
228 | unsigned long flags; |
229 | ||
230 | spin_lock_irqsave(&gpio_lock, flags); | |
231 | ||
1da177e4 LT |
232 | /* Find what PA interrupts are active */ |
233 | tmp = (*R_IRQ_READ1); | |
234 | ||
235 | /* Find those that we have enabled */ | |
236 | tmp &= gpio_pa_irq_enabled_mask; | |
237 | ||
238 | /* Clear them.. */ | |
239 | *R_IRQ_MASK1_CLR = tmp; | |
240 | gpio_pa_irq_enabled_mask &= ~tmp; | |
241 | ||
45a4127c | 242 | spin_unlock_irqrestore(&gpio_lock, flags); |
7e920426 | 243 | |
45a4127c | 244 | if (gpio_some_alarms) |
1da177e4 | 245 | return IRQ_RETVAL(etrax_gpio_wake_up_check()); |
45a4127c | 246 | |
1da177e4 LT |
247 | return IRQ_NONE; |
248 | } | |
249 | ||
45a4127c JN |
250 | static void gpio_write_bit(struct gpio_private *priv, |
251 | unsigned char data, int bit) | |
252 | { | |
253 | *priv->port = *priv->shadow &= ~(priv->clk_mask); | |
254 | if (data & 1 << bit) | |
255 | *priv->port = *priv->shadow |= priv->data_mask; | |
256 | else | |
257 | *priv->port = *priv->shadow &= ~(priv->data_mask); | |
258 | ||
259 | /* For FPGA: min 5.0ns (DCC) before CCLK high */ | |
260 | *priv->port = *priv->shadow |= priv->clk_mask; | |
261 | } | |
262 | ||
263 | static void gpio_write_byte(struct gpio_private *priv, unsigned char data) | |
264 | { | |
265 | int i; | |
266 | ||
267 | if (priv->write_msb) | |
268 | for (i = 7; i >= 0; i--) | |
269 | gpio_write_bit(priv, data, i); | |
270 | else | |
271 | for (i = 0; i <= 7; i++) | |
272 | gpio_write_bit(priv, data, i); | |
273 | } | |
1da177e4 | 274 | |
ad433f23 JN |
275 | static ssize_t gpio_write(struct file *file, const char __user *buf, |
276 | size_t count, loff_t *off) | |
1da177e4 | 277 | { |
ad433f23 | 278 | struct gpio_private *priv = file->private_data; |
1da177e4 | 279 | unsigned long flags; |
45a4127c | 280 | ssize_t retval = count; |
7e920426 | 281 | |
45a4127c JN |
282 | if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) |
283 | return -EFAULT; | |
284 | ||
285 | if (!access_ok(VERIFY_READ, buf, count)) | |
286 | return -EFAULT; | |
287 | ||
288 | spin_lock_irqsave(&gpio_lock, flags); | |
7e920426 | 289 | |
1da177e4 LT |
290 | /* It must have been configured using the IO_CFG_WRITE_MODE */ |
291 | /* Perhaps a better error code? */ | |
45a4127c | 292 | if (priv->clk_mask == 0 || priv->data_mask == 0) { |
5c6ff79d RK |
293 | retval = -EPERM; |
294 | goto out; | |
1da177e4 | 295 | } |
45a4127c JN |
296 | |
297 | D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " | |
298 | "clk 0x%02X msb: %i\n", | |
299 | count, priv->data_mask, priv->clk_mask, priv->write_msb)); | |
300 | ||
301 | while (count--) | |
302 | gpio_write_byte(priv, *buf++); | |
303 | ||
5c6ff79d | 304 | out: |
45a4127c | 305 | spin_unlock_irqrestore(&gpio_lock, flags); |
1da177e4 LT |
306 | return retval; |
307 | } | |
308 | ||
309 | ||
310 | ||
311 | static int | |
312 | gpio_open(struct inode *inode, struct file *filp) | |
313 | { | |
314 | struct gpio_private *priv; | |
32ea086b | 315 | int p = iminor(inode); |
45a4127c | 316 | unsigned long flags; |
1da177e4 LT |
317 | |
318 | if (p > GPIO_MINOR_LAST) | |
319 | return -EINVAL; | |
320 | ||
ad433f23 | 321 | priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); |
1da177e4 LT |
322 | |
323 | if (!priv) | |
324 | return -ENOMEM; | |
325 | ||
0c401df3 | 326 | lock_kernel(); |
1da177e4 LT |
327 | priv->minor = p; |
328 | ||
45a4127c | 329 | /* initialize the io/alarm struct */ |
1da177e4 | 330 | |
1da177e4 LT |
331 | if (USE_PORTS(priv)) { /* A and B */ |
332 | priv->port = ports[p]; | |
333 | priv->shadow = shads[p]; | |
334 | priv->dir = dir[p]; | |
335 | priv->dir_shadow = dir_shadow[p]; | |
336 | priv->changeable_dir = changeable_dir[p]; | |
337 | priv->changeable_bits = changeable_bits[p]; | |
338 | } else { | |
339 | priv->port = NULL; | |
340 | priv->shadow = NULL; | |
341 | priv->dir = NULL; | |
342 | priv->dir_shadow = NULL; | |
343 | priv->changeable_dir = 0; | |
344 | priv->changeable_bits = 0; | |
345 | } | |
346 | ||
347 | priv->highalarm = 0; | |
348 | priv->lowalarm = 0; | |
349 | priv->clk_mask = 0; | |
350 | priv->data_mask = 0; | |
351 | init_waitqueue_head(&priv->alarm_wq); | |
352 | ||
ad433f23 | 353 | filp->private_data = priv; |
1da177e4 | 354 | |
45a4127c JN |
355 | /* link it into our alarmlist */ |
356 | spin_lock_irqsave(&gpio_lock, flags); | |
357 | priv->next = alarmlist; | |
358 | alarmlist = priv; | |
359 | spin_unlock_irqrestore(&gpio_lock, flags); | |
360 | ||
0c401df3 | 361 | unlock_kernel(); |
1da177e4 LT |
362 | return 0; |
363 | } | |
364 | ||
365 | static int | |
366 | gpio_release(struct inode *inode, struct file *filp) | |
367 | { | |
7e920426 MS |
368 | struct gpio_private *p; |
369 | struct gpio_private *todel; | |
45a4127c | 370 | unsigned long flags; |
7e920426 | 371 | |
45a4127c | 372 | spin_lock_irqsave(&gpio_lock, flags); |
7e920426 | 373 | |
45a4127c | 374 | p = alarmlist; |
ad433f23 | 375 | todel = filp->private_data; |
7e920426 | 376 | |
1da177e4 LT |
377 | /* unlink from alarmlist and free the private structure */ |
378 | ||
379 | if (p == todel) { | |
380 | alarmlist = todel->next; | |
381 | } else { | |
382 | while (p->next != todel) | |
383 | p = p->next; | |
384 | p->next = todel->next; | |
385 | } | |
386 | ||
387 | kfree(todel); | |
388 | /* Check if there are still any alarms set */ | |
389 | p = alarmlist; | |
390 | while (p) { | |
391 | if (p->highalarm | p->lowalarm) { | |
392 | gpio_some_alarms = 1; | |
45a4127c | 393 | goto out; |
1da177e4 LT |
394 | } |
395 | p = p->next; | |
396 | } | |
397 | gpio_some_alarms = 0; | |
45a4127c JN |
398 | out: |
399 | spin_unlock_irqrestore(&gpio_lock, flags); | |
1da177e4 LT |
400 | return 0; |
401 | } | |
402 | ||
45a4127c | 403 | /* Main device API. ioctl's to read/set/clear bits, as well as to |
1da177e4 LT |
404 | * set alarms to wait for using a subsequent select(). |
405 | */ | |
1da177e4 LT |
406 | unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) |
407 | { | |
45a4127c JN |
408 | /* Set direction 0=unchanged 1=input, |
409 | * return mask with 1=input */ | |
1da177e4 | 410 | if (USE_PORTS(priv)) { |
45a4127c | 411 | *priv->dir = *priv->dir_shadow &= |
1da177e4 | 412 | ~((unsigned char)arg & priv->changeable_dir); |
1da177e4 | 413 | return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ |
45a4127c JN |
414 | } |
415 | ||
416 | if (priv->minor != GPIO_MINOR_G) | |
417 | return 0; | |
418 | ||
419 | /* We must fiddle with R_GEN_CONFIG to change dir */ | |
420 | if (((arg & dir_g_in_bits) != arg) && | |
421 | (arg & changeable_dir_g)) { | |
422 | arg &= changeable_dir_g; | |
423 | /* Clear bits in genconfig to set to input */ | |
424 | if (arg & (1<<0)) { | |
425 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); | |
426 | dir_g_in_bits |= (1<<0); | |
427 | dir_g_out_bits &= ~(1<<0); | |
428 | } | |
429 | if ((arg & 0x0000FF00) == 0x0000FF00) { | |
430 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); | |
431 | dir_g_in_bits |= 0x0000FF00; | |
432 | dir_g_out_bits &= ~0x0000FF00; | |
433 | } | |
434 | if ((arg & 0x00FF0000) == 0x00FF0000) { | |
435 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); | |
436 | dir_g_in_bits |= 0x00FF0000; | |
437 | dir_g_out_bits &= ~0x00FF0000; | |
438 | } | |
439 | if (arg & (1<<24)) { | |
440 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); | |
441 | dir_g_in_bits |= (1<<24); | |
442 | dir_g_out_bits &= ~(1<<24); | |
1da177e4 | 443 | } |
45a4127c JN |
444 | D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " |
445 | "genconfig to 0x%08lX " | |
446 | "in_bits: 0x%08lX " | |
447 | "out_bits: 0x%08lX\n", | |
448 | (unsigned long)genconfig_shadow, | |
449 | dir_g_in_bits, dir_g_out_bits)); | |
450 | *R_GEN_CONFIG = genconfig_shadow; | |
451 | /* Must be a >120 ns delay before writing this again */ | |
452 | ||
1da177e4 | 453 | } |
45a4127c | 454 | return dir_g_in_bits; |
1da177e4 LT |
455 | } /* setget_input */ |
456 | ||
457 | unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) | |
458 | { | |
1da177e4 | 459 | if (USE_PORTS(priv)) { |
45a4127c JN |
460 | *priv->dir = *priv->dir_shadow |= |
461 | ((unsigned char)arg & priv->changeable_dir); | |
1da177e4 | 462 | return *priv->dir_shadow; |
45a4127c JN |
463 | } |
464 | if (priv->minor != GPIO_MINOR_G) | |
465 | return 0; | |
466 | ||
467 | /* We must fiddle with R_GEN_CONFIG to change dir */ | |
468 | if (((arg & dir_g_out_bits) != arg) && | |
469 | (arg & changeable_dir_g)) { | |
470 | /* Set bits in genconfig to set to output */ | |
471 | if (arg & (1<<0)) { | |
472 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); | |
473 | dir_g_out_bits |= (1<<0); | |
474 | dir_g_in_bits &= ~(1<<0); | |
475 | } | |
476 | if ((arg & 0x0000FF00) == 0x0000FF00) { | |
477 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); | |
478 | dir_g_out_bits |= 0x0000FF00; | |
479 | dir_g_in_bits &= ~0x0000FF00; | |
480 | } | |
481 | if ((arg & 0x00FF0000) == 0x00FF0000) { | |
482 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); | |
483 | dir_g_out_bits |= 0x00FF0000; | |
484 | dir_g_in_bits &= ~0x00FF0000; | |
1da177e4 | 485 | } |
45a4127c JN |
486 | if (arg & (1<<24)) { |
487 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); | |
488 | dir_g_out_bits |= (1<<24); | |
489 | dir_g_in_bits &= ~(1<<24); | |
490 | } | |
491 | D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " | |
492 | "genconfig to 0x%08lX " | |
493 | "in_bits: 0x%08lX " | |
494 | "out_bits: 0x%08lX\n", | |
495 | (unsigned long)genconfig_shadow, | |
496 | dir_g_in_bits, dir_g_out_bits)); | |
497 | *R_GEN_CONFIG = genconfig_shadow; | |
498 | /* Must be a >120 ns delay before writing this again */ | |
1da177e4 | 499 | } |
45a4127c | 500 | return dir_g_out_bits & 0x7FFFFFFF; |
1da177e4 LT |
501 | } /* setget_output */ |
502 | ||
503 | static int | |
504 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg); | |
505 | ||
506 | static int | |
90276a1a | 507 | gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) |
1da177e4 LT |
508 | { |
509 | unsigned long flags; | |
510 | unsigned long val; | |
7e920426 MS |
511 | int ret = 0; |
512 | ||
ad433f23 | 513 | struct gpio_private *priv = file->private_data; |
45a4127c | 514 | if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) |
1da177e4 | 515 | return -EINVAL; |
1da177e4 | 516 | |
45a4127c | 517 | spin_lock_irqsave(&gpio_lock, flags); |
7e920426 | 518 | |
1da177e4 LT |
519 | switch (_IOC_NR(cmd)) { |
520 | case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ | |
521 | // read the port | |
522 | if (USE_PORTS(priv)) { | |
7e920426 | 523 | ret = *priv->port; |
1da177e4 | 524 | } else if (priv->minor == GPIO_MINOR_G) { |
7e920426 | 525 | ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; |
1da177e4 LT |
526 | } |
527 | break; | |
528 | case IO_SETBITS: | |
1da177e4 LT |
529 | // set changeable bits with a 1 in arg |
530 | if (USE_PORTS(priv)) { | |
531 | *priv->port = *priv->shadow |= | |
532 | ((unsigned char)arg & priv->changeable_bits); | |
533 | } else if (priv->minor == GPIO_MINOR_G) { | |
534 | *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); | |
535 | } | |
1da177e4 LT |
536 | break; |
537 | case IO_CLRBITS: | |
1da177e4 LT |
538 | // clear changeable bits with a 1 in arg |
539 | if (USE_PORTS(priv)) { | |
540 | *priv->port = *priv->shadow &= | |
541 | ~((unsigned char)arg & priv->changeable_bits); | |
542 | } else if (priv->minor == GPIO_MINOR_G) { | |
543 | *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); | |
544 | } | |
1da177e4 LT |
545 | break; |
546 | case IO_HIGHALARM: | |
547 | // set alarm when bits with 1 in arg go high | |
548 | priv->highalarm |= arg; | |
549 | gpio_some_alarms = 1; | |
550 | break; | |
551 | case IO_LOWALARM: | |
552 | // set alarm when bits with 1 in arg go low | |
553 | priv->lowalarm |= arg; | |
554 | gpio_some_alarms = 1; | |
555 | break; | |
556 | case IO_CLRALARM: | |
557 | // clear alarm for bits with 1 in arg | |
558 | priv->highalarm &= ~arg; | |
559 | priv->lowalarm &= ~arg; | |
560 | { | |
561 | /* Must update gpio_some_alarms */ | |
562 | struct gpio_private *p = alarmlist; | |
563 | int some_alarms; | |
45a4127c JN |
564 | spin_lock_irq(&gpio_lock); |
565 | p = alarmlist; | |
1da177e4 LT |
566 | some_alarms = 0; |
567 | while (p) { | |
568 | if (p->highalarm | p->lowalarm) { | |
569 | some_alarms = 1; | |
570 | break; | |
571 | } | |
572 | p = p->next; | |
573 | } | |
574 | gpio_some_alarms = some_alarms; | |
45a4127c | 575 | spin_unlock_irq(&gpio_lock); |
1da177e4 LT |
576 | } |
577 | break; | |
578 | case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ | |
579 | /* Read direction 0=input 1=output */ | |
580 | if (USE_PORTS(priv)) { | |
7e920426 | 581 | ret = *priv->dir_shadow; |
1da177e4 LT |
582 | } else if (priv->minor == GPIO_MINOR_G) { |
583 | /* Note: Some bits are both in and out, | |
584 | * Those that are dual is set here as well. | |
585 | */ | |
7e920426 | 586 | ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; |
1da177e4 | 587 | } |
7e920426 | 588 | break; |
1da177e4 LT |
589 | case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ |
590 | /* Set direction 0=unchanged 1=input, | |
591 | * return mask with 1=input | |
592 | */ | |
7e920426 | 593 | ret = setget_input(priv, arg) & 0x7FFFFFFF; |
1da177e4 LT |
594 | break; |
595 | case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ | |
596 | /* Set direction 0=unchanged 1=output, | |
597 | * return mask with 1=output | |
598 | */ | |
7e920426 MS |
599 | ret = setget_output(priv, arg) & 0x7FFFFFFF; |
600 | break; | |
1da177e4 LT |
601 | case IO_SHUTDOWN: |
602 | SOFT_SHUTDOWN(); | |
603 | break; | |
604 | case IO_GET_PWR_BT: | |
605 | #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) | |
7e920426 | 606 | ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); |
1da177e4 | 607 | #else |
7e920426 | 608 | ret = 0; |
1da177e4 LT |
609 | #endif |
610 | break; | |
611 | case IO_CFG_WRITE_MODE: | |
612 | priv->clk_mask = arg & 0xFF; | |
613 | priv->data_mask = (arg >> 8) & 0xFF; | |
614 | priv->write_msb = (arg >> 16) & 0x01; | |
615 | /* Check if we're allowed to change the bits and | |
616 | * the direction is correct | |
617 | */ | |
618 | if (!((priv->clk_mask & priv->changeable_bits) && | |
619 | (priv->data_mask & priv->changeable_bits) && | |
620 | (priv->clk_mask & *priv->dir_shadow) && | |
621 | (priv->data_mask & *priv->dir_shadow))) | |
622 | { | |
623 | priv->clk_mask = 0; | |
624 | priv->data_mask = 0; | |
7e920426 | 625 | ret = -EPERM; |
1da177e4 LT |
626 | } |
627 | break; | |
628 | case IO_READ_INBITS: | |
629 | /* *arg is result of reading the input pins */ | |
630 | if (USE_PORTS(priv)) { | |
631 | val = *priv->port; | |
632 | } else if (priv->minor == GPIO_MINOR_G) { | |
633 | val = *R_PORT_G_DATA; | |
634 | } | |
ad433f23 | 635 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
7e920426 | 636 | ret = -EFAULT; |
1da177e4 LT |
637 | break; |
638 | case IO_READ_OUTBITS: | |
639 | /* *arg is result of reading the output shadow */ | |
640 | if (USE_PORTS(priv)) { | |
641 | val = *priv->shadow; | |
642 | } else if (priv->minor == GPIO_MINOR_G) { | |
643 | val = port_g_data_shadow; | |
644 | } | |
ad433f23 | 645 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
7e920426 | 646 | ret = -EFAULT; |
1da177e4 LT |
647 | break; |
648 | case IO_SETGET_INPUT: | |
649 | /* bits set in *arg is set to input, | |
650 | * *arg updated with current input pins. | |
651 | */ | |
ad433f23 | 652 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) |
7e920426 MS |
653 | { |
654 | ret = -EFAULT; | |
655 | break; | |
656 | } | |
1da177e4 | 657 | val = setget_input(priv, val); |
ad433f23 | 658 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
7e920426 | 659 | ret = -EFAULT; |
1da177e4 LT |
660 | break; |
661 | case IO_SETGET_OUTPUT: | |
662 | /* bits set in *arg is set to output, | |
663 | * *arg updated with current output pins. | |
664 | */ | |
ad433f23 | 665 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { |
7e920426 MS |
666 | ret = -EFAULT; |
667 | break; | |
668 | } | |
1da177e4 | 669 | val = setget_output(priv, val); |
ad433f23 | 670 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
7e920426 | 671 | ret = -EFAULT; |
1da177e4 LT |
672 | break; |
673 | default: | |
674 | if (priv->minor == GPIO_MINOR_LEDS) | |
7e920426 | 675 | ret = gpio_leds_ioctl(cmd, arg); |
1da177e4 | 676 | else |
7e920426 | 677 | ret = -EINVAL; |
1da177e4 | 678 | } /* switch */ |
7e920426 | 679 | |
45a4127c | 680 | spin_unlock_irqrestore(&gpio_lock, flags); |
7e920426 | 681 | return ret; |
1da177e4 LT |
682 | } |
683 | ||
90276a1a JN |
684 | static int |
685 | gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
686 | { | |
687 | long ret; | |
688 | ||
689 | lock_kernel(); | |
690 | ret = gpio_ioctl_unlocked(file, cmd, arg); | |
691 | unlock_kernel(); | |
692 | ||
693 | return ret; | |
694 | } | |
695 | ||
1da177e4 LT |
696 | static int |
697 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg) | |
698 | { | |
699 | unsigned char green; | |
700 | unsigned char red; | |
701 | ||
702 | switch (_IOC_NR(cmd)) { | |
703 | case IO_LEDACTIVE_SET: | |
45a4127c JN |
704 | green = ((unsigned char)arg) & 1; |
705 | red = (((unsigned char)arg) >> 1) & 1; | |
706 | CRIS_LED_ACTIVE_SET_G(green); | |
707 | CRIS_LED_ACTIVE_SET_R(red); | |
1da177e4 LT |
708 | break; |
709 | ||
710 | case IO_LED_SETBIT: | |
45a4127c | 711 | CRIS_LED_BIT_SET(arg); |
1da177e4 LT |
712 | break; |
713 | ||
714 | case IO_LED_CLRBIT: | |
45a4127c | 715 | CRIS_LED_BIT_CLR(arg); |
1da177e4 LT |
716 | break; |
717 | ||
718 | default: | |
719 | return -EINVAL; | |
720 | } /* switch */ | |
721 | ||
722 | return 0; | |
723 | } | |
724 | ||
ad433f23 | 725 | static const struct file_operations gpio_fops = { |
90276a1a JN |
726 | .owner = THIS_MODULE, |
727 | .poll = gpio_poll, | |
728 | .unlocked_ioctl = gpio_ioctl, | |
729 | .write = gpio_write, | |
730 | .open = gpio_open, | |
731 | .release = gpio_release, | |
1da177e4 LT |
732 | }; |
733 | ||
ad433f23 JN |
734 | static void ioif_watcher(const unsigned int gpio_in_available, |
735 | const unsigned int gpio_out_available, | |
736 | const unsigned char pa_available, | |
737 | const unsigned char pb_available) | |
1da177e4 | 738 | { |
7e920426 | 739 | unsigned long int flags; |
45a4127c JN |
740 | |
741 | D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); | |
742 | D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x " | |
743 | "PA: 0x%02x PB: 0x%02x\n", | |
744 | gpio_in_available, gpio_out_available, | |
745 | pa_available, pb_available)); | |
1da177e4 | 746 | |
7e920426 | 747 | spin_lock_irqsave(&gpio_lock, flags); |
1da177e4 | 748 | |
7e920426 MS |
749 | dir_g_in_bits = gpio_in_available; |
750 | dir_g_out_bits = gpio_out_available; | |
1da177e4 LT |
751 | |
752 | /* Initialise the dir_g_shadow etc. depending on genconfig */ | |
753 | /* 0=input 1=output */ | |
45a4127c | 754 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) |
1da177e4 LT |
755 | dir_g_shadow |= (1 << 0); |
756 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) | |
757 | dir_g_shadow |= 0x0000FF00; | |
758 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out)) | |
759 | dir_g_shadow |= 0x00FF0000; | |
760 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) | |
761 | dir_g_shadow |= (1 << 24); | |
762 | ||
7e920426 | 763 | changeable_dir_g = changeable_dir_g_mask; |
1da177e4 LT |
764 | changeable_dir_g &= dir_g_out_bits; |
765 | changeable_dir_g &= dir_g_in_bits; | |
45a4127c JN |
766 | |
767 | /* Correct the bits that can change direction */ | |
1da177e4 LT |
768 | dir_g_out_bits &= ~changeable_dir_g; |
769 | dir_g_out_bits |= dir_g_shadow; | |
770 | dir_g_in_bits &= ~changeable_dir_g; | |
771 | dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); | |
772 | ||
7e920426 | 773 | spin_unlock_irqrestore(&gpio_lock, flags); |
1da177e4 | 774 | |
45a4127c JN |
775 | printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX " |
776 | "val: %08lX\n", | |
1da177e4 LT |
777 | dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); |
778 | printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", | |
779 | dir_g_shadow, changeable_dir_g); | |
780 | } | |
781 | ||
782 | /* main driver initialization routine, called from mem.c */ | |
783 | ||
ad433f23 | 784 | static int __init gpio_init(void) |
1da177e4 LT |
785 | { |
786 | int res; | |
787 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | |
788 | int i; | |
789 | #endif | |
1da177e4 LT |
790 | |
791 | res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); | |
792 | if (res < 0) { | |
793 | printk(KERN_ERR "gpio: couldn't get a major number.\n"); | |
794 | return res; | |
795 | } | |
796 | ||
797 | /* Clear all leds */ | |
798 | #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) | |
45a4127c JN |
799 | CRIS_LED_NETWORK_SET(0); |
800 | CRIS_LED_ACTIVE_SET(0); | |
801 | CRIS_LED_DISK_READ(0); | |
802 | CRIS_LED_DISK_WRITE(0); | |
1da177e4 LT |
803 | |
804 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | |
45a4127c JN |
805 | for (i = 0; i < 32; i++) |
806 | CRIS_LED_BIT_SET(i); | |
1da177e4 LT |
807 | #endif |
808 | ||
809 | #endif | |
7e920426 MS |
810 | /* The I/O interface allocation watcher will be called when |
811 | * registering it. */ | |
812 | if (cris_io_interface_register_watcher(ioif_watcher)){ | |
45a4127c JN |
813 | printk(KERN_WARNING "gpio_init: Failed to install IO " |
814 | "if allocator watcher\n"); | |
7e920426 MS |
815 | } |
816 | ||
45a4127c JN |
817 | printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 " |
818 | "Axis Communications AB\n"); | |
1da177e4 LT |
819 | /* We call etrax_gpio_wake_up_check() from timer interrupt and |
820 | * from cpu_idle() in kernel/process.c | |
821 | * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms | |
822 | * in some tests. | |
45a4127c JN |
823 | */ |
824 | res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, | |
825 | IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name); | |
826 | if (res) { | |
1da177e4 | 827 | printk(KERN_CRIT "err: timer0 irq for gpio\n"); |
45a4127c | 828 | return res; |
1da177e4 | 829 | } |
ad433f23 | 830 | res = request_irq(PA_IRQ_NBR, gpio_interrupt, |
45a4127c JN |
831 | IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name); |
832 | if (res) | |
1da177e4 | 833 | printk(KERN_CRIT "err: PA irq for gpio\n"); |
1da177e4 LT |
834 | |
835 | return res; | |
836 | } | |
837 | ||
838 | /* this makes sure that gpio_init is called during kernel boot */ | |
1da177e4 | 839 | module_init(gpio_init); |
ad433f23 | 840 |