* As far as I can tell, the About interrupt doesn't work if Sample is
* also enabled. It turns out that About really isn't needed, since
* we always count down samples read.
- *
- * There was some timer/counter code, but it didn't follow the right API.
*/
/*
#include "../comedi_pci.h"
+#include "comedi_8254.h"
#include "plx9080.h"
/*
#define FS_DIN_NOT_EMPTY BIT(12) /* DIN FIFO not empty */
#define FS_DIN_HEMPTY BIT(13) /* DIN FIFO half empty */
#define FS_DIN_NOT_FULL BIT(14) /* DIN FIFO not full */
-#define LAS0_DAC1 0x0014 /* Software D/A1 Update (w) */
-#define LAS0_DAC2 0x0018 /* Software D/A2 Update (w) */
+#define LAS0_UPDATE_DAC(x) (0x0014 + ((x) * 0x4)) /* D/Ax Update (w) */
#define LAS0_DAC 0x0024 /* Software Simultaneous Update (w) */
#define LAS0_PACER 0x0028 /* Software Pacer Start/Stop */
#define LAS0_TIMER 0x002c /* Timer Status/HDIN Software Trig. */
#define LAS0_DCNT 0x0054 /* Delay counter (16 bit) */
#define LAS0_ACNT 0x0058 /* About counter (16 bit) */
#define LAS0_DAC_CLK 0x005c /* DAC clock (16bit) */
-#define LAS0_UTC0 0x0060 /* 8254 TC Counter 0 */
-#define LAS0_UTC1 0x0064 /* 8254 TC Counter 1 */
-#define LAS0_UTC2 0x0068 /* 8254 TC Counter 2 */
-#define LAS0_UTC_CTRL 0x006c /* 8254 TC Control */
+#define LAS0_8254_TIMER_BASE 0x0060 /* 8254 timer/counter base */
#define LAS0_DIO0 0x0070 /* Digital I/O Port 0 */
#define LAS0_DIO1 0x0074 /* Digital I/O Port 1 */
#define LAS0_DIO0_CTRL 0x0078 /* Digital I/O Control */
#define LAS0_CGT_PAUSE 0x0144 /* Table Pause Enable */
#define LAS0_CGT_RESET 0x0148 /* Reset Channel Gain Table */
#define LAS0_CGT_CLEAR 0x014c /* Clear Channel Gain Table */
-#define LAS0_DAC1_CTRL 0x0150 /* D/A1 output type/range */
-#define LAS0_DAC1_SRC 0x0154 /* D/A1 update source */
-#define LAS0_DAC1_CYCLE 0x0158 /* D/A1 cycle mode */
-#define LAS0_DAC1_RESET 0x015c /* D/A1 FIFO reset */
-#define LAS0_DAC1_FIFO_CLEAR 0x0160 /* D/A1 FIFO clear */
-#define LAS0_DAC2_CTRL 0x0164 /* D/A2 output type/range */
-#define LAS0_DAC2_SRC 0x0168 /* D/A2 update source */
-#define LAS0_DAC2_CYCLE 0x016c /* D/A2 cycle mode */
-#define LAS0_DAC2_RESET 0x0170 /* D/A2 FIFO reset */
-#define LAS0_DAC2_FIFO_CLEAR 0x0174 /* D/A2 FIFO clear */
+#define LAS0_DAC_CTRL(x) (0x0150 + ((x) * 0x14)) /* D/Ax type/range */
+#define LAS0_DAC_SRC(x) (0x0154 + ((x) * 0x14)) /* D/Ax update source */
+#define LAS0_DAC_CYCLE(x) (0x0158 + ((x) * 0x14)) /* D/Ax cycle mode */
+#define LAS0_DAC_RESET(x) (0x015c + ((x) * 0x14)) /* D/Ax FIFO reset */
+#define LAS0_DAC_FIFO_CLEAR(x) (0x0160 + ((x) * 0x14)) /* D/Ax FIFO clear */
#define LAS0_ADC_SCNT_SRC 0x0178 /* A/D Sample Counter Source select */
#define LAS0_PACER_SELECT 0x0180 /* Pacer Clock select */
#define LAS0_SBUS0_SRC 0x0184 /* SyncBus 0 Source select */
#define LAS0_SBUS2_ENABLE 0x019c /* SyncBus 2 enable */
#define LAS0_ETRG_POLARITY 0x01a4 /* Ext. Trigger polarity select */
#define LAS0_EINT_POLARITY 0x01a8 /* Ext. Interrupt polarity select */
-#define LAS0_UTC0_CLOCK 0x01ac /* UTC0 Clock select */
-#define LAS0_UTC0_GATE 0x01b0 /* UTC0 Gate select */
-#define LAS0_UTC1_CLOCK 0x01b4 /* UTC1 Clock select */
-#define LAS0_UTC1_GATE 0x01b8 /* UTC1 Gate select */
-#define LAS0_UTC2_CLOCK 0x01bc /* UTC2 Clock select */
-#define LAS0_UTC2_GATE 0x01c0 /* UTC2 Gate select */
+#define LAS0_8254_CLK_SEL(x) (0x01ac + ((x) * 0x8)) /* 8254 clock select */
+#define LAS0_8254_GATE_SEL(x) (0x01b0 + ((x) * 0x8)) /* 8254 gate select */
#define LAS0_UOUT0_SELECT 0x01c4 /* User Output 0 source select */
#define LAS0_UOUT1_SELECT 0x01c8 /* User Output 1 source select */
#define LAS0_DMA0_RESET 0x01cc /* DMA0 Request state machine reset */
*/
#define LAS1_ADC_FIFO 0x0000 /* A/D FIFO (16bit) */
#define LAS1_HDIO_FIFO 0x0004 /* HiSpd DI FIFO (16bit) */
-#define LAS1_DAC1_FIFO 0x0008 /* D/A1 FIFO (16bit) */
-#define LAS1_DAC2_FIFO 0x000c /* D/A2 FIFO (16bit) */
+#define LAS1_DAC_FIFO(x) (0x0008 + ((x) * 0x4)) /* D/Ax FIFO (16bit) */
-/*======================================================================
- Driver specific stuff (tunable)
-======================================================================*/
+/*
+ * Driver specific stuff (tunable)
+ */
-/* We really only need 2 buffers. More than that means being much
- smarter about knowing which ones are full. */
+/*
+ * We really only need 2 buffers. More than that means being much
+ * smarter about knowing which ones are full.
+ */
#define DMA_CHAIN_COUNT 2 /* max DMA segments/buffers in a ring (min 2) */
/* Target period for periodic transfers. This sets the user read latency. */
/* The board support a channel list up to the FIFO length (1K or 8K) */
#define RTD_MAX_CHANLIST 128 /* max channel list that we allow */
-/*======================================================================
- Board specific stuff
-======================================================================*/
+/*
+ * Board specific stuff
+ */
#define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */
#define RTD_CLOCK_BASE 125 /* clock period in ns */
/* interrupt at end of block */ | PLX_INTR_TERM_COUNT \
/* from board to PCI */ | PLX_XFER_LOCAL_TO_PCI)
-/*======================================================================
- Comedi specific stuff
-======================================================================*/
+/*
+ * Comedi specific stuff
+ */
/*
* The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)
const struct comedi_lrange *ai_range;
};
-static const struct rtd_boardinfo rtd520Boards[] = {
+static const struct rtd_boardinfo rtd520_boards[] = {
[BOARD_DM7520] = {
.name = "DM7520",
.range_bip10 = 6,
int xfer_count; /* # to transfer data. 0->1/2FIFO */
int flags; /* flag event modes */
unsigned fifosz;
+
+ /* 8254 Timer/Counter gate and clock sources */
+ unsigned char timer_gate_src[3];
+ unsigned char timer_clk_src[3];
};
/* bit defines for "flags" */
#define DMA1_ACTIVE 0x04 /* DMA1 is active */
/*
- Given a desired period and the clock period (both in ns),
- return the proper counter value (divider-1).
- Sets the original period to be the true value.
- Note: you have to check if the value is larger than the counter range!
-*/
+ * Given a desired period and the clock period (both in ns), return the
+ * proper counter value (divider-1). Sets the original period to be the
+ * true value.
+ * Note: you have to check if the value is larger than the counter range!
+ */
static int rtd_ns_to_timer_base(unsigned int *nanosec,
unsigned int flags, int base)
{
switch (flags & CMDF_ROUND_MASK) {
case CMDF_ROUND_NEAREST:
default:
- divider = (*nanosec + base / 2) / base;
+ divider = DIV_ROUND_CLOSEST(*nanosec, base);
break;
case CMDF_ROUND_DOWN:
divider = (*nanosec) / base;
break;
case CMDF_ROUND_UP:
- divider = (*nanosec + base - 1) / base;
+ divider = DIV_ROUND_UP(*nanosec, base);
break;
}
if (divider < 2)
divider = 2; /* min is divide by 2 */
- /* Note: we don't check for max, because different timers
- have different ranges */
+ /*
+ * Note: we don't check for max, because different timers
+ * have different ranges
+ */
*nanosec = base * divider;
return divider - 1; /* countdown is divisor+1 */
}
/*
- Given a desired period (in ns),
- return the proper counter value (divider-1) for the internal clock.
- Sets the original period to be the true value.
-*/
+ * Given a desired period (in ns), return the proper counter value
+ * (divider-1) for the internal clock. Sets the original period to
+ * be the true value.
+ */
static int rtd_ns_to_timer(unsigned int *ns, unsigned int flags)
{
return rtd_ns_to_timer_base(ns, flags, RTD_CLOCK_BASE);
}
-/*
- Convert a single comedi channel-gain entry to a RTD520 table entry
-*/
+/* Convert a single comedi channel-gain entry to a RTD520 table entry */
static unsigned short rtd_convert_chan_gain(struct comedi_device *dev,
unsigned int chanspec, int index)
{
return r;
}
-/*
- Setup the channel-gain table from a comedi list
-*/
+/* Setup the channel-gain table from a comedi list */
static void rtd_load_channelgain_list(struct comedi_device *dev,
unsigned int n_chan, unsigned int *list)
{
}
}
-/* determine fifo size by doing adc conversions until the fifo half
-empty status flag clears */
+/*
+ * Determine fifo size by doing adc conversions until the fifo half
+ * empty status flag clears.
+ */
static int rtd520_probe_fifo_depth(struct comedi_device *dev)
{
unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
return n;
}
-/*
- Get what we know is there.... Fast!
- This uses 1/2 the bus cycles of read_dregs (below).
-
- The manual claims that we can do a lword read, but it doesn't work here.
-*/
static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
int count)
{
unsigned int range = CR_RANGE(cmd->chanlist[async->cur_chan]);
unsigned short d;
- if (0 == devpriv->ai_count) { /* done */
+ if (devpriv->ai_count == 0) { /* done */
d = readw(devpriv->las1 + LAS1_ADC_FIFO);
continue;
}
return 0;
}
-/*
- Handle all rtd520 interrupts.
- Runs atomically and is never re-entered.
- This is a "slow handler"; other interrupts may be active.
- The data conversion may someday happen in a "bottom half".
-*/
static irqreturn_t rtd_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
status = readw(dev->mmio + LAS0_IT);
/* if interrupt was not caused by our board, or handled above */
- if (0 == status)
+ if (status == 0)
return IRQ_HANDLED;
if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */
if (ai_read_n(dev, s, devpriv->fifosz / 2) < 0)
goto xfer_abort;
- if (0 == devpriv->ai_count)
+ if (devpriv->ai_count == 0)
goto xfer_done;
} else if (devpriv->xfer_count > 0) {
if (fifo_status & FS_ADC_NOT_EMPTY) {
if (ai_read_n(dev, s, devpriv->xfer_count) < 0)
goto xfer_abort;
- if (0 == devpriv->ai_count)
+ if (devpriv->ai_count == 0)
goto xfer_done;
}
}
return IRQ_HANDLED;
}
-/*
- cmdtest tests a particular command to see if it is valid.
- Using the cmdtest ioctl, a user can create a valid cmd
- and then have it executed by the cmd ioctl (asynchronously).
-
- cmdtest returns 1,2,3,4 or 0, depending on which tests
- the command passes.
-*/
-
static int rtd_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
if (cmd->scan_begin_src == TRIG_TIMER) {
/* Note: these are time periods, not actual rates */
- if (1 == cmd->chanlist_len) { /* no scanning */
+ if (cmd->chanlist_len == 1) { /* no scanning */
if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
RTD_MAX_SPEED_1)) {
rtd_ns_to_timer(&cmd->scan_begin_arg,
}
if (cmd->convert_src == TRIG_TIMER) {
- if (1 == cmd->chanlist_len) { /* no scanning */
+ if (cmd->chanlist_len == 1) { /* no scanning */
if (comedi_check_trigger_arg_min(&cmd->convert_arg,
RTD_MAX_SPEED_1)) {
rtd_ns_to_timer(&cmd->convert_arg,
return 0;
}
-/*
- Execute a analog in command with many possible triggering options.
- The data get stored in the async structure of the subdevice.
- This is usually done by an interrupt handler.
- Userland gets to the data using read calls.
-*/
static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct rtd_private *devpriv = dev->private;
}
writel((devpriv->fifosz / 2 - 1) & 0xffff, dev->mmio + LAS0_ACNT);
- if (TRIG_TIMER == cmd->scan_begin_src) {
+ if (cmd->scan_begin_src == TRIG_TIMER) {
/* scan_begin_arg is in nanoseconds */
/* find out how many samples to wait before transferring */
if (cmd->flags & CMDF_WAKE_EOS) {
}
/* end configuration */
- /* This doesn't seem to work. There is no way to clear an interrupt
- that the priority controller has queued! */
+ /*
+ * This doesn't seem to work. There is no way to clear an interrupt
+ * that the priority controller has queued!
+ */
writew(~0, dev->mmio + LAS0_CLEAR);
readw(dev->mmio + LAS0_CLEAR);
return 0;
}
-/*
- Stop a running data acquisition.
-*/
static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct rtd_private *devpriv = dev->private;
return -EBUSY;
}
-static int rtd_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+static int rtd_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct rtd_private *devpriv = dev->private;
- int i;
- int chan = CR_CHAN(insn->chanspec);
- int range = CR_RANGE(insn->chanspec);
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
int ret;
+ int i;
/* Configure the output range (table index matches the range values) */
- writew(range & 7,
- dev->mmio + ((chan == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL));
+ writew(range & 7, dev->mmio + LAS0_DAC_CTRL(chan));
- /* Writing a list of values to an AO channel is probably not
- * very useful, but that's how the interface is defined. */
for (i = 0; i < insn->n; ++i) {
- int val = data[i] << 3;
+ unsigned int val = data[i];
- /* VERIFY: comedi range and offset conversions */
-
- if ((range > 1) && (data[i] < 2048)) { /* bipolar */
- /* offset and sign extend */
- val = (((int)data[i]) - 2048) << 3;
- } else { /* unipolor */
- val = data[i] << 3;
+ /* bipolar uses 2's complement values with an extended sign */
+ if (comedi_range_is_bipolar(s, range)) {
+ val = comedi_offset_munge(s, val);
+ val |= (val & ((s->maxdata + 1) >> 1)) << 1;
}
- /* a typical programming sequence */
- writew(val, devpriv->las1 +
- ((chan == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO));
- writew(0, dev->mmio + ((chan == 0) ? LAS0_DAC1 : LAS0_DAC2));
+ /* shift the 12-bit data (+ sign) to match the register */
+ val <<= 3;
- s->readback[chan] = data[i];
+ writew(val, devpriv->las1 + LAS1_DAC_FIFO(chan));
+ writew(0, dev->mmio + LAS0_UPDATE_DAC(chan));
ret = comedi_timeout(dev, s, insn, rtd_ao_eoc, 0);
if (ret)
return ret;
+
+ s->readback[chan] = data[i];
}
- /* return the number of samples read/written */
- return i;
+ return insn->n;
}
static int rtd_dio_insn_bits(struct comedi_device *dev,
return insn->n;
}
+static int rtd_counter_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rtd_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int max_src;
+ unsigned int src;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_GATE_SRC:
+ /*
+ * 8254 Timer/Counter gate sources:
+ *
+ * 0 = Not gated, free running (reset state)
+ * 1 = Gated, off
+ * 2 = Ext. TC Gate 1
+ * 3 = Ext. TC Gate 2
+ * 4 = Previous TC out (chan 1 and 2 only)
+ */
+ src = data[2];
+ max_src = (chan == 0) ? 3 : 4;
+ if (src > max_src)
+ return -EINVAL;
+
+ devpriv->timer_gate_src[chan] = src;
+ writeb(src, dev->mmio + LAS0_8254_GATE_SEL(chan));
+ break;
+ case INSN_CONFIG_GET_GATE_SRC:
+ data[2] = devpriv->timer_gate_src[chan];
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ /*
+ * 8254 Timer/Counter clock sources:
+ *
+ * 0 = 8 MHz (reset state)
+ * 1 = Ext. TC Clock 1
+ * 2 = Ext. TX Clock 2
+ * 3 = Ext. Pacer Clock
+ * 4 = Previous TC out (chan 1 and 2 only)
+ * 5 = High-Speed Digital Input Sampling signal (chan 1 only)
+ */
+ src = data[1];
+ switch (chan) {
+ case 0:
+ max_src = 3;
+ break;
+ case 1:
+ max_src = 5;
+ break;
+ case 2:
+ max_src = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (src > max_src)
+ return -EINVAL;
+
+ devpriv->timer_clk_src[chan] = src;
+ writeb(src, dev->mmio + LAS0_8254_CLK_SEL(chan));
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ src = devpriv->timer_clk_src[chan];
+ data[1] = devpriv->timer_clk_src[chan];
+ data[2] = (src == 0) ? RTD_CLOCK_BASE : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
static void rtd_reset(struct comedi_device *dev)
{
struct rtd_private *devpriv = dev->private;
writel(0, dev->mmio + LAS0_OVERRUN);
writel(0, dev->mmio + LAS0_CGT_CLEAR);
writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
- writel(0, dev->mmio + LAS0_DAC1_RESET);
- writel(0, dev->mmio + LAS0_DAC2_RESET);
+ writel(0, dev->mmio + LAS0_DAC_RESET(0));
+ writel(0, dev->mmio + LAS0_DAC_RESET(1));
/* clear digital IO fifo */
writew(0, dev->mmio + LAS0_DIO_STATUS);
- writeb((0 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
- writeb((1 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
- writeb((2 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
- writeb((3 << 6) | 0x00, dev->mmio + LAS0_UTC_CTRL);
/* TODO: set user out source ??? */
}
struct comedi_subdevice *s;
int ret;
- if (context < ARRAY_SIZE(rtd520Boards))
- board = &rtd520Boards[context];
+ if (context < ARRAY_SIZE(rtd520_boards))
+ board = &rtd520_boards[context];
if (!board)
return -ENODEV;
dev->board_ptr = board;
s->n_chan = 2;
s->maxdata = 0x0fff;
s->range_table = &rtd_ao_range;
- s->insn_write = rtd_ao_winsn;
+ s->insn_write = rtd_ao_insn_write;
ret = comedi_alloc_subdev_readback(s);
if (ret)
s->insn_bits = rtd_dio_insn_bits;
s->insn_config = rtd_dio_insn_config;
- /* timer/counter subdevices (not currently supported) */
+ /* 8254 Timer/Counter subdevice */
s = &dev->subdevices[3];
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 3;
- s->maxdata = 0xffff;
+ dev->pacer = comedi_8254_mm_init(dev->mmio + LAS0_8254_TIMER_BASE,
+ RTD_CLOCK_BASE, I8254_IO8, 2);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ comedi_8254_subdevice_init(s, dev->pacer);
+ dev->pacer->insn_config = rtd_counter_insn_config;
rtd_init_board(dev);