18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * /dev/lcd driver for Apple Network Servers. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 108c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <asm/sections.h> 178c2ecf20Sopenharmony_ci#include <asm/prom.h> 188c2ecf20Sopenharmony_ci#include <asm/io.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "ans-lcd.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define ANSLCD_ADDR 0xf301c000 238c2ecf20Sopenharmony_ci#define ANSLCD_CTRL_IX 0x00 248c2ecf20Sopenharmony_ci#define ANSLCD_DATA_IX 0x10 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic unsigned long anslcd_short_delay = 80; 278c2ecf20Sopenharmony_cistatic unsigned long anslcd_long_delay = 3280; 288c2ecf20Sopenharmony_cistatic volatile unsigned char __iomem *anslcd_ptr; 298c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(anslcd_mutex); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#undef DEBUG 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void 348c2ecf20Sopenharmony_cianslcd_write_byte_ctrl ( unsigned char c ) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci#ifdef DEBUG 378c2ecf20Sopenharmony_ci printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c); 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci out_8(anslcd_ptr + ANSLCD_CTRL_IX, c); 408c2ecf20Sopenharmony_ci switch(c) { 418c2ecf20Sopenharmony_ci case 1: 428c2ecf20Sopenharmony_ci case 2: 438c2ecf20Sopenharmony_ci case 3: 448c2ecf20Sopenharmony_ci udelay(anslcd_long_delay); break; 458c2ecf20Sopenharmony_ci default: udelay(anslcd_short_delay); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void 508c2ecf20Sopenharmony_cianslcd_write_byte_data ( unsigned char c ) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci out_8(anslcd_ptr + ANSLCD_DATA_IX, c); 538c2ecf20Sopenharmony_ci udelay(anslcd_short_delay); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic ssize_t 578c2ecf20Sopenharmony_cianslcd_write( struct file * file, const char __user * buf, 588c2ecf20Sopenharmony_ci size_t count, loff_t *ppos ) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci const char __user *p = buf; 618c2ecf20Sopenharmony_ci int i; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#ifdef DEBUG 648c2ecf20Sopenharmony_ci printk(KERN_DEBUG "LCD: write\n"); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 688c2ecf20Sopenharmony_ci return -EFAULT; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci mutex_lock(&anslcd_mutex); 718c2ecf20Sopenharmony_ci for ( i = *ppos; count > 0; ++i, ++p, --count ) 728c2ecf20Sopenharmony_ci { 738c2ecf20Sopenharmony_ci char c; 748c2ecf20Sopenharmony_ci __get_user(c, p); 758c2ecf20Sopenharmony_ci anslcd_write_byte_data( c ); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci mutex_unlock(&anslcd_mutex); 788c2ecf20Sopenharmony_ci *ppos = i; 798c2ecf20Sopenharmony_ci return p - buf; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic long 838c2ecf20Sopenharmony_cianslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci char ch, __user *temp; 868c2ecf20Sopenharmony_ci long ret = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#ifdef DEBUG 898c2ecf20Sopenharmony_ci printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); 908c2ecf20Sopenharmony_ci#endif 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mutex_lock(&anslcd_mutex); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci switch ( cmd ) 958c2ecf20Sopenharmony_ci { 968c2ecf20Sopenharmony_ci case ANSLCD_CLEAR: 978c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x38 ); 988c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x0f ); 998c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x06 ); 1008c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x01 ); 1018c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x02 ); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case ANSLCD_SENDCTRL: 1048c2ecf20Sopenharmony_ci temp = (char __user *) arg; 1058c2ecf20Sopenharmony_ci __get_user(ch, temp); 1068c2ecf20Sopenharmony_ci for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */ 1078c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( ch ); 1088c2ecf20Sopenharmony_ci __get_user(ch, temp); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case ANSLCD_SETSHORTDELAY: 1128c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 1138c2ecf20Sopenharmony_ci ret =-EACCES; 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci anslcd_short_delay=arg; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case ANSLCD_SETLONGDELAY: 1188c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 1198c2ecf20Sopenharmony_ci ret = -EACCES; 1208c2ecf20Sopenharmony_ci else 1218c2ecf20Sopenharmony_ci anslcd_long_delay=arg; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci ret = -EINVAL; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mutex_unlock(&anslcd_mutex); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int 1328c2ecf20Sopenharmony_cianslcd_open( struct inode * inode, struct file * file ) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciconst struct file_operations anslcd_fops = { 1388c2ecf20Sopenharmony_ci .write = anslcd_write, 1398c2ecf20Sopenharmony_ci .unlocked_ioctl = anslcd_ioctl, 1408c2ecf20Sopenharmony_ci .open = anslcd_open, 1418c2ecf20Sopenharmony_ci .llseek = default_llseek, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct miscdevice anslcd_dev = { 1458c2ecf20Sopenharmony_ci LCD_MINOR, 1468c2ecf20Sopenharmony_ci "anslcd", 1478c2ecf20Sopenharmony_ci &anslcd_fops 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const char anslcd_logo[] __initconst = 1518c2ecf20Sopenharmony_ci "********************" /* Line #1 */ 1528c2ecf20Sopenharmony_ci "* LINUX! *" /* Line #3 */ 1538c2ecf20Sopenharmony_ci "* Welcome to *" /* Line #2 */ 1548c2ecf20Sopenharmony_ci "********************"; /* Line #4 */ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int __init 1578c2ecf20Sopenharmony_cianslcd_init(void) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int a; 1608c2ecf20Sopenharmony_ci int retval; 1618c2ecf20Sopenharmony_ci struct device_node* node; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci node = of_find_node_by_name(NULL, "lcd"); 1648c2ecf20Sopenharmony_ci if (!node || !of_node_name_eq(node->parent, "gc")) { 1658c2ecf20Sopenharmony_ci of_node_put(node); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci of_node_put(node); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci retval = misc_register(&anslcd_dev); 1738c2ecf20Sopenharmony_ci if(retval < 0){ 1748c2ecf20Sopenharmony_ci printk(KERN_INFO "LCD: misc_register failed\n"); 1758c2ecf20Sopenharmony_ci iounmap(anslcd_ptr); 1768c2ecf20Sopenharmony_ci return retval; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#ifdef DEBUG 1808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "LCD: init\n"); 1818c2ecf20Sopenharmony_ci#endif 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mutex_lock(&anslcd_mutex); 1848c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x38 ); 1858c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x0c ); 1868c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x06 ); 1878c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x01 ); 1888c2ecf20Sopenharmony_ci anslcd_write_byte_ctrl ( 0x02 ); 1898c2ecf20Sopenharmony_ci for(a=0;a<80;a++) { 1908c2ecf20Sopenharmony_ci anslcd_write_byte_data(anslcd_logo[a]); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci mutex_unlock(&anslcd_mutex); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void __exit 1978c2ecf20Sopenharmony_cianslcd_exit(void) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci misc_deregister(&anslcd_dev); 2008c2ecf20Sopenharmony_ci iounmap(anslcd_ptr); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cimodule_init(anslcd_init); 2048c2ecf20Sopenharmony_cimodule_exit(anslcd_exit); 2058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 206