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 */ 18 #include <linux/smp_lock.h> 19 #include <asm/atomic.h> 20 #include <asm/ebus.h> /* EBus device */ 21 #include <asm/oplib.h> /* OpenProm Library */ 22 #include <asm/uaccess.h> /* put_/get_user */ 23 24 #include <asm/display7seg.h> 25 26 #define D7S_MINOR 193 27 #define D7S_OBPNAME "display7seg" 28 #define D7S_DEVNAME "d7s" 29 30 static int sol_compat = 0; /* Solaris compatibility mode */ 31 32 #ifdef MODULE 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_param 49 (sol_compat, int, 0); 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_LICENSE("GPL"); 59 MODULE_SUPPORTED_DEVICE 60 ("d7s"); 61 #endif /* ifdef MODULE */ 62 63 /* 64 * Register block address- see header for details 65 * ----------------------------------------- 66 * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 | 67 * ----------------------------------------- 68 * 69 * DP - Toggles decimal point on/off 70 * ALARM - Toggles "Alarm" LED green/red 71 * FLIP - Inverts display for upside-down mounted board 72 * bits 0-4 - 7-segment display contents 73 */ 74 static void __iomem* d7s_regs; 75 76 static inline void d7s_free(void) 77 { 78 iounmap(d7s_regs); 79 } 80 81 static inline int d7s_obpflipped(void) 82 { 83 int opt_node; 84 85 opt_node = prom_getchild(prom_root_node); 86 opt_node = prom_searchsiblings(opt_node, "options"); 87 return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1); 88 } 89 90 static atomic_t d7s_users = ATOMIC_INIT(0); 91 92 static int d7s_open(struct inode *inode, struct file *f) 93 { 94 if (D7S_MINOR != iminor(inode)) 95 return -ENODEV; 96 atomic_inc(&d7s_users); 97 return 0; 98 } 99 100 static int d7s_release(struct inode *inode, struct file *f) 101 { 102 /* Reset flipped state to OBP default only if 103 * no other users have the device open and we 104 * are not operating in solaris-compat mode 105 */ 106 if (atomic_dec_and_test(&d7s_users) && !sol_compat) { 107 int regval = 0; 108 109 regval = readb(d7s_regs); 110 (0 == d7s_obpflipped()) ? 111 writeb(regval |= D7S_FLIP, d7s_regs): 112 writeb(regval &= ~D7S_FLIP, d7s_regs); 113 } 114 115 return 0; 116 } 117 118 static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 119 { 120 __u8 regs = readb(d7s_regs); 121 __u8 ireg = 0; 122 int error = 0; 123 124 if (D7S_MINOR != iminor(file->f_dentry->d_inode)) 125 return -ENODEV; 126 127 lock_kernel(); 128 switch (cmd) { 129 case D7SIOCWR: 130 /* assign device register values 131 * we mask-out D7S_FLIP if in sol_compat mode 132 */ 133 if (get_user(ireg, (int __user *) arg)) { 134 error = -EFAULT; 135 break; 136 } 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 __user *) arg)) { 152 error = -EFAULT; 153 break; 154 } 155 break; 156 157 case D7SIOCTM: 158 /* toggle device mode-- flip display orientation */ 159 (regs & D7S_FLIP) ? 160 (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP); 161 writeb(regs, d7s_regs); 162 break; 163 }; 164 unlock_kernel(); 165 166 return error; 167 } 168 169 static struct file_operations d7s_fops = { 170 .owner = THIS_MODULE, 171 .unlocked_ioctl = d7s_ioctl, 172 .compat_ioctl = d7s_ioctl, 173 .open = d7s_open, 174 .release = d7s_release, 175 }; 176 177 static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops }; 178 179 static int __init d7s_init(void) 180 { 181 struct linux_ebus *ebus = NULL; 182 struct linux_ebus_device *edev = NULL; 183 int iTmp = 0, regs = 0; 184 185 for_each_ebus(ebus) { 186 for_each_ebusdev(edev, ebus) { 187 if (!strcmp(edev->prom_name, D7S_OBPNAME)) 188 goto ebus_done; 189 } 190 } 191 192 ebus_done: 193 if(!edev) { 194 printk("%s: unable to locate device\n", D7S_DEVNAME); 195 return -ENODEV; 196 } 197 198 d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8)); 199 200 iTmp = misc_register(&d7s_miscdev); 201 if (0 != iTmp) { 202 printk("%s: unable to acquire miscdevice minor %i\n", 203 D7S_DEVNAME, D7S_MINOR); 204 iounmap(d7s_regs); 205 return iTmp; 206 } 207 208 /* OBP option "d7s-flipped?" is honored as default 209 * for the device, and reset default when detached 210 */ 211 regs = readb(d7s_regs); 212 iTmp = d7s_obpflipped(); 213 (0 == iTmp) ? 214 writeb(regs |= D7S_FLIP, d7s_regs): 215 writeb(regs &= ~D7S_FLIP, d7s_regs); 216 217 printk("%s: 7-Segment Display%s at 0x%lx %s\n", 218 D7S_DEVNAME, 219 (0 == iTmp) ? (" (FLIPPED)") : (""), 220 edev->resource[0].start, 221 (0 != sol_compat) ? ("in sol_compat mode") : ("")); 222 223 return 0; 224 } 225 226 static void __exit d7s_cleanup(void) 227 { 228 int regs = readb(d7s_regs); 229 230 /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */ 231 if (0 == sol_compat) { 232 (0 == d7s_obpflipped()) ? 233 writeb(regs |= D7S_FLIP, d7s_regs): 234 writeb(regs &= ~D7S_FLIP, d7s_regs); 235 } 236 237 misc_deregister(&d7s_miscdev); 238 d7s_free(); 239 } 240 241 module_init(d7s_init); 242 module_exit(d7s_cleanup); 243