linux/drivers/staging/comedi/drivers/8255.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/8255.c
   3 * Driver for 8255
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19/*
  20 * Driver: 8255
  21 * Description: generic 8255 support
  22 * Devices: [standard] 8255 (8255)
  23 * Author: ds
  24 * Status: works
  25 * Updated: Fri,  7 Jun 2002 12:56:45 -0700
  26 *
  27 * The classic in digital I/O.  The 8255 appears in Comedi as a single
  28 * digital I/O subdevice with 24 channels.  The channel 0 corresponds
  29 * to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
  30 * 7.  Direction configuration is done in blocks, with channels 0-7,
  31 * 8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
  32 * supported is mode 0.
  33 *
  34 * You should enable compilation this driver if you plan to use a board
  35 * that has an 8255 chip.  For multifunction boards, the main driver will
  36 * configure the 8255 subdevice automatically.
  37 *
  38 * This driver also works independently with ISA and PCI cards that
  39 * directly map the 8255 registers to I/O ports, including cards with
  40 * multiple 8255 chips.  To configure the driver for such a card, the
  41 * option list should be a list of the I/O port bases for each of the
  42 * 8255 chips.  For example,
  43 *
  44 *   comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
  45 *
  46 * Note that most PCI 8255 boards do NOT work with this driver, and
  47 * need a separate driver as a wrapper.  For those that do work, the
  48 * I/O port base address can be found in the output of 'lspci -v'.
  49 */
  50
  51#include <linux/module.h>
  52#include "../comedidev.h"
  53
  54#include "8255.h"
  55
  56struct subdev_8255_private {
  57        unsigned long regbase;
  58        int (*io)(struct comedi_device *, int, int, int, unsigned long);
  59};
  60
  61static int subdev_8255_io(struct comedi_device *dev,
  62                          int dir, int port, int data, unsigned long regbase)
  63{
  64        if (dir) {
  65                outb(data, dev->iobase + regbase + port);
  66                return 0;
  67        }
  68        return inb(dev->iobase + regbase + port);
  69}
  70
  71static int subdev_8255_mmio(struct comedi_device *dev,
  72                            int dir, int port, int data, unsigned long regbase)
  73{
  74        if (dir) {
  75                writeb(data, dev->mmio + regbase + port);
  76                return 0;
  77        }
  78        return readb(dev->mmio + regbase + port);
  79}
  80
  81static int subdev_8255_insn(struct comedi_device *dev,
  82                            struct comedi_subdevice *s,
  83                            struct comedi_insn *insn,
  84                            unsigned int *data)
  85{
  86        struct subdev_8255_private *spriv = s->private;
  87        unsigned long regbase = spriv->regbase;
  88        unsigned int mask;
  89        unsigned int v;
  90
  91        mask = comedi_dio_update_state(s, data);
  92        if (mask) {
  93                if (mask & 0xff)
  94                        spriv->io(dev, 1, I8255_DATA_A_REG,
  95                                  s->state & 0xff, regbase);
  96                if (mask & 0xff00)
  97                        spriv->io(dev, 1, I8255_DATA_B_REG,
  98                                  (s->state >> 8) & 0xff, regbase);
  99                if (mask & 0xff0000)
 100                        spriv->io(dev, 1, I8255_DATA_C_REG,
 101                                  (s->state >> 16) & 0xff, regbase);
 102        }
 103
 104        v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase);
 105        v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8);
 106        v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16);
 107
 108        data[1] = v;
 109
 110        return insn->n;
 111}
 112
 113static void subdev_8255_do_config(struct comedi_device *dev,
 114                                  struct comedi_subdevice *s)
 115{
 116        struct subdev_8255_private *spriv = s->private;
 117        unsigned long regbase = spriv->regbase;
 118        int config;
 119
 120        config = I8255_CTRL_CW;
 121        /* 1 in io_bits indicates output, 1 in config indicates input */
 122        if (!(s->io_bits & 0x0000ff))
 123                config |= I8255_CTRL_A_IO;
 124        if (!(s->io_bits & 0x00ff00))
 125                config |= I8255_CTRL_B_IO;
 126        if (!(s->io_bits & 0x0f0000))
 127                config |= I8255_CTRL_C_LO_IO;
 128        if (!(s->io_bits & 0xf00000))
 129                config |= I8255_CTRL_C_HI_IO;
 130
 131        spriv->io(dev, 1, I8255_CTRL_REG, config, regbase);
 132}
 133
 134static int subdev_8255_insn_config(struct comedi_device *dev,
 135                                   struct comedi_subdevice *s,
 136                                   struct comedi_insn *insn,
 137                                   unsigned int *data)
 138{
 139        unsigned int chan = CR_CHAN(insn->chanspec);
 140        unsigned int mask;
 141        int ret;
 142
 143        if (chan < 8)
 144                mask = 0x0000ff;
 145        else if (chan < 16)
 146                mask = 0x00ff00;
 147        else if (chan < 20)
 148                mask = 0x0f0000;
 149        else
 150                mask = 0xf00000;
 151
 152        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 153        if (ret)
 154                return ret;
 155
 156        subdev_8255_do_config(dev, s);
 157
 158        return insn->n;
 159}
 160
 161static int __subdev_8255_init(struct comedi_device *dev,
 162                              struct comedi_subdevice *s,
 163                              int (*io)(struct comedi_device *,
 164                                        int, int, int, unsigned long),
 165                              unsigned long regbase,
 166                              bool is_mmio)
 167{
 168        struct subdev_8255_private *spriv;
 169
 170        spriv = comedi_alloc_spriv(s, sizeof(*spriv));
 171        if (!spriv)
 172                return -ENOMEM;
 173
 174        if (io)
 175                spriv->io = io;
 176        else if (is_mmio)
 177                spriv->io = subdev_8255_mmio;
 178        else
 179                spriv->io = subdev_8255_io;
 180        spriv->regbase  = regbase;
 181
 182        s->type         = COMEDI_SUBD_DIO;
 183        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 184        s->n_chan       = 24;
 185        s->range_table  = &range_digital;
 186        s->maxdata      = 1;
 187        s->insn_bits    = subdev_8255_insn;
 188        s->insn_config  = subdev_8255_insn_config;
 189
 190        subdev_8255_do_config(dev, s);
 191
 192        return 0;
 193}
 194
 195/**
 196 * subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255
 197 * @dev: comedi device owning subdevice
 198 * @s: comedi subdevice to initialize
 199 * @io: (optional) register I/O call-back function
 200 * @regbase: offset of 8255 registers from dev->iobase, or call-back context
 201 *
 202 * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
 203 *
 204 * If the optional I/O call-back function is provided, its prototype is of
 205 * the following form:
 206 *
 207 *   int my_8255_callback(struct comedi_device *dev,
 208 *                        struct comedi_subdevice *s, int dir, int port,
 209 *                        int data, unsigned long regbase);
 210 *
 211 * where 'dev', 's', and 'regbase' match the values passed to this function,
 212 * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
 213 * is the direction (0 for read, 1 for write) and 'data' is the value to be
 214 * written.  It should return 0 if writing or the value read if reading.
 215 *
 216 * If the optional I/O call-back function is not provided, an internal
 217 * call-back function is used which uses consecutive I/O port addresses
 218 * starting at dev->iobase + regbase.
 219 *
 220 * Return: -ENOMEM if failed to allocate memory, zero on success.
 221 */
 222int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
 223                     int (*io)(struct comedi_device *,
 224                               int, int, int, unsigned long),
 225                     unsigned long regbase)
 226{
 227        return __subdev_8255_init(dev, s, io, regbase, false);
 228}
 229EXPORT_SYMBOL_GPL(subdev_8255_init);
 230
 231/**
 232 * subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255
 233 * @dev: comedi device owning subdevice
 234 * @s: comedi subdevice to initialize
 235 * @io: (optional) register I/O call-back function
 236 * @regbase: offset of 8255 registers from dev->mmio, or call-back context
 237 *
 238 * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
 239 *
 240 * If the optional I/O call-back function is provided, its prototype is of
 241 * the following form:
 242 *
 243 *   int my_8255_callback(struct comedi_device *dev,
 244 *                        struct comedi_subdevice *s, int dir, int port,
 245 *                        int data, unsigned long regbase);
 246 *
 247 * where 'dev', 's', and 'regbase' match the values passed to this function,
 248 * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
 249 * is the direction (0 for read, 1 for write) and 'data' is the value to be
 250 * written.  It should return 0 if writing or the value read if reading.
 251 *
 252 * If the optional I/O call-back function is not provided, an internal
 253 * call-back function is used which uses consecutive MMIO virtual addresses
 254 * starting at dev->mmio + regbase.
 255 *
 256 * Return: -ENOMEM if failed to allocate memory, zero on success.
 257 */
 258int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
 259                        int (*io)(struct comedi_device *,
 260                                  int, int, int, unsigned long),
 261                        unsigned long regbase)
 262{
 263        return __subdev_8255_init(dev, s, io, regbase, true);
 264}
 265EXPORT_SYMBOL_GPL(subdev_8255_mm_init);
 266
 267/*
 268 * Start of the 8255 standalone device
 269 */
 270
 271static int dev_8255_attach(struct comedi_device *dev,
 272                           struct comedi_devconfig *it)
 273{
 274        struct comedi_subdevice *s;
 275        unsigned long iobase;
 276        int ret;
 277        int i;
 278
 279        for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
 280                iobase = it->options[i];
 281                if (!iobase)
 282                        break;
 283        }
 284        if (i == 0) {
 285                dev_warn(dev->class_dev, "no devices specified\n");
 286                return -EINVAL;
 287        }
 288
 289        ret = comedi_alloc_subdevices(dev, i);
 290        if (ret)
 291                return ret;
 292
 293        for (i = 0; i < dev->n_subdevices; i++) {
 294                s = &dev->subdevices[i];
 295                iobase = it->options[i];
 296
 297                /*
 298                 * __comedi_request_region() does not set dev->iobase.
 299                 *
 300                 * For 8255 devices that are manually attached using
 301                 * comedi_config, the 'iobase' is the actual I/O port
 302                 * base address of the chip.
 303                 */
 304                ret = __comedi_request_region(dev, iobase, I8255_SIZE);
 305                if (ret) {
 306                        s->type = COMEDI_SUBD_UNUSED;
 307                } else {
 308                        ret = subdev_8255_init(dev, s, NULL, iobase);
 309                        if (ret)
 310                                return ret;
 311                }
 312        }
 313
 314        return 0;
 315}
 316
 317static void dev_8255_detach(struct comedi_device *dev)
 318{
 319        struct comedi_subdevice *s;
 320        struct subdev_8255_private *spriv;
 321        int i;
 322
 323        for (i = 0; i < dev->n_subdevices; i++) {
 324                s = &dev->subdevices[i];
 325                if (s->type != COMEDI_SUBD_UNUSED) {
 326                        spriv = s->private;
 327                        release_region(spriv->regbase, I8255_SIZE);
 328                }
 329        }
 330}
 331
 332static struct comedi_driver dev_8255_driver = {
 333        .driver_name    = "8255",
 334        .module         = THIS_MODULE,
 335        .attach         = dev_8255_attach,
 336        .detach         = dev_8255_detach,
 337};
 338module_comedi_driver(dev_8255_driver);
 339
 340MODULE_AUTHOR("Comedi http://www.comedi.org");
 341MODULE_DESCRIPTION("Comedi low-level driver");
 342MODULE_LICENSE("GPL");
 343