linux/drivers/staging/comedi/drivers/ni_6527.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/ni_6527.c
   3    driver for National Instruments PCI-6527
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 1999,2002,2003 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    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21
  22*/
  23/*
  24Driver: ni_6527
  25Description: National Instruments 6527
  26Author: ds
  27Status: works
  28Devices: [National Instruments] PCI-6527 (ni6527), PXI-6527
  29Updated: Sat, 25 Jan 2003 13:24:40 -0800
  30
  31
  32*/
  33
  34/*
  35   Manuals (available from ftp://ftp.natinst.com/support/manuals)
  36
  37        370106b.pdf     6527 Register Level Programmer Manual
  38
  39 */
  40
  41#define DEBUG 1
  42#define DEBUG_FLAGS
  43
  44#include <linux/interrupt.h>
  45#include "../comedidev.h"
  46
  47#include "mite.h"
  48
  49#define NI6527_DIO_SIZE 4096
  50#define NI6527_MITE_SIZE 4096
  51
  52#define Port_Register(x)                        (0x00+(x))
  53#define ID_Register                             0x06
  54
  55#define Clear_Register                          0x07
  56#define ClrEdge                         0x08
  57#define ClrOverflow                     0x04
  58#define ClrFilter                       0x02
  59#define ClrInterval                     0x01
  60
  61#define Filter_Interval(x)                      (0x08+(x))
  62#define Filter_Enable(x)                        (0x0c+(x))
  63
  64#define Change_Status                           0x14
  65#define MasterInterruptStatus           0x04
  66#define Overflow                        0x02
  67#define EdgeStatus                      0x01
  68
  69#define Master_Interrupt_Control                0x15
  70#define FallingEdgeIntEnable            0x10
  71#define RisingEdgeIntEnable             0x08
  72#define MasterInterruptEnable           0x04
  73#define OverflowIntEnable               0x02
  74#define EdgeIntEnable                   0x01
  75
  76#define Rising_Edge_Detection_Enable(x)         (0x018+(x))
  77#define Falling_Edge_Detection_Enable(x)        (0x020+(x))
  78
  79static int ni6527_attach(struct comedi_device *dev,
  80                         struct comedi_devconfig *it);
  81static int ni6527_detach(struct comedi_device *dev);
  82static struct comedi_driver driver_ni6527 = {
  83        .driver_name = "ni6527",
  84        .module = THIS_MODULE,
  85        .attach = ni6527_attach,
  86        .detach = ni6527_detach,
  87};
  88
  89struct ni6527_board {
  90
  91        int dev_id;
  92        const char *name;
  93};
  94
  95static const struct ni6527_board ni6527_boards[] = {
  96        {
  97         .dev_id = 0x2b20,
  98         .name = "pci-6527",
  99         },
 100        {
 101         .dev_id = 0x2b10,
 102         .name = "pxi-6527",
 103         },
 104};
 105
 106#define n_ni6527_boards ARRAY_SIZE(ni6527_boards)
 107#define this_board ((const struct ni6527_board *)dev->board_ptr)
 108
 109static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = {
 110        {
 111        PCI_VENDOR_ID_NATINST, 0x2b10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 112        PCI_VENDOR_ID_NATINST, 0x2b20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 113        0}
 114};
 115
 116MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
 117
 118struct ni6527_private {
 119        struct mite_struct *mite;
 120        unsigned int filter_interval;
 121        unsigned int filter_enable;
 122};
 123
 124#define devpriv ((struct ni6527_private *)dev->private)
 125
 126static int ni6527_find_device(struct comedi_device *dev, int bus, int slot);
 127
 128static int ni6527_di_insn_config(struct comedi_device *dev,
 129                                 struct comedi_subdevice *s,
 130                                 struct comedi_insn *insn, unsigned int *data)
 131{
 132        int chan = CR_CHAN(insn->chanspec);
 133        unsigned int interval;
 134
 135        if (insn->n != 2)
 136                return -EINVAL;
 137
 138        if (data[0] != INSN_CONFIG_FILTER)
 139                return -EINVAL;
 140
 141        if (data[1]) {
 142                interval = (data[1] + 100) / 200;
 143                data[1] = interval * 200;
 144
 145                if (interval != devpriv->filter_interval) {
 146                        writeb(interval & 0xff,
 147                               devpriv->mite->daq_io_addr + Filter_Interval(0));
 148                        writeb((interval >> 8) & 0xff,
 149                               devpriv->mite->daq_io_addr + Filter_Interval(1));
 150                        writeb((interval >> 16) & 0x0f,
 151                               devpriv->mite->daq_io_addr + Filter_Interval(2));
 152
 153                        writeb(ClrInterval,
 154                               devpriv->mite->daq_io_addr + Clear_Register);
 155
 156                        devpriv->filter_interval = interval;
 157                }
 158
 159                devpriv->filter_enable |= 1 << chan;
 160        } else {
 161                devpriv->filter_enable &= ~(1 << chan);
 162        }
 163
 164        writeb(devpriv->filter_enable,
 165               devpriv->mite->daq_io_addr + Filter_Enable(0));
 166        writeb(devpriv->filter_enable >> 8,
 167               devpriv->mite->daq_io_addr + Filter_Enable(1));
 168        writeb(devpriv->filter_enable >> 16,
 169               devpriv->mite->daq_io_addr + Filter_Enable(2));
 170
 171        return 2;
 172}
 173
 174static int ni6527_di_insn_bits(struct comedi_device *dev,
 175                               struct comedi_subdevice *s,
 176                               struct comedi_insn *insn, unsigned int *data)
 177{
 178        if (insn->n != 2)
 179                return -EINVAL;
 180
 181        data[1] = readb(devpriv->mite->daq_io_addr + Port_Register(0));
 182        data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(1)) << 8;
 183        data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(2)) << 16;
 184
 185        return 2;
 186}
 187
 188static int ni6527_do_insn_bits(struct comedi_device *dev,
 189                               struct comedi_subdevice *s,
 190                               struct comedi_insn *insn, unsigned int *data)
 191{
 192        if (insn->n != 2)
 193                return -EINVAL;
 194        if (data[0]) {
 195                s->state &= ~data[0];
 196                s->state |= (data[0] & data[1]);
 197
 198                /* The open relay state on the board cooresponds to 1,
 199                 * but in Comedi, it is represented by 0. */
 200                if (data[0] & 0x0000ff) {
 201                        writeb((s->state ^ 0xff),
 202                               devpriv->mite->daq_io_addr + Port_Register(3));
 203                }
 204                if (data[0] & 0x00ff00) {
 205                        writeb((s->state >> 8) ^ 0xff,
 206                               devpriv->mite->daq_io_addr + Port_Register(4));
 207                }
 208                if (data[0] & 0xff0000) {
 209                        writeb((s->state >> 16) ^ 0xff,
 210                               devpriv->mite->daq_io_addr + Port_Register(5));
 211                }
 212        }
 213        data[1] = s->state;
 214
 215        return 2;
 216}
 217
 218static irqreturn_t ni6527_interrupt(int irq, void *d)
 219{
 220        struct comedi_device *dev = d;
 221        struct comedi_subdevice *s = dev->subdevices + 2;
 222        unsigned int status;
 223
 224        status = readb(devpriv->mite->daq_io_addr + Change_Status);
 225        if ((status & MasterInterruptStatus) == 0)
 226                return IRQ_NONE;
 227        if ((status & EdgeStatus) == 0)
 228                return IRQ_NONE;
 229
 230        writeb(ClrEdge | ClrOverflow,
 231               devpriv->mite->daq_io_addr + Clear_Register);
 232
 233        comedi_buf_put(s->async, 0);
 234        s->async->events |= COMEDI_CB_EOS;
 235        comedi_event(dev, s);
 236        return IRQ_HANDLED;
 237}
 238
 239static int ni6527_intr_cmdtest(struct comedi_device *dev,
 240                               struct comedi_subdevice *s,
 241                               struct comedi_cmd *cmd)
 242{
 243        int err = 0;
 244        int tmp;
 245
 246        /* step 1: make sure trigger sources are trivially valid */
 247
 248        tmp = cmd->start_src;
 249        cmd->start_src &= TRIG_NOW;
 250        if (!cmd->start_src || tmp != cmd->start_src)
 251                err++;
 252
 253        tmp = cmd->scan_begin_src;
 254        cmd->scan_begin_src &= TRIG_OTHER;
 255        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 256                err++;
 257
 258        tmp = cmd->convert_src;
 259        cmd->convert_src &= TRIG_FOLLOW;
 260        if (!cmd->convert_src || tmp != cmd->convert_src)
 261                err++;
 262
 263        tmp = cmd->scan_end_src;
 264        cmd->scan_end_src &= TRIG_COUNT;
 265        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 266                err++;
 267
 268        tmp = cmd->stop_src;
 269        cmd->stop_src &= TRIG_COUNT;
 270        if (!cmd->stop_src || tmp != cmd->stop_src)
 271                err++;
 272
 273        if (err)
 274                return 1;
 275
 276        /* step 2: make sure trigger sources are unique and mutually compatible */
 277
 278        if (err)
 279                return 2;
 280
 281        /* step 3: make sure arguments are trivially compatible */
 282
 283        if (cmd->start_arg != 0) {
 284                cmd->start_arg = 0;
 285                err++;
 286        }
 287        if (cmd->scan_begin_arg != 0) {
 288                cmd->scan_begin_arg = 0;
 289                err++;
 290        }
 291        if (cmd->convert_arg != 0) {
 292                cmd->convert_arg = 0;
 293                err++;
 294        }
 295
 296        if (cmd->scan_end_arg != 1) {
 297                cmd->scan_end_arg = 1;
 298                err++;
 299        }
 300        if (cmd->stop_arg != 0) {
 301                cmd->stop_arg = 0;
 302                err++;
 303        }
 304
 305        if (err)
 306                return 3;
 307
 308        /* step 4: fix up any arguments */
 309
 310        if (err)
 311                return 4;
 312
 313        return 0;
 314}
 315
 316static int ni6527_intr_cmd(struct comedi_device *dev,
 317                           struct comedi_subdevice *s)
 318{
 319        /* struct comedi_cmd *cmd = &s->async->cmd; */
 320
 321        writeb(ClrEdge | ClrOverflow,
 322               devpriv->mite->daq_io_addr + Clear_Register);
 323        writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
 324               MasterInterruptEnable | EdgeIntEnable,
 325               devpriv->mite->daq_io_addr + Master_Interrupt_Control);
 326
 327        return 0;
 328}
 329
 330static int ni6527_intr_cancel(struct comedi_device *dev,
 331                              struct comedi_subdevice *s)
 332{
 333        writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
 334
 335        return 0;
 336}
 337
 338static int ni6527_intr_insn_bits(struct comedi_device *dev,
 339                                 struct comedi_subdevice *s,
 340                                 struct comedi_insn *insn, unsigned int *data)
 341{
 342        if (insn->n < 1)
 343                return -EINVAL;
 344
 345        data[1] = 0;
 346        return 2;
 347}
 348
 349static int ni6527_intr_insn_config(struct comedi_device *dev,
 350                                   struct comedi_subdevice *s,
 351                                   struct comedi_insn *insn, unsigned int *data)
 352{
 353        if (insn->n < 1)
 354                return -EINVAL;
 355        if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
 356                return -EINVAL;
 357
 358        writeb(data[1],
 359               devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(0));
 360        writeb(data[1] >> 8,
 361               devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(1));
 362        writeb(data[1] >> 16,
 363               devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(2));
 364
 365        writeb(data[2],
 366               devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(0));
 367        writeb(data[2] >> 8,
 368               devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(1));
 369        writeb(data[2] >> 16,
 370               devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(2));
 371
 372        return 2;
 373}
 374
 375static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 376{
 377        struct comedi_subdevice *s;
 378        int ret;
 379
 380        printk("comedi%d: ni6527:", dev->minor);
 381
 382        ret = alloc_private(dev, sizeof(struct ni6527_private));
 383        if (ret < 0)
 384                return ret;
 385
 386        ret = ni6527_find_device(dev, it->options[0], it->options[1]);
 387        if (ret < 0)
 388                return ret;
 389
 390        ret = mite_setup(devpriv->mite);
 391        if (ret < 0) {
 392                printk("error setting up mite\n");
 393                return ret;
 394        }
 395
 396        dev->board_name = this_board->name;
 397        printk(" %s", dev->board_name);
 398
 399        printk(" ID=0x%02x", readb(devpriv->mite->daq_io_addr + ID_Register));
 400
 401        ret = alloc_subdevices(dev, 3);
 402        if (ret < 0)
 403                return ret;
 404
 405        s = dev->subdevices + 0;
 406        s->type = COMEDI_SUBD_DI;
 407        s->subdev_flags = SDF_READABLE;
 408        s->n_chan = 24;
 409        s->range_table = &range_digital;
 410        s->maxdata = 1;
 411        s->insn_config = ni6527_di_insn_config;
 412        s->insn_bits = ni6527_di_insn_bits;
 413
 414        s = dev->subdevices + 1;
 415        s->type = COMEDI_SUBD_DO;
 416        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 417        s->n_chan = 24;
 418        s->range_table = &range_unknown;        /* FIXME: actually conductance */
 419        s->maxdata = 1;
 420        s->insn_bits = ni6527_do_insn_bits;
 421
 422        s = dev->subdevices + 2;
 423        dev->read_subdev = s;
 424        s->type = COMEDI_SUBD_DI;
 425        s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 426        s->n_chan = 1;
 427        s->range_table = &range_unknown;
 428        s->maxdata = 1;
 429        s->do_cmdtest = ni6527_intr_cmdtest;
 430        s->do_cmd = ni6527_intr_cmd;
 431        s->cancel = ni6527_intr_cancel;
 432        s->insn_bits = ni6527_intr_insn_bits;
 433        s->insn_config = ni6527_intr_insn_config;
 434
 435        writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(0));
 436        writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(1));
 437        writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(2));
 438
 439        writeb(ClrEdge | ClrOverflow | ClrFilter | ClrInterval,
 440               devpriv->mite->daq_io_addr + Clear_Register);
 441        writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
 442
 443        ret = request_irq(mite_irq(devpriv->mite), ni6527_interrupt,
 444                          IRQF_SHARED, "ni6527", dev);
 445        if (ret < 0) {
 446                printk(" irq not available");
 447        } else
 448                dev->irq = mite_irq(devpriv->mite);
 449
 450        printk("\n");
 451
 452        return 0;
 453}
 454
 455static int ni6527_detach(struct comedi_device *dev)
 456{
 457        if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr) {
 458                writeb(0x00,
 459                       devpriv->mite->daq_io_addr + Master_Interrupt_Control);
 460        }
 461
 462        if (dev->irq) {
 463                free_irq(dev->irq, dev);
 464        }
 465
 466        if (devpriv && devpriv->mite) {
 467                mite_unsetup(devpriv->mite);
 468        }
 469
 470        return 0;
 471}
 472
 473static int ni6527_find_device(struct comedi_device *dev, int bus, int slot)
 474{
 475        struct mite_struct *mite;
 476        int i;
 477
 478        for (mite = mite_devices; mite; mite = mite->next) {
 479                if (mite->used)
 480                        continue;
 481                if (bus || slot) {
 482                        if (bus != mite->pcidev->bus->number ||
 483                            slot != PCI_SLOT(mite->pcidev->devfn))
 484                                continue;
 485                }
 486                for (i = 0; i < n_ni6527_boards; i++) {
 487                        if (mite_device_id(mite) == ni6527_boards[i].dev_id) {
 488                                dev->board_ptr = ni6527_boards + i;
 489                                devpriv->mite = mite;
 490                                return 0;
 491                        }
 492                }
 493        }
 494        printk("no device found\n");
 495        mite_list_devices();
 496        return -EIO;
 497}
 498
 499COMEDI_PCI_INITCLEANUP(driver_ni6527, ni6527_pci_table);
 500