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 <asm/uaccess.h>
13#include <asm/sections.h>
14#include <asm/prom.h>
15#include <asm/ans-lcd.h>
16#include <asm/io.h>
17
18#define ANSLCD_ADDR		0xf301c000
19#define ANSLCD_CTRL_IX 0x00
20#define ANSLCD_DATA_IX 0x10
21
22static unsigned long anslcd_short_delay = 80;
23static unsigned long anslcd_long_delay = 3280;
24static volatile unsigned char* anslcd_ptr;
25
26#undef DEBUG
27
28static void __pmac
29anslcd_write_byte_ctrl ( unsigned char c )
30{
31#ifdef DEBUG
32	printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
33#endif
34	out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
35	switch(c) {
36		case 1:
37		case 2:
38		case 3:
39			udelay(anslcd_long_delay); break;
40		default: udelay(anslcd_short_delay);
41	}
42}
43
44static void __pmac
45anslcd_write_byte_data ( unsigned char c )
46{
47	out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
48	udelay(anslcd_short_delay);
49}
50
51static ssize_t __pmac
52anslcd_write( struct file * file, const char * buf,
53				size_t count, loff_t *ppos )
54{
55	const char * p = buf;
56	int i;
57
58#ifdef DEBUG
59	printk(KERN_DEBUG "LCD: write\n");
60#endif
61
62	if ( verify_area(VERIFY_READ, buf, count) )
63		return -EFAULT;
64	for ( i = *ppos; count > 0; ++i, ++p, --count )
65	{
66		char c;
67		__get_user(c, p);
68		anslcd_write_byte_data( c );
69	}
70	*ppos = i;
71	return p - buf;
72}
73
74static int __pmac
75anslcd_ioctl( struct inode * inode, struct file * file,
76				unsigned int cmd, unsigned long arg )
77{
78	char ch, *temp;
79
80#ifdef DEBUG
81	printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
82#endif
83
84	switch ( cmd )
85	{
86	case ANSLCD_CLEAR:
87		anslcd_write_byte_ctrl ( 0x38 );
88		anslcd_write_byte_ctrl ( 0x0f );
89		anslcd_write_byte_ctrl ( 0x06 );
90		anslcd_write_byte_ctrl ( 0x01 );
91		anslcd_write_byte_ctrl ( 0x02 );
92		return 0;
93	case ANSLCD_SENDCTRL:
94		temp = (char *) arg;
95		__get_user(ch, temp);
96		for (; ch; temp++) {
97			anslcd_write_byte_ctrl ( ch );
98			__get_user(ch, temp);
99		}
100		return 0;
101	case ANSLCD_SETSHORTDELAY:
102		if (!capable(CAP_SYS_ADMIN))
103			return -EACCES;
104		anslcd_short_delay=arg;
105		return 0;
106	case ANSLCD_SETLONGDELAY:
107		if (!capable(CAP_SYS_ADMIN))
108			return -EACCES;
109		anslcd_long_delay=arg;
110		return 0;
111	default:
112		return -EINVAL;
113	}
114}
115
116static int __pmac
117anslcd_open( struct inode * inode, struct file * file )
118{
119	return 0;
120}
121
122struct file_operations anslcd_fops = {
123	write:	anslcd_write,
124	ioctl:	anslcd_ioctl,
125	open:	anslcd_open,
126};
127
128static struct miscdevice anslcd_dev = {
129	ANSLCD_MINOR,
130	"anslcd",
131	&anslcd_fops
132};
133
134const char anslcd_logo[] =	"********************"  /* Line #1 */
135				"*      LINUX!      *"  /* Line #3 */
136				"*    Welcome to    *"  /* Line #2 */
137				"********************"; /* Line #4 */
138
139int __init
140anslcd_init(void)
141{
142	int a;
143	struct device_node* node;
144
145	node = find_devices("lcd");
146	if (!node || !node->parent)
147		return -ENODEV;
148	if (strcmp(node->parent->name, "gc"))
149		return -ENODEV;
150
151	anslcd_ptr = (volatile unsigned char*)ioremap(ANSLCD_ADDR, 0x20);
152
153	misc_register(&anslcd_dev);
154
155#ifdef DEBUG
156	printk(KERN_DEBUG "LCD: init\n");
157#endif
158
159	anslcd_write_byte_ctrl ( 0x38 );
160	anslcd_write_byte_ctrl ( 0x0c );
161	anslcd_write_byte_ctrl ( 0x06 );
162	anslcd_write_byte_ctrl ( 0x01 );
163	anslcd_write_byte_ctrl ( 0x02 );
164	for(a=0;a<80;a++) {
165		anslcd_write_byte_data(anslcd_logo[a]);
166	}
167	return 0;
168}
169
170__initcall(anslcd_init);
171
172