Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/drivers/char/lcd.c
Parent Directory
|
Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13353 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13353 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * LCD, LED and Button interface for Cobalt |
3 | * |
4 | * This file is subject to the terms and conditions of the GNU General Public |
5 | * License. See the file "COPYING" in the main directory of this archive |
6 | * for more details. |
7 | * |
8 | * Copyright (C) 1996, 1997 by Andrew Bose |
9 | * |
10 | * Linux kernel version history: |
11 | * March 2001: Ported from 2.0.34 by Liam Davies |
12 | * |
13 | */ |
14 | |
15 | #define RTC_IO_EXTENT 0x10 /*Only really two ports, but... */ |
16 | |
17 | #include <linux/config.h> |
18 | #include <linux/types.h> |
19 | #include <linux/errno.h> |
20 | #include <linux/miscdevice.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/ioport.h> |
23 | #include <linux/fcntl.h> |
24 | #include <linux/mc146818rtc.h> |
25 | #include <linux/netdevice.h> |
26 | #include <linux/sched.h> |
27 | #include <linux/delay.h> |
28 | |
29 | #include <asm/io.h> |
30 | #include <asm/uaccess.h> |
31 | #include <asm/system.h> |
32 | #include <linux/delay.h> |
33 | |
34 | #include "lcd.h" |
35 | |
36 | static DEFINE_SPINLOCK(lcd_lock); |
37 | |
38 | static int lcd_ioctl(struct inode *inode, struct file *file, |
39 | unsigned int cmd, unsigned long arg); |
40 | |
41 | static unsigned int lcd_present = 1; |
42 | |
43 | /* used in arch/mips/cobalt/reset.c */ |
44 | int led_state = 0; |
45 | |
46 | #if defined(CONFIG_TULIP) && 0 |
47 | |
48 | #define MAX_INTERFACES 8 |
49 | static linkcheck_func_t linkcheck_callbacks[MAX_INTERFACES]; |
50 | static void *linkcheck_cookies[MAX_INTERFACES]; |
51 | |
52 | int lcd_register_linkcheck_func(int iface_num, void *func, void *cookie) |
53 | { |
54 | if (iface_num < 0 || |
55 | iface_num >= MAX_INTERFACES || |
56 | linkcheck_callbacks[iface_num] != NULL) |
57 | return -1; |
58 | linkcheck_callbacks[iface_num] = (linkcheck_func_t) func; |
59 | linkcheck_cookies[iface_num] = cookie; |
60 | return 0; |
61 | } |
62 | #endif |
63 | |
64 | static int lcd_ioctl(struct inode *inode, struct file *file, |
65 | unsigned int cmd, unsigned long arg) |
66 | { |
67 | struct lcd_display button_display; |
68 | unsigned long address, a; |
69 | |
70 | switch (cmd) { |
71 | case LCD_On: |
72 | udelay(150); |
73 | BusyCheck(); |
74 | LCDWriteInst(0x0F); |
75 | break; |
76 | |
77 | case LCD_Off: |
78 | udelay(150); |
79 | BusyCheck(); |
80 | LCDWriteInst(0x08); |
81 | break; |
82 | |
83 | case LCD_Reset: |
84 | udelay(150); |
85 | LCDWriteInst(0x3F); |
86 | udelay(150); |
87 | LCDWriteInst(0x3F); |
88 | udelay(150); |
89 | LCDWriteInst(0x3F); |
90 | udelay(150); |
91 | LCDWriteInst(0x3F); |
92 | udelay(150); |
93 | LCDWriteInst(0x01); |
94 | udelay(150); |
95 | LCDWriteInst(0x06); |
96 | break; |
97 | |
98 | case LCD_Clear: |
99 | udelay(150); |
100 | BusyCheck(); |
101 | LCDWriteInst(0x01); |
102 | break; |
103 | |
104 | case LCD_Cursor_Left: |
105 | udelay(150); |
106 | BusyCheck(); |
107 | LCDWriteInst(0x10); |
108 | break; |
109 | |
110 | case LCD_Cursor_Right: |
111 | udelay(150); |
112 | BusyCheck(); |
113 | LCDWriteInst(0x14); |
114 | break; |
115 | |
116 | case LCD_Cursor_Off: |
117 | udelay(150); |
118 | BusyCheck(); |
119 | LCDWriteInst(0x0C); |
120 | break; |
121 | |
122 | case LCD_Cursor_On: |
123 | udelay(150); |
124 | BusyCheck(); |
125 | LCDWriteInst(0x0F); |
126 | break; |
127 | |
128 | case LCD_Blink_Off: |
129 | udelay(150); |
130 | BusyCheck(); |
131 | LCDWriteInst(0x0E); |
132 | break; |
133 | |
134 | case LCD_Get_Cursor_Pos:{ |
135 | struct lcd_display display; |
136 | |
137 | udelay(150); |
138 | BusyCheck(); |
139 | display.cursor_address = (LCDReadInst); |
140 | display.cursor_address = |
141 | (display.cursor_address & 0x07F); |
142 | if (copy_to_user |
143 | ((struct lcd_display *) arg, &display, |
144 | sizeof(struct lcd_display))) |
145 | return -EFAULT; |
146 | |
147 | break; |
148 | } |
149 | |
150 | |
151 | case LCD_Set_Cursor_Pos:{ |
152 | struct lcd_display display; |
153 | |
154 | if (copy_from_user |
155 | (&display, (struct lcd_display *) arg, |
156 | sizeof(struct lcd_display))) |
157 | return -EFAULT; |
158 | |
159 | a = (display.cursor_address | kLCD_Addr); |
160 | |
161 | udelay(150); |
162 | BusyCheck(); |
163 | LCDWriteInst(a); |
164 | |
165 | break; |
166 | } |
167 | |
168 | case LCD_Get_Cursor:{ |
169 | struct lcd_display display; |
170 | |
171 | udelay(150); |
172 | BusyCheck(); |
173 | display.character = LCDReadData; |
174 | |
175 | if (copy_to_user |
176 | ((struct lcd_display *) arg, &display, |
177 | sizeof(struct lcd_display))) |
178 | return -EFAULT; |
179 | udelay(150); |
180 | BusyCheck(); |
181 | LCDWriteInst(0x10); |
182 | |
183 | break; |
184 | } |
185 | |
186 | case LCD_Set_Cursor:{ |
187 | struct lcd_display display; |
188 | |
189 | if (copy_from_user |
190 | (&display, (struct lcd_display *) arg, |
191 | sizeof(struct lcd_display))) |
192 | return -EFAULT; |
193 | |
194 | udelay(150); |
195 | BusyCheck(); |
196 | LCDWriteData(display.character); |
197 | udelay(150); |
198 | BusyCheck(); |
199 | LCDWriteInst(0x10); |
200 | |
201 | break; |
202 | } |
203 | |
204 | |
205 | case LCD_Disp_Left: |
206 | udelay(150); |
207 | BusyCheck(); |
208 | LCDWriteInst(0x18); |
209 | break; |
210 | |
211 | case LCD_Disp_Right: |
212 | udelay(150); |
213 | BusyCheck(); |
214 | LCDWriteInst(0x1C); |
215 | break; |
216 | |
217 | case LCD_Home: |
218 | udelay(150); |
219 | BusyCheck(); |
220 | LCDWriteInst(0x02); |
221 | break; |
222 | |
223 | case LCD_Write:{ |
224 | struct lcd_display display; |
225 | unsigned int index; |
226 | |
227 | |
228 | if (copy_from_user |
229 | (&display, (struct lcd_display *) arg, |
230 | sizeof(struct lcd_display))) |
231 | return -EFAULT; |
232 | |
233 | udelay(150); |
234 | BusyCheck(); |
235 | LCDWriteInst(0x80); |
236 | udelay(150); |
237 | BusyCheck(); |
238 | |
239 | for (index = 0; index < (display.size1); index++) { |
240 | udelay(150); |
241 | BusyCheck(); |
242 | LCDWriteData(display.line1[index]); |
243 | BusyCheck(); |
244 | } |
245 | |
246 | udelay(150); |
247 | BusyCheck(); |
248 | LCDWriteInst(0xC0); |
249 | udelay(150); |
250 | BusyCheck(); |
251 | for (index = 0; index < (display.size2); index++) { |
252 | udelay(150); |
253 | BusyCheck(); |
254 | LCDWriteData(display.line2[index]); |
255 | } |
256 | |
257 | break; |
258 | } |
259 | |
260 | case LCD_Read:{ |
261 | struct lcd_display display; |
262 | |
263 | BusyCheck(); |
264 | for (address = kDD_R00; address <= kDD_R01; |
265 | address++) { |
266 | a = (address | kLCD_Addr); |
267 | |
268 | udelay(150); |
269 | BusyCheck(); |
270 | LCDWriteInst(a); |
271 | udelay(150); |
272 | BusyCheck(); |
273 | display.line1[address] = LCDReadData; |
274 | } |
275 | |
276 | display.line1[0x27] = '\0'; |
277 | |
278 | for (address = kDD_R10; address <= kDD_R11; |
279 | address++) { |
280 | a = (address | kLCD_Addr); |
281 | |
282 | udelay(150); |
283 | BusyCheck(); |
284 | LCDWriteInst(a); |
285 | |
286 | udelay(150); |
287 | BusyCheck(); |
288 | display.line2[address - 0x40] = |
289 | LCDReadData; |
290 | } |
291 | |
292 | display.line2[0x27] = '\0'; |
293 | |
294 | if (copy_to_user |
295 | ((struct lcd_display *) arg, &display, |
296 | sizeof(struct lcd_display))) |
297 | return -EFAULT; |
298 | break; |
299 | } |
300 | |
301 | // set all GPIO leds to led_display.leds |
302 | |
303 | case LED_Set:{ |
304 | struct lcd_display led_display; |
305 | |
306 | |
307 | if (copy_from_user |
308 | (&led_display, (struct lcd_display *) arg, |
309 | sizeof(struct lcd_display))) |
310 | return -EFAULT; |
311 | |
312 | led_state = led_display.leds; |
313 | LEDSet(led_state); |
314 | |
315 | break; |
316 | } |
317 | |
318 | |
319 | // set only bit led_display.leds |
320 | |
321 | case LED_Bit_Set:{ |
322 | unsigned int i; |
323 | int bit = 1; |
324 | struct lcd_display led_display; |
325 | |
326 | |
327 | if (copy_from_user |
328 | (&led_display, (struct lcd_display *) arg, |
329 | sizeof(struct lcd_display))) |
330 | return -EFAULT; |
331 | |
332 | for (i = 0; i < (int) led_display.leds; i++) { |
333 | bit = 2 * bit; |
334 | } |
335 | |
336 | led_state = led_state | bit; |
337 | LEDSet(led_state); |
338 | break; |
339 | } |
340 | |
341 | // clear only bit led_display.leds |
342 | |
343 | case LED_Bit_Clear:{ |
344 | unsigned int i; |
345 | int bit = 1; |
346 | struct lcd_display led_display; |
347 | |
348 | |
349 | if (copy_from_user |
350 | (&led_display, (struct lcd_display *) arg, |
351 | sizeof(struct lcd_display))) |
352 | return -EFAULT; |
353 | |
354 | for (i = 0; i < (int) led_display.leds; i++) { |
355 | bit = 2 * bit; |
356 | } |
357 | |
358 | led_state = led_state & ~bit; |
359 | LEDSet(led_state); |
360 | break; |
361 | } |
362 | |
363 | |
364 | case BUTTON_Read:{ |
365 | button_display.buttons = GPIRead; |
366 | if (copy_to_user |
367 | ((struct lcd_display *) arg, &button_display, |
368 | sizeof(struct lcd_display))) |
369 | return -EFAULT; |
370 | break; |
371 | } |
372 | |
373 | case LINK_Check:{ |
374 | button_display.buttons = |
375 | *((volatile unsigned long *) (0xB0100060)); |
376 | if (copy_to_user |
377 | ((struct lcd_display *) arg, &button_display, |
378 | sizeof(struct lcd_display))) |
379 | return -EFAULT; |
380 | break; |
381 | } |
382 | |
383 | case LINK_Check_2:{ |
384 | int iface_num; |
385 | |
386 | /* panel-utils should pass in the desired interface status is wanted for |
387 | * in "buttons" of the structure. We will set this to non-zero if the |
388 | * link is in fact up for the requested interface. --DaveM |
389 | */ |
390 | if (copy_from_user |
391 | (&button_display, (struct lcd_display *) arg, |
392 | sizeof(button_display))) |
393 | return -EFAULT; |
394 | iface_num = button_display.buttons; |
395 | #if defined(CONFIG_TULIP) && 0 |
396 | if (iface_num >= 0 && |
397 | iface_num < MAX_INTERFACES && |
398 | linkcheck_callbacks[iface_num] != NULL) { |
399 | button_display.buttons = |
400 | linkcheck_callbacks[iface_num] |
401 | (linkcheck_cookies[iface_num]); |
402 | } else |
403 | #endif |
404 | button_display.buttons = 0; |
405 | |
406 | if (__copy_to_user |
407 | ((struct lcd_display *) arg, &button_display, |
408 | sizeof(struct lcd_display))) |
409 | return -EFAULT; |
410 | break; |
411 | } |
412 | |
413 | // Erase the flash |
414 | |
415 | case FLASH_Erase:{ |
416 | |
417 | int ctr = 0; |
418 | |
419 | if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; |
420 | |
421 | pr_info(LCD "Erasing Flash\n"); |
422 | |
423 | // Chip Erase Sequence |
424 | WRITE_FLASH(kFlash_Addr1, kFlash_Data1); |
425 | WRITE_FLASH(kFlash_Addr2, kFlash_Data2); |
426 | WRITE_FLASH(kFlash_Addr1, kFlash_Erase3); |
427 | WRITE_FLASH(kFlash_Addr1, kFlash_Data1); |
428 | WRITE_FLASH(kFlash_Addr2, kFlash_Data2); |
429 | WRITE_FLASH(kFlash_Addr1, kFlash_Erase6); |
430 | |
431 | while ((!dqpoll(0x00000000, 0xFF)) |
432 | && (!timeout(0x00000000))) { |
433 | ctr++; |
434 | } |
435 | |
436 | if (READ_FLASH(0x07FFF0) == 0xFF) { |
437 | pr_info(LCD "Erase Successful\n"); |
438 | } else if (timeout) { |
439 | pr_info(LCD "Erase Timed Out\n"); |
440 | } |
441 | |
442 | break; |
443 | } |
444 | |
445 | // burn the flash |
446 | |
447 | case FLASH_Burn:{ |
448 | |
449 | volatile unsigned long burn_addr; |
450 | unsigned long flags; |
451 | unsigned int i, index; |
452 | unsigned char *rom; |
453 | |
454 | |
455 | struct lcd_display display; |
456 | |
457 | if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; |
458 | |
459 | if (copy_from_user |
460 | (&display, (struct lcd_display *) arg, |
461 | sizeof(struct lcd_display))) |
462 | return -EFAULT; |
463 | rom = (unsigned char *) kmalloc((128), GFP_ATOMIC); |
464 | if (rom == NULL) { |
465 | printk(KERN_ERR LCD "kmalloc() failed in %s\n", |
466 | __FUNCTION__); |
467 | return -ENOMEM; |
468 | } |
469 | |
470 | pr_info(LCD "Starting Flash burn\n"); |
471 | for (i = 0; i < FLASH_SIZE; i = i + 128) { |
472 | |
473 | if (copy_from_user |
474 | (rom, display.RomImage + i, 128)) { |
475 | kfree(rom); |
476 | return -EFAULT; |
477 | } |
478 | burn_addr = kFlashBase + i; |
479 | spin_lock_irqsave(&lcd_lock, flags); |
480 | for (index = 0; index < (128); index++) { |
481 | |
482 | WRITE_FLASH(kFlash_Addr1, |
483 | kFlash_Data1); |
484 | WRITE_FLASH(kFlash_Addr2, |
485 | kFlash_Data2); |
486 | WRITE_FLASH(kFlash_Addr1, |
487 | kFlash_Prog); |
488 | *((volatile unsigned char *)burn_addr) = |
489 | (volatile unsigned char) rom[index]; |
490 | |
491 | while ((!dqpoll (burn_addr, |
492 | (volatile unsigned char) |
493 | rom[index])) && |
494 | (!timeout(burn_addr))) { } |
495 | burn_addr++; |
496 | } |
497 | spin_unlock_irqrestore(&lcd_lock, flags); |
498 | if (* ((volatile unsigned char *) |
499 | (burn_addr - 1)) == |
500 | (volatile unsigned char) |
501 | rom[index - 1]) { |
502 | } else if (timeout) { |
503 | pr_info(LCD "Flash burn timed out\n"); |
504 | } |
505 | |
506 | |
507 | } |
508 | kfree(rom); |
509 | |
510 | pr_info(LCD "Flash successfully burned\n"); |
511 | |
512 | break; |
513 | } |
514 | |
515 | // read the flash all at once |
516 | |
517 | case FLASH_Read:{ |
518 | |
519 | unsigned char *user_bytes; |
520 | volatile unsigned long read_addr; |
521 | unsigned int i; |
522 | |
523 | user_bytes = |
524 | &(((struct lcd_display *) arg)->RomImage[0]); |
525 | |
526 | if (!access_ok |
527 | (VERIFY_WRITE, user_bytes, FLASH_SIZE)) |
528 | return -EFAULT; |
529 | |
530 | pr_info(LCD "Reading Flash"); |
531 | for (i = 0; i < FLASH_SIZE; i++) { |
532 | unsigned char tmp_byte; |
533 | read_addr = kFlashBase + i; |
534 | tmp_byte = |
535 | *((volatile unsigned char *) |
536 | read_addr); |
537 | if (__put_user(tmp_byte, &user_bytes[i])) |
538 | return -EFAULT; |
539 | } |
540 | |
541 | |
542 | break; |
543 | } |
544 | |
545 | default: |
546 | return -EINVAL; |
547 | |
548 | } |
549 | |
550 | return 0; |
551 | |
552 | } |
553 | |
554 | static int lcd_open(struct inode *inode, struct file *file) |
555 | { |
556 | if (!lcd_present) |
557 | return -ENXIO; |
558 | else |
559 | return 0; |
560 | } |
561 | |
562 | /* Only RESET or NEXT counts as button pressed */ |
563 | |
564 | static inline int button_pressed(void) |
565 | { |
566 | unsigned long buttons = GPIRead; |
567 | |
568 | if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B) |
569 | || (buttons == BUTTON_Reset_B)) |
570 | return buttons; |
571 | return 0; |
572 | } |
573 | |
574 | /* LED daemon sits on this and we wake him up once a key is pressed. */ |
575 | |
576 | static int lcd_waiters = 0; |
577 | |
578 | static long lcd_read(struct inode *inode, struct file *file, char *buf, |
579 | unsigned long count) |
580 | { |
581 | long buttons_now; |
582 | |
583 | if (lcd_waiters > 0) |
584 | return -EINVAL; |
585 | |
586 | lcd_waiters++; |
587 | while (((buttons_now = (long) button_pressed()) == 0) && |
588 | !(signal_pending(current))) { |
589 | msleep_interruptible(2000); |
590 | } |
591 | lcd_waiters--; |
592 | |
593 | if (signal_pending(current)) |
594 | return -ERESTARTSYS; |
595 | return buttons_now; |
596 | } |
597 | |
598 | /* |
599 | * The various file operations we support. |
600 | */ |
601 | |
602 | static struct file_operations lcd_fops = { |
603 | .read = lcd_read, |
604 | .ioctl = lcd_ioctl, |
605 | .open = lcd_open, |
606 | }; |
607 | |
608 | static struct miscdevice lcd_dev = { |
609 | MISC_DYNAMIC_MINOR, |
610 | "lcd", |
611 | &lcd_fops |
612 | }; |
613 | |
614 | static int lcd_init(void) |
615 | { |
616 | unsigned long data; |
617 | |
618 | pr_info("%s\n", LCD_DRIVER); |
619 | misc_register(&lcd_dev); |
620 | |
621 | /* Check region? Naaah! Just snarf it up. */ |
622 | /* request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/ |
623 | |
624 | udelay(150); |
625 | data = LCDReadData; |
626 | if ((data & 0x000000FF) == (0x00)) { |
627 | lcd_present = 0; |
628 | pr_info(LCD "LCD Not Present\n"); |
629 | } else { |
630 | lcd_present = 1; |
631 | WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg); |
632 | WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg); |
633 | } |
634 | |
635 | return 0; |
636 | } |
637 | |
638 | static void __exit lcd_exit(void) |
639 | { |
640 | misc_deregister(&lcd_dev); |
641 | } |
642 | |
643 | // |
644 | // Function: dqpoll |
645 | // |
646 | // Description: Polls the data lines to see if the flash is busy |
647 | // |
648 | // In: address, byte data |
649 | // |
650 | // Out: 0 = busy, 1 = write or erase complete |
651 | // |
652 | // |
653 | |
654 | static int dqpoll(volatile unsigned long address, volatile unsigned char data) |
655 | { |
656 | volatile unsigned char dq7; |
657 | |
658 | dq7 = data & 0x80; |
659 | |
660 | return ((READ_FLASH(address) & 0x80) == dq7); |
661 | } |
662 | |
663 | // |
664 | // Function: timeout |
665 | // |
666 | // Description: Checks to see if erase or write has timed out |
667 | // By polling dq5 |
668 | // |
669 | // In: address |
670 | // |
671 | // |
672 | // Out: 0 = not timed out, 1 = timed out |
673 | |
674 | static int timeout(volatile unsigned long address) |
675 | { |
676 | return (READ_FLASH(address) & 0x20) == 0x20; |
677 | } |
678 | |
679 | module_init(lcd_init); |
680 | module_exit(lcd_exit); |
681 | |
682 | MODULE_AUTHOR("Andrew Bose"); |
683 | MODULE_LICENSE("GPL"); |