linux/drivers/staging/comedi/drivers/c6xdigio.c
<<
>>
Prefs
   1/*
   2 * c6xdigio.c
   3 * Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
   4 * http://web.archive.org/web/%2A/http://robot0.ge.uiuc.edu/~spong/mecha/
   5 *
   6 * COMEDI - Linux Control and Measurement Device Interface
   7 * Copyright (C) 1999 Dan Block
   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
  20/*
  21 * Driver: c6xdigio
  22 * Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
  23 * Author: Dan Block
  24 * Status: unknown
  25 * Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
  26 * Updated: Sun Nov 20 20:18:34 EST 2005
  27 *
  28 * Configuration Options:
  29 *      [0] - base address
  30 */
  31
  32#include <linux/kernel.h>
  33#include <linux/module.h>
  34#include <linux/sched.h>
  35#include <linux/mm.h>
  36#include <linux/errno.h>
  37#include <linux/interrupt.h>
  38#include <linux/timex.h>
  39#include <linux/timer.h>
  40#include <linux/io.h>
  41#include <linux/pnp.h>
  42
  43#include "../comedidev.h"
  44
  45/*
  46 * Register I/O map
  47 */
  48#define C6XDIGIO_DATA_REG       0x00
  49#define C6XDIGIO_DATA_CHAN(x)   (((x) + 1) << 4)
  50#define C6XDIGIO_DATA_PWM       BIT(5)
  51#define C6XDIGIO_DATA_ENCODER   BIT(6)
  52#define C6XDIGIO_STATUS_REG     0x01
  53#define C6XDIGIO_CTRL_REG       0x02
  54
  55#define C6XDIGIO_TIME_OUT 20
  56
  57static int c6xdigio_chk_status(struct comedi_device *dev, unsigned long context)
  58{
  59        unsigned int status;
  60        int timeout = 0;
  61
  62        do {
  63                status = inb(dev->iobase + C6XDIGIO_STATUS_REG);
  64                if ((status & 0x80) != context)
  65                        return 0;
  66                timeout++;
  67        } while  (timeout < C6XDIGIO_TIME_OUT);
  68
  69        return -EBUSY;
  70}
  71
  72static int c6xdigio_write_data(struct comedi_device *dev,
  73                               unsigned int val, unsigned int status)
  74{
  75        outb_p(val, dev->iobase + C6XDIGIO_DATA_REG);
  76        return c6xdigio_chk_status(dev, status);
  77}
  78
  79static int c6xdigio_get_encoder_bits(struct comedi_device *dev,
  80                                     unsigned int *bits,
  81                                     unsigned int cmd,
  82                                     unsigned int status)
  83{
  84        unsigned int val;
  85
  86        val = inb(dev->iobase + C6XDIGIO_STATUS_REG);
  87        val >>= 3;
  88        val &= 0x07;
  89
  90        *bits = val;
  91
  92        return c6xdigio_write_data(dev, cmd, status);
  93}
  94
  95static void c6xdigio_pwm_write(struct comedi_device *dev,
  96                               unsigned int chan, unsigned int val)
  97{
  98        unsigned int cmd = C6XDIGIO_DATA_PWM | C6XDIGIO_DATA_CHAN(chan);
  99        unsigned int bits;
 100
 101        if (val > 498)
 102                val = 498;
 103        if (val < 2)
 104                val = 2;
 105
 106        bits = (val >> 0) & 0x03;
 107        c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
 108        bits = (val >> 2) & 0x03;
 109        c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
 110        bits = (val >> 4) & 0x03;
 111        c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
 112        bits = (val >> 6) & 0x03;
 113        c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
 114        bits = (val >> 8) & 0x03;
 115        c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
 116
 117        c6xdigio_write_data(dev, 0x00, 0x80);
 118}
 119
 120static int c6xdigio_encoder_read(struct comedi_device *dev,
 121                                 unsigned int chan)
 122{
 123        unsigned int cmd = C6XDIGIO_DATA_ENCODER | C6XDIGIO_DATA_CHAN(chan);
 124        unsigned int val = 0;
 125        unsigned int bits;
 126
 127        c6xdigio_write_data(dev, cmd, 0x00);
 128
 129        c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
 130        val |= (bits << 0);
 131
 132        c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
 133        val |= (bits << 3);
 134
 135        c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
 136        val |= (bits << 6);
 137
 138        c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
 139        val |= (bits << 9);
 140
 141        c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
 142        val |= (bits << 12);
 143
 144        c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
 145        val |= (bits << 15);
 146
 147        c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
 148        val |= (bits << 18);
 149
 150        c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
 151        val |= (bits << 21);
 152
 153        c6xdigio_write_data(dev, 0x00, 0x80);
 154
 155        return val;
 156}
 157
 158static int c6xdigio_pwm_insn_write(struct comedi_device *dev,
 159                                   struct comedi_subdevice *s,
 160                                   struct comedi_insn *insn,
 161                                   unsigned int *data)
 162{
 163        unsigned int chan = CR_CHAN(insn->chanspec);
 164        unsigned int val = (s->state >> (16 * chan)) & 0xffff;
 165        int i;
 166
 167        for (i = 0; i < insn->n; i++) {
 168                val = data[i];
 169                c6xdigio_pwm_write(dev, chan, val);
 170        }
 171
 172        /*
 173         * There are only 2 PWM channels and they have a maxdata of 500.
 174         * Instead of allocating private data to save the values in for
 175         * readback this driver just packs the values for the two channels
 176         * in the s->state.
 177         */
 178        s->state &= (0xffff << (16 * chan));
 179        s->state |= (val << (16 * chan));
 180
 181        return insn->n;
 182}
 183
 184static int c6xdigio_pwm_insn_read(struct comedi_device *dev,
 185                                  struct comedi_subdevice *s,
 186                                  struct comedi_insn *insn,
 187                                  unsigned int *data)
 188{
 189        unsigned int chan = CR_CHAN(insn->chanspec);
 190        unsigned int val;
 191        int i;
 192
 193        val = (s->state >> (16 * chan)) & 0xffff;
 194
 195        for (i = 0; i < insn->n; i++)
 196                data[i] = val;
 197
 198        return insn->n;
 199}
 200
 201static int c6xdigio_encoder_insn_read(struct comedi_device *dev,
 202                                      struct comedi_subdevice *s,
 203                                      struct comedi_insn *insn,
 204                                      unsigned int *data)
 205{
 206        unsigned int chan = CR_CHAN(insn->chanspec);
 207        unsigned int val;
 208        int i;
 209
 210        for (i = 0; i < insn->n; i++) {
 211                val = c6xdigio_encoder_read(dev, chan);
 212
 213                /* munge two's complement value to offset binary */
 214                data[i] = comedi_offset_munge(s, val);
 215        }
 216
 217        return insn->n;
 218}
 219
 220static void c6xdigio_init(struct comedi_device *dev)
 221{
 222        /* Initialize the PWM */
 223        c6xdigio_write_data(dev, 0x70, 0x00);
 224        c6xdigio_write_data(dev, 0x74, 0x80);
 225        c6xdigio_write_data(dev, 0x70, 0x00);
 226        c6xdigio_write_data(dev, 0x00, 0x80);
 227
 228        /* Reset the encoders */
 229        c6xdigio_write_data(dev, 0x68, 0x00);
 230        c6xdigio_write_data(dev, 0x6c, 0x80);
 231        c6xdigio_write_data(dev, 0x68, 0x00);
 232        c6xdigio_write_data(dev, 0x00, 0x80);
 233}
 234
 235static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
 236        /* Standard LPT Printer Port */
 237        {.id = "PNP0400", .driver_data = 0},
 238        /* ECP Printer Port */
 239        {.id = "PNP0401", .driver_data = 0},
 240        {}
 241};
 242
 243static struct pnp_driver c6xdigio_pnp_driver = {
 244        .name = "c6xdigio",
 245        .id_table = c6xdigio_pnp_tbl,
 246};
 247
 248static int c6xdigio_attach(struct comedi_device *dev,
 249                           struct comedi_devconfig *it)
 250{
 251        struct comedi_subdevice *s;
 252        int ret;
 253
 254        ret = comedi_request_region(dev, it->options[0], 0x03);
 255        if (ret)
 256                return ret;
 257
 258        ret = comedi_alloc_subdevices(dev, 2);
 259        if (ret)
 260                return ret;
 261
 262        /*  Make sure that PnP ports get activated */
 263        pnp_register_driver(&c6xdigio_pnp_driver);
 264
 265        s = &dev->subdevices[0];
 266        /* pwm output subdevice */
 267        s->type         = COMEDI_SUBD_PWM;
 268        s->subdev_flags = SDF_WRITABLE;
 269        s->n_chan       = 2;
 270        s->maxdata      = 500;
 271        s->range_table  = &range_unknown;
 272        s->insn_write   = c6xdigio_pwm_insn_write;
 273        s->insn_read    = c6xdigio_pwm_insn_read;
 274
 275        s = &dev->subdevices[1];
 276        /* encoder (counter) subdevice */
 277        s->type         = COMEDI_SUBD_COUNTER;
 278        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 279        s->n_chan       = 2;
 280        s->maxdata      = 0xffffff;
 281        s->range_table  = &range_unknown;
 282        s->insn_read    = c6xdigio_encoder_insn_read;
 283
 284        /*  I will call this init anyway but more than likely the DSP board */
 285        /*  will not be connected when device driver is loaded. */
 286        c6xdigio_init(dev);
 287
 288        return 0;
 289}
 290
 291static void c6xdigio_detach(struct comedi_device *dev)
 292{
 293        comedi_legacy_detach(dev);
 294        pnp_unregister_driver(&c6xdigio_pnp_driver);
 295}
 296
 297static struct comedi_driver c6xdigio_driver = {
 298        .driver_name    = "c6xdigio",
 299        .module         = THIS_MODULE,
 300        .attach         = c6xdigio_attach,
 301        .detach         = c6xdigio_detach,
 302};
 303module_comedi_driver(c6xdigio_driver);
 304
 305MODULE_AUTHOR("Comedi http://www.comedi.org");
 306MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card");
 307MODULE_LICENSE("GPL");
 308