Files
msm-5.15/drivers/comedi/drivers/ni_pcidio.c
Greg Kroah-Hartman 8ffdff6a8c staging: comedi: move out of staging directory
The comedi code came into the kernel back in 2008, but traces its
lifetime to much much earlier.  It's been polished and buffed and
there's really nothing preventing it from being part of the "real"
portion of the kernel.

So move it to drivers/comedi/ as it belongs there.

Many thanks to the hundreds of developers who did the work to make this
happen.

Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
Link: https://lore.kernel.org/r/YHauop4u3sP6lz8j@kroah.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-04-15 09:26:25 +02:00

1011 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Comedi driver for National Instruments PCI-DIO-32HS
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1999,2002 David A. Schleef <ds@schleef.org>
*/
/*
* Driver: ni_pcidio
* Description: National Instruments PCI-DIO32HS, PCI-6533
* Author: ds
* Status: works
* Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio)
* [National Instruments] PXI-6533, PCI-6533 (pxi-6533)
* [National Instruments] PCI-6534 (pci-6534)
* Updated: Mon, 09 Jan 2012 14:27:23 +0000
*
* The DIO32HS board appears as one subdevice, with 32 channels. Each
* channel is individually I/O configurable. The channel order is 0=A0,
* 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0. The driver only supports simple
* digital I/O; no handshaking is supported.
*
* DMA mostly works for the PCI-DIO32HS, but only in timed input mode.
*
* The PCI-DIO-32HS/PCI-6533 has a configurable external trigger. Setting
* scan_begin_arg to 0 or CR_EDGE triggers on the leading edge. Setting
* scan_begin_arg to CR_INVERT or (CR_EDGE | CR_INVERT) triggers on the
* trailing edge.
*
* This driver could be easily modified to support AT-MIO32HS and AT-MIO96.
*
* The PCI-6534 requires a firmware upload after power-up to work, the
* firmware data and instructions for loading it with comedi_config
* it are contained in the comedi_nonfree_firmware tarball available from
* https://www.comedi.org
*/
#define USE_DMA
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include "../comedi_pci.h"
#include "mite.h"
/* defines for the PCI-DIO-32HS */
#define WINDOW_ADDRESS 4 /* W */
#define INTERRUPT_AND_WINDOW_STATUS 4 /* R */
#define INT_STATUS_1 BIT(0)
#define INT_STATUS_2 BIT(1)
#define WINDOW_ADDRESS_STATUS_MASK 0x7c
#define MASTER_DMA_AND_INTERRUPT_CONTROL 5 /* W */
#define INTERRUPT_LINE(x) ((x) & 3)
#define OPEN_INT BIT(2)
#define GROUP_STATUS 5 /* R */
#define DATA_LEFT BIT(0)
#define REQ BIT(2)
#define STOP_TRIG BIT(3)
#define GROUP_1_FLAGS 6 /* R */
#define GROUP_2_FLAGS 7 /* R */
#define TRANSFER_READY BIT(0)
#define COUNT_EXPIRED BIT(1)
#define WAITED BIT(5)
#define PRIMARY_TC BIT(6)
#define SECONDARY_TC BIT(7)
/* #define SerialRose */
/* #define ReqRose */
/* #define Paused */
#define GROUP_1_FIRST_CLEAR 6 /* W */
#define GROUP_2_FIRST_CLEAR 7 /* W */
#define CLEAR_WAITED BIT(3)
#define CLEAR_PRIMARY_TC BIT(4)
#define CLEAR_SECONDARY_TC BIT(5)
#define DMA_RESET BIT(6)
#define FIFO_RESET BIT(7)
#define CLEAR_ALL 0xf8
#define GROUP_1_FIFO 8 /* W */
#define GROUP_2_FIFO 12 /* W */
#define TRANSFER_COUNT 20
#define CHIP_ID_D 24
#define CHIP_ID_I 25
#define CHIP_ID_O 26
#define CHIP_VERSION 27
#define PORT_IO(x) (28 + (x))
#define PORT_PIN_DIRECTIONS(x) (32 + (x))
#define PORT_PIN_MASK(x) (36 + (x))
#define PORT_PIN_POLARITIES(x) (40 + (x))
#define MASTER_CLOCK_ROUTING 45
#define RTSI_CLOCKING(x) (((x) & 3) << 4)
#define GROUP_1_SECOND_CLEAR 46 /* W */
#define GROUP_2_SECOND_CLEAR 47 /* W */
#define CLEAR_EXPIRED BIT(0)
#define PORT_PATTERN(x) (48 + (x))
#define DATA_PATH 64
#define FIFO_ENABLE_A BIT(0)
#define FIFO_ENABLE_B BIT(1)
#define FIFO_ENABLE_C BIT(2)
#define FIFO_ENABLE_D BIT(3)
#define FUNNELING(x) (((x) & 3) << 4)
#define GROUP_DIRECTION BIT(7)
#define PROTOCOL_REGISTER_1 65
#define OP_MODE PROTOCOL_REGISTER_1
#define RUN_MODE(x) ((x) & 7)
#define NUMBERED BIT(3)
#define PROTOCOL_REGISTER_2 66
#define CLOCK_REG PROTOCOL_REGISTER_2
#define CLOCK_LINE(x) (((x) & 3) << 5)
#define INVERT_STOP_TRIG BIT(7)
#define DATA_LATCHING(x) (((x) & 3) << 5)
#define PROTOCOL_REGISTER_3 67
#define SEQUENCE PROTOCOL_REGISTER_3
#define PROTOCOL_REGISTER_14 68 /* 16 bit */
#define CLOCK_SPEED PROTOCOL_REGISTER_14
#define PROTOCOL_REGISTER_4 70
#define REQ_REG PROTOCOL_REGISTER_4
#define REQ_CONDITIONING(x) (((x) & 7) << 3)
#define PROTOCOL_REGISTER_5 71
#define BLOCK_MODE PROTOCOL_REGISTER_5
#define FIFO_Control 72
#define READY_LEVEL(x) ((x) & 7)
#define PROTOCOL_REGISTER_6 73
#define LINE_POLARITIES PROTOCOL_REGISTER_6
#define INVERT_ACK BIT(0)
#define INVERT_REQ BIT(1)
#define INVERT_CLOCK BIT(2)
#define INVERT_SERIAL BIT(3)
#define OPEN_ACK BIT(4)
#define OPEN_CLOCK BIT(5)
#define PROTOCOL_REGISTER_7 74
#define ACK_SER PROTOCOL_REGISTER_7
#define ACK_LINE(x) (((x) & 3) << 2)
#define EXCHANGE_PINS BIT(7)
#define INTERRUPT_CONTROL 75
/* bits same as flags */
#define DMA_LINE_CONTROL_GROUP1 76
#define DMA_LINE_CONTROL_GROUP2 108
/* channel zero is none */
static inline unsigned int primary_DMAChannel_bits(unsigned int channel)
{
return channel & 0x3;
}
static inline unsigned int secondary_DMAChannel_bits(unsigned int channel)
{
return (channel << 2) & 0xc;
}
#define TRANSFER_SIZE_CONTROL 77
#define TRANSFER_WIDTH(x) ((x) & 3)
#define TRANSFER_LENGTH(x) (((x) & 3) << 3)
#define REQUIRE_R_LEVEL BIT(5)
#define PROTOCOL_REGISTER_15 79
#define DAQ_OPTIONS PROTOCOL_REGISTER_15
#define START_SOURCE(x) ((x) & 0x3)
#define INVERT_START BIT(2)
#define STOP_SOURCE(x) (((x) & 0x3) << 3)
#define REQ_START BIT(6)
#define PRE_START BIT(7)
#define PATTERN_DETECTION 81
#define DETECTION_METHOD BIT(0)
#define INVERT_MATCH BIT(1)
#define IE_PATTERN_DETECTION BIT(2)
#define PROTOCOL_REGISTER_9 82
#define REQ_DELAY PROTOCOL_REGISTER_9
#define PROTOCOL_REGISTER_10 83
#define REQ_NOT_DELAY PROTOCOL_REGISTER_10
#define PROTOCOL_REGISTER_11 84
#define ACK_DELAY PROTOCOL_REGISTER_11
#define PROTOCOL_REGISTER_12 85
#define ACK_NOT_DELAY PROTOCOL_REGISTER_12
#define PROTOCOL_REGISTER_13 86
#define DATA_1_DELAY PROTOCOL_REGISTER_13
#define PROTOCOL_REGISTER_8 88 /* 32 bit */
#define START_DELAY PROTOCOL_REGISTER_8
/* Firmware files for PCI-6524 */
#define FW_PCI_6534_MAIN "ni6534a.bin"
#define FW_PCI_6534_SCARAB_DI "niscrb01.bin"
#define FW_PCI_6534_SCARAB_DO "niscrb02.bin"
MODULE_FIRMWARE(FW_PCI_6534_MAIN);
MODULE_FIRMWARE(FW_PCI_6534_SCARAB_DI);
MODULE_FIRMWARE(FW_PCI_6534_SCARAB_DO);
enum pci_6534_firmware_registers { /* 16 bit */
Firmware_Control_Register = 0x100,
Firmware_Status_Register = 0x104,
Firmware_Data_Register = 0x108,
Firmware_Mask_Register = 0x10c,
Firmware_Debug_Register = 0x110,
};
/* main fpga registers (32 bit)*/
enum pci_6534_fpga_registers {
FPGA_Control1_Register = 0x200,
FPGA_Control2_Register = 0x204,
FPGA_Irq_Mask_Register = 0x208,
FPGA_Status_Register = 0x20c,
FPGA_Signature_Register = 0x210,
FPGA_SCALS_Counter_Register = 0x280, /*write-clear */
FPGA_SCAMS_Counter_Register = 0x284, /*write-clear */
FPGA_SCBLS_Counter_Register = 0x288, /*write-clear */
FPGA_SCBMS_Counter_Register = 0x28c, /*write-clear */
FPGA_Temp_Control_Register = 0x2a0,
FPGA_DAR_Register = 0x2a8,
FPGA_ELC_Read_Register = 0x2b8,
FPGA_ELC_Write_Register = 0x2bc,
};
enum FPGA_Control_Bits {
FPGA_Enable_Bit = 0x8000,
};
#define TIMER_BASE 50 /* nanoseconds */
#ifdef USE_DMA
#define INT_EN (COUNT_EXPIRED | WAITED | PRIMARY_TC | SECONDARY_TC)
#else
#define INT_EN (TRANSFER_READY | COUNT_EXPIRED | WAITED \
| PRIMARY_TC | SECONDARY_TC)
#endif
enum nidio_boardid {
BOARD_PCIDIO_32HS,
BOARD_PXI6533,
BOARD_PCI6534,
};
struct nidio_board {
const char *name;
unsigned int uses_firmware:1;
unsigned int dio_speed;
};
static const struct nidio_board nidio_boards[] = {
[BOARD_PCIDIO_32HS] = {
.name = "pci-dio-32hs",
.dio_speed = 50,
},
[BOARD_PXI6533] = {
.name = "pxi-6533",
.dio_speed = 50,
},
[BOARD_PCI6534] = {
.name = "pci-6534",
.uses_firmware = 1,
.dio_speed = 50,
},
};
struct nidio96_private {
struct mite *mite;
int boardtype;
int dio;
unsigned short OP_MODEBits;
struct mite_channel *di_mite_chan;
struct mite_ring *di_mite_ring;
spinlock_t mite_channel_lock;
};
static int ni_pcidio_request_di_mite_channel(struct comedi_device *dev)
{
struct nidio96_private *devpriv = dev->private;
unsigned long flags;
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
BUG_ON(devpriv->di_mite_chan);
devpriv->di_mite_chan =
mite_request_channel_in_range(devpriv->mite,
devpriv->di_mite_ring, 1, 2);
if (!devpriv->di_mite_chan) {
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
dev_err(dev->class_dev, "failed to reserve mite dma channel\n");
return -EBUSY;
}
devpriv->di_mite_chan->dir = COMEDI_INPUT;
writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) |
secondary_DMAChannel_bits(devpriv->di_mite_chan->channel),
dev->mmio + DMA_LINE_CONTROL_GROUP1);
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
return 0;
}
static void ni_pcidio_release_di_mite_channel(struct comedi_device *dev)
{
struct nidio96_private *devpriv = dev->private;
unsigned long flags;
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if (devpriv->di_mite_chan) {
mite_release_channel(devpriv->di_mite_chan);
devpriv->di_mite_chan = NULL;
writeb(primary_DMAChannel_bits(0) |
secondary_DMAChannel_bits(0),
dev->mmio + DMA_LINE_CONTROL_GROUP1);
}
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
}
static int setup_mite_dma(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct nidio96_private *devpriv = dev->private;
int retval;
unsigned long flags;
retval = ni_pcidio_request_di_mite_channel(dev);
if (retval)
return retval;
/* write alloc the entire buffer */
comedi_buf_write_alloc(s, s->async->prealloc_bufsz);
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if (devpriv->di_mite_chan) {
mite_prep_dma(devpriv->di_mite_chan, 32, 32);
mite_dma_arm(devpriv->di_mite_chan);
} else {
retval = -EIO;
}
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
return retval;
}
static int ni_pcidio_poll(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct nidio96_private *devpriv = dev->private;
unsigned long irq_flags;
int count;
spin_lock_irqsave(&dev->spinlock, irq_flags);
spin_lock(&devpriv->mite_channel_lock);
if (devpriv->di_mite_chan)
mite_sync_dma(devpriv->di_mite_chan, s);
spin_unlock(&devpriv->mite_channel_lock);
count = comedi_buf_n_bytes_ready(s);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
return count;
}
static irqreturn_t nidio_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct nidio96_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
struct comedi_async *async = s->async;
unsigned int auxdata;
int flags;
int status;
int work = 0;
/* interrupcions parasites */
if (!dev->attached) {
/* assume it's from another card */
return IRQ_NONE;
}
/* Lock to avoid race with comedi_poll */
spin_lock(&dev->spinlock);
status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS);
flags = readb(dev->mmio + GROUP_1_FLAGS);
spin_lock(&devpriv->mite_channel_lock);
if (devpriv->di_mite_chan) {
mite_ack_linkc(devpriv->di_mite_chan, s, false);
/* XXX need to byteswap sync'ed dma */
}
spin_unlock(&devpriv->mite_channel_lock);
while (status & DATA_LEFT) {
work++;
if (work > 20) {
dev_dbg(dev->class_dev, "too much work in interrupt\n");
writeb(0x00,
dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
break;
}
flags &= INT_EN;
if (flags & TRANSFER_READY) {
while (flags & TRANSFER_READY) {
work++;
if (work > 100) {
dev_dbg(dev->class_dev,
"too much work in interrupt\n");
writeb(0x00, dev->mmio +
MASTER_DMA_AND_INTERRUPT_CONTROL
);
goto out;
}
auxdata = readl(dev->mmio + GROUP_1_FIFO);
comedi_buf_write_samples(s, &auxdata, 1);
flags = readb(dev->mmio + GROUP_1_FLAGS);
}
}
if (flags & COUNT_EXPIRED) {
writeb(CLEAR_EXPIRED, dev->mmio + GROUP_1_SECOND_CLEAR);
async->events |= COMEDI_CB_EOA;
writeb(0x00, dev->mmio + OP_MODE);
break;
} else if (flags & WAITED) {
writeb(CLEAR_WAITED, dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_ERROR;
break;
} else if (flags & PRIMARY_TC) {
writeb(CLEAR_PRIMARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_EOA;
} else if (flags & SECONDARY_TC) {
writeb(CLEAR_SECONDARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_EOA;
}
flags = readb(dev->mmio + GROUP_1_FLAGS);
status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS);
}
out:
comedi_handle_events(dev, s);
#if 0
if (!tag)
writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
#endif
spin_unlock(&dev->spinlock);
return IRQ_HANDLED;
}
static int ni_pcidio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
int ret;
if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) {
const struct nidio_board *board = dev->board_ptr;
/* we don't care about actual channels */
data[1] = board->dio_speed;
data[2] = 0;
return 0;
}
ret = comedi_dio_insn_config(dev, s, insn, data, 0);
if (ret)
return ret;
writel(s->io_bits, dev->mmio + PORT_PIN_DIRECTIONS(0));
return insn->n;
}
static int ni_pcidio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
if (comedi_dio_update_state(s, data))
writel(s->state, dev->mmio + PORT_IO(0));
data[1] = readl(dev->mmio + PORT_IO(0));
return insn->n;
}
static int ni_pcidio_ns_to_timer(int *nanosec, unsigned int flags)
{
int divider, base;
base = TIMER_BASE;
switch (flags & CMDF_ROUND_MASK) {
case CMDF_ROUND_NEAREST:
default:
divider = DIV_ROUND_CLOSEST(*nanosec, base);
break;
case CMDF_ROUND_DOWN:
divider = (*nanosec) / base;
break;
case CMDF_ROUND_UP:
divider = DIV_ROUND_UP(*nanosec, base);
break;
}
*nanosec = base * divider;
return divider;
}
static int ni_pcidio_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int err = 0;
unsigned int arg;
/* Step 1 : check if triggers are trivially valid */
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
err |= comedi_check_trigger_src(&cmd->scan_begin_src,
TRIG_TIMER | TRIG_EXT);
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
/* Step 2a : make sure trigger sources are unique */
err |= comedi_check_trigger_is_unique(cmd->start_src);
err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
err |= comedi_check_trigger_is_unique(cmd->stop_src);
/* Step 2b : and mutually compatible */
if (err)
return 2;
/* Step 3: check if arguments are trivially valid */
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
#define MAX_SPEED (TIMER_BASE) /* in nanoseconds */
if (cmd->scan_begin_src == TRIG_TIMER) {
err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
MAX_SPEED);
/* no minimum speed */
} else {
/* TRIG_EXT */
/* should be level/edge, hi/lo specification here */
if ((cmd->scan_begin_arg & ~(CR_EDGE | CR_INVERT)) != 0) {
cmd->scan_begin_arg &= (CR_EDGE | CR_INVERT);
err |= -EINVAL;
}
}
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len);
if (cmd->stop_src == TRIG_COUNT)
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
else /* TRIG_NONE */
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
if (err)
return 3;
/* step 4: fix up any arguments */
if (cmd->scan_begin_src == TRIG_TIMER) {
arg = cmd->scan_begin_arg;
ni_pcidio_ns_to_timer(&arg, cmd->flags);
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
}
if (err)
return 4;
return 0;
}
static int ni_pcidio_inttrig(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int trig_num)
{
struct nidio96_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
if (trig_num != cmd->start_arg)
return -EINVAL;
writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE);
s->async->inttrig = NULL;
return 1;
}
static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct nidio96_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
/* XXX configure ports for input */
writel(0x0000, dev->mmio + PORT_PIN_DIRECTIONS(0));
if (1) {
/* enable fifos A B C D */
writeb(0x0f, dev->mmio + DATA_PATH);
/* set transfer width a 32 bits */
writeb(TRANSFER_WIDTH(0) | TRANSFER_LENGTH(0),
dev->mmio + TRANSFER_SIZE_CONTROL);
} else {
writeb(0x03, dev->mmio + DATA_PATH);
writeb(TRANSFER_WIDTH(3) | TRANSFER_LENGTH(0),
dev->mmio + TRANSFER_SIZE_CONTROL);
}
/* protocol configuration */
if (cmd->scan_begin_src == TRIG_TIMER) {
/* page 4-5, "input with internal REQs" */
writeb(0, dev->mmio + OP_MODE);
writeb(0x00, dev->mmio + CLOCK_REG);
writeb(1, dev->mmio + SEQUENCE);
writeb(0x04, dev->mmio + REQ_REG);
writeb(4, dev->mmio + BLOCK_MODE);
writeb(3, dev->mmio + LINE_POLARITIES);
writeb(0xc0, dev->mmio + ACK_SER);
writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg,
CMDF_ROUND_NEAREST),
dev->mmio + START_DELAY);
writeb(1, dev->mmio + REQ_DELAY);
writeb(1, dev->mmio + REQ_NOT_DELAY);
writeb(1, dev->mmio + ACK_DELAY);
writeb(0x0b, dev->mmio + ACK_NOT_DELAY);
writeb(0x01, dev->mmio + DATA_1_DELAY);
/*
* manual, page 4-5:
* CLOCK_SPEED comment is incorrectly listed on DAQ_OPTIONS
*/
writew(0, dev->mmio + CLOCK_SPEED);
writeb(0, dev->mmio + DAQ_OPTIONS);
} else {
/* TRIG_EXT */
/* page 4-5, "input with external REQs" */
writeb(0, dev->mmio + OP_MODE);
writeb(0x00, dev->mmio + CLOCK_REG);
writeb(0, dev->mmio + SEQUENCE);
writeb(0x00, dev->mmio + REQ_REG);
writeb(4, dev->mmio + BLOCK_MODE);
if (!(cmd->scan_begin_arg & CR_INVERT)) /* Leading Edge */
writeb(0, dev->mmio + LINE_POLARITIES);
else /* Trailing Edge */
writeb(2, dev->mmio + LINE_POLARITIES);
writeb(0x00, dev->mmio + ACK_SER);
writel(1, dev->mmio + START_DELAY);
writeb(1, dev->mmio + REQ_DELAY);
writeb(1, dev->mmio + REQ_NOT_DELAY);
writeb(1, dev->mmio + ACK_DELAY);
writeb(0x0C, dev->mmio + ACK_NOT_DELAY);
writeb(0x10, dev->mmio + DATA_1_DELAY);
writew(0, dev->mmio + CLOCK_SPEED);
writeb(0x60, dev->mmio + DAQ_OPTIONS);
}
if (cmd->stop_src == TRIG_COUNT) {
writel(cmd->stop_arg,
dev->mmio + TRANSFER_COUNT);
} else {
/* XXX */
}
#ifdef USE_DMA
writeb(CLEAR_PRIMARY_TC | CLEAR_SECONDARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
{
int retval = setup_mite_dma(dev, s);
if (retval)
return retval;
}
#else
writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP1);
#endif
writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP2);
/* clear and enable interrupts */
writeb(0xff, dev->mmio + GROUP_1_FIRST_CLEAR);
/* writeb(CLEAR_EXPIRED, dev->mmio+GROUP_1_SECOND_CLEAR); */
writeb(INT_EN, dev->mmio + INTERRUPT_CONTROL);
writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
if (cmd->stop_src == TRIG_NONE) {
devpriv->OP_MODEBits = DATA_LATCHING(0) | RUN_MODE(7);
} else { /* TRIG_TIMER */
devpriv->OP_MODEBits = NUMBERED | RUN_MODE(7);
}
if (cmd->start_src == TRIG_NOW) {
/* start */
writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE);
s->async->inttrig = NULL;
} else {
/* TRIG_INT */
s->async->inttrig = ni_pcidio_inttrig;
}
return 0;
}
static int ni_pcidio_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
writeb(0x00, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
ni_pcidio_release_di_mite_channel(dev);
return 0;
}
static int ni_pcidio_change(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct nidio96_private *devpriv = dev->private;
int ret;
ret = mite_buf_change(devpriv->di_mite_ring, s);
if (ret < 0)
return ret;
memset(s->async->prealloc_buf, 0xaa, s->async->prealloc_bufsz);
return 0;
}
static int pci_6534_load_fpga(struct comedi_device *dev,
const u8 *data, size_t data_len,
unsigned long context)
{
static const int timeout = 1000;
int fpga_index = context;
int i;
size_t j;
writew(0x80 | fpga_index, dev->mmio + Firmware_Control_Register);
writew(0xc0 | fpga_index, dev->mmio + Firmware_Control_Register);
for (i = 0;
(readw(dev->mmio + Firmware_Status_Register) & 0x2) == 0 &&
i < timeout; ++i) {
udelay(1);
}
if (i == timeout) {
dev_warn(dev->class_dev,
"ni_pcidio: failed to load fpga %i, waiting for status 0x2\n",
fpga_index);
return -EIO;
}
writew(0x80 | fpga_index, dev->mmio + Firmware_Control_Register);
for (i = 0;
readw(dev->mmio + Firmware_Status_Register) != 0x3 &&
i < timeout; ++i) {
udelay(1);
}
if (i == timeout) {
dev_warn(dev->class_dev,
"ni_pcidio: failed to load fpga %i, waiting for status 0x3\n",
fpga_index);
return -EIO;
}
for (j = 0; j + 1 < data_len;) {
unsigned int value = data[j++];
value |= data[j++] << 8;
writew(value, dev->mmio + Firmware_Data_Register);
for (i = 0;
(readw(dev->mmio + Firmware_Status_Register) & 0x2) == 0
&& i < timeout; ++i) {
udelay(1);
}
if (i == timeout) {
dev_warn(dev->class_dev,
"ni_pcidio: failed to load word into fpga %i\n",
fpga_index);
return -EIO;
}
if (need_resched())
schedule();
}
writew(0x0, dev->mmio + Firmware_Control_Register);
return 0;
}
static int pci_6534_reset_fpga(struct comedi_device *dev, int fpga_index)
{
return pci_6534_load_fpga(dev, NULL, 0, fpga_index);
}
static int pci_6534_reset_fpgas(struct comedi_device *dev)
{
int ret;
int i;
writew(0x0, dev->mmio + Firmware_Control_Register);
for (i = 0; i < 3; ++i) {
ret = pci_6534_reset_fpga(dev, i);
if (ret < 0)
break;
}
writew(0x0, dev->mmio + Firmware_Mask_Register);
return ret;
}
static void pci_6534_init_main_fpga(struct comedi_device *dev)
{
writel(0, dev->mmio + FPGA_Control1_Register);
writel(0, dev->mmio + FPGA_Control2_Register);
writel(0, dev->mmio + FPGA_SCALS_Counter_Register);
writel(0, dev->mmio + FPGA_SCAMS_Counter_Register);
writel(0, dev->mmio + FPGA_SCBLS_Counter_Register);
writel(0, dev->mmio + FPGA_SCBMS_Counter_Register);
}
static int pci_6534_upload_firmware(struct comedi_device *dev)
{
struct nidio96_private *devpriv = dev->private;
static const char *const fw_file[3] = {
FW_PCI_6534_SCARAB_DI, /* loaded into scarab A for DI */
FW_PCI_6534_SCARAB_DO, /* loaded into scarab B for DO */
FW_PCI_6534_MAIN, /* loaded into main FPGA */
};
int ret;
int n;
ret = pci_6534_reset_fpgas(dev);
if (ret < 0)
return ret;
/* load main FPGA first, then the two scarabs */
for (n = 2; n >= 0; n--) {
ret = comedi_load_firmware(dev, &devpriv->mite->pcidev->dev,
fw_file[n],
pci_6534_load_fpga, n);
if (ret == 0 && n == 2)
pci_6534_init_main_fpga(dev);
if (ret < 0)
break;
}
return ret;
}
static void nidio_reset_board(struct comedi_device *dev)
{
writel(0, dev->mmio + PORT_IO(0));
writel(0, dev->mmio + PORT_PIN_DIRECTIONS(0));
writel(0, dev->mmio + PORT_PIN_MASK(0));
/* disable interrupts on board */
writeb(0, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
}
static int nidio_auto_attach(struct comedi_device *dev,
unsigned long context)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct nidio_board *board = NULL;
struct nidio96_private *devpriv;
struct comedi_subdevice *s;
int ret;
unsigned int irq;
if (context < ARRAY_SIZE(nidio_boards))
board = &nidio_boards[context];
if (!board)
return -ENODEV;
dev->board_ptr = board;
dev->board_name = board->name;
ret = comedi_pci_enable(dev);
if (ret)
return ret;
devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
if (!devpriv)
return -ENOMEM;
spin_lock_init(&devpriv->mite_channel_lock);
devpriv->mite = mite_attach(dev, false); /* use win0 */
if (!devpriv->mite)
return -ENOMEM;
devpriv->di_mite_ring = mite_alloc_ring(devpriv->mite);
if (!devpriv->di_mite_ring)
return -ENOMEM;
if (board->uses_firmware) {
ret = pci_6534_upload_firmware(dev);
if (ret < 0)
return ret;
}
nidio_reset_board(dev);
ret = comedi_alloc_subdevices(dev, 1);
if (ret)
return ret;
dev_info(dev->class_dev, "%s rev=%d\n", dev->board_name,
readb(dev->mmio + CHIP_VERSION));
s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_DIO;
s->subdev_flags =
SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_PACKED |
SDF_CMD_READ;
s->n_chan = 32;
s->range_table = &range_digital;
s->maxdata = 1;
s->insn_config = &ni_pcidio_insn_config;
s->insn_bits = &ni_pcidio_insn_bits;
s->do_cmd = &ni_pcidio_cmd;
s->do_cmdtest = &ni_pcidio_cmdtest;
s->cancel = &ni_pcidio_cancel;
s->len_chanlist = 32; /* XXX */
s->buf_change = &ni_pcidio_change;
s->async_dma_dir = DMA_BIDIRECTIONAL;
s->poll = &ni_pcidio_poll;
irq = pcidev->irq;
if (irq) {
ret = request_irq(irq, nidio_interrupt, IRQF_SHARED,
dev->board_name, dev);
if (ret == 0)
dev->irq = irq;
}
return 0;
}
static void nidio_detach(struct comedi_device *dev)
{
struct nidio96_private *devpriv = dev->private;
if (dev->irq)
free_irq(dev->irq, dev);
if (devpriv) {
if (devpriv->di_mite_ring) {
mite_free_ring(devpriv->di_mite_ring);
devpriv->di_mite_ring = NULL;
}
mite_detach(devpriv->mite);
}
if (dev->mmio)
iounmap(dev->mmio);
comedi_pci_disable(dev);
}
static struct comedi_driver ni_pcidio_driver = {
.driver_name = "ni_pcidio",
.module = THIS_MODULE,
.auto_attach = nidio_auto_attach,
.detach = nidio_detach,
};
static int ni_pcidio_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
return comedi_pci_auto_config(dev, &ni_pcidio_driver, id->driver_data);
}
static const struct pci_device_id ni_pcidio_pci_table[] = {
{ PCI_VDEVICE(NI, 0x1150), BOARD_PCIDIO_32HS },
{ PCI_VDEVICE(NI, 0x12b0), BOARD_PCI6534 },
{ PCI_VDEVICE(NI, 0x1320), BOARD_PXI6533 },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table);
static struct pci_driver ni_pcidio_pci_driver = {
.name = "ni_pcidio",
.id_table = ni_pcidio_pci_table,
.probe = ni_pcidio_pci_probe,
.remove = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(ni_pcidio_driver, ni_pcidio_pci_driver);
MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");