1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * /dev/lcd driver for Apple Network Servers. 41da177e4SLinus Torvalds */ 51da177e4SLinus Torvalds 61da177e4SLinus Torvalds #include <linux/types.h> 71da177e4SLinus Torvalds #include <linux/errno.h> 81da177e4SLinus Torvalds #include <linux/kernel.h> 91da177e4SLinus Torvalds #include <linux/miscdevice.h> 101da177e4SLinus Torvalds #include <linux/fcntl.h> 11120d200aSLuis Henriques #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/delay.h> 131da177e4SLinus Torvalds #include <linux/fs.h> 14*a486e512SChristophe Leroy #include <linux/of.h> 151da177e4SLinus Torvalds 167c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 171da177e4SLinus Torvalds #include <asm/sections.h> 181da177e4SLinus Torvalds #include <asm/io.h> 191da177e4SLinus Torvalds 2033d71d26SKumar Gala #include "ans-lcd.h" 2133d71d26SKumar Gala 221da177e4SLinus Torvalds #define ANSLCD_ADDR 0xf301c000 231da177e4SLinus Torvalds #define ANSLCD_CTRL_IX 0x00 241da177e4SLinus Torvalds #define ANSLCD_DATA_IX 0x10 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds static unsigned long anslcd_short_delay = 80; 271da177e4SLinus Torvalds static unsigned long anslcd_long_delay = 3280; 281da177e4SLinus Torvalds static volatile unsigned char __iomem *anslcd_ptr; 2995fdac73SThomas Gleixner static DEFINE_MUTEX(anslcd_mutex); 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #undef DEBUG 321da177e4SLinus Torvalds 33aacaf9bdSJon Loeliger static void 341da177e4SLinus Torvalds anslcd_write_byte_ctrl ( unsigned char c ) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds #ifdef DEBUG 371da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c); 381da177e4SLinus Torvalds #endif 391da177e4SLinus Torvalds out_8(anslcd_ptr + ANSLCD_CTRL_IX, c); 401da177e4SLinus Torvalds switch(c) { 411da177e4SLinus Torvalds case 1: 421da177e4SLinus Torvalds case 2: 431da177e4SLinus Torvalds case 3: 441da177e4SLinus Torvalds udelay(anslcd_long_delay); break; 451da177e4SLinus Torvalds default: udelay(anslcd_short_delay); 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds 49aacaf9bdSJon Loeliger static void 501da177e4SLinus Torvalds anslcd_write_byte_data ( unsigned char c ) 511da177e4SLinus Torvalds { 521da177e4SLinus Torvalds out_8(anslcd_ptr + ANSLCD_DATA_IX, c); 531da177e4SLinus Torvalds udelay(anslcd_short_delay); 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds 56aacaf9bdSJon Loeliger static ssize_t 571da177e4SLinus Torvalds anslcd_write( struct file * file, const char __user * buf, 581da177e4SLinus Torvalds size_t count, loff_t *ppos ) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds const char __user *p = buf; 611da177e4SLinus Torvalds int i; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds #ifdef DEBUG 641da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: write\n"); 651da177e4SLinus Torvalds #endif 661da177e4SLinus Torvalds 6796d4f267SLinus Torvalds if (!access_ok(buf, count)) 681da177e4SLinus Torvalds return -EFAULT; 6995fdac73SThomas Gleixner 7095fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 711da177e4SLinus Torvalds for ( i = *ppos; count > 0; ++i, ++p, --count ) 721da177e4SLinus Torvalds { 731da177e4SLinus Torvalds char c; 741da177e4SLinus Torvalds __get_user(c, p); 751da177e4SLinus Torvalds anslcd_write_byte_data( c ); 761da177e4SLinus Torvalds } 7795fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 781da177e4SLinus Torvalds *ppos = i; 791da177e4SLinus Torvalds return p - buf; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 8295fdac73SThomas Gleixner static long 8395fdac73SThomas Gleixner anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds char ch, __user *temp; 8695fdac73SThomas Gleixner long ret = 0; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds #ifdef DEBUG 891da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); 901da177e4SLinus Torvalds #endif 911da177e4SLinus Torvalds 9295fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 9395fdac73SThomas Gleixner 941da177e4SLinus Torvalds switch ( cmd ) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds case ANSLCD_CLEAR: 971da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x38 ); 981da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x0f ); 991da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x06 ); 1001da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x01 ); 1011da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x02 ); 10295fdac73SThomas Gleixner break; 1031da177e4SLinus Torvalds case ANSLCD_SENDCTRL: 1041da177e4SLinus Torvalds temp = (char __user *) arg; 1051da177e4SLinus Torvalds __get_user(ch, temp); 1061da177e4SLinus Torvalds for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */ 1071da177e4SLinus Torvalds anslcd_write_byte_ctrl ( ch ); 1081da177e4SLinus Torvalds __get_user(ch, temp); 1091da177e4SLinus Torvalds } 11095fdac73SThomas Gleixner break; 1111da177e4SLinus Torvalds case ANSLCD_SETSHORTDELAY: 1121da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 11395fdac73SThomas Gleixner ret =-EACCES; 11495fdac73SThomas Gleixner else 1151da177e4SLinus Torvalds anslcd_short_delay=arg; 11695fdac73SThomas Gleixner break; 1171da177e4SLinus Torvalds case ANSLCD_SETLONGDELAY: 1181da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 11995fdac73SThomas Gleixner ret = -EACCES; 12095fdac73SThomas Gleixner else 1211da177e4SLinus Torvalds anslcd_long_delay=arg; 12295fdac73SThomas Gleixner break; 1231da177e4SLinus Torvalds default: 12495fdac73SThomas Gleixner ret = -EINVAL; 1251da177e4SLinus Torvalds } 12695fdac73SThomas Gleixner 12795fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 12895fdac73SThomas Gleixner return ret; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 131aacaf9bdSJon Loeliger static int 1321da177e4SLinus Torvalds anslcd_open( struct inode * inode, struct file * file ) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds return 0; 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds 137fa027c2aSArjan van de Ven const struct file_operations anslcd_fops = { 1381da177e4SLinus Torvalds .write = anslcd_write, 13995fdac73SThomas Gleixner .unlocked_ioctl = anslcd_ioctl, 1401da177e4SLinus Torvalds .open = anslcd_open, 1416038f373SArnd Bergmann .llseek = default_llseek, 1421da177e4SLinus Torvalds }; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds static struct miscdevice anslcd_dev = { 1456ce6ae7cSZhenzhong Duan LCD_MINOR, 1461da177e4SLinus Torvalds "anslcd", 1471da177e4SLinus Torvalds &anslcd_fops 1481da177e4SLinus Torvalds }; 1491da177e4SLinus Torvalds 1503775026aSRasmus Villemoes static const char anslcd_logo[] __initconst = 1513775026aSRasmus Villemoes "********************" /* Line #1 */ 1521da177e4SLinus Torvalds "* LINUX! *" /* Line #3 */ 1531da177e4SLinus Torvalds "* Welcome to *" /* Line #2 */ 1541da177e4SLinus Torvalds "********************"; /* Line #4 */ 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds static int __init 1571da177e4SLinus Torvalds anslcd_init(void) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds int a; 1601da177e4SLinus Torvalds int retval; 1611da177e4SLinus Torvalds struct device_node* node; 1621da177e4SLinus Torvalds 16330686ba6SStephen Rothwell node = of_find_node_by_name(NULL, "lcd"); 164f1e0addcSRob Herring if (!node || !of_node_name_eq(node->parent, "gc")) { 16530686ba6SStephen Rothwell of_node_put(node); 1661da177e4SLinus Torvalds return -ENODEV; 16730686ba6SStephen Rothwell } 16830686ba6SStephen Rothwell of_node_put(node); 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds retval = misc_register(&anslcd_dev); 1731da177e4SLinus Torvalds if(retval < 0){ 1741da177e4SLinus Torvalds printk(KERN_INFO "LCD: misc_register failed\n"); 1751da177e4SLinus Torvalds iounmap(anslcd_ptr); 1761da177e4SLinus Torvalds return retval; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds #ifdef DEBUG 1801da177e4SLinus Torvalds printk(KERN_DEBUG "LCD: init\n"); 1811da177e4SLinus Torvalds #endif 1821da177e4SLinus Torvalds 18395fdac73SThomas Gleixner mutex_lock(&anslcd_mutex); 1841da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x38 ); 1851da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x0c ); 1861da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x06 ); 1871da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x01 ); 1881da177e4SLinus Torvalds anslcd_write_byte_ctrl ( 0x02 ); 1891da177e4SLinus Torvalds for(a=0;a<80;a++) { 1901da177e4SLinus Torvalds anslcd_write_byte_data(anslcd_logo[a]); 1911da177e4SLinus Torvalds } 19295fdac73SThomas Gleixner mutex_unlock(&anslcd_mutex); 1931da177e4SLinus Torvalds return 0; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds static void __exit 1971da177e4SLinus Torvalds anslcd_exit(void) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds misc_deregister(&anslcd_dev); 2001da177e4SLinus Torvalds iounmap(anslcd_ptr); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds module_init(anslcd_init); 2041da177e4SLinus Torvalds module_exit(anslcd_exit); 20547d703e1SLarry Finger MODULE_LICENSE("GPL v2"); 206