11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * /dev/lcd driver for Apple Network Servers. 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds #include <linux/types.h> 61da177e4SLinus Torvalds #include <linux/errno.h> 71da177e4SLinus Torvalds #include <linux/kernel.h> 81da177e4SLinus Torvalds #include <linux/miscdevice.h> 91da177e4SLinus Torvalds #include <linux/fcntl.h> 101da177e4SLinus Torvalds #include <linux/init.h> 111da177e4SLinus Torvalds #include <linux/delay.h> 121da177e4SLinus Torvalds #include <linux/fs.h> 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <asm/uaccess.h> 151da177e4SLinus Torvalds #include <asm/sections.h> 161da177e4SLinus Torvalds #include <asm/prom.h> 171da177e4SLinus Torvalds #include <asm/io.h> 181da177e4SLinus Torvalds 1933d71d26SKumar Gala #include "ans-lcd.h" 2033d71d26SKumar Gala 211da177e4SLinus Torvalds #define ANSLCD_ADDR 0xf301c000 221da177e4SLinus Torvalds #define ANSLCD_CTRL_IX 0x00 231da177e4SLinus Torvalds #define ANSLCD_DATA_IX 0x10 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds static unsigned long anslcd_short_delay = 80; 261da177e4SLinus Torvalds static unsigned long anslcd_long_delay = 3280; 271da177e4SLinus Torvalds static volatile unsigned char __iomem *anslcd_ptr; 28*95fdac73SThomas Gleixner static DEFINE_MUTEX(anslcd_mutex); 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds #undef DEBUG 311da177e4SLinus Torvalds 32aacaf9bdSJon Loeliger static void 331da177e4SLinus Torvalds anslcd_write_byte_ctrl ( unsigned char c ) 341da177e4SLinus Torvalds { 351da177e4SLinus Torvalds #ifdef DEBUG 361da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c); 371da177e4SLinus Torvalds #endif 381da177e4SLinus Torvalds out_8(anslcd_ptr + ANSLCD_CTRL_IX, c); 391da177e4SLinus Torvalds switch(c) { 401da177e4SLinus Torvalds case 1: 411da177e4SLinus Torvalds case 2: 421da177e4SLinus Torvalds case 3: 431da177e4SLinus Torvalds udelay(anslcd_long_delay); break; 441da177e4SLinus Torvalds default: udelay(anslcd_short_delay); 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds 48aacaf9bdSJon Loeliger static void 491da177e4SLinus Torvalds anslcd_write_byte_data ( unsigned char c ) 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds out_8(anslcd_ptr + ANSLCD_DATA_IX, c); 521da177e4SLinus Torvalds udelay(anslcd_short_delay); 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds 55aacaf9bdSJon Loeliger static ssize_t 561da177e4SLinus Torvalds anslcd_write( struct file * file, const char __user * buf, 571da177e4SLinus Torvalds size_t count, loff_t *ppos ) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds const char __user *p = buf; 601da177e4SLinus Torvalds int i; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds #ifdef DEBUG 631da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: write\n"); 641da177e4SLinus Torvalds #endif 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, buf, count)) 671da177e4SLinus Torvalds return -EFAULT; 68*95fdac73SThomas Gleixner 69*95fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 701da177e4SLinus Torvalds for ( i = *ppos; count > 0; ++i, ++p, --count ) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds char c; 731da177e4SLinus Torvalds __get_user(c, p); 741da177e4SLinus Torvalds anslcd_write_byte_data( c ); 751da177e4SLinus Torvalds } 76*95fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 771da177e4SLinus Torvalds *ppos = i; 781da177e4SLinus Torvalds return p - buf; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 81*95fdac73SThomas Gleixner static long 82*95fdac73SThomas Gleixner anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds char ch, __user *temp; 85*95fdac73SThomas Gleixner long ret = 0; 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds #ifdef DEBUG 881da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); 891da177e4SLinus Torvalds #endif 901da177e4SLinus Torvalds 91*95fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 92*95fdac73SThomas Gleixner 931da177e4SLinus Torvalds switch ( cmd ) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds case ANSLCD_CLEAR: 961da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x38 ); 971da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x0f ); 981da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x06 ); 991da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x01 ); 1001da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x02 ); 101*95fdac73SThomas Gleixner break; 1021da177e4SLinus Torvalds case ANSLCD_SENDCTRL: 1031da177e4SLinus Torvalds temp = (char __user *) arg; 1041da177e4SLinus Torvalds __get_user(ch, temp); 1051da177e4SLinus Torvalds for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */ 1061da177e4SLinus Torvalds anslcd_write_byte_ctrl ( ch ); 1071da177e4SLinus Torvalds __get_user(ch, temp); 1081da177e4SLinus Torvalds } 109*95fdac73SThomas Gleixner break; 1101da177e4SLinus Torvalds case ANSLCD_SETSHORTDELAY: 1111da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 112*95fdac73SThomas Gleixner ret =-EACCES; 113*95fdac73SThomas Gleixner else 1141da177e4SLinus Torvalds anslcd_short_delay=arg; 115*95fdac73SThomas Gleixner break; 1161da177e4SLinus Torvalds case ANSLCD_SETLONGDELAY: 1171da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 118*95fdac73SThomas Gleixner ret = -EACCES; 119*95fdac73SThomas Gleixner else 1201da177e4SLinus Torvalds anslcd_long_delay=arg; 121*95fdac73SThomas Gleixner break; 1221da177e4SLinus Torvalds default: 123*95fdac73SThomas Gleixner ret = -EINVAL; 1241da177e4SLinus Torvalds } 125*95fdac73SThomas Gleixner 126*95fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 127*95fdac73SThomas Gleixner return ret; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 130aacaf9bdSJon Loeliger static int 1311da177e4SLinus Torvalds anslcd_open( struct inode * inode, struct file * file ) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds return 0; 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 136fa027c2aSArjan van de Ven const struct file_operations anslcd_fops = { 1371da177e4SLinus Torvalds .write = anslcd_write, 138*95fdac73SThomas Gleixner .unlocked_ioctl = anslcd_ioctl, 1391da177e4SLinus Torvalds .open = anslcd_open, 1401da177e4SLinus Torvalds }; 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds static struct miscdevice anslcd_dev = { 1431da177e4SLinus Torvalds ANSLCD_MINOR, 1441da177e4SLinus Torvalds "anslcd", 1451da177e4SLinus Torvalds &anslcd_fops 1461da177e4SLinus Torvalds }; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds const char anslcd_logo[] = "********************" /* Line #1 */ 1491da177e4SLinus Torvalds "* LINUX! *" /* Line #3 */ 1501da177e4SLinus Torvalds "* Welcome to *" /* Line #2 */ 1511da177e4SLinus Torvalds "********************"; /* Line #4 */ 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds static int __init 1541da177e4SLinus Torvalds anslcd_init(void) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds int a; 1571da177e4SLinus Torvalds int retval; 1581da177e4SLinus Torvalds struct device_node* node; 1591da177e4SLinus Torvalds 16030686ba6SStephen Rothwell node = of_find_node_by_name(NULL, "lcd"); 16130686ba6SStephen Rothwell if (!node || !node->parent || strcmp(node->parent->name, "gc")) { 16230686ba6SStephen Rothwell of_node_put(node); 1631da177e4SLinus Torvalds return -ENODEV; 16430686ba6SStephen Rothwell } 16530686ba6SStephen Rothwell of_node_put(node); 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds retval = misc_register(&anslcd_dev); 1701da177e4SLinus Torvalds if(retval < 0){ 1711da177e4SLinus Torvalds printk(KERN_INFO "LCD: misc_register failed\n"); 1721da177e4SLinus Torvalds iounmap(anslcd_ptr); 1731da177e4SLinus Torvalds return retval; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds #ifdef DEBUG 1771da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: init\n"); 1781da177e4SLinus Torvalds #endif 1791da177e4SLinus Torvalds 180*95fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 1811da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x38 ); 1821da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x0c ); 1831da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x06 ); 1841da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x01 ); 1851da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x02 ); 1861da177e4SLinus Torvalds for(a=0;a<80;a++) { 1871da177e4SLinus Torvalds anslcd_write_byte_data(anslcd_logo[a]); 1881da177e4SLinus Torvalds } 189*95fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 1901da177e4SLinus Torvalds return 0; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static void __exit 1941da177e4SLinus Torvalds anslcd_exit(void) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds misc_deregister(&anslcd_dev); 1971da177e4SLinus Torvalds iounmap(anslcd_ptr); 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds module_init(anslcd_init); 2011da177e4SLinus Torvalds module_exit(anslcd_exit); 202