Commit 3063d6de authored by Michal Dobes's avatar Michal Dobes Committed by Greg Kroah-Hartman

Staging: comedi: add adl_pci9118 driver

For ADLink cards:
	PCI-9118DG, PCI-9118HG, PCI-9118HR

From: Michal Dobes <dobes@tesnet.cz>
Cc: David Schleef <ds@schleef.org>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent d351ec84
/*
* 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 "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...) rt_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 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 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(comedi_device * dev, comedi_devconfig * it);
static int pci9118_detach(comedi_device * dev);
typedef struct {
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 comedi_lrange *rangelist_ai; // rangelist for A/D
const 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
} boardtype;
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 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(boardtype))
static 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(boardtype),
};
COMEDI_PCI_INITCLEANUP(driver_pci9118, pci9118_pci_table);
typedef struct {
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;
sampl_t *ai_data;
sampl_t 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
sampl_t *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) (comedi_device *, 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
} pci9118_private;
#define devpriv ((pci9118_private *)dev->private)
#define this_board ((boardtype *)dev->board_ptr)
/*
==============================================================================
*/
static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
int n_chan, unsigned int *chanlist, int frontadd, int backadd);
static int setup_channel_list(comedi_device * dev, comedi_subdevice * s,
int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd,
int usedma, char eoshandle);
static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1,
unsigned int divisor2);
static int pci9118_reset(comedi_device * dev);
static int pci9118_exttrg_add(comedi_device * dev, unsigned char source);
static int pci9118_exttrg_del(comedi_device * dev, unsigned char source);
static int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s);
static void pci9118_calc_divisors(char mode, comedi_device * dev,
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(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * 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 */
comedi_udelay(2);
timeout = 100;
while (timeout--) {
if (inl(dev->iobase + PCI9118_ADSTAT) & AdStatus_ADrdy)
goto conv_finish;
comedi_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(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * 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(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * 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(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
data[1] = inl(dev->iobase + PCI9118_DI) & 0xf;
return 2;
}
/*
==============================================================================
*/
static int pci9118_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * 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(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(comedi_device * dev,
comedi_subdevice * s, sampl_t * 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(comedi_device * dev,
comedi_subdevice * s, sampl_t * 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(sampl_t));
if (num_bytes < num_samples * sizeof(sampl_t))
return -1;
return 0;
}
/*
==============================================================================
*/
static char pci9118_decode_error_status(comedi_device * dev,
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(comedi_device * dev, comedi_subdevice * s,
void *data, unsigned int num_bytes, unsigned int start_chan_index)
{
unsigned int i, num_samples = num_bytes / sizeof(sampl_t);
sampl_t *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(comedi_device * dev,
comedi_subdevice * s, unsigned short int_adstat, unsigned int int_amcc,
unsigned short int_daq)
{
register sampl_t 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!
rt_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(comedi_device * dev, 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 PT_REGS_ARG)
{
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(comedi_device * dev, 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(comedi_device * dev, comedi_subdevice * s,
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;
// rt_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);
// rt_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);
// rt_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);
// rt_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;
// rt_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(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);
rt_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) {
rt_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);
rt_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) {
rt_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(sampl_t);
devpriv->dmabuf_panic_size[1] =
(this_board->half_fifo_size / devpriv->ai_n_scanlen +
1) * devpriv->ai_n_scanlen * sizeof(sampl_t);
} 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(comedi_device * dev, 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(comedi_device * dev, 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(comedi_device * dev, comedi_subdevice * s)
{
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);
comedi_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(comedi_device * dev, 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) {
rt_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(comedi_device * dev, 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
// comedi_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, comedi_device * dev,
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(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);
comedi_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(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(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(comedi_device * dev, 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(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
comedi_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(comedi_device * dev, comedi_devconfig * it)
{
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;
rt_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;
}
if ((ret = alloc_private(dev, sizeof(pci9118_private))) < 0) {
rt_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) {
rt_printk(" - Card at b:s %d:%d %s\n",
opt_bus, opt_slot, errstr);
} else {
rt_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);
rt_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 (comedi_request_irq(irq, interrupt_pci9118, IRQF_SHARED,
"ADLink PCI-9118", dev)) {
rt_printk(", unable to allocate IRQ %d, DISABLING IT",
irq);
irq = 0; /* Can't use IRQ */
} else {
rt_printk(", irq=%u", irq);
}
} else {
rt_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--)
if ((devpriv->dmabuf_virt[i] = (sampl_t *)
__get_free_pages(GFP_KERNEL,
pages)))
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]) {
rt_printk(", Can't allocate DMA buffer, DMA disabled!");
master = 0;
}
if (devpriv->dmabuf_virt[1])
devpriv->dma_doublebuf = 1;
}
if ((devpriv->master = master)) {
rt_printk(", bus master");
} else {
rt_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!
}
rt_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;
}
rt_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
if ((ret = alloc_subdevices(dev, 4)) < 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(comedi_device * dev)
{
if (dev->private) {
if (devpriv->valid)
pci9118_reset(dev);
if (dev->irq)
comedi_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;
}
/*
==============================================================================
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment