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