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