2151 lines
69 KiB
C
2151 lines
69 KiB
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 neccessary 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 cann't be used if cmd->scan_begin_src=TRIG_FOLLOW
|
|
and is used flag TRIG_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] - 0=autoselect DMA or EOC interrupts operation
|
|
1=disable DMA mode
|
|
3=disable DMA and INT, only insn interface will work
|
|
[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] - 0=stop measure on all hardware errors
|
|
2|=ignore ADOR - A/D Overrun status
|
|
8|=ignore Bover - A/D Burst Mode Overrun status
|
|
256|=ignore nFull - A/D FIFO Full status
|
|
|
|
*/
|
|
#include "../comedidev.h"
|
|
#include "../pci_ids.h"
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include "amcc_s5933.h"
|
|
#include "8253.h"
|
|
#include "comedi_pci.h"
|
|
#include "comedi_fc.h"
|
|
|
|
/* paranoid checks are broken */
|
|
#undef PCI9118_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */
|
|
|
|
#undef PCI9118_EXTDEBUG /* if defined then driver prints a lot of messages */
|
|
|
|
#undef DPRINTK
|
|
#ifdef PCI9118_EXTDEBUG
|
|
#define DPRINTK(fmt, args...) printk(fmt, ## args)
|
|
#else
|
|
#define DPRINTK(fmt, args...)
|
|
#endif
|
|
|
|
#define IORANGE_9118 64 /* I hope */
|
|
#define PCI9118_CHANLEN 255 /* len of chanlist, some source say 256, but reality looks like 255 :-( */
|
|
|
|
#define PCI9118_CNT0 0x00 /* R/W: 8254 couter 0 */
|
|
#define PCI9118_CNT1 0x04 /* R/W: 8254 couter 0 */
|
|
#define PCI9118_CNT2 0x08 /* R/W: 8254 couter 0 */
|
|
#define PCI9118_CNTCTRL 0x0c /* W: 8254 counter control */
|
|
#define PCI9118_AD_DATA 0x10 /* R: A/D data */
|
|
#define PCI9118_DA1 0x10 /* W: D/A registers */
|
|
#define PCI9118_DA2 0x14
|
|
#define PCI9118_ADSTAT 0x18 /* R: A/D status register */
|
|
#define PCI9118_ADCNTRL 0x18 /* W: A/D control register */
|
|
#define PCI9118_DI 0x1c /* R: digi input register */
|
|
#define PCI9118_DO 0x1c /* W: digi output register */
|
|
#define PCI9118_SOFTTRG 0x20 /* W: soft trigger for A/D */
|
|
#define PCI9118_GAIN 0x24 /* W: A/D gain/channel register */
|
|
#define PCI9118_BURST 0x28 /* W: A/D burst number register */
|
|
#define PCI9118_SCANMOD 0x2c /* W: A/D auto scan mode */
|
|
#define PCI9118_ADFUNC 0x30 /* W: A/D function register */
|
|
#define PCI9118_DELFIFO 0x34 /* W: A/D data FIFO reset */
|
|
#define PCI9118_INTSRC 0x38 /* R: interrupt reason register */
|
|
#define PCI9118_INTCTRL 0x38 /* W: interrupt control register */
|
|
|
|
/* bits from A/D control register (PCI9118_ADCNTRL) */
|
|
#define AdControl_UniP 0x80 /* 1=bipolar, 0=unipolar */
|
|
#define AdControl_Diff 0x40 /* 1=differential, 0= single end inputs */
|
|
#define AdControl_SoftG 0x20 /* 1=8254 counter works, 0=counter stops */
|
|
#define AdControl_ExtG 0x10 /* 1=8254 countrol controlled by TGIN(pin 46), 0=controled by SoftG */
|
|
#define AdControl_ExtM 0x08 /* 1=external hardware trigger (pin 44), 0=internal trigger */
|
|
#define AdControl_TmrTr 0x04 /* 1=8254 is iternal trigger source, 0=software trigger is source (register PCI9118_SOFTTRG) */
|
|
#define AdControl_Int 0x02 /* 1=enable INT, 0=disable */
|
|
#define AdControl_Dma 0x01 /* 1=enable DMA, 0=disable */
|
|
|
|
/* bits from A/D function register (PCI9118_ADFUNC) */
|
|
#define AdFunction_PDTrg 0x80 /* 1=positive, 0=negative digital trigger (only positive is correct) */
|
|
#define AdFunction_PETrg 0x40 /* 1=positive, 0=negative external trigger (only positive is correct) */
|
|
#define AdFunction_BSSH 0x20 /* 1=with sample&hold, 0=without */
|
|
#define AdFunction_BM 0x10 /* 1=burst mode, 0=normal mode */
|
|
#define AdFunction_BS 0x08 /* 1=burst mode start, 0=burst mode stop */
|
|
#define AdFunction_PM 0x04 /* 1=post trigger mode, 0=not post trigger */
|
|
#define AdFunction_AM 0x02 /* 1=about trigger mode, 0=not about trigger */
|
|
#define AdFunction_Start 0x01 /* 1=trigger start, 0=trigger stop */
|
|
|
|
/* bits from A/D status register (PCI9118_ADSTAT) */
|
|
#define AdStatus_nFull 0x100 /* 0=FIFO full (fatal), 1=not full */
|
|
#define AdStatus_nHfull 0x080 /* 0=FIFO half full, 1=FIFO not half full */
|
|
#define AdStatus_nEpty 0x040 /* 0=FIFO empty, 1=FIFO not empty */
|
|
#define AdStatus_Acmp 0x020 /* */
|
|
#define AdStatus_DTH 0x010 /* 1=external digital trigger */
|
|
#define AdStatus_Bover 0x008 /* 1=burst mode overrun (fatal) */
|
|
#define AdStatus_ADOS 0x004 /* 1=A/D over speed (warning) */
|
|
#define AdStatus_ADOR 0x002 /* 1=A/D overrun (fatal) */
|
|
#define AdStatus_ADrdy 0x001 /* 1=A/D already ready, 0=not ready */
|
|
|
|
/* bits for interrupt reason and control (PCI9118_INTSRC, PCI9118_INTCTRL) */
|
|
/* 1=interrupt occur, enable source, 0=interrupt not occur, disable source */
|
|
#define Int_Timer 0x08 /* timer interrupt */
|
|
#define Int_About 0x04 /* about trigger complete */
|
|
#define Int_Hfull 0x02 /* A/D FIFO hlaf full */
|
|
#define Int_DTrg 0x01 /* external digital trigger */
|
|
|
|
#define START_AI_EXT 0x01 /* start measure on external trigger */
|
|
#define STOP_AI_EXT 0x02 /* stop measure on external trigger */
|
|
#define START_AI_INT 0x04 /* start measure on internal trigger */
|
|
#define STOP_AI_INT 0x08 /* stop measure on internal trigger */
|
|
|
|
#define EXTTRG_AI 0 /* ext trg is used by AI */
|
|
|
|
static const struct comedi_lrange range_pci9118dg_hr = { 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 range_pci9118hg = { 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)
|
|
}
|
|
};
|
|
|
|
#define PCI9118_BIPOLAR_RANGES 4 /* used for test on mixture of BIP/UNI ranges */
|
|
|
|
static int pci9118_attach(struct comedi_device *dev,
|
|
struct comedi_devconfig *it);
|
|
static int pci9118_detach(struct comedi_device *dev);
|
|
|
|
struct boardtype {
|
|
const char *name; /* board name */
|
|
int vendor_id; /* PCI vendor a device ID of card */
|
|
int device_id;
|
|
int iorange_amcc; /* iorange for own S5933 region */
|
|
int iorange_9118; /* pass thru card region size */
|
|
int n_aichan; /* num of A/D chans */
|
|
int n_aichand; /* num of A/D chans in diff mode */
|
|
int mux_aichan; /* num of A/D chans with external multiplexor */
|
|
int n_aichanlist; /* len of chanlist */
|
|
int n_aochan; /* num of D/A chans */
|
|
int ai_maxdata; /* resolution of A/D */
|
|
int ao_maxdata; /* resolution of D/A */
|
|
const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
|
|
const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
|
|
unsigned int ai_ns_min; /* max sample speed of card v ns */
|
|
unsigned int ai_pacer_min; /* minimal pacer value (c1*c2 or c1 in burst) */
|
|
int half_fifo_size; /* size of FIFO/2 */
|
|
|
|
};
|
|
|
|
static DEFINE_PCI_DEVICE_TABLE(pci9118_pci_table) = {
|
|
{
|
|
PCI_VENDOR_ID_AMCC, 0x80d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
|
|
0}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, pci9118_pci_table);
|
|
|
|
static const struct boardtype boardtypes[] = {
|
|
{"pci9118dg", PCI_VENDOR_ID_AMCC, 0x80d9,
|
|
AMCC_OP_REG_SIZE, IORANGE_9118,
|
|
16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff,
|
|
&range_pci9118dg_hr, &range_bipolar10,
|
|
3000, 12, 512},
|
|
{"pci9118hg", PCI_VENDOR_ID_AMCC, 0x80d9,
|
|
AMCC_OP_REG_SIZE, IORANGE_9118,
|
|
16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff,
|
|
&range_pci9118hg, &range_bipolar10,
|
|
3000, 12, 512},
|
|
{"pci9118hr", PCI_VENDOR_ID_AMCC, 0x80d9,
|
|
AMCC_OP_REG_SIZE, IORANGE_9118,
|
|
16, 8, 256, PCI9118_CHANLEN, 2, 0xffff, 0x0fff,
|
|
&range_pci9118dg_hr, &range_bipolar10,
|
|
10000, 40, 512},
|
|
};
|
|
|
|
#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
|
|
|
|
static struct comedi_driver driver_pci9118 = {
|
|
.driver_name = "adl_pci9118",
|
|
.module = THIS_MODULE,
|
|
.attach = pci9118_attach,
|
|
.detach = pci9118_detach,
|
|
.num_names = n_boardtypes,
|
|
.board_name = &boardtypes[0].name,
|
|
.offset = sizeof(struct boardtype),
|
|
};
|
|
|
|
COMEDI_PCI_INITCLEANUP(driver_pci9118, pci9118_pci_table);
|
|
|
|
struct pci9118_private {
|
|
unsigned long iobase_a; /* base+size for AMCC chip */
|
|
unsigned int master; /* master capable */
|
|
struct pci_dev *pcidev; /* ptr to actual pcidev */
|
|
unsigned int usemux; /* we want to use external multiplexor! */
|
|
#ifdef PCI9118_PARANOIDCHECK
|
|
unsigned short chanlist[PCI9118_CHANLEN + 1]; /* list of scaned channel */
|
|
unsigned char chanlistlen; /* number of scanlist */
|
|
#endif
|
|
unsigned char AdControlReg; /* A/D control register */
|
|
unsigned char IntControlReg; /* Interrupt control register */
|
|
unsigned char AdFunctionReg; /* A/D function register */
|
|
char valid; /* driver is ok */
|
|
char ai_neverending; /* we do unlimited AI */
|
|
unsigned int i8254_osc_base; /* frequence of onboard oscilator */
|
|
unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
|
|
unsigned int ai_act_scan; /* how many scans we finished */
|
|
unsigned int ai_buf_ptr; /* data buffer ptr in samples */
|
|
unsigned int ai_n_chan; /* how many channels is measured */
|
|
unsigned int ai_n_scanlen; /* len of actual scanlist */
|
|
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_chanlist; /* actaul chanlist */
|
|
unsigned int ai_timer1;
|
|
unsigned int ai_timer2;
|
|
unsigned int ai_flags;
|
|
char ai12_startstop; /* measure can start/stop on external trigger */
|
|
unsigned int ai_divisor1, ai_divisor2; /* divisors for start of measure on external start */
|
|
unsigned int ai_data_len;
|
|
short *ai_data;
|
|
short ao_data[2]; /* data output buffer */
|
|
unsigned int ai_scans; /* number of scans to do */
|
|
char dma_doublebuf; /* we can use double buffring */
|
|
unsigned int dma_actbuf; /* which buffer is used now */
|
|
short *dmabuf_virt[2]; /* pointers to begin of DMA buffer */
|
|
unsigned long dmabuf_hw[2]; /* hw address of DMA buff */
|
|
unsigned int dmabuf_size[2]; /* size of dma buffer in bytes */
|
|
unsigned int dmabuf_use_size[2]; /* which size we may now used for transfer */
|
|
unsigned int dmabuf_used_size[2]; /* which size was trully used */
|
|
unsigned int dmabuf_panic_size[2];
|
|
unsigned int dmabuf_samples[2]; /* size in samples */
|
|
int dmabuf_pages[2]; /* number of pages in buffer */
|
|
unsigned char cnt0_users; /* bit field of 8254 CNT0 users (0-unused, 1-AO, 2-DI, 3-DO) */
|
|
unsigned char exttrg_users; /* bit field of external trigger users (0-AI, 1-AO, 2-DI, 3-DO) */
|
|
unsigned int cnt0_divisor; /* actual CNT0 divisor */
|
|
void (*int_ai_func) (struct comedi_device *, struct comedi_subdevice *, unsigned short, unsigned int, unsigned short); /* ptr to actual interrupt AI function */
|
|
unsigned char ai16bits; /* =1 16 bit card */
|
|
unsigned char usedma; /* =1 use DMA transfer and not INT */
|
|
unsigned char useeoshandle; /* =1 change WAKE_EOS DMA transfer to fit on every second */
|
|
unsigned char usessh; /* =1 turn on S&H support */
|
|
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_maskerr; /* which warning was printed */
|
|
unsigned int ai_maskharderr; /* on which error bits stops */
|
|
unsigned int ai_inttrig_start; /* TRIG_INT for start */
|
|
};
|
|
|
|
#define devpriv ((struct pci9118_private *)dev->private)
|
|
#define this_board ((struct boardtype *)dev->board_ptr)
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
|
|
static int check_channel_list(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, int n_chan,
|
|
unsigned int *chanlist, int frontadd,
|
|
int backadd);
|
|
static int setup_channel_list(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, int n_chan,
|
|
unsigned int *chanlist, int rot, int frontadd,
|
|
int backadd, int usedma, char eoshandle);
|
|
static void start_pacer(struct comedi_device *dev, int mode,
|
|
unsigned int divisor1, unsigned int divisor2);
|
|
static int pci9118_reset(struct comedi_device *dev);
|
|
static int pci9118_exttrg_add(struct comedi_device *dev, unsigned char source);
|
|
static int pci9118_exttrg_del(struct comedi_device *dev, unsigned char source);
|
|
static int pci9118_ai_cancel(struct comedi_device *dev,
|
|
struct comedi_subdevice *s);
|
|
static void pci9118_calc_divisors(char mode, 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,
|
|
char usessh, unsigned int chnsshfront);
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_insn_read_ai(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn, unsigned int *data)
|
|
{
|
|
|
|
int n, timeout;
|
|
|
|
devpriv->AdControlReg = AdControl_Int & 0xff;
|
|
devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */
|
|
|
|
if (!setup_channel_list(dev, s, 1, &insn->chanspec, 0, 0, 0, 0, 0))
|
|
return -EINVAL;
|
|
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
|
|
for (n = 0; n < insn->n; n++) {
|
|
outw(0, dev->iobase + PCI9118_SOFTTRG); /* start conversion */
|
|
udelay(2);
|
|
timeout = 100;
|
|
while (timeout--) {
|
|
if (inl(dev->iobase + PCI9118_ADSTAT) & AdStatus_ADrdy)
|
|
goto conv_finish;
|
|
udelay(1);
|
|
}
|
|
|
|
comedi_error(dev, "A/D insn timeout");
|
|
data[n] = 0;
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
return -ETIME;
|
|
|
|
conv_finish:
|
|
if (devpriv->ai16bits) {
|
|
data[n] =
|
|
(inl(dev->iobase +
|
|
PCI9118_AD_DATA) & 0xffff) ^ 0x8000;
|
|
} else {
|
|
data[n] =
|
|
(inw(dev->iobase + PCI9118_AD_DATA) >> 4) & 0xfff;
|
|
}
|
|
}
|
|
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
return n;
|
|
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_insn_write_ao(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn, unsigned int *data)
|
|
{
|
|
int n, chanreg, ch;
|
|
|
|
ch = CR_CHAN(insn->chanspec);
|
|
if (ch) {
|
|
chanreg = PCI9118_DA2;
|
|
} else {
|
|
chanreg = PCI9118_DA1;
|
|
}
|
|
|
|
for (n = 0; n < insn->n; n++) {
|
|
outl(data[n], dev->iobase + chanreg);
|
|
devpriv->ao_data[ch] = data[n];
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_insn_read_ao(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn, unsigned int *data)
|
|
{
|
|
int n, chan;
|
|
|
|
chan = CR_CHAN(insn->chanspec);
|
|
for (n = 0; n < insn->n; n++)
|
|
data[n] = devpriv->ao_data[chan];
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_insn_bits_di(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn, unsigned int *data)
|
|
{
|
|
data[1] = inl(dev->iobase + PCI9118_DI) & 0xf;
|
|
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_insn_bits_do(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn, unsigned int *data)
|
|
{
|
|
if (data[0]) {
|
|
s->state &= ~data[0];
|
|
s->state |= (data[0] & data[1]);
|
|
outl(s->state & 0x0f, dev->iobase + PCI9118_DO);
|
|
}
|
|
data[1] = s->state;
|
|
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev)
|
|
{
|
|
devpriv->AdFunctionReg =
|
|
AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
outl(0x30, dev->iobase + PCI9118_CNTCTRL);
|
|
outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 1) & 0xff,
|
|
dev->iobase + PCI9118_CNT0);
|
|
outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 9) & 0xff,
|
|
dev->iobase + PCI9118_CNT0);
|
|
devpriv->AdFunctionReg |= AdFunction_Start;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
}
|
|
|
|
static unsigned int defragment_dma_buffer(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
short *dma_buffer,
|
|
unsigned int num_samples)
|
|
{
|
|
unsigned int i = 0, j = 0;
|
|
unsigned int start_pos = devpriv->ai_add_front,
|
|
stop_pos = devpriv->ai_add_front + devpriv->ai_n_chan;
|
|
unsigned int raw_scanlen = devpriv->ai_add_front + devpriv->ai_n_chan +
|
|
devpriv->ai_add_back;
|
|
|
|
for (i = 0; i < num_samples; i++) {
|
|
if (devpriv->ai_act_dmapos >= start_pos &&
|
|
devpriv->ai_act_dmapos < stop_pos) {
|
|
dma_buffer[j++] = dma_buffer[i];
|
|
}
|
|
devpriv->ai_act_dmapos++;
|
|
devpriv->ai_act_dmapos %= raw_scanlen;
|
|
}
|
|
|
|
return j;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static unsigned int move_block_from_dma(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
short *dma_buffer,
|
|
unsigned int num_samples)
|
|
{
|
|
unsigned int num_bytes;
|
|
|
|
num_samples = defragment_dma_buffer(dev, s, dma_buffer, num_samples);
|
|
devpriv->ai_act_scan +=
|
|
(s->async->cur_chan + num_samples) / devpriv->ai_n_scanlen;
|
|
s->async->cur_chan += num_samples;
|
|
s->async->cur_chan %= devpriv->ai_n_scanlen;
|
|
num_bytes =
|
|
cfc_write_array_to_buffer(s, dma_buffer,
|
|
num_samples * sizeof(short));
|
|
if (num_bytes < num_samples * sizeof(short))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static char pci9118_decode_error_status(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
unsigned char m)
|
|
{
|
|
if (m & 0x100) {
|
|
comedi_error(dev, "A/D FIFO Full status (Fatal Error!)");
|
|
devpriv->ai_maskerr &= ~0x100L;
|
|
}
|
|
if (m & 0x008) {
|
|
comedi_error(dev,
|
|
"A/D Burst Mode Overrun Status (Fatal Error!)");
|
|
devpriv->ai_maskerr &= ~0x008L;
|
|
}
|
|
if (m & 0x004) {
|
|
comedi_error(dev, "A/D Over Speed Status (Warning!)");
|
|
devpriv->ai_maskerr &= ~0x004L;
|
|
}
|
|
if (m & 0x002) {
|
|
comedi_error(dev, "A/D Overrun Status (Fatal Error!)");
|
|
devpriv->ai_maskerr &= ~0x002L;
|
|
}
|
|
if (m & devpriv->ai_maskharderr) {
|
|
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
|
|
pci9118_ai_cancel(dev, s);
|
|
comedi_event(dev, s);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pci9118_ai_munge(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, void *data,
|
|
unsigned int num_bytes,
|
|
unsigned int start_chan_index)
|
|
{
|
|
unsigned int i, num_samples = num_bytes / sizeof(short);
|
|
short *array = data;
|
|
|
|
for (i = 0; i < num_samples; i++) {
|
|
if (devpriv->usedma)
|
|
array[i] = be16_to_cpu(array[i]);
|
|
if (devpriv->ai16bits) {
|
|
array[i] ^= 0x8000;
|
|
} else {
|
|
array[i] = (array[i] >> 4) & 0x0fff;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static void interrupt_pci9118_ai_onesample(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
unsigned short int_adstat,
|
|
unsigned int int_amcc,
|
|
unsigned short int_daq)
|
|
{
|
|
register short sampl;
|
|
|
|
s->async->events = 0;
|
|
|
|
if (int_adstat & devpriv->ai_maskerr)
|
|
if (pci9118_decode_error_status(dev, s, int_adstat))
|
|
return;
|
|
|
|
sampl = inw(dev->iobase + PCI9118_AD_DATA);
|
|
|
|
#ifdef PCI9118_PARANOIDCHECK
|
|
if (devpriv->ai16bits == 0) {
|
|
if ((sampl & 0x000f) != devpriv->chanlist[s->async->cur_chan]) { /* data dropout! */
|
|
printk
|
|
("comedi: A/D SAMPL - data dropout: received channel %d, expected %d!\n",
|
|
sampl & 0x000f,
|
|
devpriv->chanlist[s->async->cur_chan]);
|
|
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
|
|
pci9118_ai_cancel(dev, s);
|
|
comedi_event(dev, s);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
cfc_write_to_buffer(s, sampl);
|
|
s->async->cur_chan++;
|
|
if (s->async->cur_chan >= devpriv->ai_n_scanlen) { /* one scan done */
|
|
s->async->cur_chan %= devpriv->ai_n_scanlen;
|
|
devpriv->ai_act_scan++;
|
|
if (!(devpriv->ai_neverending))
|
|
if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
|
|
pci9118_ai_cancel(dev, s);
|
|
s->async->events |= COMEDI_CB_EOA;
|
|
}
|
|
}
|
|
|
|
if (s->async->events)
|
|
comedi_event(dev, s);
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
unsigned short int_adstat,
|
|
unsigned int int_amcc,
|
|
unsigned short int_daq)
|
|
{
|
|
unsigned int next_dma_buf, samplesinbuf, sampls, m;
|
|
|
|
if (int_amcc & MASTER_ABORT_INT) {
|
|
comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!");
|
|
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
|
|
pci9118_ai_cancel(dev, s);
|
|
comedi_event(dev, s);
|
|
return;
|
|
}
|
|
|
|
if (int_amcc & TARGET_ABORT_INT) {
|
|
comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!");
|
|
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
|
|
pci9118_ai_cancel(dev, s);
|
|
comedi_event(dev, s);
|
|
return;
|
|
}
|
|
|
|
if (int_adstat & devpriv->ai_maskerr)
|
|
/* if (int_adstat & 0x106) */
|
|
if (pci9118_decode_error_status(dev, s, int_adstat))
|
|
return;
|
|
|
|
samplesinbuf = devpriv->dmabuf_use_size[devpriv->dma_actbuf] >> 1; /* number of received real samples */
|
|
/* DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf); */
|
|
|
|
if (devpriv->dma_doublebuf) { /* switch DMA buffers if is used double buffering */
|
|
next_dma_buf = 1 - devpriv->dma_actbuf;
|
|
outl(devpriv->dmabuf_hw[next_dma_buf],
|
|
devpriv->iobase_a + AMCC_OP_REG_MWAR);
|
|
outl(devpriv->dmabuf_use_size[next_dma_buf],
|
|
devpriv->iobase_a + AMCC_OP_REG_MWTC);
|
|
devpriv->dmabuf_used_size[next_dma_buf] =
|
|
devpriv->dmabuf_use_size[next_dma_buf];
|
|
if (devpriv->ai_do == 4)
|
|
interrupt_pci9118_ai_mode4_switch(dev);
|
|
}
|
|
|
|
if (samplesinbuf) {
|
|
m = devpriv->ai_data_len >> 1; /* how many samples is to end of buffer */
|
|
/* DPRINTK("samps=%d m=%d %d %d\n",samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr); */
|
|
sampls = m;
|
|
move_block_from_dma(dev, s,
|
|
devpriv->dmabuf_virt[devpriv->dma_actbuf],
|
|
samplesinbuf);
|
|
m = m - sampls; /* m= how many samples was transfered */
|
|
}
|
|
/* DPRINTK("YYY\n"); */
|
|
|
|
if (!devpriv->ai_neverending)
|
|
if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
|
|
pci9118_ai_cancel(dev, s);
|
|
s->async->events |= COMEDI_CB_EOA;
|
|
}
|
|
|
|
if (devpriv->dma_doublebuf) { /* switch dma buffers */
|
|
devpriv->dma_actbuf = 1 - devpriv->dma_actbuf;
|
|
} else { /* restart DMA if is not used double buffering */
|
|
outl(devpriv->dmabuf_hw[0],
|
|
devpriv->iobase_a + AMCC_OP_REG_MWAR);
|
|
outl(devpriv->dmabuf_use_size[0],
|
|
devpriv->iobase_a + AMCC_OP_REG_MWTC);
|
|
if (devpriv->ai_do == 4)
|
|
interrupt_pci9118_ai_mode4_switch(dev);
|
|
}
|
|
|
|
comedi_event(dev, s);
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static irqreturn_t interrupt_pci9118(int irq, void *d)
|
|
{
|
|
struct comedi_device *dev = d;
|
|
unsigned int int_daq = 0, int_amcc, int_adstat;
|
|
|
|
if (!dev->attached)
|
|
return IRQ_NONE; /* not fully initialized */
|
|
|
|
int_daq = inl(dev->iobase + PCI9118_INTSRC) & 0xf; /* get IRQ reasons from card */
|
|
int_amcc = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* get INT register from AMCC chip */
|
|
|
|
/* DPRINTK("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x MWTC=0x%08x ADSTAT=0x%02x ai_do=%d\n", int_daq, int_amcc, inl(devpriv->iobase_a+AMCC_OP_REG_MWAR), inl(devpriv->iobase_a+AMCC_OP_REG_MWTC), inw(dev->iobase+PCI9118_ADSTAT)&0x1ff,devpriv->ai_do); */
|
|
|
|
if ((!int_daq) && (!(int_amcc & ANY_S593X_INT)))
|
|
return IRQ_NONE; /* interrupt from other source */
|
|
|
|
outl(int_amcc | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* shutdown IRQ reasons in AMCC */
|
|
|
|
int_adstat = inw(dev->iobase + PCI9118_ADSTAT) & 0x1ff; /* get STATUS register */
|
|
|
|
if (devpriv->ai_do) {
|
|
if (devpriv->ai12_startstop)
|
|
if ((int_adstat & AdStatus_DTH) && (int_daq & Int_DTrg)) { /* start stop of measure */
|
|
if (devpriv->ai12_startstop & START_AI_EXT) {
|
|
devpriv->ai12_startstop &=
|
|
~START_AI_EXT;
|
|
if (!(devpriv->ai12_startstop &
|
|
STOP_AI_EXT))
|
|
pci9118_exttrg_del(dev, EXTTRG_AI); /* deactivate EXT trigger */
|
|
start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, devpriv->ai_divisor2); /* start pacer */
|
|
outl(devpriv->AdControlReg,
|
|
dev->iobase + PCI9118_ADCNTRL);
|
|
} else {
|
|
if (devpriv->ai12_startstop &
|
|
STOP_AI_EXT) {
|
|
devpriv->ai12_startstop &=
|
|
~STOP_AI_EXT;
|
|
pci9118_exttrg_del(dev, EXTTRG_AI); /* deactivate EXT trigger */
|
|
devpriv->ai_neverending = 0; /* well, on next interrupt from DMA/EOC measure will stop */
|
|
}
|
|
}
|
|
}
|
|
|
|
(devpriv->int_ai_func) (dev, dev->subdevices + 0, int_adstat,
|
|
int_amcc, int_daq);
|
|
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_inttrig(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, unsigned int trignum)
|
|
{
|
|
if (trignum != devpriv->ai_inttrig_start)
|
|
return -EINVAL;
|
|
|
|
devpriv->ai12_startstop &= ~START_AI_INT;
|
|
s->async->inttrig = NULL;
|
|
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
if (devpriv->ai_do != 3) {
|
|
start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
|
|
devpriv->ai_divisor2);
|
|
devpriv->AdControlReg |= AdControl_SoftG;
|
|
}
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_cmdtest(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_cmd *cmd)
|
|
{
|
|
int err = 0;
|
|
int tmp, divisor1, divisor2;
|
|
|
|
/* step 1: make sure trigger sources are trivially valid */
|
|
|
|
tmp = cmd->start_src;
|
|
cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
|
|
if (!cmd->start_src || tmp != cmd->start_src)
|
|
err++;
|
|
|
|
tmp = cmd->scan_begin_src;
|
|
if (devpriv->master) {
|
|
cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW;
|
|
} else {
|
|
cmd->scan_begin_src &= TRIG_FOLLOW;
|
|
}
|
|
if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
|
|
err++;
|
|
|
|
tmp = cmd->convert_src;
|
|
if (devpriv->master) {
|
|
cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW;
|
|
} else {
|
|
cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
|
|
}
|
|
if (!cmd->convert_src || tmp != cmd->convert_src)
|
|
err++;
|
|
|
|
tmp = cmd->scan_end_src;
|
|
cmd->scan_end_src &= TRIG_COUNT;
|
|
if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
|
|
err++;
|
|
|
|
tmp = cmd->stop_src;
|
|
cmd->stop_src &= TRIG_COUNT | TRIG_NONE | TRIG_EXT;
|
|
if (!cmd->stop_src || tmp != cmd->stop_src)
|
|
err++;
|
|
|
|
if (err)
|
|
return 1;
|
|
|
|
/* step 2: make sure trigger sources are unique and mutually compatible */
|
|
|
|
if (cmd->start_src != TRIG_NOW &&
|
|
cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT) {
|
|
cmd->start_src = TRIG_NOW;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->scan_begin_src != TRIG_TIMER &&
|
|
cmd->scan_begin_src != TRIG_EXT &&
|
|
cmd->scan_begin_src != TRIG_INT &&
|
|
cmd->scan_begin_src != TRIG_FOLLOW) {
|
|
cmd->scan_begin_src = TRIG_FOLLOW;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->convert_src != TRIG_TIMER &&
|
|
cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) {
|
|
cmd->convert_src = TRIG_TIMER;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->scan_end_src != TRIG_COUNT) {
|
|
cmd->scan_end_src = TRIG_COUNT;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->stop_src != TRIG_NONE &&
|
|
cmd->stop_src != TRIG_COUNT &&
|
|
cmd->stop_src != TRIG_INT && cmd->stop_src != TRIG_EXT) {
|
|
cmd->stop_src = TRIG_COUNT;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
|
|
cmd->start_src = TRIG_NOW;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) {
|
|
cmd->start_src = TRIG_NOW;
|
|
err++;
|
|
}
|
|
|
|
if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) &&
|
|
(!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) {
|
|
cmd->convert_src = TRIG_TIMER;
|
|
err++;
|
|
}
|
|
|
|
if ((cmd->scan_begin_src == TRIG_FOLLOW) &&
|
|
(!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) {
|
|
cmd->convert_src = TRIG_TIMER;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
|
|
cmd->stop_src = TRIG_COUNT;
|
|
err++;
|
|
}
|
|
|
|
if (err)
|
|
return 2;
|
|
|
|
/* step 3: make sure arguments are trivially compatible */
|
|
|
|
if (cmd->start_src & (TRIG_NOW | TRIG_EXT))
|
|
if (cmd->start_arg != 0) {
|
|
cmd->start_arg = 0;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT))
|
|
if (cmd->scan_begin_arg != 0) {
|
|
cmd->scan_begin_arg = 0;
|
|
err++;
|
|
}
|
|
|
|
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)
|
|
if (cmd->scan_begin_arg < this_board->ai_ns_min) {
|
|
cmd->scan_begin_arg = this_board->ai_ns_min;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->scan_begin_src == TRIG_EXT)
|
|
if (cmd->scan_begin_arg) {
|
|
cmd->scan_begin_arg = 0;
|
|
err++;
|
|
if (cmd->scan_end_arg > 65535) {
|
|
cmd->scan_end_arg = 65535;
|
|
err++;
|
|
}
|
|
}
|
|
|
|
if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW))
|
|
if (cmd->convert_arg < this_board->ai_ns_min) {
|
|
cmd->convert_arg = this_board->ai_ns_min;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->convert_src == TRIG_EXT)
|
|
if (cmd->convert_arg) {
|
|
cmd->convert_arg = 0;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->stop_src == TRIG_COUNT) {
|
|
if (!cmd->stop_arg) {
|
|
cmd->stop_arg = 1;
|
|
err++;
|
|
}
|
|
} else { /* TRIG_NONE */
|
|
if (cmd->stop_arg != 0) {
|
|
cmd->stop_arg = 0;
|
|
err++;
|
|
}
|
|
}
|
|
|
|
if (!cmd->chanlist_len) {
|
|
cmd->chanlist_len = 1;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->chanlist_len > this_board->n_aichanlist) {
|
|
cmd->chanlist_len = this_board->n_aichanlist;
|
|
err++;
|
|
}
|
|
|
|
if (cmd->scan_end_arg < cmd->chanlist_len) {
|
|
cmd->scan_end_arg = cmd->chanlist_len;
|
|
err++;
|
|
}
|
|
|
|
if ((cmd->scan_end_arg % cmd->chanlist_len)) {
|
|
cmd->scan_end_arg =
|
|
cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len);
|
|
err++;
|
|
}
|
|
|
|
if (err)
|
|
return 3;
|
|
|
|
/* step 4: fix up any arguments */
|
|
|
|
if (cmd->scan_begin_src == TRIG_TIMER) {
|
|
tmp = cmd->scan_begin_arg;
|
|
/* printk("S1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
|
|
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
|
|
&divisor2, &cmd->scan_begin_arg,
|
|
cmd->flags & TRIG_ROUND_MASK);
|
|
/* printk("S2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
|
|
if (cmd->scan_begin_arg < this_board->ai_ns_min)
|
|
cmd->scan_begin_arg = this_board->ai_ns_min;
|
|
if (tmp != cmd->scan_begin_arg)
|
|
err++;
|
|
}
|
|
|
|
if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
|
|
tmp = cmd->convert_arg;
|
|
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
|
|
&divisor2, &cmd->convert_arg,
|
|
cmd->flags & TRIG_ROUND_MASK);
|
|
/* printk("s1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
|
|
if (cmd->convert_arg < this_board->ai_ns_min)
|
|
cmd->convert_arg = this_board->ai_ns_min;
|
|
if (tmp != cmd->convert_arg)
|
|
err++;
|
|
if (cmd->scan_begin_src == TRIG_TIMER
|
|
&& cmd->convert_src == TRIG_NOW) {
|
|
if (cmd->convert_arg == 0) {
|
|
if (cmd->scan_begin_arg <
|
|
this_board->ai_ns_min *
|
|
(cmd->scan_end_arg + 2)) {
|
|
cmd->scan_begin_arg =
|
|
this_board->ai_ns_min *
|
|
(cmd->scan_end_arg + 2);
|
|
/* printk("s2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
|
|
err++;
|
|
}
|
|
} else {
|
|
if (cmd->scan_begin_arg <
|
|
cmd->convert_arg * cmd->chanlist_len) {
|
|
cmd->scan_begin_arg =
|
|
cmd->convert_arg *
|
|
cmd->chanlist_len;
|
|
/* printk("s3 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
|
|
err++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
return 4;
|
|
|
|
if (cmd->chanlist)
|
|
if (!check_channel_list(dev, s, cmd->chanlist_len,
|
|
cmd->chanlist, 0, 0))
|
|
return 5; /* incorrect channels list */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int Compute_and_setup_dma(struct comedi_device *dev)
|
|
{
|
|
unsigned int dmalen0, dmalen1, i;
|
|
|
|
DPRINTK("adl_pci9118 EDBG: BGN: Compute_and_setup_dma()\n");
|
|
dmalen0 = devpriv->dmabuf_size[0];
|
|
dmalen1 = devpriv->dmabuf_size[1];
|
|
DPRINTK("1 dmalen0=%d dmalen1=%d ai_data_len=%d\n", dmalen0, dmalen1,
|
|
devpriv->ai_data_len);
|
|
/* isn't output buff smaller that our DMA buff? */
|
|
if (dmalen0 > (devpriv->ai_data_len)) {
|
|
dmalen0 = devpriv->ai_data_len & ~3L; /* allign to 32bit down */
|
|
}
|
|
if (dmalen1 > (devpriv->ai_data_len)) {
|
|
dmalen1 = devpriv->ai_data_len & ~3L; /* allign to 32bit down */
|
|
}
|
|
DPRINTK("2 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
|
|
|
|
/* we want wake up every scan? */
|
|
if (devpriv->ai_flags & TRIG_WAKE_EOS) {
|
|
if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) {
|
|
/* uff, too short DMA buffer, disable EOS support! */
|
|
devpriv->ai_flags &= (~TRIG_WAKE_EOS);
|
|
printk
|
|
("comedi%d: WAR: DMA0 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n",
|
|
dev->minor, dmalen0,
|
|
devpriv->ai_n_realscanlen << 1);
|
|
} else {
|
|
/* short first DMA buffer to one scan */
|
|
dmalen0 = devpriv->ai_n_realscanlen << 1;
|
|
DPRINTK
|
|
("21 dmalen0=%d ai_n_realscanlen=%d useeoshandle=%d\n",
|
|
dmalen0, devpriv->ai_n_realscanlen,
|
|
devpriv->useeoshandle);
|
|
if (devpriv->useeoshandle)
|
|
dmalen0 += 2;
|
|
if (dmalen0 < 4) {
|
|
printk
|
|
("comedi%d: ERR: DMA0 buf len bug? (%d<4)\n",
|
|
dev->minor, dmalen0);
|
|
dmalen0 = 4;
|
|
}
|
|
}
|
|
}
|
|
if (devpriv->ai_flags & TRIG_WAKE_EOS) {
|
|
if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) {
|
|
/* uff, too short DMA buffer, disable EOS support! */
|
|
devpriv->ai_flags &= (~TRIG_WAKE_EOS);
|
|
printk
|
|
("comedi%d: WAR: DMA1 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n",
|
|
dev->minor, dmalen1,
|
|
devpriv->ai_n_realscanlen << 1);
|
|
} else {
|
|
/* short second DMA buffer to one scan */
|
|
dmalen1 = devpriv->ai_n_realscanlen << 1;
|
|
DPRINTK
|
|
("22 dmalen1=%d ai_n_realscanlen=%d useeoshandle=%d\n",
|
|
dmalen1, devpriv->ai_n_realscanlen,
|
|
devpriv->useeoshandle);
|
|
if (devpriv->useeoshandle)
|
|
dmalen1 -= 2;
|
|
if (dmalen1 < 4) {
|
|
printk
|
|
("comedi%d: ERR: DMA1 buf len bug? (%d<4)\n",
|
|
dev->minor, dmalen1);
|
|
dmalen1 = 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPRINTK("3 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
|
|
/* transfer without TRIG_WAKE_EOS */
|
|
if (!(devpriv->ai_flags & TRIG_WAKE_EOS)) {
|
|
/* if it's possible then allign DMA buffers to length of scan */
|
|
i = dmalen0;
|
|
dmalen0 =
|
|
(dmalen0 / (devpriv->ai_n_realscanlen << 1)) *
|
|
(devpriv->ai_n_realscanlen << 1);
|
|
dmalen0 &= ~3L;
|
|
if (!dmalen0)
|
|
dmalen0 = i; /* uff. very long scan? */
|
|
i = dmalen1;
|
|
dmalen1 =
|
|
(dmalen1 / (devpriv->ai_n_realscanlen << 1)) *
|
|
(devpriv->ai_n_realscanlen << 1);
|
|
dmalen1 &= ~3L;
|
|
if (!dmalen1)
|
|
dmalen1 = i; /* uff. very long scan? */
|
|
/* if measure isn't neverending then test, if it whole fits into one or two DMA buffers */
|
|
if (!devpriv->ai_neverending) {
|
|
/* fits whole measure into one DMA buffer? */
|
|
if (dmalen0 >
|
|
((devpriv->ai_n_realscanlen << 1) *
|
|
devpriv->ai_scans)) {
|
|
DPRINTK
|
|
("3.0 ai_n_realscanlen=%d ai_scans=%d \n",
|
|
devpriv->ai_n_realscanlen,
|
|
devpriv->ai_scans);
|
|
dmalen0 =
|
|
(devpriv->ai_n_realscanlen << 1) *
|
|
devpriv->ai_scans;
|
|
DPRINTK("3.1 dmalen0=%d dmalen1=%d \n", dmalen0,
|
|
dmalen1);
|
|
dmalen0 &= ~3L;
|
|
} else { /* fits whole measure into two DMA buffer? */
|
|
if (dmalen1 >
|
|
((devpriv->ai_n_realscanlen << 1) *
|
|
devpriv->ai_scans - dmalen0))
|
|
dmalen1 =
|
|
(devpriv->ai_n_realscanlen << 1) *
|
|
devpriv->ai_scans - dmalen0;
|
|
DPRINTK("3.2 dmalen0=%d dmalen1=%d \n", dmalen0,
|
|
dmalen1);
|
|
dmalen1 &= ~3L;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPRINTK("4 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
|
|
|
|
/* these DMA buffer size we'll be used */
|
|
devpriv->dma_actbuf = 0;
|
|
devpriv->dmabuf_use_size[0] = dmalen0;
|
|
devpriv->dmabuf_use_size[1] = dmalen1;
|
|
|
|
DPRINTK("5 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
|
|
#if 0
|
|
if (devpriv->ai_n_scanlen < this_board->half_fifo_size) {
|
|
devpriv->dmabuf_panic_size[0] =
|
|
(this_board->half_fifo_size / devpriv->ai_n_scanlen +
|
|
1) * devpriv->ai_n_scanlen * sizeof(short);
|
|
devpriv->dmabuf_panic_size[1] =
|
|
(this_board->half_fifo_size / devpriv->ai_n_scanlen +
|
|
1) * devpriv->ai_n_scanlen * sizeof(short);
|
|
} else {
|
|
devpriv->dmabuf_panic_size[0] =
|
|
(devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[0];
|
|
devpriv->dmabuf_panic_size[1] =
|
|
(devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[1];
|
|
}
|
|
#endif
|
|
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */
|
|
outl(devpriv->dmabuf_hw[0], devpriv->iobase_a + AMCC_OP_REG_MWAR);
|
|
outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a + AMCC_OP_REG_MWTC);
|
|
/* 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); */
|
|
|
|
outl(inl(devpriv->iobase_a +
|
|
AMCC_OP_REG_MCSR) | RESET_A2P_FLAGS | A2P_HI_PRIORITY |
|
|
EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_MCSR);
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow bus mastering */
|
|
|
|
DPRINTK("adl_pci9118 EDBG: END: Compute_and_setup_dma()\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_docmd_sampl(struct comedi_device *dev,
|
|
struct comedi_subdevice *s)
|
|
{
|
|
DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_sampl(%d,) [%d]\n",
|
|
dev->minor, devpriv->ai_do);
|
|
switch (devpriv->ai_do) {
|
|
case 1:
|
|
devpriv->AdControlReg |= AdControl_TmrTr;
|
|
break;
|
|
case 2:
|
|
comedi_error(dev, "pci9118_ai_docmd_sampl() mode 2 bug!\n");
|
|
return -EIO;
|
|
case 3:
|
|
devpriv->AdControlReg |= AdControl_ExtM;
|
|
break;
|
|
case 4:
|
|
comedi_error(dev, "pci9118_ai_docmd_sampl() mode 4 bug!\n");
|
|
return -EIO;
|
|
default:
|
|
comedi_error(dev,
|
|
"pci9118_ai_docmd_sampl() mode number bug!\n");
|
|
return -EIO;
|
|
};
|
|
|
|
devpriv->int_ai_func = interrupt_pci9118_ai_onesample; /* transfer function */
|
|
|
|
if (devpriv->ai12_startstop)
|
|
pci9118_exttrg_add(dev, EXTTRG_AI); /* activate EXT trigger */
|
|
|
|
if ((devpriv->ai_do == 1) || (devpriv->ai_do == 2))
|
|
devpriv->IntControlReg |= Int_Timer;
|
|
|
|
devpriv->AdControlReg |= AdControl_Int;
|
|
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */
|
|
|
|
if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) {
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
if (devpriv->ai_do != 3) {
|
|
start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
|
|
devpriv->ai_divisor2);
|
|
devpriv->AdControlReg |= AdControl_SoftG;
|
|
}
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
}
|
|
|
|
DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_docmd_sampl()\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_docmd_dma(struct comedi_device *dev,
|
|
struct comedi_subdevice *s)
|
|
{
|
|
DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma(%d,) [%d,%d]\n",
|
|
dev->minor, devpriv->ai_do, devpriv->usedma);
|
|
Compute_and_setup_dma(dev);
|
|
|
|
switch (devpriv->ai_do) {
|
|
case 1:
|
|
devpriv->AdControlReg |=
|
|
((AdControl_TmrTr | AdControl_Dma) & 0xff);
|
|
break;
|
|
case 2:
|
|
devpriv->AdControlReg |=
|
|
((AdControl_TmrTr | AdControl_Dma) & 0xff);
|
|
devpriv->AdFunctionReg =
|
|
AdFunction_PDTrg | AdFunction_PETrg | AdFunction_BM |
|
|
AdFunction_BS;
|
|
if (devpriv->usessh && (!devpriv->softsshdelay))
|
|
devpriv->AdFunctionReg |= AdFunction_BSSH;
|
|
outl(devpriv->ai_n_realscanlen, dev->iobase + PCI9118_BURST);
|
|
break;
|
|
case 3:
|
|
devpriv->AdControlReg |=
|
|
((AdControl_ExtM | AdControl_Dma) & 0xff);
|
|
devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
|
|
break;
|
|
case 4:
|
|
devpriv->AdControlReg |=
|
|
((AdControl_TmrTr | AdControl_Dma) & 0xff);
|
|
devpriv->AdFunctionReg =
|
|
AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
outl(0x30, dev->iobase + PCI9118_CNTCTRL);
|
|
outl((devpriv->dmabuf_hw[0] >> 1) & 0xff,
|
|
dev->iobase + PCI9118_CNT0);
|
|
outl((devpriv->dmabuf_hw[0] >> 9) & 0xff,
|
|
dev->iobase + PCI9118_CNT0);
|
|
devpriv->AdFunctionReg |= AdFunction_Start;
|
|
break;
|
|
default:
|
|
comedi_error(dev, "pci9118_ai_docmd_dma() mode number bug!\n");
|
|
return -EIO;
|
|
};
|
|
|
|
if (devpriv->ai12_startstop) {
|
|
pci9118_exttrg_add(dev, EXTTRG_AI); /* activate EXT trigger */
|
|
}
|
|
|
|
devpriv->int_ai_func = interrupt_pci9118_ai_dma; /* transfer function */
|
|
|
|
outl(0x02000000 | AINT_WRITE_COMPL,
|
|
devpriv->iobase_a + AMCC_OP_REG_INTCSR);
|
|
|
|
if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) {
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
if (devpriv->ai_do != 3) {
|
|
start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
|
|
devpriv->ai_divisor2);
|
|
devpriv->AdControlReg |= AdControl_SoftG;
|
|
}
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
|
|
}
|
|
|
|
DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma()\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
{
|
|
struct comedi_cmd *cmd = &s->async->cmd;
|
|
unsigned int addchans = 0;
|
|
int ret = 0;
|
|
|
|
DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_cmd(%d,)\n", dev->minor);
|
|
devpriv->ai12_startstop = 0;
|
|
devpriv->ai_flags = cmd->flags;
|
|
devpriv->ai_n_chan = cmd->chanlist_len;
|
|
devpriv->ai_n_scanlen = cmd->scan_end_arg;
|
|
devpriv->ai_chanlist = cmd->chanlist;
|
|
devpriv->ai_data = s->async->prealloc_buf;
|
|
devpriv->ai_data_len = s->async->prealloc_bufsz;
|
|
devpriv->ai_timer1 = 0;
|
|
devpriv->ai_timer2 = 0;
|
|
devpriv->ai_add_front = 0;
|
|
devpriv->ai_add_back = 0;
|
|
devpriv->ai_maskerr = 0x10e;
|
|
|
|
/* 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->start_src == TRIG_INT) {
|
|
devpriv->ai12_startstop |= START_AI_INT;
|
|
devpriv->ai_inttrig_start = cmd->start_arg;
|
|
s->async->inttrig = pci9118_ai_inttrig;
|
|
}
|
|
#if 0
|
|
if (cmd->stop_src == TRIG_INT) {
|
|
devpriv->ai_neverending = 1;
|
|
devpriv->ai12_startstop |= STOP_AI_INT;
|
|
}
|
|
#endif
|
|
if (cmd->stop_src == TRIG_NONE)
|
|
devpriv->ai_neverending = 1;
|
|
if (cmd->stop_src == TRIG_COUNT) {
|
|
devpriv->ai_scans = cmd->stop_arg;
|
|
devpriv->ai_neverending = 0;
|
|
} else {
|
|
devpriv->ai_scans = 0;
|
|
}
|
|
|
|
/* use sample&hold signal? */
|
|
if (cmd->convert_src == TRIG_NOW) {
|
|
devpriv->usessh = 1;
|
|
} /* yes */
|
|
else {
|
|
devpriv->usessh = 0;
|
|
} /* no */
|
|
|
|
DPRINTK("1 neverending=%d scans=%u usessh=%d ai_startstop=0x%2x\n",
|
|
devpriv->ai_neverending, devpriv->ai_scans, devpriv->usessh,
|
|
devpriv->ai12_startstop);
|
|
|
|
/* use additional sample at end of every scan to satisty DMA 32 bit transfer? */
|
|
devpriv->ai_add_front = 0;
|
|
devpriv->ai_add_back = 0;
|
|
devpriv->useeoshandle = 0;
|
|
if (devpriv->master) {
|
|
devpriv->usedma = 1;
|
|
if ((cmd->flags & TRIG_WAKE_EOS) &&
|
|
(devpriv->ai_n_scanlen == 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 & TRIG_WAKE_EOS) &&
|
|
(devpriv->ai_n_scanlen & 1) &&
|
|
(devpriv->ai_n_scanlen > 1)) {
|
|
if (cmd->scan_begin_src == TRIG_FOLLOW) {
|
|
/* vpriv->useeoshandle=1; // change DMA transfer block to fit EOS on every second call */
|
|
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 add two samples before every scan as minimum */
|
|
if (devpriv->usessh && 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 < this_board->ai_ns_min)
|
|
cmd->convert_arg = this_board->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 +
|
|
devpriv->ai_n_chan +
|
|
devpriv->ai_add_back) & 1)
|
|
devpriv->ai_add_front++; /* round up to 32 bit */
|
|
}
|
|
}
|
|
/* well, we now know what must be all added */
|
|
devpriv->ai_n_realscanlen = /* what we must take from card in real to have ai_n_scanlen on output? */
|
|
(devpriv->ai_add_front + devpriv->ai_n_chan +
|
|
devpriv->ai_add_back) * (devpriv->ai_n_scanlen /
|
|
devpriv->ai_n_chan);
|
|
|
|
DPRINTK("2 usedma=%d realscan=%d af=%u n_chan=%d ab=%d n_scanlen=%d\n",
|
|
devpriv->usedma,
|
|
devpriv->ai_n_realscanlen, devpriv->ai_add_front,
|
|
devpriv->ai_n_chan, devpriv->ai_add_back,
|
|
devpriv->ai_n_scanlen);
|
|
|
|
/* check and setup channel list */
|
|
if (!check_channel_list(dev, s, devpriv->ai_n_chan,
|
|
devpriv->ai_chanlist, devpriv->ai_add_front,
|
|
devpriv->ai_add_back))
|
|
return -EINVAL;
|
|
if (!setup_channel_list(dev, s, devpriv->ai_n_chan,
|
|
devpriv->ai_chanlist, 0, devpriv->ai_add_front,
|
|
devpriv->ai_add_back, devpriv->usedma,
|
|
devpriv->useeoshandle))
|
|
return -EINVAL;
|
|
|
|
/* compute timers settings */
|
|
/* simplest way, fr=4Mhz/(tim1*tim2), channel manipulation without timers effect */
|
|
if (((cmd->scan_begin_src == TRIG_FOLLOW) || (cmd->scan_begin_src == TRIG_EXT) || (cmd->scan_begin_src == TRIG_INT)) && (cmd->convert_src == TRIG_TIMER)) { /* both timer is used for one time */
|
|
if (cmd->scan_begin_src == TRIG_EXT) {
|
|
devpriv->ai_do = 4;
|
|
} else {
|
|
devpriv->ai_do = 1;
|
|
}
|
|
pci9118_calc_divisors(devpriv->ai_do, dev, s,
|
|
&cmd->scan_begin_arg, &cmd->convert_arg,
|
|
devpriv->ai_flags,
|
|
devpriv->ai_n_realscanlen,
|
|
&devpriv->ai_divisor1,
|
|
&devpriv->ai_divisor2, devpriv->usessh,
|
|
devpriv->ai_add_front);
|
|
devpriv->ai_timer2 = cmd->convert_arg;
|
|
}
|
|
|
|
if ((cmd->scan_begin_src == TRIG_TIMER) && ((cmd->convert_src == TRIG_TIMER) || (cmd->convert_src == TRIG_NOW))) { /* double timed action */
|
|
if (!devpriv->usedma) {
|
|
comedi_error(dev,
|
|
"cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!");
|
|
return -EIO;
|
|
}
|
|
|
|
devpriv->ai_do = 2;
|
|
pci9118_calc_divisors(devpriv->ai_do, dev, s,
|
|
&cmd->scan_begin_arg, &cmd->convert_arg,
|
|
devpriv->ai_flags,
|
|
devpriv->ai_n_realscanlen,
|
|
&devpriv->ai_divisor1,
|
|
&devpriv->ai_divisor2, devpriv->usessh,
|
|
devpriv->ai_add_front);
|
|
devpriv->ai_timer1 = cmd->scan_begin_arg;
|
|
devpriv->ai_timer2 = cmd->convert_arg;
|
|
}
|
|
|
|
if ((cmd->scan_begin_src == TRIG_FOLLOW)
|
|
&& (cmd->convert_src == TRIG_EXT)) {
|
|
devpriv->ai_do = 3;
|
|
}
|
|
|
|
start_pacer(dev, -1, 0, 0); /* stop pacer */
|
|
|
|
devpriv->AdControlReg = 0; /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable DMA */
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
|
|
devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
|
|
udelay(1);
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
inl(dev->iobase + PCI9118_ADSTAT); /* flush A/D and INT status register */
|
|
inl(dev->iobase + PCI9118_INTSRC);
|
|
|
|
devpriv->ai_act_scan = 0;
|
|
devpriv->ai_act_dmapos = 0;
|
|
s->async->cur_chan = 0;
|
|
devpriv->ai_buf_ptr = 0;
|
|
|
|
if (devpriv->usedma)
|
|
ret = pci9118_ai_docmd_dma(dev, s);
|
|
else
|
|
ret = pci9118_ai_docmd_sampl(dev, s);
|
|
|
|
DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_cmd()\n");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int check_channel_list(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, int n_chan,
|
|
unsigned int *chanlist, int frontadd, int backadd)
|
|
{
|
|
unsigned int i, differencial = 0, bipolar = 0;
|
|
|
|
/* correct channel and range number check itself comedi/range.c */
|
|
if (n_chan < 1) {
|
|
comedi_error(dev, "range/channel list is empty!");
|
|
return 0;
|
|
}
|
|
if ((frontadd + n_chan + backadd) > s->len_chanlist) {
|
|
printk
|
|
("comedi%d: range/channel list is too long for actual configuration (%d>%d)!",
|
|
dev->minor, n_chan, s->len_chanlist - frontadd - backadd);
|
|
return 0;
|
|
}
|
|
|
|
if (CR_AREF(chanlist[0]) == AREF_DIFF)
|
|
differencial = 1; /* all input must be diff */
|
|
if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
|
|
bipolar = 1; /* all input must be bipolar */
|
|
if (n_chan > 1)
|
|
for (i = 1; i < n_chan; i++) { /* check S.E/diff */
|
|
if ((CR_AREF(chanlist[i]) == AREF_DIFF) !=
|
|
(differencial)) {
|
|
comedi_error(dev,
|
|
"Differencial and single ended inputs cann't be mixtured!");
|
|
return 0;
|
|
}
|
|
if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) !=
|
|
(bipolar)) {
|
|
comedi_error(dev,
|
|
"Bipolar and unipolar ranges cann't be mixtured!");
|
|
return 0;
|
|
}
|
|
if ((!devpriv->usemux) & (differencial) &
|
|
(CR_CHAN(chanlist[i]) >= this_board->n_aichand)) {
|
|
comedi_error(dev,
|
|
"If AREF_DIFF is used then is available only first 8 channels!");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int setup_channel_list(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, int n_chan,
|
|
unsigned int *chanlist, int rot, int frontadd,
|
|
int backadd, int usedma, char useeos)
|
|
{
|
|
unsigned int i, differencial = 0, bipolar = 0;
|
|
unsigned int scanquad, gain, ssh = 0x00;
|
|
|
|
DPRINTK
|
|
("adl_pci9118 EDBG: BGN: setup_channel_list(%d,.,%d,.,%d,%d,%d,%d)\n",
|
|
dev->minor, n_chan, rot, frontadd, backadd, usedma);
|
|
|
|
if (usedma == 1) {
|
|
rot = 8;
|
|
usedma = 0;
|
|
}
|
|
|
|
if (CR_AREF(chanlist[0]) == AREF_DIFF)
|
|
differencial = 1; /* all input must be diff */
|
|
if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
|
|
bipolar = 1; /* all input must be bipolar */
|
|
|
|
/* All is ok, so we can setup channel/range list */
|
|
|
|
if (!bipolar) {
|
|
devpriv->AdControlReg |= AdControl_UniP; /* set unibipolar */
|
|
} else {
|
|
devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff); /* enable bipolar */
|
|
}
|
|
|
|
if (differencial) {
|
|
devpriv->AdControlReg |= AdControl_Diff; /* enable diff inputs */
|
|
} else {
|
|
devpriv->AdControlReg &= ((~AdControl_Diff) & 0xff); /* set single ended inputs */
|
|
}
|
|
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* setup mode */
|
|
|
|
outl(2, dev->iobase + PCI9118_SCANMOD); /* gods know why this sequence! */
|
|
outl(0, dev->iobase + PCI9118_SCANMOD);
|
|
outl(1, dev->iobase + PCI9118_SCANMOD);
|
|
|
|
#ifdef PCI9118_PARANOIDCHECK
|
|
devpriv->chanlistlen = n_chan;
|
|
for (i = 0; i < (PCI9118_CHANLEN + 1); i++)
|
|
devpriv->chanlist[i] = 0x55aa;
|
|
#endif
|
|
|
|
if (frontadd) { /* insert channels for S&H */
|
|
ssh = devpriv->softsshsample;
|
|
DPRINTK("FA: %04x: ", ssh);
|
|
for (i = 0; i < frontadd; i++) { /* store range list to card */
|
|
scanquad = CR_CHAN(chanlist[0]); /* get channel number; */
|
|
gain = CR_RANGE(chanlist[0]); /* get gain number */
|
|
scanquad |= ((gain & 0x03) << 8);
|
|
outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
|
|
DPRINTK("%02x ", scanquad | ssh);
|
|
ssh = devpriv->softsshhold;
|
|
}
|
|
DPRINTK("\n ");
|
|
}
|
|
|
|
DPRINTK("SL: ", ssh);
|
|
for (i = 0; i < n_chan; i++) { /* store range list to card */
|
|
scanquad = CR_CHAN(chanlist[i]); /* get channel number; */
|
|
#ifdef PCI9118_PARANOIDCHECK
|
|
devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot;
|
|
#endif
|
|
gain = CR_RANGE(chanlist[i]); /* get gain number */
|
|
scanquad |= ((gain & 0x03) << 8);
|
|
outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
|
|
DPRINTK("%02x ", scanquad | ssh);
|
|
}
|
|
DPRINTK("\n ");
|
|
|
|
if (backadd) { /* insert channels for fit onto 32bit DMA */
|
|
DPRINTK("BA: %04x: ", ssh);
|
|
for (i = 0; i < backadd; i++) { /* store range list to card */
|
|
scanquad = CR_CHAN(chanlist[0]); /* get channel number; */
|
|
gain = CR_RANGE(chanlist[0]); /* get gain number */
|
|
scanquad |= ((gain & 0x03) << 8);
|
|
outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
|
|
DPRINTK("%02x ", scanquad | ssh);
|
|
}
|
|
DPRINTK("\n ");
|
|
}
|
|
#ifdef PCI9118_PARANOIDCHECK
|
|
devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma]; /* for 32bit oerations */
|
|
if (useeos) {
|
|
for (i = 1; i < n_chan; i++) { /* store range list to card */
|
|
devpriv->chanlist[(n_chan + i) ^ usedma] =
|
|
(CR_CHAN(chanlist[i]) & 0xf) << rot;
|
|
}
|
|
devpriv->chanlist[(2 * n_chan) ^ usedma] = devpriv->chanlist[0 ^ usedma]; /* for 32bit oerations */
|
|
useeos = 2;
|
|
} else {
|
|
useeos = 1;
|
|
}
|
|
#ifdef PCI9118_EXTDEBUG
|
|
DPRINTK("CHL: ");
|
|
for (i = 0; i <= (useeos * n_chan); i++) {
|
|
DPRINTK("%04x ", devpriv->chanlist[i]);
|
|
}
|
|
DPRINTK("\n ");
|
|
#endif
|
|
#endif
|
|
outl(0, dev->iobase + PCI9118_SCANMOD); /* close scan queue */
|
|
/* udelay(100); important delay, or first sample will be cripled */
|
|
|
|
DPRINTK("adl_pci9118 EDBG: END: setup_channel_list()\n");
|
|
return 1; /* we can serve this with scan logic */
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
calculate 8254 divisors if they are used for dual timing
|
|
*/
|
|
static void pci9118_calc_divisors(char mode, 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,
|
|
char usessh, unsigned int chnsshfront)
|
|
{
|
|
DPRINTK
|
|
("adl_pci9118 EDBG: BGN: pci9118_calc_divisors(%d,%d,.,%u,%u,%u,%d,.,.,,%u,%u)\n",
|
|
mode, dev->minor, *tim1, *tim2, flags, chans, usessh, chnsshfront);
|
|
switch (mode) {
|
|
case 1:
|
|
case 4:
|
|
if (*tim2 < this_board->ai_ns_min)
|
|
*tim2 = this_board->ai_ns_min;
|
|
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2,
|
|
tim2, flags & TRIG_ROUND_NEAREST);
|
|
DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u\n",
|
|
devpriv->i8254_osc_base, *div1, *div2, *tim1);
|
|
break;
|
|
case 2:
|
|
if (*tim2 < this_board->ai_ns_min)
|
|
*tim2 = this_board->ai_ns_min;
|
|
DPRINTK("1 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
*div1 = *tim2 / devpriv->i8254_osc_base; /* convert timer (burst) */
|
|
DPRINTK("2 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
if (*div1 < this_board->ai_pacer_min)
|
|
*div1 = this_board->ai_pacer_min;
|
|
DPRINTK("3 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
*div2 = *tim1 / devpriv->i8254_osc_base; /* scan timer */
|
|
DPRINTK("4 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
*div2 = *div2 / *div1; /* major timer is c1*c2 */
|
|
DPRINTK("5 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
if (*div2 < chans)
|
|
*div2 = chans;
|
|
DPRINTK("6 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
|
|
*tim2 = *div1 * devpriv->i8254_osc_base; /* real convert timer */
|
|
|
|
if (usessh & (chnsshfront == 0)) /* use BSSH signal */
|
|
if (*div2 < (chans + 2))
|
|
*div2 = chans + 2;
|
|
|
|
DPRINTK("7 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
|
|
*tim1, *tim2);
|
|
*tim1 = *div1 * *div2 * devpriv->i8254_osc_base;
|
|
DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u timer2=%u\n",
|
|
devpriv->i8254_osc_base, *div1, *div2, *tim1, *tim2);
|
|
break;
|
|
}
|
|
DPRINTK("adl_pci9118 EDBG: END: pci9118_calc_divisors(%u,%u)\n",
|
|
*div1, *div2);
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static void start_pacer(struct comedi_device *dev, int mode,
|
|
unsigned int divisor1, unsigned int divisor2)
|
|
{
|
|
outl(0x74, dev->iobase + PCI9118_CNTCTRL);
|
|
outl(0xb4, dev->iobase + PCI9118_CNTCTRL);
|
|
/* outl(0x30, dev->iobase + PCI9118_CNTCTRL); */
|
|
udelay(1);
|
|
|
|
if ((mode == 1) || (mode == 2) || (mode == 4)) {
|
|
outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2);
|
|
outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2);
|
|
outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1);
|
|
outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_exttrg_add(struct comedi_device *dev, unsigned char source)
|
|
{
|
|
if (source > 3)
|
|
return -1; /* incorrect source */
|
|
devpriv->exttrg_users |= (1 << source);
|
|
devpriv->IntControlReg |= Int_DTrg;
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_exttrg_del(struct comedi_device *dev, unsigned char source)
|
|
{
|
|
if (source > 3)
|
|
return -1; /* incorrect source */
|
|
devpriv->exttrg_users &= ~(1 << source);
|
|
if (!devpriv->exttrg_users) { /* shutdown ext trg intterrupts */
|
|
devpriv->IntControlReg &= ~Int_DTrg;
|
|
if (!devpriv->IntControlReg) /* all IRQ disabled */
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) & (~0x00001f00), devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* disable int in AMCC */
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_ai_cancel(struct comedi_device *dev,
|
|
struct comedi_subdevice *s)
|
|
{
|
|
if (devpriv->usedma)
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */
|
|
pci9118_exttrg_del(dev, EXTTRG_AI);
|
|
start_pacer(dev, 0, 0, 0); /* stop 8254 counters */
|
|
devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */
|
|
devpriv->AdControlReg = 0x00;
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */
|
|
outl(0, dev->iobase + PCI9118_BURST);
|
|
outl(1, dev->iobase + PCI9118_SCANMOD);
|
|
outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
|
|
devpriv->ai_do = 0;
|
|
devpriv->usedma = 0;
|
|
|
|
devpriv->ai_act_scan = 0;
|
|
devpriv->ai_act_dmapos = 0;
|
|
s->async->cur_chan = 0;
|
|
s->async->inttrig = NULL;
|
|
devpriv->ai_buf_ptr = 0;
|
|
devpriv->ai_neverending = 0;
|
|
devpriv->dma_actbuf = 0;
|
|
|
|
if (!devpriv->IntControlReg)
|
|
outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_reset(struct comedi_device *dev)
|
|
{
|
|
devpriv->IntControlReg = 0;
|
|
devpriv->exttrg_users = 0;
|
|
inl(dev->iobase + PCI9118_INTCTRL);
|
|
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); /* disable interrupts source */
|
|
outl(0x30, dev->iobase + PCI9118_CNTCTRL);
|
|
/* outl(0xb4, dev->iobase + PCI9118_CNTCTRL); */
|
|
start_pacer(dev, 0, 0, 0); /* stop 8254 counters */
|
|
devpriv->AdControlReg = 0;
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */
|
|
outl(0, dev->iobase + PCI9118_BURST);
|
|
outl(1, dev->iobase + PCI9118_SCANMOD);
|
|
outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */
|
|
devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
|
|
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */
|
|
|
|
devpriv->ao_data[0] = 2047;
|
|
devpriv->ao_data[1] = 2047;
|
|
outl(devpriv->ao_data[0], dev->iobase + PCI9118_DA1); /* reset A/D outs to 0V */
|
|
outl(devpriv->ao_data[1], dev->iobase + PCI9118_DA2);
|
|
outl(0, dev->iobase + PCI9118_DO); /* reset digi outs to L */
|
|
udelay(10);
|
|
inl(dev->iobase + PCI9118_AD_DATA);
|
|
outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
|
|
outl(0, dev->iobase + PCI9118_INTSRC); /* remove INT requests */
|
|
inl(dev->iobase + PCI9118_ADSTAT); /* flush A/D status register */
|
|
inl(dev->iobase + PCI9118_INTSRC); /* flush INT requests */
|
|
devpriv->AdControlReg = 0;
|
|
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */
|
|
|
|
devpriv->cnt0_users = 0;
|
|
devpriv->exttrg_users = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_attach(struct comedi_device *dev,
|
|
struct comedi_devconfig *it)
|
|
{
|
|
struct comedi_subdevice *s;
|
|
int ret, pages, i;
|
|
unsigned short master;
|
|
unsigned int irq;
|
|
unsigned long iobase_a, iobase_9;
|
|
struct pci_dev *pcidev;
|
|
int opt_bus, opt_slot;
|
|
const char *errstr;
|
|
unsigned char pci_bus, pci_slot, pci_func;
|
|
u16 u16w;
|
|
|
|
printk("comedi%d: adl_pci9118: board=%s", dev->minor, this_board->name);
|
|
|
|
opt_bus = it->options[0];
|
|
opt_slot = it->options[1];
|
|
if (it->options[3] & 1) {
|
|
master = 0; /* user don't want use bus master */
|
|
} else {
|
|
master = 1;
|
|
}
|
|
|
|
ret = alloc_private(dev, sizeof(struct pci9118_private));
|
|
if (ret < 0) {
|
|
printk(" - Allocation failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Look for matching PCI device */
|
|
errstr = "not found!";
|
|
pcidev = NULL;
|
|
while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_AMCC,
|
|
this_board->device_id,
|
|
pcidev))) {
|
|
/* Found matching vendor/device. */
|
|
if (opt_bus || opt_slot) {
|
|
/* Check bus/slot. */
|
|
if (opt_bus != pcidev->bus->number
|
|
|| opt_slot != PCI_SLOT(pcidev->devfn))
|
|
continue; /* no match */
|
|
}
|
|
/*
|
|
* Look for device that isn't in use.
|
|
* Enable PCI device and request regions.
|
|
*/
|
|
if (comedi_pci_enable(pcidev, "adl_pci9118")) {
|
|
errstr =
|
|
"failed to enable PCI device and request regions!";
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!pcidev) {
|
|
if (opt_bus || opt_slot) {
|
|
printk(" - Card at b:s %d:%d %s\n",
|
|
opt_bus, opt_slot, errstr);
|
|
} else {
|
|
printk(" - Card %s\n", errstr);
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
if (master) {
|
|
pci_set_master(pcidev);
|
|
}
|
|
|
|
pci_bus = pcidev->bus->number;
|
|
pci_slot = PCI_SLOT(pcidev->devfn);
|
|
pci_func = PCI_FUNC(pcidev->devfn);
|
|
irq = pcidev->irq;
|
|
iobase_a = pci_resource_start(pcidev, 0);
|
|
iobase_9 = pci_resource_start(pcidev, 2);
|
|
|
|
printk(", b:s:f=%d:%d:%d, io=0x%4lx, 0x%4lx", pci_bus, pci_slot,
|
|
pci_func, iobase_9, iobase_a);
|
|
|
|
dev->iobase = iobase_9;
|
|
dev->board_name = this_board->name;
|
|
|
|
devpriv->pcidev = pcidev;
|
|
devpriv->iobase_a = iobase_a;
|
|
|
|
pci9118_reset(dev);
|
|
|
|
if (it->options[3] & 2)
|
|
irq = 0; /* user don't want use IRQ */
|
|
if (irq > 0) {
|
|
if (request_irq(irq, interrupt_pci9118, IRQF_SHARED,
|
|
"ADLink PCI-9118", dev)) {
|
|
printk(", unable to allocate IRQ %d, DISABLING IT",
|
|
irq);
|
|
irq = 0; /* Can't use IRQ */
|
|
} else {
|
|
printk(", irq=%u", irq);
|
|
}
|
|
} else {
|
|
printk(", IRQ disabled");
|
|
}
|
|
|
|
dev->irq = irq;
|
|
|
|
if (master) { /* alloc DMA buffers */
|
|
devpriv->dma_doublebuf = 0;
|
|
for (i = 0; i < 2; i++) {
|
|
for (pages = 4; pages >= 0; pages--) {
|
|
devpriv->dmabuf_virt[i] =
|
|
(short *)__get_free_pages(GFP_KERNEL,
|
|
pages);
|
|
if (devpriv->dmabuf_virt[i])
|
|
break;
|
|
}
|
|
if (devpriv->dmabuf_virt[i]) {
|
|
devpriv->dmabuf_pages[i] = pages;
|
|
devpriv->dmabuf_size[i] = PAGE_SIZE * pages;
|
|
devpriv->dmabuf_samples[i] =
|
|
devpriv->dmabuf_size[i] >> 1;
|
|
devpriv->dmabuf_hw[i] =
|
|
virt_to_bus((void *)
|
|
devpriv->dmabuf_virt[i]);
|
|
}
|
|
}
|
|
if (!devpriv->dmabuf_virt[0]) {
|
|
printk(", Can't allocate DMA buffer, DMA disabled!");
|
|
master = 0;
|
|
}
|
|
|
|
if (devpriv->dmabuf_virt[1])
|
|
devpriv->dma_doublebuf = 1;
|
|
|
|
}
|
|
|
|
devpriv->master = master;
|
|
if (devpriv->master)
|
|
printk(", bus master");
|
|
else
|
|
printk(", no bus master");
|
|
|
|
devpriv->usemux = 0;
|
|
if (it->options[2] > 0) {
|
|
devpriv->usemux = it->options[2];
|
|
if (devpriv->usemux > 256)
|
|
devpriv->usemux = 256; /* max 256 channels! */
|
|
if (it->options[4] > 0)
|
|
if (devpriv->usemux > 128) {
|
|
devpriv->usemux = 128; /* max 128 channels with softare S&H! */
|
|
}
|
|
printk(", ext. mux %d channels", devpriv->usemux);
|
|
}
|
|
|
|
devpriv->softsshdelay = it->options[4];
|
|
if (devpriv->softsshdelay < 0) { /* select sample&hold signal polarity */
|
|
devpriv->softsshdelay = -devpriv->softsshdelay;
|
|
devpriv->softsshsample = 0x80;
|
|
devpriv->softsshhold = 0x00;
|
|
} else {
|
|
devpriv->softsshsample = 0x00;
|
|
devpriv->softsshhold = 0x80;
|
|
}
|
|
|
|
printk(".\n");
|
|
|
|
pci_read_config_word(devpriv->pcidev, PCI_COMMAND, &u16w);
|
|
pci_write_config_word(devpriv->pcidev, PCI_COMMAND, u16w | 64); /* Enable parity check for parity error */
|
|
|
|
ret = alloc_subdevices(dev, 4);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s = dev->subdevices + 0;
|
|
dev->read_subdev = s;
|
|
s->type = COMEDI_SUBD_AI;
|
|
s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
|
|
if (devpriv->usemux) {
|
|
s->n_chan = devpriv->usemux;
|
|
} else {
|
|
s->n_chan = this_board->n_aichan;
|
|
}
|
|
s->maxdata = this_board->ai_maxdata;
|
|
s->len_chanlist = this_board->n_aichanlist;
|
|
s->range_table = this_board->rangelist_ai;
|
|
s->cancel = pci9118_ai_cancel;
|
|
s->insn_read = pci9118_insn_read_ai;
|
|
if (dev->irq) {
|
|
s->subdev_flags |= SDF_CMD_READ;
|
|
s->do_cmdtest = pci9118_ai_cmdtest;
|
|
s->do_cmd = pci9118_ai_cmd;
|
|
s->munge = pci9118_ai_munge;
|
|
}
|
|
|
|
s = dev->subdevices + 1;
|
|
s->type = COMEDI_SUBD_AO;
|
|
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
|
|
s->n_chan = this_board->n_aochan;
|
|
s->maxdata = this_board->ao_maxdata;
|
|
s->len_chanlist = this_board->n_aochan;
|
|
s->range_table = this_board->rangelist_ao;
|
|
s->insn_write = pci9118_insn_write_ao;
|
|
s->insn_read = pci9118_insn_read_ao;
|
|
|
|
s = dev->subdevices + 2;
|
|
s->type = COMEDI_SUBD_DI;
|
|
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
|
|
s->n_chan = 4;
|
|
s->maxdata = 1;
|
|
s->len_chanlist = 4;
|
|
s->range_table = &range_digital;
|
|
s->io_bits = 0; /* all bits input */
|
|
s->insn_bits = pci9118_insn_bits_di;
|
|
|
|
s = dev->subdevices + 3;
|
|
s->type = COMEDI_SUBD_DO;
|
|
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
|
|
s->n_chan = 4;
|
|
s->maxdata = 1;
|
|
s->len_chanlist = 4;
|
|
s->range_table = &range_digital;
|
|
s->io_bits = 0xf; /* all bits output */
|
|
s->insn_bits = pci9118_insn_bits_do;
|
|
|
|
devpriv->valid = 1;
|
|
devpriv->i8254_osc_base = 250; /* 250ns=4MHz */
|
|
devpriv->ai_maskharderr = 0x10a; /* default measure crash condition */
|
|
if (it->options[5]) /* disable some requested */
|
|
devpriv->ai_maskharderr &= ~it->options[5];
|
|
|
|
switch (this_board->ai_maxdata) {
|
|
case 0xffff:
|
|
devpriv->ai16bits = 1;
|
|
break;
|
|
default:
|
|
devpriv->ai16bits = 0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|
|
static int pci9118_detach(struct comedi_device *dev)
|
|
{
|
|
if (dev->private) {
|
|
if (devpriv->valid)
|
|
pci9118_reset(dev);
|
|
if (dev->irq)
|
|
free_irq(dev->irq, dev);
|
|
if (devpriv->pcidev) {
|
|
if (dev->iobase) {
|
|
comedi_pci_disable(devpriv->pcidev);
|
|
}
|
|
pci_dev_put(devpriv->pcidev);
|
|
}
|
|
if (devpriv->dmabuf_virt[0])
|
|
free_pages((unsigned long)devpriv->dmabuf_virt[0],
|
|
devpriv->dmabuf_pages[0]);
|
|
if (devpriv->dmabuf_virt[1])
|
|
free_pages((unsigned long)devpriv->dmabuf_virt[1],
|
|
devpriv->dmabuf_pages[1]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
*/
|