1/* 2 * /dev/lcd driver for Apple Network Servers. 3 */ 4 5#include <linux/types.h> 6#include <linux/errno.h> 7#include <linux/kernel.h> 8#include <linux/miscdevice.h> 9#include <linux/fcntl.h> 10#include <linux/init.h> 11#include <linux/delay.h> 12#include <linux/fs.h> 13 14#include <asm/uaccess.h> 15#include <asm/sections.h> 16#include <asm/prom.h> 17#include <asm/ans-lcd.h> 18#include <asm/io.h> 19 20#define ANSLCD_ADDR 0xf301c000 21#define ANSLCD_CTRL_IX 0x00 22#define ANSLCD_DATA_IX 0x10 23 24static unsigned long anslcd_short_delay = 80; 25static unsigned long anslcd_long_delay = 3280; 26static volatile unsigned char __iomem *anslcd_ptr; 27 28#undef DEBUG 29 30static void 31anslcd_write_byte_ctrl ( unsigned char c ) 32{ 33#ifdef DEBUG 34 printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c); 35#endif 36 out_8(anslcd_ptr + ANSLCD_CTRL_IX, c); 37 switch(c) { 38 case 1: 39 case 2: 40 case 3: 41 udelay(anslcd_long_delay); break; 42 default: udelay(anslcd_short_delay); 43 } 44} 45 46static void 47anslcd_write_byte_data ( unsigned char c ) 48{ 49 out_8(anslcd_ptr + ANSLCD_DATA_IX, c); 50 udelay(anslcd_short_delay); 51} 52 53static ssize_t 54anslcd_write( struct file * file, const char __user * buf, 55 size_t count, loff_t *ppos ) 56{ 57 const char __user *p = buf; 58 int i; 59 60#ifdef DEBUG 61 printk(KERN_DEBUG "LCD: write\n"); 62#endif 63 64 if (!access_ok(VERIFY_READ, buf, count)) 65 return -EFAULT; 66 for ( i = *ppos; count > 0; ++i, ++p, --count ) 67 { 68 char c; 69 __get_user(c, p); 70 anslcd_write_byte_data( c ); 71 } 72 *ppos = i; 73 return p - buf; 74} 75 76static int 77anslcd_ioctl( struct inode * inode, struct file * file, 78 unsigned int cmd, unsigned long arg ) 79{ 80 char ch, __user *temp; 81 82#ifdef DEBUG 83 printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); 84#endif 85 86 switch ( cmd ) 87 { 88 case ANSLCD_CLEAR: 89 anslcd_write_byte_ctrl ( 0x38 ); 90 anslcd_write_byte_ctrl ( 0x0f ); 91 anslcd_write_byte_ctrl ( 0x06 ); 92 anslcd_write_byte_ctrl ( 0x01 ); 93 anslcd_write_byte_ctrl ( 0x02 ); 94 return 0; 95 case ANSLCD_SENDCTRL: 96 temp = (char __user *) arg; 97 __get_user(ch, temp); 98 for (; ch; temp++) { 99 anslcd_write_byte_ctrl ( ch ); 100 __get_user(ch, temp); 101 } 102 return 0; 103 case ANSLCD_SETSHORTDELAY: 104 if (!capable(CAP_SYS_ADMIN)) 105 return -EACCES; 106 anslcd_short_delay=arg; 107 return 0; 108 case ANSLCD_SETLONGDELAY: 109 if (!capable(CAP_SYS_ADMIN)) 110 return -EACCES; 111 anslcd_long_delay=arg; 112 return 0; 113 default: 114 return -EINVAL; 115 } 116} 117 118static int 119anslcd_open( struct inode * inode, struct file * file ) 120{ 121 return 0; 122} 123 124const struct file_operations anslcd_fops = { 125 .write = anslcd_write, 126 .ioctl = anslcd_ioctl, 127 .open = anslcd_open, 128}; 129 130static struct miscdevice anslcd_dev = { 131 ANSLCD_MINOR, 132 "anslcd", 133 &anslcd_fops 134}; 135 136const char anslcd_logo[] = "********************" /* Line #1 */ 137 "* LINUX! *" /* Line #3 */ 138 "* Welcome to *" /* Line #2 */ 139 "********************"; /* Line #4 */ 140 141static int __init 142anslcd_init(void) 143{ 144 int a; 145 int retval; 146 struct device_node* node; 147 148 node = of_find_node_by_name(NULL, "lcd"); 149 if (!node || !node->parent || strcmp(node->parent->name, "gc")) { 150 of_node_put(node); 151 return -ENODEV; 152 } 153 of_node_put(node); 154 155 anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); 156 157 retval = misc_register(&anslcd_dev); 158 if(retval < 0){ 159 printk(KERN_INFO "LCD: misc_register failed\n"); 160 iounmap(anslcd_ptr); 161 return retval; 162 } 163 164#ifdef DEBUG 165 printk(KERN_DEBUG "LCD: init\n"); 166#endif 167 168 anslcd_write_byte_ctrl ( 0x38 ); 169 anslcd_write_byte_ctrl ( 0x0c ); 170 anslcd_write_byte_ctrl ( 0x06 ); 171 anslcd_write_byte_ctrl ( 0x01 ); 172 anslcd_write_byte_ctrl ( 0x02 ); 173 for(a=0;a<80;a++) { 174 anslcd_write_byte_data(anslcd_logo[a]); 175 } 176 return 0; 177} 178 179static void __exit 180anslcd_exit(void) 181{ 182 misc_deregister(&anslcd_dev); 183 iounmap(anslcd_ptr); 184} 185 186module_init(anslcd_init); 187module_exit(anslcd_exit); 188