1 /* $Id: display7seg.c,v 1.6 2002/01/08 16:00:16 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/fs.h> 13 #include <linux/errno.h> 14 #include <linux/major.h> 15 #include <linux/init.h> 16 #include <linux/miscdevice.h> 17 #include <linux/ioport.h> /* request_region, check_region */ 18 #include <asm/atomic.h> 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 33 /* Solaris compatibility flag - 34 * The Solaris implementation omits support for several 35 * documented driver features (ref Sun doc 806-0180-03). 36 * By default, this module supports the documented driver 37 * abilities, rather than the Solaris implementation: 38 * 39 * 1) Device ALWAYS reverts to OBP-specified FLIPPED mode 40 * upon closure of device or module unload. 41 * 2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of 42 * FLIP bit 43 * 44 * If you wish the device to operate as under Solaris, 45 * omitting above features, set this parameter to non-zero. 46 */ 47 module_param 48 (sol_compat, int, 0); 49 MODULE_PARM_DESC 50 (sol_compat, 51 "Disables documented functionality omitted from Solaris driver"); 52 53 MODULE_AUTHOR 54 ("Eric Brower <ebrower@usa.net>"); 55 MODULE_DESCRIPTION 56 ("7-Segment Display driver for Sun Microsystems CP1400/1500"); 57 MODULE_LICENSE("GPL"); 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 static void __iomem* d7s_regs; 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 atomic_t d7s_users = ATOMIC_INIT(0); 90 91 static int d7s_open(struct inode *inode, struct file *f) 92 { 93 if (D7S_MINOR != iminor(inode)) 94 return -ENODEV; 95 atomic_inc(&d7s_users); 96 return 0; 97 } 98 99 static int d7s_release(struct inode *inode, struct file *f) 100 { 101 /* Reset flipped state to OBP default only if 102 * no other users have the device open and we 103 * are not operating in solaris-compat mode 104 */ 105 if (atomic_dec_and_test(&d7s_users) && !sol_compat) { 106 int regval = 0; 107 108 regval = readb(d7s_regs); 109 (0 == d7s_obpflipped()) ? 110 writeb(regval |= D7S_FLIP, d7s_regs): 111 writeb(regval &= ~D7S_FLIP, d7s_regs); 112 } 113 114 return 0; 115 } 116 117 static int d7s_ioctl(struct inode *inode, struct file *f, 118 unsigned int cmd, unsigned long arg) 119 { 120 __u8 regs = readb(d7s_regs); 121 __u8 ireg = 0; 122 123 if (D7S_MINOR != iminor(inode)) 124 return -ENODEV; 125 126 switch (cmd) { 127 case D7SIOCWR: 128 /* assign device register values 129 * we mask-out D7S_FLIP if in sol_compat mode 130 */ 131 if (get_user(ireg, (int __user *) arg)) 132 return -EFAULT; 133 if (0 != sol_compat) { 134 (regs & D7S_FLIP) ? 135 (ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP); 136 } 137 writeb(ireg, d7s_regs); 138 break; 139 140 case D7SIOCRD: 141 /* retrieve device register values 142 * NOTE: Solaris implementation returns D7S_FLIP bit 143 * as toggled by user, even though it does not honor it. 144 * This driver will not misinform you about the state 145 * of your hardware while in sol_compat mode 146 */ 147 if (put_user(regs, (int __user *) arg)) 148 return -EFAULT; 149 break; 150 151 case D7SIOCTM: 152 /* toggle device mode-- flip display orientation */ 153 (regs & D7S_FLIP) ? 154 (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP); 155 writeb(regs, d7s_regs); 156 break; 157 }; 158 159 return 0; 160 } 161 162 static struct file_operations d7s_fops = { 163 .owner = THIS_MODULE, 164 .ioctl = d7s_ioctl, 165 .open = d7s_open, 166 .release = d7s_release, 167 }; 168 169 static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops }; 170 171 static int __init d7s_init(void) 172 { 173 struct linux_ebus *ebus = NULL; 174 struct linux_ebus_device *edev = NULL; 175 int iTmp = 0, regs = 0; 176 177 for_each_ebus(ebus) { 178 for_each_ebusdev(edev, ebus) { 179 if (!strcmp(edev->prom_name, D7S_OBPNAME)) 180 goto ebus_done; 181 } 182 } 183 184 ebus_done: 185 if(!edev) { 186 printk("%s: unable to locate device\n", D7S_DEVNAME); 187 return -ENODEV; 188 } 189 190 d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8)); 191 192 iTmp = misc_register(&d7s_miscdev); 193 if (0 != iTmp) { 194 printk("%s: unable to acquire miscdevice minor %i\n", 195 D7S_DEVNAME, D7S_MINOR); 196 iounmap(d7s_regs); 197 return iTmp; 198 } 199 200 /* OBP option "d7s-flipped?" is honored as default 201 * for the device, and reset default when detached 202 */ 203 regs = readb(d7s_regs); 204 iTmp = d7s_obpflipped(); 205 (0 == iTmp) ? 206 writeb(regs |= D7S_FLIP, d7s_regs): 207 writeb(regs &= ~D7S_FLIP, d7s_regs); 208 209 printk("%s: 7-Segment Display%s at 0x%lx %s\n", 210 D7S_DEVNAME, 211 (0 == iTmp) ? (" (FLIPPED)") : (""), 212 edev->resource[0].start, 213 (0 != sol_compat) ? ("in sol_compat mode") : ("")); 214 215 return 0; 216 } 217 218 static void __exit d7s_cleanup(void) 219 { 220 int regs = readb(d7s_regs); 221 222 /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */ 223 if (0 == sol_compat) { 224 (0 == d7s_obpflipped()) ? 225 writeb(regs |= D7S_FLIP, d7s_regs): 226 writeb(regs &= ~D7S_FLIP, d7s_regs); 227 } 228 229 misc_deregister(&d7s_miscdev); 230 d7s_free(); 231 } 232 233 module_init(d7s_init); 234 module_exit(d7s_cleanup); 235