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