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