cregit-Linux how code gets into the kernel

Release 4.7 drivers/staging/comedi/drivers/adl_pci9118.c

/*
 *  comedi/drivers/adl_pci9118.c
 *
 *  hardware driver for ADLink cards:
 *   card:   PCI-9118DG, PCI-9118HG, PCI-9118HR
 *   driver: pci9118dg,  pci9118hg,  pci9118hr
 *
 * Author: Michal Dobes <dobes@tesnet.cz>
 *
 */

/*
 * Driver: adl_pci9118
 * Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR
 * Author: Michal Dobes <dobes@tesnet.cz>
 * Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg),
 * PCI-9118HR (pci9118hr)
 * Status: works
 *
 * This driver supports AI, AO, DI and DO subdevices.
 * AI subdevice supports cmd and insn interface,
 * other subdevices support only insn interface.
 * For AI:
 * - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46).
 * - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44).
 * - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46).
 * - It is not necessary to have cmd.scan_end_arg=cmd.chanlist_len but
 * cmd.scan_end_arg modulo cmd.chanlist_len must by 0.
 * - If return value of cmdtest is 5 then you've bad channel list
 * (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar
 * ranges).
 *
 * There are some hardware limitations:
 * a) You cann't use mixture of unipolar/bipoar ranges or differencial/single
 *  ended inputs.
 * b) DMA transfers must have the length aligned to two samples (32 bit),
 *  so there is some problems if cmd->chanlist_len is odd. This driver tries
 *  bypass this with adding one sample to the end of the every scan and discard
 *  it on output but this can't be used if cmd->scan_begin_src=TRIG_FOLLOW
 *  and is used flag CMDF_WAKE_EOS, then driver switch to interrupt driven mode
 *  with interrupt after every sample.
 * c) If isn't used DMA then you can use only mode where
 *  cmd->scan_begin_src=TRIG_FOLLOW.
 *
 * Configuration options:
 * [0] - PCI bus of device (optional)
 * [1] - PCI slot of device (optional)
 *       If bus/slot is not specified, then first available PCI
 *       card will be used.
 * [2] - 0= standard 8 DIFF/16 SE channels configuration
 *       n = external multiplexer connected, 1 <= n <= 256
 * [3] - ignored
 * [4] - sample&hold signal - card can generate signal for external S&H board
 *       0 = use SSHO(pin 45) signal is generated in onboard hardware S&H logic
 *       0 != use ADCHN7(pin 23) signal is generated from driver, number say how
 *              long delay is requested in ns and sign polarity of the hold
 *              (in this case external multiplexor can serve only 128 channels)
 * [5] - ignored
 */

/*
 * FIXME
 *
 * All the supported boards have the same PCI vendor and device IDs, so
 * auto-attachment of PCI devices will always find the first board type.
 *
 * Perhaps the boards have different subdevice IDs that we could use to
 * distinguish them?
 *
 * Need some device attributes so the board type can be corrected after
 * attachment if necessary, and possibly to set other options supported by
 * manual attachment.
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/io.h>

#include "../comedi_pci.h"

#include "amcc_s5933.h"
#include "comedi_8254.h"

/*
 * PCI BAR2 Register map (dev->iobase)
 */

#define PCI9118_TIMER_BASE		0x00

#define PCI9118_AI_FIFO_REG		0x10

#define PCI9118_AO_REG(x)		(0x10 + ((x) * 4))

#define PCI9118_AI_STATUS_REG		0x18

#define PCI9118_AI_STATUS_NFULL		BIT(8)	
/* 0=FIFO full (fatal) */

#define PCI9118_AI_STATUS_NHFULL	BIT(7)	
/* 0=FIFO half full */

#define PCI9118_AI_STATUS_NEPTY		BIT(6)	
/* 0=FIFO empty */

#define PCI9118_AI_STATUS_ACMP		BIT(5)	
/* 1=about trigger complete */

#define PCI9118_AI_STATUS_DTH		BIT(4)	
/* 1=ext. digital trigger */

#define PCI9118_AI_STATUS_BOVER		BIT(3)	
/* 1=burst overrun (fatal) */

#define PCI9118_AI_STATUS_ADOS		BIT(2)	
/* 1=A/D over speed (warn) */

#define PCI9118_AI_STATUS_ADOR		BIT(1)	
/* 1=A/D overrun (fatal) */

#define PCI9118_AI_STATUS_ADRDY		BIT(0)	
/* 1=A/D ready */

#define PCI9118_AI_CTRL_REG		0x18

#define PCI9118_AI_CTRL_UNIP		BIT(7)	
/* 1=unipolar */

#define PCI9118_AI_CTRL_DIFF		BIT(6)	
/* 1=differential inputs */

#define PCI9118_AI_CTRL_SOFTG		BIT(5)	
/* 1=8254 software gate */

#define PCI9118_AI_CTRL_EXTG		BIT(4)	
/* 1=8254 TGIN(pin 46) gate */

#define PCI9118_AI_CTRL_EXTM		BIT(3)	
/* 1=ext. trigger (pin 44) */

#define PCI9118_AI_CTRL_TMRTR		BIT(2)	
/* 1=8254 is trigger source */

#define PCI9118_AI_CTRL_INT		BIT(1)	
/* 1=enable interrupt */

#define PCI9118_AI_CTRL_DMA		BIT(0)	
/* 1=enable DMA */

#define PCI9118_DIO_REG			0x1c

#define PCI9118_SOFTTRG_REG		0x20

#define PCI9118_AI_CHANLIST_REG		0x24

#define PCI9118_AI_CHANLIST_RANGE(x)	(((x) & 0x3) << 8)

#define PCI9118_AI_CHANLIST_CHAN(x)	((x) << 0)

#define PCI9118_AI_BURST_NUM_REG	0x28

#define PCI9118_AI_AUTOSCAN_MODE_REG	0x2c

#define PCI9118_AI_CFG_REG		0x30

#define PCI9118_AI_CFG_PDTRG		BIT(7)	
/* 1=positive trigger */

#define PCI9118_AI_CFG_PETRG		BIT(6)	
/* 1=positive ext. trigger */

#define PCI9118_AI_CFG_BSSH		BIT(5)	
/* 1=with sample & hold */

#define PCI9118_AI_CFG_BM		BIT(4)	
/* 1=burst mode */

#define PCI9118_AI_CFG_BS		BIT(3)	
/* 1=burst mode start */

#define PCI9118_AI_CFG_PM		BIT(2)	
/* 1=post trigger */

#define PCI9118_AI_CFG_AM		BIT(1)	
/* 1=about trigger */

#define PCI9118_AI_CFG_START		BIT(0)	
/* 1=trigger start */

#define PCI9118_FIFO_RESET_REG		0x34

#define PCI9118_INT_CTRL_REG		0x38

#define PCI9118_INT_CTRL_TIMER		BIT(3)	
/* timer interrupt */

#define PCI9118_INT_CTRL_ABOUT		BIT(2)	
/* about trigger complete */

#define PCI9118_INT_CTRL_HFULL		BIT(1)	
/* A/D FIFO half full */

#define PCI9118_INT_CTRL_DTRG		BIT(0)	
/* ext. digital trigger */


#define START_AI_EXT	0x01	
/* start measure on external trigger */

#define STOP_AI_EXT	0x02	
/* stop measure on external trigger */

#define STOP_AI_INT	0x08	
/* stop measure on internal trigger */


static const struct comedi_lrange pci9118_ai_range = {
	8, {
		BIP_RANGE(5),
		BIP_RANGE(2.5),
		BIP_RANGE(1.25),
		BIP_RANGE(0.625),
		UNI_RANGE(10),
		UNI_RANGE(5),
		UNI_RANGE(2.5),
		UNI_RANGE(1.25)
	}
};


static const struct comedi_lrange pci9118hg_ai_range = {
	8, {
		BIP_RANGE(5),
		BIP_RANGE(0.5),
		BIP_RANGE(0.05),
		BIP_RANGE(0.005),
		UNI_RANGE(10),
		UNI_RANGE(1),
		UNI_RANGE(0.1),
		UNI_RANGE(0.01)
	}
};


enum pci9118_boardid {
	
BOARD_PCI9118DG,
	
BOARD_PCI9118HG,
	
BOARD_PCI9118HR,
};


struct pci9118_boardinfo {
	
const char *name;
	
unsigned int ai_is_16bit:1;
	
unsigned int is_hg:1;
};


static const struct pci9118_boardinfo pci9118_boards[] = {
	[BOARD_PCI9118DG] = {
		.name		= "pci9118dg",
        },
	[BOARD_PCI9118HG] = {
		.name		= "pci9118hg",
		.is_hg		= 1,
        },
	[BOARD_PCI9118HR] = {
		.name		= "pci9118hr",
		.ai_is_16bit	= 1,
        },
};


struct pci9118_dmabuf {
	
unsigned short *virt;	/* virtual address of buffer */
	
dma_addr_t hw;		/* hardware (bus) address of buffer */
	
unsigned int size;	/* size of dma buffer in bytes */
	
unsigned int use_size;	/* which size we may now use for transfer */
};


struct pci9118_private {
	
unsigned long iobase_a;	/* base+size for AMCC chip */
	
unsigned int master:1;
	
unsigned int dma_doublebuf:1;
	
unsigned int ai_neverending:1;
	
unsigned int usedma:1;
	
unsigned int usemux:1;
	
unsigned char ai_ctrl;
	
unsigned char int_ctrl;
	
unsigned char ai_cfg;
	
unsigned int ai_do;		/* what do AI? 0=nothing, 1 to 4 mode */
	
unsigned int ai_n_realscanlen;	/*
                                         * what we must transfer for one
                                         * outgoing scan include front/back adds
                                         */
	
unsigned int ai_act_dmapos;	/* position in actual real stream */
	
unsigned int ai_add_front;	/*
                                         * how many channels we must add
                                         * before scan to satisfy S&H?
                                         */
	
unsigned int ai_add_back;	/*
                                         * how many channels we must add
                                         * before scan to satisfy DMA?
                                         */
	
unsigned int ai_flags;
	
char ai12_startstop;		/*
                                         * measure can start/stop
                                         * on external trigger
                                         */
	
unsigned int dma_actbuf;		/* which buffer is used now */
	
struct pci9118_dmabuf dmabuf[2];
	
int softsshdelay;		/*
                                         * >0 use software S&H,
                                         * numer is requested delay in ns
                                         */
	
unsigned char softsshsample;	/*
                                         * polarity of S&H signal
                                         * in sample state
                                         */
	
unsigned char softsshhold;	/*
                                         * polarity of S&H signal
                                         * in hold state
                                         */
	
unsigned int ai_ns_min;
};


static void pci9118_amcc_setup_dma(struct comedi_device *dev, unsigned int buf) { struct pci9118_private *devpriv = dev->private; struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[buf]; /* set the master write address and transfer count */ outl(dmabuf->hw, devpriv->iobase_a + AMCC_OP_REG_MWAR); outl(dmabuf->use_size, devpriv->iobase_a + AMCC_OP_REG_MWTC); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten64100.00%2100.00%
Total64100.00%2100.00%


static void pci9118_amcc_dma_ena(struct comedi_device *dev, bool enable) { struct pci9118_private *devpriv = dev->private; unsigned int mcsr; mcsr = inl(devpriv->iobase_a + AMCC_OP_REG_MCSR); if (enable) mcsr |= RESET_A2P_FLAGS | A2P_HI_PRIORITY | EN_A2P_TRANSFERS; else mcsr &= ~EN_A2P_TRANSFERS; outl(mcsr, devpriv->iobase_a + AMCC_OP_REG_MCSR); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten67100.00%1100.00%
Total67100.00%1100.00%


static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable) { struct pci9118_private *devpriv = dev->private; unsigned int intcsr; /* enable/disable interrupt for AMCC Incoming Mailbox 4 (32-bit) */ intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); if (enable) intcsr |= 0x1f00; else intcsr &= ~0x1f00; outl(intcsr, devpriv->iobase_a + AMCC_OP_REG_INTCSR); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten64100.00%1100.00%
Total64100.00%1100.00%


static void pci9118_ai_reset_fifo(struct comedi_device *dev) { /* writing any value resets the A/D FIFO */ outl(0, dev->iobase + PCI9118_FIFO_RESET_REG); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten23100.00%1100.00%
Total23100.00%1100.00%


static int pci9118_ai_check_chanlist(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { struct pci9118_private *devpriv = dev->private; unsigned int range0 = CR_RANGE(cmd->chanlist[0]); unsigned int aref0 = CR_AREF(cmd->chanlist[0]); int i; /* single channel scans are always ok */ if (cmd->chanlist_len == 1) return 0; for (i = 1; i < cmd->chanlist_len; i++) { unsigned int chan = CR_CHAN(cmd->chanlist[i]); unsigned int range = CR_RANGE(cmd->chanlist[i]); unsigned int aref = CR_AREF(cmd->chanlist[i]); if (aref != aref0) { dev_err(dev->class_dev, "Differential and single ended inputs can't be mixed!\n"); return -EINVAL; } if (comedi_range_is_bipolar(s, range) != comedi_range_is_bipolar(s, range0)) { dev_err(dev->class_dev, "Bipolar and unipolar ranges can't be mixed!\n"); return -EINVAL; } if (!devpriv->usemux && aref == AREF_DIFF && (chan >= (s->n_chan / 2))) { dev_err(dev->class_dev, "AREF_DIFF is only available for the first 8 channels!\n"); return -EINVAL; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten21093.33%457.14%
michal dobesmichal dobes135.78%114.29%
bill pembertonbill pemberton20.89%228.57%
Total225100.00%7100.00%


static void pci9118_set_chanlist(struct comedi_device *dev, struct comedi_subdevice *s, int n_chan, unsigned int *chanlist, int frontadd, int backadd) { struct pci9118_private *devpriv = dev->private; unsigned int chan0 = CR_CHAN(chanlist[0]); unsigned int range0 = CR_RANGE(chanlist[0]); unsigned int aref0 = CR_AREF(chanlist[0]); unsigned int ssh = 0x00; unsigned int val; int i; /* * Configure analog input based on the first chanlist entry. * All entries are either unipolar or bipolar and single-ended * or differential. */ devpriv->ai_ctrl = 0; if (comedi_range_is_unipolar(s, range0)) devpriv->ai_ctrl |= PCI9118_AI_CTRL_UNIP; if (aref0 == AREF_DIFF) devpriv->ai_ctrl |= PCI9118_AI_CTRL_DIFF; outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); /* gods know why this sequence! */ outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); /* insert channels for S&H */ if (frontadd) { val = PCI9118_AI_CHANLIST_CHAN(chan0) | PCI9118_AI_CHANLIST_RANGE(range0); ssh = devpriv->softsshsample; for (i = 0; i < frontadd; i++) { outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); ssh = devpriv->softsshhold; } } /* store chanlist */ for (i = 0; i < n_chan; i++) { unsigned int chan = CR_CHAN(chanlist[i]); unsigned int range = CR_RANGE(chanlist[i]); val = PCI9118_AI_CHANLIST_CHAN(chan) | PCI9118_AI_CHANLIST_RANGE(range); outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); } /* insert channels to fit onto 32bit DMA */ if (backadd) { val = PCI9118_AI_CHANLIST_CHAN(chan0) | PCI9118_AI_CHANLIST_RANGE(range0); for (i = 0; i < backadd; i++) outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); } /* close scan queue */ outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); /* udelay(100); important delay, or first sample will be crippled */ }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten24469.52%975.00%
michal dobesmichal dobes10529.91%18.33%
bill pembertonbill pemberton20.57%216.67%
Total351100.00%12100.00%


static void pci9118_ai_mode4_switch(struct comedi_device *dev, unsigned int next_buf) { struct pci9118_private *devpriv = dev->private; struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf]; devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG | PCI9118_AI_CFG_AM; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); comedi_8254_load(dev->pacer, 0, dmabuf->hw >> 1, I8254_MODE0 | I8254_BINARY); devpriv->ai_cfg |= PCI9118_AI_CFG_START; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten98100.00%7100.00%
Total98100.00%7100.00%


static unsigned int pci9118_ai_samples_ready(struct comedi_device *dev, struct comedi_subdevice *s, unsigned int n_raw_samples) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; unsigned int start_pos = devpriv->ai_add_front; unsigned int stop_pos = start_pos + cmd->chanlist_len; unsigned int span_len = stop_pos + devpriv->ai_add_back; unsigned int dma_pos = devpriv->ai_act_dmapos; unsigned int whole_spans, n_samples, x; if (span_len == cmd->chanlist_len) return n_raw_samples; /* use all samples */ /* * Not all samples are to be used. Buffer contents consist of a * possibly non-whole number of spans and a region of each span * is to be used. * * Account for samples in whole number of spans. */ whole_spans = n_raw_samples / span_len; n_samples = whole_spans * cmd->chanlist_len; n_raw_samples -= whole_spans * span_len; /* * Deal with remaining samples which could overlap up to two spans. */ while (n_raw_samples) { if (dma_pos < start_pos) { /* Skip samples before start position. */ x = start_pos - dma_pos; if (x > n_raw_samples) x = n_raw_samples; dma_pos += x; n_raw_samples -= x; if (!n_raw_samples) break; } if (dma_pos < stop_pos) { /* Include samples before stop position. */ x = stop_pos - dma_pos; if (x > n_raw_samples) x = n_raw_samples; n_samples += x; dma_pos += x; n_raw_samples -= x; } /* Advance to next span. */ start_pos += span_len; stop_pos += span_len; } return n_samples; }

Contributors

PersonTokensPropCommitsCommitProp
ian abbottian abbott21399.53%150.00%
h hartley sweetenh hartley sweeten10.47%150.00%
Total214100.00%2100.00%


static void pci9118_ai_dma_xfer(struct comedi_device *dev, struct comedi_subdevice *s, unsigned short *dma_buffer, unsigned int n_raw_samples) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; unsigned int start_pos = devpriv->ai_add_front; unsigned int stop_pos = start_pos + cmd->chanlist_len; unsigned int span_len = stop_pos + devpriv->ai_add_back; unsigned int dma_pos = devpriv->ai_act_dmapos; unsigned int x; if (span_len == cmd->chanlist_len) { /* All samples are to be copied. */ comedi_buf_write_samples(s, dma_buffer, n_raw_samples); dma_pos += n_raw_samples; } else { /* * Not all samples are to be copied. Buffer contents consist * of a possibly non-whole number of spans and a region of * each span is to be copied. */ while (n_raw_samples) { if (dma_pos < start_pos) { /* Skip samples before start position. */ x = start_pos - dma_pos; if (x > n_raw_samples) x = n_raw_samples; dma_pos += x; n_raw_samples -= x; if (!n_raw_samples) break; } if (dma_pos < stop_pos) { /* Copy samples before stop position. */ x = stop_pos - dma_pos; if (x > n_raw_samples) x = n_raw_samples; comedi_buf_write_samples(s, dma_buffer, x); dma_pos += x; n_raw_samples -= x; } /* Advance to next span. */ start_pos += span_len; stop_pos += span_len; } } /* Update position in span for next time. */ devpriv->ai_act_dmapos = dma_pos % span_len; }

Contributors

PersonTokensPropCommitsCommitProp
ian abbottian abbott13662.10%112.50%
h hartley sweetenh hartley sweeten7132.42%562.50%
michal dobesmichal dobes115.02%112.50%
bill pembertonbill pemberton10.46%112.50%
Total219100.00%8100.00%


static void pci9118_exttrg_enable(struct comedi_device *dev, bool enable) { struct pci9118_private *devpriv = dev->private; if (enable) devpriv->int_ctrl |= PCI9118_INT_CTRL_DTRG; else devpriv->int_ctrl &= ~PCI9118_INT_CTRL_DTRG; outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); if (devpriv->int_ctrl) pci9118_amcc_int_ena(dev, true); else pci9118_amcc_int_ena(dev, false); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten5168.00%880.00%
michal dobesmichal dobes2330.67%110.00%
bill pembertonbill pemberton11.33%110.00%
Total75100.00%10100.00%


static void pci9118_calc_divisors(struct comedi_device *dev, struct comedi_subdevice *s, unsigned int *tim1, unsigned int *tim2, unsigned int flags, int chans, unsigned int *div1, unsigned int *div2, unsigned int chnsshfront) { struct comedi_8254 *pacer = dev->pacer; struct comedi_cmd *cmd = &s->async->cmd; *div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */ *div2 = *tim1 / pacer->osc_base; /* scan timer */ *div2 = *div2 / *div1; /* major timer is c1*c2 */ if (*div2 < chans) *div2 = chans; *tim2 = *div1 * pacer->osc_base; /* real convert timer */ if (cmd->convert_src == TRIG_NOW && !chnsshfront) { /* use BSSH signal */ if (*div2 < (chans + 2)) *div2 = chans + 2; } *tim1 = *div1 * *div2 * pacer->osc_base; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten14888.10%360.00%
michal dobesmichal dobes1911.31%120.00%
peter huewepeter huewe10.60%120.00%
Total168100.00%5100.00%


static void pci9118_start_pacer(struct comedi_device *dev, int mode) { if (mode == 1 || mode == 2 || mode == 4) comedi_8254_pacer_enable(dev->pacer, 1, 2, true); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten3790.24%480.00%
michal dobesmichal dobes49.76%120.00%
Total41100.00%5100.00%


static int pci9118_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; if (devpriv->usedma) pci9118_amcc_dma_ena(dev, false); pci9118_exttrg_enable(dev, false); comedi_8254_pacer_enable(dev->pacer, 1, 2, false); /* set default config (disable burst and triggers) */ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); /* reset acqusition control */ devpriv->ai_ctrl = 0; outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG); /* reset scan queue */ outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); pci9118_ai_reset_fifo(dev); devpriv->int_ctrl = 0; outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); pci9118_amcc_int_ena(dev, false); devpriv->ai_do = 0; devpriv->usedma = 0; devpriv->ai_act_dmapos = 0; s->async->inttrig = NULL; devpriv->ai_neverending = 0; devpriv->dma_actbuf = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten13465.05%1583.33%
michal dobesmichal dobes7033.98%15.56%
bill pembertonbill pemberton20.97%211.11%
Total206100.00%18100.00%


static void pci9118_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s, void *data, unsigned int num_bytes, unsigned int start_chan_index) { struct pci9118_private *devpriv = dev->private; unsigned short *array = data; unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes); unsigned int i; __be16 *barray = data; for (i = 0; i < num_samples; i++) { if (devpriv->usedma) array[i] = be16_to_cpu(barray[i]); if (s->maxdata == 0xffff) array[i] ^= 0x8000; else array[i] = (array[i] >> 4) & 0x0fff; } }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten10882.44%350.00%
michal dobesmichal dobes1511.45%116.67%
ksenija stanojevicksenija stanojevic75.34%116.67%
ian abbottian abbott10.76%116.67%
Total131100.00%6100.00%


static void pci9118_ai_get_onesample(struct comedi_device *dev, struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; unsigned short sampl; sampl = inl(dev->iobase + PCI9118_AI_FIFO_REG); comedi_buf_write_samples(s, &sampl, 1); if (!devpriv->ai_neverending) { if (s->async->scans_done >= cmd->stop_arg) s->async->events |= COMEDI_CB_EOA; } }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten7481.32%981.82%
michal dobesmichal dobes1617.58%19.09%
ian abbottian abbott11.10%19.09%
Total91100.00%11100.00%


static void pci9118_ai_get_dma(struct comedi_device *dev, struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[devpriv->dma_actbuf]; unsigned int n_all = comedi_bytes_to_samples(s, dmabuf->use_size); unsigned int n_valid; bool more_dma; /* determine whether more DMA buffers to do after this one */ n_valid = pci9118_ai_samples_ready(dev, s, n_all); more_dma = n_valid < comedi_nsamples_left(s, n_valid + 1); /* switch DMA buffers and restart DMA if double buffering */ if (more_dma && devpriv->dma_doublebuf) { devpriv->dma_actbuf = 1 - devpriv->dma_actbuf; pci9118_amcc_setup_dma(dev, devpriv->dma_actbuf); if (devpriv->ai_do == 4) pci9118_ai_mode4_switch(dev, devpriv->dma_actbuf); } if (n_all) pci9118_ai_dma_xfer(dev, s, dmabuf->virt, n_all); if (!devpriv->ai_neverending) { if (s->async->scans_done >= cmd->stop_arg) s->async->events |= COMEDI_CB_EOA; } if (s->async->events & COMEDI_CB_CANCEL_MASK) more_dma = false; /* restart DMA if not double buffering */ if (more_dma && !devpriv->dma_doublebuf) { pci9118_amcc_setup_dma(dev, 0); if (devpriv->ai_do == 4) pci9118_ai_mode4_switch(dev, 0); } }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten13355.88%1381.25%
ian abbottian abbott6426.89%212.50%
michal dobesmichal dobes4117.23%16.25%
Total238100.00%16100.00%


static irqreturn_t pci9118_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; struct pci9118_private *devpriv = dev->private; unsigned int intsrc; /* IRQ reasons from card */ unsigned int intcsr; /* INT register from AMCC chip */ unsigned int adstat; /* STATUS register */ if (!dev->attached) return IRQ_NONE; intsrc = inl(dev->iobase + PCI9118_INT_CTRL_REG) & 0xf; intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); if (!intsrc && !(intcsr & ANY_S593X_INT)) return IRQ_NONE; outl(intcsr | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); if (intcsr & MASTER_ABORT_INT) { dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n"); s->async->events |= COMEDI_CB_ERROR; goto interrupt_exit; } if (intcsr & TARGET_ABORT_INT) { dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n"); s->async->events |= COMEDI_CB_ERROR; goto interrupt_exit; } adstat = inl(dev->iobase + PCI9118_AI_STATUS_REG); if ((adstat & PCI9118_AI_STATUS_NFULL) == 0) { dev_err(dev->class_dev, "A/D FIFO Full status (Fatal Error!)\n"); s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; goto interrupt_exit; } if (adstat & PCI9118_AI_STATUS_BOVER) { dev_err(dev->class_dev, "A/D Burst Mode Overrun Status (Fatal Error!)\n"); s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; goto interrupt_exit; } if (adstat & PCI9118_AI_STATUS_ADOS) { dev_err(dev->class_dev, "A/D Over Speed Status (Warning!)\n"); s->async->events |= COMEDI_CB_ERROR; goto interrupt_exit; } if (adstat & PCI9118_AI_STATUS_ADOR) { dev_err(dev->class_dev, "A/D Overrun Status (Fatal Error!)\n"); s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; goto interrupt_exit; } if (!devpriv->ai_do) return IRQ_HANDLED; if (devpriv->ai12_startstop) { if ((adstat & PCI9118_AI_STATUS_DTH) && (intsrc & PCI9118_INT_CTRL_DTRG)) { /* start/stop of measure */ if (devpriv->ai12_startstop & START_AI_EXT) { /* deactivate EXT trigger */ devpriv->ai12_startstop &= ~START_AI_EXT; if (!(devpriv->ai12_startstop & STOP_AI_EXT)) pci9118_exttrg_enable(dev, false); /* start pacer */ pci9118_start_pacer(dev, devpriv->ai_do); outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); } else if (devpriv->ai12_startstop & STOP_AI_EXT) { /* deactivate EXT trigger */ devpriv->ai12_startstop &= ~STOP_AI_EXT; pci9118_exttrg_enable(dev, false); /* on next interrupt measure will stop */ devpriv->ai_neverending = 0; } } } if (devpriv->usedma) pci9118_ai_get_dma(dev, s); else pci9118_ai_get_onesample(dev, s); interrupt_exit: comedi_handle_events(dev, s); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten40386.85%1894.74%
michal dobesmichal dobes6113.15%15.26%
Total464100.00%19100.00%


static void pci9118_ai_cmd_start(struct comedi_device *dev) { struct pci9118_private *devpriv = dev->private; outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); if (devpriv->ai_do != 3) { pci9118_start_pacer(dev, devpriv->ai_do); devpriv->ai_ctrl |= PCI9118_AI_CTRL_SOFTG; } outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten5261.90%1184.62%
michal dobesmichal dobes3136.90%17.69%
bill pembertonbill pemberton11.19%17.69%
Total84100.00%13100.00%


static int pci9118_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s, unsigned int trig_num) { struct comedi_cmd *cmd = &s->async->cmd; if (trig_num != cmd->start_arg) return -EINVAL; s->async->inttrig = NULL; pci9118_ai_cmd_start(dev); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten5998.33%266.67%
michal dobesmichal dobes11.67%133.33%
Total60100.00%3100.00%


static int pci9118_ai_setup_dma(struct comedi_device *dev, struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; unsigned int dmalen0 = dmabuf0->size; unsigned int dmalen1 = dmabuf1->size; unsigned int scan_bytes = devpriv->ai_n_realscanlen * comedi_bytes_per_sample(s); /* isn't output buff smaller that our DMA buff? */ if (dmalen0 > s->async->prealloc_bufsz) { /* align to 32bit down */ dmalen0 = s->async->prealloc_bufsz & ~3L; } if (dmalen1 > s->async->prealloc_bufsz) { /* align to 32bit down */ dmalen1 = s->async->prealloc_bufsz & ~3L; } /* we want wake up every scan? */ if (devpriv->ai_flags & CMDF_WAKE_EOS) { if (dmalen0 < scan_bytes) { /* uff, too short DMA buffer, disable EOS support! */ devpriv->ai_flags &= (~CMDF_WAKE_EOS); dev_info(dev->class_dev, "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", dmalen0, scan_bytes); } else { /* short first DMA buffer to one scan */ dmalen0 = scan_bytes; if (dmalen0 < 4) { dev_info(dev->class_dev, "ERR: DMA0 buf len bug? (%d<4)\n", dmalen0); dmalen0 = 4; } } } if (devpriv->ai_flags & CMDF_WAKE_EOS) { if (dmalen1 < scan_bytes) { /* uff, too short DMA buffer, disable EOS support! */ devpriv->ai_flags &= (~CMDF_WAKE_EOS); dev_info(dev->class_dev, "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", dmalen1, scan_bytes); } else { /* short second DMA buffer to one scan */ dmalen1 = scan_bytes; if (dmalen1 < 4) { dev_info(dev->class_dev, "ERR: DMA1 buf len bug? (%d<4)\n", dmalen1); dmalen1 = 4; } } } /* transfer without CMDF_WAKE_EOS */ if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) { unsigned int tmp; /* if it's possible then align DMA buffers to length of scan */ tmp = dmalen0; dmalen0 = (dmalen0 / scan_bytes) * scan_bytes; dmalen0 &= ~3L; if (!dmalen0) dmalen0 = tmp; /* uff. very long scan? */ tmp = dmalen1; dmalen1 = (dmalen1 / scan_bytes) * scan_bytes; dmalen1 &= ~3L; if (!dmalen1) dmalen1 = tmp; /* uff. very long scan? */ /* * if measure isn't neverending then test, if it fits whole * into one or two DMA buffers */ if (!devpriv->ai_neverending) { unsigned long long scanlen; scanlen = (unsigned long long)scan_bytes * cmd->stop_arg; /* fits whole measure into one DMA buffer? */ if (dmalen0 > scanlen) { dmalen0 = scanlen; dmalen0 &= ~3L; } else { /* fits whole measure into two DMA buffer? */ if (dmalen1 > (scanlen - dmalen0)) { dmalen1 = scanlen - dmalen0; dmalen1 &= ~3L; } } } } /* these DMA buffer size will be used */ devpriv->dma_actbuf = 0; dmabuf0->use_size = dmalen0; dmabuf1->use_size = dmalen1; pci9118_amcc_dma_ena(dev, false); pci9118_amcc_setup_dma(dev, 0); /* init DMA transfer */ outl(0x00000000 | AINT_WRITE_COMPL, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */ pci9118_amcc_dma_ena(dev, true); outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow bus mastering */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten46890.87%888.89%
michal dobesmichal dobes479.13%111.11%
Total515100.00%9100.00%


static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_8254 *pacer = dev->pacer; struct comedi_cmd *cmd = &s->async->cmd; unsigned int addchans = 0; unsigned int scanlen; devpriv->ai12_startstop = 0; devpriv->ai_flags = cmd->flags; devpriv->ai_add_front = 0; devpriv->ai_add_back = 0; /* prepare for start/stop conditions */ if (cmd->start_src == TRIG_EXT) devpriv->ai12_startstop |= START_AI_EXT; if (cmd->stop_src == TRIG_EXT) { devpriv->ai_neverending = 1; devpriv->ai12_startstop |= STOP_AI_EXT; } if (cmd->stop_src == TRIG_NONE) devpriv->ai_neverending = 1; if (cmd->stop_src == TRIG_COUNT) devpriv->ai_neverending = 0; /* * use additional sample at end of every scan * to satisty DMA 32 bit transfer? */ devpriv->ai_add_front = 0; devpriv->ai_add_back = 0; if (devpriv->master) { devpriv->usedma = 1; if ((cmd->flags & CMDF_WAKE_EOS) && (cmd->scan_end_arg == 1)) { if (cmd->convert_src == TRIG_NOW) devpriv->ai_add_back = 1; if (cmd->convert_src == TRIG_TIMER) { devpriv->usedma = 0; /* * use INT transfer if scanlist * have only one channel */ } } if ((cmd->flags & CMDF_WAKE_EOS) && (cmd->scan_end_arg & 1) && (cmd->scan_end_arg > 1)) { if (cmd->scan_begin_src == TRIG_FOLLOW) { devpriv->usedma = 0; /* * XXX maybe can be corrected to use 16 bit DMA */ } else { /* * well, we must insert one sample * to end of EOS to meet 32 bit transfer */ devpriv->ai_add_back = 1; } } } else { /* interrupt transfer don't need any correction */ devpriv->usedma = 0; } /* * we need software S&H signal? * It adds two samples before every scan as minimum */ if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) { devpriv->ai_add_front = 2; if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { /* move it to front */ devpriv->ai_add_front++; devpriv->ai_add_back = 0; } if (cmd->convert_arg < devpriv->ai_ns_min) cmd->convert_arg = devpriv->ai_ns_min; addchans = devpriv->softsshdelay / cmd->convert_arg; if (devpriv->softsshdelay % cmd->convert_arg) addchans++; if (addchans > (devpriv->ai_add_front - 1)) { /* uff, still short */ devpriv->ai_add_front = addchans + 1; if (devpriv->usedma == 1) if ((devpriv->ai_add_front + cmd->chanlist_len + devpriv->ai_add_back) & 1) devpriv->ai_add_front++; /* round up to 32 bit */ } } /* well, we now know what must be all added */ scanlen = devpriv->ai_add_front + cmd->chanlist_len + devpriv->ai_add_back; /* * what we must take from card in real to have cmd->scan_end_arg * on output? */ devpriv->ai_n_realscanlen = scanlen * (cmd->scan_end_arg / cmd->chanlist_len); if (scanlen > s->len_chanlist) { dev_err(dev->class_dev, "range/channel list is too long for actual configuration!\n"); return -EINVAL; } /* * Configure analog input and load the chanlist. * The acqusition control bits are enabled later. */ pci9118_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist, devpriv->ai_add_front, devpriv->ai_add_back); /* Determine acqusition mode and calculate timing */ devpriv->ai_do = 0; if (cmd->scan_begin_src != TRIG_TIMER && cmd->convert_src == TRIG_TIMER) { /* cascaded timers 1 and 2 are used for convert timing */ if (cmd->scan_begin_src == TRIG_EXT) devpriv->ai_do = 4; else devpriv->ai_do = 1; comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg, devpriv->ai_flags & CMDF_ROUND_NEAREST); comedi_8254_update_divisors(pacer); devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; if (!devpriv->usedma) { devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT; devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER; } if (cmd->scan_begin_src == TRIG_EXT) { struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0]; devpriv->ai_cfg |= PCI9118_AI_CFG_AM; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); comedi_8254_load(pacer, 0, dmabuf->hw >> 1, I8254_MODE0 | I8254_BINARY); devpriv->ai_cfg |= PCI9118_AI_CFG_START; } } if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src != TRIG_EXT) { if (!devpriv->usedma) { dev_err(dev->class_dev, "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n"); return -EIO; } /* double timed action */ devpriv->ai_do = 2; pci9118_calc_divisors(dev, s, &cmd->scan_begin_arg, &cmd->convert_arg, devpriv->ai_flags, devpriv->ai_n_realscanlen, &pacer->divisor1, &pacer->divisor2, devpriv->ai_add_front); devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS; if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay) devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH; outl(devpriv->ai_n_realscanlen, dev->iobase + PCI9118_AI_BURST_NUM_REG); } if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_EXT) { /* external trigger conversion */ devpriv->ai_do = 3; devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM; } if (devpriv->ai_do == 0) { dev_err(dev->class_dev, "Unable to determine acqusition mode! BUG in (*do_cmdtest)?\n"); return -EINVAL; } if (devpriv->usedma) devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA; /* set default config (disable burst and triggers) */ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); udelay(1); pci9118_ai_reset_fifo(dev); /* clear A/D and INT status registers */ inl(dev->iobase + PCI9118_AI_STATUS_REG); inl(dev->iobase + PCI9118_INT_CTRL_REG); devpriv->ai_act_dmapos = 0; if (devpriv->usedma) { pci9118_ai_setup_dma(dev, s); outl(0x02000000 | AINT_WRITE_COMPL, devpriv->iobase_a + AMCC_OP_REG_INTCSR); } else { pci9118_amcc_int_ena(dev, true); } /* start async command now or wait for internal trigger */ if (cmd->start_src == TRIG_NOW) pci9118_ai_cmd_start(dev); else if (cmd->start_src == TRIG_INT) s->async->inttrig = pci9118_ai_inttrig; /* enable external trigger for command start/stop */ if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT) pci9118_exttrg_enable(dev, true); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten88987.07%1386.67%
michal dobesmichal dobes12812.54%16.67%
ian abbottian abbott40.39%16.67%
Total1021100.00%15100.00%


static int pci9118_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { struct pci9118_private *devpriv = dev->private; int err = 0; unsigned int flags; unsigned int arg; /* Step 1 : check if triggers are trivially valid */ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT | TRIG_INT); flags = TRIG_FOLLOW; if (devpriv->master) flags |= TRIG_TIMER | TRIG_EXT; err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags); flags = TRIG_TIMER | TRIG_EXT; if (devpriv->master) flags |= TRIG_NOW; err |= comedi_check_trigger_src(&cmd->convert_src, flags); err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE | TRIG_EXT); 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 (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) err |= -EINVAL; if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) err |= -EINVAL; if ((cmd->scan_begin_src == TRIG_FOLLOW) && (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) err |= -EINVAL; if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) err |= -EINVAL; if (err) return 2; /* Step 3: check if arguments are trivially valid */ switch (cmd->start_src) { case TRIG_NOW: case TRIG_EXT: err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); break; case TRIG_INT: /* start_arg is the internal trigger (any value) */ break; } if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT)) err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); if ((cmd->scan_begin_src == TRIG_TIMER) && (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) { cmd->scan_begin_src = TRIG_FOLLOW; cmd->convert_arg = cmd->scan_begin_arg; cmd->scan_begin_arg = 0; } if (cmd->scan_begin_src == TRIG_TIMER) { err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, devpriv->ai_ns_min); } if (cmd->scan_begin_src == TRIG_EXT) { if (cmd->scan_begin_arg) { cmd->scan_begin_arg = 0; err |= -EINVAL; err |= comedi_check_trigger_arg_max(&cmd->scan_end_arg, 65535); } } if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { err |= comedi_check_trigger_arg_min(&cmd->convert_arg, devpriv->ai_ns_min); } if (cmd->convert_src == TRIG_EXT) err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); if (cmd->stop_src == TRIG_COUNT) err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); else /* TRIG_NONE */ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); err |= comedi_check_trigger_arg_min(&cmd->scan_end_arg, cmd->chanlist_len); if ((cmd->scan_end_arg % cmd->chanlist_len)) { cmd->scan_end_arg = cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len); err |= -EINVAL; } if (err) return 3; /* step 4: fix up any arguments */ if (cmd->scan_begin_src == TRIG_TIMER) { arg = cmd->scan_begin_arg; comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); } if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { arg = cmd->convert_arg; comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_NOW) { if (cmd->convert_arg == 0) { arg = devpriv->ai_ns_min * (cmd->scan_end_arg + 2); } else { arg = cmd->convert_arg * cmd->chanlist_len; } err |= comedi_check_trigger_arg_min(&cmd-> scan_begin_arg, arg); } } if (err) return 4; /* Step 5: check channel list if it exists */ if (cmd->chanlist) err |= pci9118_ai_check_chanlist(dev, s, cmd); if (err) return 5; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten70787.39%1285.71%
michal dobesmichal dobes749.15%17.14%
ian abbottian abbott283.46%17.14%
Total809100.00%14100.00%


static int pci9118_ai_eoc(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned long context) { unsigned int status; status = inl(dev->iobase + PCI9118_AI_STATUS_REG); if (status & PCI9118_AI_STATUS_ADRDY) return 0; return -EBUSY; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten53100.00%1100.00%
Total53100.00%1100.00%


static void pci9118_ai_start_conv(struct comedi_device *dev) { /* writing any value triggers an A/D conversion */ outl(0, dev->iobase + PCI9118_SOFTTRG_REG); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten23100.00%1100.00%
Total23100.00%1100.00%


static int pci9118_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { struct pci9118_private *devpriv = dev->private; unsigned int val; int ret; int i; /* * Configure analog input based on the chanspec. * Acqusition is software controlled without interrupts. */ pci9118_set_chanlist(dev, s, 1, &insn->chanspec, 0, 0); /* set default config (disable burst and triggers) */ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); pci9118_ai_reset_fifo(dev); for (i = 0; i < insn->n; i++) { pci9118_ai_start_conv(dev); ret = comedi_timeout(dev, s, insn, pci9118_ai_eoc, 0); if (ret) return ret; val = inl(dev->iobase + PCI9118_AI_FIFO_REG); if (s->maxdata == 0xffff) data[i] = (val & 0xffff) ^ 0x8000; else data[i] = (val >> 4) & 0xfff; } return insn->n; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten186100.00%2100.00%
Total186100.00%2100.00%


static int pci9118_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { unsigned int chan = CR_CHAN(insn->chanspec); unsigned int val = s->readback[chan]; int i; for (i = 0; i < insn->n; i++) { val = data[i]; outl(val, dev->iobase + PCI9118_AO_REG(chan)); } s->readback[chan] = val; return insn->n; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten103100.00%1100.00%
Total103100.00%1100.00%


static int pci9118_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { /* * The digital inputs and outputs share the read register. * bits [7:4] are the digital outputs * bits [3:0] are the digital inputs */ data[1] = inl(dev->iobase + PCI9118_DIO_REG) & 0xf; return insn->n; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten48100.00%1100.00%
Total48100.00%1100.00%


static int pci9118_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { /* * The digital outputs are set with the same register that * the digital inputs and outputs are read from. But the * outputs are set with bits [3:0] so we can simply write * the s->state to set them. */ if (comedi_dio_update_state(s, data)) outl(s->state, dev->iobase + PCI9118_DIO_REG); data[1] = s->state; return insn->n; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten63100.00%1100.00%
Total63100.00%1100.00%


static void pci9118_reset(struct comedi_device *dev) { /* reset analog input subsystem */ outl(0, dev->iobase + PCI9118_INT_CTRL_REG); outl(0, dev->iobase + PCI9118_AI_CTRL_REG); outl(0, dev->iobase + PCI9118_AI_CFG_REG); pci9118_ai_reset_fifo(dev); /* clear any pending interrupts and status */ inl(dev->iobase + PCI9118_INT_CTRL_REG); inl(dev->iobase + PCI9118_AI_STATUS_REG); /* reset DMA and scan queue */ outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG); outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); /* reset analog outputs to 0V */ outl(2047, dev->iobase + PCI9118_AO_REG(0)); outl(2047, dev->iobase + PCI9118_AO_REG(1)); }

Contributors

PersonTokensPropCommitsCommitProp
michal dobesmichal dobes7657.58%111.11%
h hartley sweetenh hartley sweeten5541.67%777.78%
bill pembertonbill pemberton10.76%111.11%
Total132100.00%9100.00%


static struct pci_dev *pci9118_find_pci(struct comedi_device *dev, struct comedi_devconfig *it) { struct pci_dev *pcidev = NULL; int bus = it->options[0]; int slot = it->options[1]; for_each_pci_dev(pcidev) { if (pcidev->vendor != PCI_VENDOR_ID_AMCC) continue; if (pcidev->device != 0x80d9) continue; if (bus || slot) { /* requested particular bus/slot */ if (pcidev->bus->number != bus || PCI_SLOT(pcidev->devfn) != slot) continue; } return pcidev; } dev_err(dev->class_dev, "no supported board found! (req. bus/slot : %d/%d)\n", bus, slot); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten6858.12%250.00%
ian abbottian abbott4941.88%250.00%
Total117100.00%4100.00%


static void pci9118_alloc_dma(struct comedi_device *dev) { struct pci9118_private *devpriv = dev->private; struct pci9118_dmabuf *dmabuf; int order; int i; for (i = 0; i < 2; i++) { dmabuf = &devpriv->dmabuf[i]; for (order = 2; order >= 0; order--) { dmabuf->virt = dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order, &dmabuf->hw, GFP_KERNEL); if (dmabuf->virt) break; } if (!dmabuf->virt) break; dmabuf->size = PAGE_SIZE << order; if (i == 0) devpriv->master = 1; if (i == 1) devpriv->dma_doublebuf = 1; } }

Contributors

PersonTokensPropCommitsCommitProp
michal dobesmichal dobes6747.86%19.09%
h hartley sweetenh hartley sweeten4230.00%436.36%
ian abbottian abbott2417.14%436.36%
bill pembertonbill pemberton75.00%218.18%
Total140100.00%11100.00%


static void pci9118_free_dma(struct comedi_device *dev) { struct pci9118_private *devpriv = dev->private; struct pci9118_dmabuf *dmabuf; int i; if (!devpriv) return; for (i = 0; i < 2; i++) { dmabuf = &devpriv->dmabuf[i]; if (dmabuf->virt) { dma_free_coherent(dev->hw_dev, dmabuf->size, dmabuf->virt, dmabuf->hw); } } }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten7384.88%250.00%
ian abbottian abbott1213.95%125.00%
michal dobesmichal dobes11.16%125.00%
Total86100.00%4100.00%


static int pci9118_common_attach(struct comedi_device *dev, int ext_mux, int softsshdelay) { const struct pci9118_boardinfo *board = dev->board_ptr; struct pci_dev *pcidev = comedi_to_pci_dev(dev); struct pci9118_private *devpriv; struct comedi_subdevice *s; int ret; int i; u16 u16w; devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); if (!devpriv) return -ENOMEM; ret = comedi_pci_enable(dev); if (ret) return ret; pci_set_master(pcidev); devpriv->iobase_a = pci_resource_start(pcidev, 0); dev->iobase = pci_resource_start(pcidev, 2); dev->pacer = comedi_8254_init(dev->iobase + PCI9118_TIMER_BASE, I8254_OSC_BASE_4MHZ, I8254_IO32, 0); if (!dev->pacer) return -ENOMEM; pci9118_reset(dev); if (pcidev->irq) { ret = request_irq(pcidev->irq, pci9118_interrupt, IRQF_SHARED, dev->board_name, dev); if (ret == 0) { dev->irq = pcidev->irq; pci9118_alloc_dma(dev); } } if (ext_mux > 0) { if (ext_mux > 256) ext_mux = 256; /* max 256 channels! */ if (softsshdelay > 0) if (ext_mux > 128) ext_mux = 128; devpriv->usemux = 1; } else { devpriv->usemux = 0; } if (softsshdelay < 0) { /* select sample&hold signal polarity */ devpriv->softsshdelay = -softsshdelay; devpriv->softsshsample = 0x80; devpriv->softsshhold = 0x00; } else { devpriv->softsshdelay = softsshdelay; devpriv->softsshsample = 0x00; devpriv->softsshhold = 0x80; } pci_read_config_word(pcidev, PCI_COMMAND, &u16w); pci_write_config_word(pcidev, PCI_COMMAND, u16w | 64); /* Enable parity check for parity error */ ret = comedi_alloc_subdevices(dev, 4); if (ret) return ret; /* Analog Input subdevice */ s = &dev->subdevices[0]; s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; s->n_chan = (devpriv->usemux) ? ext_mux : 16; s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; s->range_table = board->is_hg ? &pci9118hg_ai_range : &pci9118_ai_range; s->insn_read = pci9118_ai_insn_read; if (dev->irq) { dev->read_subdev = s; s->subdev_flags |= SDF_CMD_READ; s->len_chanlist = 255; s->do_cmdtest = pci9118_ai_cmdtest; s->do_cmd = pci9118_ai_cmd; s->cancel = pci9118_ai_cancel; s->munge = pci9118_ai_munge; } if (s->maxdata == 0xffff) { /* * 16-bit samples are from an ADS7805 A/D converter. * Minimum sampling rate is 10us. */ devpriv->ai_ns_min = 10000; } else { /* * 12-bit samples are from an ADS7800 A/D converter. * Minimum sampling rate is 3us. */ devpriv->ai_ns_min = 3000; } /* Analog Output subdevice */ s = &dev->subdevices[1]; s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; s->n_chan = 2; s->maxdata = 0x0fff; s->range_table = &range_bipolar10; s->insn_write = pci9118_ao_insn_write; ret = comedi_alloc_subdev_readback(s); if (ret) return ret; /* the analog outputs were reset to 0V, make the readback match */ for (i = 0; i < s->n_chan; i++) s->readback[i] = 2047; /* Digital Input subdevice */ s = &dev->subdevices[2]; s->type = COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = pci9118_di_insn_bits; /* Digital Output subdevice */ s = &dev->subdevices[3]; s->type = COMEDI_SUBD_DO; s->subdev_flags = SDF_WRITABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = pci9118_do_insn_bits; /* get the current state of the digital outputs */ s->state = inl(dev->iobase + PCI9118_DIO_REG) >> 4; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten36251.94%2683.87%
michal dobesmichal dobes30543.76%13.23%
ian abbottian abbott223.16%26.45%
bill pembertonbill pemberton50.72%13.23%
maurice dawsonmaurice dawson30.43%13.23%
Total697100.00%31100.00%


static int pci9118_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct pci_dev *pcidev; int ext_mux, softsshdelay; ext_mux = it->options[2]; softsshdelay = it->options[4]; pcidev = pci9118_find_pci(dev, it); if (!pcidev) return -EIO; comedi_set_hw_dev(dev, &pcidev->dev); return pci9118_common_attach(dev, ext_mux, softsshdelay); }

Contributors

PersonTokensPropCommitsCommitProp
ian abbottian abbott82100.00%1100.00%
Total82100.00%1100.00%


static int pci9118_auto_attach(struct comedi_device *dev, unsigned long context) { struct pci_dev *pcidev = comedi_to_pci_dev(dev); const struct pci9118_boardinfo *board = NULL; if (context < ARRAY_SIZE(pci9118_boards)) board = &pci9118_boards[context]; if (!board) return -ENODEV; dev->board_ptr = board; dev->board_name = board->name; /* * Need to 'get' the PCI device to match the 'put' in pci9118_detach(). * (The 'put' also matches the implicit 'get' by pci9118_find_pci().) */ pci_dev_get(pcidev); /* no external mux, no sample-hold delay */ return pci9118_common_attach(dev, 0, 0); }

Contributors

PersonTokensPropCommitsCommitProp
ian abbottian abbott4752.22%114.29%
h hartley sweetenh hartley sweeten3538.89%457.14%
michal dobesmichal dobes77.78%114.29%
bill pembertonbill pemberton11.11%114.29%
Total90100.00%7100.00%


static void pci9118_detach(struct comedi_device *dev) { struct pci_dev *pcidev = comedi_to_pci_dev(dev); if (dev->iobase) pci9118_reset(dev); comedi_pci_detach(dev); pci9118_free_dma(dev); if (pcidev) pci_dev_put(pcidev); }

Contributors

PersonTokensPropCommitsCommitProp
ian abbottian abbott4180.39%116.67%
h hartley sweetenh hartley sweeten1019.61%583.33%
Total51100.00%6100.00%

static struct comedi_driver adl_pci9118_driver = { .driver_name = "adl_pci9118", .module = THIS_MODULE, .attach = pci9118_attach, .auto_attach = pci9118_auto_attach, .detach = pci9118_detach, .num_names = ARRAY_SIZE(pci9118_boards), .board_name = &pci9118_boards[0].name, .offset = sizeof(struct pci9118_boardinfo), };
static int adl_pci9118_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { return comedi_pci_auto_config(dev, &adl_pci9118_driver, id->driver_data); }

Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten30100.00%2100.00%
Total30100.00%2100.00%

/* FIXME: All the supported board types have the same device ID! */ static const struct pci_device_id adl_pci9118_pci_table[] = { { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG }, /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */ /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */ { 0 } }; MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table); static struct pci_driver adl_pci9118_pci_driver = { .name = "adl_pci9118", .id_table = adl_pci9118_pci_table, .probe = adl_pci9118_pci_probe, .remove = comedi_pci_auto_unconfig, }; module_comedi_pci_driver(adl_pci9118_driver, adl_pci9118_pci_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
h hartley sweetenh hartley sweeten562771.62%11679.45%
michal dobesmichal dobes133717.02%10.68%
ian abbottian abbott82010.44%138.90%
bill pembertonbill pemberton270.34%64.11%
arun thomasarun thomas150.19%10.68%
ksenija stanojevicksenija stanojevic70.09%10.68%
jingoo hanjingoo han60.08%10.68%
maurice dawsonmaurice dawson60.08%10.68%
jiri slabyjiri slaby30.04%10.68%
tejun heotejun heo30.04%10.68%
greg kroah-hartmangreg kroah-hartman30.04%10.68%
peter huewepeter huewe20.03%21.37%
sam asadisam asadi10.01%10.68%
Total7857100.00%146100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}