at v2.6.26-rc2 513 lines 9.8 kB view raw
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#include <linux/types.h> 15#include <linux/errno.h> 16#include <linux/miscdevice.h> 17#include <linux/slab.h> 18#include <linux/ioport.h> 19#include <linux/fcntl.h> 20#include <linux/mc146818rtc.h> 21#include <linux/netdevice.h> 22#include <linux/sched.h> 23#include <linux/delay.h> 24 25#include <asm/io.h> 26#include <asm/uaccess.h> 27#include <asm/system.h> 28 29#include "lcd.h" 30 31static int lcd_ioctl(struct inode *inode, struct file *file, 32 unsigned int cmd, unsigned long arg); 33 34static unsigned int lcd_present = 1; 35 36/* used in arch/mips/cobalt/reset.c */ 37int led_state = 0; 38 39#if defined(CONFIG_TULIP) && 0 40 41#define MAX_INTERFACES 8 42static linkcheck_func_t linkcheck_callbacks[MAX_INTERFACES]; 43static void *linkcheck_cookies[MAX_INTERFACES]; 44 45int lcd_register_linkcheck_func(int iface_num, void *func, void *cookie) 46{ 47 if (iface_num < 0 || 48 iface_num >= MAX_INTERFACES || 49 linkcheck_callbacks[iface_num] != NULL) 50 return -1; 51 linkcheck_callbacks[iface_num] = (linkcheck_func_t) func; 52 linkcheck_cookies[iface_num] = cookie; 53 return 0; 54} 55#endif 56 57static int lcd_ioctl(struct inode *inode, struct file *file, 58 unsigned int cmd, unsigned long arg) 59{ 60 struct lcd_display button_display; 61 unsigned long address, a; 62 63 switch (cmd) { 64 case LCD_On: 65 udelay(150); 66 BusyCheck(); 67 LCDWriteInst(0x0F); 68 break; 69 70 case LCD_Off: 71 udelay(150); 72 BusyCheck(); 73 LCDWriteInst(0x08); 74 break; 75 76 case LCD_Reset: 77 udelay(150); 78 LCDWriteInst(0x3F); 79 udelay(150); 80 LCDWriteInst(0x3F); 81 udelay(150); 82 LCDWriteInst(0x3F); 83 udelay(150); 84 LCDWriteInst(0x3F); 85 udelay(150); 86 LCDWriteInst(0x01); 87 udelay(150); 88 LCDWriteInst(0x06); 89 break; 90 91 case LCD_Clear: 92 udelay(150); 93 BusyCheck(); 94 LCDWriteInst(0x01); 95 break; 96 97 case LCD_Cursor_Left: 98 udelay(150); 99 BusyCheck(); 100 LCDWriteInst(0x10); 101 break; 102 103 case LCD_Cursor_Right: 104 udelay(150); 105 BusyCheck(); 106 LCDWriteInst(0x14); 107 break; 108 109 case LCD_Cursor_Off: 110 udelay(150); 111 BusyCheck(); 112 LCDWriteInst(0x0C); 113 break; 114 115 case LCD_Cursor_On: 116 udelay(150); 117 BusyCheck(); 118 LCDWriteInst(0x0F); 119 break; 120 121 case LCD_Blink_Off: 122 udelay(150); 123 BusyCheck(); 124 LCDWriteInst(0x0E); 125 break; 126 127 case LCD_Get_Cursor_Pos:{ 128 struct lcd_display display; 129 130 udelay(150); 131 BusyCheck(); 132 display.cursor_address = (LCDReadInst); 133 display.cursor_address = 134 (display.cursor_address & 0x07F); 135 if (copy_to_user 136 ((struct lcd_display *) arg, &display, 137 sizeof(struct lcd_display))) 138 return -EFAULT; 139 140 break; 141 } 142 143 144 case LCD_Set_Cursor_Pos:{ 145 struct lcd_display display; 146 147 if (copy_from_user 148 (&display, (struct lcd_display *) arg, 149 sizeof(struct lcd_display))) 150 return -EFAULT; 151 152 a = (display.cursor_address | kLCD_Addr); 153 154 udelay(150); 155 BusyCheck(); 156 LCDWriteInst(a); 157 158 break; 159 } 160 161 case LCD_Get_Cursor:{ 162 struct lcd_display display; 163 164 udelay(150); 165 BusyCheck(); 166 display.character = LCDReadData; 167 168 if (copy_to_user 169 ((struct lcd_display *) arg, &display, 170 sizeof(struct lcd_display))) 171 return -EFAULT; 172 udelay(150); 173 BusyCheck(); 174 LCDWriteInst(0x10); 175 176 break; 177 } 178 179 case LCD_Set_Cursor:{ 180 struct lcd_display display; 181 182 if (copy_from_user 183 (&display, (struct lcd_display *) arg, 184 sizeof(struct lcd_display))) 185 return -EFAULT; 186 187 udelay(150); 188 BusyCheck(); 189 LCDWriteData(display.character); 190 udelay(150); 191 BusyCheck(); 192 LCDWriteInst(0x10); 193 194 break; 195 } 196 197 198 case LCD_Disp_Left: 199 udelay(150); 200 BusyCheck(); 201 LCDWriteInst(0x18); 202 break; 203 204 case LCD_Disp_Right: 205 udelay(150); 206 BusyCheck(); 207 LCDWriteInst(0x1C); 208 break; 209 210 case LCD_Home: 211 udelay(150); 212 BusyCheck(); 213 LCDWriteInst(0x02); 214 break; 215 216 case LCD_Write:{ 217 struct lcd_display display; 218 unsigned int index; 219 220 221 if (copy_from_user 222 (&display, (struct lcd_display *) arg, 223 sizeof(struct lcd_display))) 224 return -EFAULT; 225 226 udelay(150); 227 BusyCheck(); 228 LCDWriteInst(0x80); 229 udelay(150); 230 BusyCheck(); 231 232 for (index = 0; index < (display.size1); index++) { 233 udelay(150); 234 BusyCheck(); 235 LCDWriteData(display.line1[index]); 236 BusyCheck(); 237 } 238 239 udelay(150); 240 BusyCheck(); 241 LCDWriteInst(0xC0); 242 udelay(150); 243 BusyCheck(); 244 for (index = 0; index < (display.size2); index++) { 245 udelay(150); 246 BusyCheck(); 247 LCDWriteData(display.line2[index]); 248 } 249 250 break; 251 } 252 253 case LCD_Read:{ 254 struct lcd_display display; 255 256 BusyCheck(); 257 for (address = kDD_R00; address <= kDD_R01; 258 address++) { 259 a = (address | kLCD_Addr); 260 261 udelay(150); 262 BusyCheck(); 263 LCDWriteInst(a); 264 udelay(150); 265 BusyCheck(); 266 display.line1[address] = LCDReadData; 267 } 268 269 display.line1[0x27] = '\0'; 270 271 for (address = kDD_R10; address <= kDD_R11; 272 address++) { 273 a = (address | kLCD_Addr); 274 275 udelay(150); 276 BusyCheck(); 277 LCDWriteInst(a); 278 279 udelay(150); 280 BusyCheck(); 281 display.line2[address - 0x40] = 282 LCDReadData; 283 } 284 285 display.line2[0x27] = '\0'; 286 287 if (copy_to_user 288 ((struct lcd_display *) arg, &display, 289 sizeof(struct lcd_display))) 290 return -EFAULT; 291 break; 292 } 293 294// set all GPIO leds to led_display.leds 295 296 case LED_Set:{ 297 struct lcd_display led_display; 298 299 300 if (copy_from_user 301 (&led_display, (struct lcd_display *) arg, 302 sizeof(struct lcd_display))) 303 return -EFAULT; 304 305 led_state = led_display.leds; 306 LEDSet(led_state); 307 308 break; 309 } 310 311 312// set only bit led_display.leds 313 314 case LED_Bit_Set:{ 315 unsigned int i; 316 int bit = 1; 317 struct lcd_display led_display; 318 319 320 if (copy_from_user 321 (&led_display, (struct lcd_display *) arg, 322 sizeof(struct lcd_display))) 323 return -EFAULT; 324 325 for (i = 0; i < (int) led_display.leds; i++) { 326 bit = 2 * bit; 327 } 328 329 led_state = led_state | bit; 330 LEDSet(led_state); 331 break; 332 } 333 334// clear only bit led_display.leds 335 336 case LED_Bit_Clear:{ 337 unsigned int i; 338 int bit = 1; 339 struct lcd_display led_display; 340 341 342 if (copy_from_user 343 (&led_display, (struct lcd_display *) arg, 344 sizeof(struct lcd_display))) 345 return -EFAULT; 346 347 for (i = 0; i < (int) led_display.leds; i++) { 348 bit = 2 * bit; 349 } 350 351 led_state = led_state & ~bit; 352 LEDSet(led_state); 353 break; 354 } 355 356 357 case BUTTON_Read:{ 358 button_display.buttons = GPIRead; 359 if (copy_to_user 360 ((struct lcd_display *) arg, &button_display, 361 sizeof(struct lcd_display))) 362 return -EFAULT; 363 break; 364 } 365 366 case LINK_Check:{ 367 button_display.buttons = 368 *((volatile unsigned long *) (0xB0100060)); 369 if (copy_to_user 370 ((struct lcd_display *) arg, &button_display, 371 sizeof(struct lcd_display))) 372 return -EFAULT; 373 break; 374 } 375 376 case LINK_Check_2:{ 377 int iface_num; 378 379 /* panel-utils should pass in the desired interface status is wanted for 380 * in "buttons" of the structure. We will set this to non-zero if the 381 * link is in fact up for the requested interface. --DaveM 382 */ 383 if (copy_from_user 384 (&button_display, (struct lcd_display *) arg, 385 sizeof(button_display))) 386 return -EFAULT; 387 iface_num = button_display.buttons; 388#if defined(CONFIG_TULIP) && 0 389 if (iface_num >= 0 && 390 iface_num < MAX_INTERFACES && 391 linkcheck_callbacks[iface_num] != NULL) { 392 button_display.buttons = 393 linkcheck_callbacks[iface_num] 394 (linkcheck_cookies[iface_num]); 395 } else 396#endif 397 button_display.buttons = 0; 398 399 if (__copy_to_user 400 ((struct lcd_display *) arg, &button_display, 401 sizeof(struct lcd_display))) 402 return -EFAULT; 403 break; 404 } 405 406 default: 407 return -EINVAL; 408 409 } 410 411 return 0; 412 413} 414 415static int lcd_open(struct inode *inode, struct file *file) 416{ 417 if (!lcd_present) 418 return -ENXIO; 419 else 420 return 0; 421} 422 423/* Only RESET or NEXT counts as button pressed */ 424 425static inline int button_pressed(void) 426{ 427 unsigned long buttons = GPIRead; 428 429 if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B) 430 || (buttons == BUTTON_Reset_B)) 431 return buttons; 432 return 0; 433} 434 435/* LED daemon sits on this and we wake him up once a key is pressed. */ 436 437static int lcd_waiters = 0; 438 439static ssize_t lcd_read(struct file *file, char *buf, 440 size_t count, loff_t *ofs) 441{ 442 long buttons_now; 443 444 if (lcd_waiters > 0) 445 return -EINVAL; 446 447 lcd_waiters++; 448 while (((buttons_now = (long) button_pressed()) == 0) && 449 !(signal_pending(current))) { 450 msleep_interruptible(2000); 451 } 452 lcd_waiters--; 453 454 if (signal_pending(current)) 455 return -ERESTARTSYS; 456 return buttons_now; 457} 458 459/* 460 * The various file operations we support. 461 */ 462 463static const struct file_operations lcd_fops = { 464 .read = lcd_read, 465 .ioctl = lcd_ioctl, 466 .open = lcd_open, 467}; 468 469static struct miscdevice lcd_dev = { 470 MISC_DYNAMIC_MINOR, 471 "lcd", 472 &lcd_fops 473}; 474 475static int lcd_init(void) 476{ 477 int ret; 478 unsigned long data; 479 480 pr_info("%s\n", LCD_DRIVER); 481 ret = misc_register(&lcd_dev); 482 if (ret) { 483 printk(KERN_WARNING LCD "Unable to register misc device.\n"); 484 return ret; 485 } 486 487 /* Check region? Naaah! Just snarf it up. */ 488/* request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/ 489 490 udelay(150); 491 data = LCDReadData; 492 if ((data & 0x000000FF) == (0x00)) { 493 lcd_present = 0; 494 pr_info(LCD "LCD Not Present\n"); 495 } else { 496 lcd_present = 1; 497 WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg); 498 WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg); 499 } 500 501 return 0; 502} 503 504static void __exit lcd_exit(void) 505{ 506 misc_deregister(&lcd_dev); 507} 508 509module_init(lcd_init); 510module_exit(lcd_exit); 511 512MODULE_AUTHOR("Andrew Bose"); 513MODULE_LICENSE("GPL");