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