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