Merge tag 'mtd/for-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
[linux-2.6-microblaze.git] / drivers / macintosh / ans-lcd.c
1 // SPDX-License-Identifier: GPL-2.0
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>
11 #include <linux/module.h>
12 #include <linux/delay.h>
13 #include <linux/fs.h>
14
15 #include <linux/uaccess.h>
16 #include <asm/sections.h>
17 #include <asm/prom.h>
18 #include <asm/io.h>
19
20 #include "ans-lcd.h"
21
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;
29 static DEFINE_MUTEX(anslcd_mutex);
30
31 #undef DEBUG
32
33 static void
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
49 static void
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
56 static ssize_t
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
67         if (!access_ok(buf, count))
68                 return -EFAULT;
69
70         mutex_lock(&anslcd_mutex);
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         }
77         mutex_unlock(&anslcd_mutex);
78         *ppos = i;
79         return p - buf;
80 }
81
82 static long
83 anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
84 {
85         char ch, __user *temp;
86         long ret = 0;
87
88 #ifdef DEBUG
89         printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
90 #endif
91
92         mutex_lock(&anslcd_mutex);
93
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 );
102                 break;
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                 }
110                 break;
111         case ANSLCD_SETSHORTDELAY:
112                 if (!capable(CAP_SYS_ADMIN))
113                         ret =-EACCES;
114                 else
115                         anslcd_short_delay=arg;
116                 break;
117         case ANSLCD_SETLONGDELAY:
118                 if (!capable(CAP_SYS_ADMIN))
119                         ret = -EACCES;
120                 else
121                         anslcd_long_delay=arg;
122                 break;
123         default:
124                 ret = -EINVAL;
125         }
126
127         mutex_unlock(&anslcd_mutex);
128         return ret;
129 }
130
131 static int
132 anslcd_open( struct inode * inode, struct file * file )
133 {
134         return 0;
135 }
136
137 const struct file_operations anslcd_fops = {
138         .write          = anslcd_write,
139         .unlocked_ioctl = anslcd_ioctl,
140         .open           = anslcd_open,
141         .llseek         = default_llseek,
142 };
143
144 static struct miscdevice anslcd_dev = {
145         LCD_MINOR,
146         "anslcd",
147         &anslcd_fops
148 };
149
150 static const char anslcd_logo[] __initconst =
151                                 "********************"  /* Line #1 */
152                                 "*      LINUX!      *"  /* Line #3 */
153                                 "*    Welcome to    *"  /* Line #2 */
154                                 "********************"; /* Line #4 */
155
156 static int __init
157 anslcd_init(void)
158 {
159         int a;
160         int retval;
161         struct device_node* node;
162
163         node = of_find_node_by_name(NULL, "lcd");
164         if (!node || !of_node_name_eq(node->parent, "gc")) {
165                 of_node_put(node);
166                 return -ENODEV;
167         }
168         of_node_put(node);
169
170         anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
171         
172         retval = misc_register(&anslcd_dev);
173         if(retval < 0){
174                 printk(KERN_INFO "LCD: misc_register failed\n");
175                 iounmap(anslcd_ptr);
176                 return retval;
177         }
178
179 #ifdef DEBUG
180         printk(KERN_DEBUG "LCD: init\n");
181 #endif
182
183         mutex_lock(&anslcd_mutex);
184         anslcd_write_byte_ctrl ( 0x38 );
185         anslcd_write_byte_ctrl ( 0x0c );
186         anslcd_write_byte_ctrl ( 0x06 );
187         anslcd_write_byte_ctrl ( 0x01 );
188         anslcd_write_byte_ctrl ( 0x02 );
189         for(a=0;a<80;a++) {
190                 anslcd_write_byte_data(anslcd_logo[a]);
191         }
192         mutex_unlock(&anslcd_mutex);
193         return 0;
194 }
195
196 static void __exit
197 anslcd_exit(void)
198 {
199         misc_deregister(&anslcd_dev);
200         iounmap(anslcd_ptr);
201 }
202
203 module_init(anslcd_init);
204 module_exit(anslcd_exit);
205 MODULE_LICENSE("GPL v2");