File: /usr/src/linux/drivers/sbus/char/display7seg.c

1     /* $Id: display7seg.c,v 1.4 2000/11/08 05:08:23 davem Exp $
2      *
3      * display7seg - Driver implementation for the 7-segment display
4      * present on Sun Microsystems CP1400 and CP1500
5      *
6      * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
7      *
8      */
9     
10     #include <linux/kernel.h>
11     #include <linux/module.h>
12     #include <linux/version.h>
13     #include <linux/fs.h>
14     #include <linux/errno.h>
15     #include <linux/major.h>
16     #include <linux/init.h>
17     #include <linux/miscdevice.h>
18     #include <linux/ioport.h>		/* request_region, check_region */
19     #include <asm/ebus.h>			/* EBus device					*/
20     #include <asm/oplib.h>			/* OpenProm Library 			*/
21     #include <asm/uaccess.h>		/* put_/get_user			*/
22     
23     #include <asm/display7seg.h>
24     
25     #define D7S_MINOR	193
26     #define D7S_OBPNAME	"display7seg"
27     #define D7S_DEVNAME "d7s"
28     
29     static int sol_compat = 0;		/* Solaris compatibility mode	*/
30     
31     #ifdef MODULE
32     EXPORT_NO_SYMBOLS;
33     
34     /* Solaris compatibility flag -
35      * The Solaris implementation omits support for several
36      * documented driver features (ref Sun doc 806-0180-03).  
37      * By default, this module supports the documented driver 
38      * abilities, rather than the Solaris implementation:
39      *
40      * 	1) Device ALWAYS reverts to OBP-specified FLIPPED mode
41      * 	   upon closure of device or module unload.
42      * 	2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of
43      * 	   FLIP bit
44      *
45      * If you wish the device to operate as under Solaris,
46      * omitting above features, set this parameter to non-zero.
47      */
48     MODULE_PARM
49     	(sol_compat, "1i");
50     MODULE_PARM_DESC
51     	(sol_compat, 
52     	 "Disables documented functionality omitted from Solaris driver");
53     
54     MODULE_AUTHOR
55     	("Eric Brower <ebrower@usa.net>");
56     MODULE_DESCRIPTION
57     	("7-Segment Display driver for Sun Microsystems CP1400/1500");
58     MODULE_SUPPORTED_DEVICE
59     	("d7s");
60     #endif /* ifdef MODULE */
61     
62     /*
63      * Register block address- see header for details
64      * -----------------------------------------
65      * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |
66      * -----------------------------------------
67      *
68      * DP 		- Toggles decimal point on/off 
69      * ALARM	- Toggles "Alarm" LED green/red
70      * FLIP		- Inverts display for upside-down mounted board
71      * bits 0-4	- 7-segment display contents
72      */
73     volatile u8* d7s_regs = 0;
74     
75     static inline void d7s_free(void)
76     {
77     	iounmap(d7s_regs);
78     }
79     
80     static inline int d7s_obpflipped(void)
81     {
82     	int opt_node;
83     
84     	opt_node = prom_getchild(prom_root_node);
85     	opt_node = prom_searchsiblings(opt_node, "options");
86     	return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1);
87     }
88     
89     static int d7s_open(struct inode *inode, struct file *f)
90     {
91     	if (D7S_MINOR != MINOR(inode->i_rdev))
92     		return -ENODEV;
93     
94     	MOD_INC_USE_COUNT;
95     	return 0;
96     }
97     
98     static int d7s_release(struct inode *inode, struct file *f)
99     {
100     	if (D7S_MINOR != MINOR(inode->i_rdev))
101     		return -ENODEV;
102     	
103     	MOD_DEC_USE_COUNT;
104     
105     	/* Reset flipped state to OBP default only if
106     	 * no other users have the device open and we
107     	 * are not operating in solaris-compat mode
108     	 */
109     	if (0 == MOD_IN_USE && 0 == sol_compat) {
110     		int regval = 0;
111     
112     		regval = readb(d7s_regs);
113     		(0 == d7s_obpflipped())	? 
114     			writeb(regval |= D7S_FLIP,  d7s_regs): 
115     			writeb(regval &= ~D7S_FLIP, d7s_regs);
116     	}
117     
118     	return 0;
119     }
120     
121     static int d7s_ioctl(struct inode *inode, struct file *f, 
122     		     unsigned int cmd, unsigned long arg)
123     {
124     	__u8 regs = readb(d7s_regs);
125     	__u8 ireg = 0;
126     
127     	if (D7S_MINOR != MINOR(inode->i_rdev))
128     		return -ENODEV;
129     
130     	switch (cmd) {
131     	case D7SIOCWR:
132     		/* assign device register values
133     		 * we mask-out D7S_FLIP if in sol_compat mode
134     		 */
135     		if (get_user(ireg, (int *) arg))
136     			return -EFAULT;
137     		if (0 != sol_compat) {
138     			(regs & D7S_FLIP) ? 
139     				(ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP);
140     		}
141     		writeb(ireg, d7s_regs);
142     		break;
143     
144     	case D7SIOCRD:
145     		/* retrieve device register values
146     		 * NOTE: Solaris implementation returns D7S_FLIP bit
147     		 * as toggled by user, even though it does not honor it.
148     		 * This driver will not misinform you about the state
149     		 * of your hardware while in sol_compat mode
150     		 */
151     		if (put_user(regs, (int *) arg))
152     			return -EFAULT;
153     		break;
154     
155     	case D7SIOCTM:
156     		/* toggle device mode-- flip display orientation */
157     		(regs & D7S_FLIP) ? 
158     			(regs &= ~D7S_FLIP) : (regs |= D7S_FLIP);
159     		writeb(regs, d7s_regs);
160     		break;
161     	};
162     
163     	return 0;
164     }
165     
166     static struct file_operations d7s_fops = {
167     	owner:		THIS_MODULE,
168     	ioctl:		d7s_ioctl,
169     	open:		d7s_open,
170     	release:	d7s_release,
171     };
172     
173     static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops };
174     
175     static int __init d7s_init(void)
176     {
177     	struct linux_ebus *ebus = NULL;
178     	struct linux_ebus_device *edev = NULL;
179     	int iTmp = 0, regs = 0;
180     
181     	for_each_ebus(ebus) {
182     		for_each_ebusdev(edev, ebus) {
183     			if (!strcmp(edev->prom_name, D7S_OBPNAME))
184     				goto ebus_done;
185     		}
186     	}
187     
188     ebus_done:
189     	if(!edev) {
190     		printk("%s: unable to locate device\n", D7S_DEVNAME);
191     		return -ENODEV;
192     	}
193     
194     	d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8));
195     
196     	iTmp = misc_register(&d7s_miscdev);
197     	if (0 != iTmp) {
198     		printk("%s: unable to acquire miscdevice minor %i\n",
199     		       D7S_DEVNAME, D7S_MINOR);
200     		return iTmp;
201     	}
202     
203     	/* OBP option "d7s-flipped?" is honored as default
204     	 * for the device, and reset default when detached
205     	 */
206     	regs = readb(d7s_regs);
207     	iTmp = d7s_obpflipped();
208     	(0 == iTmp) ? 
209     		writeb(regs |= D7S_FLIP,  d7s_regs): 
210     		writeb(regs &= ~D7S_FLIP, d7s_regs);
211     
212     	printk("%s: 7-Segment Display%s at 0x%lx %s\n", 
213     	       D7S_DEVNAME,
214     	       (0 == iTmp) ? (" (FLIPPED)") : (""),
215     	       edev->resource[0].start,
216     	       (0 != sol_compat) ? ("in sol_compat mode") : (""));
217     
218     	return 0;
219     }
220     
221     static void __exit d7s_cleanup(void)
222     {
223     	int regs = readb(d7s_regs);
224     
225     	/* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
226     	if (0 == sol_compat) {
227     		(0 == d7s_obpflipped())	? 
228     			writeb(regs |= D7S_FLIP,  d7s_regs):
229     			writeb(regs &= ~D7S_FLIP, d7s_regs);
230     	}
231     
232     	misc_deregister(&d7s_miscdev);
233     	d7s_free();
234     }
235     
236     module_init(d7s_init);
237     module_exit(d7s_cleanup);
238