root/drivers/sbus/char/display7seg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. d7s_open
  2. d7s_release
  3. d7s_ioctl
  4. d7s_probe
  5. d7s_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /* display7seg.c - Driver implementation for the 7-segment display
   3  *                 present on Sun Microsystems CP1400 and CP1500
   4  *
   5  * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
   6  */
   7 
   8 #include <linux/device.h>
   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/miscdevice.h>
  15 #include <linux/ioport.h>               /* request_region */
  16 #include <linux/slab.h>
  17 #include <linux/mutex.h>
  18 #include <linux/of.h>
  19 #include <linux/of_device.h>
  20 #include <linux/atomic.h>
  21 #include <linux/uaccess.h>              /* put_/get_user                        */
  22 #include <asm/io.h>
  23 
  24 #include <asm/display7seg.h>
  25 
  26 #define DRIVER_NAME     "d7s"
  27 #define PFX             DRIVER_NAME ": "
  28 
  29 static DEFINE_MUTEX(d7s_mutex);
  30 static int sol_compat = 0;              /* Solaris compatibility mode   */
  31 
  32 /* Solaris compatibility flag -
  33  * The Solaris implementation omits support for several
  34  * documented driver features (ref Sun doc 806-0180-03).  
  35  * By default, this module supports the documented driver 
  36  * abilities, rather than the Solaris implementation:
  37  *
  38  *      1) Device ALWAYS reverts to OBP-specified FLIPPED mode
  39  *         upon closure of device or module unload.
  40  *      2) Device ioctls D7SIOCRD/D7SIOCWR honor toggling of
  41  *         FLIP bit
  42  *
  43  * If you wish the device to operate as under Solaris,
  44  * omitting above features, set this parameter to non-zero.
  45  */
  46 module_param(sol_compat, int, 0);
  47 MODULE_PARM_DESC(sol_compat, 
  48                  "Disables documented functionality omitted from Solaris driver");
  49 
  50 MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
  51 MODULE_DESCRIPTION("7-Segment Display driver for Sun Microsystems CP1400/1500");
  52 MODULE_LICENSE("GPL");
  53 MODULE_SUPPORTED_DEVICE("d7s");
  54 
  55 struct d7s {
  56         void __iomem    *regs;
  57         bool            flipped;
  58 };
  59 struct d7s *d7s_device;
  60 
  61 /*
  62  * Register block address- see header for details
  63  * -----------------------------------------
  64  * | DP | ALARM | FLIP | 4 | 3 | 2 | 1 | 0 |
  65  * -----------------------------------------
  66  *
  67  * DP           - Toggles decimal point on/off 
  68  * ALARM        - Toggles "Alarm" LED green/red
  69  * FLIP         - Inverts display for upside-down mounted board
  70  * bits 0-4     - 7-segment display contents
  71  */
  72 static atomic_t d7s_users = ATOMIC_INIT(0);
  73 
  74 static int d7s_open(struct inode *inode, struct file *f)
  75 {
  76         if (D7S_MINOR != iminor(inode))
  77                 return -ENODEV;
  78         atomic_inc(&d7s_users);
  79         return 0;
  80 }
  81 
  82 static int d7s_release(struct inode *inode, struct file *f)
  83 {
  84         /* Reset flipped state to OBP default only if
  85          * no other users have the device open and we
  86          * are not operating in solaris-compat mode
  87          */
  88         if (atomic_dec_and_test(&d7s_users) && !sol_compat) {
  89                 struct d7s *p = d7s_device;
  90                 u8 regval = 0;
  91 
  92                 regval = readb(p->regs);
  93                 if (p->flipped)
  94                         regval |= D7S_FLIP;
  95                 else
  96                         regval &= ~D7S_FLIP;
  97                 writeb(regval, p->regs);
  98         }
  99 
 100         return 0;
 101 }
 102 
 103 static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 104 {
 105         struct d7s *p = d7s_device;
 106         u8 regs = readb(p->regs);
 107         int error = 0;
 108         u8 ireg = 0;
 109 
 110         if (D7S_MINOR != iminor(file_inode(file)))
 111                 return -ENODEV;
 112 
 113         mutex_lock(&d7s_mutex);
 114         switch (cmd) {
 115         case D7SIOCWR:
 116                 /* assign device register values we mask-out D7S_FLIP
 117                  * if in sol_compat mode
 118                  */
 119                 if (get_user(ireg, (int __user *) arg)) {
 120                         error = -EFAULT;
 121                         break;
 122                 }
 123                 if (sol_compat) {
 124                         if (regs & D7S_FLIP)
 125                                 ireg |= D7S_FLIP;
 126                         else
 127                                 ireg &= ~D7S_FLIP;
 128                 }
 129                 writeb(ireg, p->regs);
 130                 break;
 131 
 132         case D7SIOCRD:
 133                 /* retrieve device register values
 134                  * NOTE: Solaris implementation returns D7S_FLIP bit
 135                  * as toggled by user, even though it does not honor it.
 136                  * This driver will not misinform you about the state
 137                  * of your hardware while in sol_compat mode
 138                  */
 139                 if (put_user(regs, (int __user *) arg)) {
 140                         error = -EFAULT;
 141                         break;
 142                 }
 143                 break;
 144 
 145         case D7SIOCTM:
 146                 /* toggle device mode-- flip display orientation */
 147                 regs ^= D7S_FLIP;
 148                 writeb(regs, p->regs);
 149                 break;
 150         }
 151         mutex_unlock(&d7s_mutex);
 152 
 153         return error;
 154 }
 155 
 156 static const struct file_operations d7s_fops = {
 157         .owner =                THIS_MODULE,
 158         .unlocked_ioctl =       d7s_ioctl,
 159         .compat_ioctl =         d7s_ioctl,
 160         .open =                 d7s_open,
 161         .release =              d7s_release,
 162         .llseek = noop_llseek,
 163 };
 164 
 165 static struct miscdevice d7s_miscdev = {
 166         .minor          = D7S_MINOR,
 167         .name           = DRIVER_NAME,
 168         .fops           = &d7s_fops
 169 };
 170 
 171 static int d7s_probe(struct platform_device *op)
 172 {
 173         struct device_node *opts;
 174         int err = -EINVAL;
 175         struct d7s *p;
 176         u8 regs;
 177 
 178         if (d7s_device)
 179                 goto out;
 180 
 181         p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
 182         err = -ENOMEM;
 183         if (!p)
 184                 goto out;
 185 
 186         p->regs = of_ioremap(&op->resource[0], 0, sizeof(u8), "d7s");
 187         if (!p->regs) {
 188                 printk(KERN_ERR PFX "Cannot map chip registers\n");
 189                 goto out_free;
 190         }
 191 
 192         err = misc_register(&d7s_miscdev);
 193         if (err) {
 194                 printk(KERN_ERR PFX "Unable to acquire miscdevice minor %i\n",
 195                        D7S_MINOR);
 196                 goto out_iounmap;
 197         }
 198 
 199         /* OBP option "d7s-flipped?" is honored as default for the
 200          * device, and reset default when detached
 201          */
 202         regs = readb(p->regs);
 203         opts = of_find_node_by_path("/options");
 204         if (opts &&
 205             of_get_property(opts, "d7s-flipped?", NULL))
 206                 p->flipped = true;
 207 
 208         if (p->flipped)
 209                 regs |= D7S_FLIP;
 210         else
 211                 regs &= ~D7S_FLIP;
 212 
 213         writeb(regs,  p->regs);
 214 
 215         printk(KERN_INFO PFX "7-Segment Display%pOF at [%s:0x%llx] %s\n",
 216                op->dev.of_node,
 217                (regs & D7S_FLIP) ? " (FLIPPED)" : "",
 218                op->resource[0].start,
 219                sol_compat ? "in sol_compat mode" : "");
 220 
 221         dev_set_drvdata(&op->dev, p);
 222         d7s_device = p;
 223         err = 0;
 224         of_node_put(opts);
 225 
 226 out:
 227         return err;
 228 
 229 out_iounmap:
 230         of_iounmap(&op->resource[0], p->regs, sizeof(u8));
 231 
 232 out_free:
 233         goto out;
 234 }
 235 
 236 static int d7s_remove(struct platform_device *op)
 237 {
 238         struct d7s *p = dev_get_drvdata(&op->dev);
 239         u8 regs = readb(p->regs);
 240 
 241         /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
 242         if (sol_compat) {
 243                 if (p->flipped)
 244                         regs |= D7S_FLIP;
 245                 else
 246                         regs &= ~D7S_FLIP;
 247                 writeb(regs, p->regs);
 248         }
 249 
 250         misc_deregister(&d7s_miscdev);
 251         of_iounmap(&op->resource[0], p->regs, sizeof(u8));
 252 
 253         return 0;
 254 }
 255 
 256 static const struct of_device_id d7s_match[] = {
 257         {
 258                 .name = "display7seg",
 259         },
 260         {},
 261 };
 262 MODULE_DEVICE_TABLE(of, d7s_match);
 263 
 264 static struct platform_driver d7s_driver = {
 265         .driver = {
 266                 .name = DRIVER_NAME,
 267                 .of_match_table = d7s_match,
 268         },
 269         .probe          = d7s_probe,
 270         .remove         = d7s_remove,
 271 };
 272 
 273 module_platform_driver(d7s_driver);

/* [<][>][^][v][top][bottom][index][help] */