display7seg.c 5.8 KB

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