xref: /OK3568_Linux_fs/kernel/drivers/macintosh/ans-lcd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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