display7seg.c 5.7 KB

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