| // 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 <linux/comedi/comedi_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 */ |
| devpriv->ao_mode3 &= ~NISTC_AO_MODE3_TRIG_LEN; |
| ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_counters(struct comedi_device *dev, |
| const struct comedi_cmd *cmd) |
| { |
| struct ni_private *devpriv = dev->private; |
| /* Not supporting 'waveform staging' or 'local buffer with pauses' */ |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| /* |
| * This relies on ao_mode1/(Trigger_Once | Continuous) being set in |
| * set_trigger above. It is unclear whether we really need to re-write |
| * this register with these values. The mhddk examples for e-series |
| * show writing this in both places, but the examples for m-series show |
| * a single write in the set_counters function (here). |
| */ |
| ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); |
| |
| /* sync (upload number of buffer iterations -1) */ |
| /* indicate that we want to use BC_Load_A_Register as the source */ |
| devpriv->ao_mode2 &= ~NISTC_AO_MODE2_BC_INIT_LOAD_SRC; |
| ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); |
| |
| /* |
| * if the BC_TC interrupt is still issued in spite of UC, BC, UI |
| * ignoring BC_TC, then we will need to find a way to ignore that |
| * interrupt in continuous mode. |
| */ |
| ni_stc_writel(dev, 0, NISTC_AO_BC_LOADA_REG); /* iter once */ |
| |
| /* sync (issue command to load number of buffer iterations -1) */ |
| ni_stc_writew(dev, NISTC_AO_CMD1_BC_LOAD, NISTC_AO_CMD1_REG); |
| |
| /* sync (upload number of updates in buffer) */ |
| /* indicate that we want to use UC_Load_A_Register as the source */ |
| devpriv->ao_mode2 &= ~NISTC_AO_MODE2_UC_INIT_LOAD_SRC; |
| ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); |
| |
| /* |
| * if a user specifies '0', this automatically assumes the entire 24bit |
| * address space is available for the (multiple iterations of single |
| * buffer) MISB. Otherwise, stop_arg specifies the MISB length that |
| * will be used, regardless of whether we are in continuous mode or not. |
| * In continuous mode, the output will just iterate indefinitely over |
| * the MISB. |
| */ |
| { |
| unsigned int stop_arg = cmd->stop_arg > 0 ? |
| (cmd->stop_arg & 0xffffff) : 0xffffff; |
| |
| if (devpriv->is_m_series) { |
| /* |
| * this is how the NI example code does it for m-series |
| * boards, verified correct with 6259 |
| */ |
| ni_stc_writel(dev, stop_arg - 1, NISTC_AO_UC_LOADA_REG); |
| |
| /* sync (issue cmd to load number of updates in MISB) */ |
| ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, |
| NISTC_AO_CMD1_REG); |
| } else { |
| ni_stc_writel(dev, stop_arg, NISTC_AO_UC_LOADA_REG); |
| |
| /* sync (issue cmd to load number of updates in MISB) */ |
| ni_stc_writew(dev, NISTC_AO_CMD1_UC_LOAD, |
| NISTC_AO_CMD1_REG); |
| |
| /* |
| * sync (upload number of updates-1 in MISB) |
| * --eseries only? |
| */ |
| ni_stc_writel(dev, stop_arg - 1, NISTC_AO_UC_LOADA_REG); |
| } |
| } |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_update(struct comedi_device *dev, |
| const struct comedi_cmd *cmd) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| /* |
| * zero out these bit fields to be set below. Does an ao-reset do this |
| * automatically? |
| */ |
| devpriv->ao_mode1 &= ~(NISTC_AO_MODE1_UI_SRC_MASK | |
| NISTC_AO_MODE1_UI_SRC_POLARITY | |
| NISTC_AO_MODE1_UPDATE_SRC_MASK | |
| NISTC_AO_MODE1_UPDATE_SRC_POLARITY); |
| |
| if (cmd->scan_begin_src == TRIG_TIMER) { |
| unsigned int trigvar; |
| |
| devpriv->ao_cmd2 &= ~NISTC_AO_CMD2_BC_GATE_ENA; |
| |
| /* |
| * NOTE: there are several other ways of configuring internal |
| * updates, but we'll only support one for now: using |
| * AO_IN_TIMEBASE, w/o waveform staging, w/o a delay between |
| * START1 and first update, and also w/o local buffer mode w/ |
| * pauses. |
| */ |
| |
| /* |
| * This is already done above: |
| * devpriv->ao_mode1 &= ~( |
| * // set UPDATE_Source to UI_TC: |
| * NISTC_AO_MODE1_UPDATE_SRC_MASK | |
| * // set UPDATE_Source_Polarity to rising (required?) |
| * NISTC_AO_MODE1_UPDATE_SRC_POLARITY | |
| * // set UI_Source to AO_IN_TIMEBASE1: |
| * NISTC_AO_MODE1_UI_SRC_MASK | |
| * // set UI_Source_Polarity to rising (required?) |
| * NISTC_AO_MODE1_UI_SRC_POLARITY |
| * ); |
| */ |
| |
| /* |
| * TODO: use ao_ui_clock_source to allow all possible signals |
| * to be routed to UI_Source_Select. See tSTC.h for |
| * eseries/ni67xx and tMSeries.h for mseries. |
| */ |
| |
| trigvar = ni_ns_to_timer(dev, cmd->scan_begin_arg, |
| CMDF_ROUND_NEAREST); |
| |
| /* |
| * Wait N TB3 ticks after the start trigger before |
| * clocking (N must be >=2). |
| */ |
| /* following line: 2-1 per STC */ |
| ni_stc_writel(dev, 1, NISTC_AO_UI_LOADA_REG); |
| ni_stc_writew(dev, NISTC_AO_CMD1_UI_LOAD, NISTC_AO_CMD1_REG); |
| ni_stc_writel(dev, trigvar, NISTC_AO_UI_LOADA_REG); |
| } else { /* TRIG_EXT */ |
| /* FIXME: assert scan_begin_arg != 0, ret failure otherwise */ |
| devpriv->ao_cmd2 |= NISTC_AO_CMD2_BC_GATE_ENA; |
| devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC( |
| ni_get_reg_value( |
| CR_CHAN(cmd->scan_begin_arg), |
| NI_AO_SampleClock, |
| &devpriv->routing_tables)); |
| if (cmd->scan_begin_arg & CR_INVERT) |
| devpriv->ao_mode1 |= NISTC_AO_MODE1_UPDATE_SRC_POLARITY; |
| } |
| |
| ni_stc_writew(dev, devpriv->ao_cmd2, NISTC_AO_CMD2_REG); |
| ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); |
| devpriv->ao_mode2 &= ~(NISTC_AO_MODE2_UI_RELOAD_MODE(3) | |
| NISTC_AO_MODE2_UI_INIT_LOAD_SRC); |
| ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); |
| |
| /* Configure DAQ-STC for Timed update mode */ |
| devpriv->ao_cmd1 |= NISTC_AO_CMD1_DAC1_UPDATE_MODE | |
| NISTC_AO_CMD1_DAC0_UPDATE_MODE; |
| /* We are not using UPDATE2-->don't have to set DACx_Source_Select */ |
| ni_stc_writew(dev, devpriv->ao_cmd1, NISTC_AO_CMD1_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_channels(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| const struct comedi_cmd *cmd = &s->async->cmd; |
| unsigned int bits = 0; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| if (devpriv->is_6xxx) { |
| unsigned int i; |
| |
| bits = 0; |
| for (i = 0; i < cmd->chanlist_len; ++i) { |
| int chan = CR_CHAN(cmd->chanlist[i]); |
| |
| bits |= 1 << chan; |
| ni_ao_win_outw(dev, chan, NI611X_AO_WAVEFORM_GEN_REG); |
| } |
| ni_ao_win_outw(dev, bits, NI611X_AO_TIMED_REG); |
| } |
| |
| ni_ao_config_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 1); |
| |
| if (cmd->scan_end_arg > 1) { |
| devpriv->ao_mode1 |= NISTC_AO_MODE1_MULTI_CHAN; |
| bits = NISTC_AO_OUT_CTRL_CHANS(cmd->scan_end_arg - 1) |
| | NISTC_AO_OUT_CTRL_UPDATE_SEL_HIGHZ; |
| |
| } else { |
| devpriv->ao_mode1 &= ~NISTC_AO_MODE1_MULTI_CHAN; |
| bits = NISTC_AO_OUT_CTRL_UPDATE_SEL_HIGHZ; |
| if (devpriv->is_m_series | devpriv->is_6xxx) |
| bits |= NISTC_AO_OUT_CTRL_CHANS(0); |
| else |
| bits |= NISTC_AO_OUT_CTRL_CHANS( |
| CR_CHAN(cmd->chanlist[0])); |
| } |
| |
| ni_stc_writew(dev, devpriv->ao_mode1, NISTC_AO_MODE1_REG); |
| ni_stc_writew(dev, bits, NISTC_AO_OUT_CTRL_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_stop_conditions(struct comedi_device *dev, |
| const struct comedi_cmd *cmd) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| devpriv->ao_mode3 |= NISTC_AO_MODE3_STOP_ON_OVERRUN_ERR; |
| ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); |
| |
| /* |
| * Since we are not supporting waveform staging, we ignore these errors: |
| * NISTC_AO_MODE3_STOP_ON_BC_TC_ERR, |
| * NISTC_AO_MODE3_STOP_ON_BC_TC_TRIG_ERR |
| */ |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| } |
| |
| static void ni_ao_cmd_set_fifo_mode(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| devpriv->ao_mode2 &= ~NISTC_AO_MODE2_FIFO_MODE_MASK; |
| #ifdef PCIDMA |
| devpriv->ao_mode2 |= NISTC_AO_MODE2_FIFO_MODE_HF_F; |
| #else |
| devpriv->ao_mode2 |= NISTC_AO_MODE2_FIFO_MODE_HF; |
| #endif |
| /* NOTE: this is where use_onboard_memory=True would be implemented */ |
| devpriv->ao_mode2 &= ~NISTC_AO_MODE2_FIFO_REXMIT_ENA; |
| ni_stc_writew(dev, devpriv->ao_mode2, NISTC_AO_MODE2_REG); |
| |
| /* enable sending of ao fifo requests (dma request) */ |
| ni_stc_writew(dev, NISTC_AO_START_AOFREQ_ENA, NISTC_AO_START_SEL_REG); |
| |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| |
| /* we are not supporting boards with virtual fifos */ |
| } |
| |
| static void ni_ao_cmd_set_interrupts(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| if (s->async->cmd.stop_src == TRIG_COUNT) |
| ni_set_bits(dev, NISTC_INTB_ENA_REG, |
| NISTC_INTB_ENA_AO_BC_TC, 1); |
| |
| s->async->inttrig = ni_ao_inttrig; |
| } |
| |
| static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| const struct comedi_cmd *cmd = &s->async->cmd; |
| |
| if (dev->irq == 0) { |
| dev_err(dev->class_dev, "cannot run command without an irq"); |
| return -EIO; |
| } |
| |
| /* ni_ao_reset should have already been done */ |
| ni_ao_cmd_personalize(dev, cmd); |
| /* clearing fifo and preload happens elsewhere */ |
| |
| ni_ao_cmd_set_trigger(dev, cmd); |
| ni_ao_cmd_set_counters(dev, cmd); |
| ni_ao_cmd_set_update(dev, cmd); |
| ni_ao_cmd_set_channels(dev, s); |
| ni_ao_cmd_set_stop_conditions(dev, cmd); |
| ni_ao_cmd_set_fifo_mode(dev); |
| ni_cmd_set_mite_transfer(devpriv->ao_mite_ring, s, cmd, 0x00ffffff); |
| ni_ao_cmd_set_interrupts(dev, s); |
| |
| /* |
| * arm(ing) must happen later so that DMA can be setup and DACs |
| * preloaded with the actual output buffer before starting. |
| * |
| * start(ing) must happen _after_ arming is completed. Starting can be |
| * done either via ni_ao_inttrig, or via an external trigger. |
| * |
| * **Currently, ni_ao_inttrig will automatically attempt a call to |
| * ni_ao_arm if the device still needs arming at that point. This |
| * allows backwards compatibility. |
| */ |
| devpriv->ao_needs_arming = 1; |
| return 0; |
| } |
| |
| /* end ni_ao_cmd */ |
| |
| static int ni_ao_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 tmp; |
| |
| /* Step 1 : check if triggers are trivially valid */ |
| |
| err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT); |
| err |= comedi_check_trigger_src(&cmd->scan_begin_src, |
| TRIG_TIMER | TRIG_EXT); |
| err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); |
| err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
| err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
| |
| if (err) |
| return 1; |
| |
| /* Step 2a : make sure trigger sources are unique */ |
| |
| err |= comedi_check_trigger_is_unique(cmd->start_src); |
| err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); |
| err |= comedi_check_trigger_is_unique(cmd->stop_src); |
| |
| /* Step 2b : and mutually compatible */ |
| |
| if (err) |
| return 2; |
| |
| /* Step 3: check if arguments are trivially valid */ |
| |
| switch (cmd->start_src) { |
| 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_AO_StartTrigger, |
| &devpriv->routing_tables, 1); |
| break; |
| } |
| |
| if (cmd->scan_begin_src == TRIG_TIMER) { |
| err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, |
| board->ao_speed); |
| err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, |
| devpriv->clock_ns * |
| 0xffffff); |
| } else { /* TRIG_EXT */ |
| err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), |
| NI_AO_SampleClock, |
| &devpriv->routing_tables); |
| } |
| |
| err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); |
| err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
| cmd->chanlist_len); |
| err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); |
| |
| if (err) |
| return 3; |
| |
| /* step 4: fix up any arguments */ |
| if (cmd->scan_begin_src == TRIG_TIMER) { |
| 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 (err) |
| return 4; |
| |
| return 0; |
| } |
| |
| static int ni_ao_reset(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| /* See 3.6.1.2 "Resetting", of DAQ-STC Technical Reference Manual */ |
| |
| /* |
| * In the following, the "--sync" comments are meant to denote |
| * asynchronous boundaries for setting the registers as described in the |
| * DAQ-STC mostly in the order also described in the DAQ-STC. |
| */ |
| |
| struct ni_private *devpriv = dev->private; |
| |
| ni_release_ao_mite_channel(dev); |
| |
| /* --sync (reset AO) */ |
| if (devpriv->is_m_series) |
| /* following example in mhddk for m-series */ |
| ni_stc_writew(dev, NISTC_RESET_AO, NISTC_RESET_REG); |
| |
| /*--sync (start config) */ |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_START, NISTC_RESET_REG); |
| |
| /*--sync (Disarm) */ |
| ni_stc_writew(dev, NISTC_AO_CMD1_DISARM, NISTC_AO_CMD1_REG); |
| |
| /* |
| * --sync |
| * (clear bunch of registers--mseries mhddk examples do not include |
| * this) |
| */ |
| devpriv->ao_cmd1 = 0; |
| devpriv->ao_cmd2 = 0; |
| devpriv->ao_mode1 = 0; |
| devpriv->ao_mode2 = 0; |
| if (devpriv->is_m_series) |
| devpriv->ao_mode3 = NISTC_AO_MODE3_LAST_GATE_DISABLE; |
| else |
| devpriv->ao_mode3 = 0; |
| |
| ni_stc_writew(dev, 0, NISTC_AO_PERSONAL_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_CMD1_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_CMD2_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_MODE1_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_MODE2_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_OUT_CTRL_REG); |
| ni_stc_writew(dev, devpriv->ao_mode3, NISTC_AO_MODE3_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_START_SEL_REG); |
| ni_stc_writew(dev, 0, NISTC_AO_TRIG_SEL_REG); |
| |
| /*--sync (disable interrupts) */ |
| ni_set_bits(dev, NISTC_INTB_ENA_REG, ~0, 0); |
| |
| /*--sync (ack) */ |
| ni_stc_writew(dev, NISTC_AO_PERSONAL_BC_SRC_SEL, NISTC_AO_PERSONAL_REG); |
| ni_stc_writew(dev, NISTC_INTB_ACK_AO_ALL, NISTC_INTB_ACK_REG); |
| |
| /*--not in DAQ-STC. which doc? */ |
| if (devpriv->is_6xxx) { |
| ni_ao_win_outw(dev, (1u << s->n_chan) - 1u, |
| NI671X_AO_IMMEDIATE_REG); |
| ni_ao_win_outw(dev, NI611X_AO_MISC_CLEAR_WG, |
| NI611X_AO_MISC_REG); |
| } |
| ni_stc_writew(dev, NISTC_RESET_AO_CFG_END, NISTC_RESET_REG); |
| /*--end */ |
| |
| return 0; |
| } |
| |
| /* digital io */ |
| |
| static int ni_dio_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| int ret; |
| |
| ret = comedi_dio_insn_config(dev, s, insn, data, 0); |
| if (ret) |
| return ret; |
| |
| devpriv->dio_control &= ~NISTC_DIO_CTRL_DIR_MASK; |
| devpriv->dio_control |= NISTC_DIO_CTRL_DIR(s->io_bits); |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| |
| return insn->n; |
| } |
| |
| static int ni_dio_insn_bits(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| /* Make sure we're not using the serial part of the dio */ |
| if ((data[0] & (NISTC_DIO_SDIN | NISTC_DIO_SDOUT)) && |
| devpriv->serial_interval_ns) |
| return -EBUSY; |
| |
| if (comedi_dio_update_state(s, data)) { |
| devpriv->dio_output &= ~NISTC_DIO_OUT_PARALLEL_MASK; |
| devpriv->dio_output |= NISTC_DIO_OUT_PARALLEL(s->state); |
| ni_stc_writew(dev, devpriv->dio_output, NISTC_DIO_OUT_REG); |
| } |
| |
| data[1] = ni_stc_readw(dev, NISTC_DIO_IN_REG); |
| |
| return insn->n; |
| } |
| |
| #ifdef PCIDMA |
| static int ni_m_series_dio_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| int ret; |
| |
| if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) { |
| const struct ni_board_struct *board = dev->board_ptr; |
| |
| /* we don't care about actual channels */ |
| data[1] = board->dio_speed; |
| data[2] = 0; |
| return 0; |
| } |
| |
| ret = comedi_dio_insn_config(dev, s, insn, data, 0); |
| if (ret) |
| return ret; |
| |
| ni_writel(dev, s->io_bits, NI_M_DIO_DIR_REG); |
| |
| return insn->n; |
| } |
| |
| static int ni_m_series_dio_insn_bits(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| if (comedi_dio_update_state(s, data)) |
| ni_writel(dev, s->state, NI_M_DIO_REG); |
| |
| data[1] = ni_readl(dev, NI_M_DIO_REG); |
| |
| return insn->n; |
| } |
| |
| static int ni_cdio_check_chanlist(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_cmd *cmd) |
| { |
| int i; |
| |
| for (i = 0; i < cmd->chanlist_len; ++i) { |
| unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
| |
| if (chan != i) |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int ni_cdio_cmdtest(struct comedi_device *dev, |
| struct comedi_subdevice *s, struct comedi_cmd *cmd) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int bytes_per_scan; |
| int err = 0; |
| |
| /* Step 1 : check if triggers are trivially valid */ |
| |
| err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT); |
| err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); |
| err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); |
| err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
| err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE); |
| |
| if (err) |
| return 1; |
| |
| /* Step 2a : make sure trigger sources are unique */ |
| /* Step 2b : and mutually compatible */ |
| |
| /* Step 3: check if arguments are trivially valid */ |
| |
| err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
| |
| /* |
| * Although NI_D[IO]_SampleClock are the same, perhaps we should still, |
| * for completeness, test whether the cmd is output or input? |
| */ |
| err |= ni_check_trigger_arg(CR_CHAN(cmd->scan_begin_arg), |
| NI_DO_SampleClock, |
| &devpriv->routing_tables); |
| if (CR_RANGE(cmd->scan_begin_arg) != 0 || |
| CR_AREF(cmd->scan_begin_arg) != 0) |
| err |= -EINVAL; |
| |
| err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); |
| err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
| cmd->chanlist_len); |
| bytes_per_scan = comedi_bytes_per_scan_cmd(s, cmd); |
| if (bytes_per_scan) { |
| err |= comedi_check_trigger_arg_max(&cmd->stop_arg, |
| s->async->prealloc_bufsz / |
| bytes_per_scan); |
| } |
| |
| if (err) |
| return 3; |
| |
| /* Step 4: fix up any arguments */ |
| |
| /* Step 5: check channel list if it exists */ |
| |
| if (cmd->chanlist && cmd->chanlist_len > 0) |
| err |= ni_cdio_check_chanlist(dev, s, cmd); |
| |
| if (err) |
| return 5; |
| |
| return 0; |
| } |
| |
| static int ni_cdo_inttrig(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned int trig_num) |
| { |
| struct comedi_cmd *cmd = &s->async->cmd; |
| const unsigned int timeout = 1000; |
| int retval = 0; |
| unsigned int i; |
| struct ni_private *devpriv = dev->private; |
| unsigned long flags; |
| |
| if (trig_num != cmd->start_arg) |
| return -EINVAL; |
| |
| s->async->inttrig = NULL; |
| |
| /* read alloc the entire buffer */ |
| comedi_buf_read_alloc(s, s->async->prealloc_bufsz); |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->cdo_mite_chan) { |
| mite_prep_dma(devpriv->cdo_mite_chan, 32, 32); |
| mite_dma_arm(devpriv->cdo_mite_chan); |
| } else { |
| dev_err(dev->class_dev, "BUG: no cdo mite channel?\n"); |
| retval = -EIO; |
| } |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| if (retval < 0) |
| return retval; |
| |
| /* |
| * XXX not sure what interrupt C group does |
| * wait for dma to fill output fifo |
| * ni_writeb(dev, NI_M_INTC_ENA, NI_M_INTC_ENA_REG); |
| */ |
| for (i = 0; i < timeout; ++i) { |
| if (ni_readl(dev, NI_M_CDIO_STATUS_REG) & |
| NI_M_CDIO_STATUS_CDO_FIFO_FULL) |
| break; |
| usleep_range(10, 100); |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, "dma failed to fill cdo fifo!\n"); |
| s->cancel(dev, s); |
| return -EIO; |
| } |
| ni_writel(dev, NI_M_CDO_CMD_ARM | |
| NI_M_CDO_CMD_ERR_INT_ENA_SET | |
| NI_M_CDO_CMD_F_E_INT_ENA_SET, |
| NI_M_CDIO_CMD_REG); |
| return retval; |
| } |
| |
| static int ni_cdio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_private *devpriv = dev->private; |
| const struct comedi_cmd *cmd = &s->async->cmd; |
| unsigned int cdo_mode_bits; |
| int retval; |
| |
| ni_writel(dev, NI_M_CDO_CMD_RESET, NI_M_CDIO_CMD_REG); |
| /* |
| * Although NI_D[IO]_SampleClock are the same, perhaps we should still, |
| * for completeness, test whether the cmd is output or input(?) |
| */ |
| cdo_mode_bits = NI_M_CDO_MODE_FIFO_MODE | |
| NI_M_CDO_MODE_HALT_ON_ERROR | |
| NI_M_CDO_MODE_SAMPLE_SRC( |
| ni_get_reg_value( |
| CR_CHAN(cmd->scan_begin_arg), |
| NI_DO_SampleClock, |
| &devpriv->routing_tables)); |
| if (cmd->scan_begin_arg & CR_INVERT) |
| cdo_mode_bits |= NI_M_CDO_MODE_POLARITY; |
| ni_writel(dev, cdo_mode_bits, NI_M_CDO_MODE_REG); |
| if (s->io_bits) { |
| ni_writel(dev, s->state, NI_M_CDO_FIFO_DATA_REG); |
| ni_writel(dev, NI_M_CDO_CMD_SW_UPDATE, NI_M_CDIO_CMD_REG); |
| ni_writel(dev, s->io_bits, NI_M_CDO_MASK_ENA_REG); |
| } else { |
| dev_err(dev->class_dev, |
| "attempted to run digital output command with no lines configured as outputs\n"); |
| return -EIO; |
| } |
| retval = ni_request_cdo_mite_channel(dev); |
| if (retval < 0) |
| return retval; |
| |
| ni_cmd_set_mite_transfer(devpriv->cdo_mite_ring, s, cmd, |
| s->async->prealloc_bufsz / |
| comedi_bytes_per_scan(s)); |
| |
| s->async->inttrig = ni_cdo_inttrig; |
| |
| return 0; |
| } |
| |
| static int ni_cdio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| ni_writel(dev, NI_M_CDO_CMD_DISARM | |
| NI_M_CDO_CMD_ERR_INT_ENA_CLR | |
| NI_M_CDO_CMD_F_E_INT_ENA_CLR | |
| NI_M_CDO_CMD_F_REQ_INT_ENA_CLR, |
| NI_M_CDIO_CMD_REG); |
| /* |
| * XXX not sure what interrupt C group does |
| * ni_writeb(dev, 0, NI_M_INTC_ENA_REG); |
| */ |
| ni_writel(dev, 0, NI_M_CDO_MASK_ENA_REG); |
| ni_release_cdo_mite_channel(dev); |
| return 0; |
| } |
| |
| static void handle_cdio_interrupt(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int cdio_status; |
| struct comedi_subdevice *s = &dev->subdevices[NI_DIO_SUBDEV]; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags); |
| if (devpriv->cdo_mite_chan) |
| mite_ack_linkc(devpriv->cdo_mite_chan, s, true); |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); |
| |
| cdio_status = ni_readl(dev, NI_M_CDIO_STATUS_REG); |
| if (cdio_status & NI_M_CDIO_STATUS_CDO_ERROR) { |
| /* XXX just guessing this is needed and does something useful */ |
| ni_writel(dev, NI_M_CDO_CMD_ERR_INT_CONFIRM, |
| NI_M_CDIO_CMD_REG); |
| s->async->events |= COMEDI_CB_OVERFLOW; |
| } |
| if (cdio_status & NI_M_CDIO_STATUS_CDO_FIFO_EMPTY) { |
| ni_writel(dev, NI_M_CDO_CMD_F_E_INT_ENA_CLR, |
| NI_M_CDIO_CMD_REG); |
| /* s->async->events |= COMEDI_CB_EOA; */ |
| } |
| comedi_handle_events(dev, s); |
| } |
| #endif /* PCIDMA */ |
| |
| static int ni_serial_hw_readwrite8(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned char data_out, |
| unsigned char *data_in) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int status1; |
| int err = 0, count = 20; |
| |
| devpriv->dio_output &= ~NISTC_DIO_OUT_SERIAL_MASK; |
| devpriv->dio_output |= NISTC_DIO_OUT_SERIAL(data_out); |
| ni_stc_writew(dev, devpriv->dio_output, NISTC_DIO_OUT_REG); |
| |
| status1 = ni_stc_readw(dev, NISTC_STATUS1_REG); |
| if (status1 & NISTC_STATUS1_SERIO_IN_PROG) { |
| err = -EBUSY; |
| goto error; |
| } |
| |
| devpriv->dio_control |= NISTC_DIO_CTRL_HW_SER_START; |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| devpriv->dio_control &= ~NISTC_DIO_CTRL_HW_SER_START; |
| |
| /* Wait until STC says we're done, but don't loop infinitely. */ |
| while ((status1 = ni_stc_readw(dev, NISTC_STATUS1_REG)) & |
| NISTC_STATUS1_SERIO_IN_PROG) { |
| /* Delay one bit per loop */ |
| udelay((devpriv->serial_interval_ns + 999) / 1000); |
| if (--count < 0) { |
| dev_err(dev->class_dev, |
| "SPI serial I/O didn't finish in time!\n"); |
| err = -ETIME; |
| goto error; |
| } |
| } |
| |
| /* |
| * Delay for last bit. This delay is absolutely necessary, because |
| * NISTC_STATUS1_SERIO_IN_PROG goes high one bit too early. |
| */ |
| udelay((devpriv->serial_interval_ns + 999) / 1000); |
| |
| if (data_in) |
| *data_in = ni_stc_readw(dev, NISTC_DIO_SERIAL_IN_REG); |
| |
| error: |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| |
| return err; |
| } |
| |
| static int ni_serial_sw_readwrite8(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| unsigned char data_out, |
| unsigned char *data_in) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned char mask, input = 0; |
| |
| /* Wait for one bit before transfer */ |
| udelay((devpriv->serial_interval_ns + 999) / 1000); |
| |
| for (mask = 0x80; mask; mask >>= 1) { |
| /* |
| * Output current bit; note that we cannot touch s->state |
| * because it is a per-subdevice field, and serial is |
| * a separate subdevice from DIO. |
| */ |
| devpriv->dio_output &= ~NISTC_DIO_SDOUT; |
| if (data_out & mask) |
| devpriv->dio_output |= NISTC_DIO_SDOUT; |
| ni_stc_writew(dev, devpriv->dio_output, NISTC_DIO_OUT_REG); |
| |
| /* |
| * Assert SDCLK (active low, inverted), wait for half of |
| * the delay, deassert SDCLK, and wait for the other half. |
| */ |
| devpriv->dio_control |= NISTC_DIO_SDCLK; |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| |
| udelay((devpriv->serial_interval_ns + 999) / 2000); |
| |
| devpriv->dio_control &= ~NISTC_DIO_SDCLK; |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| |
| udelay((devpriv->serial_interval_ns + 999) / 2000); |
| |
| /* Input current bit */ |
| if (ni_stc_readw(dev, NISTC_DIO_IN_REG) & NISTC_DIO_SDIN) |
| input |= mask; |
| } |
| |
| if (data_in) |
| *data_in = input; |
| |
| return 0; |
| } |
| |
| static int ni_serial_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int clk_fout = devpriv->clock_and_fout; |
| int err = insn->n; |
| unsigned char byte_out, byte_in = 0; |
| |
| if (insn->n != 2) |
| return -EINVAL; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_SERIAL_CLOCK: |
| devpriv->serial_hw_mode = 1; |
| devpriv->dio_control |= NISTC_DIO_CTRL_HW_SER_ENA; |
| |
| if (data[1] == SERIAL_DISABLED) { |
| devpriv->serial_hw_mode = 0; |
| devpriv->dio_control &= ~(NISTC_DIO_CTRL_HW_SER_ENA | |
| NISTC_DIO_SDCLK); |
| data[1] = SERIAL_DISABLED; |
| devpriv->serial_interval_ns = data[1]; |
| } else if (data[1] <= SERIAL_600NS) { |
| /* |
| * Warning: this clock speed is too fast to reliably |
| * control SCXI. |
| */ |
| devpriv->dio_control &= ~NISTC_DIO_CTRL_HW_SER_TIMEBASE; |
| clk_fout |= NISTC_CLK_FOUT_SLOW_TIMEBASE; |
| clk_fout &= ~NISTC_CLK_FOUT_DIO_SER_OUT_DIV2; |
| data[1] = SERIAL_600NS; |
| devpriv->serial_interval_ns = data[1]; |
| } else if (data[1] <= SERIAL_1_2US) { |
| devpriv->dio_control &= ~NISTC_DIO_CTRL_HW_SER_TIMEBASE; |
| clk_fout |= NISTC_CLK_FOUT_SLOW_TIMEBASE | |
| NISTC_CLK_FOUT_DIO_SER_OUT_DIV2; |
| data[1] = SERIAL_1_2US; |
| devpriv->serial_interval_ns = data[1]; |
| } else if (data[1] <= SERIAL_10US) { |
| devpriv->dio_control |= NISTC_DIO_CTRL_HW_SER_TIMEBASE; |
| clk_fout |= NISTC_CLK_FOUT_SLOW_TIMEBASE | |
| NISTC_CLK_FOUT_DIO_SER_OUT_DIV2; |
| /* |
| * Note: NISTC_CLK_FOUT_DIO_SER_OUT_DIV2 only affects |
| * 600ns/1.2us. If you turn divide_by_2 off with the |
| * slow clock, you will still get 10us, except then |
| * all your delays are wrong. |
| */ |
| data[1] = SERIAL_10US; |
| devpriv->serial_interval_ns = data[1]; |
| } else { |
| devpriv->dio_control &= ~(NISTC_DIO_CTRL_HW_SER_ENA | |
| NISTC_DIO_SDCLK); |
| devpriv->serial_hw_mode = 0; |
| data[1] = (data[1] / 1000) * 1000; |
| devpriv->serial_interval_ns = data[1]; |
| } |
| devpriv->clock_and_fout = clk_fout; |
| |
| ni_stc_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| ni_stc_writew(dev, devpriv->clock_and_fout, NISTC_CLK_FOUT_REG); |
| return 1; |
| |
| case INSN_CONFIG_BIDIRECTIONAL_DATA: |
| |
| if (devpriv->serial_interval_ns == 0) |
| return -EINVAL; |
| |
| byte_out = data[1] & 0xFF; |
| |
| if (devpriv->serial_hw_mode) { |
| err = ni_serial_hw_readwrite8(dev, s, byte_out, |
| &byte_in); |
| } else if (devpriv->serial_interval_ns > 0) { |
| err = ni_serial_sw_readwrite8(dev, s, byte_out, |
| &byte_in); |
| } else { |
| dev_err(dev->class_dev, "serial disabled!\n"); |
| return -EINVAL; |
| } |
| if (err < 0) |
| return err; |
| data[1] = byte_in & 0xFF; |
| return insn->n; |
| |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static void init_ao_67xx(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| int i; |
| |
| for (i = 0; i < s->n_chan; i++) { |
| ni_ao_win_outw(dev, NI_E_AO_DACSEL(i) | 0x0, |
| NI67XX_AO_CFG2_REG); |
| } |
| ni_ao_win_outw(dev, 0x0, NI67XX_AO_SP_UPDATES_REG); |
| } |
| |
| static const struct mio_regmap ni_gpct_to_stc_regmap[] = { |
| [NITIO_G0_AUTO_INC] = { NISTC_G0_AUTOINC_REG, 2 }, |
| [NITIO_G1_AUTO_INC] = { NISTC_G1_AUTOINC_REG, 2 }, |
| [NITIO_G0_CMD] = { NISTC_G0_CMD_REG, 2 }, |
| [NITIO_G1_CMD] = { NISTC_G1_CMD_REG, 2 }, |
| [NITIO_G0_HW_SAVE] = { NISTC_G0_HW_SAVE_REG, 4 }, |
| [NITIO_G1_HW_SAVE] = { NISTC_G1_HW_SAVE_REG, 4 }, |
| [NITIO_G0_SW_SAVE] = { NISTC_G0_SAVE_REG, 4 }, |
| [NITIO_G1_SW_SAVE] = { NISTC_G1_SAVE_REG, 4 }, |
| [NITIO_G0_MODE] = { NISTC_G0_MODE_REG, 2 }, |
| [NITIO_G1_MODE] = { NISTC_G1_MODE_REG, 2 }, |
| [NITIO_G0_LOADA] = { NISTC_G0_LOADA_REG, 4 }, |
| [NITIO_G1_LOADA] = { NISTC_G1_LOADA_REG, 4 }, |
| [NITIO_G0_LOADB] = { NISTC_G0_LOADB_REG, 4 }, |
| [NITIO_G1_LOADB] = { NISTC_G1_LOADB_REG, 4 }, |
| [NITIO_G0_INPUT_SEL] = { NISTC_G0_INPUT_SEL_REG, 2 }, |
| [NITIO_G1_INPUT_SEL] = { NISTC_G1_INPUT_SEL_REG, 2 }, |
| [NITIO_G0_CNT_MODE] = { 0x1b0, 2 }, /* M-Series only */ |
| [NITIO_G1_CNT_MODE] = { 0x1b2, 2 }, /* M-Series only */ |
| [NITIO_G0_GATE2] = { 0x1b4, 2 }, /* M-Series only */ |
| [NITIO_G1_GATE2] = { 0x1b6, 2 }, /* M-Series only */ |
| [NITIO_G01_STATUS] = { NISTC_G01_STATUS_REG, 2 }, |
| [NITIO_G01_RESET] = { NISTC_RESET_REG, 2 }, |
| [NITIO_G01_STATUS1] = { NISTC_STATUS1_REG, 2 }, |
| [NITIO_G01_STATUS2] = { NISTC_STATUS2_REG, 2 }, |
| [NITIO_G0_DMA_CFG] = { 0x1b8, 2 }, /* M-Series only */ |
| [NITIO_G1_DMA_CFG] = { 0x1ba, 2 }, /* M-Series only */ |
| [NITIO_G0_DMA_STATUS] = { 0x1b8, 2 }, /* M-Series only */ |
| [NITIO_G1_DMA_STATUS] = { 0x1ba, 2 }, /* M-Series only */ |
| [NITIO_G0_ABZ] = { 0x1c0, 2 }, /* M-Series only */ |
| [NITIO_G1_ABZ] = { 0x1c2, 2 }, /* M-Series only */ |
| [NITIO_G0_INT_ACK] = { NISTC_INTA_ACK_REG, 2 }, |
| [NITIO_G1_INT_ACK] = { NISTC_INTB_ACK_REG, 2 }, |
| [NITIO_G0_STATUS] = { NISTC_AI_STATUS1_REG, 2 }, |
| [NITIO_G1_STATUS] = { NISTC_AO_STATUS1_REG, 2 }, |
| [NITIO_G0_INT_ENA] = { NISTC_INTA_ENA_REG, 2 }, |
| [NITIO_G1_INT_ENA] = { NISTC_INTB_ENA_REG, 2 }, |
| }; |
| |
| static unsigned int ni_gpct_to_stc_register(struct comedi_device *dev, |
| enum ni_gpct_register reg) |
| { |
| const struct mio_regmap *regmap; |
| |
| if (reg < ARRAY_SIZE(ni_gpct_to_stc_regmap)) { |
| regmap = &ni_gpct_to_stc_regmap[reg]; |
| } else { |
| dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n", |
| __func__, reg); |
| return 0; |
| } |
| |
| return regmap->mio_reg; |
| } |
| |
| static void ni_gpct_write_register(struct ni_gpct *counter, unsigned int bits, |
| enum ni_gpct_register reg) |
| { |
| struct comedi_device *dev = counter->counter_dev->dev; |
| unsigned int stc_register = ni_gpct_to_stc_register(dev, reg); |
| |
| if (stc_register == 0) |
| return; |
| |
| switch (reg) { |
| /* m-series only registers */ |
| case NITIO_G0_CNT_MODE: |
| case NITIO_G1_CNT_MODE: |
| case NITIO_G0_GATE2: |
| case NITIO_G1_GATE2: |
| case NITIO_G0_DMA_CFG: |
| case NITIO_G1_DMA_CFG: |
| case NITIO_G0_ABZ: |
| case NITIO_G1_ABZ: |
| ni_writew(dev, bits, stc_register); |
| break; |
| |
| /* 32 bit registers */ |
| case NITIO_G0_LOADA: |
| case NITIO_G1_LOADA: |
| case NITIO_G0_LOADB: |
| case NITIO_G1_LOADB: |
| ni_stc_writel(dev, bits, stc_register); |
| break; |
| |
| /* 16 bit registers */ |
| case NITIO_G0_INT_ENA: |
| ni_set_bitfield(dev, stc_register, |
| NISTC_INTA_ENA_G0_GATE | NISTC_INTA_ENA_G0_TC, |
| bits); |
| break; |
| case NITIO_G1_INT_ENA: |
| ni_set_bitfield(dev, stc_register, |
| NISTC_INTB_ENA_G1_GATE | NISTC_INTB_ENA_G1_TC, |
| bits); |
| break; |
| default: |
| ni_stc_writew(dev, bits, stc_register); |
| } |
| } |
| |
| static unsigned int ni_gpct_read_register(struct ni_gpct *counter, |
| enum ni_gpct_register reg) |
| { |
| struct comedi_device *dev = counter->counter_dev->dev; |
| unsigned int stc_register = ni_gpct_to_stc_register(dev, reg); |
| |
| if (stc_register == 0) |
| return 0; |
| |
| switch (reg) { |
| /* m-series only registers */ |
| case NITIO_G0_DMA_STATUS: |
| case NITIO_G1_DMA_STATUS: |
| return ni_readw(dev, stc_register); |
| |
| /* 32 bit registers */ |
| case NITIO_G0_HW_SAVE: |
| case NITIO_G1_HW_SAVE: |
| case NITIO_G0_SW_SAVE: |
| case NITIO_G1_SW_SAVE: |
| return ni_stc_readl(dev, stc_register); |
| |
| /* 16 bit registers */ |
| default: |
| return ni_stc_readw(dev, stc_register); |
| } |
| } |
| |
| static int ni_freq_out_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 val = NISTC_CLK_FOUT_TO_DIVIDER(devpriv->clock_and_fout); |
| int i; |
| |
| for (i = 0; i < insn->n; i++) |
| data[i] = val; |
| |
| return insn->n; |
| } |
| |
| static int ni_freq_out_insn_write(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (insn->n) { |
| unsigned int val = data[insn->n - 1]; |
| |
| devpriv->clock_and_fout &= ~NISTC_CLK_FOUT_ENA; |
| ni_stc_writew(dev, devpriv->clock_and_fout, NISTC_CLK_FOUT_REG); |
| devpriv->clock_and_fout &= ~NISTC_CLK_FOUT_DIVIDER_MASK; |
| |
| /* use the last data value to set the fout divider */ |
| devpriv->clock_and_fout |= NISTC_CLK_FOUT_DIVIDER(val); |
| |
| devpriv->clock_and_fout |= NISTC_CLK_FOUT_ENA; |
| ni_stc_writew(dev, devpriv->clock_and_fout, NISTC_CLK_FOUT_REG); |
| } |
| return insn->n; |
| } |
| |
| static int ni_freq_out_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_SET_CLOCK_SRC: |
| switch (data[1]) { |
| case NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC: |
| devpriv->clock_and_fout &= ~NISTC_CLK_FOUT_TIMEBASE_SEL; |
| break; |
| case NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC: |
| devpriv->clock_and_fout |= NISTC_CLK_FOUT_TIMEBASE_SEL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| ni_stc_writew(dev, devpriv->clock_and_fout, NISTC_CLK_FOUT_REG); |
| break; |
| case INSN_CONFIG_GET_CLOCK_SRC: |
| if (devpriv->clock_and_fout & NISTC_CLK_FOUT_TIMEBASE_SEL) { |
| data[1] = NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC; |
| data[2] = TIMEBASE_2_NS; |
| } else { |
| data[1] = NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC; |
| data[2] = TIMEBASE_1_NS * 2; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| return insn->n; |
| } |
| |
| static int ni_8255_callback(struct comedi_device *dev, |
| int dir, int port, int data, unsigned long iobase) |
| { |
| if (dir) { |
| ni_writeb(dev, data, iobase + 2 * port); |
| return 0; |
| } |
| |
| return ni_readb(dev, iobase + 2 * port); |
| } |
| |
| static int ni_get_pwm_config(struct comedi_device *dev, unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| data[1] = devpriv->pwm_up_count * devpriv->clock_ns; |
| data[2] = devpriv->pwm_down_count * devpriv->clock_ns; |
| return 3; |
| } |
| |
| static int ni_m_series_pwm_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int up_count, down_count; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_PWM_OUTPUT: |
| switch (data[1]) { |
| case CMDF_ROUND_NEAREST: |
| up_count = DIV_ROUND_CLOSEST(data[2], |
| devpriv->clock_ns); |
| break; |
| case CMDF_ROUND_DOWN: |
| up_count = data[2] / devpriv->clock_ns; |
| break; |
| case CMDF_ROUND_UP: |
| up_count = |
| DIV_ROUND_UP(data[2], devpriv->clock_ns); |
| break; |
| default: |
| return -EINVAL; |
| } |
| switch (data[3]) { |
| case CMDF_ROUND_NEAREST: |
| down_count = DIV_ROUND_CLOSEST(data[4], |
| devpriv->clock_ns); |
| break; |
| case CMDF_ROUND_DOWN: |
| down_count = data[4] / devpriv->clock_ns; |
| break; |
| case CMDF_ROUND_UP: |
| down_count = |
| DIV_ROUND_UP(data[4], devpriv->clock_ns); |
| break; |
| default: |
| return -EINVAL; |
| } |
| if (up_count * devpriv->clock_ns != data[2] || |
| down_count * devpriv->clock_ns != data[4]) { |
| data[2] = up_count * devpriv->clock_ns; |
| data[4] = down_count * devpriv->clock_ns; |
| return -EAGAIN; |
| } |
| ni_writel(dev, NI_M_CAL_PWM_HIGH_TIME(up_count) | |
| NI_M_CAL_PWM_LOW_TIME(down_count), |
| NI_M_CAL_PWM_REG); |
| devpriv->pwm_up_count = up_count; |
| devpriv->pwm_down_count = down_count; |
| return 5; |
| case INSN_CONFIG_GET_PWM_OUTPUT: |
| return ni_get_pwm_config(dev, data); |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int ni_6143_pwm_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int up_count, down_count; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_PWM_OUTPUT: |
| switch (data[1]) { |
| case CMDF_ROUND_NEAREST: |
| up_count = DIV_ROUND_CLOSEST(data[2], |
| devpriv->clock_ns); |
| break; |
| case CMDF_ROUND_DOWN: |
| up_count = data[2] / devpriv->clock_ns; |
| break; |
| case CMDF_ROUND_UP: |
| up_count = |
| DIV_ROUND_UP(data[2], devpriv->clock_ns); |
| break; |
| default: |
| return -EINVAL; |
| } |
| switch (data[3]) { |
| case CMDF_ROUND_NEAREST: |
| down_count = DIV_ROUND_CLOSEST(data[4], |
| devpriv->clock_ns); |
| break; |
| case CMDF_ROUND_DOWN: |
| down_count = data[4] / devpriv->clock_ns; |
| break; |
| case CMDF_ROUND_UP: |
| down_count = |
| DIV_ROUND_UP(data[4], devpriv->clock_ns); |
| break; |
| default: |
| return -EINVAL; |
| } |
| if (up_count * devpriv->clock_ns != data[2] || |
| down_count * devpriv->clock_ns != data[4]) { |
| data[2] = up_count * devpriv->clock_ns; |
| data[4] = down_count * devpriv->clock_ns; |
| return -EAGAIN; |
| } |
| ni_writel(dev, up_count, NI6143_CALIB_HI_TIME_REG); |
| devpriv->pwm_up_count = up_count; |
| ni_writel(dev, down_count, NI6143_CALIB_LO_TIME_REG); |
| devpriv->pwm_down_count = down_count; |
| return 5; |
| case INSN_CONFIG_GET_PWM_OUTPUT: |
| return ni_get_pwm_config(dev, data); |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int pack_mb88341(int addr, int val, int *bitstring) |
| { |
| /* |
| * Fujitsu MB 88341 |
| * Note that address bits are reversed. Thanks to |
| * Ingo Keen for noticing this. |
| * |
| * Note also that the 88341 expects address values from |
| * 1-12, whereas we use channel numbers 0-11. The NI |
| * docs use 1-12, also, so be careful here. |
| */ |
| addr++; |
| *bitstring = ((addr & 0x1) << 11) | |
| ((addr & 0x2) << 9) | |
| ((addr & 0x4) << 7) | ((addr & 0x8) << 5) | (val & 0xff); |
| return 12; |
| } |
| |
| static int pack_dac8800(int addr, int val, int *bitstring) |
| { |
| *bitstring = ((addr & 0x7) << 8) | (val & 0xff); |
| return 11; |
| } |
| |
| static int pack_dac8043(int addr, int val, int *bitstring) |
| { |
| *bitstring = val & 0xfff; |
| return 12; |
| } |
| |
| static int pack_ad8522(int addr, int val, int *bitstring) |
| { |
| *bitstring = (val & 0xfff) | (addr ? 0xc000 : 0xa000); |
| return 16; |
| } |
| |
| static int pack_ad8804(int addr, int val, int *bitstring) |
| { |
| *bitstring = ((addr & 0xf) << 8) | (val & 0xff); |
| return 12; |
| } |
| |
| static int pack_ad8842(int addr, int val, int *bitstring) |
| { |
| *bitstring = ((addr + 1) << 8) | (val & 0xff); |
| return 12; |
| } |
| |
| struct caldac_struct { |
| int n_chans; |
| int n_bits; |
| int (*packbits)(int address, int value, int *bitstring); |
| }; |
| |
| static struct caldac_struct caldacs[] = { |
| [mb88341] = {12, 8, pack_mb88341}, |
| [dac8800] = {8, 8, pack_dac8800}, |
| [dac8043] = {1, 12, pack_dac8043}, |
| [ad8522] = {2, 12, pack_ad8522}, |
| [ad8804] = {12, 8, pack_ad8804}, |
| [ad8842] = {8, 8, pack_ad8842}, |
| [ad8804_debug] = {16, 8, pack_ad8804}, |
| }; |
| |
| static void ni_write_caldac(struct comedi_device *dev, int addr, int val) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| unsigned int loadbit = 0, bits = 0, bit, bitstring = 0; |
| unsigned int cmd; |
| int i; |
| int type; |
| |
| if (devpriv->caldacs[addr] == val) |
| return; |
| devpriv->caldacs[addr] = val; |
| |
| for (i = 0; i < 3; i++) { |
| type = board->caldac[i]; |
| if (type == caldac_none) |
| break; |
| if (addr < caldacs[type].n_chans) { |
| bits = caldacs[type].packbits(addr, val, &bitstring); |
| loadbit = NI_E_SERIAL_CMD_DAC_LD(i); |
| break; |
| } |
| addr -= caldacs[type].n_chans; |
| } |
| |
| /* bits will be 0 if there is no caldac for the given addr */ |
| if (bits == 0) |
| return; |
| |
| for (bit = 1 << (bits - 1); bit; bit >>= 1) { |
| cmd = (bit & bitstring) ? NI_E_SERIAL_CMD_SDATA : 0; |
| ni_writeb(dev, cmd, NI_E_SERIAL_CMD_REG); |
| udelay(1); |
| ni_writeb(dev, NI_E_SERIAL_CMD_SCLK | cmd, NI_E_SERIAL_CMD_REG); |
| udelay(1); |
| } |
| ni_writeb(dev, loadbit, NI_E_SERIAL_CMD_REG); |
| udelay(1); |
| ni_writeb(dev, 0, NI_E_SERIAL_CMD_REG); |
| } |
| |
| static int ni_calib_insn_write(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| if (insn->n) { |
| /* only bother writing the last sample to the channel */ |
| ni_write_caldac(dev, CR_CHAN(insn->chanspec), |
| data[insn->n - 1]); |
| } |
| |
| return insn->n; |
| } |
| |
| static int ni_calib_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 i; |
| |
| for (i = 0; i < insn->n; i++) |
| data[0] = devpriv->caldacs[CR_CHAN(insn->chanspec)]; |
| |
| return insn->n; |
| } |
| |
| static void caldac_setup(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| int i, j; |
| int n_dacs; |
| int n_chans = 0; |
| int n_bits; |
| int diffbits = 0; |
| int type; |
| int chan; |
| |
| type = board->caldac[0]; |
| if (type == caldac_none) |
| return; |
| n_bits = caldacs[type].n_bits; |
| for (i = 0; i < 3; i++) { |
| type = board->caldac[i]; |
| if (type == caldac_none) |
| break; |
| if (caldacs[type].n_bits != n_bits) |
| diffbits = 1; |
| n_chans += caldacs[type].n_chans; |
| } |
| n_dacs = i; |
| s->n_chan = n_chans; |
| |
| if (diffbits) { |
| unsigned int *maxdata_list = devpriv->caldac_maxdata_list; |
| |
| if (n_chans > MAX_N_CALDACS) |
| dev_err(dev->class_dev, |
| "BUG! MAX_N_CALDACS too small\n"); |
| s->maxdata_list = maxdata_list; |
| chan = 0; |
| for (i = 0; i < n_dacs; i++) { |
| type = board->caldac[i]; |
| for (j = 0; j < caldacs[type].n_chans; j++) { |
| maxdata_list[chan] = |
| (1 << caldacs[type].n_bits) - 1; |
| chan++; |
| } |
| } |
| |
| for (chan = 0; chan < s->n_chan; chan++) |
| ni_write_caldac(dev, i, s->maxdata_list[i] / 2); |
| } else { |
| type = board->caldac[0]; |
| s->maxdata = (1 << caldacs[type].n_bits) - 1; |
| |
| for (chan = 0; chan < s->n_chan; chan++) |
| ni_write_caldac(dev, i, s->maxdata / 2); |
| } |
| } |
| |
| static int ni_read_eeprom(struct comedi_device *dev, int addr) |
| { |
| unsigned int cmd = NI_E_SERIAL_CMD_EEPROM_CS; |
| int bit; |
| int bitstring; |
| |
| bitstring = 0x0300 | ((addr & 0x100) << 3) | (addr & 0xff); |
| ni_writeb(dev, cmd, NI_E_SERIAL_CMD_REG); |
| for (bit = 0x8000; bit; bit >>= 1) { |
| if (bit & bitstring) |
| cmd |= NI_E_SERIAL_CMD_SDATA; |
| else |
| cmd &= ~NI_E_SERIAL_CMD_SDATA; |
| |
| ni_writeb(dev, cmd, NI_E_SERIAL_CMD_REG); |
| ni_writeb(dev, NI_E_SERIAL_CMD_SCLK | cmd, NI_E_SERIAL_CMD_REG); |
| } |
| cmd = NI_E_SERIAL_CMD_EEPROM_CS; |
| bitstring = 0; |
| for (bit = 0x80; bit; bit >>= 1) { |
| ni_writeb(dev, cmd, NI_E_SERIAL_CMD_REG); |
| ni_writeb(dev, NI_E_SERIAL_CMD_SCLK | cmd, NI_E_SERIAL_CMD_REG); |
| if (ni_readb(dev, NI_E_STATUS_REG) & NI_E_STATUS_PROMOUT) |
| bitstring |= bit; |
| } |
| ni_writeb(dev, 0, NI_E_SERIAL_CMD_REG); |
| |
| return bitstring; |
| } |
| |
| static int ni_eeprom_insn_read(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| unsigned int val; |
| unsigned int i; |
| |
| if (insn->n) { |
| val = ni_read_eeprom(dev, CR_CHAN(insn->chanspec)); |
| for (i = 0; i < insn->n; i++) |
| data[i] = val; |
| } |
| return insn->n; |
| } |
| |
| static int ni_m_series_eeprom_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 i; |
| |
| for (i = 0; i < insn->n; i++) |
| data[i] = devpriv->eeprom_buffer[CR_CHAN(insn->chanspec)]; |
| |
| return insn->n; |
| } |
| |
| static unsigned int ni_old_get_pfi_routing(struct comedi_device *dev, |
| unsigned int chan) |
| { |
| /* pre-m-series boards have fixed signals on pfi pins */ |
| switch (chan) { |
| case 0: |
| return NI_PFI_OUTPUT_AI_START1; |
| case 1: |
| return NI_PFI_OUTPUT_AI_START2; |
| case 2: |
| return NI_PFI_OUTPUT_AI_CONVERT; |
| case 3: |
| return NI_PFI_OUTPUT_G_SRC1; |
| case 4: |
| return NI_PFI_OUTPUT_G_GATE1; |
| case 5: |
| return NI_PFI_OUTPUT_AO_UPDATE_N; |
| case 6: |
| return NI_PFI_OUTPUT_AO_START1; |
| case 7: |
| return NI_PFI_OUTPUT_AI_START_PULSE; |
| case 8: |
| return NI_PFI_OUTPUT_G_SRC0; |
| case 9: |
| return NI_PFI_OUTPUT_G_GATE0; |
| default: |
| dev_err(dev->class_dev, "bug, unhandled case in switch.\n"); |
| break; |
| } |
| return 0; |
| } |
| |
| static int ni_old_set_pfi_routing(struct comedi_device *dev, |
| unsigned int chan, unsigned int source) |
| { |
| /* pre-m-series boards have fixed signals on pfi pins */ |
| if (source != ni_old_get_pfi_routing(dev, chan)) |
| return -EINVAL; |
| return 2; |
| } |
| |
| static unsigned int ni_m_series_get_pfi_routing(struct comedi_device *dev, |
| unsigned int chan) |
| { |
| struct ni_private *devpriv = dev->private; |
| const unsigned int array_offset = chan / 3; |
| |
| return NI_M_PFI_OUT_SEL_TO_SRC(chan, |
| devpriv->pfi_output_select_reg[array_offset]); |
| } |
| |
| static int ni_m_series_set_pfi_routing(struct comedi_device *dev, |
| unsigned int chan, unsigned int source) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int index = chan / 3; |
| unsigned short val = devpriv->pfi_output_select_reg[index]; |
| |
| if ((source & 0x1f) != source) |
| return -EINVAL; |
| |
| val &= ~NI_M_PFI_OUT_SEL_MASK(chan); |
| val |= NI_M_PFI_OUT_SEL(chan, source); |
| ni_writew(dev, val, NI_M_PFI_OUT_SEL_REG(index)); |
| devpriv->pfi_output_select_reg[index] = val; |
| |
| return 2; |
| } |
| |
| static unsigned int ni_get_pfi_routing(struct comedi_device *dev, |
| unsigned int chan) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= NI_PFI(0)) { |
| /* allow new and old names of pfi channels to work. */ |
| chan -= NI_PFI(0); |
| } |
| return (devpriv->is_m_series) |
| ? ni_m_series_get_pfi_routing(dev, chan) |
| : ni_old_get_pfi_routing(dev, chan); |
| } |
| |
| /* Sets the output mux for the specified PFI channel. */ |
| static int ni_set_pfi_routing(struct comedi_device *dev, |
| unsigned int chan, unsigned int source) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= NI_PFI(0)) { |
| /* allow new and old names of pfi channels to work. */ |
| chan -= NI_PFI(0); |
| } |
| return (devpriv->is_m_series) |
| ? ni_m_series_set_pfi_routing(dev, chan, source) |
| : ni_old_set_pfi_routing(dev, chan, source); |
| } |
| |
| static int ni_config_pfi_filter(struct comedi_device *dev, |
| unsigned int chan, |
| enum ni_pfi_filter_select filter) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int bits; |
| |
| if (!devpriv->is_m_series) |
| return -ENOTSUPP; |
| |
| if (chan >= NI_PFI(0)) { |
| /* allow new and old names of pfi channels to work. */ |
| chan -= NI_PFI(0); |
| } |
| |
| bits = ni_readl(dev, NI_M_PFI_FILTER_REG); |
| bits &= ~NI_M_PFI_FILTER_SEL_MASK(chan); |
| bits |= NI_M_PFI_FILTER_SEL(chan, filter); |
| ni_writel(dev, bits, NI_M_PFI_FILTER_REG); |
| return 0; |
| } |
| |
| static void ni_set_pfi_direction(struct comedi_device *dev, int chan, |
| unsigned int direction) |
| { |
| if (chan >= NI_PFI(0)) { |
| /* allow new and old names of pfi channels to work. */ |
| chan -= NI_PFI(0); |
| } |
| direction = (direction == COMEDI_OUTPUT) ? 1u : 0u; |
| ni_set_bits(dev, NISTC_IO_BIDIR_PIN_REG, 1 << chan, direction); |
| } |
| |
| static int ni_get_pfi_direction(struct comedi_device *dev, int chan) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= NI_PFI(0)) { |
| /* allow new and old names of pfi channels to work. */ |
| chan -= NI_PFI(0); |
| } |
| return devpriv->io_bidirection_pin_reg & (1 << chan) ? |
| COMEDI_OUTPUT : COMEDI_INPUT; |
| } |
| |
| static int ni_pfi_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| unsigned int chan; |
| |
| if (insn->n < 1) |
| return -EINVAL; |
| |
| chan = CR_CHAN(insn->chanspec); |
| |
| switch (data[0]) { |
| case COMEDI_OUTPUT: |
| case COMEDI_INPUT: |
| ni_set_pfi_direction(dev, chan, data[0]); |
| break; |
| case INSN_CONFIG_DIO_QUERY: |
| data[1] = ni_get_pfi_direction(dev, chan); |
| break; |
| case INSN_CONFIG_SET_ROUTING: |
| return ni_set_pfi_routing(dev, chan, data[1]); |
| case INSN_CONFIG_GET_ROUTING: |
| data[1] = ni_get_pfi_routing(dev, chan); |
| break; |
| case INSN_CONFIG_FILTER: |
| return ni_config_pfi_filter(dev, chan, data[1]); |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int ni_pfi_insn_bits(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (!devpriv->is_m_series) |
| return -ENOTSUPP; |
| |
| if (comedi_dio_update_state(s, data)) |
| ni_writew(dev, s->state, NI_M_PFI_DO_REG); |
| |
| data[1] = ni_readw(dev, NI_M_PFI_DI_REG); |
| |
| return insn->n; |
| } |
| |
| static int cs5529_wait_for_idle(struct comedi_device *dev) |
| { |
| unsigned short status; |
| const int timeout = HZ; |
| int i; |
| |
| for (i = 0; i < timeout; i++) { |
| status = ni_ao_win_inw(dev, NI67XX_CAL_STATUS_REG); |
| if ((status & NI67XX_CAL_STATUS_BUSY) == 0) |
| break; |
| set_current_state(TASK_INTERRUPTIBLE); |
| if (schedule_timeout(1)) |
| return -EIO; |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, "timeout\n"); |
| return -ETIME; |
| } |
| return 0; |
| } |
| |
| static void cs5529_command(struct comedi_device *dev, unsigned short value) |
| { |
| static const int timeout = 100; |
| int i; |
| |
| ni_ao_win_outw(dev, value, NI67XX_CAL_CMD_REG); |
| /* give time for command to start being serially clocked into cs5529. |
| * this insures that the NI67XX_CAL_STATUS_BUSY bit will get properly |
| * set before we exit this function. |
| */ |
| for (i = 0; i < timeout; i++) { |
| if (ni_ao_win_inw(dev, NI67XX_CAL_STATUS_REG) & |
| NI67XX_CAL_STATUS_BUSY) |
| break; |
| udelay(1); |
| } |
| if (i == timeout) |
| dev_err(dev->class_dev, |
| "possible problem - never saw adc go busy?\n"); |
| } |
| |
| static int cs5529_do_conversion(struct comedi_device *dev, |
| unsigned short *data) |
| { |
| int retval; |
| unsigned short status; |
| |
| cs5529_command(dev, CS5529_CMD_CB | CS5529_CMD_SINGLE_CONV); |
| retval = cs5529_wait_for_idle(dev); |
| if (retval) { |
| dev_err(dev->class_dev, |
| "timeout or signal in %s()\n", __func__); |
| return -ETIME; |
| } |
| status = ni_ao_win_inw(dev, NI67XX_CAL_STATUS_REG); |
| if (status & NI67XX_CAL_STATUS_OSC_DETECT) { |
| dev_err(dev->class_dev, |
| "cs5529 conversion error, status CSS_OSC_DETECT\n"); |
| return -EIO; |
| } |
| if (status & NI67XX_CAL_STATUS_OVERRANGE) { |
| dev_err(dev->class_dev, |
| "cs5529 conversion error, overrange (ignoring)\n"); |
| } |
| if (data) { |
| *data = ni_ao_win_inw(dev, NI67XX_CAL_DATA_REG); |
| /* cs5529 returns 16 bit signed data in bipolar mode */ |
| *data ^= BIT(15); |
| } |
| return 0; |
| } |
| |
| static int cs5529_ai_insn_read(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| int n, retval; |
| unsigned short sample; |
| unsigned int channel_select; |
| const unsigned int INTERNAL_REF = 0x1000; |
| |
| /* |
| * Set calibration adc source. Docs lie, reference select bits 8 to 11 |
| * do nothing. bit 12 seems to chooses internal reference voltage, bit |
| * 13 causes the adc input to go overrange (maybe reads external |
| * reference?) |
| */ |
| if (insn->chanspec & CR_ALT_SOURCE) |
| channel_select = INTERNAL_REF; |
| else |
| channel_select = CR_CHAN(insn->chanspec); |
| ni_ao_win_outw(dev, channel_select, NI67XX_AO_CAL_CHAN_SEL_REG); |
| |
| for (n = 0; n < insn->n; n++) { |
| retval = cs5529_do_conversion(dev, &sample); |
| if (retval < 0) |
| return retval; |
| data[n] = sample; |
| } |
| return insn->n; |
| } |
| |
| static void cs5529_config_write(struct comedi_device *dev, unsigned int value, |
| unsigned int reg_select_bits) |
| { |
| ni_ao_win_outw(dev, (value >> 16) & 0xff, NI67XX_CAL_CFG_HI_REG); |
| ni_ao_win_outw(dev, value & 0xffff, NI67XX_CAL_CFG_LO_REG); |
| reg_select_bits &= CS5529_CMD_REG_MASK; |
| cs5529_command(dev, CS5529_CMD_CB | reg_select_bits); |
| if (cs5529_wait_for_idle(dev)) |
| dev_err(dev->class_dev, |
| "timeout or signal in %s\n", __func__); |
| } |
| |
| static int init_cs5529(struct comedi_device *dev) |
| { |
| unsigned int config_bits = CS5529_CFG_PORT_FLAG | |
| CS5529_CFG_WORD_RATE_2180; |
| |
| #if 1 |
| /* do self-calibration */ |
| cs5529_config_write(dev, config_bits | CS5529_CFG_CALIB_BOTH_SELF, |
| CS5529_CFG_REG); |
| /* need to force a conversion for calibration to run */ |
| cs5529_do_conversion(dev, NULL); |
| #else |
| /* force gain calibration to 1 */ |
| cs5529_config_write(dev, 0x400000, CS5529_GAIN_REG); |
| cs5529_config_write(dev, config_bits | CS5529_CFG_CALIB_OFFSET_SELF, |
| CS5529_CFG_REG); |
| if (cs5529_wait_for_idle(dev)) |
| dev_err(dev->class_dev, |
| "timeout or signal in %s\n", __func__); |
| #endif |
| return 0; |
| } |
| |
| /* |
| * Find best multiplier/divider to try and get the PLL running at 80 MHz |
| * given an arbitrary frequency input clock. |
| */ |
| static int ni_mseries_get_pll_parameters(unsigned int reference_period_ns, |
| unsigned int *freq_divider, |
| unsigned int *freq_multiplier, |
| unsigned int *actual_period_ns) |
| { |
| unsigned int div; |
| unsigned int best_div = 1; |
| unsigned int mult; |
| unsigned int best_mult = 1; |
| static const unsigned int pico_per_nano = 1000; |
| const unsigned int reference_picosec = reference_period_ns * |
| pico_per_nano; |
| /* |
| * m-series wants the phased-locked loop to output 80MHz, which is |
| * divided by 4 to 20 MHz for most timing clocks |
| */ |
| static const unsigned int target_picosec = 12500; |
| int best_period_picosec = 0; |
| |
| for (div = 1; div <= NI_M_PLL_MAX_DIVISOR; ++div) { |
| for (mult = 1; mult <= NI_M_PLL_MAX_MULTIPLIER; ++mult) { |
| unsigned int new_period_ps = |
| (reference_picosec * div) / mult; |
| if (abs(new_period_ps - target_picosec) < |
| abs(best_period_picosec - target_picosec)) { |
| best_period_picosec = new_period_ps; |
| best_div = div; |
| best_mult = mult; |
| } |
| } |
| } |
| if (best_period_picosec == 0) |
| return -EIO; |
| |
| *freq_divider = best_div; |
| *freq_multiplier = best_mult; |
| /* return the actual period (* fudge factor for 80 to 20 MHz) */ |
| *actual_period_ns = DIV_ROUND_CLOSEST(best_period_picosec * 4, |
| pico_per_nano); |
| return 0; |
| } |
| |
| static int ni_mseries_set_pll_master_clock(struct comedi_device *dev, |
| unsigned int source, |
| unsigned int period_ns) |
| { |
| struct ni_private *devpriv = dev->private; |
| static const unsigned int min_period_ns = 50; |
| static const unsigned int max_period_ns = 1000; |
| static const unsigned int timeout = 1000; |
| unsigned int pll_control_bits; |
| unsigned int freq_divider; |
| unsigned int freq_multiplier; |
| unsigned int rtsi; |
| unsigned int i; |
| int retval; |
| |
| if (source == NI_MIO_PLL_PXI10_CLOCK) |
| period_ns = 100; |
| /* |
| * These limits are somewhat arbitrary, but NI advertises 1 to 20MHz |
| * range so we'll use that. |
| */ |
| if (period_ns < min_period_ns || period_ns > max_period_ns) { |
| dev_err(dev->class_dev, |
| "%s: you must specify an input clock frequency between %i and %i nanosec for the phased-lock loop\n", |
| __func__, min_period_ns, max_period_ns); |
| return -EINVAL; |
| } |
| devpriv->rtsi_trig_direction_reg &= ~NISTC_RTSI_TRIG_USE_CLK; |
| ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, |
| NISTC_RTSI_TRIG_DIR_REG); |
| pll_control_bits = NI_M_PLL_CTRL_ENA | NI_M_PLL_CTRL_VCO_MODE_75_150MHZ; |
| devpriv->clock_and_fout2 |= NI_M_CLK_FOUT2_TIMEBASE1_PLL | |
| NI_M_CLK_FOUT2_TIMEBASE3_PLL; |
| devpriv->clock_and_fout2 &= ~NI_M_CLK_FOUT2_PLL_SRC_MASK; |
| switch (source) { |
| case NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK: |
| devpriv->clock_and_fout2 |= NI_M_CLK_FOUT2_PLL_SRC_STAR; |
| break; |
| case NI_MIO_PLL_PXI10_CLOCK: |
| /* pxi clock is 10MHz */ |
| devpriv->clock_and_fout2 |= NI_M_CLK_FOUT2_PLL_SRC_PXI10; |
| break; |
| default: |
| for (rtsi = 0; rtsi <= NI_M_MAX_RTSI_CHAN; ++rtsi) { |
| if (source == NI_MIO_PLL_RTSI_CLOCK(rtsi)) { |
| devpriv->clock_and_fout2 |= |
| NI_M_CLK_FOUT2_PLL_SRC_RTSI(rtsi); |
| break; |
| } |
| } |
| if (rtsi > NI_M_MAX_RTSI_CHAN) |
| return -EINVAL; |
| break; |
| } |
| retval = ni_mseries_get_pll_parameters(period_ns, |
| &freq_divider, |
| &freq_multiplier, |
| &devpriv->clock_ns); |
| if (retval < 0) { |
| dev_err(dev->class_dev, |
| "bug, failed to find pll parameters\n"); |
| return retval; |
| } |
| |
| ni_writew(dev, devpriv->clock_and_fout2, NI_M_CLK_FOUT2_REG); |
| pll_control_bits |= NI_M_PLL_CTRL_DIVISOR(freq_divider) | |
| NI_M_PLL_CTRL_MULTIPLIER(freq_multiplier); |
| |
| ni_writew(dev, pll_control_bits, NI_M_PLL_CTRL_REG); |
| devpriv->clock_source = source; |
| /* it takes a few hundred microseconds for PLL to lock */ |
| for (i = 0; i < timeout; ++i) { |
| if (ni_readw(dev, NI_M_PLL_STATUS_REG) & NI_M_PLL_STATUS_LOCKED) |
| break; |
| udelay(1); |
| } |
| if (i == timeout) { |
| dev_err(dev->class_dev, |
| "%s: timed out waiting for PLL to lock to reference clock source %i with period %i ns\n", |
| __func__, source, period_ns); |
| return -ETIMEDOUT; |
| } |
| return 3; |
| } |
| |
| static int ni_set_master_clock(struct comedi_device *dev, |
| unsigned int source, unsigned int period_ns) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (source == NI_MIO_INTERNAL_CLOCK) { |
| devpriv->rtsi_trig_direction_reg &= ~NISTC_RTSI_TRIG_USE_CLK; |
| ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, |
| NISTC_RTSI_TRIG_DIR_REG); |
| devpriv->clock_ns = TIMEBASE_1_NS; |
| if (devpriv->is_m_series) { |
| devpriv->clock_and_fout2 &= |
| ~(NI_M_CLK_FOUT2_TIMEBASE1_PLL | |
| NI_M_CLK_FOUT2_TIMEBASE3_PLL); |
| ni_writew(dev, devpriv->clock_and_fout2, |
| NI_M_CLK_FOUT2_REG); |
| ni_writew(dev, 0, NI_M_PLL_CTRL_REG); |
| } |
| devpriv->clock_source = source; |
| } else { |
| if (devpriv->is_m_series) { |
| return ni_mseries_set_pll_master_clock(dev, source, |
| period_ns); |
| } else { |
| if (source == NI_MIO_RTSI_CLOCK) { |
| devpriv->rtsi_trig_direction_reg |= |
| NISTC_RTSI_TRIG_USE_CLK; |
| ni_stc_writew(dev, |
| devpriv->rtsi_trig_direction_reg, |
| NISTC_RTSI_TRIG_DIR_REG); |
| if (period_ns == 0) { |
| dev_err(dev->class_dev, |
| "we don't handle an unspecified clock period correctly yet, returning error\n"); |
| return -EINVAL; |
| } |
| devpriv->clock_ns = period_ns; |
| devpriv->clock_source = source; |
| } else { |
| return -EINVAL; |
| } |
| } |
| } |
| return 3; |
| } |
| |
| static int ni_valid_rtsi_output_source(struct comedi_device *dev, |
| unsigned int chan, unsigned int source) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { |
| if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| if (source == NI_RTSI_OUTPUT_RTSI_OSC) |
| return 1; |
| |
| dev_err(dev->class_dev, |
| "%s: invalid source for channel=%i, channel %i is always the RTSI clock for pre-m-series boards\n", |
| __func__, chan, NISTC_RTSI_TRIG_OLD_CLK_CHAN); |
| return 0; |
| } |
| return 0; |
| } |
| switch (source) { |
| case NI_RTSI_OUTPUT_ADR_START1: |
| case NI_RTSI_OUTPUT_ADR_START2: |
| case NI_RTSI_OUTPUT_SCLKG: |
| case NI_RTSI_OUTPUT_DACUPDN: |
| case NI_RTSI_OUTPUT_DA_START1: |
| case NI_RTSI_OUTPUT_G_SRC0: |
| case NI_RTSI_OUTPUT_G_GATE0: |
| case NI_RTSI_OUTPUT_RGOUT0: |
| case NI_RTSI_OUTPUT_RTSI_BRD(0): |
| case NI_RTSI_OUTPUT_RTSI_BRD(1): |
| case NI_RTSI_OUTPUT_RTSI_BRD(2): |
| case NI_RTSI_OUTPUT_RTSI_BRD(3): |
| return 1; |
| case NI_RTSI_OUTPUT_RTSI_OSC: |
| return (devpriv->is_m_series) ? 1 : 0; |
| default: |
| return 0; |
| } |
| } |
| |
| static int ni_set_rtsi_routing(struct comedi_device *dev, |
| unsigned int chan, unsigned int src) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= TRIGGER_LINE(0)) |
| /* allow new and old names of rtsi channels to work. */ |
| chan -= TRIGGER_LINE(0); |
| |
| if (ni_valid_rtsi_output_source(dev, chan, src) == 0) |
| return -EINVAL; |
| if (chan < 4) { |
| devpriv->rtsi_trig_a_output_reg &= ~NISTC_RTSI_TRIG_MASK(chan); |
| devpriv->rtsi_trig_a_output_reg |= NISTC_RTSI_TRIG(chan, src); |
| ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg, |
| NISTC_RTSI_TRIGA_OUT_REG); |
| } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { |
| devpriv->rtsi_trig_b_output_reg &= ~NISTC_RTSI_TRIG_MASK(chan); |
| devpriv->rtsi_trig_b_output_reg |= NISTC_RTSI_TRIG(chan, src); |
| ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, |
| NISTC_RTSI_TRIGB_OUT_REG); |
| } else if (chan != NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| /* probably should never reach this, since the |
| * ni_valid_rtsi_output_source above errors out if chan is too |
| * high |
| */ |
| dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); |
| return -EINVAL; |
| } |
| return 2; |
| } |
| |
| static unsigned int ni_get_rtsi_routing(struct comedi_device *dev, |
| unsigned int chan) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (chan >= TRIGGER_LINE(0)) |
| /* allow new and old names of rtsi channels to work. */ |
| chan -= TRIGGER_LINE(0); |
| |
| if (chan < 4) { |
| return NISTC_RTSI_TRIG_TO_SRC(chan, |
| devpriv->rtsi_trig_a_output_reg); |
| } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { |
| return NISTC_RTSI_TRIG_TO_SRC(chan, |
| devpriv->rtsi_trig_b_output_reg); |
| } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| return NI_RTSI_OUTPUT_RTSI_OSC; |
| } |
| |
| dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); |
| return -EINVAL; |
| } |
| |
| static void ni_set_rtsi_direction(struct comedi_device *dev, int chan, |
| unsigned int direction) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int max_chan = NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series); |
| |
| if (chan >= TRIGGER_LINE(0)) |
| /* allow new and old names of rtsi channels to work. */ |
| chan -= TRIGGER_LINE(0); |
| |
| if (direction == COMEDI_OUTPUT) { |
| if (chan < max_chan) { |
| devpriv->rtsi_trig_direction_reg |= |
| NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series); |
| } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| devpriv->rtsi_trig_direction_reg |= |
| NISTC_RTSI_TRIG_DRV_CLK; |
| } |
| } else { |
| if (chan < max_chan) { |
| devpriv->rtsi_trig_direction_reg &= |
| ~NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series); |
| } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| devpriv->rtsi_trig_direction_reg &= |
| ~NISTC_RTSI_TRIG_DRV_CLK; |
| } |
| } |
| ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, |
| NISTC_RTSI_TRIG_DIR_REG); |
| } |
| |
| static int ni_get_rtsi_direction(struct comedi_device *dev, int chan) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int max_chan = NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series); |
| |
| if (chan >= TRIGGER_LINE(0)) |
| /* allow new and old names of rtsi channels to work. */ |
| chan -= TRIGGER_LINE(0); |
| |
| if (chan < max_chan) { |
| return (devpriv->rtsi_trig_direction_reg & |
| NISTC_RTSI_TRIG_DIR(chan, devpriv->is_m_series)) |
| ? COMEDI_OUTPUT : COMEDI_INPUT; |
| } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { |
| return (devpriv->rtsi_trig_direction_reg & |
| NISTC_RTSI_TRIG_DRV_CLK) |
| ? COMEDI_OUTPUT : COMEDI_INPUT; |
| } |
| return -EINVAL; |
| } |
| |
| static int ni_rtsi_insn_config(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); |
| |
| switch (data[0]) { |
| case COMEDI_OUTPUT: |
| case COMEDI_INPUT: |
| ni_set_rtsi_direction(dev, chan, data[0]); |
| break; |
| case INSN_CONFIG_DIO_QUERY: { |
| int ret = ni_get_rtsi_direction(dev, chan); |
| |
| if (ret < 0) |
| return ret; |
| data[1] = ret; |
| return 2; |
| } |
| case INSN_CONFIG_SET_CLOCK_SRC: |
| return ni_set_master_clock(dev, data[1], data[2]); |
| case INSN_CONFIG_GET_CLOCK_SRC: |
| data[1] = devpriv->clock_source; |
| data[2] = devpriv->clock_ns; |
| return 3; |
| case INSN_CONFIG_SET_ROUTING: |
| return ni_set_rtsi_routing(dev, chan, data[1]); |
| case INSN_CONFIG_GET_ROUTING: { |
| int ret = ni_get_rtsi_routing(dev, chan); |
| |
| if (ret < 0) |
| return ret; |
| data[1] = ret; |
| return 2; |
| } |
| default: |
| return -EINVAL; |
| } |
| return 1; |
| } |
| |
| static int ni_rtsi_insn_bits(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| data[1] = 0; |
| |
| return insn->n; |
| } |
| |
| /* |
| * Default routing for RTSI trigger lines. |
| * |
| * These values are used here in the init function, as well as in the |
| * disconnect_route function, after a RTSI route has been disconnected. |
| */ |
| static const int default_rtsi_routing[] = { |
| [0] = NI_RTSI_OUTPUT_ADR_START1, |
| [1] = NI_RTSI_OUTPUT_ADR_START2, |
| [2] = NI_RTSI_OUTPUT_SCLKG, |
| [3] = NI_RTSI_OUTPUT_DACUPDN, |
| [4] = NI_RTSI_OUTPUT_DA_START1, |
| [5] = NI_RTSI_OUTPUT_G_SRC0, |
| [6] = NI_RTSI_OUTPUT_G_GATE0, |
| [7] = NI_RTSI_OUTPUT_RTSI_OSC, |
| }; |
| |
| /* |
| * Route signals through RGOUT0 terminal. |
| * @reg: raw register value of RGOUT0 bits (only bit0 is important). |
| * @dev: comedi device handle. |
| */ |
| static void set_rgout0_reg(int reg, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (devpriv->is_m_series) { |
| devpriv->rtsi_trig_direction_reg &= |
| ~NISTC_RTSI_TRIG_DIR_SUB_SEL1; |
| devpriv->rtsi_trig_direction_reg |= |
| (reg << NISTC_RTSI_TRIG_DIR_SUB_SEL1_SHIFT) & |
| NISTC_RTSI_TRIG_DIR_SUB_SEL1; |
| ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg, |
| NISTC_RTSI_TRIG_DIR_REG); |
| } else { |
| devpriv->rtsi_trig_b_output_reg &= ~NISTC_RTSI_TRIGB_SUB_SEL1; |
| devpriv->rtsi_trig_b_output_reg |= |
| (reg << NISTC_RTSI_TRIGB_SUB_SEL1_SHIFT) & |
| NISTC_RTSI_TRIGB_SUB_SEL1; |
| ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, |
| NISTC_RTSI_TRIGB_OUT_REG); |
| } |
| } |
| |
| static int get_rgout0_reg(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int reg; |
| |
| if (devpriv->is_m_series) |
| reg = (devpriv->rtsi_trig_direction_reg & |
| NISTC_RTSI_TRIG_DIR_SUB_SEL1) |
| >> NISTC_RTSI_TRIG_DIR_SUB_SEL1_SHIFT; |
| else |
| reg = (devpriv->rtsi_trig_b_output_reg & |
| NISTC_RTSI_TRIGB_SUB_SEL1) |
| >> NISTC_RTSI_TRIGB_SUB_SEL1_SHIFT; |
| return reg; |
| } |
| |
| static inline int get_rgout0_src(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int reg = get_rgout0_reg(dev); |
| |
| return ni_find_route_source(reg, NI_RGOUT0, &devpriv->routing_tables); |
| } |
| |
| /* |
| * Route signals through RGOUT0 terminal and increment the RGOUT0 use for this |
| * particular route. |
| * @src: device-global signal name |
| * @dev: comedi device handle |
| * |
| * Return: -EINVAL if the source is not valid to route to RGOUT0; |
| * -EBUSY if the RGOUT0 is already used; |
| * 0 if successful. |
| */ |
| static int incr_rgout0_src_use(int src, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_lookup_route_register(CR_CHAN(src), NI_RGOUT0, |
| &devpriv->routing_tables); |
| |
| if (reg < 0) |
| return -EINVAL; |
| |
| if (devpriv->rgout0_usage > 0 && get_rgout0_reg(dev) != reg) |
| return -EBUSY; |
| |
| ++devpriv->rgout0_usage; |
| set_rgout0_reg(reg, dev); |
| return 0; |
| } |
| |
| /* |
| * Unroute signals through RGOUT0 terminal and deccrement the RGOUT0 use for |
| * this particular source. This function does not actually unroute anything |
| * with respect to RGOUT0. It does, on the other hand, decrement the usage |
| * counter for the current src->RGOUT0 mapping. |
| * |
| * Return: -EINVAL if the source is not already routed to RGOUT0 (or usage is |
| * already at zero); 0 if successful. |
| */ |
| static int decr_rgout0_src_use(int src, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_lookup_route_register(CR_CHAN(src), NI_RGOUT0, |
| &devpriv->routing_tables); |
| |
| if (devpriv->rgout0_usage > 0 && get_rgout0_reg(dev) == reg) { |
| --devpriv->rgout0_usage; |
| if (!devpriv->rgout0_usage) |
| set_rgout0_reg(0, dev); /* ok default? */ |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| /* |
| * Route signals through given NI_RTSI_BRD mux. |
| * @i: index of mux to route |
| * @reg: raw register value of RTSI_BRD bits |
| * @dev: comedi device handle |
| */ |
| static void set_ith_rtsi_brd_reg(int i, int reg, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int reg_i_sz = 3; /* value for e-series */ |
| int reg_i_mask; |
| int reg_i_shift; |
| |
| if (devpriv->is_m_series) |
| reg_i_sz = 4; |
| reg_i_mask = ~((~0) << reg_i_sz); |
| reg_i_shift = i * reg_i_sz; |
| |
| /* clear out the current reg_i for ith brd */ |
| devpriv->rtsi_shared_mux_reg &= ~(reg_i_mask << reg_i_shift); |
| /* (softcopy) write the new reg_i for ith brd */ |
| devpriv->rtsi_shared_mux_reg |= (reg & reg_i_mask) << reg_i_shift; |
| /* (hardcopy) write the new reg_i for ith brd */ |
| ni_stc_writew(dev, devpriv->rtsi_shared_mux_reg, NISTC_RTSI_BOARD_REG); |
| } |
| |
| static int get_ith_rtsi_brd_reg(int i, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int reg_i_sz = 3; /* value for e-series */ |
| int reg_i_mask; |
| int reg_i_shift; |
| |
| if (devpriv->is_m_series) |
| reg_i_sz = 4; |
| reg_i_mask = ~((~0) << reg_i_sz); |
| reg_i_shift = i * reg_i_sz; |
| |
| return (devpriv->rtsi_shared_mux_reg >> reg_i_shift) & reg_i_mask; |
| } |
| |
| static inline int get_rtsi_brd_src(int brd, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int brd_index = brd; |
| int reg; |
| |
| if (brd >= NI_RTSI_BRD(0)) |
| brd_index = brd - NI_RTSI_BRD(0); |
| else |
| brd = NI_RTSI_BRD(brd); |
| /* |
| * And now: |
| * brd : device-global name |
| * brd_index : index number of RTSI_BRD mux |
| */ |
| |
| reg = get_ith_rtsi_brd_reg(brd_index, dev); |
| |
| return ni_find_route_source(reg, brd, &devpriv->routing_tables); |
| } |
| |
| /* |
| * Route signals through NI_RTSI_BRD mux and increment the use counter for this |
| * particular route. |
| * |
| * Return: -EINVAL if the source is not valid to route to NI_RTSI_BRD(i); |
| * -EBUSY if all NI_RTSI_BRD muxes are already used; |
| * NI_RTSI_BRD(i) of allocated ith mux if successful. |
| */ |
| static int incr_rtsi_brd_src_use(int src, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int first_available = -1; |
| int err = -EINVAL; |
| s8 reg; |
| int i; |
| |
| /* first look for a mux that is already configured to provide src */ |
| for (i = 0; i < NUM_RTSI_SHARED_MUXS; ++i) { |
| reg = ni_lookup_route_register(CR_CHAN(src), NI_RTSI_BRD(i), |
| &devpriv->routing_tables); |
| |
| if (reg < 0) |
| continue; /* invalid route */ |
| |
| if (!devpriv->rtsi_shared_mux_usage[i]) { |
| if (first_available < 0) |
| /* found the first unused, but usable mux */ |
| first_available = i; |
| } else { |
| /* |
| * we've seen at least one possible route, so change the |
| * final error to -EBUSY in case there are no muxes |
| * available. |
| */ |
| err = -EBUSY; |
| |
| if (get_ith_rtsi_brd_reg(i, dev) == reg) { |
| /* |
| * we've found a mux that is already being used |
| * to provide the requested signal. Reuse it. |
| */ |
| goto success; |
| } |
| } |
| } |
| |
| if (first_available < 0) |
| return err; |
| |
| /* we did not find a mux to reuse, but there is at least one usable */ |
| i = first_available; |
| |
| success: |
| ++devpriv->rtsi_shared_mux_usage[i]; |
| set_ith_rtsi_brd_reg(i, reg, dev); |
| return NI_RTSI_BRD(i); |
| } |
| |
| /* |
| * Unroute signals through NI_RTSI_BRD mux and decrement the user counter for |
| * this particular route. |
| * |
| * Return: -EINVAL if the source is not already routed to rtsi_brd(i) (or usage |
| * is already at zero); 0 if successful. |
| */ |
| static int decr_rtsi_brd_src_use(int src, int rtsi_brd, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_lookup_route_register(CR_CHAN(src), rtsi_brd, |
| &devpriv->routing_tables); |
| const int i = rtsi_brd - NI_RTSI_BRD(0); |
| |
| if (devpriv->rtsi_shared_mux_usage[i] > 0 && |
| get_ith_rtsi_brd_reg(i, dev) == reg) { |
| --devpriv->rtsi_shared_mux_usage[i]; |
| if (!devpriv->rtsi_shared_mux_usage[i]) |
| set_ith_rtsi_brd_reg(i, 0, dev); /* ok default? */ |
| return 0; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static void ni_rtsi_init(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int i; |
| |
| /* Initialises the RTSI bus signal switch to a default state */ |
| |
| /* |
| * Use 10MHz instead of 20MHz for RTSI clock frequency. Appears |
| * to have no effect, at least on pxi-6281, which always uses |
| * 20MHz rtsi clock frequency |
| */ |
| devpriv->clock_and_fout2 = NI_M_CLK_FOUT2_RTSI_10MHZ; |
| /* Set clock mode to internal */ |
| if (ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0) |
| dev_err(dev->class_dev, "ni_set_master_clock failed, bug?\n"); |
| |
| /* default internal lines routing to RTSI bus lines */ |
| for (i = 0; i < 8; ++i) { |
| ni_set_rtsi_direction(dev, i, COMEDI_INPUT); |
| ni_set_rtsi_routing(dev, i, default_rtsi_routing[i]); |
| } |
| |
| /* |
| * Sets the source and direction of the 4 on board lines. |
| * This configures all board lines to be: |
| * for e-series: |
| * 1) inputs (not sure what "output" would mean) |
| * 2) copying TRIGGER_LINE(0) (or RTSI0) output |
| * for m-series: |
| * copying NI_PFI(0) output |
| */ |
| devpriv->rtsi_shared_mux_reg = 0; |
| for (i = 0; i < 4; ++i) |
| set_ith_rtsi_brd_reg(i, 0, dev); |
| memset(devpriv->rtsi_shared_mux_usage, 0, |
| sizeof(devpriv->rtsi_shared_mux_usage)); |
| |
| /* initialize rgout0 pin as unused. */ |
| devpriv->rgout0_usage = 0; |
| set_rgout0_reg(0, dev); |
| } |
| |
| /* Get route of GPFO_i/CtrOut pins */ |
| static inline int ni_get_gout_routing(unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| unsigned int reg = devpriv->an_trig_etc_reg; |
| |
| switch (dest) { |
| case 0: |
| if (reg & NISTC_ATRIG_ETC_GPFO_0_ENA) |
| return NISTC_ATRIG_ETC_GPFO_0_SEL_TO_SRC(reg); |
| break; |
| case 1: |
| if (reg & NISTC_ATRIG_ETC_GPFO_1_ENA) |
| return NISTC_ATRIG_ETC_GPFO_1_SEL_TO_SRC(reg); |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| /* Set route of GPFO_i/CtrOut pins */ |
| static inline int ni_disable_gout_routing(unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| switch (dest) { |
| case 0: |
| devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_0_ENA; |
| break; |
| case 1: |
| devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_1_ENA; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG); |
| return 0; |
| } |
| |
| /* Set route of GPFO_i/CtrOut pins */ |
| static inline int ni_set_gout_routing(unsigned int src, unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| switch (dest) { |
| case 0: |
| /* clear reg */ |
| devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_0_SEL(-1); |
| /* set reg */ |
| devpriv->an_trig_etc_reg |= NISTC_ATRIG_ETC_GPFO_0_ENA |
| | NISTC_ATRIG_ETC_GPFO_0_SEL(src); |
| break; |
| case 1: |
| /* clear reg */ |
| devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_GPFO_1_SEL; |
| src = src ? NISTC_ATRIG_ETC_GPFO_1_SEL : 0; |
| /* set reg */ |
| devpriv->an_trig_etc_reg |= NISTC_ATRIG_ETC_GPFO_1_ENA | src; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG); |
| return 0; |
| } |
| |
| /* |
| * Retrieves the current source of the output selector for the given |
| * destination. If the terminal for the destination is not already configured |
| * as an output, this function returns -EINVAL as error. |
| * |
| * Return: the register value of the destination output selector; |
| * -EINVAL if terminal is not configured for output. |
| */ |
| static int get_output_select_source(int dest, struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| int reg = -1; |
| |
| if (channel_is_pfi(dest)) { |
| if (ni_get_pfi_direction(dev, dest) == COMEDI_OUTPUT) |
| reg = ni_get_pfi_routing(dev, dest); |
| } else if (channel_is_rtsi(dest)) { |
| if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) { |
| reg = ni_get_rtsi_routing(dev, dest); |
| |
| if (reg == NI_RTSI_OUTPUT_RGOUT0) { |
| dest = NI_RGOUT0; /* prepare for lookup below */ |
| reg = get_rgout0_reg(dev); |
| } else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) && |
| reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) { |
| const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0); |
| |
| dest = NI_RTSI_BRD(i); /* prepare for lookup */ |
| reg = get_ith_rtsi_brd_reg(i, dev); |
| } |
| } |
| } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { |
| /* |
| * not handled by ni_tio. Only available for GPFO registers in |
| * e/m series. |
| */ |
| dest -= NI_CtrOut(0); |
| if (dest > 1) |
| /* there are only two g_out outputs. */ |
| return -EINVAL; |
| reg = ni_get_gout_routing(dest, dev); |
| } else if (channel_is_ctr(dest)) { |
| reg = ni_tio_get_routing(devpriv->counter_dev, dest); |
| } else { |
| dev_dbg(dev->class_dev, "%s: unhandled destination (%d) queried\n", |
| __func__, dest); |
| } |
| |
| if (reg >= 0) |
| return ni_find_route_source(CR_CHAN(reg), dest, |
| &devpriv->routing_tables); |
| return -EINVAL; |
| } |
| |
| /* |
| * Test a route: |
| * |
| * Return: -1 if not connectible; |
| * 0 if connectible and not connected; |
| * 1 if connectible and connected. |
| */ |
| static int test_route(unsigned int src, unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_route_to_register(CR_CHAN(src), dest, |
| &devpriv->routing_tables); |
| |
| if (reg < 0) |
| return -1; |
| if (get_output_select_source(dest, dev) != CR_CHAN(src)) |
| return 0; |
| return 1; |
| } |
| |
| /* Connect the actual route. */ |
| static int connect_route(unsigned int src, unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_route_to_register(CR_CHAN(src), dest, |
| &devpriv->routing_tables); |
| s8 current_src; |
| |
| if (reg < 0) |
| /* route is not valid */ |
| return -EINVAL; |
| |
| current_src = get_output_select_source(dest, dev); |
| if (current_src == CR_CHAN(src)) |
| return -EALREADY; |
| if (current_src >= 0) |
| /* destination mux is already busy. complain, don't overwrite */ |
| return -EBUSY; |
| |
| /* The route is valid and available. Now connect... */ |
| if (channel_is_pfi(dest)) { |
| /* set routing source, then open output */ |
| ni_set_pfi_routing(dev, dest, reg); |
| ni_set_pfi_direction(dev, dest, COMEDI_OUTPUT); |
| } else if (channel_is_rtsi(dest)) { |
| if (reg == NI_RTSI_OUTPUT_RGOUT0) { |
| int ret = incr_rgout0_src_use(src, dev); |
| |
| if (ret < 0) |
| return ret; |
| } else if (ni_rtsi_route_requires_mux(reg)) { |
| /* Attempt to allocate and route (src->brd) */ |
| int brd = incr_rtsi_brd_src_use(src, dev); |
| |
| if (brd < 0) |
| return brd; |
| |
| /* Now lookup the register value for (brd->dest) */ |
| reg = ni_lookup_route_register( |
| brd, dest, &devpriv->routing_tables); |
| } |
| |
| ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT); |
| ni_set_rtsi_routing(dev, dest, reg); |
| } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { |
| /* |
| * not handled by ni_tio. Only available for GPFO registers in |
| * e/m series. |
| */ |
| dest -= NI_CtrOut(0); |
| if (dest > 1) |
| /* there are only two g_out outputs. */ |
| return -EINVAL; |
| if (ni_set_gout_routing(src, dest, dev)) |
| return -EINVAL; |
| } else if (channel_is_ctr(dest)) { |
| /* |
| * we are adding back the channel modifier info to set |
| * invert/edge info passed by the user |
| */ |
| ni_tio_set_routing(devpriv->counter_dev, dest, |
| reg | (src & ~CR_CHAN(-1))); |
| } else { |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int disconnect_route(unsigned int src, unsigned int dest, |
| struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| s8 reg = ni_route_to_register(CR_CHAN(src), dest, |
| &devpriv->routing_tables); |
| |
| if (reg < 0) |
| /* route is not valid */ |
| return -EINVAL; |
| if (get_output_select_source(dest, dev) != src) |
| /* cannot disconnect something not connected */ |
| return -EINVAL; |
| |
| /* The route is valid and is connected. Now disconnect... */ |
| if (channel_is_pfi(dest)) { |
| /* set the pfi to high impedance, and disconnect */ |
| ni_set_pfi_direction(dev, dest, COMEDI_INPUT); |
| ni_set_pfi_routing(dev, dest, NI_PFI_OUTPUT_PFI_DEFAULT); |
| } else if (channel_is_rtsi(dest)) { |
| if (reg == NI_RTSI_OUTPUT_RGOUT0) { |
| int ret = decr_rgout0_src_use(src, dev); |
| |
| if (ret < 0) |
| return ret; |
| } else if (ni_rtsi_route_requires_mux(reg)) { |
| /* find which RTSI_BRD line is source for rtsi pin */ |
| int brd = ni_find_route_source( |
| ni_get_rtsi_routing(dev, dest), dest, |
| &devpriv->routing_tables); |
| |
| if (brd < 0) |
| return brd; |
| |
| /* decrement/disconnect RTSI_BRD line from source */ |
| decr_rtsi_brd_src_use(src, brd, dev); |
| } |
| |
| /* set rtsi output selector to default state */ |
| reg = default_rtsi_routing[dest - TRIGGER_LINE(0)]; |
| ni_set_rtsi_direction(dev, dest, COMEDI_INPUT); |
| ni_set_rtsi_routing(dev, dest, reg); |
| } else if (dest >= NI_CtrOut(0) && dest <= NI_CtrOut(-1)) { |
| /* |
| * not handled by ni_tio. Only available for GPFO registers in |
| * e/m series. |
| */ |
| dest -= NI_CtrOut(0); |
| if (dest > 1) |
| /* there are only two g_out outputs. */ |
| return -EINVAL; |
| reg = ni_disable_gout_routing(dest, dev); |
| } else if (channel_is_ctr(dest)) { |
| ni_tio_unset_routing(devpriv->counter_dev, dest); |
| } else { |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int ni_global_insn_config(struct comedi_device *dev, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| switch (data[0]) { |
| case INSN_DEVICE_CONFIG_TEST_ROUTE: |
| data[0] = test_route(data[1], data[2], dev); |
| return 2; |
| case INSN_DEVICE_CONFIG_CONNECT_ROUTE: |
| return connect_route(data[1], data[2], dev); |
| case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: |
| return disconnect_route(data[1], data[2], dev); |
| /* |
| * This case is already handled one level up. |
| * case INSN_DEVICE_CONFIG_GET_ROUTES: |
| */ |
| default: |
| return -EINVAL; |
| } |
| return 1; |
| } |
| |
| #ifdef PCIDMA |
| static int ni_gpct_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_gpct *counter = s->private; |
| int retval; |
| |
| retval = ni_request_gpct_mite_channel(dev, counter->counter_index, |
| COMEDI_INPUT); |
| if (retval) { |
| dev_err(dev->class_dev, |
| "no dma channel available for use by counter\n"); |
| return retval; |
| } |
| ni_tio_acknowledge(counter); |
| ni_e_series_enable_second_irq(dev, counter->counter_index, 1); |
| |
| return ni_tio_cmd(dev, s); |
| } |
| |
| static int ni_gpct_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
| { |
| struct ni_gpct *counter = s->private; |
| int retval; |
| |
| retval = ni_tio_cancel(counter); |
| ni_e_series_enable_second_irq(dev, counter->counter_index, 0); |
| ni_release_gpct_mite_channel(dev, counter->counter_index); |
| return retval; |
| } |
| #endif |
| |
| static irqreturn_t ni_E_interrupt(int irq, void *d) |
| { |
| struct comedi_device *dev = d; |
| struct comedi_subdevice *s_ai = dev->read_subdev; |
| struct comedi_subdevice *s_ao = dev->write_subdev; |
| unsigned short a_status; |
| unsigned short b_status; |
| unsigned long flags; |
| #ifdef PCIDMA |
| struct ni_private *devpriv = dev->private; |
| #endif |
| |
| if (!dev->attached) |
| return IRQ_NONE; |
| smp_mb(); /* make sure dev->attached is checked */ |
| |
| /* lock to avoid race with comedi_poll */ |
| spin_lock_irqsave(&dev->spinlock, flags); |
| a_status = ni_stc_readw(dev, NISTC_AI_STATUS1_REG); |
| b_status = ni_stc_readw(dev, NISTC_AO_STATUS1_REG); |
| #ifdef PCIDMA |
| if (devpriv->mite) { |
| unsigned long flags_too; |
| |
| spin_lock_irqsave(&devpriv->mite_channel_lock, flags_too); |
| if (s_ai && devpriv->ai_mite_chan) |
| mite_ack_linkc(devpriv->ai_mite_chan, s_ai, false); |
| if (s_ao && devpriv->ao_mite_chan) |
| mite_ack_linkc(devpriv->ao_mite_chan, s_ao, false); |
| spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags_too); |
| } |
| #endif |
| ack_a_interrupt(dev, a_status); |
| ack_b_interrupt(dev, b_status); |
| if (s_ai) { |
| if (a_status & NISTC_AI_STATUS1_INTA) |
| handle_a_interrupt(dev, s_ai, a_status); |
| /* handle any interrupt or dma events */ |
| comedi_handle_events(dev, s_ai); |
| } |
| if (s_ao) { |
| if (b_status & NISTC_AO_STATUS1_INTB) |
| handle_b_interrupt(dev, s_ao, b_status); |
| /* handle any interrupt or dma events */ |
| comedi_handle_events(dev, s_ao); |
| } |
| handle_gpct_interrupt(dev, 0); |
| handle_gpct_interrupt(dev, 1); |
| #ifdef PCIDMA |
| if (devpriv->is_m_series) |
| handle_cdio_interrupt(dev); |
| #endif |
| |
| spin_unlock_irqrestore(&dev->spinlock, flags); |
| return IRQ_HANDLED; |
| } |
| |
| static int ni_alloc_private(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv; |
| |
| devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
| if (!devpriv) |
| return -ENOMEM; |
| |
| spin_lock_init(&devpriv->window_lock); |
| spin_lock_init(&devpriv->soft_reg_copy_lock); |
| spin_lock_init(&devpriv->mite_channel_lock); |
| |
| return 0; |
| } |
| |
| static unsigned int _ni_get_valid_routes(struct comedi_device *dev, |
| unsigned int n_pairs, |
| unsigned int *pair_data) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| return ni_get_valid_routes(&devpriv->routing_tables, n_pairs, |
| pair_data); |
| } |
| |
| static int ni_E_init(struct comedi_device *dev, |
| unsigned int interrupt_pin, unsigned int irq_polarity) |
| { |
| const struct ni_board_struct *board = dev->board_ptr; |
| struct ni_private *devpriv = dev->private; |
| struct comedi_subdevice *s; |
| int ret; |
| int i; |
| const char *dev_family = devpriv->is_m_series ? "ni_mseries" |
| : "ni_eseries"; |
| |
| /* prepare the device for globally-named routes. */ |
| if (ni_assign_device_routes(dev_family, board->name, |
| board->alt_route_name, |
| &devpriv->routing_tables) < 0) { |
| dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", |
| __func__, board->name); |
| dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n", |
| __func__, board->name); |
| } else { |
| /* |
| * only(?) assign insn_device_config if we have global names for |
| * this device. |
| */ |
| dev->insn_device_config = ni_global_insn_config; |
| dev->get_valid_routes = _ni_get_valid_routes; |
| } |
| |
| if (board->n_aochan > MAX_N_AO_CHAN) { |
| dev_err(dev->class_dev, "bug! n_aochan > MAX_N_AO_CHAN\n"); |
| return -EINVAL; |
| } |
| |
| /* initialize clock dividers */ |
| devpriv->clock_and_fout = NISTC_CLK_FOUT_SLOW_DIV2 | |
| NISTC_CLK_FOUT_SLOW_TIMEBASE | |
| NISTC_CLK_FOUT_TO_BOARD_DIV2 | |
| NISTC_CLK_FOUT_TO_BOARD; |
| if (!devpriv->is_6xxx) { |
| /* BEAM is this needed for PCI-6143 ?? */ |
| devpriv->clock_and_fout |= (NISTC_CLK_FOUT_AI_OUT_DIV2 | |
| NISTC_CLK_FOUT_AO_OUT_DIV2); |
| } |
| ni_stc_writew(dev, devpriv->clock_and_fout, NISTC_CLK_FOUT_REG); |
| |
| ret = comedi_alloc_subdevices(dev, NI_NUM_SUBDEVICES); |
| if (ret) |
| return ret; |
| |
| /* Analog Input subdevice */ |
| s = &dev->subdevices[NI_AI_SUBDEV]; |
| if (board->n_adchan) { |
| s->type = COMEDI_SUBD_AI; |
| s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_DITHER; |
| if (!devpriv->is_611x) |
| s->subdev_flags |= SDF_GROUND | SDF_COMMON | SDF_OTHER; |
| if (board->ai_maxdata > 0xffff) |
| s->subdev_flags |= SDF_LSAMPL; |
| if (devpriv->is_m_series) |
| s->subdev_flags |= SDF_SOFT_CALIBRATED; |
| s->n_chan = board->n_adchan; |
| s->maxdata = board->ai_maxdata; |
| s->range_table = ni_range_lkup[board->gainlkup]; |
| s->insn_read = ni_ai_insn_read; |
| s->insn_config = ni_ai_insn_config; |
| if (dev->irq) { |
| dev->read_subdev = s; |
| s->subdev_flags |= SDF_CMD_READ; |
| s->len_chanlist = 512; |
| s->do_cmdtest = ni_ai_cmdtest; |
| s->do_cmd = ni_ai_cmd; |
| s->cancel = ni_ai_reset; |
| s->poll = ni_ai_poll; |
| s->munge = ni_ai_munge; |
| |
| if (devpriv->mite) |
| s->async_dma_dir = DMA_FROM_DEVICE; |
| } |
| |
| /* reset the analog input configuration */ |
| ni_ai_reset(dev, s); |
| } else { |
| s->type = COMEDI_SUBD_UNUSED; |
| } |
| |
| /* Analog Output subdevice */ |
| s = &dev->subdevices[NI_AO_SUBDEV]; |
| if (board->n_aochan) { |
| s->type = COMEDI_SUBD_AO; |
| s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND; |
| if (devpriv->is_m_series) |
| s->subdev_flags |= SDF_SOFT_CALIBRATED; |
| s->n_chan = board->n_aochan; |
| s->maxdata = board->ao_maxdata; |
| s->range_table = board->ao_range_table; |
| s->insn_config = ni_ao_insn_config; |
| s->insn_write = ni_ao_insn_write; |
| |
| ret = comedi_alloc_subdev_readback(s); |
| if (ret) |
| return ret; |
| |
| /* |
| * Along with the IRQ we need either a FIFO or DMA for |
| * async command support. |
| */ |
| if (dev->irq && (board->ao_fifo_depth || devpriv->mite)) { |
| dev->write_subdev = s; |
| s->subdev_flags |= SDF_CMD_WRITE; |
| s->len_chanlist = s->n_chan; |
| s->do_cmdtest = ni_ao_cmdtest; |
| s->do_cmd = ni_ao_cmd; |
| s->cancel = ni_ao_reset; |
| if (!devpriv->is_m_series) |
| s->munge = ni_ao_munge; |
| |
| if (devpriv->mite) |
| s->async_dma_dir = DMA_TO_DEVICE; |
| } |
| |
| if (devpriv->is_67xx) |
| init_ao_67xx(dev, s); |
| |
| /* reset the analog output configuration */ |
| ni_ao_reset(dev, s); |
| } else { |
| s->type = COMEDI_SUBD_UNUSED; |
| } |
| |
| /* Digital I/O subdevice */ |
| s = &dev->subdevices[NI_DIO_SUBDEV]; |
| s->type = COMEDI_SUBD_DIO; |
| s->subdev_flags = SDF_WRITABLE | SDF_READABLE; |
| s->n_chan = board->has_32dio_chan ? 32 : 8; |
| s->maxdata = 1; |
| s->range_table = &range_digital; |
| if (devpriv->is_m_series) { |
| #ifdef PCIDMA |
| s->subdev_flags |= SDF_LSAMPL; |
| s->insn_bits = ni_m_series_dio_insn_bits; |
| s->insn_config = ni_m_series_dio_insn_config; |
| if (dev->irq) { |
| s->subdev_flags |= SDF_CMD_WRITE /* | SDF_CMD_READ */; |
| s->len_chanlist = s->n_chan; |
| s->do_cmdtest = ni_cdio_cmdtest; |
| s->do_cmd = ni_cdio_cmd; |
| s->cancel = ni_cdio_cancel; |
| |
| /* M-series boards use DMA */ |
| s->async_dma_dir = DMA_BIDIRECTIONAL; |
| } |
| |
| /* reset DIO and set all channels to inputs */ |
| ni_writel(dev, NI_M_CDO_CMD_RESET | |
| NI_M_CDI_CMD_RESET, |
| NI_M_CDIO_CMD_REG); |
| ni_writel(dev, s->io_bits, NI_M_DIO_DIR_REG); |
| #endif /* PCIDMA */ |
| } else { |
| s->insn_bits = ni_dio_insn_bits; |
| s->insn_config = ni_dio_insn_config; |
| |
| /* set all channels to inputs */ |
| devpriv->dio_control = NISTC_DIO_CTRL_DIR(s->io_bits); |
| ni_writew(dev, devpriv->dio_control, NISTC_DIO_CTRL_REG); |
| } |
| |
| /* 8255 device */ |
| s = &dev->subdevices[NI_8255_DIO_SUBDEV]; |
| if (board->has_8255) { |
| ret = subdev_8255_init(dev, s, ni_8255_callback, |
| NI_E_8255_BASE); |
| if (ret) |
| return ret; |
| } else { |
| s->type = COMEDI_SUBD_UNUSED; |
| } |
| |
| /* formerly general purpose counter/timer device, but no longer used */ |
| s = &dev->subdevices[NI_UNUSED_SUBDEV]; |
| s->type = COMEDI_SUBD_UNUSED; |
| |
| /* Calibration subdevice */ |
| s = &dev->subdevices[NI_CALIBRATION_SUBDEV]; |
| s->type = COMEDI_SUBD_CALIB; |
| s->subdev_flags = SDF_INTERNAL; |
| s->n_chan = 1; |
| s->maxdata = 0; |
| if (devpriv->is_m_series) { |
| /* internal PWM output used for AI nonlinearity calibration */ |
| s->insn_config = ni_m_series_pwm_config; |
| |
| ni_writel(dev, 0x0, NI_M_CAL_PWM_REG); |
| } else if (devpriv->is_6143) { |
| /* internal PWM output used for AI nonlinearity calibration */ |
| s->insn_config = ni_6143_pwm_config; |
| } else { |
| s->subdev_flags |= SDF_WRITABLE; |
| s->insn_read = ni_calib_insn_read; |
| s->insn_write = ni_calib_insn_write; |
| |
| /* setup the caldacs and find the real n_chan and maxdata */ |
| caldac_setup(dev, s); |
| } |
| |
| /* EEPROM subdevice */ |
| s = &dev->subdevices[NI_EEPROM_SUBDEV]; |
| s->type = COMEDI_SUBD_MEMORY; |
| s->subdev_flags = SDF_READABLE | SDF_INTERNAL; |
| s->maxdata = 0xff; |
| if (devpriv->is_m_series) { |
| s->n_chan = M_SERIES_EEPROM_SIZE; |
| s->insn_read = ni_m_series_eeprom_insn_read; |
| } else { |
| s->n_chan = 512; |
| s->insn_read = ni_eeprom_insn_read; |
| } |
| |
| /* Digital I/O (PFI) subdevice */ |
| s = &dev->subdevices[NI_PFI_DIO_SUBDEV]; |
| s->type = COMEDI_SUBD_DIO; |
| s->maxdata = 1; |
| if (devpriv->is_m_series) { |
| s->n_chan = 16; |
| s->insn_bits = ni_pfi_insn_bits; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; |
| |
| ni_writew(dev, s->state, NI_M_PFI_DO_REG); |
| for (i = 0; i < NUM_PFI_OUTPUT_SELECT_REGS; ++i) { |
| ni_writew(dev, devpriv->pfi_output_select_reg[i], |
| NI_M_PFI_OUT_SEL_REG(i)); |
| } |
| } else { |
| s->n_chan = 10; |
| s->subdev_flags = SDF_INTERNAL; |
| } |
| s->insn_config = ni_pfi_insn_config; |
| |
| ni_set_bits(dev, NISTC_IO_BIDIR_PIN_REG, ~0, 0); |
| |
| /* cs5529 calibration adc */ |
| s = &dev->subdevices[NI_CS5529_CALIBRATION_SUBDEV]; |
| if (devpriv->is_67xx) { |
| s->type = COMEDI_SUBD_AI; |
| s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_INTERNAL; |
| /* one channel for each analog output channel */ |
| s->n_chan = board->n_aochan; |
| s->maxdata = BIT(16) - 1; |
| s->range_table = &range_unknown; /* XXX */ |
| s->insn_read = cs5529_ai_insn_read; |
| s->insn_config = NULL; |
| init_cs5529(dev); |
| } else { |
| s->type = COMEDI_SUBD_UNUSED; |
| } |
| |
| /* Serial */ |
| s = &dev->subdevices[NI_SERIAL_SUBDEV]; |
| s->type = COMEDI_SUBD_SERIAL; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; |
| s->n_chan = 1; |
| s->maxdata = 0xff; |
| s->insn_config = ni_serial_insn_config; |
| devpriv->serial_interval_ns = 0; |
| devpriv->serial_hw_mode = 0; |
| |
| /* RTSI */ |
| s = &dev->subdevices[NI_RTSI_SUBDEV]; |
| s->type = COMEDI_SUBD_DIO; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; |
| s->n_chan = 8; |
| s->maxdata = 1; |
| s->insn_bits = ni_rtsi_insn_bits; |
| s->insn_config = ni_rtsi_insn_config; |
| ni_rtsi_init(dev); |
| |
| /* allocate and initialize the gpct counter device */ |
| devpriv->counter_dev = ni_gpct_device_construct(dev, |
| ni_gpct_write_register, |
| ni_gpct_read_register, |
| (devpriv->is_m_series) |
| ? ni_gpct_variant_m_series |
| : ni_gpct_variant_e_series, |
| NUM_GPCT, |
| NUM_GPCT, |
| &devpriv->routing_tables); |
| if (!devpriv->counter_dev) |
| return -ENOMEM; |
| |
| /* Counter (gpct) subdevices */ |
| for (i = 0; i < NUM_GPCT; ++i) { |
| struct ni_gpct *gpct = &devpriv->counter_dev->counters[i]; |
| |
| /* setup and initialize the counter */ |
| ni_tio_init_counter(gpct); |
| |
| s = &dev->subdevices[NI_GPCT_SUBDEV(i)]; |
| s->type = COMEDI_SUBD_COUNTER; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL; |
| s->n_chan = 3; |
| s->maxdata = (devpriv->is_m_series) ? 0xffffffff |
| : 0x00ffffff; |
| s->insn_read = ni_tio_insn_read; |
| s->insn_write = ni_tio_insn_write; |
| s->insn_config = ni_tio_insn_config; |
| #ifdef PCIDMA |
| if (dev->irq && devpriv->mite) { |
| s->subdev_flags |= SDF_CMD_READ /* | SDF_CMD_WRITE */; |
| s->len_chanlist = 1; |
| s->do_cmdtest = ni_tio_cmdtest; |
| s->do_cmd = ni_gpct_cmd; |
| s->cancel = ni_gpct_cancel; |
| |
| s->async_dma_dir = DMA_BIDIRECTIONAL; |
| } |
| #endif |
| s->private = gpct; |
| } |
| |
| /* Initialize GPFO_{0,1} to produce output of counters */ |
| ni_set_gout_routing(0, 0, dev); /* output of counter 0; DAQ STC, p338 */ |
| ni_set_gout_routing(0, 1, dev); /* output of counter 1; DAQ STC, p338 */ |
| |
| /* Frequency output subdevice */ |
| s = &dev->subdevices[NI_FREQ_OUT_SUBDEV]; |
| s->type = COMEDI_SUBD_COUNTER; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE; |
| s->n_chan = 1; |
| s->maxdata = 0xf; |
| s->insn_read = ni_freq_out_insn_read; |
| s->insn_write = ni_freq_out_insn_write; |
| s->insn_config = ni_freq_out_insn_config; |
| |
| if (dev->irq) { |
| ni_stc_writew(dev, |
| (irq_polarity ? NISTC_INT_CTRL_INT_POL : 0) | |
| (NISTC_INT_CTRL_3PIN_INT & 0) | |
| NISTC_INT_CTRL_INTA_ENA | |
| NISTC_INT_CTRL_INTB_ENA | |
| NISTC_INT_CTRL_INTA_SEL(interrupt_pin) | |
| NISTC_INT_CTRL_INTB_SEL(interrupt_pin), |
| NISTC_INT_CTRL_REG); |
| } |
| |
| /* DMA setup */ |
| ni_writeb(dev, devpriv->ai_ao_select_reg, NI_E_DMA_AI_AO_SEL_REG); |
| ni_writeb(dev, devpriv->g0_g1_select_reg, NI_E_DMA_G0_G1_SEL_REG); |
| |
| if (devpriv->is_6xxx) { |
| ni_writeb(dev, 0, NI611X_MAGIC_REG); |
| } else if (devpriv->is_m_series) { |
| int channel; |
| |
| for (channel = 0; channel < board->n_aochan; ++channel) { |
| ni_writeb(dev, 0xf, |
| NI_M_AO_WAVEFORM_ORDER_REG(channel)); |
| ni_writeb(dev, 0x0, |
| NI_M_AO_REF_ATTENUATION_REG(channel)); |
| } |
| ni_writeb(dev, 0x0, NI_M_AO_CALIB_REG); |
| } |
| |
| return 0; |
| } |
| |
| static void mio_common_detach(struct comedi_device *dev) |
| { |
| struct ni_private *devpriv = dev->private; |
| |
| if (devpriv) |
| ni_gpct_device_destroy(devpriv->counter_dev); |
| } |