linux/drivers/staging/comedi/drivers/c6xdigio.c
<<
>>
Prefs
   1/*
   2   comedi/drivers/c6xdigio.c
   3
   4   Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
   5   (http://robot0.ge.uiuc.edu/~spong/mecha/)
   6
   7   COMEDI - Linux Control and Measurement Device Interface
   8   Copyright (C) 1999 Dan Block
   9
  10   This program is free software; you can redistribute it and/or modify
  11   it under the terms of the GNU General Public License as published by
  12   the Free Software Foundation; either version 2 of the License, or
  13   (at your option) any later version.
  14
  15   This program is distributed in the hope that it will be useful,
  16   but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18   GNU General Public License for more details.
  19
  20   You should have received a copy of the GNU General Public License
  21   along with this program; if not, write to the Free Software
  22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23
  24 */
  25/*
  26Driver: c6xdigio
  27Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
  28Author: Dan Block
  29Status: unknown
  30Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
  31Updated: Sun Nov 20 20:18:34 EST 2005
  32
  33This driver will not work with a 2.4 kernel.
  34http://robot0.ge.uiuc.edu/~spong/mecha/
  35
  36*/
  37
  38#include <linux/kernel.h>
  39#include <linux/module.h>
  40#include <linux/sched.h>
  41#include <linux/mm.h>
  42#include <linux/errno.h>
  43#include <linux/ioport.h>
  44#include <linux/delay.h>
  45#include <linux/interrupt.h>
  46#include <linux/timex.h>
  47#include <linux/timer.h>
  48#include <linux/io.h>
  49#include <linux/pnp.h>
  50
  51#include "../comedidev.h"
  52
  53static u8 ReadByteFromHwPort(unsigned long addr)
  54{
  55        u8 result = inb(addr);
  56        return result;
  57}
  58
  59static void WriteByteToHwPort(unsigned long addr, u8 val)
  60{
  61        outb_p(val, addr);
  62}
  63
  64#define C6XDIGIO_SIZE 3
  65
  66/*
  67 * port offsets
  68 */
  69#define C6XDIGIO_PARALLEL_DATA 0
  70#define C6XDIGIO_PARALLEL_STATUS 1
  71#define C6XDIGIO_PARALLEL_CONTROL 2
  72struct pwmbitstype {
  73        unsigned sb0:2;
  74        unsigned sb1:2;
  75        unsigned sb2:2;
  76        unsigned sb3:2;
  77        unsigned sb4:2;
  78};
  79union pwmcmdtype {
  80        unsigned cmd;           /*  assuming here that int is 32bit */
  81        struct pwmbitstype bits;
  82};
  83struct encbitstype {
  84        unsigned sb0:3;
  85        unsigned sb1:3;
  86        unsigned sb2:3;
  87        unsigned sb3:3;
  88        unsigned sb4:3;
  89        unsigned sb5:3;
  90        unsigned sb6:3;
  91        unsigned sb7:3;
  92};
  93union encvaluetype {
  94        unsigned value;
  95        struct encbitstype bits;
  96};
  97
  98#define C6XDIGIO_TIME_OUT 20
  99
 100static void C6X_pwmInit(unsigned long baseAddr)
 101{
 102        int timeout = 0;
 103
 104/* printk("Inside C6X_pwmInit\n"); */
 105
 106        WriteByteToHwPort(baseAddr, 0x70);
 107        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
 108               && (timeout < C6XDIGIO_TIME_OUT)) {
 109                timeout++;
 110        }
 111
 112        WriteByteToHwPort(baseAddr, 0x74);
 113        timeout = 0;
 114        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
 115               && (timeout < C6XDIGIO_TIME_OUT)) {
 116                timeout++;
 117        }
 118
 119        WriteByteToHwPort(baseAddr, 0x70);
 120        timeout = 0;
 121        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
 122               && (timeout < C6XDIGIO_TIME_OUT)) {
 123                timeout++;
 124        }
 125
 126        WriteByteToHwPort(baseAddr, 0x0);
 127        timeout = 0;
 128        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
 129               && (timeout < C6XDIGIO_TIME_OUT)) {
 130                timeout++;
 131        }
 132
 133}
 134
 135static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
 136{
 137        unsigned ppcmd;
 138        union pwmcmdtype pwm;
 139        int timeout = 0;
 140        unsigned tmp;
 141
 142        /* printk("Inside C6X_pwmOutput\n"); */
 143
 144        pwm.cmd = value;
 145        if (pwm.cmd > 498)
 146                pwm.cmd = 498;
 147        if (pwm.cmd < 2)
 148                pwm.cmd = 2;
 149
 150        if (channel == 0) {
 151                ppcmd = 0x28;
 152        } else {                /*  if channel == 1 */
 153                ppcmd = 0x30;
 154        }                       /* endif */
 155
 156        WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
 157        tmp = ReadByteFromHwPort(baseAddr + 1);
 158        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 159                tmp = ReadByteFromHwPort(baseAddr + 1);
 160                timeout++;
 161        }
 162
 163        WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
 164        timeout = 0;
 165        tmp = ReadByteFromHwPort(baseAddr + 1);
 166        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 167                tmp = ReadByteFromHwPort(baseAddr + 1);
 168                timeout++;
 169        }
 170
 171        WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
 172        tmp = ReadByteFromHwPort(baseAddr + 1);
 173        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 174                tmp = ReadByteFromHwPort(baseAddr + 1);
 175                timeout++;
 176        }
 177
 178        WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
 179        timeout = 0;
 180        tmp = ReadByteFromHwPort(baseAddr + 1);
 181        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 182                tmp = ReadByteFromHwPort(baseAddr + 1);
 183                timeout++;
 184        }
 185
 186        WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
 187        tmp = ReadByteFromHwPort(baseAddr + 1);
 188        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 189                tmp = ReadByteFromHwPort(baseAddr + 1);
 190                timeout++;
 191        }
 192
 193        WriteByteToHwPort(baseAddr, 0x0);
 194        timeout = 0;
 195        tmp = ReadByteFromHwPort(baseAddr + 1);
 196        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 197                tmp = ReadByteFromHwPort(baseAddr + 1);
 198                timeout++;
 199        }
 200
 201}
 202
 203static int C6X_encInput(unsigned long baseAddr, unsigned channel)
 204{
 205        unsigned ppcmd;
 206        union encvaluetype enc;
 207        int timeout = 0;
 208        int tmp;
 209
 210        /* printk("Inside C6X_encInput\n"); */
 211
 212        enc.value = 0;
 213        if (channel == 0)
 214                ppcmd = 0x48;
 215        else
 216                ppcmd = 0x50;
 217
 218        WriteByteToHwPort(baseAddr, ppcmd);
 219        tmp = ReadByteFromHwPort(baseAddr + 1);
 220        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 221                tmp = ReadByteFromHwPort(baseAddr + 1);
 222                timeout++;
 223        }
 224
 225        enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 226        WriteByteToHwPort(baseAddr, ppcmd + 0x4);
 227        timeout = 0;
 228        tmp = ReadByteFromHwPort(baseAddr + 1);
 229        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 230                tmp = ReadByteFromHwPort(baseAddr + 1);
 231                timeout++;
 232        }
 233        enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 234        WriteByteToHwPort(baseAddr, ppcmd);
 235        timeout = 0;
 236        tmp = ReadByteFromHwPort(baseAddr + 1);
 237        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 238                tmp = ReadByteFromHwPort(baseAddr + 1);
 239                timeout++;
 240        }
 241        enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 242        WriteByteToHwPort(baseAddr, ppcmd + 0x4);
 243        timeout = 0;
 244        tmp = ReadByteFromHwPort(baseAddr + 1);
 245        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 246                tmp = ReadByteFromHwPort(baseAddr + 1);
 247                timeout++;
 248        }
 249        enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 250        WriteByteToHwPort(baseAddr, ppcmd);
 251        timeout = 0;
 252        tmp = ReadByteFromHwPort(baseAddr + 1);
 253        while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
 254                tmp = ReadByteFromHwPort(baseAddr + 1);
 255                timeout++;
 256        }
 257        enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 258        WriteByteToHwPort(baseAddr, ppcmd + 0x4);
 259        timeout = 0;
 260        tmp = ReadByteFromHwPort(baseAddr + 1);
 261        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 262                tmp = ReadByteFromHwPort(baseAddr + 1);
 263                timeout++;
 264        }
 265        enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 266        WriteByteToHwPort(baseAddr, ppcmd);
 267        timeout = 0;
 268        tmp = ReadByteFromHwPort(baseAddr + 1);
 269        while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
 270                tmp = ReadByteFromHwPort(baseAddr + 1);
 271                timeout++;
 272        }
 273        enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 274        WriteByteToHwPort(baseAddr, ppcmd + 0x4);
 275        timeout = 0;
 276        tmp = ReadByteFromHwPort(baseAddr + 1);
 277        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 278                tmp = ReadByteFromHwPort(baseAddr + 1);
 279                timeout++;
 280        }
 281        enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
 282        WriteByteToHwPort(baseAddr, ppcmd);
 283        timeout = 0;
 284        tmp = ReadByteFromHwPort(baseAddr + 1);
 285        while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
 286                tmp = ReadByteFromHwPort(baseAddr + 1);
 287                timeout++;
 288        }
 289
 290        WriteByteToHwPort(baseAddr, 0x0);
 291        timeout = 0;
 292        tmp = ReadByteFromHwPort(baseAddr + 1);
 293        while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
 294                tmp = ReadByteFromHwPort(baseAddr + 1);
 295                timeout++;
 296        }
 297
 298        return enc.value ^ 0x800000;
 299}
 300
 301static void C6X_encResetAll(unsigned long baseAddr)
 302{
 303        unsigned timeout = 0;
 304
 305/* printk("Inside C6X_encResetAll\n"); */
 306
 307        WriteByteToHwPort(baseAddr, 0x68);
 308        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
 309               && (timeout < C6XDIGIO_TIME_OUT)) {
 310                timeout++;
 311        }
 312        WriteByteToHwPort(baseAddr, 0x6C);
 313        timeout = 0;
 314        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
 315               && (timeout < C6XDIGIO_TIME_OUT)) {
 316                timeout++;
 317        }
 318        WriteByteToHwPort(baseAddr, 0x68);
 319        timeout = 0;
 320        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
 321               && (timeout < C6XDIGIO_TIME_OUT)) {
 322                timeout++;
 323        }
 324        WriteByteToHwPort(baseAddr, 0x0);
 325        timeout = 0;
 326        while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
 327               && (timeout < C6XDIGIO_TIME_OUT)) {
 328                timeout++;
 329        }
 330}
 331
 332static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
 333                                   struct comedi_subdevice *s,
 334                                   struct comedi_insn *insn, unsigned int *data)
 335{
 336        printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
 337        return insn->n;
 338}
 339
 340static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
 341                                    struct comedi_subdevice *s,
 342                                    struct comedi_insn *insn,
 343                                    unsigned int *data)
 344{
 345        int i;
 346        int chan = CR_CHAN(insn->chanspec);
 347
 348        /*   printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
 349        for (i = 0; i < insn->n; i++) {
 350                C6X_pwmOutput(dev->iobase, chan, data[i]);
 351                /*    devpriv->ao_readback[chan] = data[i]; */
 352        }
 353        return i;
 354}
 355
 356/* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
 357/* struct comedi_subdevice *s, */
 358/* struct comedi_insn *insn, */
 359/* unsigned int *data) */
 360/* { */
 361/* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
 362/* return insn->n; */
 363/* } */
 364
 365/* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
 366/* struct comedi_subdevice *s, */
 367/* struct comedi_insn *insn, */
 368/* unsigned int *data) */
 369/* { */
 370/* int i; */
 371/* int chan = CR_CHAN(insn->chanspec); */
 372      /*  *//* C6X_encResetAll( dev->iobase ); */
 373      /*  *//* return insn->n; */
 374/* } */
 375
 376static int c6xdigio_ei_insn_read(struct comedi_device *dev,
 377                                 struct comedi_subdevice *s,
 378                                 struct comedi_insn *insn, unsigned int *data)
 379{
 380        /*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
 381        int n;
 382        int chan = CR_CHAN(insn->chanspec);
 383
 384        for (n = 0; n < insn->n; n++)
 385                data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
 386
 387        return n;
 388}
 389
 390static void board_init(struct comedi_device *dev)
 391{
 392
 393        /* printk("Inside board_init\n"); */
 394
 395        C6X_pwmInit(dev->iobase);
 396        C6X_encResetAll(dev->iobase);
 397
 398}
 399
 400/*
 401   options[0] - I/O port
 402   options[1] - irq
 403   options[2] - number of encoder chips installed
 404 */
 405
 406static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
 407        /* Standard LPT Printer Port */
 408        {.id = "PNP0400", .driver_data = 0},
 409        /* ECP Printer Port */
 410        {.id = "PNP0401", .driver_data = 0},
 411        {}
 412};
 413
 414static struct pnp_driver c6xdigio_pnp_driver = {
 415        .name = "c6xdigio",
 416        .id_table = c6xdigio_pnp_tbl,
 417};
 418
 419static int c6xdigio_attach(struct comedi_device *dev,
 420                           struct comedi_devconfig *it)
 421{
 422        int result = 0;
 423        unsigned long iobase;
 424        unsigned int irq;
 425        struct comedi_subdevice *s;
 426
 427        iobase = it->options[0];
 428        printk(KERN_DEBUG "comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
 429        if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
 430                printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
 431                return -EIO;
 432        }
 433        dev->iobase = iobase;
 434        dev->board_name = "c6xdigio";
 435
 436        result = comedi_alloc_subdevices(dev, 2);
 437        if (result)
 438                return result;
 439
 440        /*  Make sure that PnP ports get activated */
 441        pnp_register_driver(&c6xdigio_pnp_driver);
 442
 443        irq = it->options[1];
 444        if (irq > 0)
 445                printk(KERN_DEBUG "comedi%d: irq = %u ignored\n",
 446                                dev->minor, irq);
 447        else if (irq == 0)
 448                printk(KERN_DEBUG "comedi%d: no irq\n", dev->minor);
 449
 450        s = &dev->subdevices[0];
 451        /* pwm output subdevice */
 452        s->type = COMEDI_SUBD_AO;       /*  Not sure what to put here */
 453        s->subdev_flags = SDF_WRITEABLE;
 454        s->n_chan = 2;
 455        /*      s->trig[0] = c6xdigio_pwmo; */
 456        s->insn_read = c6xdigio_pwmo_insn_read;
 457        s->insn_write = c6xdigio_pwmo_insn_write;
 458        s->maxdata = 500;
 459        s->range_table = &range_bipolar10;      /*  A suitable lie */
 460
 461        s = &dev->subdevices[1];
 462        /* encoder (counter) subdevice */
 463        s->type = COMEDI_SUBD_COUNTER;
 464        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 465        s->n_chan = 2;
 466        /* s->trig[0] = c6xdigio_ei; */
 467        s->insn_read = c6xdigio_ei_insn_read;
 468        s->maxdata = 0xffffff;
 469        s->range_table = &range_unknown;
 470
 471        /*      s = &dev->subdevices[2]; */
 472        /* pwm output subdevice */
 473        /*      s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here */
 474        /*      s->subdev_flags = SDF_WRITEABLE; */
 475        /*      s->n_chan = 1; */
 476        /*      s->trig[0] = c6xdigio_ei_init; */
 477        /*      s->insn_read = c6xdigio_ei_init_insn_read; */
 478        /*      s->insn_write = c6xdigio_ei_init_insn_write; */
 479        /*      s->maxdata = 0xFFFF;  // Really just a don't care */
 480        /*      s->range_table = &range_unknown; // Not sure what to put here */
 481
 482        /*  I will call this init anyway but more than likely the DSP board */
 483        /*  will not be connected when device driver is loaded. */
 484        board_init(dev);
 485
 486        return 0;
 487}
 488
 489static void c6xdigio_detach(struct comedi_device *dev)
 490{
 491        if (dev->iobase)
 492                release_region(dev->iobase, C6XDIGIO_SIZE);
 493        if (dev->irq)
 494                free_irq(dev->irq, dev);
 495        pnp_unregister_driver(&c6xdigio_pnp_driver);
 496}
 497
 498static struct comedi_driver c6xdigio_driver = {
 499        .driver_name    = "c6xdigio",
 500        .module         = THIS_MODULE,
 501        .attach         = c6xdigio_attach,
 502        .detach         = c6xdigio_detach,
 503};
 504module_comedi_driver(c6xdigio_driver);
 505
 506MODULE_AUTHOR("Comedi http://www.comedi.org");
 507MODULE_DESCRIPTION("Comedi low-level driver");
 508MODULE_LICENSE("GPL");
 509
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.