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