Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * /dev/lcd driver for Apple Network Servers.
0004  */
0005 
0006 #include <linux/types.h>
0007 #include <linux/errno.h>
0008 #include <linux/kernel.h>
0009 #include <linux/miscdevice.h>
0010 #include <linux/fcntl.h>
0011 #include <linux/module.h>
0012 #include <linux/delay.h>
0013 #include <linux/fs.h>
0014 #include <linux/of.h>
0015 
0016 #include <linux/uaccess.h>
0017 #include <asm/sections.h>
0018 #include <asm/io.h>
0019 
0020 #include "ans-lcd.h"
0021 
0022 #define ANSLCD_ADDR     0xf301c000
0023 #define ANSLCD_CTRL_IX 0x00
0024 #define ANSLCD_DATA_IX 0x10
0025 
0026 static unsigned long anslcd_short_delay = 80;
0027 static unsigned long anslcd_long_delay = 3280;
0028 static volatile unsigned char __iomem *anslcd_ptr;
0029 static DEFINE_MUTEX(anslcd_mutex);
0030 
0031 #undef DEBUG
0032 
0033 static void
0034 anslcd_write_byte_ctrl ( unsigned char c )
0035 {
0036 #ifdef DEBUG
0037     printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
0038 #endif
0039     out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
0040     switch(c) {
0041         case 1:
0042         case 2:
0043         case 3:
0044             udelay(anslcd_long_delay); break;
0045         default: udelay(anslcd_short_delay);
0046     }
0047 }
0048 
0049 static void
0050 anslcd_write_byte_data ( unsigned char c )
0051 {
0052     out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
0053     udelay(anslcd_short_delay);
0054 }
0055 
0056 static ssize_t
0057 anslcd_write( struct file * file, const char __user * buf, 
0058                 size_t count, loff_t *ppos )
0059 {
0060     const char __user *p = buf;
0061     int i;
0062 
0063 #ifdef DEBUG
0064     printk(KERN_DEBUG "LCD: write\n");
0065 #endif
0066 
0067     if (!access_ok(buf, count))
0068         return -EFAULT;
0069 
0070     mutex_lock(&anslcd_mutex);
0071     for ( i = *ppos; count > 0; ++i, ++p, --count ) 
0072     {
0073         char c;
0074         __get_user(c, p);
0075         anslcd_write_byte_data( c );
0076     }
0077     mutex_unlock(&anslcd_mutex);
0078     *ppos = i;
0079     return p - buf;
0080 }
0081 
0082 static long
0083 anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0084 {
0085     char ch, __user *temp;
0086     long ret = 0;
0087 
0088 #ifdef DEBUG
0089     printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
0090 #endif
0091 
0092     mutex_lock(&anslcd_mutex);
0093 
0094     switch ( cmd )
0095     {
0096     case ANSLCD_CLEAR:
0097         anslcd_write_byte_ctrl ( 0x38 );
0098         anslcd_write_byte_ctrl ( 0x0f );
0099         anslcd_write_byte_ctrl ( 0x06 );
0100         anslcd_write_byte_ctrl ( 0x01 );
0101         anslcd_write_byte_ctrl ( 0x02 );
0102         break;
0103     case ANSLCD_SENDCTRL:
0104         temp = (char __user *) arg;
0105         __get_user(ch, temp);
0106         for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
0107             anslcd_write_byte_ctrl ( ch );
0108             __get_user(ch, temp);
0109         }
0110         break;
0111     case ANSLCD_SETSHORTDELAY:
0112         if (!capable(CAP_SYS_ADMIN))
0113             ret =-EACCES;
0114         else
0115             anslcd_short_delay=arg;
0116         break;
0117     case ANSLCD_SETLONGDELAY:
0118         if (!capable(CAP_SYS_ADMIN))
0119             ret = -EACCES;
0120         else
0121             anslcd_long_delay=arg;
0122         break;
0123     default:
0124         ret = -EINVAL;
0125     }
0126 
0127     mutex_unlock(&anslcd_mutex);
0128     return ret;
0129 }
0130 
0131 static int
0132 anslcd_open( struct inode * inode, struct file * file )
0133 {
0134     return 0;
0135 }
0136 
0137 const struct file_operations anslcd_fops = {
0138     .write      = anslcd_write,
0139     .unlocked_ioctl = anslcd_ioctl,
0140     .open       = anslcd_open,
0141     .llseek     = default_llseek,
0142 };
0143 
0144 static struct miscdevice anslcd_dev = {
0145     LCD_MINOR,
0146     "anslcd",
0147     &anslcd_fops
0148 };
0149 
0150 static const char anslcd_logo[] __initconst =
0151                 "********************"  /* Line #1 */
0152                 "*      LINUX!      *"  /* Line #3 */
0153                 "*    Welcome to    *"  /* Line #2 */
0154                 "********************"; /* Line #4 */
0155 
0156 static int __init
0157 anslcd_init(void)
0158 {
0159     int a;
0160     int retval;
0161     struct device_node* node;
0162 
0163     node = of_find_node_by_name(NULL, "lcd");
0164     if (!node || !of_node_name_eq(node->parent, "gc")) {
0165         of_node_put(node);
0166         return -ENODEV;
0167     }
0168     of_node_put(node);
0169 
0170     anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
0171     
0172     retval = misc_register(&anslcd_dev);
0173     if(retval < 0){
0174         printk(KERN_INFO "LCD: misc_register failed\n");
0175         iounmap(anslcd_ptr);
0176         return retval;
0177     }
0178 
0179 #ifdef DEBUG
0180     printk(KERN_DEBUG "LCD: init\n");
0181 #endif
0182 
0183     mutex_lock(&anslcd_mutex);
0184     anslcd_write_byte_ctrl ( 0x38 );
0185     anslcd_write_byte_ctrl ( 0x0c );
0186     anslcd_write_byte_ctrl ( 0x06 );
0187     anslcd_write_byte_ctrl ( 0x01 );
0188     anslcd_write_byte_ctrl ( 0x02 );
0189     for(a=0;a<80;a++) {
0190         anslcd_write_byte_data(anslcd_logo[a]);
0191     }
0192     mutex_unlock(&anslcd_mutex);
0193     return 0;
0194 }
0195 
0196 static void __exit
0197 anslcd_exit(void)
0198 {
0199     misc_deregister(&anslcd_dev);
0200     iounmap(anslcd_ptr);
0201 }
0202 
0203 module_init(anslcd_init);
0204 module_exit(anslcd_exit);
0205 MODULE_LICENSE("GPL v2");