/drivers/staging/comedi/drivers/pcl816.c
https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t · C · 1360 lines · 979 code · 182 blank · 199 comment · 193 complexity · 4b1f4253d2f8b6f4748236fbfe47d5cc MD5 · raw file
- /*
- comedi/drivers/pcl816.c
- Author: Juan Grigera <juan@grigera.com.ar>
- based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
- hardware driver for Advantech cards:
- card: PCL-816, PCL814B
- driver: pcl816
- */
- /*
- Driver: pcl816
- Description: Advantech PCL-816 cards, PCL-814
- Author: Juan Grigera <juan@grigera.com.ar>
- Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
- Status: works
- Updated: Tue, 2 Apr 2002 23:15:21 -0800
- PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
- Differences are at resolution (16 vs 12 bits).
- The driver support AI command mode, other subdevices not written.
- Analog output and digital input and output are not supported.
- Configuration Options:
- [0] - IO Base
- [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
- [2] - DMA (0=disable, 1, 3)
- [3] - 0, 10=10MHz clock for 8254
- 1= 1MHz clock for 8254
- */
- #include "../comedidev.h"
- #include <linux/ioport.h>
- #include <linux/mc146818rtc.h>
- #include <linux/gfp.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <asm/dma.h>
- #include "8253.h"
- #define DEBUG(x) x
- /* boards constants */
- /* IO space len */
- #define PCLx1x_RANGE 16
- /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
- /* INTEL 8254 counters */
- #define PCL816_CTR0 4
- #define PCL816_CTR1 5
- #define PCL816_CTR2 6
- /* R: counter read-back register W: counter control */
- #define PCL816_CTRCTL 7
- /* R: A/D high byte W: A/D range control */
- #define PCL816_RANGE 9
- /* W: clear INT request */
- #define PCL816_CLRINT 10
- /* R: next mux scan channel W: mux scan channel & range control pointer */
- #define PCL816_MUX 11
- /* R/W: operation control register */
- #define PCL816_CONTROL 12
- /* R: return status byte W: set DMA/IRQ */
- #define PCL816_STATUS 13
- #define PCL816_STATUS_DRDY_MASK 0x80
- /* R: low byte of A/D W: soft A/D trigger */
- #define PCL816_AD_LO 8
- /* R: high byte of A/D W: A/D range control */
- #define PCL816_AD_HI 9
- /* type of interrupt handler */
- #define INT_TYPE_AI1_INT 1
- #define INT_TYPE_AI1_DMA 2
- #define INT_TYPE_AI3_INT 4
- #define INT_TYPE_AI3_DMA 5
- #ifdef unused
- #define INT_TYPE_AI1_DMA_RTC 9
- #define INT_TYPE_AI3_DMA_RTC 10
- /* RTC stuff... */
- #define RTC_IRQ 8
- #define RTC_IO_EXTENT 0x10
- #endif
- #define MAGIC_DMA_WORD 0x5a5a
- static const struct comedi_lrange range_pcl816 = { 8, {
- BIP_RANGE(10),
- BIP_RANGE(5),
- BIP_RANGE(2.5),
- BIP_RANGE(1.25),
- UNI_RANGE(10),
- UNI_RANGE(5),
- UNI_RANGE(2.5),
- UNI_RANGE(1.25),
- }
- };
- struct pcl816_board {
- const char *name; /* board name */
- int n_ranges; /* len of range list */
- int n_aichan; /* num of A/D chans in diferencial mode */
- unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */
- int n_aochan; /* num of D/A chans */
- int n_dichan; /* num of DI chans */
- int n_dochan; /* num of DO chans */
- const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
- const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
- unsigned int io_range; /* len of IO space */
- unsigned int IRQbits; /* allowed interrupts */
- unsigned int DMAbits; /* allowed DMA chans */
- int ai_maxdata; /* maxdata for A/D */
- int ao_maxdata; /* maxdata for D/A */
- int ai_chanlist; /* allowed len of channel list A/D */
- int ao_chanlist; /* allowed len of channel list D/A */
- int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
- };
- static const struct pcl816_board boardtypes[] = {
- {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
- &range_pcl816, PCLx1x_RANGE,
- 0x00fc, /* IRQ mask */
- 0x0a, /* DMA mask */
- 0xffff, /* 16-bit card */
- 0xffff, /* D/A maxdata */
- 1024,
- 1, /* ao chan list */
- 100},
- {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
- &range_pcl816, PCLx1x_RANGE,
- 0x00fc,
- 0x0a,
- 0x3fff, /* 14 bit card */
- 0x3fff,
- 1024,
- 1,
- 100},
- };
- #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
- #define devpriv ((struct pcl816_private *)dev->private)
- #define this_board ((const struct pcl816_board *)dev->board_ptr)
- static int pcl816_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
- static int pcl816_detach(struct comedi_device *dev);
- #ifdef unused
- static int RTC_lock = 0; /* RTC lock */
- static int RTC_timer_lock = 0; /* RTC int lock */
- #endif
- static struct comedi_driver driver_pcl816 = {
- .driver_name = "pcl816",
- .module = THIS_MODULE,
- .attach = pcl816_attach,
- .detach = pcl816_detach,
- .board_name = &boardtypes[0].name,
- .num_names = n_boardtypes,
- .offset = sizeof(struct pcl816_board),
- };
- static int __init driver_pcl816_init_module(void)
- {
- return comedi_driver_register(&driver_pcl816);
- }
- static void __exit driver_pcl816_cleanup_module(void)
- {
- comedi_driver_unregister(&driver_pcl816);
- }
- module_init(driver_pcl816_init_module);
- module_exit(driver_pcl816_cleanup_module);
- struct pcl816_private {
- unsigned int dma; /* used DMA, 0=don't use DMA */
- int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
- #ifdef unused
- unsigned long rtc_iobase; /* RTC port region */
- unsigned int rtc_iosize;
- unsigned int rtc_irq;
- #endif
- unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
- unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
- unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
- unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
- unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
- unsigned int last_top_dma; /* DMA pointer in last RTC int */
- int next_dma_buf; /* which DMA buffer will be used next round */
- long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
- unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
- unsigned int ai_scans; /* len of scanlist */
- unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
- int irq_free; /* 1=have allocated IRQ */
- int irq_blocked; /* 1=IRQ now uses any subdev */
- #ifdef unused
- int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
- #endif
- int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
- int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
- struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
- int ai_act_scan; /* how many scans we finished */
- unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
- unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
- unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
- unsigned int ai_n_chan; /* how many channels per scan */
- unsigned int ai_poll_ptr; /* how many sampes transfer poll */
- struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
- #ifdef unused
- struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
- unsigned long rtc_freq; /* RTC int freq */
- #endif
- };
- /*
- ==============================================================================
- */
- static int check_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *chanlist, unsigned int chanlen);
- static void setup_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *chanlist, unsigned int seglen);
- static int pcl816_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s);
- static void start_pacer(struct comedi_device *dev, int mode,
- unsigned int divisor1, unsigned int divisor2);
- #ifdef unused
- static int set_rtc_irq_bit(unsigned char bit);
- #endif
- static int pcl816_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd);
- static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
- /*
- ==============================================================================
- ANALOG INPUT MODE0, 816 cards, slow version
- */
- static int pcl816_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
- {
- int n;
- int timeout;
- DPRINTK("mode 0 analog input\n");
- /* software trigger, DMA and INT off */
- outb(0, dev->iobase + PCL816_CONTROL);
- /* clear INT (conversion end) flag */
- outb(0, dev->iobase + PCL816_CLRINT);
- /* Set the input channel */
- outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
- /* select gain */
- outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
- for (n = 0; n < insn->n; n++) {
- outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
- timeout = 100;
- while (timeout--) {
- if (!(inb(dev->iobase + PCL816_STATUS) &
- PCL816_STATUS_DRDY_MASK)) {
- /* return read value */
- data[n] =
- ((inb(dev->iobase +
- PCL816_AD_HI) << 8) |
- (inb(dev->iobase + PCL816_AD_LO)));
- /* clear INT (conversion end) flag */
- outb(0, dev->iobase + PCL816_CLRINT);
- break;
- }
- udelay(1);
- }
- /* Return timeout error */
- if (!timeout) {
- comedi_error(dev, "A/D insn timeout\n");
- data[0] = 0;
- /* clear INT (conversion end) flag */
- outb(0, dev->iobase + PCL816_CLRINT);
- return -EIO;
- }
- }
- return n;
- }
- /*
- ==============================================================================
- analog input interrupt mode 1 & 3, 818 cards
- one sample per interrupt version
- */
- static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
- {
- struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
- int low, hi;
- int timeout = 50; /* wait max 50us */
- while (timeout--) {
- if (!(inb(dev->iobase + PCL816_STATUS) &
- PCL816_STATUS_DRDY_MASK))
- break;
- udelay(1);
- }
- if (!timeout) { /* timeout, bail error */
- outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
- comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
- pcl816_ai_cancel(dev, s);
- s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
- comedi_event(dev, s);
- return IRQ_HANDLED;
- }
- /* get the sample */
- low = inb(dev->iobase + PCL816_AD_LO);
- hi = inb(dev->iobase + PCL816_AD_HI);
- comedi_buf_put(s->async, (hi << 8) | low);
- outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
- if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
- devpriv->ai_act_chanlist_pos = 0;
- s->async->cur_chan++;
- if (s->async->cur_chan >= devpriv->ai_n_chan) {
- s->async->cur_chan = 0;
- devpriv->ai_act_scan++;
- }
- if (!devpriv->ai_neverending)
- /* all data sampled */
- if (devpriv->ai_act_scan >= devpriv->ai_scans) {
- /* all data sampled */
- pcl816_ai_cancel(dev, s);
- s->async->events |= COMEDI_CB_EOA;
- }
- comedi_event(dev, s);
- return IRQ_HANDLED;
- }
- /*
- ==============================================================================
- analog input dma mode 1 & 3, 816 cards
- */
- static void transfer_from_dma_buf(struct comedi_device *dev,
- struct comedi_subdevice *s, short *ptr,
- unsigned int bufptr, unsigned int len)
- {
- int i;
- s->async->events = 0;
- for (i = 0; i < len; i++) {
- comedi_buf_put(s->async, ptr[bufptr++]);
- if (++devpriv->ai_act_chanlist_pos >=
- devpriv->ai_act_chanlist_len) {
- devpriv->ai_act_chanlist_pos = 0;
- }
- s->async->cur_chan++;
- if (s->async->cur_chan >= devpriv->ai_n_chan) {
- s->async->cur_chan = 0;
- devpriv->ai_act_scan++;
- }
- if (!devpriv->ai_neverending)
- /* all data sampled */
- if (devpriv->ai_act_scan >= devpriv->ai_scans) {
- pcl816_ai_cancel(dev, s);
- s->async->events |= COMEDI_CB_EOA;
- s->async->events |= COMEDI_CB_BLOCK;
- break;
- }
- }
- comedi_event(dev, s);
- }
- static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
- {
- struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
- int len, bufptr, this_dma_buf;
- unsigned long dma_flags;
- short *ptr;
- disable_dma(devpriv->dma);
- this_dma_buf = devpriv->next_dma_buf;
- /* switch dma bufs */
- if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
- devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
- set_dma_mode(devpriv->dma, DMA_MODE_READ);
- dma_flags = claim_dma_lock();
- /* clear_dma_ff (devpriv->dma); */
- set_dma_addr(devpriv->dma,
- devpriv->hwdmaptr[devpriv->next_dma_buf]);
- if (devpriv->dma_runs_to_end) {
- set_dma_count(devpriv->dma,
- devpriv->hwdmasize[devpriv->
- next_dma_buf]);
- } else {
- set_dma_count(devpriv->dma, devpriv->last_dma_run);
- }
- release_dma_lock(dma_flags);
- enable_dma(devpriv->dma);
- }
- devpriv->dma_runs_to_end--;
- outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
- ptr = (short *)devpriv->dmabuf[this_dma_buf];
- len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
- bufptr = devpriv->ai_poll_ptr;
- devpriv->ai_poll_ptr = 0;
- transfer_from_dma_buf(dev, s, ptr, bufptr, len);
- return IRQ_HANDLED;
- }
- /*
- ==============================================================================
- INT procedure
- */
- static irqreturn_t interrupt_pcl816(int irq, void *d)
- {
- struct comedi_device *dev = d;
- DPRINTK("<I>");
- if (!dev->attached) {
- comedi_error(dev, "premature interrupt");
- return IRQ_HANDLED;
- }
- switch (devpriv->int816_mode) {
- case INT_TYPE_AI1_DMA:
- case INT_TYPE_AI3_DMA:
- return interrupt_pcl816_ai_mode13_dma(irq, d);
- case INT_TYPE_AI1_INT:
- case INT_TYPE_AI3_INT:
- return interrupt_pcl816_ai_mode13_int(irq, d);
- }
- outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
- if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
- (!devpriv->int816_mode)) {
- if (devpriv->irq_was_now_closed) {
- devpriv->irq_was_now_closed = 0;
- /* comedi_error(dev,"last IRQ.."); */
- return IRQ_HANDLED;
- }
- comedi_error(dev, "bad IRQ!");
- return IRQ_NONE;
- }
- comedi_error(dev, "IRQ from unknown source!");
- return IRQ_NONE;
- }
- /*
- ==============================================================================
- COMMAND MODE
- */
- static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
- {
- printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
- cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
- printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
- cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
- printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
- cmd->stop_src, cmd->scan_end_src);
- printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
- e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
- }
- /*
- ==============================================================================
- */
- static int pcl816_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_cmd *cmd)
- {
- int err = 0;
- int tmp, divisor1 = 0, divisor2 = 0;
- DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
- pcl816_cmdtest_out(-1, cmd);
- );
- /* step 1: make sure trigger sources are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
- 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;
- 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_NOW;
- err++;
- }
- if (cmd->scan_begin_src != TRIG_FOLLOW) {
- cmd->scan_begin_src = TRIG_FOLLOW;
- err++;
- }
- if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
- 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)
- err++;
- if (err)
- return 2;
- /* step 3: make sure arguments are trivially compatible */
- if (cmd->start_arg != 0) {
- cmd->start_arg = 0;
- err++;
- }
- if (cmd->scan_begin_arg != 0) {
- cmd->scan_begin_arg = 0;
- err++;
- }
- if (cmd->convert_src == TRIG_TIMER) {
- if (cmd->convert_arg < this_board->ai_ns_min) {
- cmd->convert_arg = this_board->ai_ns_min;
- err++;
- }
- } else { /* TRIG_EXT */
- if (cmd->convert_arg != 0) {
- cmd->convert_arg = 0;
- err++;
- }
- }
- if (cmd->scan_end_arg != cmd->chanlist_len) {
- cmd->scan_end_arg = cmd->chanlist_len;
- 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 (err)
- return 3;
- /* step 4: fix up any arguments */
- if (cmd->convert_src == TRIG_TIMER) {
- tmp = cmd->convert_arg;
- i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
- &divisor1, &divisor2,
- &cmd->convert_arg,
- cmd->flags & TRIG_ROUND_MASK);
- if (cmd->convert_arg < this_board->ai_ns_min)
- cmd->convert_arg = this_board->ai_ns_min;
- if (tmp != cmd->convert_arg)
- err++;
- }
- if (err)
- return 4;
- /* step 5: complain about special chanlist considerations */
- if (cmd->chanlist) {
- if (!check_channel_list(dev, s, cmd->chanlist,
- cmd->chanlist_len))
- return 5; /* incorrect channels list */
- }
- return 0;
- }
- static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
- {
- unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int seglen;
- if (cmd->start_src != TRIG_NOW)
- return -EINVAL;
- if (cmd->scan_begin_src != TRIG_FOLLOW)
- return -EINVAL;
- if (cmd->scan_end_src != TRIG_COUNT)
- return -EINVAL;
- if (cmd->scan_end_arg != cmd->chanlist_len)
- return -EINVAL;
- /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
- if (devpriv->irq_blocked)
- return -EBUSY;
- if (cmd->convert_src == TRIG_TIMER) {
- if (cmd->convert_arg < this_board->ai_ns_min)
- cmd->convert_arg = this_board->ai_ns_min;
- i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
- &divisor2, &cmd->convert_arg,
- cmd->flags & TRIG_ROUND_MASK);
- /* PCL816 crash if any divisor is set to 1 */
- if (divisor1 == 1) {
- divisor1 = 2;
- divisor2 /= 2;
- }
- if (divisor2 == 1) {
- divisor2 = 2;
- divisor1 /= 2;
- }
- }
- start_pacer(dev, -1, 0, 0); /* stop pacer */
- seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
- if (seglen < 1)
- return -EINVAL;
- setup_channel_list(dev, s, cmd->chanlist, seglen);
- udelay(1);
- devpriv->ai_n_chan = cmd->chanlist_len;
- devpriv->ai_act_scan = 0;
- s->async->cur_chan = 0;
- devpriv->irq_blocked = 1;
- devpriv->ai_poll_ptr = 0;
- devpriv->irq_was_now_closed = 0;
- if (cmd->stop_src == TRIG_COUNT) {
- devpriv->ai_scans = cmd->stop_arg;
- devpriv->ai_neverending = 0;
- } else {
- devpriv->ai_scans = 0;
- devpriv->ai_neverending = 1;
- }
- /* don't we want wake up every scan? */
- if ((cmd->flags & TRIG_WAKE_EOS)) {
- printk(KERN_INFO
- "pl816: You wankt WAKE_EOS but I dont want handle it");
- /* devpriv->ai_eos=1; */
- /* if (devpriv->ai_n_chan==1) */
- /* devpriv->dma=0; // DMA is useless for this situation */
- }
- if (devpriv->dma) {
- bytes = devpriv->hwdmasize[0];
- if (!devpriv->ai_neverending) {
- /* how many */
- bytes = s->async->cmd.chanlist_len *
- s->async->cmd.chanlist_len *
- sizeof(short);
- /* how many DMA pages we must fill */
- devpriv->dma_runs_to_end = bytes /
- devpriv->hwdmasize[0];
- /* on last dma transfer must be moved */
- devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
- devpriv->dma_runs_to_end--;
- if (devpriv->dma_runs_to_end >= 0)
- bytes = devpriv->hwdmasize[0];
- } else
- devpriv->dma_runs_to_end = -1;
- devpriv->next_dma_buf = 0;
- set_dma_mode(devpriv->dma, DMA_MODE_READ);
- dma_flags = claim_dma_lock();
- clear_dma_ff(devpriv->dma);
- set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
- set_dma_count(devpriv->dma, bytes);
- release_dma_lock(dma_flags);
- enable_dma(devpriv->dma);
- }
- start_pacer(dev, 1, divisor1, divisor2);
- dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
- switch (cmd->convert_src) {
- case TRIG_TIMER:
- devpriv->int816_mode = INT_TYPE_AI1_DMA;
- /* Pacer+IRQ+DMA */
- outb(0x32, dev->iobase + PCL816_CONTROL);
- /* write irq and DMA to card */
- outb(dmairq, dev->iobase + PCL816_STATUS);
- break;
- default:
- devpriv->int816_mode = INT_TYPE_AI3_DMA;
- /* Ext trig+IRQ+DMA */
- outb(0x34, dev->iobase + PCL816_CONTROL);
- /* write irq to card */
- outb(dmairq, dev->iobase + PCL816_STATUS);
- break;
- }
- DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
- return 0;
- }
- static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
- {
- unsigned long flags;
- unsigned int top1, top2, i;
- if (!devpriv->dma)
- return 0; /* poll is valid only for DMA transfer */
- spin_lock_irqsave(&dev->spinlock, flags);
- for (i = 0; i < 20; i++) {
- top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
- top2 = get_dma_residue(devpriv->dma);
- if (top1 == top2)
- break;
- }
- if (top1 != top2) {
- spin_unlock_irqrestore(&dev->spinlock, flags);
- return 0;
- }
- /* where is now DMA in buffer */
- top1 = devpriv->hwdmasize[0] - top1;
- top1 >>= 1; /* sample position */
- top2 = top1 - devpriv->ai_poll_ptr;
- if (top2 < 1) { /* no new samples */
- spin_unlock_irqrestore(&dev->spinlock, flags);
- return 0;
- }
- transfer_from_dma_buf(dev, s,
- (short *)devpriv->dmabuf[devpriv->next_dma_buf],
- devpriv->ai_poll_ptr, top2);
- devpriv->ai_poll_ptr = top1; /* new buffer position */
- spin_unlock_irqrestore(&dev->spinlock, flags);
- return s->async->buf_write_count - s->async->buf_read_count;
- }
- /*
- ==============================================================================
- cancel any mode 1-4 AI
- */
- static int pcl816_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
- {
- /* DEBUG(printk("pcl816_ai_cancel()\n");) */
- if (devpriv->irq_blocked > 0) {
- switch (devpriv->int816_mode) {
- #ifdef unused
- case INT_TYPE_AI1_DMA_RTC:
- case INT_TYPE_AI3_DMA_RTC:
- set_rtc_irq_bit(0); /* stop RTC */
- del_timer(&devpriv->rtc_irq_timer);
- #endif
- case INT_TYPE_AI1_DMA:
- case INT_TYPE_AI3_DMA:
- disable_dma(devpriv->dma);
- case INT_TYPE_AI1_INT:
- case INT_TYPE_AI3_INT:
- outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
- dev->iobase + PCL816_CONTROL); /* Stop A/D */
- udelay(1);
- outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
- /* Stop pacer */
- outb(0xb0, dev->iobase + PCL816_CTRCTL);
- outb(0x70, dev->iobase + PCL816_CTRCTL);
- outb(0, dev->iobase + PCL816_AD_LO);
- inb(dev->iobase + PCL816_AD_LO);
- inb(dev->iobase + PCL816_AD_HI);
- /* clear INT request */
- outb(0, dev->iobase + PCL816_CLRINT);
- /* Stop A/D */
- outb(0, dev->iobase + PCL816_CONTROL);
- devpriv->irq_blocked = 0;
- devpriv->irq_was_now_closed = devpriv->int816_mode;
- devpriv->int816_mode = 0;
- devpriv->last_int_sub = s;
- /* s->busy = 0; */
- break;
- }
- }
- DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
- return 0;
- }
- /*
- ==============================================================================
- chech for PCL816
- */
- static int pcl816_check(unsigned long iobase)
- {
- outb(0x00, iobase + PCL816_MUX);
- udelay(1);
- if (inb(iobase + PCL816_MUX) != 0x00)
- return 1; /* there isn't card */
- outb(0x55, iobase + PCL816_MUX);
- udelay(1);
- if (inb(iobase + PCL816_MUX) != 0x55)
- return 1; /* there isn't card */
- outb(0x00, iobase + PCL816_MUX);
- udelay(1);
- outb(0x18, iobase + PCL816_CONTROL);
- udelay(1);
- if (inb(iobase + PCL816_CONTROL) != 0x18)
- return 1; /* there isn't card */
- return 0; /* ok, card exist */
- }
- /*
- ==============================================================================
- reset whole PCL-816 cards
- */
- static void pcl816_reset(struct comedi_device *dev)
- {
- /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
- /* outb (0, dev->iobase + PCL818_DA_HI); */
- /* udelay (1); */
- /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
- /* outb (0, dev->iobase + PCL818_DO_LO); */
- /* udelay (1); */
- outb(0, dev->iobase + PCL816_CONTROL);
- outb(0, dev->iobase + PCL816_MUX);
- outb(0, dev->iobase + PCL816_CLRINT);
- outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
- outb(0x70, dev->iobase + PCL816_CTRCTL);
- outb(0x30, dev->iobase + PCL816_CTRCTL);
- outb(0, dev->iobase + PCL816_RANGE);
- }
- /*
- ==============================================================================
- Start/stop pacer onboard pacer
- */
- static void
- start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
- unsigned int divisor2)
- {
- outb(0x32, dev->iobase + PCL816_CTRCTL);
- outb(0xff, dev->iobase + PCL816_CTR0);
- outb(0x00, dev->iobase + PCL816_CTR0);
- udelay(1);
- /* set counter 2 as mode 3 */
- outb(0xb4, dev->iobase + PCL816_CTRCTL);
- /* set counter 1 as mode 3 */
- outb(0x74, dev->iobase + PCL816_CTRCTL);
- udelay(1);
- if (mode == 1) {
- DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
- divisor2);
- outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
- outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
- outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
- outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
- }
- /* clear pending interrupts (just in case) */
- /* outb(0, dev->iobase + PCL816_CLRINT); */
- }
- /*
- ==============================================================================
- Check if channel list from user is builded correctly
- If it's ok, then return non-zero length of repeated segment of channel list
- */
- static int
- check_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s, unsigned int *chanlist,
- unsigned int chanlen)
- {
- unsigned int chansegment[16];
- unsigned int i, nowmustbechan, seglen, segpos;
- /* correct channel and range number check itself comedi/range.c */
- if (chanlen < 1) {
- comedi_error(dev, "range/channel list is empty!");
- return 0;
- }
- if (chanlen > 1) {
- /* first channel is every time ok */
- chansegment[0] = chanlist[0];
- for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
- /* build part of chanlist */
- DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
- CR_CHAN(chanlist[i]),
- CR_RANGE(chanlist[i]));)
- /* we detect loop, this must by finish */
- if (chanlist[0] == chanlist[i])
- break;
- nowmustbechan =
- (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
- if (nowmustbechan != CR_CHAN(chanlist[i])) {
- /* channel list isn't continuous :-( */
- printk(KERN_WARNING
- "comedi%d: pcl816: channel list must "
- "be continuous! chanlist[%i]=%d but "
- "must be %d or %d!\n", dev->minor,
- i, CR_CHAN(chanlist[i]), nowmustbechan,
- CR_CHAN(chanlist[0]));
- return 0;
- }
- /* well, this is next correct channel in list */
- chansegment[i] = chanlist[i];
- }
- /* check whole chanlist */
- for (i = 0, segpos = 0; i < chanlen; i++) {
- DEBUG(printk("%d %d=%d %d\n",
- CR_CHAN(chansegment[i % seglen]),
- CR_RANGE(chansegment[i % seglen]),
- CR_CHAN(chanlist[i]),
- CR_RANGE(chanlist[i]));)
- if (chanlist[i] != chansegment[i % seglen]) {
- printk(KERN_WARNING
- "comedi%d: pcl816: bad channel or range"
- " number! chanlist[%i]=%d,%d,%d and not"
- " %d,%d,%d!\n", dev->minor, i,
- CR_CHAN(chansegment[i]),
- CR_RANGE(chansegment[i]),
- CR_AREF(chansegment[i]),
- CR_CHAN(chanlist[i % seglen]),
- CR_RANGE(chanlist[i % seglen]),
- CR_AREF(chansegment[i % seglen]));
- return 0; /* chan/gain list is strange */
- }
- }
- } else {
- seglen = 1;
- }
- return seglen; /* we can serve this with MUX logic */
- }
- /*
- ==============================================================================
- Program scan/gain logic with channel list.
- */
- static void
- setup_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s, unsigned int *chanlist,
- unsigned int seglen)
- {
- unsigned int i;
- devpriv->ai_act_chanlist_len = seglen;
- devpriv->ai_act_chanlist_pos = 0;
- for (i = 0; i < seglen; i++) { /* store range list to card */
- devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
- outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
- /* select gain */
- outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
- }
- udelay(1);
- /* select channel interval to scan */
- outb(devpriv->ai_act_chanlist[0] |
- (devpriv->ai_act_chanlist[seglen - 1] << 4),
- dev->iobase + PCL816_MUX);
- }
- #ifdef unused
- /*
- ==============================================================================
- Enable(1)/disable(0) periodic interrupts from RTC
- */
- static int set_rtc_irq_bit(unsigned char bit)
- {
- unsigned char val;
- unsigned long flags;
- if (bit == 1) {
- RTC_timer_lock++;
- if (RTC_timer_lock > 1)
- return 0;
- } else {
- RTC_timer_lock--;
- if (RTC_timer_lock < 0)
- RTC_timer_lock = 0;
- if (RTC_timer_lock > 0)
- return 0;
- }
- save_flags(flags);
- cli();
- val = CMOS_READ(RTC_CONTROL);
- if (bit)
- val |= RTC_PIE;
- else
- val &= ~RTC_PIE;
- CMOS_WRITE(val, RTC_CONTROL);
- CMOS_READ(RTC_INTR_FLAGS);
- restore_flags(flags);
- return 0;
- }
- #endif
- /*
- ==============================================================================
- Free any resources that we have claimed
- */
- static void free_resources(struct comedi_device *dev)
- {
- /* printk("free_resource()\n"); */
- if (dev->private) {
- pcl816_ai_cancel(dev, devpriv->sub_ai);
- pcl816_reset(dev);
- if (devpriv->dma)
- free_dma(devpriv->dma);
- if (devpriv->dmabuf[0])
- free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
- if (devpriv->dmabuf[1])
- free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
- #ifdef unused
- if (devpriv->rtc_irq)
- free_irq(devpriv->rtc_irq, dev);
- if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
- if (devpriv->rtc_iobase)
- release_region(devpriv->rtc_iobase,
- devpriv->rtc_iosize);
- }
- #endif
- }
- if (dev->irq)
- free_irq(dev->irq, dev);
- if (dev->iobase)
- release_region(dev->iobase, this_board->io_range);
- /* printk("free_resource() end\n"); */
- }
- /*
- ==============================================================================
- Initialization
- */
- static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
- {
- int ret;
- unsigned long iobase;
- unsigned int irq, dma;
- unsigned long pages;
- /* int i; */
- struct comedi_subdevice *s;
- /* claim our I/O space */
- iobase = it->options[0];
- printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
- this_board->name, iobase);
- if (!request_region(iobase, this_board->io_range, "pcl816")) {
- printk("I/O port conflict\n");
- return -EIO;
- }
- dev->iobase = iobase;
- if (pcl816_check(iobase)) {
- printk(KERN_ERR ", I cann't detect board. FAIL!\n");
- return -EIO;
- }
- ret = alloc_private(dev, sizeof(struct pcl816_private));
- if (ret < 0)
- return ret; /* Can't alloc mem */
- /* set up some name stuff */
- dev->board_name = this_board->name;
- /* grab our IRQ */
- irq = 0;
- if (this_board->IRQbits != 0) { /* board support IRQ */
- irq = it->options[1];
- if (irq) { /* we want to use IRQ */
- if (((1 << irq) & this_board->IRQbits) == 0) {
- printk
- (", IRQ %u is out of allowed range, "
- "DISABLING IT", irq);
- irq = 0; /* Bad IRQ */
- } else {
- if (request_irq
- (irq, interrupt_pcl816, 0, "pcl816", dev)) {
- printk
- (", unable to allocate IRQ %u, "
- "DISABLING IT", irq);
- irq = 0; /* Can't use IRQ */
- } else {
- printk(KERN_INFO ", irq=%u", irq);
- }
- }
- }
- }
- dev->irq = irq;
- if (irq) /* 1=we have allocated irq */
- devpriv->irq_free = 1;
- else
- devpriv->irq_free = 0;
- devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
- devpriv->int816_mode = 0; /* mode of irq */
- #ifdef unused
- /* grab RTC for DMA operations */
- devpriv->dma_rtc = 0;
- if (it->options[2] > 0) { /* we want to use DMA */
- if (RTC_lock == 0) {
- if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
- "pcl816 (RTC)"))
- goto no_rtc;
- }
- devpriv->rtc_iobase = RTC_PORT(0);
- devpriv->rtc_iosize = RTC_IO_EXTENT;
- RTC_lock++;
- #ifdef UNTESTED_CODE
- if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
- "pcl816 DMA (RTC)", dev)) {
- devpriv->dma_rtc = 1;
- devpriv->rtc_irq = RTC_IRQ;
- printk(", dma_irq=%u", devpriv->rtc_irq);
- } else {
- RTC_lock--;
- if (RTC_lock == 0) {
- if (devpriv->rtc_iobase)
- release_region(devpriv->rtc_iobase,
- devpriv->rtc_iosize);
- }
- devpriv->rtc_iobase = 0;
- devpriv->rtc_iosize = 0;
- }
- #else
- printk("pcl816: RTC code missing");
- #endif
- }
- no_rtc:
- #endif
- /* grab our DMA */
- dma = 0;
- devpriv->dma = dma;
- if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
- goto no_dma; /* if we haven't IRQ, we can't use DMA */
- if (this_board->DMAbits != 0) { /* board support DMA */
- dma = it->options[2];
- if (dma < 1)
- goto no_dma; /* DMA disabled */
- if (((1 << dma) & this_board->DMAbits) == 0) {
- printk(", DMA is out of allowed range, FAIL!\n");
- return -EINVAL; /* Bad DMA */
- }
- ret = request_dma(dma, "pcl816");
- if (ret) {
- printk(KERN_ERR
- ", unable to allocate DMA %u, FAIL!\n", dma);
- return -EBUSY; /* DMA isn't free */
- }
- devpriv->dma = dma;
- printk(KERN_INFO ", dma=%u", dma);
- pages = 2; /* we need 16KB */
- devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
- if (!devpriv->dmabuf[0]) {
- printk(", unable to allocate DMA buffer, FAIL!\n");
- /*
- * maybe experiment with try_to_free_pages()
- * will help ....
- */
- return -EBUSY; /* no buffer :-( */
- }
- devpriv->dmapages[0] = pages;
- devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
- devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
- /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
- if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
- devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
- if (!devpriv->dmabuf[1]) {
- printk(KERN_ERR
- ", unable to allocate DMA buffer, "
- "FAIL!\n");
- return -EBUSY;
- }
- devpriv->dmapages[1] = pages;
- devpriv->hwdmaptr[1] =
- virt_to_bus((void *)devpriv->dmabuf[1]);
- devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
- }
- }
- no_dma:
- /* if (this_board->n_aochan > 0)
- subdevs[1] = COMEDI_SUBD_AO;
- if (this_board->n_dichan > 0)
- subdevs[2] = COMEDI_SUBD_DI;
- if (this_board->n_dochan > 0)
- subdevs[3] = COMEDI_SUBD_DO;
- */
- ret = alloc_subdevices(dev, 1);
- if (ret < 0)
- return ret;
- s = dev->subdevices + 0;
- if (this_board->n_aichan > 0) {
- s->type = COMEDI_SUBD_AI;
- devpriv->sub_ai = s;
- dev->read_subdev = s;
- s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
- s->n_chan = this_board->n_aichan;
- s->subdev_flags |= SDF_DIFF;
- /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
- s->maxdata = this_board->ai_maxdata;
- s->len_chanlist = this_board->ai_chanlist;
- s->range_table = this_board->ai_range_type;
- s->cancel = pcl816_ai_cancel;
- s->do_cmdtest = pcl816_ai_cmdtest;
- s->do_cmd = pcl816_ai_cmd;
- s->poll = pcl816_ai_poll;
- s->insn_read = pcl816_ai_insn_read;
- } else {
- s->type = COMEDI_SUBD_UNUSED;
- }
- #if 0
- case COMEDI_SUBD_AO:
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
- s->n_chan = this_board->n_aochan;
- s->maxdata = this_board->ao_maxdata;
- s->len_chanlist = this_board->ao_chanlist;
- s->range_table = this_board->ao_range_type;
- break;
- case COMEDI_SUBD_DI:
- s->subdev_flags = SDF_READABLE;
- s->n_chan = this_board->n_dichan;
- s->maxdata = 1;
- s->len_chanlist = this_board->n_dichan;
- s->range_table = &range_digital;
- break;
- case COMEDI_SUBD_DO:
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = this_board->n_dochan;
- s->maxdata = 1;
- s->len_chanlist = this_board->n_dochan;
- s->range_table = &range_digital;
- break;
- #endif
- pcl816_reset(dev);
- printk("\n");
- return 0;
- }
- /*
- ==============================================================================
- Removes device
- */
- static int pcl816_detach(struct comedi_device *dev)
- {
- DEBUG(printk(KERN_INFO "comedi%d: pcl816: remove\n", dev->minor);)
- free_resources(dev);
- #ifdef unused
- if (devpriv->dma_rtc)
- RTC_lock--;
- #endif
- return 0;
- }
- MODULE_AUTHOR("Comedi http://www.comedi.org");
- MODULE_DESCRIPTION("Comedi low-level driver");
- MODULE_LICENSE("GPL");