Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
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> | |
120d200a | 11 | #include <linux/module.h> |
1da177e4 LT |
12 | #include <linux/delay.h> |
13 | #include <linux/fs.h> | |
14 | ||
7c0f6ba6 | 15 | #include <linux/uaccess.h> |
1da177e4 LT |
16 | #include <asm/sections.h> |
17 | #include <asm/prom.h> | |
1da177e4 LT |
18 | #include <asm/io.h> |
19 | ||
33d71d26 KG |
20 | #include "ans-lcd.h" |
21 | ||
1da177e4 LT |
22 | #define ANSLCD_ADDR 0xf301c000 |
23 | #define ANSLCD_CTRL_IX 0x00 | |
24 | #define ANSLCD_DATA_IX 0x10 | |
25 | ||
26 | static unsigned long anslcd_short_delay = 80; | |
27 | static unsigned long anslcd_long_delay = 3280; | |
28 | static volatile unsigned char __iomem *anslcd_ptr; | |
95fdac73 | 29 | static DEFINE_MUTEX(anslcd_mutex); |
1da177e4 LT |
30 | |
31 | #undef DEBUG | |
32 | ||
aacaf9bd | 33 | static void |
1da177e4 LT |
34 | anslcd_write_byte_ctrl ( unsigned char c ) |
35 | { | |
36 | #ifdef DEBUG | |
37 | printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c); | |
38 | #endif | |
39 | 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 | ||
aacaf9bd | 49 | static void |
1da177e4 LT |
50 | anslcd_write_byte_data ( unsigned char c ) |
51 | { | |
52 | out_8(anslcd_ptr + ANSLCD_DATA_IX, c); | |
53 | udelay(anslcd_short_delay); | |
54 | } | |
55 | ||
aacaf9bd | 56 | static ssize_t |
1da177e4 LT |
57 | anslcd_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 DEBUG | |
64 | printk(KERN_DEBUG "LCD: write\n"); | |
65 | #endif | |
66 | ||
96d4f267 | 67 | if (!access_ok(buf, count)) |
1da177e4 | 68 | return -EFAULT; |
95fdac73 TG |
69 | |
70 | mutex_lock(&anslcd_mutex); | |
1da177e4 LT |
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 | } | |
95fdac73 | 77 | mutex_unlock(&anslcd_mutex); |
1da177e4 LT |
78 | *ppos = i; |
79 | return p - buf; | |
80 | } | |
81 | ||
95fdac73 TG |
82 | static long |
83 | anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
1da177e4 LT |
84 | { |
85 | char ch, __user *temp; | |
95fdac73 | 86 | long ret = 0; |
1da177e4 LT |
87 | |
88 | #ifdef DEBUG | |
89 | printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg); | |
90 | #endif | |
91 | ||
95fdac73 TG |
92 | mutex_lock(&anslcd_mutex); |
93 | ||
1da177e4 LT |
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 ); | |
95fdac73 | 102 | break; |
1da177e4 LT |
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 | } | |
95fdac73 | 110 | break; |
1da177e4 LT |
111 | case ANSLCD_SETSHORTDELAY: |
112 | if (!capable(CAP_SYS_ADMIN)) | |
95fdac73 TG |
113 | ret =-EACCES; |
114 | else | |
115 | anslcd_short_delay=arg; | |
116 | break; | |
1da177e4 LT |
117 | case ANSLCD_SETLONGDELAY: |
118 | if (!capable(CAP_SYS_ADMIN)) | |
95fdac73 TG |
119 | ret = -EACCES; |
120 | else | |
121 | anslcd_long_delay=arg; | |
122 | break; | |
1da177e4 | 123 | default: |
95fdac73 | 124 | ret = -EINVAL; |
1da177e4 | 125 | } |
95fdac73 TG |
126 | |
127 | mutex_unlock(&anslcd_mutex); | |
128 | return ret; | |
1da177e4 LT |
129 | } |
130 | ||
aacaf9bd | 131 | static int |
1da177e4 LT |
132 | anslcd_open( struct inode * inode, struct file * file ) |
133 | { | |
134 | return 0; | |
135 | } | |
136 | ||
fa027c2a | 137 | const struct file_operations anslcd_fops = { |
95fdac73 TG |
138 | .write = anslcd_write, |
139 | .unlocked_ioctl = anslcd_ioctl, | |
140 | .open = anslcd_open, | |
6038f373 | 141 | .llseek = default_llseek, |
1da177e4 LT |
142 | }; |
143 | ||
144 | static struct miscdevice anslcd_dev = { | |
145 | ANSLCD_MINOR, | |
146 | "anslcd", | |
147 | &anslcd_fops | |
148 | }; | |
149 | ||
150 | const char anslcd_logo[] = "********************" /* Line #1 */ | |
151 | "* LINUX! *" /* Line #3 */ | |
152 | "* Welcome to *" /* Line #2 */ | |
153 | "********************"; /* Line #4 */ | |
154 | ||
155 | static int __init | |
156 | anslcd_init(void) | |
157 | { | |
158 | int a; | |
159 | int retval; | |
160 | struct device_node* node; | |
161 | ||
30686ba6 | 162 | node = of_find_node_by_name(NULL, "lcd"); |
f1e0addc | 163 | if (!node || !of_node_name_eq(node->parent, "gc")) { |
30686ba6 | 164 | of_node_put(node); |
1da177e4 | 165 | return -ENODEV; |
30686ba6 SR |
166 | } |
167 | of_node_put(node); | |
1da177e4 LT |
168 | |
169 | anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); | |
170 | ||
171 | retval = misc_register(&anslcd_dev); | |
172 | if(retval < 0){ | |
173 | printk(KERN_INFO "LCD: misc_register failed\n"); | |
174 | iounmap(anslcd_ptr); | |
175 | return retval; | |
176 | } | |
177 | ||
178 | #ifdef DEBUG | |
179 | printk(KERN_DEBUG "LCD: init\n"); | |
180 | #endif | |
181 | ||
95fdac73 | 182 | mutex_lock(&anslcd_mutex); |
1da177e4 LT |
183 | anslcd_write_byte_ctrl ( 0x38 ); |
184 | anslcd_write_byte_ctrl ( 0x0c ); | |
185 | anslcd_write_byte_ctrl ( 0x06 ); | |
186 | anslcd_write_byte_ctrl ( 0x01 ); | |
187 | anslcd_write_byte_ctrl ( 0x02 ); | |
188 | for(a=0;a<80;a++) { | |
189 | anslcd_write_byte_data(anslcd_logo[a]); | |
190 | } | |
95fdac73 | 191 | mutex_unlock(&anslcd_mutex); |
1da177e4 LT |
192 | return 0; |
193 | } | |
194 | ||
195 | static void __exit | |
196 | anslcd_exit(void) | |
197 | { | |
198 | misc_deregister(&anslcd_dev); | |
199 | iounmap(anslcd_ptr); | |
200 | } | |
201 | ||
202 | module_init(anslcd_init); | |
203 | module_exit(anslcd_exit); | |
47d703e1 | 204 | MODULE_LICENSE("GPL v2"); |