📄 pcl816.c
字号:
/* module/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.oDescription: Advantech PCL-816 cards, PCL-814Author: Juan Grigera <juan@grigera.com.ar>Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)Status: worksUpdated: Tue, 2 Apr 2002 23:15:21 -0800PCL 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 <linux/comedidev.h>#include <linux/ioport.h>#include <linux/mc146818rtc.h>#include <linux/delay.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#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#define MAGIC_DMA_WORD 0x5a5astatic 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), }};typedef struct { char *name; // driver 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 alllowed 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 comedi_lrange *ai_range_type; // default A/D rangelist comedi_lrange *ao_range_type; // dafault D/A rangelist 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}boardtype;static boardtype 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(boardtype))#define devpriv ((pcl816_private *)dev->private)#define this_board ((boardtype *)dev->board_ptr)static int pcl816_attach (comedi_device * dev, comedi_devconfig * it);static int pcl816_detach (comedi_device * dev);static int RTC_lock = 0; /* RTC lock */static int RTC_timer_lock = 0; /* RTC int lock */static comedi_driver driver_pcl816 = { driver_name: "pcl816", module: THIS_MODULE, attach: pcl816_attach, detach: pcl816_detach, board_name: boardtypes, num_names: n_boardtypes, offset: sizeof(boardtype),};COMEDI_INITCLEANUP(driver_pcl816);typedef struct{ int dma; // used DMA, 0=don't use DMA int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc unsigned int rtc_iobase; // RTC port region unsigned int rtc_iosize; unsigned int rtc_irq; 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 int rtc_irq_blocked; // 1=we now do AI with DMA&RTC 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 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_poll_ptr; // how many sampes transfer poll comedi_subdevice *sub_ai; // ptr to AI subdevice struct timer_list rtc_irq_timer; // timer for RTC sanity check unsigned long rtc_freq; // RTC int freq} pcl816_private;/* ==============================================================================*/static int check_and_setup_channel_list (comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, int chanlen);static int pcl816_ai_cancel (comedi_device * dev, comedi_subdevice * s);static void start_pacer (comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2);static int set_rtc_irq_bit (unsigned char bit);static int pcl816_ai_cmdtest(comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd);static int pcl816_ai_cmd(comedi_device *dev, comedi_subdevice *s);/* ============================================================================== ANALOG INPUT MODE0, 816 cards, slow version*/static int pcl816_ai_insn_read(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *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); outb (CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */ 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))); outb (0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */ break; } comedi_udelay (1); } // Return timeout error if(!timeout) { comedi_error (dev, "A/D insn timeout\n"); data[0] = 0; outb (0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */ return -EIO; } } return n;}/*============================================================================== analog input interrupt mode 1 & 3, 818 cards one sample per interrupt version */static voidinterrupt_pcl816_ai_mode13_int (int irq, void *d, struct pt_regs *regs){ comedi_device *dev = d; 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; comedi_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, s->async->events); return; } // 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; if (s->async->cur_chan == 0){ devpriv->ai_act_scan++; } if (!devpriv->ai_neverending) if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ /* all data sampled */ pcl816_ai_cancel (dev, s); s->async->events |= COMEDI_CB_EOA; return; } comedi_event(dev, s, s->async->events);}/*============================================================================== analog input dma mode 1 & 3, 816 cards*/static void transfer_from_dma_buf(comedi_device *dev,comedi_subdevice *s, sampl_t *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; devpriv->ai_act_scan++; } if (!devpriv->ai_neverending) if (devpriv->ai_act_scan >= devpriv->ai_scans) { // all data sampled pcl816_ai_cancel(dev,s); s->async->events |= COMEDI_CB_EOA; s->async->events |= COMEDI_CB_BLOCK; break; } } comedi_event(dev,s,s->async->events);}static void interrupt_pcl816_ai_mode13_dma(int irq, void *d, struct pt_regs *regs){ comedi_device *dev = d; comedi_subdevice *s = dev->subdevices + 0; int len, bufptr, this_dma_buf; unsigned long dma_flags; sampl_t *ptr; disable_dma (devpriv->dma); this_dma_buf = devpriv->next_dma_buf; if((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { // switch dma bufs 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 = (sampl_t *) 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);}/*============================================================================== INT procedure*/static voidinterrupt_pcl816 (int irq, void *d, struct pt_regs *regs){ comedi_device *dev = d; DPRINTK("<I>");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -