linux/drivers/staging/comedi/drivers/ssv_dnp.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/ssv_dnp.c
   3    generic comedi driver for SSV Embedded Systems' DIL/Net-PCs
   4    Copyright (C) 2001 Robert Schwebel <robert@schwebel.de>
   5
   6    COMEDI - Linux Control and Measurement Device Interface
   7    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   8
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 2 of the License, or
  12    (at your option) any later version.
  13
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18
  19    You should have received a copy of the GNU General Public License
  20    along with this program; if not, write to the Free Software
  21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22
  23*/
  24/*
  25Driver: ssv_dnp
  26Description: SSV Embedded Systems DIL/Net-PC
  27Author: Robert Schwebel <robert@schwebel.de>
  28Devices: [SSV Embedded Systems] DIL/Net-PC 1486 (dnp-1486)
  29Status: unknown
  30*/
  31
  32/* include files ----------------------------------------------------------- */
  33
  34#include "../comedidev.h"
  35
  36/* Some global definitions: the registers of the DNP ----------------------- */
  37/*                                                                           */
  38/* For port A and B the mode register has bits corresponding to the output   */
  39/* pins, where Bit-N = 0 -> input, Bit-N = 1 -> output. Note that bits       */
  40/* 4 to 7 correspond to pin 0..3 for port C data register. Ensure that bits  */
  41/* 0..3 remain unchanged! For details about Port C Mode Register see         */
  42/* the remarks in dnp_insn_config() below.                                   */
  43
  44#define CSCIR 0x22              /* Chip Setup and Control Index Register     */
  45#define CSCDR 0x23              /* Chip Setup and Control Data Register      */
  46#define PAMR  0xa5              /* Port A Mode Register                      */
  47#define PADR  0xa9              /* Port A Data Register                      */
  48#define PBMR  0xa4              /* Port B Mode Register                      */
  49#define PBDR  0xa8              /* Port B Data Register                      */
  50#define PCMR  0xa3              /* Port C Mode Register                      */
  51#define PCDR  0xa7              /* Port C Data Register                      */
  52
  53/* This data structure holds information about the supported boards -------- */
  54
  55struct dnp_board {
  56        const char *name;
  57        int ai_chans;
  58        int ai_bits;
  59        int have_dio;
  60};
  61
  62/* ------------------------------------------------------------------------- */
  63/* The insn_bits interface allows packed reading/writing of DIO channels.    */
  64/* The comedi core can convert between insn_bits and insn_read/write, so you */
  65/* are able to use these instructions as well.                               */
  66/* ------------------------------------------------------------------------- */
  67
  68static int dnp_dio_insn_bits(struct comedi_device *dev,
  69                             struct comedi_subdevice *s,
  70                             struct comedi_insn *insn, unsigned int *data)
  71{
  72        /* The insn data is a mask in data[0] and the new data in data[1],   */
  73        /* each channel cooresponding to a bit.                              */
  74
  75        /* Ports A and B are straight forward: each bit corresponds to an    */
  76        /* output pin with the same order. Port C is different: bits 0...3   */
  77        /* correspond to bits 4...7 of the output register (PCDR).           */
  78
  79        if (data[0]) {
  80
  81                outb(PADR, CSCIR);
  82                outb((inb(CSCDR)
  83                      & ~(u8) (data[0] & 0x0000FF))
  84                     | (u8) (data[1] & 0x0000FF), CSCDR);
  85
  86                outb(PBDR, CSCIR);
  87                outb((inb(CSCDR)
  88                      & ~(u8) ((data[0] & 0x00FF00) >> 8))
  89                     | (u8) ((data[1] & 0x00FF00) >> 8), CSCDR);
  90
  91                outb(PCDR, CSCIR);
  92                outb((inb(CSCDR)
  93                      & ~(u8) ((data[0] & 0x0F0000) >> 12))
  94                     | (u8) ((data[1] & 0x0F0000) >> 12), CSCDR);
  95        }
  96
  97        /* on return, data[1] contains the value of the digital input lines. */
  98        outb(PADR, CSCIR);
  99        data[0] = inb(CSCDR);
 100        outb(PBDR, CSCIR);
 101        data[0] += inb(CSCDR) << 8;
 102        outb(PCDR, CSCIR);
 103        data[0] += ((inb(CSCDR) & 0xF0) << 12);
 104
 105        return insn->n;
 106
 107}
 108
 109/* ------------------------------------------------------------------------- */
 110/* Configure the direction of the bidirectional digital i/o pins. chanspec   */
 111/* contains the channel to be changed and data[0] contains either            */
 112/* COMEDI_INPUT or COMEDI_OUTPUT.                                            */
 113/* ------------------------------------------------------------------------- */
 114
 115static int dnp_dio_insn_config(struct comedi_device *dev,
 116                               struct comedi_subdevice *s,
 117                               struct comedi_insn *insn, unsigned int *data)
 118{
 119
 120        u8 register_buffer;
 121
 122        /* reduces chanspec to lower 16 bits */
 123        int chan = CR_CHAN(insn->chanspec);
 124
 125        switch (data[0]) {
 126        case INSN_CONFIG_DIO_OUTPUT:
 127        case INSN_CONFIG_DIO_INPUT:
 128                break;
 129        case INSN_CONFIG_DIO_QUERY:
 130                data[1] =
 131                    (inb(CSCDR) & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
 132                return insn->n;
 133                break;
 134        default:
 135                return -EINVAL;
 136                break;
 137        }
 138        /* Test: which port does the channel belong to?                       */
 139
 140        /* We have to pay attention with port C: this is the meaning of PCMR: */
 141        /* Bit in PCMR:              7 6 5 4 3 2 1 0                          */
 142        /* Corresponding port C pin: d 3 d 2 d 1 d 0   d= don't touch         */
 143
 144        if ((chan >= 0) && (chan <= 7)) {
 145                /* this is port A */
 146                outb(PAMR, CSCIR);
 147        } else if ((chan >= 8) && (chan <= 15)) {
 148                /* this is port B */
 149                chan -= 8;
 150                outb(PBMR, CSCIR);
 151        } else if ((chan >= 16) && (chan <= 19)) {
 152                /* this is port C; multiplication with 2 brings bits into     */
 153                /* correct position for PCMR!                                 */
 154                chan -= 16;
 155                chan *= 2;
 156                outb(PCMR, CSCIR);
 157        } else {
 158                return -EINVAL;
 159        }
 160
 161        /* read 'old' direction of the port and set bits (out=1, in=0)        */
 162        register_buffer = inb(CSCDR);
 163        if (data[0] == COMEDI_OUTPUT)
 164                register_buffer |= (1 << chan);
 165        else
 166                register_buffer &= ~(1 << chan);
 167
 168        outb(register_buffer, CSCDR);
 169
 170        return 1;
 171
 172}
 173
 174static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 175{
 176        const struct dnp_board *board = comedi_board(dev);
 177        struct comedi_subdevice *s;
 178        int ret;
 179
 180        dev->board_name = board->name;
 181
 182        ret = comedi_alloc_subdevices(dev, 1);
 183        if (ret)
 184                return ret;
 185
 186        s = &dev->subdevices[0];
 187        /* digital i/o subdevice                                             */
 188        s->type = COMEDI_SUBD_DIO;
 189        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 190        s->n_chan = 20;
 191        s->maxdata = 1;
 192        s->range_table = &range_digital;
 193        s->insn_bits = dnp_dio_insn_bits;
 194        s->insn_config = dnp_dio_insn_config;
 195
 196        /* We use the I/O ports 0x22,0x23 and 0xa3-0xa9, which are always
 197         * allocated for the primary 8259, so we don't need to allocate them
 198         * ourselves. */
 199
 200        /* configure all ports as input (default)                            */
 201        outb(PAMR, CSCIR);
 202        outb(0x00, CSCDR);
 203        outb(PBMR, CSCIR);
 204        outb(0x00, CSCDR);
 205        outb(PCMR, CSCIR);
 206        outb((inb(CSCDR) & 0xAA), CSCDR);
 207
 208        dev_info(dev->class_dev, "%s: attached\n", dev->board_name);
 209        return 1;
 210}
 211
 212static void dnp_detach(struct comedi_device *dev)
 213{
 214        outb(PAMR, CSCIR);
 215        outb(0x00, CSCDR);
 216        outb(PBMR, CSCIR);
 217        outb(0x00, CSCDR);
 218        outb(PCMR, CSCIR);
 219        outb((inb(CSCDR) & 0xAA), CSCDR);
 220}
 221
 222static const struct dnp_board dnp_boards[] = {
 223        {
 224                .name           = "dnp-1486",
 225                .ai_chans       = 16,
 226                .ai_bits        = 12,
 227                .have_dio       = 1,
 228        },
 229};
 230
 231static struct comedi_driver dnp_driver = {
 232        .driver_name    = "ssv_dnp",
 233        .module         = THIS_MODULE,
 234        .attach         = dnp_attach,
 235        .detach         = dnp_detach,
 236        .board_name     = &dnp_boards[0].name,
 237        .offset         = sizeof(struct dnp_board),
 238        .num_names      = ARRAY_SIZE(dnp_boards),
 239};
 240module_comedi_driver(dnp_driver);
 241
 242MODULE_AUTHOR("Comedi http://www.comedi.org");
 243MODULE_DESCRIPTION("Comedi low-level driver");
 244MODULE_LICENSE("GPL");
 245