#define CTRS_PER_CHIP 4 // The number of counters per ni-tio chip
#define NUM_PFI_CHANNELS 40
+// really there are only up to 3 dma channels, but the register layout allows for 4
+#define MAX_DMA_CHANNEL 4
/* See Register-Level Programmer Manual page 3.1 */
typedef enum
G1StatusRegister,
G01StatusRegister,
G0CommandRegister,
+ STCDIOParallelInput,
G1CommandRegister,
G0HWSaveRegister,
G1HWSaveRegister,
+ STCDIOOutput,
+ STCDIOControl,
G0SWSaveRegister,
G1SWSaveRegister,
G0ModeRegister,
G01JointStatus1Register,
G1ModeRegister,
+ STCDIOSerialInput,
G0LoadARegister,
G01JointStatus2Register,
G0LoadBRegister,
G2DMAStatusRegister,
G3DMAConfigRegister,
G3DMAStatusRegister,
+ DIO32Input,
+ DIO32Output,
ClockConfigRegister,
- DMAConfigRegister, // XXX will need to use this according to mite channel used
+ GlobalInterruptStatusRegister,
+ DMAConfigRegister,
+ GlobalInterruptConfigRegister,
IOConfigReg0_1,
IOConfigReg2_3,
IOConfigReg4_5,
IOConfigReg34_35,
IOConfigReg36_37,
IOConfigReg38_39,
- STCDIOParallelInput,
- STCDIOOutput,
- STCDIOControl,
- STCDIOSerialInput,
- DIO32Input,
- DIO32Output,
NumRegisters,
} NI_660x_Register;
NI_660x_READ_WRITE
};
+enum ni_660x_pfi_output_select
+{
+ pfi_output_select_high_Z = 0,
+ pfi_output_select_counter = 1,
+ pfi_output_select_do = 2,
+ num_pfi_output_selects
+};
+
typedef struct
{
const char *name; // Register Name
{"G1 Status Register", 0x006, NI_660x_READ, DATA_2B},
{"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B},
{"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B},
+ {"STD DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B},
{"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B},
{"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B},
{"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B},
+ {"STD DIO Output", 0x014, NI_660x_WRITE, DATA_2B},
+ {"STD DIO Control", 0x016, NI_660x_WRITE, DATA_2B},
{"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B},
{"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B},
{"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B},
{"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B},
{"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B},
+ {"STD DIO Serial Input", 0x038, NI_660x_READ, DATA_2B},
{"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B},
{"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B},
{"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B},
{"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B},
{"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B},
{"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B},
+ {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B},
+ {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B},
{"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B},
- {"DMA Configuration Register", 0x76c, NI_660x_WRITE, DATA_4B},
- {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B}, // READWRITE
+ {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B},
+ {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B},
+ {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B},
+ {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B},
{"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B},
- {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B},
- {"STD DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B},
- {"STD DIO Output", 0x014, NI_660x_WRITE, DATA_2B},
- {"STD DIO Control", 0x016, NI_660x_WRITE, DATA_2B},
- {"STD DIO Serial Input", 0x038, NI_660x_READ, DATA_2B},
- {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B},
- {"32 bit Digital Output", 0x414, NI_660x_WRITE, DATA_4B}
+ {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B}
};
-#define GateSelectPin38 0x1<<8 // Take internal time-based 20
-
// kind of ENABLE for the second counter
-#define CounterSwap 0x1<<21
+enum clock_config_register_bits
+{
+ CounterSwap = 0x1 << 21
+};
// ioconfigreg
+static inline unsigned ioconfig_bitshift(unsigned pfi_channel)
+{
+ if(pfi_channel % 2) return 0;
+ else return 8;
+}
static inline unsigned pfi_output_select_mask(unsigned pfi_channel)
{
- return 0x3 << (8 * (pfi_channel % 2));
+ return 0x3 << ioconfig_bitshift(pfi_channel);
}
static inline unsigned pfi_output_select_bits(unsigned pfi_channel, unsigned output_select)
{
- return (output_select & 0x3) << (8 * (pfi_channel % 2));
+ return (output_select & 0x3) << ioconfig_bitshift(pfi_channel);
}
static inline unsigned pfi_input_select_mask(unsigned pfi_channel)
{
- return 0x7 << (4 + 8 * (pfi_channel % 2));
+ return 0x7 << (4 + ioconfig_bitshift(pfi_channel));
}
static inline unsigned pfi_input_select_bits(unsigned pfi_channel, unsigned input_select)
{
- return (input_select & 0x7) << (4 + 8 * (pfi_channel % 2));
+ return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel));
+}
+
+// dma configuration register bits
+static inline unsigned dma_select_mask(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x1f << (8 * dma_channel);
+}
+enum dma_selection
+{
+ dma_selection_none = 0x1f,
+};
+static inline unsigned dma_selection_counter(unsigned counter_index)
+{
+ BUG_ON(counter_index >= CTRS_PER_CHIP);
+ return counter_index;
+}
+static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel);
+}
+static inline unsigned dma_reset_bit(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x80 << (8 * dma_channel);
}
// Offset of the GPCT chips from the base-adress of the card
{
unsigned short dev_id; /* `lspci` will show you this */
const char *name;
- int n_ctrs; /* total number of counters */
- int cnt_bits; /* number of bits in each counter */
+ unsigned n_chips; /* total number of TIO chips */
} ni_660x_board;
static const ni_660x_board ni_660x_boards[] =
{
dev_id : 0x2c60,
name : "PCI-6601",
- n_ctrs : 1*CTRS_PER_CHIP,
- cnt_bits : 32,
+ n_chips : 1,
},
{
dev_id : 0x1310,
name : "PCI-6602",
- n_ctrs : 2*CTRS_PER_CHIP,
- cnt_bits : 32,
+ n_chips : 2,
},
};
-#define NI_660X_MAX_NUM_COUNTERS 8
+#define NI_660X_MAX_NUM_CHIPS 2
+#define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * CTRS_PER_CHIP)
static struct pci_device_id ni_660x_pci_table[] __devinitdata = {
{ PCI_VENDOR_ID_NATINST, 0x2c60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
};
MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
-#define thisboard ((const ni_660x_board *)dev->board_ptr)
-/* initialized in ni_660x_find_device() */
-
typedef struct
{
struct mite_struct *mite;
- int boardtype;
struct ni_gpct_device *counter_dev;
uint64_t pfi_direction_bits;
+ struct mite_dma_descriptor_ring *mite_rings[NI_660X_MAX_NUM_CHIPS][CTRS_PER_CHIP];
+ spinlock_t mite_channel_lock;
+ unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS];
+ spinlock_t soft_reg_copy_lock;
+ unsigned short pfi_output_selects[NUM_PFI_CHANNELS];
}ni_660x_private;
-#define devpriv ((ni_660x_private *)dev->private)
+static inline ni_660x_private* private(comedi_device *dev)
+{
+ return dev->private;
+}
+/* initialized in ni_660x_find_device() */
+static inline const ni_660x_board* board(comedi_device *dev)
+{
+ return dev->board_ptr;
+}
+
#define n_ni_660x_boards (sizeof(ni_660x_boards)/sizeof(ni_660x_boards[0]))
static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it);
static int ni_660x_detach(comedi_device *dev);
static void init_tio_chip(comedi_device *dev, int chipset);
+static void ni_660x_select_pfi_output(comedi_device *dev, unsigned pfi_channel, unsigned output_select);
static comedi_driver driver_ni_660x=
{
comedi_insn *insn,
lsampl_t * data);
-// NYI
-#if 0
-static int ni_660x_GPCT_cmdtest(comedi_device *dev,comedi_subdevice *s,
- comedi_cmd *cmd);
-static int ni_660x_GPCT_cmd(comedi_device *dev,comedi_subdevice *s);
-#endif
-
/* Possible instructions for Digital IO */
static int ni_660x_dio_insn_config(comedi_device *dev,
comedi_subdevice *s,
comedi_insn *insn,
lsampl_t *data);
-#if 0
-static int ni_660x_GPCT_cmdtest(comedi_device *dev,comedi_subdevice *s,
- comedi_cmd *cmd)
-{
- DPRINTK("NI_660X: COMMANDS not implemented yet for GPCT\n");
- return -EINVAL;
-}
-
-static int ni_660x_GPCT_cmd(comedi_device *dev,comedi_subdevice *s)
+static inline unsigned ni_660x_num_counters(comedi_device *dev)
{
- DPRINTK("NI_660X: COMMANDS not implemented yet for GPCT\n");
- return -EINVAL;
+ return board(dev)->n_chips * CTRS_PER_CHIP;
}
-#endif
static NI_660x_Register ni_gpct_to_660x_register(enum ni_gpct_register reg)
{
case NITIO_G3_Status_Reg:
ni_660x_register = G0StatusRegister;
break;
+ case NITIO_G0_Interrupt_Enable_Reg:
+ ni_660x_register = G0InterruptEnable;
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ ni_660x_register = G1InterruptEnable;
+ break;
+ case NITIO_G2_Interrupt_Enable_Reg:
+ ni_660x_register = G2InterruptEnable;
+ break;
+ case NITIO_G3_Interrupt_Enable_Reg:
+ ni_660x_register = G3InterruptEnable;
+ break;
default:
rt_printk("%s: unhandled register 0x%x in switch.\n", __FUNCTION__, reg);
BUG();
return ni_660x_register;
}
-static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits, enum ni_gpct_register reg)
+static inline void ni_660x_write_register(comedi_device *dev, unsigned chip_index, unsigned bits, NI_660x_Register reg)
{
- NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
- comedi_device *dev = counter->counter_dev->dev;
- void * const write_address = devpriv->mite->daq_io_addr + GPCT_OFFSET[counter->chip_index] + registerData[ni_660x_register].offset;
+ void * const write_address = private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] +
+ registerData[reg].offset;
- switch(registerData[ni_660x_register].size)
+ switch(registerData[reg].size)
{
case DATA_2B:
writew(bits, write_address);
writel(bits, write_address);
break;
default:
- rt_printk("%s: %s: bug! unhandled case = 0x%x in switch.\n", __FILE__, __FUNCTION__, reg);
+ rt_printk("%s: %s: bug! unhandled case (reg=0x%x) in switch.\n", __FILE__, __FUNCTION__, reg);
BUG();
break;
}
}
-static unsigned ni_gpct_read_register(struct ni_gpct *counter, enum ni_gpct_register reg)
+static inline unsigned ni_660x_read_register(comedi_device *dev, unsigned chip_index, NI_660x_Register reg)
{
- NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
- comedi_device *dev = counter->counter_dev->dev;
- void * const read_address = devpriv->mite->daq_io_addr + GPCT_OFFSET[counter->chip_index] + registerData[ni_660x_register].offset;
+ void * const read_address = private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] + registerData[reg].offset;
- switch(registerData[ni_660x_register].size)
+ switch(registerData[reg].size)
{
case DATA_2B:
return readw(read_address);
return readl(read_address);
break;
default:
- rt_printk("%s: %s: bug! unhandled case = 0x%x in switch.\n", __FILE__, __FUNCTION__, reg);
+ rt_printk("%s: %s: bug! unhandled case (reg=0x%x) in switch.\n", __FILE__, __FUNCTION__, reg);
BUG();
break;
}
return 0;
}
+static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits, enum ni_gpct_register reg)
+{
+ comedi_device *dev = counter->counter_dev->dev;
+ NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
+ ni_660x_write_register(dev, counter->chip_index, bits, ni_660x_register);
+}
+
+static unsigned ni_gpct_read_register(struct ni_gpct *counter, enum ni_gpct_register reg)
+{
+ comedi_device *dev = counter->counter_dev->dev;
+ NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
+ return ni_660x_read_register(dev, counter->chip_index, ni_660x_register);
+}
+
+static inline struct mite_dma_descriptor_ring* mite_ring(ni_660x_private *priv, struct ni_gpct *counter)
+{
+ return priv->mite_rings[counter->chip_index][counter->counter_index];
+}
+
+static inline void ni_660x_set_dma_channel(comedi_device *dev, unsigned mite_channel, struct ni_gpct *counter)
+{
+ unsigned long flags;
+ comedi_spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] &=
+ ~dma_select_mask(mite_channel);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] |=
+ dma_select_bits(mite_channel, dma_selection_counter(counter->counter_index));
+ ni_660x_write_register(dev, counter->chip_index, private(dev)->dma_configuration_soft_copies[counter->chip_index] |
+ dma_reset_bit(mite_channel), DMAConfigRegister);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags);
+}
+
+static inline void ni_660x_unset_dma_channel(comedi_device *dev, unsigned mite_channel, struct ni_gpct *counter)
+{
+ unsigned long flags;
+ comedi_spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] &=
+ ~dma_select_mask(mite_channel);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] |=
+ dma_select_bits(mite_channel, dma_selection_none);
+ ni_660x_write_register(dev, counter->chip_index, private(dev)->dma_configuration_soft_copies[counter->chip_index],
+ DMAConfigRegister);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags);
+}
+
+static int ni_660x_request_mite_channel(comedi_device *dev, struct ni_gpct *counter, enum comedi_io_direction direction)
+{
+ unsigned long flags;
+ struct mite_channel *mite_chan;
+
+ comedi_spin_lock_irqsave(&private(dev)->mite_channel_lock, flags);
+ BUG_ON(counter->mite_chan);
+ mite_chan = mite_request_channel(private(dev)->mite, mite_ring(private(dev), counter));
+ if(mite_chan == NULL)
+ {
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags);
+ comedi_error(dev, "failed to reserve mite dma channel for counter.");
+ return -EBUSY;
+ }
+ mite_chan->dir = direction;
+ ni_tio_set_mite_channel(counter, mite_chan);
+ ni_660x_set_dma_channel(dev, mite_chan->channel, counter);
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags);
+ return 0;
+}
+
+void ni_660x_release_mite_channel(comedi_device *dev, struct ni_gpct *counter)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&private(dev)->mite_channel_lock, flags);
+ if(counter->mite_chan)
+ {
+ struct mite_channel *mite_chan = counter->mite_chan;
+
+ ni_660x_unset_dma_channel(dev, mite_chan->channel, counter);
+ ni_tio_set_mite_channel(counter, NULL);
+ mite_release_channel(mite_chan);
+ }
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags);
+}
+
+static int ni_660x_cmd(comedi_device *dev, comedi_subdevice *s)
+{
+ int retval;
+
+ struct ni_gpct *counter = subdev_to_counter(s);
+// const comedi_cmd *cmd = &s->async->cmd;
+
+ retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT);
+ if(retval)
+ {
+ comedi_error(dev, "no dma channel available for use by counter");
+ return retval;
+ }
+ ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL);
+ retval = ni_tio_cmd(counter, s->async);
+
+ return retval;
+}
+
+static int ni_660x_cmdtest(comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd)
+{
+ struct ni_gpct *counter = subdev_to_counter(s);
+
+ return ni_tio_cmdtest(counter, cmd);
+}
+
+static int ni_660x_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+ struct ni_gpct *counter = subdev_to_counter(s);
+ int retval;
+
+ retval = ni_tio_cancel(counter);
+ ni_660x_release_mite_channel(dev, counter);
+ return retval;
+}
+
+static irqreturn_t ni_660x_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ return IRQ_HANDLED;
+}
+
+static int ni_660x_buf_change(comedi_device *dev, comedi_subdevice *s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(mite_ring(private(dev), subdev_to_counter(s)), s->async);
+ if(ret < 0) return ret;
+
+ return 0;
+}
+
+static int ni_660x_allocate_private(comedi_device *dev)
+{
+ int retval;
+ unsigned i;
+
+ if((retval = alloc_private(dev, sizeof(ni_660x_private))) < 0) return retval;
+ spin_lock_init(&private(dev)->mite_channel_lock);
+ spin_lock_init(&private(dev)->soft_reg_copy_lock);
+ for(i = 0; i < NUM_PFI_CHANNELS; ++i)
+ {
+ private(dev)->pfi_output_selects[i] = pfi_output_select_counter;
+ }
+ return 0;
+}
+
+static int ni_660x_alloc_mite_rings(comedi_device *dev)
+{
+ unsigned i;
+ unsigned j;
+
+ for(i = 0; i < board(dev)->n_chips; ++i)
+ {
+ for(j = 0; j < CTRS_PER_CHIP; ++j)
+ {
+ private(dev)->mite_rings[i][j] = mite_alloc_ring(private(dev)->mite);
+ if(private(dev)->mite_rings[i][j] == NULL)
+ {
+ return -ENOMEM;
+ }
+ }
+ }
+ return 0;
+}
+
+static void ni_660x_free_mite_rings(comedi_device *dev)
+{
+ unsigned i;
+ unsigned j;
+
+ for(i = 0; i < board(dev)->n_chips; ++i)
+ {
+ for(j = 0; j < CTRS_PER_CHIP; ++j)
+ {
+ mite_free_ring(private(dev)->mite_rings[i][j]);
+ }
+ }
+}
+
static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it)
{
comedi_subdevice *s;
printk("comedi%d: ni_660x: ",dev->minor);
- if ((ret=alloc_private(dev,sizeof(ni_660x_private))) < 0) return ret;
-
+ ret = ni_660x_allocate_private(dev);
+ if(ret < 0) return ret;
ret = ni_660x_find_device(dev, it->options[0], it->options[1]);
if (ret<0) return ret;
- ret = mite_setup(devpriv->mite);
+ dev->board_name = board(dev)->name;
+
+ ret = mite_setup(private(dev)->mite);
if (ret < 0) {
- printk("error setting up mite\n");
- return ret;
+ printk("error setting up mite\n");
+ return ret;
}
- dev->board_name = thisboard->name;
- /* we don't support the interrupt yet */
- //dev->irq = mite_irq(devpriv->mite);
+ comedi_set_hw_dev(dev, &private(dev)->mite->pcidev->dev);
+ ret = ni_660x_alloc_mite_rings(dev);
+ if(ret < 0) return ret;
printk(" %s ", dev->board_name);
s->insn_config = ni_660x_dio_insn_config;
s->io_bits = 0; /* all bits default to input */
// we use the ioconfig registers to control dio direction, so zero output enables in stc dio control reg
- writew(0, devpriv->mite->daq_io_addr + registerData[STCDIOControl].offset);
+ ni_660x_write_register(dev, 0, 0, STCDIOControl);
- devpriv->counter_dev = ni_gpct_device_construct(dev,
+ private(dev)->counter_dev = ni_gpct_device_construct(dev,
&ni_gpct_write_register, &ni_gpct_read_register,
- ni_gpct_variant_660x, thisboard->n_ctrs);
- if(devpriv->counter_dev == NULL) return -ENOMEM;
+ ni_gpct_variant_660x, ni_660x_num_counters(dev));
+ if(private(dev)->counter_dev == NULL) return -ENOMEM;
for(i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i)
{
s = dev->subdevices + 2 + i;
- if(i < thisboard->n_ctrs)
+ if(i < ni_660x_num_counters(dev))
{
s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_CMD_READ /* | SDF_CMD_WRITE */;
s->n_chan = 3;
s->maxdata = 0xffffffff;
s->insn_read = ni_660x_GPCT_rinsn;
s->insn_write = ni_660x_GPCT_winsn;
s->insn_config = ni_660x_GPCT_insn_config;
- s->private = &devpriv->counter_dev->counters[i];
-
- devpriv->counter_dev->counters[i].chip_index = i / CTRS_PER_CHIP;
- devpriv->counter_dev->counters[i].counter_index = i % CTRS_PER_CHIP;
+ s->do_cmd = &ni_660x_cmd;
+ s->len_chanlist = 1;
+ s->do_cmdtest = &ni_660x_cmdtest;
+ s->cancel = &ni_660x_cancel;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->buf_change = &ni_660x_buf_change;
+ s->private = &private(dev)->counter_dev->counters[i];
+
+ private(dev)->counter_dev->counters[i].chip_index = i / CTRS_PER_CHIP;
+ private(dev)->counter_dev->counters[i].counter_index = i % CTRS_PER_CHIP;
}else
{
s->type = COMEDI_SUBD_UNUSED;
}
}
- for(i = 0; i < thisboard->n_ctrs / CTRS_PER_CHIP; ++i)
+ for(i = 0; i < board(dev)->n_chips; ++i)
{
init_tio_chip(dev, i);
}
- for(i = 0; i < thisboard->n_ctrs; ++i)
+ for(i = 0; i < ni_660x_num_counters(dev); ++i)
{
- ni_tio_init_counter(&devpriv->counter_dev->counters[i]);
+ ni_tio_init_counter(&private(dev)->counter_dev->counters[i]);
}
-
+ for(i = 0; i < NUM_PFI_CHANNELS; ++i)
+ {
+ ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z);
+ }
+ if((ret = comedi_request_irq(mite_irq(private(dev)->mite), &ni_660x_interrupt, IRQF_SHARED, "ni_660x", dev)) < 0)
+ {
+ printk(" irq not available\n");
+ return ret;
+ }
+ dev->irq = mite_irq(private(dev)->mite);
printk("attached\n");
return 0;
}
{
printk("comedi%d: ni_660x: remove\n",dev->minor);
+ /* Free irq */
+ if(dev->irq) comedi_free_irq(dev->irq, dev);
+
if(dev->private)
{
- if(devpriv->counter_dev)
- ni_gpct_device_destroy(devpriv->counter_dev);
- if(devpriv->mite)
- mite_unsetup(devpriv->mite);
+ if(private(dev)->counter_dev)
+ ni_gpct_device_destroy(private(dev)->counter_dev);
+ if(private(dev)->mite)
+ {
+ ni_660x_free_mite_rings(dev);
+ mite_unsetup(private(dev)->mite);
+ }
}
- /* Free irq */
-
- if(dev->irq) comedi_free_irq(dev->irq,dev);
-
- /* Same question as with attach ... */
return 0;
}
ni_660x_GPCT_rinsn(comedi_device *dev, comedi_subdevice *s,
comedi_insn *insn, lsampl_t *data)
{
- struct ni_gpct *counter = s->private;
- return ni_tio_rinsn(counter, insn, data);
+ return ni_tio_rinsn(subdev_to_counter(s), insn, data);
}
static void init_tio_chip(comedi_device *dev, int chipset)
{
+ unsigned i;
+
/* See P. 3.5 of the Register-Level Programming manual. The
CounterSwap bit has to be set on the second chip, otherwise
it will try to use the same pins as the first chip.
*/
if(chipset)
- writel(CounterSwap,devpriv->mite->daq_io_addr + GPCT_OFFSET[1]
- + registerData[ClockConfigRegister].offset);
+ ni_660x_write_register(dev, chipset, CounterSwap, ClockConfigRegister);
else
- writel(0,devpriv->mite->daq_io_addr + GPCT_OFFSET[0]
- + registerData[ClockConfigRegister].offset);
+ ni_660x_write_register(dev, chipset, 0, ClockConfigRegister);
+ // init dma configuration register
+ private(dev)->dma_configuration_soft_copies[chipset] = 0;
+ for(i = 0; i < MAX_DMA_CHANNEL; ++i)
+ {
+ private(dev)->dma_configuration_soft_copies[chipset] |=
+ dma_select_bits(i, dma_selection_none) & dma_select_mask(i);
+ }
+ ni_660x_write_register(dev, chipset, private(dev)->dma_configuration_soft_copies[chipset],
+ DMAConfigRegister);
}
static int
ni_660x_GPCT_insn_config(comedi_device *dev, comedi_subdevice *s,
comedi_insn *insn, lsampl_t *data)
{
- struct ni_gpct *counter = s->private;
- return ni_tio_insn_config(counter, insn, data);
+ return ni_tio_insn_config(subdev_to_counter(s), insn, data);
}
static int ni_660x_GPCT_winsn(comedi_device *dev,
comedi_insn *insn,
lsampl_t * data)
{
- struct ni_gpct *counter = s->private;
- return ni_tio_winsn(counter, insn, data);
+ return ni_tio_winsn(subdev_to_counter(s), insn, data);
}
static int
for (i=0; i<n_ni_660x_boards; i++) {
if (mite_device_id(mite)==ni_660x_boards[i].dev_id) {
dev->board_ptr=ni_660x_boards+i;
- devpriv->mite=mite;
+ private(dev)->mite=mite;
return 0;
}
}
// Check if we have to write some bits
if(data[0])
{
- /* Only the first 8 lines can be read/written. The rest can
- only have their input/output configuration changed (although
- the user manual implies the first 32 channels can be used as
- general purpose dio, the register manual doesn't tell you how
- this can be accomplished. */
- /*FIXME: use DIO32Input and DIO32Output registers for dio
- to the 32 bit port */
- if((data[0] << base_bitfield_channel) > 0xff)
- {
- return -EINVAL;
- }
s->state &= ~(data[0] << base_bitfield_channel);
s->state |= (data[0] & data[1]) << base_bitfield_channel;
/* Write out the new digital output lines */
- writew(s->state,devpriv->mite->daq_io_addr + registerData[STCDIOOutput].offset);
+ ni_660x_write_register(dev, 0, s->state, DIO32Output);
}
/* on return, data[1] contains the value of the digital
* input and output lines. */
- data[1] = (readw(devpriv->mite->daq_io_addr + registerData[STCDIOParallelInput].offset) >> base_bitfield_channel) &
- (0xff >> base_bitfield_channel);
+ data[1] = (ni_660x_read_register(dev, 0, DIO32Input) >> base_bitfield_channel);
return 2;
}
static void ni_660x_select_pfi_output(comedi_device *dev, unsigned pfi_channel, unsigned output_select)
{
- unsigned bits = readw(devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset);
+ unsigned bits = ni_660x_read_register(dev, 0, IOConfigReg(pfi_channel));
+
bits &= ~pfi_output_select_mask(pfi_channel);
bits |= pfi_output_select_bits(pfi_channel, output_select);
- writew(bits, devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset);
+ ni_660x_write_register(dev, 0, bits, IOConfigReg(pfi_channel));
+}
+
+static int ni_660x_set_pfi_routing(comedi_device *dev, unsigned chan, unsigned source)
+{
+ if(source > num_pfi_output_selects) return -EINVAL;
+ BUG_ON(chan >= NUM_PFI_CHANNELS);
+
+ private(dev)->pfi_output_selects[chan] = source;
+ if(private(dev)->pfi_direction_bits & (((uint64_t)1) << chan))
+ ni_660x_select_pfi_output(dev, chan, private(dev)->pfi_output_selects[chan]);
+ return 0;
+}
+
+static unsigned ni_660x_get_pfi_routing(comedi_device *dev, unsigned chan)
+{
+ BUG_ON(chan >= NUM_PFI_CHANNELS);
+ return private(dev)->pfi_output_selects[chan];
}
static void ni660x_config_filter(comedi_device *dev, unsigned pfi_channel, enum ni_gpct_filter_select filter)
{
- unsigned bits = readw(devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset);
+ unsigned bits = ni_660x_read_register(dev, 0, IOConfigReg(pfi_channel));
bits &= ~pfi_input_select_mask(pfi_channel);
bits |= pfi_input_select_bits(pfi_channel, filter);
- writew(bits, devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset);
+ ni_660x_write_register(dev, 0, bits, IOConfigReg(pfi_channel));
}
static int ni_660x_dio_insn_config(comedi_device *dev,
comedi_insn *insn,
lsampl_t *data)
{
- int chan=CR_CHAN(insn->chanspec);
+ int chan = CR_CHAN(insn->chanspec);
/* The input or output configuration of each digital line is
* configured by a special insn_config instruction. chanspec
switch(data[0])
{
case INSN_CONFIG_DIO_OUTPUT:
- devpriv->pfi_direction_bits |= ((uint64_t)1) << chan;
- //FIXME: output select 1 is counter output, 2 is digital output
- ni_660x_select_pfi_output(dev, chan, 1);
+ private(dev)->pfi_direction_bits |= ((uint64_t)1) << chan;
+ ni_660x_select_pfi_output(dev, chan, private(dev)->pfi_output_selects[chan]);
break;
case INSN_CONFIG_DIO_INPUT:
- devpriv->pfi_direction_bits &= ~(((uint64_t)1) << chan);
- ni_660x_select_pfi_output(dev, chan, 0);
+ private(dev)->pfi_direction_bits &= ~(((uint64_t)1) << chan);
+ ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z);
break;
case INSN_CONFIG_DIO_QUERY:
- data[1] = (devpriv->pfi_direction_bits & (((uint64_t)1) << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ data[1] = (private(dev)->pfi_direction_bits & (((uint64_t)1) << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
return 0;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_660x_set_pfi_routing(dev, chan, data[1]);
+ break;
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_660x_get_pfi_routing(dev, chan);
+ break;
case INSN_CONFIG_FILTER:
ni660x_config_filter(dev, chan, data[1]);
break;
static void get_last_sample_6143( comedi_device *dev );
#ifdef PCIDMA
static int ni_ai_drain_dma(comedi_device *dev );
+static inline void ni_set_bitfield(comedi_device *dev, int reg, unsigned bit_mask, unsigned bit_values);
/* DMA channel setup */
// negative channel means no channel
static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel)
{
- unsigned long flags;
+ unsigned bitfield;
- comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
- devpriv->ai_ao_select_reg &= ~AI_DMA_Select_Mask;
if(channel >= 0)
{
- devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
+ bitfield = (ni_stc_dma_channel_select_bitfield(channel) << AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
+ }else
+ {
+ bitfield = 0;
}
- ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
- mmiowb();
- comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+ ni_set_bitfield(dev, AI_AO_Select, AI_DMA_Select_Mask, bitfield);
}
// negative channel means no channel
static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel)
{
- unsigned long flags;
+ unsigned bitfield;
- comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
- devpriv->ai_ao_select_reg &= ~AO_DMA_Select_Mask;
if(channel >= 0)
{
- devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
+ bitfield = (ni_stc_dma_channel_select_bitfield(channel) << AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
+ }else
+ {
+ bitfield = 0;
}
- ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
- mmiowb();
- comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+ ni_set_bitfield(dev, AI_AO_Select, AO_DMA_Select_Mask, bitfield);
}
// negative mite_channel means no channel
static inline void ni_set_gpct_dma_channel(comedi_device *dev, unsigned gpct_index, int mite_channel)
{
- unsigned long flags;
+ unsigned bitfield;
- comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
- devpriv->g0_g1_select_reg &= ~GPCT_DMA_Select_Mask(gpct_index);
if(mite_channel >= 0)
{
- devpriv->g0_g1_select_reg |= GPCT_DMA_Select_Bits(gpct_index, mite_channel);
+ bitfield = GPCT_DMA_Select_Bits(gpct_index, mite_channel);
+ }else
+ {
+ bitfield = 0;
}
- ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
- mmiowb();
- comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+ ni_set_bitfield(dev, G0_G1_Select, GPCT_DMA_Select_Mask(gpct_index), bitfield);
}
// negative mite_channel means no channel
comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if(devpriv->counter_dev->counters[gpct_index].mite_chan)
{
+ struct mite_channel *mite_chan = devpriv->counter_dev->counters[gpct_index].mite_chan;
+
ni_set_gpct_dma_channel(dev, gpct_index, -1);
- mite_release_channel(devpriv->counter_dev->counters[gpct_index].mite_chan);
ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index], NULL);
+ mite_release_channel(mite_chan);
}
comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
#endif // PCIDMA
return data;
}
-/* ni_set_bits( ) allows different parts of the ni_mio_common driver to
-* share registers (such as Interrupt_A_Register) without interfering with
-* each other.
-*
-* NOTE: the switch/case statements are optimized out for a constant argument
-* so this is actually quite fast--- If you must wrap another function around this
-* make it inline to avoid a large speed penalty.
-*
-* value should only be 1 or 0.
-*/
-static inline void ni_set_bits(comedi_device *dev, int reg, int bits, int value)
+static inline void ni_set_bitfield(comedi_device *dev, int reg, unsigned bit_mask, unsigned bit_values)
{
unsigned long flags;
comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
switch (reg){
case Interrupt_A_Enable_Register:
- if(value)
- devpriv->int_a_enable_reg |= bits;
- else
- devpriv->int_a_enable_reg &= ~bits;
- devpriv->stc_writew(dev, devpriv->int_a_enable_reg,Interrupt_A_Enable_Register);
+ devpriv->int_a_enable_reg &= ~bit_mask;
+ devpriv->int_a_enable_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->int_a_enable_reg, Interrupt_A_Enable_Register);
break;
case Interrupt_B_Enable_Register:
- if(value)
- devpriv->int_b_enable_reg |= bits;
- else
- devpriv->int_b_enable_reg &= ~bits;
- devpriv->stc_writew(dev, devpriv->int_b_enable_reg,Interrupt_B_Enable_Register);
+ devpriv->int_b_enable_reg &= ~bit_mask;
+ devpriv->int_b_enable_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->int_b_enable_reg, Interrupt_B_Enable_Register);
break;
case IO_Bidirection_Pin_Register:
- if(value)
- devpriv->io_bidirection_pin_reg |= bits;
- else
- devpriv->io_bidirection_pin_reg &= ~bits;
- devpriv->stc_writew(dev, devpriv->io_bidirection_pin_reg,IO_Bidirection_Pin_Register);
+ devpriv->io_bidirection_pin_reg &= ~bit_mask;
+ devpriv->io_bidirection_pin_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->io_bidirection_pin_reg, IO_Bidirection_Pin_Register);
+ break;
+ case AI_AO_Select:
+ devpriv->ai_ao_select_reg &= ~bit_mask;
+ devpriv->ai_ao_select_reg |= bit_values & bit_mask;
+ ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+ break;
+ case G0_G1_Select:
+ devpriv->g0_g1_select_reg &= ~bit_mask;
+ devpriv->g0_g1_select_reg |= bit_values & bit_mask;
+ ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
break;
default:
- rt_printk("Warning ni_set_bits() called with invalid arguments\n");
- rt_printk("reg is %d\n",reg);
+ rt_printk("Warning %s() called with invalid register\n", __FUNCTION__);
+ rt_printk("reg is %d\n", reg);
break;
}
+ mmiowb();
comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags );
}
+/* ni_set_bits( ) allows different parts of the ni_mio_common driver to
+* share registers (such as Interrupt_A_Register) without interfering with
+* each other.
+*
+* NOTE: the switch/case statements are optimized out for a constant argument
+* so this is actually quite fast--- If you must wrap another function around this
+* make it inline to avoid a large speed penalty.
+*
+* value should only be 1 or 0.
+*/
+static inline void ni_set_bits(comedi_device *dev, int reg, unsigned bits, unsigned value)
+{
+ unsigned bit_values;
+
+ if(value)
+ bit_values = bits;
+ else
+ bit_values = 0;
+ ni_set_bitfield(dev, reg, bits, bit_values);
+}
static irqreturn_t ni_E_interrupt(int irq, void *d PT_REGS_ARG)
{
case NITIO_G1_Status_Reg:
stc_register = AO_Status_1_Register;
break;
+ case NITIO_G0_Interrupt_Enable_Reg:
+ stc_register = Interrupt_A_Enable_Register;
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ stc_register = Interrupt_B_Enable_Register;
+ break;
default:
rt_printk("%s: unhandled register 0x%x in switch.\n", __FUNCTION__, reg);
BUG();
unsigned stc_register;
/* bits in the join reset register which are relevant to counters */
static const unsigned gpct_joint_reset_mask = G0_Reset | G1_Reset;
+ static const unsigned gpct_interrupt_a_enable_mask = G0_Gate_Interrupt_Enable | G0_TC_Interrupt_Enable;
+ static const unsigned gpct_interrupt_b_enable_mask = G1_Gate_Interrupt_Enable | G1_TC_Interrupt_Enable;
+
switch(reg)
{
/* m-series-only registers */
break;
/* 16 bit registers */
+ case NITIO_G0_Interrupt_Enable_Reg:
+ BUG_ON(bits & ~gpct_interrupt_a_enable_mask);
+ ni_set_bitfield(dev, Interrupt_A_Enable_Register, gpct_interrupt_a_enable_mask, bits);
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ BUG_ON(bits & ~gpct_interrupt_b_enable_mask);
+ ni_set_bitfield(dev, Interrupt_B_Enable_Register, gpct_interrupt_b_enable_mask, bits);
+ break;
case NITIO_G01_Joint_Reset_Reg:
BUG_ON(bits & ~gpct_joint_reset_mask);
/* fall-through */
return ni_tio_winsn(counter, insn, data);
}
-static inline unsigned Gi_Interrupt_Enable_Register(unsigned counter_index)
-{
- unsigned reg;
-
- switch(counter_index)
- {
- case 0:
- reg = Interrupt_A_Enable_Register;
- break;
- case 1:
- reg = Interrupt_B_Enable_Register;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return reg;
-}
-
-static inline unsigned Gi_Gate_Interrupt_Enable_Bit(unsigned counter_index)
-{
- unsigned bit;
-
- switch(counter_index)
- {
- case 0:
- bit = G0_Gate_Interrupt_Enable;
- break;
- case 1:
- bit = G1_Gate_Interrupt_Enable;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return bit;
-}
-
-static inline unsigned Gi_Interrupt_Ack_Register(unsigned counter_index)
-{
- unsigned reg;
-
- switch(counter_index)
- {
- case 0:
- reg = Interrupt_A_Ack_Register;
- break;
- case 1:
- reg = Interrupt_B_Ack_Register;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return reg;
-}
-
-static inline unsigned Gi_Gate_Interrupt_Ack_Bit(unsigned counter_index)
-{
- unsigned bit;
-
- switch(counter_index)
- {
- case 0:
- bit = G0_Gate_Interrupt_Ack;
- break;
- case 1:
- bit = G1_Gate_Interrupt_Ack;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return bit;
-}
-
-static inline unsigned Gi_Gate_Error_Confirm_Bit(unsigned counter_index)
-{
- unsigned bit;
-
- switch(counter_index)
- {
- case 0:
- bit = G0_Gate_Error_Confirm;
- break;
- case 1:
- bit = G1_Gate_Error_Confirm;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return bit;
-}
-
-static inline unsigned Gi_TC_Error_Confirm_Bit(unsigned counter_index)
-{
- unsigned bit;
-
- switch(counter_index)
- {
- case 0:
- bit = G0_TC_Error_Confirm;
- break;
- case 1:
- bit = G1_TC_Error_Confirm;
- break;
- default:
- BUG();
- return 0;
- break;
- }
- return bit;
-}
-
static int ni_gpct_cmd(comedi_device *dev, comedi_subdevice *s)
{
int retval;
#ifdef PCIDMA
struct ni_gpct *counter = s->private;
- const comedi_cmd *cmd = &s->async->cmd;
+// const comedi_cmd *cmd = &s->async->cmd;
retval = ni_request_gpct_mite_channel(dev, counter->counter_index, COMEDI_INPUT);
if(retval)
comedi_error(dev, "no dma channel available for use by counter");
return retval;
}
- devpriv->stc_writew(dev, Gi_Gate_Interrupt_Ack_Bit(counter->counter_index) |
- Gi_Gate_Error_Confirm_Bit(counter->counter_index) |
- Gi_TC_Error_Confirm_Bit(counter->counter_index),
- Gi_Interrupt_Ack_Register(counter->counter_index));
- if(cmd->flags & TRIG_WAKE_EOS)
- {
- ni_set_bits(dev, Gi_Interrupt_Enable_Register(counter->counter_index),
- Gi_Gate_Interrupt_Enable_Bit(counter->counter_index), 1);
- }
+ ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL);
ni_e_series_enable_second_irq(dev, counter->counter_index, 1);
retval = ni_tio_cmd(counter, s->async);
#else
retval = ni_tio_cancel(counter);
ni_e_series_enable_second_irq(dev, counter->counter_index, 0);
- ni_set_bits(dev, Gi_Interrupt_Enable_Register(counter->counter_index),
- Gi_Gate_Interrupt_Enable_Bit(counter->counter_index), 0);
ni_release_gpct_mite_channel(dev, counter->counter_index);
return retval;
}