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 cycle_kernel_lock(); 98 atomic_inc(&d7s_users); 99 return 0; 100 } 101 102 static int d7s_release(struct inode *inode, struct file *f) 103 { 104 /* Reset flipped state to OBP default only if 105 * no other users have the device open and we 106 * are not operating in solaris-compat mode 107 */ 108 if (atomic_dec_and_test(&d7s_users) && !sol_compat) { 109 int regval = 0; 110 111 regval = readb(d7s_regs); 112 (0 == d7s_obpflipped()) ? 113 writeb(regval |= D7S_FLIP, d7s_regs): 114 writeb(regval &= ~D7S_FLIP, d7s_regs); 115 } 116 117 return 0; 118 } 119 120 static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 121 { 122 __u8 regs = readb(d7s_regs); 123 __u8 ireg = 0; 124 int error = 0; 125 126 if (D7S_MINOR != iminor(file->f_path.dentry->d_inode)) 127 return -ENODEV; 128 129 lock_kernel(); 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 __user *) arg)) { 136 error = -EFAULT; 137 break; 138 } 139 if (0 != sol_compat) { 140 (regs & D7S_FLIP) ? 141 (ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP); 142 } 143 writeb(ireg, d7s_regs); 144 break; 145 146 case D7SIOCRD: 147 /* retrieve device register values 148 * NOTE: Solaris implementation returns D7S_FLIP bit 149 * as toggled by user, even though it does not honor it. 150 * This driver will not misinform you about the state 151 * of your hardware while in sol_compat mode 152 */ 153 if (put_user(regs, (int __user *) arg)) { 154 error = -EFAULT; 155 break; 156 } 157 break; 158 159 case D7SIOCTM: 160 /* toggle device mode-- flip display orientation */ 161 (regs & D7S_FLIP) ? 162 (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP); 163 writeb(regs, d7s_regs); 164 break; 165 }; 166 unlock_kernel(); 167 168 return error; 169 } 170 171 static const struct file_operations d7s_fops = { 172 .owner = THIS_MODULE, 173 .unlocked_ioctl = d7s_ioctl, 174 .compat_ioctl = d7s_ioctl, 175 .open = d7s_open, 176 .release = d7s_release, 177 }; 178 179 static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops }; 180 181 static int __init d7s_init(void) 182 { 183 struct linux_ebus *ebus = NULL; 184 struct linux_ebus_device *edev = NULL; 185 int iTmp = 0, regs = 0; 186 187 for_each_ebus(ebus) { 188 for_each_ebusdev(edev, ebus) { 189 if (!strcmp(edev->prom_node->name, D7S_OBPNAME)) 190 goto ebus_done; 191 } 192 } 193 194 ebus_done: 195 if(!edev) { 196 printk("%s: unable to locate device\n", D7S_DEVNAME); 197 return -ENODEV; 198 } 199 200 d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8)); 201 202 iTmp = misc_register(&d7s_miscdev); 203 if (0 != iTmp) { 204 printk("%s: unable to acquire miscdevice minor %i\n", 205 D7S_DEVNAME, D7S_MINOR); 206 iounmap(d7s_regs); 207 return iTmp; 208 } 209 210 /* OBP option "d7s-flipped?" is honored as default 211 * for the device, and reset default when detached 212 */ 213 regs = readb(d7s_regs); 214 iTmp = d7s_obpflipped(); 215 (0 == iTmp) ? 216 writeb(regs |= D7S_FLIP, d7s_regs): 217 writeb(regs &= ~D7S_FLIP, d7s_regs); 218 219 printk("%s: 7-Segment Display%s at 0x%lx %s\n", 220 D7S_DEVNAME, 221 (0 == iTmp) ? (" (FLIPPED)") : (""), 222 edev->resource[0].start, 223 (0 != sol_compat) ? ("in sol_compat mode") : ("")); 224 225 return 0; 226 } 227 228 static void __exit d7s_cleanup(void) 229 { 230 int regs = readb(d7s_regs); 231 232 /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */ 233 if (0 == sol_compat) { 234 (0 == d7s_obpflipped()) ? 235 writeb(regs |= D7S_FLIP, d7s_regs): 236 writeb(regs &= ~D7S_FLIP, d7s_regs); 237 } 238 239 misc_deregister(&d7s_miscdev); 240 d7s_free(); 241 } 242 243 module_init(d7s_init); 244 module_exit(d7s_cleanup); 245