Merge branch 'rfc' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/perfcounte...
[linux-2.6] / drivers / char / briq_panel.c
1 /*
2  * Drivers for the Total Impact PPC based computer "BRIQ"
3  * by Dr. Karsten Jeppesen
4  *
5  */
6
7 #include <linux/module.h>
8
9 #include <linux/smp_lock.h>
10 #include <linux/types.h>
11 #include <linux/errno.h>
12 #include <linux/tty.h>
13 #include <linux/timer.h>
14 #include <linux/kernel.h>
15 #include <linux/wait.h>
16 #include <linux/string.h>
17 #include <linux/slab.h>
18 #include <linux/ioport.h>
19 #include <linux/delay.h>
20 #include <linux/miscdevice.h>
21 #include <linux/fs.h>
22 #include <linux/mm.h>
23 #include <linux/init.h>
24
25 #include <asm/uaccess.h>
26 #include <asm/io.h>
27 #include <asm/prom.h>
28
29 #define         BRIQ_PANEL_MINOR        156
30 #define         BRIQ_PANEL_VFD_IOPORT   0x0390
31 #define         BRIQ_PANEL_LED_IOPORT   0x0398
32 #define         BRIQ_PANEL_VER          "1.1 (04/20/2002)"
33 #define         BRIQ_PANEL_MSG0         "Loading Linux"
34
35 static int              vfd_is_open;
36 static unsigned char    vfd[40];
37 static int              vfd_cursor;
38 static unsigned char    ledpb, led;
39
40 static void update_vfd(void)
41 {
42         int     i;
43
44         /* cursor home */
45         outb(0x02, BRIQ_PANEL_VFD_IOPORT);
46         for (i=0; i<20; i++)
47                 outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1);
48
49         /* cursor to next line */
50         outb(0xc0, BRIQ_PANEL_VFD_IOPORT);
51         for (i=20; i<40; i++)
52                 outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1);
53
54 }
55
56 static void set_led(char state)
57 {
58         if (state == 'R')
59                 led = 0x01;
60         else if (state == 'G')
61                 led = 0x02;
62         else if (state == 'Y')
63                 led = 0x03;
64         else if (state == 'X')
65                 led = 0x00;
66         outb(led, BRIQ_PANEL_LED_IOPORT);
67 }
68
69 static int briq_panel_open(struct inode *ino, struct file *filep)
70 {
71         lock_kernel();
72         /* enforce single access, vfd_is_open is protected by BKL */
73         if (vfd_is_open) {
74                 unlock_kernel();
75                 return -EBUSY;
76         }
77         vfd_is_open = 1;
78
79         unlock_kernel();
80         return 0;
81 }
82
83 static int briq_panel_release(struct inode *ino, struct file *filep)
84 {
85         if (!vfd_is_open)
86                 return -ENODEV;
87
88         vfd_is_open = 0;
89
90         return 0;
91 }
92
93 static ssize_t briq_panel_read(struct file *file, char __user *buf, size_t count,
94                          loff_t *ppos)
95 {
96         unsigned short c;
97         unsigned char cp;
98
99         if (!vfd_is_open)
100                 return -ENODEV;
101
102         c = (inb(BRIQ_PANEL_LED_IOPORT) & 0x000c) | (ledpb & 0x0003);
103         set_led(' ');
104         /* upper button released */
105         if ((!(ledpb & 0x0004)) && (c & 0x0004)) {
106                 cp = ' ';
107                 ledpb = c;
108                 if (copy_to_user(buf, &cp, 1))
109                         return -EFAULT;
110                 return 1;
111         }
112         /* lower button released */
113         else if ((!(ledpb & 0x0008)) && (c & 0x0008)) {
114                 cp = '\r';
115                 ledpb = c;
116                 if (copy_to_user(buf, &cp, 1))
117                         return -EFAULT;
118                 return 1;
119         } else {
120                 ledpb = c;
121                 return 0;
122         }
123 }
124
125 static void scroll_vfd( void )
126 {
127         int     i;
128
129         for (i=0; i<20; i++) {
130                 vfd[i] = vfd[i+20];
131                 vfd[i+20] = ' ';
132         }
133         vfd_cursor = 20;
134 }
135
136 static ssize_t briq_panel_write(struct file *file, const char __user *buf, size_t len,
137                           loff_t *ppos)
138 {
139         size_t indx = len;
140         int i, esc = 0;
141
142         if (!vfd_is_open)
143                 return -EBUSY;
144
145         for (;;) {
146                 char c;
147                 if (!indx)
148                         break;
149                 if (get_user(c, buf))
150                         return -EFAULT;
151                 if (esc) {
152                         set_led(c);
153                         esc = 0;
154                 } else if (c == 27) {
155                         esc = 1;
156                 } else if (c == 12) {
157                         /* do a form feed */
158                         for (i=0; i<40; i++)
159                                 vfd[i] = ' ';
160                         vfd_cursor = 0;
161                 } else if (c == 10) {
162                         if (vfd_cursor < 20)
163                                 vfd_cursor = 20;
164                         else if (vfd_cursor < 40)
165                                 vfd_cursor = 40;
166                         else if (vfd_cursor < 60)
167                                 vfd_cursor = 60;
168                         if (vfd_cursor > 59)
169                                 scroll_vfd();
170                 } else {
171                         /* just a character */
172                         if (vfd_cursor > 39)
173                                 scroll_vfd();
174                         vfd[vfd_cursor++] = c;
175                 }
176                 indx--;
177                 buf++;
178         }
179         update_vfd();
180
181         return len;
182 }
183
184 static const struct file_operations briq_panel_fops = {
185         .owner          = THIS_MODULE,
186         .read           = briq_panel_read,
187         .write          = briq_panel_write,
188         .open           = briq_panel_open,
189         .release        = briq_panel_release,
190 };
191
192 static struct miscdevice briq_panel_miscdev = {
193         BRIQ_PANEL_MINOR,
194         "briq_panel",
195         &briq_panel_fops
196 };
197
198 static int __init briq_panel_init(void)
199 {
200         struct device_node *root = of_find_node_by_path("/");
201         const char *machine;
202         int i;
203
204         machine = of_get_property(root, "model", NULL);
205         if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) {
206                 of_node_put(root);
207                 return -ENODEV;
208         }
209         of_node_put(root);
210
211         printk(KERN_INFO
212                 "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n",
213                 BRIQ_PANEL_VER);
214
215         if (!request_region(BRIQ_PANEL_VFD_IOPORT, 4, "BRIQ Front Panel"))
216                 return -EBUSY;
217
218         if (!request_region(BRIQ_PANEL_LED_IOPORT, 2, "BRIQ Front Panel")) {
219                 release_region(BRIQ_PANEL_VFD_IOPORT, 4);
220                 return -EBUSY;
221         }
222         ledpb = inb(BRIQ_PANEL_LED_IOPORT) & 0x000c;
223
224         if (misc_register(&briq_panel_miscdev) < 0) {
225                 release_region(BRIQ_PANEL_VFD_IOPORT, 4);
226                 release_region(BRIQ_PANEL_LED_IOPORT, 2);
227                 return -EBUSY;
228         }
229
230         outb(0x38, BRIQ_PANEL_VFD_IOPORT);      /* Function set */
231         outb(0x01, BRIQ_PANEL_VFD_IOPORT);      /* Clear display */
232         outb(0x0c, BRIQ_PANEL_VFD_IOPORT);      /* Display on */
233         outb(0x06, BRIQ_PANEL_VFD_IOPORT);      /* Entry normal */
234         for (i=0; i<40; i++)
235                 vfd[i]=' ';
236 #ifndef MODULE
237         vfd[0] = 'L';
238         vfd[1] = 'o';
239         vfd[2] = 'a';
240         vfd[3] = 'd';
241         vfd[4] = 'i';
242         vfd[5] = 'n';
243         vfd[6] = 'g';
244         vfd[7] = ' ';
245         vfd[8] = '.';
246         vfd[9] = '.';
247         vfd[10] = '.';
248 #endif /* !MODULE */
249
250         update_vfd();
251
252         return 0;
253 }
254
255 static void __exit briq_panel_exit(void)
256 {
257         misc_deregister(&briq_panel_miscdev);
258         release_region(BRIQ_PANEL_VFD_IOPORT, 4);
259         release_region(BRIQ_PANEL_LED_IOPORT, 2);
260 }
261
262 module_init(briq_panel_init);
263 module_exit(briq_panel_exit);
264
265 MODULE_LICENSE("GPL");
266 MODULE_AUTHOR("Karsten Jeppesen <karsten@jeppesens.com>");
267 MODULE_DESCRIPTION("Driver for the Total Impact briQ front panel");