206 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * /dev/lcd driver for Apple Network Servers.4 */5 6#include <linux/types.h>7#include <linux/errno.h>8#include <linux/kernel.h>9#include <linux/miscdevice.h>10#include <linux/fcntl.h>11#include <linux/module.h>12#include <linux/delay.h>13#include <linux/fs.h>14#include <linux/of.h>15 16#include <linux/uaccess.h>17#include <asm/sections.h>18#include <asm/io.h>19 20#include "ans-lcd.h"21 22#define ANSLCD_ADDR 0xf301c00023#define ANSLCD_CTRL_IX 0x0024#define ANSLCD_DATA_IX 0x1025 26static unsigned long anslcd_short_delay = 80;27static unsigned long anslcd_long_delay = 3280;28static volatile unsigned char __iomem *anslcd_ptr;29static DEFINE_MUTEX(anslcd_mutex);30 31#undef DEBUG32 33static void34anslcd_write_byte_ctrl ( unsigned char c )35{36#ifdef DEBUG37 printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);38#endif39 out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);40 switch(c) {41 case 1:42 case 2:43 case 3:44 udelay(anslcd_long_delay); break;45 default: udelay(anslcd_short_delay);46 }47}48 49static void50anslcd_write_byte_data ( unsigned char c )51{52 out_8(anslcd_ptr + ANSLCD_DATA_IX, c);53 udelay(anslcd_short_delay);54}55 56static ssize_t57anslcd_write( struct file * file, const char __user * buf, 58 size_t count, loff_t *ppos )59{60 const char __user *p = buf;61 int i;62 63#ifdef DEBUG64 printk(KERN_DEBUG "LCD: write\n");65#endif66 67 if (!access_ok(buf, count))68 return -EFAULT;69 70 mutex_lock(&anslcd_mutex);71 for ( i = *ppos; count > 0; ++i, ++p, --count ) 72 {73 char c;74 __get_user(c, p);75 anslcd_write_byte_data( c );76 }77 mutex_unlock(&anslcd_mutex);78 *ppos = i;79 return p - buf;80}81 82static long83anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)84{85 char ch, __user *temp;86 long ret = 0;87 88#ifdef DEBUG89 printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);90#endif91 92 mutex_lock(&anslcd_mutex);93 94 switch ( cmd )95 {96 case ANSLCD_CLEAR:97 anslcd_write_byte_ctrl ( 0x38 );98 anslcd_write_byte_ctrl ( 0x0f );99 anslcd_write_byte_ctrl ( 0x06 );100 anslcd_write_byte_ctrl ( 0x01 );101 anslcd_write_byte_ctrl ( 0x02 );102 break;103 case ANSLCD_SENDCTRL:104 temp = (char __user *) arg;105 __get_user(ch, temp);106 for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */107 anslcd_write_byte_ctrl ( ch );108 __get_user(ch, temp);109 }110 break;111 case ANSLCD_SETSHORTDELAY:112 if (!capable(CAP_SYS_ADMIN))113 ret =-EACCES;114 else115 anslcd_short_delay=arg;116 break;117 case ANSLCD_SETLONGDELAY:118 if (!capable(CAP_SYS_ADMIN))119 ret = -EACCES;120 else121 anslcd_long_delay=arg;122 break;123 default:124 ret = -EINVAL;125 }126 127 mutex_unlock(&anslcd_mutex);128 return ret;129}130 131static int132anslcd_open( struct inode * inode, struct file * file )133{134 return 0;135}136 137const struct file_operations anslcd_fops = {138 .write = anslcd_write,139 .unlocked_ioctl = anslcd_ioctl,140 .open = anslcd_open,141 .llseek = default_llseek,142};143 144static struct miscdevice anslcd_dev = {145 LCD_MINOR,146 "anslcd",147 &anslcd_fops148};149 150static const char anslcd_logo[] __initconst =151 "********************" /* Line #1 */152 "* LINUX! *" /* Line #3 */153 "* Welcome to *" /* Line #2 */154 "********************"; /* Line #4 */155 156static int __init157anslcd_init(void)158{159 int a;160 int retval;161 struct device_node* node;162 163 node = of_find_node_by_name(NULL, "lcd");164 if (!node || !of_node_name_eq(node->parent, "gc")) {165 of_node_put(node);166 return -ENODEV;167 }168 of_node_put(node);169 170 anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);171 172 retval = misc_register(&anslcd_dev);173 if(retval < 0){174 printk(KERN_INFO "LCD: misc_register failed\n");175 iounmap(anslcd_ptr);176 return retval;177 }178 179#ifdef DEBUG180 printk(KERN_DEBUG "LCD: init\n");181#endif182 183 mutex_lock(&anslcd_mutex);184 anslcd_write_byte_ctrl ( 0x38 );185 anslcd_write_byte_ctrl ( 0x0c );186 anslcd_write_byte_ctrl ( 0x06 );187 anslcd_write_byte_ctrl ( 0x01 );188 anslcd_write_byte_ctrl ( 0x02 );189 for(a=0;a<80;a++) {190 anslcd_write_byte_data(anslcd_logo[a]);191 }192 mutex_unlock(&anslcd_mutex);193 return 0;194}195 196static void __exit197anslcd_exit(void)198{199 misc_deregister(&anslcd_dev);200 iounmap(anslcd_ptr);201}202 203module_init(anslcd_init);204module_exit(anslcd_exit);205MODULE_LICENSE("GPL v2");206