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