summaryrefslogtreecommitdiff
path: root/drivers/char/lcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/lcd.c')
-rw-r--r--drivers/char/lcd.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c
new file mode 100644
index 000000000000..cf01a720eb2e
--- /dev/null
+++ b/drivers/char/lcd.c
@@ -0,0 +1,683 @@
+/*
+ * LCD, LED and Button interface for Cobalt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996, 1997 by Andrew Bose
+ *
+ * Linux kernel version history:
+ * March 2001: Ported from 2.0.34 by Liam Davies
+ *
+ */
+
+#define RTC_IO_EXTENT 0x10 /*Only really two ports, but... */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/mc146818rtc.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+
+#include "lcd.h"
+
+static DEFINE_SPINLOCK(lcd_lock);
+
+static int lcd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static unsigned int lcd_present = 1;
+
+/* used in arch/mips/cobalt/reset.c */
+int led_state = 0;
+
+#if defined(CONFIG_TULIP) && 0
+
+#define MAX_INTERFACES 8
+static linkcheck_func_t linkcheck_callbacks[MAX_INTERFACES];
+static void *linkcheck_cookies[MAX_INTERFACES];
+
+int lcd_register_linkcheck_func(int iface_num, void *func, void *cookie)
+{
+ if (iface_num < 0 ||
+ iface_num >= MAX_INTERFACES ||
+ linkcheck_callbacks[iface_num] != NULL)
+ return -1;
+ linkcheck_callbacks[iface_num] = (linkcheck_func_t) func;
+ linkcheck_cookies[iface_num] = cookie;
+ return 0;
+}
+#endif
+
+static int lcd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lcd_display button_display;
+ unsigned long address, a;
+
+ switch (cmd) {
+ case LCD_On:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x0F);
+ break;
+
+ case LCD_Off:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x08);
+ break;
+
+ case LCD_Reset:
+ udelay(150);
+ LCDWriteInst(0x3F);
+ udelay(150);
+ LCDWriteInst(0x3F);
+ udelay(150);
+ LCDWriteInst(0x3F);
+ udelay(150);
+ LCDWriteInst(0x3F);
+ udelay(150);
+ LCDWriteInst(0x01);
+ udelay(150);
+ LCDWriteInst(0x06);
+ break;
+
+ case LCD_Clear:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x01);
+ break;
+
+ case LCD_Cursor_Left:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x10);
+ break;
+
+ case LCD_Cursor_Right:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x14);
+ break;
+
+ case LCD_Cursor_Off:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x0C);
+ break;
+
+ case LCD_Cursor_On:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x0F);
+ break;
+
+ case LCD_Blink_Off:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x0E);
+ break;
+
+ case LCD_Get_Cursor_Pos:{
+ struct lcd_display display;
+
+ udelay(150);
+ BusyCheck();
+ display.cursor_address = (LCDReadInst);
+ display.cursor_address =
+ (display.cursor_address & 0x07F);
+ if (copy_to_user
+ ((struct lcd_display *) arg, &display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ break;
+ }
+
+
+ case LCD_Set_Cursor_Pos:{
+ struct lcd_display display;
+
+ if (copy_from_user
+ (&display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ a = (display.cursor_address | kLCD_Addr);
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(a);
+
+ break;
+ }
+
+ case LCD_Get_Cursor:{
+ struct lcd_display display;
+
+ udelay(150);
+ BusyCheck();
+ display.character = LCDReadData;
+
+ if (copy_to_user
+ ((struct lcd_display *) arg, &display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x10);
+
+ break;
+ }
+
+ case LCD_Set_Cursor:{
+ struct lcd_display display;
+
+ if (copy_from_user
+ (&display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteData(display.character);
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x10);
+
+ break;
+ }
+
+
+ case LCD_Disp_Left:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x18);
+ break;
+
+ case LCD_Disp_Right:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x1C);
+ break;
+
+ case LCD_Home:
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x02);
+ break;
+
+ case LCD_Write:{
+ struct lcd_display display;
+ unsigned int index;
+
+
+ if (copy_from_user
+ (&display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0x80);
+ udelay(150);
+ BusyCheck();
+
+ for (index = 0; index < (display.size1); index++) {
+ udelay(150);
+ BusyCheck();
+ LCDWriteData(display.line1[index]);
+ BusyCheck();
+ }
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(0xC0);
+ udelay(150);
+ BusyCheck();
+ for (index = 0; index < (display.size2); index++) {
+ udelay(150);
+ BusyCheck();
+ LCDWriteData(display.line2[index]);
+ }
+
+ break;
+ }
+
+ case LCD_Read:{
+ struct lcd_display display;
+
+ BusyCheck();
+ for (address = kDD_R00; address <= kDD_R01;
+ address++) {
+ a = (address | kLCD_Addr);
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(a);
+ udelay(150);
+ BusyCheck();
+ display.line1[address] = LCDReadData;
+ }
+
+ display.line1[0x27] = '\0';
+
+ for (address = kDD_R10; address <= kDD_R11;
+ address++) {
+ a = (address | kLCD_Addr);
+
+ udelay(150);
+ BusyCheck();
+ LCDWriteInst(a);
+
+ udelay(150);
+ BusyCheck();
+ display.line2[address - 0x40] =
+ LCDReadData;
+ }
+
+ display.line2[0x27] = '\0';
+
+ if (copy_to_user
+ ((struct lcd_display *) arg, &display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ break;
+ }
+
+// set all GPIO leds to led_display.leds
+
+ case LED_Set:{
+ struct lcd_display led_display;
+
+
+ if (copy_from_user
+ (&led_display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ led_state = led_display.leds;
+ LEDSet(led_state);
+
+ break;
+ }
+
+
+// set only bit led_display.leds
+
+ case LED_Bit_Set:{
+ unsigned int i;
+ int bit = 1;
+ struct lcd_display led_display;
+
+
+ if (copy_from_user
+ (&led_display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ for (i = 0; i < (int) led_display.leds; i++) {
+ bit = 2 * bit;
+ }
+
+ led_state = led_state | bit;
+ LEDSet(led_state);
+ break;
+ }
+
+// clear only bit led_display.leds
+
+ case LED_Bit_Clear:{
+ unsigned int i;
+ int bit = 1;
+ struct lcd_display led_display;
+
+
+ if (copy_from_user
+ (&led_display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+
+ for (i = 0; i < (int) led_display.leds; i++) {
+ bit = 2 * bit;
+ }
+
+ led_state = led_state & ~bit;
+ LEDSet(led_state);
+ break;
+ }
+
+
+ case BUTTON_Read:{
+ button_display.buttons = GPIRead;
+ if (copy_to_user
+ ((struct lcd_display *) arg, &button_display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ break;
+ }
+
+ case LINK_Check:{
+ button_display.buttons =
+ *((volatile unsigned long *) (0xB0100060));
+ if (copy_to_user
+ ((struct lcd_display *) arg, &button_display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ break;
+ }
+
+ case LINK_Check_2:{
+ int iface_num;
+
+ /* panel-utils should pass in the desired interface status is wanted for
+ * in "buttons" of the structure. We will set this to non-zero if the
+ * link is in fact up for the requested interface. --DaveM
+ */
+ if (copy_from_user
+ (&button_display, (struct lcd_display *) arg,
+ sizeof(button_display)))
+ return -EFAULT;
+ iface_num = button_display.buttons;
+#if defined(CONFIG_TULIP) && 0
+ if (iface_num >= 0 &&
+ iface_num < MAX_INTERFACES &&
+ linkcheck_callbacks[iface_num] != NULL) {
+ button_display.buttons =
+ linkcheck_callbacks[iface_num]
+ (linkcheck_cookies[iface_num]);
+ } else
+#endif
+ button_display.buttons = 0;
+
+ if (__copy_to_user
+ ((struct lcd_display *) arg, &button_display,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ break;
+ }
+
+// Erase the flash
+
+ case FLASH_Erase:{
+
+ int ctr = 0;
+
+ if ( !capable(CAP_SYS_ADMIN) ) return -EPERM;
+
+ pr_info(LCD "Erasing Flash\n");
+
+ // Chip Erase Sequence
+ WRITE_FLASH(kFlash_Addr1, kFlash_Data1);
+ WRITE_FLASH(kFlash_Addr2, kFlash_Data2);
+ WRITE_FLASH(kFlash_Addr1, kFlash_Erase3);
+ WRITE_FLASH(kFlash_Addr1, kFlash_Data1);
+ WRITE_FLASH(kFlash_Addr2, kFlash_Data2);
+ WRITE_FLASH(kFlash_Addr1, kFlash_Erase6);
+
+ while ((!dqpoll(0x00000000, 0xFF))
+ && (!timeout(0x00000000))) {
+ ctr++;
+ }
+
+ if (READ_FLASH(0x07FFF0) == 0xFF) {
+ pr_info(LCD "Erase Successful\n");
+ } else if (timeout) {
+ pr_info(LCD "Erase Timed Out\n");
+ }
+
+ break;
+ }
+
+// burn the flash
+
+ case FLASH_Burn:{
+
+ volatile unsigned long burn_addr;
+ unsigned long flags;
+ unsigned int i, index;
+ unsigned char *rom;
+
+
+ struct lcd_display display;
+
+ if ( !capable(CAP_SYS_ADMIN) ) return -EPERM;
+
+ if (copy_from_user
+ (&display, (struct lcd_display *) arg,
+ sizeof(struct lcd_display)))
+ return -EFAULT;
+ rom = (unsigned char *) kmalloc((128), GFP_ATOMIC);
+ if (rom == NULL) {
+ printk(KERN_ERR LCD "kmalloc() failed in %s\n",
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ pr_info(LCD "Starting Flash burn\n");
+ for (i = 0; i < FLASH_SIZE; i = i + 128) {
+
+ if (copy_from_user
+ (rom, display.RomImage + i, 128)) {
+ kfree(rom);
+ return -EFAULT;
+ }
+ burn_addr = kFlashBase + i;
+ spin_lock_irqsave(&lcd_lock, flags);
+ for (index = 0; index < (128); index++) {
+
+ WRITE_FLASH(kFlash_Addr1,
+ kFlash_Data1);
+ WRITE_FLASH(kFlash_Addr2,
+ kFlash_Data2);
+ WRITE_FLASH(kFlash_Addr1,
+ kFlash_Prog);
+ *((volatile unsigned char *)burn_addr) =
+ (volatile unsigned char) rom[index];
+
+ while ((!dqpoll (burn_addr,
+ (volatile unsigned char)
+ rom[index])) &&
+ (!timeout(burn_addr))) { }
+ burn_addr++;
+ }
+ spin_unlock_irqrestore(&lcd_lock, flags);
+ if (* ((volatile unsigned char *)
+ (burn_addr - 1)) ==
+ (volatile unsigned char)
+ rom[index - 1]) {
+ } else if (timeout) {
+ pr_info(LCD "Flash burn timed out\n");
+ }
+
+
+ }
+ kfree(rom);
+
+ pr_info(LCD "Flash successfully burned\n");
+
+ break;
+ }
+
+// read the flash all at once
+
+ case FLASH_Read:{
+
+ unsigned char *user_bytes;
+ volatile unsigned long read_addr;
+ unsigned int i;
+
+ user_bytes =
+ &(((struct lcd_display *) arg)->RomImage[0]);
+
+ if (!access_ok
+ (VERIFY_WRITE, user_bytes, FLASH_SIZE))
+ return -EFAULT;
+
+ pr_info(LCD "Reading Flash");
+ for (i = 0; i < FLASH_SIZE; i++) {
+ unsigned char tmp_byte;
+ read_addr = kFlashBase + i;
+ tmp_byte =
+ *((volatile unsigned char *)
+ read_addr);
+ if (__put_user(tmp_byte, &user_bytes[i]))
+ return -EFAULT;
+ }
+
+
+ break;
+ }
+
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+
+}
+
+static int lcd_open(struct inode *inode, struct file *file)
+{
+ if (!lcd_present)
+ return -ENXIO;
+ else
+ return 0;
+}
+
+/* Only RESET or NEXT counts as button pressed */
+
+static inline int button_pressed(void)
+{
+ unsigned long buttons = GPIRead;
+
+ if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B)
+ || (buttons == BUTTON_Reset_B))
+ return buttons;
+ return 0;
+}
+
+/* LED daemon sits on this and we wake him up once a key is pressed. */
+
+static int lcd_waiters = 0;
+
+static long lcd_read(struct inode *inode, struct file *file, char *buf,
+ unsigned long count)
+{
+ long buttons_now;
+
+ if (lcd_waiters > 0)
+ return -EINVAL;
+
+ lcd_waiters++;
+ while (((buttons_now = (long) button_pressed()) == 0) &&
+ !(signal_pending(current))) {
+ msleep_interruptible(2000);
+ }
+ lcd_waiters--;
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ return buttons_now;
+}
+
+/*
+ * The various file operations we support.
+ */
+
+static struct file_operations lcd_fops = {
+ .read = lcd_read,
+ .ioctl = lcd_ioctl,
+ .open = lcd_open,
+};
+
+static struct miscdevice lcd_dev = {
+ MISC_DYNAMIC_MINOR,
+ "lcd",
+ &lcd_fops
+};
+
+static int lcd_init(void)
+{
+ unsigned long data;
+
+ pr_info("%s\n", LCD_DRIVER);
+ misc_register(&lcd_dev);
+
+ /* Check region? Naaah! Just snarf it up. */
+/* request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/
+
+ udelay(150);
+ data = LCDReadData;
+ if ((data & 0x000000FF) == (0x00)) {
+ lcd_present = 0;
+ pr_info(LCD "LCD Not Present\n");
+ } else {
+ lcd_present = 1;
+ WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg);
+ WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg);
+ }
+
+ return 0;
+}
+
+static void __exit lcd_exit(void)
+{
+ misc_deregister(&lcd_dev);
+}
+
+//
+// Function: dqpoll
+//
+// Description: Polls the data lines to see if the flash is busy
+//
+// In: address, byte data
+//
+// Out: 0 = busy, 1 = write or erase complete
+//
+//
+
+static int dqpoll(volatile unsigned long address, volatile unsigned char data)
+{
+ volatile unsigned char dq7;
+
+ dq7 = data & 0x80;
+
+ return ((READ_FLASH(address) & 0x80) == dq7);
+}
+
+//
+// Function: timeout
+//
+// Description: Checks to see if erase or write has timed out
+// By polling dq5
+//
+// In: address
+//
+//
+// Out: 0 = not timed out, 1 = timed out
+
+static int timeout(volatile unsigned long address)
+{
+ return (READ_FLASH(address) & 0x20) == 0x20;
+}
+
+module_init(lcd_init);
+module_exit(lcd_exit);
+
+MODULE_AUTHOR("Andrew Bose");
+MODULE_LICENSE("GPL");