1/*
2 * LCD, LED and Button interface for Cobalt
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License.  See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * Copyright (C) 1996, 1997 by Andrew Bose
9 *
10 * Linux kernel version history:
11 *       March 2001: Ported from 2.0.34  by Liam Davies
12 *
13 */
14#include <linux/types.h>
15#include <linux/errno.h>
16#include <linux/miscdevice.h>
17#include <linux/slab.h>
18#include <linux/ioport.h>
19#include <linux/fcntl.h>
20#include <linux/mc146818rtc.h>
21#include <linux/netdevice.h>
22#include <linux/sched.h>
23#include <linux/delay.h>
24
25#include <asm/io.h>
26#include <asm/uaccess.h>
27#include <asm/system.h>
28#include <linux/delay.h>
29
30#include "lcd.h"
31
32static int lcd_ioctl(struct inode *inode, struct file *file,
33		     unsigned int cmd, unsigned long arg);
34
35static unsigned int lcd_present = 1;
36
37/* used in arch/mips/cobalt/reset.c */
38int led_state = 0;
39
40
41static int lcd_ioctl(struct inode *inode, struct file *file,
42		     unsigned int cmd, unsigned long arg)
43{
44	struct lcd_display button_display;
45	unsigned long address, a;
46
47	switch (cmd) {
48	case LCD_On:
49		udelay(150);
50		BusyCheck();
51		LCDWriteInst(0x0F);
52		break;
53
54	case LCD_Off:
55		udelay(150);
56		BusyCheck();
57		LCDWriteInst(0x08);
58		break;
59
60	case LCD_Reset:
61		udelay(150);
62		LCDWriteInst(0x3F);
63		udelay(150);
64		LCDWriteInst(0x3F);
65		udelay(150);
66		LCDWriteInst(0x3F);
67		udelay(150);
68		LCDWriteInst(0x3F);
69		udelay(150);
70		LCDWriteInst(0x01);
71		udelay(150);
72		LCDWriteInst(0x06);
73		break;
74
75	case LCD_Clear:
76		udelay(150);
77		BusyCheck();
78		LCDWriteInst(0x01);
79		break;
80
81	case LCD_Cursor_Left:
82		udelay(150);
83		BusyCheck();
84		LCDWriteInst(0x10);
85		break;
86
87	case LCD_Cursor_Right:
88		udelay(150);
89		BusyCheck();
90		LCDWriteInst(0x14);
91		break;
92
93	case LCD_Cursor_Off:
94		udelay(150);
95		BusyCheck();
96		LCDWriteInst(0x0C);
97		break;
98
99	case LCD_Cursor_On:
100		udelay(150);
101		BusyCheck();
102		LCDWriteInst(0x0F);
103		break;
104
105	case LCD_Blink_Off:
106		udelay(150);
107		BusyCheck();
108		LCDWriteInst(0x0E);
109		break;
110
111	case LCD_Get_Cursor_Pos:{
112			struct lcd_display display;
113
114			udelay(150);
115			BusyCheck();
116			display.cursor_address = (LCDReadInst);
117			display.cursor_address =
118			    (display.cursor_address & 0x07F);
119			if (copy_to_user
120			    ((struct lcd_display *) arg, &display,
121			     sizeof(struct lcd_display)))
122				return -EFAULT;
123
124			break;
125		}
126
127
128	case LCD_Set_Cursor_Pos:{
129			struct lcd_display display;
130
131			if (copy_from_user
132			    (&display, (struct lcd_display *) arg,
133			     sizeof(struct lcd_display)))
134				return -EFAULT;
135
136			a = (display.cursor_address | kLCD_Addr);
137
138			udelay(150);
139			BusyCheck();
140			LCDWriteInst(a);
141
142			break;
143		}
144
145	case LCD_Get_Cursor:{
146			struct lcd_display display;
147
148			udelay(150);
149			BusyCheck();
150			display.character = LCDReadData;
151
152			if (copy_to_user
153			    ((struct lcd_display *) arg, &display,
154			     sizeof(struct lcd_display)))
155				return -EFAULT;
156			udelay(150);
157			BusyCheck();
158			LCDWriteInst(0x10);
159
160			break;
161		}
162
163	case LCD_Set_Cursor:{
164			struct lcd_display display;
165
166			if (copy_from_user
167			    (&display, (struct lcd_display *) arg,
168			     sizeof(struct lcd_display)))
169				return -EFAULT;
170
171			udelay(150);
172			BusyCheck();
173			LCDWriteData(display.character);
174			udelay(150);
175			BusyCheck();
176			LCDWriteInst(0x10);
177
178			break;
179		}
180
181
182	case LCD_Disp_Left:
183		udelay(150);
184		BusyCheck();
185		LCDWriteInst(0x18);
186		break;
187
188	case LCD_Disp_Right:
189		udelay(150);
190		BusyCheck();
191		LCDWriteInst(0x1C);
192		break;
193
194	case LCD_Home:
195		udelay(150);
196		BusyCheck();
197		LCDWriteInst(0x02);
198		break;
199
200	case LCD_Write:{
201			struct lcd_display display;
202			unsigned int index;
203
204
205			if (copy_from_user
206			    (&display, (struct lcd_display *) arg,
207			     sizeof(struct lcd_display)))
208				return -EFAULT;
209
210			udelay(150);
211			BusyCheck();
212			LCDWriteInst(0x80);
213			udelay(150);
214			BusyCheck();
215
216			for (index = 0; index < (display.size1); index++) {
217				udelay(150);
218				BusyCheck();
219				LCDWriteData(display.line1[index]);
220				BusyCheck();
221			}
222
223			udelay(150);
224			BusyCheck();
225			LCDWriteInst(0xC0);
226			udelay(150);
227			BusyCheck();
228			for (index = 0; index < (display.size2); index++) {
229				udelay(150);
230				BusyCheck();
231				LCDWriteData(display.line2[index]);
232			}
233
234			break;
235		}
236
237	case LCD_Read:{
238			struct lcd_display display;
239
240			BusyCheck();
241			for (address = kDD_R00; address <= kDD_R01;
242			     address++) {
243				a = (address | kLCD_Addr);
244
245				udelay(150);
246				BusyCheck();
247				LCDWriteInst(a);
248				udelay(150);
249				BusyCheck();
250				display.line1[address] = LCDReadData;
251			}
252
253			display.line1[0x27] = '\0';
254
255			for (address = kDD_R10; address <= kDD_R11;
256			     address++) {
257				a = (address | kLCD_Addr);
258
259				udelay(150);
260				BusyCheck();
261				LCDWriteInst(a);
262
263				udelay(150);
264				BusyCheck();
265				display.line2[address - 0x40] =
266				    LCDReadData;
267			}
268
269			display.line2[0x27] = '\0';
270
271			if (copy_to_user
272			    ((struct lcd_display *) arg, &display,
273			     sizeof(struct lcd_display)))
274				return -EFAULT;
275			break;
276		}
277
278//  set all GPIO leds to led_display.leds
279
280	case LED_Set:{
281			struct lcd_display led_display;
282
283
284			if (copy_from_user
285			    (&led_display, (struct lcd_display *) arg,
286			     sizeof(struct lcd_display)))
287				return -EFAULT;
288
289			led_state = led_display.leds;
290			LEDSet(led_state);
291
292			break;
293		}
294
295
296//  set only bit led_display.leds
297
298	case LED_Bit_Set:{
299			unsigned int i;
300			int bit = 1;
301			struct lcd_display led_display;
302
303
304			if (copy_from_user
305			    (&led_display, (struct lcd_display *) arg,
306			     sizeof(struct lcd_display)))
307				return -EFAULT;
308
309			for (i = 0; i < (int) led_display.leds; i++) {
310				bit = 2 * bit;
311			}
312
313			led_state = led_state | bit;
314			LEDSet(led_state);
315			break;
316		}
317
318//  clear only bit led_display.leds
319
320	case LED_Bit_Clear:{
321			unsigned int i;
322			int bit = 1;
323			struct lcd_display led_display;
324
325
326			if (copy_from_user
327			    (&led_display, (struct lcd_display *) arg,
328			     sizeof(struct lcd_display)))
329				return -EFAULT;
330
331			for (i = 0; i < (int) led_display.leds; i++) {
332				bit = 2 * bit;
333			}
334
335			led_state = led_state & ~bit;
336			LEDSet(led_state);
337			break;
338		}
339
340
341	case BUTTON_Read:{
342			button_display.buttons = GPIRead;
343			if (copy_to_user
344			    ((struct lcd_display *) arg, &button_display,
345			     sizeof(struct lcd_display)))
346				return -EFAULT;
347			break;
348		}
349
350	case LINK_Check:{
351			button_display.buttons =
352			    *((volatile unsigned long *) (0xB0100060));
353			if (copy_to_user
354			    ((struct lcd_display *) arg, &button_display,
355			     sizeof(struct lcd_display)))
356				return -EFAULT;
357			break;
358		}
359
360	case LINK_Check_2:{
361			int iface_num;
362
363			/* panel-utils should pass in the desired interface status is wanted for
364			 * in "buttons" of the structure.  We will set this to non-zero if the
365			 * link is in fact up for the requested interface.  --DaveM
366			 */
367			if (copy_from_user
368			    (&button_display, (struct lcd_display *) arg,
369			     sizeof(button_display)))
370				return -EFAULT;
371			iface_num = button_display.buttons;
372				button_display.buttons = 0;
373
374			if (__copy_to_user
375			    ((struct lcd_display *) arg, &button_display,
376			     sizeof(struct lcd_display)))
377				return -EFAULT;
378			break;
379		}
380
381	default:
382		return -EINVAL;
383
384	}
385
386	return 0;
387
388}
389
390static int lcd_open(struct inode *inode, struct file *file)
391{
392	if (!lcd_present)
393		return -ENXIO;
394	else
395		return 0;
396}
397
398/* Only RESET or NEXT counts as button pressed */
399
400static inline int button_pressed(void)
401{
402	unsigned long buttons = GPIRead;
403
404	if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B)
405	    || (buttons == BUTTON_Reset_B))
406		return buttons;
407	return 0;
408}
409
410/* LED daemon sits on this and we wake him up once a key is pressed. */
411
412static int lcd_waiters = 0;
413
414static ssize_t lcd_read(struct file *file, char *buf,
415		     size_t count, loff_t *ofs)
416{
417	long buttons_now;
418
419	if (lcd_waiters > 0)
420		return -EINVAL;
421
422	lcd_waiters++;
423	while (((buttons_now = (long) button_pressed()) == 0) &&
424	       !(signal_pending(current))) {
425		msleep_interruptible(2000);
426	}
427	lcd_waiters--;
428
429	if (signal_pending(current))
430		return -ERESTARTSYS;
431	return buttons_now;
432}
433
434/*
435 *	The various file operations we support.
436 */
437
438static const struct file_operations lcd_fops = {
439	.read = lcd_read,
440	.ioctl = lcd_ioctl,
441	.open = lcd_open,
442};
443
444static struct miscdevice lcd_dev = {
445	MISC_DYNAMIC_MINOR,
446	"lcd",
447	&lcd_fops
448};
449
450static int lcd_init(void)
451{
452	int ret;
453	unsigned long data;
454
455	pr_info("%s\n", LCD_DRIVER);
456	ret = misc_register(&lcd_dev);
457	if (ret) {
458		printk(KERN_WARNING LCD "Unable to register misc device.\n");
459		return ret;
460	}
461
462	/* Check region? Naaah! Just snarf it up. */
463/*	request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/
464
465	udelay(150);
466	data = LCDReadData;
467	if ((data & 0x000000FF) == (0x00)) {
468		lcd_present = 0;
469		pr_info(LCD "LCD Not Present\n");
470	} else {
471		lcd_present = 1;
472		WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg);
473		WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg);
474	}
475
476	return 0;
477}
478
479static void __exit lcd_exit(void)
480{
481	misc_deregister(&lcd_dev);
482}
483
484module_init(lcd_init);
485module_exit(lcd_exit);
486
487MODULE_AUTHOR("Andrew Bose");
488MODULE_LICENSE("GPL");
489