| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Hardware driver for DAQ-STC based boards |
| * |
| * COMEDI - Linux Control and Measurement Device Interface |
| * Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org> |
| * Copyright (C) 2002-2006 Frank Mori Hess <fmhess@users.sourceforge.net> |
| */ |
| |
| /* |
| * This file is meant to be included by another file, e.g., |
| * ni_atmio.c or ni_pcimio.c. |
| * |
| * Interrupt support originally added by Truxton Fulton <trux@truxton.com> |
| * |
| * References (ftp://ftp.natinst.com/support/manuals): |
| * 340747b.pdf AT-MIO E series Register Level Programmer Manual |
| * 341079b.pdf PCI E Series RLPM |
| * 340934b.pdf DAQ-STC reference manual |
| * |
| * 67xx and 611x registers (ftp://ftp.ni.com/support/daq/mhddk/documentation/) |
| * release_ni611x.pdf |
| * release_ni67xx.pdf |
| * |
| * Other possibly relevant info: |
| * 320517c.pdf User manual (obsolete) |
| * 320517f.pdf User manual (new) |
| * 320889a.pdf delete |
| * 320906c.pdf maximum signal ratings |
| * 321066a.pdf about 16x |
| * 321791a.pdf discontinuation of at-mio-16e-10 rev. c |
| * 321808a.pdf about at-mio-16e-10 rev P |
| * 321837a.pdf discontinuation of at-mio-16de-10 rev d |
| * 321838a.pdf about at-mio-16de-10 rev N |
| * |
| * ISSUES: |
| * - the interrupt routine needs to be cleaned up |
| * |
| * 2006-02-07: S-Series PCI-6143: Support has been added but is not |
| * fully tested as yet. Terry Barnaby, BEAM Ltd. |
| */ |
| |
| #include <linux/interrupt.h> |
| #include <linux/sched.h> |
| #include <linux/delay.h> |
| #include "8255.h" |
| #include "mite.h" |
| |
| /* A timeout count */ |
| #define NI_TIMEOUT 1000 |
| |
| /* Note: this table must match the ai_gain_* definitions */ |
| static const short ni_gainlkup[][16] = { |
| [ai_gain_16] = {0, 1, 2, 3, 4, 5, 6, 7, |
| 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107}, |
| [ai_gain_8] = {1, 2, 4, 7, 0x101, 0x102, 0x104, 0x107}, |
| [ai_gain_14] = {1, 2, 3, 4, 5, 6, 7, |
| 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107}, |
| [ai_gain_4] = {0, 1, 4, 7}, |
| [ai_gain_611x] = {0x00a, 0x00b, 0x001, 0x002, |
| 0x003, 0x004, 0x005, 0x006}, |
| [ai_gain_622x] = {0, 1, 4, 5}, |
| [ai_gain_628x] = {1, 2, 3, 4, 5, 6, 7}, |
| [ai_gain_6143] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ai = { |
| 16, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(2.5), |
| BIP_RANGE(1), |
| BIP_RANGE(0.5), |
| BIP_RANGE(0.25), |
| BIP_RANGE(0.1), |
| BIP_RANGE(0.05), |
| UNI_RANGE(20), |
| UNI_RANGE(10), |
| UNI_RANGE(5), |
| UNI_RANGE(2), |
| UNI_RANGE(1), |
| UNI_RANGE(0.5), |
| UNI_RANGE(0.2), |
| UNI_RANGE(0.1) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ai_limited = { |
| 8, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(1), |
| BIP_RANGE(0.1), |
| UNI_RANGE(10), |
| UNI_RANGE(5), |
| UNI_RANGE(1), |
| UNI_RANGE(0.1) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ai_limited14 = { |
| 14, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(2), |
| BIP_RANGE(1), |
| BIP_RANGE(0.5), |
| BIP_RANGE(0.2), |
| BIP_RANGE(0.1), |
| UNI_RANGE(10), |
| UNI_RANGE(5), |
| UNI_RANGE(2), |
| UNI_RANGE(1), |
| UNI_RANGE(0.5), |
| UNI_RANGE(0.2), |
| UNI_RANGE(0.1) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ai_bipolar4 = { |
| 4, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(0.5), |
| BIP_RANGE(0.05) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ai_611x = { |
| 8, { |
| BIP_RANGE(50), |
| BIP_RANGE(20), |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(2), |
| BIP_RANGE(1), |
| BIP_RANGE(0.5), |
| BIP_RANGE(0.2) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_M_ai_622x = { |
| 4, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(1), |
| BIP_RANGE(0.2) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_M_ai_628x = { |
| 7, { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(2), |
| BIP_RANGE(1), |
| BIP_RANGE(0.5), |
| BIP_RANGE(0.2), |
| BIP_RANGE(0.1) |
| } |
| }; |
| |
| static const struct comedi_lrange range_ni_E_ao_ext = { |
| 4, { |
| BIP_RANGE(10), |
| UNI_RANGE(10), |
| RANGE_ext(-1, 1), |
| RANGE_ext(0, 1) |
| } |
| }; |
| |
| static const struct comedi_lrange *const ni_range_lkup[] = { |
| [ai_gain_16] = &range_ni_E_ai, |
| [ai_gain_8] = &range_ni_E_ai_limited, |
| [ai_gain_14] = &range_ni_E_ai_limited14, |
| [ai_gain_4] = &range_ni_E_ai_bipolar4, |
| [ai_gain_611x] = &range_ni_E_ai_611x, |
| [ai_gain_622x] = &range_ni_M_ai_622x, |
| [ai_gain_628x] = &range_ni_M_ai_628x, |
| [ai_gain_6143] = &range_bipolar5 |
| }; |
| |
| enum aimodes { |
| AIMODE_NONE = 0, |
| AIMODE_HALF_FULL = 1, |
| AIMODE_SCAN = 2, |
| AIMODE_SAMPLE = 3, |
| }; |
| |
| enum ni_common_subdevices { |
| NI_AI_SUBDEV, |
| NI_AO_SUBDEV, |
| NI_DIO_SUBDEV, |
| NI_8255_DIO_SUBDEV, |
| NI_UNUSED_SUBDEV, |
| NI_CALIBRATION_SUBDEV, |
| NI_EEPROM_SUBDEV, |
| NI_PFI_DIO_SUBDEV, |
| NI_CS5529_CALIBRATION_SUBDEV, |
| NI_SERIAL_SUBDEV, |
| NI_RTSI_SUBDEV, |
| NI_GPCT0_SUBDEV, |
| NI_GPCT1_SUBDEV, |
| NI_FREQ_OUT_SUBDEV, |
| NI_NUM_SUBDEVICES |
| }; |
| |
| #define NI_GPCT_SUBDEV(x) (NI_GPCT0_SUBDEV + (x)) |
| |
| enum timebase_nanoseconds { |
| TIMEBASE_1_NS = 50, |
| TIMEBASE_2_NS = 10000 |
| }; |
| |
| #define SERIAL_DISABLED 0 |
| #define SERIAL_600NS 600 |
| #define SERIAL_1_2US 1200 |
| #define SERIAL_10US 10000 |
| |
| static const int num_adc_stages_611x = 3; |
| |
| static void ni_writel(struct comedi_device *dev, unsigned int data, int reg) |
| { |
| if (dev->mmio) |
| writel(data, dev->mmio + reg); |
| else |
| outl(data, dev->iobase + reg); |
| } |
| |
| static void ni_writew(struct comedi_device *dev, unsigned int data, int reg) |
| { |
| if (dev->mmio) |
| writew(data, dev->mmio + reg); |
| else |
| outw(data, dev->iobase + reg); |
| } |
| |
| static void ni_writeb(struct comedi_device *dev, unsigned int data, int reg) |
| { |
| if (dev->mmio) |
| writeb(data, dev->mmio + reg); |
| else |
| outb(data, dev->iobase + reg); |
| } |
| |
| static unsigned int ni_readl(struct comedi_device *dev, int reg) |
| { |
| if (dev->mmio) |
| return readl(dev->mmio + reg); |
| |
| return inl(dev->iobase + reg); |
| } |
| |
| static unsigned int ni_readw(struct comedi_device *dev, int reg) |
| { |
| if (dev->mmio) |
| return readw(dev->mmio + reg); |
| |
| return inw(dev->iobase + reg); |
| } |
| |
| static unsigned int ni_readb(struct comedi_device *dev, int reg) |
| { |
| if (dev->mmio) |
| return readb(dev->mmio + reg); |
| |
| return inb(dev->iobase + reg); |
| } |
| |
| /* |
| * We automatically take advantage of STC registers that can be |
| * read/written directly in the I/O space of the board. |
| * |
| * The AT-MIO and DAQCard devices map the low 8 STC registers to |
| * iobase+reg*2. |
| * |
| * Most PCIMIO devices also map the low 8 STC registers but the |
| * 611x devices map the read registers to iobase+(addr-1)*2. |
| * For now non-windowed STC access is disabled if a PCIMIO device |
| * is detected (devpriv->mite has been initialized). |
| * |
| * The M series devices do not used windowed registers for the |
| * STC registers. The functions below handle the mapping of the |
| * windowed STC registers to the m series register offsets. |
| */ |
| |
| struct mio_regmap { |
| unsigned int mio_reg; |
| int size; |
| }; |
| |
| static const struct mio_regmap m_series_stc_write_regmap[] = { |
| [NISTC_INTA_ACK_REG] = { 0x104, 2 }, |
| [NISTC_INTB_ACK_REG] = { 0x106, 2 }, |
| [NISTC_AI_CMD2_REG] = { 0x108, 2 }, |
| [NISTC_AO_CMD2_REG] = { 0x10a, 2 }, |
| [NISTC_G0_CMD_REG] = { 0x10c, 2 }, |
| [NISTC_G1_CMD_REG] = { 0x10e, 2 }, |
| [NISTC_AI_CMD1_REG] = { 0x110, 2 }, |
| [NISTC_AO_CMD1_REG] = { 0x112, 2 }, |
| /* |
| * NISTC_DIO_OUT_REG maps to: |
| * { NI_M_DIO_REG, 4 } and { NI_M_SCXI_SER_DO_REG, 1 } |
| */ |
| [NISTC_DIO_OUT_REG] = { 0, 0 }, /* DOES NOT MAP CLEANLY */ |
| [NISTC_DIO_CTRL_REG] = { 0, 0 }, /* DOES NOT MAP CLEANLY */ |
| [NISTC_AI_MODE1_REG] = { 0x118, 2 }, |
| [NISTC_AI_MODE2_REG] = { 0x11a, 2 }, |
| [NISTC_AI_SI_LOADA_REG] = { 0x11c, 4 }, |
| [NISTC_AI_SI_LOADB_REG] = { 0x120, 4 }, |
| [NISTC_AI_SC_LOADA_REG] = { 0x124, 4 }, |
| [NISTC_AI_SC_LOADB_REG] = { 0x128, 4 }, |
| [NISTC_AI_SI2_LOADA_REG] = { 0x12c, 4 }, |
| [NISTC_AI_SI2_LOADB_REG] = { 0x130, 4 }, |
| [NISTC_G0_MODE_REG] = { 0x134, 2 }, |
| [NISTC_G1_MODE_REG] = { 0x136, 2 }, |
| [NISTC_G0_LOADA_REG] = { 0x138, 4 }, |
| [NISTC_G0_LOADB_REG] = { 0x13c, 4 }, |
| [NISTC_G1_LOADA_REG] = { 0x140, 4 }, |
| [NISTC_G1_LOADB_REG] = { 0x144, 4 }, |
| [NISTC_G0_INPUT_SEL_REG] = { 0x148, 2 }, |
| [NISTC_G1_INPUT_SEL_REG] = { 0x14a, 2 }, |
| [NISTC_AO_MODE1_REG] = { 0x14c, 2 }, |
| [NISTC_AO_MODE2_REG] = { 0x14e, 2 }, |
| [NISTC_AO_UI_LOADA_REG] = { 0x150, 4 }, |
| [NISTC_AO_UI_LOADB_REG] = { 0x154, 4 }, |
| [NISTC_AO_BC_LOADA_REG] = { 0x158, 4 }, |
| [NISTC_AO_BC_LOADB_REG] = { 0x15c, 4 }, |
| [NISTC_AO_UC_LOADA_REG] = { 0x160, 4 }, |
| [NISTC_AO_UC_LOADB_REG] = { 0x164, 4 }, |
| [NISTC_CLK_FOUT_REG] = { 0x170, 2 }, |
| [NISTC_IO_BIDIR_PIN_REG] = { 0x172, 2 }, |
| [NISTC_RTSI_TRIG_DIR_REG] = { 0x174, 2 }, |
| [NISTC_INT_CTRL_REG] = { 0x176, 2 }, |
| [NISTC_AI_OUT_CTRL_REG] = { 0x178, 2 }, |
| [NISTC_ATRIG_ETC_REG] = { 0x17a, 2 }, |
| [NISTC_AI_START_STOP_REG] = { 0x17c, 2 }, |
| [NISTC_AI_TRIG_SEL_REG] = { 0x17e, 2 }, |
| [NISTC_AI_DIV_LOADA_REG] = { 0x180, 4 }, |
| [NISTC_AO_START_SEL_REG] = { 0x184, 2 }, |
| [NISTC_AO_TRIG_SEL_REG] = { 0x186, 2 }, |
| [NISTC_G0_AUTOINC_REG] = { 0x188, 2 }, |
| [NISTC_G1_AUTOINC_REG] = { 0x18a, 2 }, |
| [NISTC_AO_MODE3_REG] = { 0x18c, 2 }, |
| [NISTC_RESET_REG] = { 0x190, 2 }, |
| [NISTC_INTA_ENA_REG] = { 0x192, 2 }, |
| [NISTC_INTA2_ENA_REG] = { 0, 0 }, /* E-Series only */ |
| [NISTC_INTB_ENA_REG] = { 0x196, 2 }, |
| [NISTC_INTB2_ENA_REG] = { 0, 0 }, /* E-Series only */ |
| [NISTC_AI_PERSONAL_REG] = { 0x19a, 2 }, |
| [NISTC_AO_PERSONAL_REG] = { 0x19c, 2 }, |
| [NISTC_RTSI_TRIGA_OUT_REG] = { 0x19e, 2 }, |
| [NISTC_RTSI_TRIGB_OUT_REG] = { 0x1a0, 2 }, |
| /* doc for following line: mhddk/nimseries/ChipObjects/tMSeries.h */ |
| [NISTC_RTSI_BOARD_REG] = { 0x1a2, 2 }, |
| [NISTC_CFG_MEM_CLR_REG] = { 0x1a4, 2 }, |
| [NISTC_ADC_FIFO_CLR_REG] = { 0x1a6, 2 }, |
| [NISTC_DAC_FIFO_CLR_REG] = { 0x1a8, 2 }, |
| [NISTC_AO_OUT_CTRL_REG] = { 0x1ac, 2 }, |
| [NISTC_AI_MODE3_REG] = { 0x1ae, 2 }, |
| }; |
| |
| static void m_series_stc_write(struct comedi_device *dev, |
| unsigned int data, unsigned int reg) |
| { |
| const struct mio_regmap *regmap; |
| |
| if (reg < ARRAY_SIZE(m_series_stc_write_regmap)) { |
| regmap = &m_series_stc_write_regmap[reg]; |
| } else { |
| dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n", |
| __func__, reg); |
| return; |
| } |
| |
| switch (regmap->size) { |
| case 4: |
| ni_writel(dev, data, regmap->mio_reg); |
| break; |
| case 2: |
| ni_writew(dev, data, regmap->mio_reg); |
| break; |
| default: |
| dev_warn(dev->class_dev, "%s: unmapped register=0x%x\n", |
| __func__, reg); |
| break; |
| } |
| } |
| |
| static const struct mio_regmap m_series_stc_read_regmap[] = { |
| [NISTC_AI_STATUS1_REG] = { 0x104, 2 }, |
| [NISTC_AO_STATUS1_REG] = { 0x106, 2 }, |
| [NISTC_G01_STATUS_REG] = { 0x108, 2 }, |
| [NISTC_AI_STATUS2_REG] = { 0, 0 }, /* Unknown */ |
| [NISTC_AO_STATUS2_REG] = { 0x10c, 2 }, |
| [NISTC_DIO_IN_REG] = { 0, 0 }, /* Unknown */ |
| [NISTC_G0_HW_SAVE_REG] = { 0x110, 4 }, |
| [NISTC_G1_HW_SAVE_REG] = { 0x114, 4 }, |
| [NISTC_G0_SAVE_REG] = { 0x118, 4 }, |
| [NISTC_G1_SAVE_REG] = { 0x11c, 4 }, |
| [NISTC_AO_UI_SAVE_REG] = { 0x120, 4 }, |
| [NISTC_AO_BC_SAVE_REG] = { 0x124, 4 }, |
| [NISTC_AO_UC_SAVE_REG] = { 0x128, 4 }, |
| [NISTC_STATUS1_REG] = { 0x136, 2 }, |
| [NISTC_DIO_SERIAL_IN_REG] = { 0x009, 1 }, |
| [NISTC_STATUS2_REG] = { 0x13a, 2 }, |
| [NISTC_AI_SI_SAVE_REG] = { 0x180, 4 }, |
| [NISTC_AI_SC_SAVE_REG] = { 0x184, 4 }, |
| }; |
| |
| static unsigned int m_series_stc_read(struct comedi_device *dev, |
| unsigned int reg) |
| { |
| const struct mio_regmap *regmap; |
| |
| if (reg < ARRAY_SIZE(m_series_stc_read_regmap)) { |
| regmap = &m_series_stc_read_regmap[reg]; |
| } else { |
| dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n", |
| __func__, reg); |
| return 0; |
| } |
| |
| switch (regmap->size) { |
| case 4: |
| return ni_readl(dev, regmap->mio_reg); |
| case 2: |
| return ni_readw(dev, regmap->mio_reg); |
| case 1: |
| return ni_readb(dev, regmap->mio_reg); |
| default: |
| dev_warn(dev->class_dev, "%s: unmapped register=0x%x\n", |
| __func__, reg); |
| return 0; |
| } |
| } |
| |
| static void ni_stc_writew(struct comedi_device *dev, |
| unsigned int data, int reg) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| if (devpriv->is_m_series) { |
| m_series_stc_write(dev, data, reg); |
| } else { |
| spin_lock_irqsave(&devpriv->window_lock, flags); |
| if (!devpriv->mite && reg < 8) { |
| ni_writew(dev, data, reg * 2); |
| } else { |
| ni_writew(dev, reg, NI_E_STC_WINDOW_ADDR_REG); |
| ni_writew(dev, data, NI_E_STC_WINDOW_DATA_REG); |
| } |
| spin_unlock_irqrestore(&devpriv->window_lock, flags); |
| } |
| } |
| |
| static void ni_stc_writel(struct comedi_device *dev, |
| unsigned int data, int reg) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (devpriv->is_m_series) { |
| m_series_stc_write(dev, data, reg); |
| } else { |
| ni_stc_writew(dev, data >> 16, reg); |
| ni_stc_writew(dev, data & 0xffff, reg + 1); |
| } |
| } |
| |
| static unsigned int ni_stc_readw(struct comedi_device *dev, int reg) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| unsigned int val; |
| |
| if (devpriv->is_m_series) { |
| val = m_series_stc_read(dev, reg); |
| } else { |
| spin_lock_irqsave(&devpriv->window_lock, flags); |
| if (!devpriv->mite && reg < 8) { |
| val = ni_readw(dev, reg * 2); |
| } else { |
| ni_writew(dev, reg, NI_E_STC_WINDOW_ADDR_REG); |
| val = ni_readw(dev, NI_E_STC_WINDOW_DATA_REG); |
| } |
| spin_unlock_irqrestore(&devpriv->window_lock, flags); |
| } |
| return val; |
| } |
| |
| static unsigned int ni_stc_readl(struct comedi_device *dev, int reg) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int val; |
| |
| if (devpriv->is_m_series) { |
| val = m_series_stc_read(dev, reg); |
| } else { |
| val = ni_stc_readw(dev, reg) << 16; |
| val |= ni_stc_readw(dev, reg + 1); |
| } |
| return val; |
| } |
| |
| static inline void ni_set_bitfield(struct comedi_device *dev, int reg, |
| unsigned int bit_mask, |
| unsigned int bit_values) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags); |
| switch (reg) { |
| case NISTC_INTA_ENA_REG: |
| devpriv->int_a_enable_reg &= ~bit_mask; |
| devpriv->int_a_enable_reg |= bit_values & bit_mask; |
| ni_stc_writew(dev, devpriv->int_a_enable_reg, reg); |
| break; |
| case NISTC_INTB_ENA_REG: |
| devpriv->int_b_enable_reg &= ~bit_mask; |
| devpriv->int_b_enable_reg |= bit_values & bit_mask; |
| ni_stc_writew(dev, devpriv->int_b_enable_reg, reg); |
| break; |
| case NISTC_IO_BIDIR_PIN_REG: |
| devpriv->io_bidirection_pin_reg &= ~bit_mask; |
| devpriv->io_bidirection_pin_reg |= bit_values & bit_mask; |
| ni_stc_writew(dev, devpriv->io_bidirection_pin_reg, reg); |
| break; |
| case NI_E_DMA_AI_AO_SEL_REG: |
| devpriv->ai_ao_select_reg &= ~bit_mask; |
| devpriv->ai_ao_select_reg |= bit_values & bit_mask; |
| ni_writeb(dev, devpriv->ai_ao_select_reg, reg); |
| break; |
| case NI_E_DMA_G0_G1_SEL_REG: |
| devpriv->g0_g1_select_reg &= ~bit_mask; |
| devpriv->g0_g1_select_reg |= bit_values & bit_mask; |
| ni_writeb(dev, devpriv->g0_g1_select_reg, reg); |
| break; |
| case NI_M_CDIO_DMA_SEL_REG: |
| devpriv->cdio_dma_select_reg &= ~bit_mask; |
| devpriv->cdio_dma_select_reg |= bit_values & bit_mask; |
| ni_writeb(dev, devpriv->cdio_dma_select_reg, reg); |
| break; |
| default: |
| dev_err(dev->class_dev, "called with invalid register %d\n", |
| reg); |
| break; |
| } |
| spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags); |
| } |
| |
| #ifdef PCIDMA |
| |
| /* selects the MITE channel to use for DMA */ |
| #define NI_STC_DMA_CHAN_SEL(x) (((x) < 4) ? BIT(x) : \ |
| ((x) == 4) ? 0x3 : \ |
| ((x) == 5) ? 0x5 : 0x0) |
| |
| /* DMA channel setup */ |
| static int ni_request_ai_mite_channel(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct mite_channel *mite_chan; |
| unsigned long flags; |
| unsigned int bits; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| mite_chan = mite_request_channel(devpriv->mite, devpriv->ai_mite_ring); |
| if (!mite_chan) { |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| dev_err(dev->class_dev, |
| "failed to reserve mite dma channel for analog input\n"); |
| return -EBUSY; |
| } |
| mite_chan->dir = COMEDI_INPUT; |
| devpriv->ai_mite_chan = mite_chan; |
| |
| bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel); |
| ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, |
| NI_E_DMA_AI_SEL_MASK, NI_E_DMA_AI_SEL(bits)); |
| |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| return 0; |
| } |
| |
| static int ni_request_ao_mite_channel(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct mite_channel *mite_chan; |
| unsigned long flags; |
| unsigned int bits; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| mite_chan = mite_request_channel(devpriv->mite, devpriv->ao_mite_ring); |
| if (!mite_chan) { |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| dev_err(dev->class_dev, |
| "failed to reserve mite dma channel for analog output\n"); |
| return -EBUSY; |
| } |
| mite_chan->dir = COMEDI_OUTPUT; |
| devpriv->ao_mite_chan = mite_chan; |
| |
| bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel); |
| ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, |
| NI_E_DMA_AO_SEL_MASK, NI_E_DMA_AO_SEL(bits)); |
| |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| return 0; |
| } |
| |
| static int ni_request_gpct_mite_channel(struct comedi_device *dev, |
| unsigned int gpct_index, |
| enum comedi_io_direction direction) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct ni_gpct *counter = &devpriv->counter_dev->counters[gpct_index]; |
| struct mite_channel *mite_chan; |
| unsigned long flags; |
| unsigned int bits; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| mite_chan = mite_request_channel(devpriv->mite, |
| devpriv->gpct_mite_ring[gpct_index]); |
| if (!mite_chan) { |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| dev_err(dev->class_dev, |
| "failed to reserve mite dma channel for counter\n"); |
| return -EBUSY; |
| } |
| mite_chan->dir = direction; |
| ni_tio_set_mite_channel(counter, mite_chan); |
| |
| bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel); |
| ni_set_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG, |
| NI_E_DMA_G0_G1_SEL_MASK(gpct_index), |
| NI_E_DMA_G0_G1_SEL(gpct_index, bits)); |
| |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| return 0; |
| } |
| |
| static int ni_request_cdo_mite_channel(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct mite_channel *mite_chan; |
| unsigned long flags; |
| unsigned int bits; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| mite_chan = mite_request_channel(devpriv->mite, devpriv->cdo_mite_ring); |
| if (!mite_chan) { |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| dev_err(dev->class_dev, |
| "failed to reserve mite dma channel for correlated digital output\n"); |
| return -EBUSY; |
| } |
| mite_chan->dir = COMEDI_OUTPUT; |
| devpriv->cdo_mite_chan = mite_chan; |
| |
| /* |
| * XXX just guessing NI_STC_DMA_CHAN_SEL() |
| * returns the right bits, under the assumption the cdio dma |
| * selection works just like ai/ao/gpct. |
| * Definitely works for dma channels 0 and 1. |
| */ |
| bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel); |
| ni_set_bitfield(dev, NI_M_CDIO_DMA_SEL_REG, |
| NI_M_CDIO_DMA_SEL_CDO_MASK, |
| NI_M_CDIO_DMA_SEL_CDO(bits)); |
| |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| return 0; |
| } |
| #endif /* PCIDMA */ |
| |
| static void ni_release_ai_mite_channel(struct comedi_device *dev) |
| { |
| #ifdef PCIDMA |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->ai_mite_chan) { |
| ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, |
| NI_E_DMA_AI_SEL_MASK, 0); |
| mite_release_channel(devpriv->ai_mite_chan); |
| devpriv->ai_mite_chan = NULL; |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| #endif /* PCIDMA */ |
| } |
| |
| static void ni_release_ao_mite_channel(struct comedi_device *dev) |
| { |
| #ifdef PCIDMA |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->ao_mite_chan) { |
| ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, |
| NI_E_DMA_AO_SEL_MASK, 0); |
| mite_release_channel(devpriv->ao_mite_chan); |
| devpriv->ao_mite_chan = NULL; |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| #endif /* PCIDMA */ |
| } |
| |
| #ifdef PCIDMA |
| static void ni_release_gpct_mite_channel(struct comedi_device *dev, |
| unsigned int gpct_index) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| 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_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG, |
| NI_E_DMA_G0_G1_SEL_MASK(gpct_index), 0); |
| ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index], |
| NULL); |
| mite_release_channel(mite_chan); |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| } |
| |
| static void ni_release_cdo_mite_channel(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->cdo_mite_chan) { |
| ni_set_bitfield(dev, NI_M_CDIO_DMA_SEL_REG, |
| NI_M_CDIO_DMA_SEL_CDO_MASK, 0); |
| mite_release_channel(devpriv->cdo_mite_chan); |
| devpriv->cdo_mite_chan = NULL; |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| } |
| |
| static void ni_e_series_enable_second_irq(struct comedi_device *dev, |
| unsigned int gpct_index, short enable) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int val = 0; |
| int reg; |
| |
| if (devpriv->is_m_series || gpct_index > 1) |
| return; |
| |
| /* |
| * e-series boards use the second irq signals to generate |
| * dma requests for their counters |
| */ |
| if (gpct_index == 0) { |
| reg = NISTC_INTA2_ENA_REG; |
| if (enable) |
| val = NISTC_INTA_ENA_G0_GATE; |
| } else { |
| reg = NISTC_INTB2_ENA_REG; |
| if (enable) |
| val = NISTC_INTB_ENA_G1_GATE; |
| } |
| ni_stc_writew(dev, val, reg); |
| } |
| #endif /* PCIDMA */ |
| |
| static void ni_clear_ai_fifo(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| static const int timeout = 10000; |
| int i; |
| |
| if (devpriv->is_6143) { |
| /* Flush the 6143 data FIFO */ |
| ni_writel(dev, 0x10, NI6143_AI_FIFO_CTRL_REG); |
| ni_writel(dev, 0x00, NI6143_AI_FIFO_CTRL_REG); |
| /* Wait for complete */ |
| for (i = 0; i < timeout; i++) { |
| if (!(ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x10)) |
| break; |
| udelay(1); |
| } |
| if (i == timeout) |
| dev_err(dev->class_dev, "FIFO flush timeout\n"); |
| } else { |
| ni_stc_writew(dev, 1, NISTC_ADC_FIFO_CLR_REG); |
| if (devpriv->is_625x) { |
| ni_writeb(dev, 0, NI_M_STATIC_AI_CTRL_REG(0)); |
| ni_writeb(dev, 1, NI_M_STATIC_AI_CTRL_REG(0)); |
| #if 0 |
| /* |
| * The NI example code does 3 convert pulses for 625x |
| * boards, But that appears to be wrong in practice. |
| */ |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| #endif |
| } |
| } |
| } |
| |
| static inline void ni_ao_win_outw(struct comedi_device *dev, |
| unsigned int data, int addr) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->window_lock, flags); |
| ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG); |
| ni_writew(dev, data, NI611X_AO_WINDOW_DATA_REG); |
| spin_unlock_irqrestore(&devpriv->window_lock, flags); |
| } |
| |
| static inline void ni_ao_win_outl(struct comedi_device *dev, |
| unsigned int data, int addr) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->window_lock, flags); |
| ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG); |
| ni_writel(dev, data, NI611X_AO_WINDOW_DATA_REG); |
| spin_unlock_irqrestore(&devpriv->window_lock, flags); |
| } |
| |
| static inline unsigned short ni_ao_win_inw(struct comedi_device *dev, int addr) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| unsigned short data; |
| |
| spin_lock_irqsave(&devpriv->window_lock, flags); |
| ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG); |
| data = ni_readw(dev, NI611X_AO_WINDOW_DATA_REG); |
| spin_unlock_irqrestore(&devpriv->window_lock, flags); |
| 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(struct comedi_device *dev, int reg, |
| unsigned int bits, unsigned int value) |
| { |
| unsigned int bit_values; |
| |
| if (value) |
| bit_values = bits; |
| else |
| bit_values = 0; |
| ni_set_bitfield(dev, reg, bits, bit_values); |
| } |
| |
| #ifdef PCIDMA |
| static void ni_sync_ai_dma(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->read_subdev; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->ai_mite_chan) |
| mite_sync_dma(devpriv->ai_mite_chan, s); |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| } |
| |
| static int ni_ai_drain_dma(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int i; |
| static const int timeout = 10000; |
| unsigned long flags; |
| int retval = 0; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->ai_mite_chan) { |
| for (i = 0; i < timeout; i++) { |
| if ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E) && |
| mite_bytes_in_transit(devpriv->ai_mite_chan) == 0) |
| break; |
| udelay(5); |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, "timed out\n"); |
| dev_err(dev->class_dev, |
| "mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n", |
| mite_bytes_in_transit(devpriv->ai_mite_chan), |
| ni_stc_readw(dev, NISTC_AI_STATUS1_REG)); |
| retval = -1; |
| } |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| |
| ni_sync_ai_dma(dev); |
| |
| return retval; |
| } |
| |
| static int ni_ao_wait_for_dma_load(struct comedi_device *dev) |
| { |
| static const int timeout = 10000; |
| int i; |
| |
| for (i = 0; i < timeout; i++) { |
| unsigned short b_status; |
| |
| b_status = ni_stc_readw(dev, NISTC_AO_STATUS1_REG); |
| if (b_status & NISTC_AO_STATUS1_FIFO_HF) |
| break; |
| /* |
| * If we poll too often, the pci bus activity seems |
| * to slow the dma transfer down. |
| */ |
| usleep_range(10, 100); |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, "timed out waiting for dma load\n"); |
| return -EPIPE; |
| } |
| return 0; |
| } |
| #endif /* PCIDMA */ |
| |
| #ifndef PCIDMA |
| |
| static void ni_ao_fifo_load(struct comedi_device *dev, |
| struct comedi_subdevice *s, int n) |
| { |
| struct ni_private *devpriv = dev->private; |
| int i; |
| unsigned short d; |
| unsigned int packed_data; |
| |
| for (i = 0; i < n; i++) { |
| comedi_buf_read_samples(s, &d, 1); |
| |
| if (devpriv->is_6xxx) { |
| packed_data = d & 0xffff; |
| /* 6711 only has 16 bit wide ao fifo */ |
| if (!devpriv->is_6711) { |
| comedi_buf_read_samples(s, &d, 1); |
| i++; |
| packed_data |= (d << 16) & 0xffff0000; |
| } |
| ni_writel(dev, packed_data, NI611X_AO_FIFO_DATA_REG); |
| } else { |
| ni_writew(dev, d, NI_E_AO_FIFO_DATA_REG); |
| } |
| } |
| } |
| |
| /* |
| * There's a small problem if the FIFO gets really low and we |
| * don't have the data to fill it. Basically, if after we fill |
| * the FIFO with all the data available, the FIFO is _still_ |
| * less than half full, we never clear the interrupt. If the |
| * IRQ is in edge mode, we never get another interrupt, because |
| * this one wasn't cleared. If in level mode, we get flooded |
| * with interrupts that we can't fulfill, because nothing ever |
| * gets put into the buffer. |
| * |
| * This kind of situation is recoverable, but it is easier to |
| * just pretend we had a FIFO underrun, since there is a good |
| * chance it will happen anyway. This is _not_ the case for |
| * RT code, as RT code might purposely be running close to the |
| * metal. Needs to be fixed eventually. |
| */ |
| static int ni_ao_fifo_half_empty(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| unsigned int nbytes; |
| unsigned int nsamples; |
| |
| nbytes = comedi_buf_read_n_available(s); |
| if (nbytes == 0) { |
| s->async->events |= COMEDI_CB_OVERFLOW; |
| return 0; |
| } |
| |
| nsamples = comedi_bytes_to_samples(s, nbytes); |
| if (nsamples > board->ao_fifo_depth / 2) |
| nsamples = board->ao_fifo_depth / 2; |
| |
| ni_ao_fifo_load(dev, s, nsamples); |
| |
| return 1; |
| } |
| |
| static int ni_ao_prep_fifo(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| unsigned int nbytes; |
| unsigned int nsamples; |
| |
| /* reset fifo */ |
| ni_stc_writew(dev, 1, NISTC_DAC_FIFO_CLR_REG); |
| if (devpriv->is_6xxx) |
| ni_ao_win_outl(dev, 0x6, NI611X_AO_FIFO_OFFSET_LOAD_REG); |
| |
| /* load some data */ |
| nbytes = comedi_buf_read_n_available(s); |
| if (nbytes == 0) |
| return 0; |
| |
| nsamples = comedi_bytes_to_samples(s, nbytes); |
| if (nsamples > board->ao_fifo_depth) |
| nsamples = board->ao_fifo_depth; |
| |
| ni_ao_fifo_load(dev, s, nsamples); |
| |
| return nsamples; |
| } |
| |
| static void ni_ai_fifo_read(struct comedi_device *dev, |
| struct comedi_subdevice *s, int n) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_async *async = s->async; |
| unsigned int dl; |
| unsigned short data; |
| int i; |
| |
| if (devpriv->is_611x) { |
| for (i = 0; i < n / 2; i++) { |
| dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG); |
| /* This may get the hi/lo data in the wrong order */ |
| data = (dl >> 16) & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| /* Check if there's a single sample stuck in the FIFO */ |
| if (n % 2) { |
| dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| } else if (devpriv->is_6143) { |
| /* |
| * This just reads the FIFO assuming the data is present, |
| * no checks on the FIFO status are performed. |
| */ |
| for (i = 0; i < n / 2; i++) { |
| dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG); |
| |
| data = (dl >> 16) & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| if (n % 2) { |
| /* Assume there is a single sample stuck in the FIFO */ |
| /* Get stranded sample into FIFO */ |
| ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG); |
| dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG); |
| data = (dl >> 16) & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| } else { |
| if (n > ARRAY_SIZE(devpriv->ai_fifo_buffer)) { |
| dev_err(dev->class_dev, |
| "bug! ai_fifo_buffer too small\n"); |
| async->events |= COMEDI_CB_ERROR; |
| return; |
| } |
| for (i = 0; i < n; i++) { |
| devpriv->ai_fifo_buffer[i] = |
| ni_readw(dev, NI_E_AI_FIFO_DATA_REG); |
| } |
| comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, n); |
| } |
| } |
| |
| static void ni_handle_fifo_half_full(struct comedi_device *dev) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct comedi_subdevice *s = dev->read_subdev; |
| int n; |
| |
| n = board->ai_fifo_depth / 2; |
| |
| ni_ai_fifo_read(dev, s, n); |
| } |
| #endif |
| |
| /* Empties the AI fifo */ |
| static void ni_handle_fifo_dregs(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->read_subdev; |
| unsigned int dl; |
| unsigned short data; |
| int i; |
| |
| if (devpriv->is_611x) { |
| while ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E) == 0) { |
| dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG); |
| |
| /* This may get the hi/lo data in the wrong order */ |
| data = dl >> 16; |
| comedi_buf_write_samples(s, &data, 1); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| } else if (devpriv->is_6143) { |
| i = 0; |
| while (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x04) { |
| dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG); |
| |
| /* This may get the hi/lo data in the wrong order */ |
| data = dl >> 16; |
| comedi_buf_write_samples(s, &data, 1); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| i += 2; |
| } |
| /* Check if stranded sample is present */ |
| if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x01) { |
| /* Get stranded sample into FIFO */ |
| ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG); |
| dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG); |
| data = (dl >> 16) & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| |
| } else { |
| unsigned short fe; /* fifo empty */ |
| |
| fe = ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E; |
| while (fe == 0) { |
| for (i = 0; |
| i < ARRAY_SIZE(devpriv->ai_fifo_buffer); i++) { |
| fe = ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E; |
| if (fe) |
| break; |
| devpriv->ai_fifo_buffer[i] = |
| ni_readw(dev, NI_E_AI_FIFO_DATA_REG); |
| } |
| comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, i); |
| } |
| } |
| } |
| |
| static void get_last_sample_611x(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->read_subdev; |
| unsigned short data; |
| unsigned int dl; |
| |
| if (!devpriv->is_611x) |
| return; |
| |
| /* Check if there's a single sample stuck in the FIFO */ |
| if (ni_readb(dev, NI_E_STATUS_REG) & 0x80) { |
| dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG); |
| data = dl & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| } |
| |
| static void get_last_sample_6143(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->read_subdev; |
| unsigned short data; |
| unsigned int dl; |
| |
| if (!devpriv->is_6143) |
| return; |
| |
| /* Check if there's a single sample stuck in the FIFO */ |
| if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x01) { |
| /* Get stranded sample into FIFO */ |
| ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG); |
| dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG); |
| |
| /* This may get the hi/lo data in the wrong order */ |
| data = (dl >> 16) & 0xffff; |
| comedi_buf_write_samples(s, &data, 1); |
| } |
| } |
| |
| static void shutdown_ai_command(struct comedi_device *dev) |
| { |
| struct comedi_subdevice *s = dev->read_subdev; |
| |
| #ifdef PCIDMA |
| ni_ai_drain_dma(dev); |
| #endif |
| ni_handle_fifo_dregs(dev); |
| get_last_sample_611x(dev); |
| get_last_sample_6143(dev); |
| |
| s->async->events |= COMEDI_CB_EOA; |
| } |
| |
| static void ni_handle_eos(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (devpriv->aimode == AIMODE_SCAN) { |
| #ifdef PCIDMA |
| static const int timeout = 10; |
| int i; |
| |
| for (i = 0; i < timeout; i++) { |
| ni_sync_ai_dma(dev); |
| if ((s->async->events & COMEDI_CB_EOS)) |
| break; |
| udelay(1); |
| } |
| #else |
| ni_handle_fifo_dregs(dev); |
| s->async->events |= COMEDI_CB_EOS; |
| #endif |
| } |
| /* handle special case of single scan */ |
| if (devpriv->ai_cmd2 & NISTC_AI_CMD2_END_ON_EOS) |
| shutdown_ai_command(dev); |
| } |
| |
| static void handle_gpct_interrupt(struct comedi_device *dev, |
| unsigned short counter_index) |
| { |
| #ifdef PCIDMA |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s; |
| |
| s = &dev->subdevices[NI_GPCT_SUBDEV(counter_index)]; |
| |
| ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index], |
| s); |
| comedi_handle_events(dev, s); |
| #endif |
| } |
| |
| static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status) |
| { |
| unsigned short ack = 0; |
| |
| if (a_status & NISTC_AI_STATUS1_SC_TC) |
| ack |= NISTC_INTA_ACK_AI_SC_TC; |
| if (a_status & NISTC_AI_STATUS1_START1) |
| ack |= NISTC_INTA_ACK_AI_START1; |
| if (a_status & NISTC_AI_STATUS1_START) |
| ack |= NISTC_INTA_ACK_AI_START; |
| if (a_status & NISTC_AI_STATUS1_STOP) |
| ack |= NISTC_INTA_ACK_AI_STOP; |
| if (a_status & NISTC_AI_STATUS1_OVER) |
| ack |= NISTC_INTA_ACK_AI_ERR; |
| if (ack) |
| ni_stc_writew(dev, ack, NISTC_INTA_ACK_REG); |
| } |
| |
| static void handle_a_interrupt(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned short status) |
| { |
| struct comedi_cmd *cmd = &s->async->cmd; |
| |
| /* test for all uncommon interrupt events at the same time */ |
| if (status & (NISTC_AI_STATUS1_ERR | |
| NISTC_AI_STATUS1_SC_TC | NISTC_AI_STATUS1_START1)) { |
| if (status == 0xffff) { |
| dev_err(dev->class_dev, "Card removed?\n"); |
| /* |
| * We probably aren't even running a command now, |
| * so it's a good idea to be careful. |
| */ |
| if (comedi_is_subdevice_running(s)) |
| s->async->events |= COMEDI_CB_ERROR; |
| return; |
| } |
| if (status & NISTC_AI_STATUS1_ERR) { |
| dev_err(dev->class_dev, "ai error a_status=%04x\n", |
| status); |
| |
| shutdown_ai_command(dev); |
| |
| s->async->events |= COMEDI_CB_ERROR; |
| if (status & NISTC_AI_STATUS1_OVER) |
| s->async->events |= COMEDI_CB_OVERFLOW; |
| return; |
| } |
| if (status & NISTC_AI_STATUS1_SC_TC) { |
| if (cmd->stop_src == TRIG_COUNT) |
| shutdown_ai_command(dev); |
| } |
| } |
| #ifndef PCIDMA |
| if (status & NISTC_AI_STATUS1_FIFO_HF) { |
| int i; |
| static const int timeout = 10; |
| /* |
| * PCMCIA cards (at least 6036) seem to stop producing |
| * interrupts if we fail to get the fifo less than half |
| * full, so loop to be sure. |
| */ |
| for (i = 0; i < timeout; ++i) { |
| ni_handle_fifo_half_full(dev); |
| if ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_HF) == 0) |
| break; |
| } |
| } |
| #endif /* !PCIDMA */ |
| |
| if (status & NISTC_AI_STATUS1_STOP) |
| ni_handle_eos(dev, s); |
| } |
| |
| static void ack_b_interrupt(struct comedi_device *dev, unsigned short b_status) |
| { |
| unsigned short ack = 0; |
| |
| if (b_status & NISTC_AO_STATUS1_BC_TC) |
| ack |= NISTC_INTB_ACK_AO_BC_TC; |
| if (b_status & NISTC_AO_STATUS1_OVERRUN) |
| ack |= NISTC_INTB_ACK_AO_ERR; |
| if (b_status & NISTC_AO_STATUS1_START) |
| ack |= NISTC_INTB_ACK_AO_START; |
| if (b_status & NISTC_AO_STATUS1_START1) |
| ack |= NISTC_INTB_ACK_AO_START1; |
| if (b_status & NISTC_AO_STATUS1_UC_TC) |
| ack |= NISTC_INTB_ACK_AO_UC_TC; |
| if (b_status & NISTC_AO_STATUS1_UI2_TC) |
| ack |= NISTC_INTB_ACK_AO_UI2_TC; |
| if (b_status & NISTC_AO_STATUS1_UPDATE) |
| ack |= NISTC_INTB_ACK_AO_UPDATE; |
| if (ack) |
| ni_stc_writew(dev, ack, NISTC_INTB_ACK_REG); |
| } |
| |
| static void handle_b_interrupt(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned short b_status) |
| { |
| if (b_status == 0xffff) |
| return; |
| if (b_status & NISTC_AO_STATUS1_OVERRUN) { |
| dev_err(dev->class_dev, |
| "AO FIFO underrun status=0x%04x status2=0x%04x\n", |
| b_status, ni_stc_readw(dev, NISTC_AO_STATUS2_REG)); |
| s->async->events |= COMEDI_CB_OVERFLOW; |
| } |
| |
| if (s->async->cmd.stop_src != TRIG_NONE && |
| b_status & NISTC_AO_STATUS1_BC_TC) |
| s->async->events |= COMEDI_CB_EOA; |
| |
| #ifndef PCIDMA |
| if (b_status & NISTC_AO_STATUS1_FIFO_REQ) { |
| int ret; |
| |
| ret = ni_ao_fifo_half_empty(dev, s); |
| if (!ret) { |
| dev_err(dev->class_dev, "AO buffer underrun\n"); |
| ni_set_bits(dev, NISTC_INTB_ENA_REG, |
| NISTC_INTB_ENA_AO_FIFO | |
| NISTC_INTB_ENA_AO_ERR, 0); |
| s->async->events |= COMEDI_CB_OVERFLOW; |
| } |
| } |
| #endif |
| } |
| |
| static void ni_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s, |
| void *data, unsigned int num_bytes, |
| unsigned int chan_index) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_async *async = s->async; |
| struct comedi_cmd *cmd = &async->cmd; |
| unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes); |
| unsigned short *array = data; |
| unsigned int *larray = data; |
| unsigned int i; |
| #ifdef PCIDMA |
| __le16 *barray = data; |
| __le32 *blarray = data; |
| #endif |
| |
| for (i = 0; i < nsamples; i++) { |
| #ifdef PCIDMA |
| if (s->subdev_flags & SDF_LSAMPL) |
| larray[i] = le32_to_cpu(blarray[i]); |
| else |
| array[i] = le16_to_cpu(barray[i]); |
| #endif |
| if (s->subdev_flags & SDF_LSAMPL) |
| larray[i] += devpriv->ai_offset[chan_index]; |
| else |
| array[i] += devpriv->ai_offset[chan_index]; |
| chan_index++; |
| chan_index %= cmd->chanlist_len; |
| } |
| } |
| |
| #ifdef PCIDMA |
| |
| static int ni_ai_setup_MITE_dma(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->read_subdev; |
| int retval; |
| unsigned long flags; |
| |
| retval = ni_request_ai_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->ai_mite_chan) { |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| return -EIO; |
| } |
| |
| if (devpriv->is_611x || devpriv->is_6143) |
| mite_prep_dma(devpriv->ai_mite_chan, 32, 16); |
| else if (devpriv->is_628x) |
| mite_prep_dma(devpriv->ai_mite_chan, 32, 32); |
| else |
| mite_prep_dma(devpriv->ai_mite_chan, 16, 16); |
| |
| /*start the MITE */ |
| mite_dma_arm(devpriv->ai_mite_chan); |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| |
| return 0; |
| } |
| |
| static int ni_ao_setup_MITE_dma(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s = dev->write_subdev; |
| int retval; |
| unsigned long flags; |
| |
| retval = ni_request_ao_mite_channel(dev); |
| if (retval) |
| return retval; |
| |
| /* read alloc the entire buffer */ |
| comedi_buf_read_alloc(s, s->async->prealloc_bufsz); |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->ao_mite_chan) { |
| if (devpriv->is_611x || devpriv->is_6713) { |
| mite_prep_dma(devpriv->ao_mite_chan, 32, 32); |
| } else { |
| /* |
| * Doing 32 instead of 16 bit wide transfers from |
| * memory makes the mite do 32 bit pci transfers, |
| * doubling pci bandwidth. |
| */ |
| mite_prep_dma(devpriv->ao_mite_chan, 16, 32); |
| } |
| mite_dma_arm(devpriv->ao_mite_chan); |
| } else { |
| retval = -EIO; |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| |
| return retval; |
| } |
| |
| #endif /* PCIDMA */ |
| |
| /* |
| * used for both cancel ioctl and board initialization |
| * |
| * this is pretty harsh for a cancel, but it works... |
| */ |
| static int ni_ai_reset(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int ai_personal; |
| unsigned int ai_out_ctrl; |
| |
| ni_release_ai_mite_channel(dev); |
| /* ai configuration */ |
| ni_stc_writew(dev, NISTC_RESET_AI_CFG_START | NISTC_RESET_AI, |
| NISTC_RESET_REG); |
| |
| ni_set_bits(dev, NISTC_INTA_ENA_REG, NISTC_INTA_ENA_AI_MASK, 0); |
| |
| ni_clear_ai_fifo(dev); |
| |
| if (!devpriv->is_6143) |
| ni_writeb(dev, NI_E_MISC_CMD_EXT_ATRIG, NI_E_MISC_CMD_REG); |
| |
| ni_stc_writew(dev, NISTC_AI_CMD1_DISARM, NISTC_AI_CMD1_REG); |
| ni_stc_writew(dev, NISTC_AI_MODE1_START_STOP | |
| NISTC_AI_MODE1_RSVD |
| /*| NISTC_AI_MODE1_TRIGGER_ONCE */, |
| NISTC_AI_MODE1_REG); |
| ni_stc_writew(dev, 0, NISTC_AI_MODE2_REG); |
| /* generate FIFO interrupts on non-empty */ |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_NE, |
| NISTC_AI_MODE3_REG); |
| |
| ai_personal = NISTC_AI_PERSONAL_SHIFTIN_PW | |
| NISTC_AI_PERSONAL_SOC_POLARITY | |
| NISTC_AI_PERSONAL_LOCALMUX_CLK_PW; |
| ai_out_ctrl = NISTC_AI_OUT_CTRL_SCAN_IN_PROG_SEL(3) | |
| NISTC_AI_OUT_CTRL_EXTMUX_CLK_SEL(0) | |
| NISTC_AI_OUT_CTRL_LOCALMUX_CLK_SEL(2) | |
| NISTC_AI_OUT_CTRL_SC_TC_SEL(3); |
| if (devpriv->is_611x) { |
| ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_HIGH; |
| } else if (devpriv->is_6143) { |
| ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_LOW; |
| } else { |
| ai_personal |= NISTC_AI_PERSONAL_CONVERT_PW; |
| if (devpriv->is_622x) |
| ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_HIGH; |
| else |
| ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_LOW; |
| } |
| ni_stc_writew(dev, ai_personal, NISTC_AI_PERSONAL_REG); |
| ni_stc_writew(dev, ai_out_ctrl, NISTC_AI_OUT_CTRL_REG); |
| |
| /* the following registers should not be changed, because there |
| * are no backup registers in devpriv. If you want to change |
| * any of these, add a backup register and other appropriate code: |
| * NISTC_AI_MODE1_REG |
| * NISTC_AI_MODE3_REG |
| * NISTC_AI_PERSONAL_REG |
| * NISTC_AI_OUT_CTRL_REG |
| */ |
| |
| /* clear interrupts */ |
| ni_stc_writew(dev, NISTC_INTA_ACK_AI_ALL, NISTC_INTA_ACK_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AI_CFG_END, NISTC_RESET_REG); |
| |
| return 0; |
| } |
| |
| static int ni_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| unsigned long flags; |
| int count; |
| |
| /* lock to avoid race with interrupt handler */ |
| spin_lock_irqsave(&dev->spinlock, flags); |
| #ifndef PCIDMA |
| ni_handle_fifo_dregs(dev); |
| #else |
| ni_sync_ai_dma(dev); |
| #endif |
| count = comedi_buf_n_bytes_ready(s); |
| spin_unlock_irqrestore(&dev->spinlock, flags); |
| |
| return count; |
| } |
| |
| static void ni_prime_channelgain_list(struct comedi_device *dev) |
| { |
| int i; |
| |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, NISTC_AI_CMD1_REG); |
| for (i = 0; i < NI_TIMEOUT; ++i) { |
| if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E)) { |
| ni_stc_writew(dev, 1, NISTC_ADC_FIFO_CLR_REG); |
| return; |
| } |
| udelay(1); |
| } |
| dev_err(dev->class_dev, "timeout loading channel/gain list\n"); |
| } |
| |
| static void ni_m_series_load_channelgain_list(struct comedi_device *dev, |
| unsigned int n_chan, |
| unsigned int *list) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| unsigned int chan, range, aref; |
| unsigned int i; |
| unsigned int dither; |
| unsigned int range_code; |
| |
| ni_stc_writew(dev, 1, NISTC_CFG_MEM_CLR_REG); |
| |
| if ((list[0] & CR_ALT_SOURCE)) { |
| unsigned int bypass_bits; |
| |
| chan = CR_CHAN(list[0]); |
| range = CR_RANGE(list[0]); |
| range_code = ni_gainlkup[board->gainlkup][range]; |
| dither = (list[0] & CR_ALT_FILTER) != 0; |
| bypass_bits = NI_M_CFG_BYPASS_FIFO | |
| NI_M_CFG_BYPASS_AI_CHAN(chan) | |
| NI_M_CFG_BYPASS_AI_GAIN(range_code) | |
| devpriv->ai_calib_source; |
| if (dither) |
| bypass_bits |= NI_M_CFG_BYPASS_AI_DITHER; |
| /* don't use 2's complement encoding */ |
| bypass_bits |= NI_M_CFG_BYPASS_AI_POLARITY; |
| ni_writel(dev, bypass_bits, NI_M_CFG_BYPASS_FIFO_REG); |
| } else { |
| ni_writel(dev, 0, NI_M_CFG_BYPASS_FIFO_REG); |
| } |
| for (i = 0; i < n_chan; i++) { |
| unsigned int config_bits = 0; |
| |
| chan = CR_CHAN(list[i]); |
| aref = CR_AREF(list[i]); |
| range = CR_RANGE(list[i]); |
| dither = (list[i] & CR_ALT_FILTER) != 0; |
| |
| range_code = ni_gainlkup[board->gainlkup][range]; |
| devpriv->ai_offset[i] = 0; |
| switch (aref) { |
| case AREF_DIFF: |
| config_bits |= NI_M_AI_CFG_CHAN_TYPE_DIFF; |
| break; |
| case AREF_COMMON: |
| config_bits |= NI_M_AI_CFG_CHAN_TYPE_COMMON; |
| break; |
| case AREF_GROUND: |
| config_bits |= NI_M_AI_CFG_CHAN_TYPE_GROUND; |
| break; |
| case AREF_OTHER: |
| break; |
| } |
| config_bits |= NI_M_AI_CFG_CHAN_SEL(chan); |
| config_bits |= NI_M_AI_CFG_BANK_SEL(chan); |
| config_bits |= NI_M_AI_CFG_GAIN(range_code); |
| if (i == n_chan - 1) |
| config_bits |= NI_M_AI_CFG_LAST_CHAN; |
| if (dither) |
| config_bits |= NI_M_AI_CFG_DITHER; |
| /* don't use 2's complement encoding */ |
| config_bits |= NI_M_AI_CFG_POLARITY; |
| ni_writew(dev, config_bits, NI_M_AI_CFG_FIFO_DATA_REG); |
| } |
| ni_prime_channelgain_list(dev); |
| } |
| |
| /* |
| * Notes on the 6110 and 6111: |
| * These boards a slightly different than the rest of the series, since |
| * they have multiple A/D converters. |
| * From the driver side, the configuration memory is a |
| * little different. |
| * Configuration Memory Low: |
| * bits 15-9: same |
| * bit 8: unipolar/bipolar (should be 0 for bipolar) |
| * bits 0-3: gain. This is 4 bits instead of 3 for the other boards |
| * 1001 gain=0.1 (+/- 50) |
| * 1010 0.2 |
| * 1011 0.1 |
| * 0001 1 |
| * 0010 2 |
| * 0011 5 |
| * 0100 10 |
| * 0101 20 |
| * 0110 50 |
| * Configuration Memory High: |
| * bits 12-14: Channel Type |
| * 001 for differential |
| * 000 for calibration |
| * bit 11: coupling (this is not currently handled) |
| * 1 AC coupling |
| * 0 DC coupling |
| * bits 0-2: channel |
| * valid channels are 0-3 |
| */ |
| static void ni_load_channelgain_list(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int n_chan, unsigned int *list) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| unsigned int offset = (s->maxdata + 1) >> 1; |
| unsigned int chan, range, aref; |
| unsigned int i; |
| unsigned int hi, lo; |
| unsigned int dither; |
| |
| if (devpriv->is_m_series) { |
| ni_m_series_load_channelgain_list(dev, n_chan, list); |
| return; |
| } |
| if (n_chan == 1 && !devpriv->is_611x && !devpriv->is_6143) { |
| if (devpriv->changain_state && |
| devpriv->changain_spec == list[0]) { |
| /* ready to go. */ |
| return; |
| } |
| devpriv->changain_state = 1; |
| devpriv->changain_spec = list[0]; |
| } else { |
| devpriv->changain_state = 0; |
| } |
| |
| ni_stc_writew(dev, 1, NISTC_CFG_MEM_CLR_REG); |
| |
| /* Set up Calibration mode if required */ |
| if (devpriv->is_6143) { |
| if ((list[0] & CR_ALT_SOURCE) && |
| !devpriv->ai_calib_source_enabled) { |
| /* Strobe Relay enable bit */ |
| ni_writew(dev, devpriv->ai_calib_source | |
| NI6143_CALIB_CHAN_RELAY_ON, |
| NI6143_CALIB_CHAN_REG); |
| ni_writew(dev, devpriv->ai_calib_source, |
| NI6143_CALIB_CHAN_REG); |
| devpriv->ai_calib_source_enabled = 1; |
| /* Allow relays to change */ |
| msleep_interruptible(100); |
| } else if (!(list[0] & CR_ALT_SOURCE) && |
| devpriv->ai_calib_source_enabled) { |
| /* Strobe Relay disable bit */ |
| ni_writew(dev, devpriv->ai_calib_source | |
| NI6143_CALIB_CHAN_RELAY_OFF, |
| NI6143_CALIB_CHAN_REG); |
| ni_writew(dev, devpriv->ai_calib_source, |
| NI6143_CALIB_CHAN_REG); |
| devpriv->ai_calib_source_enabled = 0; |
| /* Allow relays to change */ |
| msleep_interruptible(100); |
| } |
| } |
| |
| for (i = 0; i < n_chan; i++) { |
| if (!devpriv->is_6143 && (list[i] & CR_ALT_SOURCE)) |
| chan = devpriv->ai_calib_source; |
| else |
| chan = CR_CHAN(list[i]); |
| aref = CR_AREF(list[i]); |
| range = CR_RANGE(list[i]); |
| dither = (list[i] & CR_ALT_FILTER) != 0; |
| |
| /* fix the external/internal range differences */ |
| range = ni_gainlkup[board->gainlkup][range]; |
| if (devpriv->is_611x) |
| devpriv->ai_offset[i] = offset; |
| else |
| devpriv->ai_offset[i] = (range & 0x100) ? 0 : offset; |
| |
| hi = 0; |
| if ((list[i] & CR_ALT_SOURCE)) { |
| if (devpriv->is_611x) |
| ni_writew(dev, CR_CHAN(list[i]) & 0x0003, |
| NI611X_CALIB_CHAN_SEL_REG); |
| } else { |
| if (devpriv->is_611x) |
| aref = AREF_DIFF; |
| else if (devpriv->is_6143) |
| aref = AREF_OTHER; |
| switch (aref) { |
| case AREF_DIFF: |
| hi |= NI_E_AI_CFG_HI_TYPE_DIFF; |
| break; |
| case AREF_COMMON: |
| hi |= NI_E_AI_CFG_HI_TYPE_COMMON; |
| break; |
| case AREF_GROUND: |
| hi |= NI_E_AI_CFG_HI_TYPE_GROUND; |
| break; |
| case AREF_OTHER: |
| break; |
| } |
| } |
| hi |= NI_E_AI_CFG_HI_CHAN(chan); |
| |
| ni_writew(dev, hi, NI_E_AI_CFG_HI_REG); |
| |
| if (!devpriv->is_6143) { |
| lo = NI_E_AI_CFG_LO_GAIN(range); |
| |
| if (i == n_chan - 1) |
| lo |= NI_E_AI_CFG_LO_LAST_CHAN; |
| if (dither) |
| lo |= NI_E_AI_CFG_LO_DITHER; |
| |
| ni_writew(dev, lo, NI_E_AI_CFG_LO_REG); |
| } |
| } |
| |
| /* prime the channel/gain list */ |
| if (!devpriv->is_611x && !devpriv->is_6143) |
| ni_prime_channelgain_list(dev); |
| } |
| |
| static int ni_ai_insn_read(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int mask = s->maxdata; |
| int i, n; |
| unsigned int signbits; |
| unsigned int d; |
| |
| ni_load_channelgain_list(dev, s, 1, &insn->chanspec); |
| |
| ni_clear_ai_fifo(dev); |
| |
| signbits = devpriv->ai_offset[0]; |
| if (devpriv->is_611x) { |
| for (n = 0; n < num_adc_stages_611x; n++) { |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| udelay(1); |
| } |
| for (n = 0; n < insn->n; n++) { |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| /* The 611x has screwy 32-bit FIFOs. */ |
| d = 0; |
| for (i = 0; i < NI_TIMEOUT; i++) { |
| if (ni_readb(dev, NI_E_STATUS_REG) & 0x80) { |
| d = ni_readl(dev, |
| NI611X_AI_FIFO_DATA_REG); |
| d >>= 16; |
| d &= 0xffff; |
| break; |
| } |
| if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E)) { |
| d = ni_readl(dev, |
| NI611X_AI_FIFO_DATA_REG); |
| d &= 0xffff; |
| break; |
| } |
| } |
| if (i == NI_TIMEOUT) { |
| dev_err(dev->class_dev, "timeout\n"); |
| return -ETIME; |
| } |
| d += signbits; |
| data[n] = d & 0xffff; |
| } |
| } else if (devpriv->is_6143) { |
| for (n = 0; n < insn->n; n++) { |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| |
| /* |
| * The 6143 has 32-bit FIFOs. You need to strobe a |
| * bit to move a single 16bit stranded sample into |
| * the FIFO. |
| */ |
| d = 0; |
| for (i = 0; i < NI_TIMEOUT; i++) { |
| if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & |
| 0x01) { |
| /* Get stranded sample into FIFO */ |
| ni_writel(dev, 0x01, |
| NI6143_AI_FIFO_CTRL_REG); |
| d = ni_readl(dev, |
| NI6143_AI_FIFO_DATA_REG); |
| break; |
| } |
| } |
| if (i == NI_TIMEOUT) { |
| dev_err(dev->class_dev, "timeout\n"); |
| return -ETIME; |
| } |
| data[n] = (((d >> 16) & 0xFFFF) + signbits) & 0xFFFF; |
| } |
| } else { |
| for (n = 0; n < insn->n; n++) { |
| ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, |
| NISTC_AI_CMD1_REG); |
| for (i = 0; i < NI_TIMEOUT; i++) { |
| if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) & |
| NISTC_AI_STATUS1_FIFO_E)) |
| break; |
| } |
| if (i == NI_TIMEOUT) { |
| dev_err(dev->class_dev, "timeout\n"); |
| return -ETIME; |
| } |
| if (devpriv->is_m_series) { |
| d = ni_readl(dev, NI_M_AI_FIFO_DATA_REG); |
| d &= mask; |
| data[n] = d; |
| } else { |
| d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG); |
| d += signbits; |
| data[n] = d & 0xffff; |
| } |
| } |
| } |
| return insn->n; |
| } |
| |
| static int ni_ns_to_timer(const struct comedi_device *dev, |
| unsigned int nanosec, unsigned int flags) |
| { |
| struct ni_private *devpriv = dev->private; |
| int divider; |
| |
| switch (flags & CMDF_ROUND_MASK) { |
| case CMDF_ROUND_NEAREST: |
| default: |
| divider = DIV_ROUND_CLOSEST(nanosec, devpriv->clock_ns); |
| break; |
| case CMDF_ROUND_DOWN: |
| divider = (nanosec) / devpriv->clock_ns; |
| break; |
| case CMDF_ROUND_UP: |
| divider = DIV_ROUND_UP(nanosec, devpriv->clock_ns); |
| break; |
| } |
| return divider - 1; |
| } |
| |
| static unsigned int ni_timer_to_ns(const struct comedi_device *dev, int timer) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| return devpriv->clock_ns * (timer + 1); |
| } |
| |
| static void ni_cmd_set_mite_transfer(struct mite_ring *ring, |
| struct comedi_subdevice *sdev, |
| const struct comedi_cmd *cmd, |
| unsigned int max_count) |
| { |
| #ifdef PCIDMA |
| unsigned int nbytes = max_count; |
| |
| if (cmd->stop_arg > 0 && cmd->stop_arg < max_count) |
| nbytes = cmd->stop_arg; |
| nbytes *= comedi_bytes_per_scan(sdev); |
| |
| if (nbytes > sdev->async->prealloc_bufsz) { |
| if (cmd->stop_arg > 0) |
| dev_err(sdev->device->class_dev, |
| "%s: tried exact data transfer limits greater than buffer size\n", |
| __func__); |
| |
| /* |
| * we can only transfer up to the size of the buffer. In this |
| * case, the user is expected to continue to write into the |
| * comedi buffer (already implemented as a ring buffer). |
| */ |
| nbytes = sdev->async->prealloc_bufsz; |
| } |
| |
| mite_init_ring_descriptors(ring, sdev, nbytes); |
| #else |
| dev_err(sdev->device->class_dev, |
| "%s: exact data transfer limits not implemented yet without DMA\n", |
| __func__); |
| #endif |
| } |
| |
| static unsigned int ni_min_ai_scan_period_ns(struct comedi_device *dev, |
| unsigned int num_channels) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| |
| /* simultaneously-sampled inputs */ |
| if (devpriv->is_611x || devpriv->is_6143) |
| return board->ai_speed; |
| |
| /* multiplexed inputs */ |
| return board->ai_speed * num_channels; |
| } |
| |
| static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, |
| struct comedi_cmd *cmd) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| int err = 0; |
| unsigned int sources; |
| |
| /* Step 1 : check if triggers are trivially valid */ |
| |
| err |= comedi_check_trigger_src(&cmd->start_src, |
| TRIG_NOW | TRIG_INT | TRIG_EXT); |
| err |= comedi_check_trigger_src(&cmd->scan_begin_src, |
| TRIG_TIMER | TRIG_EXT); |
| |
| sources = TRIG_TIMER | TRIG_EXT; |
| if (devpriv->is_611x || devpriv->is_6143) |
| sources |= TRIG_NOW; |
| err |= comedi_check_trigger_src(&cmd->convert_src, sources); |
| |
| 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->convert_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 */ |
| |
| switch (cmd->start_src) { |
| case TRIG_NOW: |
| case TRIG_INT: |
| err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
| break; |
| case TRIG_EXT: |
| err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->start_arg), |
| NI_AI_StartTrigger, |
| &devpriv->routing_tables, 1); |
| break; |
| } |
| |
| if (cmd->scan_begin_src == TRIG_TIMER) { |
| err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, |
| ni_min_ai_scan_period_ns(dev, cmd->chanlist_len)); |
| err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, |
| devpriv->clock_ns * |
| 0xffffff); |
| } else if (cmd->scan_begin_src == TRIG_EXT) { |
| /* external trigger */ |
| err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->scan_begin_arg), |
| NI_AI_SampleClock, |
| &devpriv->routing_tables, 1); |
| } else { /* TRIG_OTHER */ |
| err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); |
| } |
| |
| if (cmd->convert_src == TRIG_TIMER) { |
| if (devpriv->is_611x || devpriv->is_6143) { |
| err |= comedi_check_trigger_arg_is(&cmd->convert_arg, |
| 0); |
| } else { |
| err |= comedi_check_trigger_arg_min(&cmd->convert_arg, |
| board->ai_speed); |
| err |= comedi_check_trigger_arg_max(&cmd->convert_arg, |
| devpriv->clock_ns * |
| 0xffff); |
| } |
| } else if (cmd->convert_src == TRIG_EXT) { |
| /* external trigger */ |
| err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->convert_arg), |
| NI_AI_ConvertClock, |
| &devpriv->routing_tables, 1); |
| } else if (cmd->convert_src == TRIG_NOW) { |
| 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) { |
| unsigned int max_count = 0x01000000; |
| |
| if (devpriv->is_611x) |
| max_count -= num_adc_stages_611x; |
| err |= comedi_check_trigger_arg_max(&cmd->stop_arg, max_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) { |
| unsigned int tmp = cmd->scan_begin_arg; |
| |
| cmd->scan_begin_arg = |
| ni_timer_to_ns(dev, ni_ns_to_timer(dev, |
| cmd->scan_begin_arg, |
| cmd->flags)); |
| if (tmp != cmd->scan_begin_arg) |
| err++; |
| } |
| if (cmd->convert_src == TRIG_TIMER) { |
| if (!devpriv->is_611x && !devpriv->is_6143) { |
| unsigned int tmp = cmd->convert_arg; |
| |
| cmd->convert_arg = |
| ni_timer_to_ns(dev, ni_ns_to_timer(dev, |
| cmd->convert_arg, |
| cmd->flags)); |
| if (tmp != cmd->convert_arg) |
| err++; |
| if (cmd->scan_begin_src == TRIG_TIMER && |
| cmd->scan_begin_arg < |
| cmd->convert_arg * cmd->scan_end_arg) { |
| cmd->scan_begin_arg = |
| cmd->convert_arg * cmd->scan_end_arg; |
| err++; |
| } |
| } |
| } |
| |
| if (err) |
| return 4; |
| |
| return 0; |
| } |
| |
| static int ni_ai_inttrig(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int trig_num) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_cmd *cmd = &s->async->cmd; |
| |
| if (trig_num != cmd->start_arg) |
| return -EINVAL; |
| |
| ni_stc_writew(dev, NISTC_AI_CMD2_START1_PULSE | devpriv->ai_cmd2, |
| NISTC_AI_CMD2_REG); |
| s->async->inttrig = NULL; |
| |
| return 1; |
| } |
| |
| static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| const struct comedi_cmd *cmd = &s->async->cmd; |
| int timer; |
| int mode1 = 0; /* mode1 is needed for both stop and convert */ |
| int mode2 = 0; |
| int start_stop_select = 0; |
| unsigned int stop_count; |
| int interrupt_a_enable = 0; |
| unsigned int ai_trig; |
| |
| if (dev->irq == 0) { |
| dev_err(dev->class_dev, "cannot run command without an irq\n"); |
| return -EIO; |
| } |
| ni_clear_ai_fifo(dev); |
| |
| ni_load_channelgain_list(dev, s, cmd->chanlist_len, cmd->chanlist); |
| |
| /* start configuration */ |
| ni_stc_writew(dev, NISTC_RESET_AI_CFG_START, NISTC_RESET_REG); |
| |
| /* |
| * Disable analog triggering for now, since it interferes |
| * with the use of pfi0. |
| */ |
| devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_ENA; |
| ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG); |
| |
| ai_trig = NISTC_AI_TRIG_START2_SEL(0) | NISTC_AI_TRIG_START1_SYNC; |
| switch (cmd->start_src) { |
| case TRIG_INT: |
| case TRIG_NOW: |
| ai_trig |= NISTC_AI_TRIG_START1_EDGE | |
| NISTC_AI_TRIG_START1_SEL(0); |
| break; |
| case TRIG_EXT: |
| ai_trig |= NISTC_AI_TRIG_START1_SEL( |
| ni_get_reg_value_roffs( |
| CR_CHAN(cmd->start_arg), |
| NI_AI_StartTrigger, |
| &devpriv->routing_tables, 1)); |
| |
| if (cmd->start_arg & CR_INVERT) |
| ai_trig |= NISTC_AI_TRIG_START1_POLARITY; |
| if (cmd->start_arg & CR_EDGE) |
| ai_trig |= NISTC_AI_TRIG_START1_EDGE; |
| break; |
| } |
| ni_stc_writew(dev, ai_trig, NISTC_AI_TRIG_SEL_REG); |
| |
| mode2 &= ~NISTC_AI_MODE2_PRE_TRIGGER; |
| mode2 &= ~NISTC_AI_MODE2_SC_INIT_LOAD_SRC; |
| mode2 &= ~NISTC_AI_MODE2_SC_RELOAD_MODE; |
| ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); |
| |
| if (cmd->chanlist_len == 1 || devpriv->is_611x || devpriv->is_6143) { |
| /* logic low */ |
| start_stop_select |= NISTC_AI_STOP_POLARITY | |
| NISTC_AI_STOP_SEL(31) | |
| NISTC_AI_STOP_SYNC; |
| } else { |
| /* ai configuration memory */ |
| start_stop_select |= NISTC_AI_STOP_SEL(19); |
| } |
| ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG); |
| |
| devpriv->ai_cmd2 = 0; |
| switch (cmd->stop_src) { |
| case TRIG_COUNT: |
| stop_count = cmd->stop_arg - 1; |
| |
| if (devpriv->is_611x) { |
| /* have to take 3 stage adc pipeline into account */ |
| stop_count += num_adc_stages_611x; |
| } |
| /* stage number of scans */ |
| ni_stc_writel(dev, stop_count, NISTC_AI_SC_LOADA_REG); |
| |
| mode1 |= NISTC_AI_MODE1_START_STOP | |
| NISTC_AI_MODE1_RSVD | |
| NISTC_AI_MODE1_TRIGGER_ONCE; |
| ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG); |
| /* load SC (Scan Count) */ |
| ni_stc_writew(dev, NISTC_AI_CMD1_SC_LOAD, NISTC_AI_CMD1_REG); |
| |
| if (stop_count == 0) { |
| devpriv->ai_cmd2 |= NISTC_AI_CMD2_END_ON_EOS; |
| interrupt_a_enable |= NISTC_INTA_ENA_AI_STOP; |
| /* |
| * This is required to get the last sample for |
| * chanlist_len > 1, not sure why. |
| */ |
| if (cmd->chanlist_len > 1) |
| start_stop_select |= NISTC_AI_STOP_POLARITY | |
| NISTC_AI_STOP_EDGE; |
| } |
| break; |
| case TRIG_NONE: |
| /* stage number of scans */ |
| ni_stc_writel(dev, 0, NISTC_AI_SC_LOADA_REG); |
| |
| mode1 |= NISTC_AI_MODE1_START_STOP | |
| NISTC_AI_MODE1_RSVD | |
| NISTC_AI_MODE1_CONTINUOUS; |
| ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG); |
| |
| /* load SC (Scan Count) */ |
| ni_stc_writew(dev, NISTC_AI_CMD1_SC_LOAD, NISTC_AI_CMD1_REG); |
| break; |
| } |
| |
| switch (cmd->scan_begin_src) { |
| case TRIG_TIMER: |
| /* |
| * stop bits for non 611x boards |
| * NISTC_AI_MODE3_SI_TRIG_DELAY=0 |
| * NISTC_AI_MODE2_PRE_TRIGGER=0 |
| * NISTC_AI_START_STOP_REG: |
| * NISTC_AI_START_POLARITY=0 (?) rising edge |
| * NISTC_AI_START_EDGE=1 edge triggered |
| * NISTC_AI_START_SYNC=1 (?) |
| * NISTC_AI_START_SEL=0 SI_TC |
| * NISTC_AI_STOP_POLARITY=0 rising edge |
| * NISTC_AI_STOP_EDGE=0 level |
| * NISTC_AI_STOP_SYNC=1 |
| * NISTC_AI_STOP_SEL=19 external pin (configuration mem) |
| */ |
| start_stop_select |= NISTC_AI_START_EDGE | NISTC_AI_START_SYNC; |
| ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG); |
| |
| mode2 &= ~NISTC_AI_MODE2_SI_INIT_LOAD_SRC; /* A */ |
| mode2 |= NISTC_AI_MODE2_SI_RELOAD_MODE(0); |
| /* mode2 |= NISTC_AI_MODE2_SC_RELOAD_MODE; */ |
| ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); |
| |
| /* load SI */ |
| timer = ni_ns_to_timer(dev, cmd->scan_begin_arg, |
| CMDF_ROUND_NEAREST); |
| ni_stc_writel(dev, timer, NISTC_AI_SI_LOADA_REG); |
| ni_stc_writew(dev, NISTC_AI_CMD1_SI_LOAD, NISTC_AI_CMD1_REG); |
| break; |
| case TRIG_EXT: |
| if (cmd->scan_begin_arg & CR_EDGE) |
| start_stop_select |= NISTC_AI_START_EDGE; |
| if (cmd->scan_begin_arg & CR_INVERT) /* falling edge */ |
| start_stop_select |= NISTC_AI_START_POLARITY; |
| if (cmd->scan_begin_src != cmd->convert_src || |
| (cmd->scan_begin_arg & ~CR_EDGE) != |
| (cmd->convert_arg & ~CR_EDGE)) |
| start_stop_select |= NISTC_AI_START_SYNC; |
| |
| start_stop_select |= NISTC_AI_START_SEL( |
| ni_get_reg_value_roffs( |
| CR_CHAN(cmd->scan_begin_arg), |
| NI_AI_SampleClock, |
| &devpriv->routing_tables, 1)); |
| ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG); |
| break; |
| } |
| |
| switch (cmd->convert_src) { |
| case TRIG_TIMER: |
| case TRIG_NOW: |
| if (cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW) |
| timer = 1; |
| else |
| timer = ni_ns_to_timer(dev, cmd->convert_arg, |
| CMDF_ROUND_NEAREST); |
| /* 0,0 does not work */ |
| ni_stc_writew(dev, 1, NISTC_AI_SI2_LOADA_REG); |
| ni_stc_writew(dev, timer, NISTC_AI_SI2_LOADB_REG); |
| |
| mode2 &= ~NISTC_AI_MODE2_SI2_INIT_LOAD_SRC; /* A */ |
| mode2 |= NISTC_AI_MODE2_SI2_RELOAD_MODE; /* alternate */ |
| ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); |
| |
| ni_stc_writew(dev, NISTC_AI_CMD1_SI2_LOAD, NISTC_AI_CMD1_REG); |
| |
| mode2 |= NISTC_AI_MODE2_SI2_INIT_LOAD_SRC; /* B */ |
| mode2 |= NISTC_AI_MODE2_SI2_RELOAD_MODE; /* alternate */ |
| ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); |
| break; |
| case TRIG_EXT: |
| mode1 |= NISTC_AI_MODE1_CONVERT_SRC( |
| ni_get_reg_value_roffs( |
| CR_CHAN(cmd->convert_arg), |
| NI_AI_ConvertClock, |
| &devpriv->routing_tables, 1)); |
| if ((cmd->convert_arg & CR_INVERT) == 0) |
| mode1 |= NISTC_AI_MODE1_CONVERT_POLARITY; |
| ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG); |
| |
| mode2 |= NISTC_AI_MODE2_SC_GATE_ENA | |
| NISTC_AI_MODE2_START_STOP_GATE_ENA; |
| ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG); |
| |
| break; |
| } |
| |
| if (dev->irq) { |
| /* interrupt on FIFO, errors, SC_TC */ |
| interrupt_a_enable |= NISTC_INTA_ENA_AI_ERR | |
| NISTC_INTA_ENA_AI_SC_TC; |
| |
| #ifndef PCIDMA |
| interrupt_a_enable |= NISTC_INTA_ENA_AI_FIFO; |
| #endif |
| |
| if ((cmd->flags & CMDF_WAKE_EOS) || |
| (devpriv->ai_cmd2 & NISTC_AI_CMD2_END_ON_EOS)) { |
| /* wake on end-of-scan */ |
| devpriv->aimode = AIMODE_SCAN; |
| } else { |
| devpriv->aimode = AIMODE_HALF_FULL; |
| } |
| |
| switch (devpriv->aimode) { |
| case AIMODE_HALF_FULL: |
| /* FIFO interrupts and DMA requests on half-full */ |
| #ifdef PCIDMA |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_HF_E, |
| NISTC_AI_MODE3_REG); |
| #else |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_HF, |
| NISTC_AI_MODE3_REG); |
| #endif |
| break; |
| case AIMODE_SAMPLE: |
| /* generate FIFO interrupts on non-empty */ |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_NE, |
| NISTC_AI_MODE3_REG); |
| break; |
| case AIMODE_SCAN: |
| #ifdef PCIDMA |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_NE, |
| NISTC_AI_MODE3_REG); |
| #else |
| ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_HF, |
| NISTC_AI_MODE3_REG); |
| #endif |
| interrupt_a_enable |= NISTC_INTA_ENA_AI_STOP; |
| break; |
| default: |
| break; |
| } |
| |
| /* clear interrupts */ |
| ni_stc_writew(dev, NISTC_INTA_ACK_AI_ALL, NISTC_INTA_ACK_REG); |
| |
| ni_set_bits(dev, NISTC_INTA_ENA_REG, interrupt_a_enable, 1); |
| } else { |
| /* interrupt on nothing */ |
| ni_set_bits(dev, NISTC_INTA_ENA_REG, ~0, 0); |
| |
| /* XXX start polling if necessary */ |
| } |
| |
| /* end configuration */ |
| ni_stc_writew(dev, NISTC_RESET_AI_CFG_END, NISTC_RESET_REG); |
| |
| switch (cmd->scan_begin_src) { |
| case TRIG_TIMER: |
| ni_stc_writew(dev, NISTC_AI_CMD1_SI2_ARM | |
| NISTC_AI_CMD1_SI_ARM | |
| NISTC_AI_CMD1_DIV_ARM | |
| NISTC_AI_CMD1_SC_ARM, |
| NISTC_AI_CMD1_REG); |
| break; |
| case TRIG_EXT: |
| ni_stc_writew(dev, NISTC_AI_CMD1_SI2_ARM | |
| NISTC_AI_CMD1_SI_ARM | /* XXX ? */ |
| NISTC_AI_CMD1_DIV_ARM | |
| NISTC_AI_CMD1_SC_ARM, |
| NISTC_AI_CMD1_REG); |
| break; |
| } |
| |
| #ifdef PCIDMA |
| { |
| int retval = ni_ai_setup_MITE_dma(dev); |
| |
| if (retval) |
| return retval; |
| } |
| #endif |
| |
| if (cmd->start_src == TRIG_NOW) { |
| ni_stc_writew(dev, NISTC_AI_CMD2_START1_PULSE | |
| devpriv->ai_cmd2, |
| NISTC_AI_CMD2_REG); |
| s->async->inttrig = NULL; |
| } else if (cmd->start_src == TRIG_EXT) { |
| s->async->inttrig = NULL; |
| } else { /* TRIG_INT */ |
| s->async->inttrig = ni_ai_inttrig; |
| } |
| |
| return 0; |
| } |
| |
| static int ni_ai_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, unsigned int *data) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| |
| if (insn->n < 1) |
| return -EINVAL; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_ALT_SOURCE: |
| if (devpriv->is_m_series) { |
| if (data[1] & ~NI_M_CFG_BYPASS_AI_CAL_MASK) |
| return -EINVAL; |
| devpriv->ai_calib_source = data[1]; |
| } else if (devpriv->is_6143) { |
| unsigned int calib_source; |
| |
| calib_source = data[1] & 0xf; |
| |
| devpriv->ai_calib_source = calib_source; |
| ni_writew(dev, calib_source, NI6143_CALIB_CHAN_REG); |
| } else { |
| unsigned int calib_source; |
| unsigned int calib_source_adjust; |
| |
| calib_source = data[1] & 0xf; |
| calib_source_adjust = (data[1] >> 4) & 0xff; |
| |
| if (calib_source >= 8) |
| return -EINVAL; |
| devpriv->ai_calib_source = calib_source; |
| if (devpriv->is_611x) { |
| ni_writeb(dev, calib_source_adjust, |
| NI611X_CAL_GAIN_SEL_REG); |
| } |
| } |
| return 2; |
| case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: |
| /* we don't care about actual channels */ |
| /* data[3] : chanlist_len */ |
| data[1] = ni_min_ai_scan_period_ns(dev, data[3]); |
| if (devpriv->is_611x || devpriv->is_6143) |
| data[2] = 0; /* simultaneous output */ |
| else |
| data[2] = board->ai_speed; |
| return 0; |
| default: |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static void ni_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s, |
| void *data, unsigned int num_bytes, |
| unsigned int chan_index) |
| { |
| struct comedi_cmd *cmd = &s->async->cmd; |
| unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes); |
| unsigned short *array = data; |
| unsigned int i; |
| #ifdef PCIDMA |
| __le16 buf, *barray = data; |
| #endif |
| |
| for (i = 0; i < nsamples; i++) { |
| unsigned int range = CR_RANGE(cmd->chanlist[chan_index]); |
| unsigned short val = array[i]; |
| |
| /* |
| * Munge data from unsigned to two's complement for |
| * bipolar ranges. |
| */ |
| if (comedi_range_is_bipolar(s, range)) |
| val = comedi_offset_munge(s, val); |
| #ifdef PCIDMA |
| buf = cpu_to_le16(val); |
| barray[i] = buf; |
| #else |
| array[i] = val; |
| #endif |
| chan_index++; |
| chan_index %= cmd->chanlist_len; |
| } |
| } |
| |
| static int ni_m_series_ao_config_chanlist(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int chanspec[], |
| unsigned int n_chans, int timed) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int range; |
| unsigned int chan; |
| unsigned int conf; |
| int i; |
| int invert = 0; |
| |
| if (timed) { |
| for (i = 0; i < s->n_chan; ++i) { |
| devpriv->ao_conf[i] &= ~NI_M_AO_CFG_BANK_UPDATE_TIMED; |
| ni_writeb(dev, devpriv->ao_conf[i], |
| NI_M_AO_CFG_BANK_REG(i)); |
| ni_writeb(dev, 0xf, NI_M_AO_WAVEFORM_ORDER_REG(i)); |
| } |
| } |
| for (i = 0; i < n_chans; i++) { |
| const struct comedi_krange *krange; |
| |
| chan = CR_CHAN(chanspec[i]); |
| range = CR_RANGE(chanspec[i]); |
| krange = s->range_table->range + range; |
| invert = 0; |
| conf = 0; |
| switch (krange->max - krange->min) { |
| case 20000000: |
| conf |= NI_M_AO_CFG_BANK_REF_INT_10V; |
| ni_writeb(dev, 0, NI_M_AO_REF_ATTENUATION_REG(chan)); |
| break; |
| case 10000000: |
| conf |= NI_M_AO_CFG_BANK_REF_INT_5V; |
| ni_writeb(dev, 0, NI_M_AO_REF_ATTENUATION_REG(chan)); |
| break; |
| case 4000000: |
| conf |= NI_M_AO_CFG_BANK_REF_INT_10V; |
| ni_writeb(dev, NI_M_AO_REF_ATTENUATION_X5, |
| NI_M_AO_REF_ATTENUATION_REG(chan)); |
| break; |
| case 2000000: |
| conf |= NI_M_AO_CFG_BANK_REF_INT_5V; |
| ni_writeb(dev, NI_M_AO_REF_ATTENUATION_X5, |
| NI_M_AO_REF_ATTENUATION_REG(chan)); |
| break; |
| default: |
| dev_err(dev->class_dev, |
| "bug! unhandled ao reference voltage\n"); |
| break; |
| } |
| switch (krange->max + krange->min) { |
| case 0: |
| conf |= NI_M_AO_CFG_BANK_OFFSET_0V; |
| break; |
| case 10000000: |
| conf |= NI_M_AO_CFG_BANK_OFFSET_5V; |
| break; |
| default: |
| dev_err(dev->class_dev, |
| "bug! unhandled ao offset voltage\n"); |
| break; |
| } |
| if (timed) |
| conf |= NI_M_AO_CFG_BANK_UPDATE_TIMED; |
| ni_writeb(dev, conf, NI_M_AO_CFG_BANK_REG(chan)); |
| devpriv->ao_conf[chan] = conf; |
| ni_writeb(dev, i, NI_M_AO_WAVEFORM_ORDER_REG(chan)); |
| } |
| return invert; |
| } |
| |
| static int ni_old_ao_config_chanlist(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int chanspec[], |
| unsigned int n_chans) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int range; |
| unsigned int chan; |
| unsigned int conf; |
| int i; |
| int invert = 0; |
| |
| for (i = 0; i < n_chans; i++) { |
| chan = CR_CHAN(chanspec[i]); |
| range = CR_RANGE(chanspec[i]); |
| conf = NI_E_AO_DACSEL(chan); |
| |
| if (comedi_range_is_bipolar(s, range)) { |
| conf |= NI_E_AO_CFG_BIP; |
| invert = (s->maxdata + 1) >> 1; |
| } else { |
| invert = 0; |
| } |
| if (comedi_range_is_external(s, range)) |
| conf |= NI_E_AO_EXT_REF; |
| |
| /* not all boards can deglitch, but this shouldn't hurt */ |
| if (chanspec[i] & CR_DEGLITCH) |
| conf |= NI_E_AO_DEGLITCH; |
| |
| /* analog reference */ |
| /* AREF_OTHER connects AO ground to AI ground, i think */ |
| if (CR_AREF(chanspec[i]) == AREF_OTHER) |
| conf |= NI_E_AO_GROUND_REF; |
| |
| ni_writew(dev, conf, NI_E_AO_CFG_REG); |
| devpriv->ao_conf[chan] = conf; |
| } |
| return invert; |
| } |
| |
| static int ni_ao_config_chanlist(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int chanspec[], unsigned int n_chans, |
| int timed) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (devpriv->is_m_series) |
| return ni_m_series_ao_config_chanlist(dev, s, chanspec, n_chans, |
| timed); |
| else |
| return ni_old_ao_config_chanlist(dev, s, chanspec, n_chans); |
| } |
| |
| static int ni_ao_insn_write(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int chan = CR_CHAN(insn->chanspec); |
| unsigned int range = CR_RANGE(insn->chanspec); |
| int reg; |
| int i; |
| |
| if (devpriv->is_6xxx) { |
| ni_ao_win_outw(dev, 1 << chan, NI671X_AO_IMMEDIATE_REG); |
| |
| reg = NI671X_DAC_DIRECT_DATA_REG(chan); |
| } else if (devpriv->is_m_series) { |
| reg = NI_M_DAC_DIRECT_DATA_REG(chan); |
| } else { |
| reg = NI_E_DAC_DIRECT_DATA_REG(chan); |
| } |
| |
| ni_ao_config_chanlist(dev, s, &insn->chanspec, 1, 0); |
| |
| for (i = 0; i < insn->n; i++) { |
| unsigned int val = data[i]; |
| |
| s->readback[chan] = val; |
| |
| if (devpriv->is_6xxx) { |
| /* |
| * 6xxx boards have bipolar outputs, munge the |
| * unsigned comedi values to 2's complement |
| */ |
| val = comedi_offset_munge(s, val); |
| |
| ni_ao_win_outw(dev, val, reg); |
| } else if (devpriv->is_m_series) { |
| /* |
| * M-series boards use offset binary values for |
| * bipolar and uinpolar outputs |
| */ |
| ni_writew(dev, val, reg); |
| } else { |
| /* |
| * Non-M series boards need two's complement values |
| * for bipolar ranges. |
| */ |
| if (comedi_range_is_bipolar(s, range)) |
| val = comedi_offset_munge(s, val); |
| |
| ni_writew(dev, val, reg); |
| } |
| } |
| |
| return insn->n; |
| } |
| |
| /* |
| * Arms the AO device in preparation for a trigger event. |
| * This function also allocates and prepares a DMA channel (or FIFO if DMA is |
| * not used). As a part of this preparation, this function preloads the DAC |
| * registers with the first values of the output stream. This ensures that the |
| * first clock cycle after the trigger can be used for output. |
| * |
| * Note that this function _must_ happen after a user has written data to the |
| * output buffers via either mmap or write(fileno,...). |
| */ |
| static int ni_ao_arm(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| int ret; |
| int interrupt_b_bits; |
| int i; |
| static const int timeout = 1000; |
| |
| /* |
| * Prevent ao from doing things like trying to allocate the ao dma |
| * channel multiple times. |
| */ |
| if (!devpriv->ao_needs_arming) { |
| dev_dbg(dev->class_dev, "%s: device does not need arming!\n", |
| __func__); |
| return -EINVAL; |
| } |
| |
| devpriv->ao_needs_arming = 0; |
| |
| ni_set_bits(dev, NISTC_INTB_ENA_REG, |
| NISTC_INTB_ENA_AO_FIFO | NISTC_INTB_ENA_AO_ERR, 0); |
| interrupt_b_bits = NISTC_INTB_ENA_AO_ERR; |
| #ifdef PCIDMA |
| ni_stc_writew(dev, 1, NISTC_DAC_FIFO_CLR_REG); |
| if (devpriv->is_6xxx) |
| ni_ao_win_outl(dev, 0x6, NI611X_AO_FIFO_OFFSET_LOAD_REG); |
| ret = ni_ao_setup_MITE_dma(dev); |
| if (ret) |
| return ret; |
| ret = ni_ao_wait_for_dma_load(dev); |
| if (ret < 0) |
| return ret; |
| #else |
| ret = ni_ao_prep_fifo(dev, s); |
| if (ret == 0) |
| return -EPIPE; |
| |
| interrupt_b_bits |= NISTC_INTB_ENA_AO_FIFO; |
| #endif |
| |
| ni_stc_writew(dev, devpriv->ao_mode3 | NISTC_AO_MODE3_NOT_AN_UPDATE, |
| NISTC_AO_MODE3_REG); |
| ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); |
| /* wait for DACs to be loaded */ |
| for (i = 0; i < timeout; i++) { |
| udelay(1); |
| if ((ni_stc_readw(dev, NISTC_STATUS2_REG) & |
| NISTC_STATUS2_AO_TMRDACWRS_IN_PROGRESS) == 0) |
| break; |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, |
| "timed out waiting for AO_TMRDACWRs_In_Progress_St to clear\n"); |
| return -EIO; |
| } |
| /* |
| * stc manual says we are need to clear error interrupt after |
| * AO_TMRDACWRs_In_Progress_St clears |
| */ |
| ni_stc_writew(dev, NISTC_INTB_ACK_AO_ERR, NISTC_INTB_ACK_REG); |
| |
| ni_set_bits(dev, NISTC_INTB_ENA_REG, interrupt_b_bits, 1); |
| |
| ni_stc_writew(dev, NISTC_AO_CMD1_UI_ARM | |
| NISTC_AO_CMD1_UC_ARM | |
| NISTC_AO_CMD1_BC_ARM | |
| devpriv->ao_cmd1, |
| NISTC_AO_CMD1_REG); |
| |
| return 0; |
| } |
| |
| static int ni_ao_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, unsigned int *data) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| unsigned int nbytes; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: |
| switch (data[1]) { |
| case COMEDI_OUTPUT: |
| nbytes = comedi_samples_to_bytes(s, |
| board->ao_fifo_depth); |
| data[2] = 1 + nbytes; |
| if (devpriv->mite) |
| data[2] += devpriv->mite->fifo_size; |
| break; |
| case COMEDI_INPUT: |
| data[2] = 0; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| case INSN_CONFIG_ARM: |
| return ni_ao_arm(dev, s); |
| case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: |
| /* we don't care about actual channels */ |
| /* data[3] : chanlist_len */ |
| data[1] = board->ao_speed * data[3]; |
| data[2] = 0; |
| return 0; |
| default: |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int ni_ao_inttrig(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int trig_num) |
| { |
| struct ni_private *devpriv = dev->private; |
| struct comedi_cmd *cmd = &s->async->cmd; |
| int ret; |
| |
| /* |
| * Require trig_num == cmd->start_arg when cmd->start_src == TRIG_INT. |
| * For backwards compatibility, also allow trig_num == 0 when |
| * cmd->start_src != TRIG_INT (i.e. when cmd->start_src == TRIG_EXT); |
| * in that case, the internal trigger is being used as a pre-trigger |
| * before the external trigger. |
| */ |
| if (!(trig_num == cmd->start_arg || |
| (trig_num == 0 && cmd->start_src != TRIG_INT))) |
| return -EINVAL; |
| |
| /* |
| * Null trig at beginning prevent ao start trigger from executing more |
| * than once per command. |
| */ |
| s->async->inttrig = NULL; |
| |
| if (devpriv->ao_needs_arming) { |
| /* only arm this device if it still needs arming */ |
| ret = ni_ao_arm(dev, s); |
| if (ret) |
| return ret; |
| } |
| |
| ni_stc_writew(dev, NISTC_AO_CMD2_START1_PULSE | devpriv->ao_cmd2, |
| NISTC_AO_CMD2_REG); |
| |
| return 0; |
| } |
| |
| /* |
| * begin ni_ao_cmd. |
| * Organized similar to NI-STC and MHDDK examples. |
| * ni_ao_cmd is broken out into configuration sub-routines for clarity. |
| */ |
| |
| static void ni_ao_cmd_personalize(struct comedi_device *dev, |
| const struct comedi_cmd *cmd) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| unsigned int bits; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| bits = |
| /* fast CPU interface--only eseries */ |
| /* ((slow CPU interface) ? 0 : AO_Fast_CPU) | */ |
| NISTC_AO_PERSONAL_BC_SRC_SEL | |
| 0 /* (use_original_pulse ? 0 : NISTC_AO_PERSONAL_UPDATE_TIMEBASE) */ | |
| /* |
| * FIXME: start setting following bit when appropriate. Need to |
| * determine whether board is E4 or E1. |
| * FROM MHHDK: |
| * if board is E4 or E1 |
| * Set bit "NISTC_AO_PERSONAL_UPDATE_PW" to 0 |
| * else |
| * set it to 1 |
| */ |
| NISTC_AO_PERSONAL_UPDATE_PW | |
| /* FIXME: when should we set following bit to zero? */ |
| NISTC_AO_PERSONAL_TMRDACWR_PW | |
| (board->ao_fifo_depth ? |
| NISTC_AO_PERSONAL_FIFO_ENA : NISTC_AO_PERSONAL_DMA_PIO_CTRL) |
| ; |
| #if 0 |
| /* |
| * FIXME: |
| * add something like ".has_individual_dacs = 0" to ni_board_struct |
| * since, as F Hess pointed out, not all in m series have singles. not |
| * sure if e-series all have duals... |
| */ |
| |
| /* |
| * F Hess: windows driver does not set NISTC_AO_PERSONAL_NUM_DAC bit for |
| * 6281, verified with bus analyzer. |
| */ |
| if (devpriv->is_m_series) |
| bits |= NISTC_AO_PERSONAL_NUM_DAC; |
| #endif |
| ni_stc_writew(dev, bits, NISTC_AO_PERSONAL_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_trigger(struct comedi_device *dev, |
| const struct comedi_cmd *cmd) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int trigsel; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| /* sync */ |
| if (cmd->stop_src == TRIG_NONE) { |
| devpriv->ao_mode1 |= NISTC_AO_MODE1_CONTINUOUS; |
| devpriv->ao_mode1 &= ~NISTC_AO_MODE1_TRIGGER_ONCE; |
| } else { |
| devpriv->ao_mode1 &= ~NISTC_AO_MODE1_CONTINUOUS; |
| devpriv->ao_mode1 |= NISTC_AO_MODE1_TRIGGER_ONCE; |
| } |
| ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); |
| |
| if (cmd->start_src == TRIG_INT) { |
| trigsel = NISTC_AO_TRIG_START1_EDGE | |
| NISTC_AO_TRIG_START1_SYNC; |
| } else { /* TRIG_EXT */ |
| trigsel = NISTC_AO_TRIG_START1_SEL( |
| ni_get_reg_value_roffs( |
| CR_CHAN(cmd->start_arg), |
| NI_AO_StartTrigger, |
| &devpriv->routing_tables, 1)); |
| |
| /* 0=active high, 1=active low. see daq-stc 3-24 (p186) */ |
| if (cmd->start_arg & CR_INVERT) |
| trigsel |= NISTC_AO_TRIG_START1_POLARITY; |
| /* 0=edge detection disabled, 1=enabled */ |
| if (cmd->start_arg & CR_EDGE) |
| trigsel |= NISTC_AO_TRIG_START1_EDGE; |
| } |
| ni_stc_writew(dev, trigsel, NISTC_AO_TRIG_SEL_REG); |
| |
| /* AO_Delayed_START1 = 0, we do not support delayed start...yet */ |
| |
| /* sync */ |
| /* select DA_START1 as PFI6/AO_START1 when configured as an output */ |
|