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