1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * pcl816.c 4*8ffdff6aSGreg Kroah-Hartman * Comedi driver for Advantech PCL-816 cards 5*8ffdff6aSGreg Kroah-Hartman * 6*8ffdff6aSGreg Kroah-Hartman * Author: Juan Grigera <juan@grigera.com.ar> 7*8ffdff6aSGreg Kroah-Hartman * based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812 8*8ffdff6aSGreg Kroah-Hartman */ 9*8ffdff6aSGreg Kroah-Hartman 10*8ffdff6aSGreg Kroah-Hartman /* 11*8ffdff6aSGreg Kroah-Hartman * Driver: pcl816 12*8ffdff6aSGreg Kroah-Hartman * Description: Advantech PCL-816 cards, PCL-814 13*8ffdff6aSGreg Kroah-Hartman * Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b) 14*8ffdff6aSGreg Kroah-Hartman * Author: Juan Grigera <juan@grigera.com.ar> 15*8ffdff6aSGreg Kroah-Hartman * Status: works 16*8ffdff6aSGreg Kroah-Hartman * Updated: Tue, 2 Apr 2002 23:15:21 -0800 17*8ffdff6aSGreg Kroah-Hartman * 18*8ffdff6aSGreg Kroah-Hartman * PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO. 19*8ffdff6aSGreg Kroah-Hartman * Differences are at resolution (16 vs 12 bits). 20*8ffdff6aSGreg Kroah-Hartman * 21*8ffdff6aSGreg Kroah-Hartman * The driver support AI command mode, other subdevices not written. 22*8ffdff6aSGreg Kroah-Hartman * 23*8ffdff6aSGreg Kroah-Hartman * Analog output and digital input and output are not supported. 24*8ffdff6aSGreg Kroah-Hartman * 25*8ffdff6aSGreg Kroah-Hartman * Configuration Options: 26*8ffdff6aSGreg Kroah-Hartman * [0] - IO Base 27*8ffdff6aSGreg Kroah-Hartman * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) 28*8ffdff6aSGreg Kroah-Hartman * [2] - DMA (0=disable, 1, 3) 29*8ffdff6aSGreg Kroah-Hartman * [3] - 0, 10=10MHz clock for 8254 30*8ffdff6aSGreg Kroah-Hartman * 1= 1MHz clock for 8254 31*8ffdff6aSGreg Kroah-Hartman */ 32*8ffdff6aSGreg Kroah-Hartman 33*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 34*8ffdff6aSGreg Kroah-Hartman #include <linux/gfp.h> 35*8ffdff6aSGreg Kroah-Hartman #include <linux/delay.h> 36*8ffdff6aSGreg Kroah-Hartman #include <linux/io.h> 37*8ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 38*8ffdff6aSGreg Kroah-Hartman 39*8ffdff6aSGreg Kroah-Hartman #include "../comedidev.h" 40*8ffdff6aSGreg Kroah-Hartman 41*8ffdff6aSGreg Kroah-Hartman #include "comedi_isadma.h" 42*8ffdff6aSGreg Kroah-Hartman #include "comedi_8254.h" 43*8ffdff6aSGreg Kroah-Hartman 44*8ffdff6aSGreg Kroah-Hartman /* 45*8ffdff6aSGreg Kroah-Hartman * Register I/O map 46*8ffdff6aSGreg Kroah-Hartman */ 47*8ffdff6aSGreg Kroah-Hartman #define PCL816_DO_DI_LSB_REG 0x00 48*8ffdff6aSGreg Kroah-Hartman #define PCL816_DO_DI_MSB_REG 0x01 49*8ffdff6aSGreg Kroah-Hartman #define PCL816_TIMER_BASE 0x04 50*8ffdff6aSGreg Kroah-Hartman #define PCL816_AI_LSB_REG 0x08 51*8ffdff6aSGreg Kroah-Hartman #define PCL816_AI_MSB_REG 0x09 52*8ffdff6aSGreg Kroah-Hartman #define PCL816_RANGE_REG 0x09 53*8ffdff6aSGreg Kroah-Hartman #define PCL816_CLRINT_REG 0x0a 54*8ffdff6aSGreg Kroah-Hartman #define PCL816_MUX_REG 0x0b 55*8ffdff6aSGreg Kroah-Hartman #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first)) 56*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_REG 0x0c 57*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_SOFT_TRIG BIT(0) 58*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_PACER_TRIG BIT(1) 59*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_EXT_TRIG BIT(2) 60*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_POE BIT(3) 61*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_DMAEN BIT(4) 62*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_INTEN BIT(5) 63*8ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_DMASRC_SLOT(x) (((x) & 0x3) << 6) 64*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_REG 0x0d 65*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0) 66*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_SLOT(x) (((x) & 0x3) << 4) 67*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_DMA PCL816_STATUS_INTSRC_SLOT(3) 68*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_MASK PCL816_STATUS_INTSRC_SLOT(3) 69*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTACT BIT(6) 70*8ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_DRDY BIT(7) 71*8ffdff6aSGreg Kroah-Hartman 72*8ffdff6aSGreg Kroah-Hartman #define MAGIC_DMA_WORD 0x5a5a 73*8ffdff6aSGreg Kroah-Hartman 74*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_pcl816 = { 75*8ffdff6aSGreg Kroah-Hartman 8, { 76*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 77*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), 78*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(2.5), 79*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(1.25), 80*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(10), 81*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(5), 82*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(2.5), 83*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(1.25) 84*8ffdff6aSGreg Kroah-Hartman } 85*8ffdff6aSGreg Kroah-Hartman }; 86*8ffdff6aSGreg Kroah-Hartman 87*8ffdff6aSGreg Kroah-Hartman struct pcl816_board { 88*8ffdff6aSGreg Kroah-Hartman const char *name; 89*8ffdff6aSGreg Kroah-Hartman int ai_maxdata; 90*8ffdff6aSGreg Kroah-Hartman int ai_chanlist; 91*8ffdff6aSGreg Kroah-Hartman }; 92*8ffdff6aSGreg Kroah-Hartman 93*8ffdff6aSGreg Kroah-Hartman static const struct pcl816_board boardtypes[] = { 94*8ffdff6aSGreg Kroah-Hartman { 95*8ffdff6aSGreg Kroah-Hartman .name = "pcl816", 96*8ffdff6aSGreg Kroah-Hartman .ai_maxdata = 0xffff, 97*8ffdff6aSGreg Kroah-Hartman .ai_chanlist = 1024, 98*8ffdff6aSGreg Kroah-Hartman }, { 99*8ffdff6aSGreg Kroah-Hartman .name = "pcl814b", 100*8ffdff6aSGreg Kroah-Hartman .ai_maxdata = 0x3fff, 101*8ffdff6aSGreg Kroah-Hartman .ai_chanlist = 1024, 102*8ffdff6aSGreg Kroah-Hartman }, 103*8ffdff6aSGreg Kroah-Hartman }; 104*8ffdff6aSGreg Kroah-Hartman 105*8ffdff6aSGreg Kroah-Hartman struct pcl816_private { 106*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma; 107*8ffdff6aSGreg Kroah-Hartman unsigned int ai_poll_ptr; /* how many sampes transfer poll */ 108*8ffdff6aSGreg Kroah-Hartman unsigned int ai_cmd_running:1; 109*8ffdff6aSGreg Kroah-Hartman unsigned int ai_cmd_canceled:1; 110*8ffdff6aSGreg Kroah-Hartman }; 111*8ffdff6aSGreg Kroah-Hartman 112*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_setup_dma(struct comedi_device *dev, 113*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 114*8ffdff6aSGreg Kroah-Hartman unsigned int unread_samples) 115*8ffdff6aSGreg Kroah-Hartman { 116*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 117*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 118*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; 119*8ffdff6aSGreg Kroah-Hartman unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize); 120*8ffdff6aSGreg Kroah-Hartman unsigned int nsamples; 121*8ffdff6aSGreg Kroah-Hartman 122*8ffdff6aSGreg Kroah-Hartman comedi_isadma_disable(dma->chan); 123*8ffdff6aSGreg Kroah-Hartman 124*8ffdff6aSGreg Kroah-Hartman /* 125*8ffdff6aSGreg Kroah-Hartman * Determine dma size based on the buffer maxsize plus the number of 126*8ffdff6aSGreg Kroah-Hartman * unread samples and the number of samples remaining in the command. 127*8ffdff6aSGreg Kroah-Hartman */ 128*8ffdff6aSGreg Kroah-Hartman nsamples = comedi_nsamples_left(s, max_samples + unread_samples); 129*8ffdff6aSGreg Kroah-Hartman if (nsamples > unread_samples) { 130*8ffdff6aSGreg Kroah-Hartman nsamples -= unread_samples; 131*8ffdff6aSGreg Kroah-Hartman desc->size = comedi_samples_to_bytes(s, nsamples); 132*8ffdff6aSGreg Kroah-Hartman comedi_isadma_program(desc); 133*8ffdff6aSGreg Kroah-Hartman } 134*8ffdff6aSGreg Kroah-Hartman } 135*8ffdff6aSGreg Kroah-Hartman 136*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_set_chan_range(struct comedi_device *dev, 137*8ffdff6aSGreg Kroah-Hartman unsigned int chan, 138*8ffdff6aSGreg Kroah-Hartman unsigned int range) 139*8ffdff6aSGreg Kroah-Hartman { 140*8ffdff6aSGreg Kroah-Hartman outb(chan, dev->iobase + PCL816_MUX_REG); 141*8ffdff6aSGreg Kroah-Hartman outb(range, dev->iobase + PCL816_RANGE_REG); 142*8ffdff6aSGreg Kroah-Hartman } 143*8ffdff6aSGreg Kroah-Hartman 144*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_set_chan_scan(struct comedi_device *dev, 145*8ffdff6aSGreg Kroah-Hartman unsigned int first_chan, 146*8ffdff6aSGreg Kroah-Hartman unsigned int last_chan) 147*8ffdff6aSGreg Kroah-Hartman { 148*8ffdff6aSGreg Kroah-Hartman outb(PCL816_MUX_SCAN(first_chan, last_chan), 149*8ffdff6aSGreg Kroah-Hartman dev->iobase + PCL816_MUX_REG); 150*8ffdff6aSGreg Kroah-Hartman } 151*8ffdff6aSGreg Kroah-Hartman 152*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_setup_chanlist(struct comedi_device *dev, 153*8ffdff6aSGreg Kroah-Hartman unsigned int *chanlist, 154*8ffdff6aSGreg Kroah-Hartman unsigned int seglen) 155*8ffdff6aSGreg Kroah-Hartman { 156*8ffdff6aSGreg Kroah-Hartman unsigned int first_chan = CR_CHAN(chanlist[0]); 157*8ffdff6aSGreg Kroah-Hartman unsigned int last_chan; 158*8ffdff6aSGreg Kroah-Hartman unsigned int range; 159*8ffdff6aSGreg Kroah-Hartman unsigned int i; 160*8ffdff6aSGreg Kroah-Hartman 161*8ffdff6aSGreg Kroah-Hartman /* store range list to card */ 162*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < seglen; i++) { 163*8ffdff6aSGreg Kroah-Hartman last_chan = CR_CHAN(chanlist[i]); 164*8ffdff6aSGreg Kroah-Hartman range = CR_RANGE(chanlist[i]); 165*8ffdff6aSGreg Kroah-Hartman 166*8ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, last_chan, range); 167*8ffdff6aSGreg Kroah-Hartman } 168*8ffdff6aSGreg Kroah-Hartman 169*8ffdff6aSGreg Kroah-Hartman udelay(1); 170*8ffdff6aSGreg Kroah-Hartman 171*8ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_scan(dev, first_chan, last_chan); 172*8ffdff6aSGreg Kroah-Hartman } 173*8ffdff6aSGreg Kroah-Hartman 174*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_clear_eoc(struct comedi_device *dev) 175*8ffdff6aSGreg Kroah-Hartman { 176*8ffdff6aSGreg Kroah-Hartman /* writing any value clears the interrupt request */ 177*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CLRINT_REG); 178*8ffdff6aSGreg Kroah-Hartman } 179*8ffdff6aSGreg Kroah-Hartman 180*8ffdff6aSGreg Kroah-Hartman static void pcl816_ai_soft_trig(struct comedi_device *dev) 181*8ffdff6aSGreg Kroah-Hartman { 182*8ffdff6aSGreg Kroah-Hartman /* writing any value triggers a software conversion */ 183*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_AI_LSB_REG); 184*8ffdff6aSGreg Kroah-Hartman } 185*8ffdff6aSGreg Kroah-Hartman 186*8ffdff6aSGreg Kroah-Hartman static unsigned int pcl816_ai_get_sample(struct comedi_device *dev, 187*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 188*8ffdff6aSGreg Kroah-Hartman { 189*8ffdff6aSGreg Kroah-Hartman unsigned int val; 190*8ffdff6aSGreg Kroah-Hartman 191*8ffdff6aSGreg Kroah-Hartman val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8; 192*8ffdff6aSGreg Kroah-Hartman val |= inb(dev->iobase + PCL816_AI_LSB_REG); 193*8ffdff6aSGreg Kroah-Hartman 194*8ffdff6aSGreg Kroah-Hartman return val & s->maxdata; 195*8ffdff6aSGreg Kroah-Hartman } 196*8ffdff6aSGreg Kroah-Hartman 197*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_eoc(struct comedi_device *dev, 198*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 199*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 200*8ffdff6aSGreg Kroah-Hartman unsigned long context) 201*8ffdff6aSGreg Kroah-Hartman { 202*8ffdff6aSGreg Kroah-Hartman unsigned int status; 203*8ffdff6aSGreg Kroah-Hartman 204*8ffdff6aSGreg Kroah-Hartman status = inb(dev->iobase + PCL816_STATUS_REG); 205*8ffdff6aSGreg Kroah-Hartman if ((status & PCL816_STATUS_DRDY) == 0) 206*8ffdff6aSGreg Kroah-Hartman return 0; 207*8ffdff6aSGreg Kroah-Hartman return -EBUSY; 208*8ffdff6aSGreg Kroah-Hartman } 209*8ffdff6aSGreg Kroah-Hartman 210*8ffdff6aSGreg Kroah-Hartman static bool pcl816_ai_next_chan(struct comedi_device *dev, 211*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 212*8ffdff6aSGreg Kroah-Hartman { 213*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 214*8ffdff6aSGreg Kroah-Hartman 215*8ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT && 216*8ffdff6aSGreg Kroah-Hartman s->async->scans_done >= cmd->stop_arg) { 217*8ffdff6aSGreg Kroah-Hartman s->async->events |= COMEDI_CB_EOA; 218*8ffdff6aSGreg Kroah-Hartman return false; 219*8ffdff6aSGreg Kroah-Hartman } 220*8ffdff6aSGreg Kroah-Hartman 221*8ffdff6aSGreg Kroah-Hartman return true; 222*8ffdff6aSGreg Kroah-Hartman } 223*8ffdff6aSGreg Kroah-Hartman 224*8ffdff6aSGreg Kroah-Hartman static void transfer_from_dma_buf(struct comedi_device *dev, 225*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 226*8ffdff6aSGreg Kroah-Hartman unsigned short *ptr, 227*8ffdff6aSGreg Kroah-Hartman unsigned int bufptr, unsigned int len) 228*8ffdff6aSGreg Kroah-Hartman { 229*8ffdff6aSGreg Kroah-Hartman unsigned short val; 230*8ffdff6aSGreg Kroah-Hartman int i; 231*8ffdff6aSGreg Kroah-Hartman 232*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < len; i++) { 233*8ffdff6aSGreg Kroah-Hartman val = ptr[bufptr++]; 234*8ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, &val, 1); 235*8ffdff6aSGreg Kroah-Hartman 236*8ffdff6aSGreg Kroah-Hartman if (!pcl816_ai_next_chan(dev, s)) 237*8ffdff6aSGreg Kroah-Hartman return; 238*8ffdff6aSGreg Kroah-Hartman } 239*8ffdff6aSGreg Kroah-Hartman } 240*8ffdff6aSGreg Kroah-Hartman 241*8ffdff6aSGreg Kroah-Hartman static irqreturn_t pcl816_interrupt(int irq, void *d) 242*8ffdff6aSGreg Kroah-Hartman { 243*8ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = d; 244*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s = dev->read_subdev; 245*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 246*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 247*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; 248*8ffdff6aSGreg Kroah-Hartman unsigned int nsamples; 249*8ffdff6aSGreg Kroah-Hartman unsigned int bufptr; 250*8ffdff6aSGreg Kroah-Hartman 251*8ffdff6aSGreg Kroah-Hartman if (!dev->attached || !devpriv->ai_cmd_running) { 252*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 253*8ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 254*8ffdff6aSGreg Kroah-Hartman } 255*8ffdff6aSGreg Kroah-Hartman 256*8ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_canceled) { 257*8ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 0; 258*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 259*8ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 260*8ffdff6aSGreg Kroah-Hartman } 261*8ffdff6aSGreg Kroah-Hartman 262*8ffdff6aSGreg Kroah-Hartman nsamples = comedi_bytes_to_samples(s, desc->size) - 263*8ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr; 264*8ffdff6aSGreg Kroah-Hartman bufptr = devpriv->ai_poll_ptr; 265*8ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = 0; 266*8ffdff6aSGreg Kroah-Hartman 267*8ffdff6aSGreg Kroah-Hartman /* restart dma with the next buffer */ 268*8ffdff6aSGreg Kroah-Hartman dma->cur_dma = 1 - dma->cur_dma; 269*8ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_dma(dev, s, nsamples); 270*8ffdff6aSGreg Kroah-Hartman 271*8ffdff6aSGreg Kroah-Hartman transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples); 272*8ffdff6aSGreg Kroah-Hartman 273*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 274*8ffdff6aSGreg Kroah-Hartman 275*8ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s); 276*8ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 277*8ffdff6aSGreg Kroah-Hartman } 278*8ffdff6aSGreg Kroah-Hartman 279*8ffdff6aSGreg Kroah-Hartman static int check_channel_list(struct comedi_device *dev, 280*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 281*8ffdff6aSGreg Kroah-Hartman unsigned int *chanlist, 282*8ffdff6aSGreg Kroah-Hartman unsigned int chanlen) 283*8ffdff6aSGreg Kroah-Hartman { 284*8ffdff6aSGreg Kroah-Hartman unsigned int chansegment[16]; 285*8ffdff6aSGreg Kroah-Hartman unsigned int i, nowmustbechan, seglen; 286*8ffdff6aSGreg Kroah-Hartman 287*8ffdff6aSGreg Kroah-Hartman /* correct channel and range number check itself comedi/range.c */ 288*8ffdff6aSGreg Kroah-Hartman if (chanlen < 1) { 289*8ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "range/channel list is empty!\n"); 290*8ffdff6aSGreg Kroah-Hartman return 0; 291*8ffdff6aSGreg Kroah-Hartman } 292*8ffdff6aSGreg Kroah-Hartman 293*8ffdff6aSGreg Kroah-Hartman if (chanlen > 1) { 294*8ffdff6aSGreg Kroah-Hartman /* first channel is every time ok */ 295*8ffdff6aSGreg Kroah-Hartman chansegment[0] = chanlist[0]; 296*8ffdff6aSGreg Kroah-Hartman for (i = 1, seglen = 1; i < chanlen; i++, seglen++) { 297*8ffdff6aSGreg Kroah-Hartman /* we detect loop, this must by finish */ 298*8ffdff6aSGreg Kroah-Hartman if (chanlist[0] == chanlist[i]) 299*8ffdff6aSGreg Kroah-Hartman break; 300*8ffdff6aSGreg Kroah-Hartman nowmustbechan = 301*8ffdff6aSGreg Kroah-Hartman (CR_CHAN(chansegment[i - 1]) + 1) % chanlen; 302*8ffdff6aSGreg Kroah-Hartman if (nowmustbechan != CR_CHAN(chanlist[i])) { 303*8ffdff6aSGreg Kroah-Hartman /* channel list isn't continuous :-( */ 304*8ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, 305*8ffdff6aSGreg Kroah-Hartman "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n", 306*8ffdff6aSGreg Kroah-Hartman i, CR_CHAN(chanlist[i]), nowmustbechan, 307*8ffdff6aSGreg Kroah-Hartman CR_CHAN(chanlist[0])); 308*8ffdff6aSGreg Kroah-Hartman return 0; 309*8ffdff6aSGreg Kroah-Hartman } 310*8ffdff6aSGreg Kroah-Hartman /* well, this is next correct channel in list */ 311*8ffdff6aSGreg Kroah-Hartman chansegment[i] = chanlist[i]; 312*8ffdff6aSGreg Kroah-Hartman } 313*8ffdff6aSGreg Kroah-Hartman 314*8ffdff6aSGreg Kroah-Hartman /* check whole chanlist */ 315*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < chanlen; i++) { 316*8ffdff6aSGreg Kroah-Hartman if (chanlist[i] != chansegment[i % seglen]) { 317*8ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, 318*8ffdff6aSGreg Kroah-Hartman "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n", 319*8ffdff6aSGreg Kroah-Hartman i, CR_CHAN(chansegment[i]), 320*8ffdff6aSGreg Kroah-Hartman CR_RANGE(chansegment[i]), 321*8ffdff6aSGreg Kroah-Hartman CR_AREF(chansegment[i]), 322*8ffdff6aSGreg Kroah-Hartman CR_CHAN(chanlist[i % seglen]), 323*8ffdff6aSGreg Kroah-Hartman CR_RANGE(chanlist[i % seglen]), 324*8ffdff6aSGreg Kroah-Hartman CR_AREF(chansegment[i % seglen])); 325*8ffdff6aSGreg Kroah-Hartman return 0; /* chan/gain list is strange */ 326*8ffdff6aSGreg Kroah-Hartman } 327*8ffdff6aSGreg Kroah-Hartman } 328*8ffdff6aSGreg Kroah-Hartman } else { 329*8ffdff6aSGreg Kroah-Hartman seglen = 1; 330*8ffdff6aSGreg Kroah-Hartman } 331*8ffdff6aSGreg Kroah-Hartman 332*8ffdff6aSGreg Kroah-Hartman return seglen; /* we can serve this with MUX logic */ 333*8ffdff6aSGreg Kroah-Hartman } 334*8ffdff6aSGreg Kroah-Hartman 335*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cmdtest(struct comedi_device *dev, 336*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, struct comedi_cmd *cmd) 337*8ffdff6aSGreg Kroah-Hartman { 338*8ffdff6aSGreg Kroah-Hartman int err = 0; 339*8ffdff6aSGreg Kroah-Hartman 340*8ffdff6aSGreg Kroah-Hartman /* Step 1 : check if triggers are trivially valid */ 341*8ffdff6aSGreg Kroah-Hartman 342*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 343*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 344*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->convert_src, 345*8ffdff6aSGreg Kroah-Hartman TRIG_EXT | TRIG_TIMER); 346*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 347*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 348*8ffdff6aSGreg Kroah-Hartman 349*8ffdff6aSGreg Kroah-Hartman if (err) 350*8ffdff6aSGreg Kroah-Hartman return 1; 351*8ffdff6aSGreg Kroah-Hartman 352*8ffdff6aSGreg Kroah-Hartman /* Step 2a : make sure trigger sources are unique */ 353*8ffdff6aSGreg Kroah-Hartman 354*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->convert_src); 355*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->stop_src); 356*8ffdff6aSGreg Kroah-Hartman 357*8ffdff6aSGreg Kroah-Hartman /* Step 2b : and mutually compatible */ 358*8ffdff6aSGreg Kroah-Hartman 359*8ffdff6aSGreg Kroah-Hartman if (err) 360*8ffdff6aSGreg Kroah-Hartman return 2; 361*8ffdff6aSGreg Kroah-Hartman 362*8ffdff6aSGreg Kroah-Hartman /* Step 3: check if arguments are trivially valid */ 363*8ffdff6aSGreg Kroah-Hartman 364*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 365*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 366*8ffdff6aSGreg Kroah-Hartman 367*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) 368*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); 369*8ffdff6aSGreg Kroah-Hartman else /* TRIG_EXT */ 370*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 371*8ffdff6aSGreg Kroah-Hartman 372*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 373*8ffdff6aSGreg Kroah-Hartman cmd->chanlist_len); 374*8ffdff6aSGreg Kroah-Hartman 375*8ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) 376*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 377*8ffdff6aSGreg Kroah-Hartman else /* TRIG_NONE */ 378*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 379*8ffdff6aSGreg Kroah-Hartman 380*8ffdff6aSGreg Kroah-Hartman if (err) 381*8ffdff6aSGreg Kroah-Hartman return 3; 382*8ffdff6aSGreg Kroah-Hartman 383*8ffdff6aSGreg Kroah-Hartman /* step 4: fix up any arguments */ 384*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) { 385*8ffdff6aSGreg Kroah-Hartman unsigned int arg = cmd->convert_arg; 386*8ffdff6aSGreg Kroah-Hartman 387*8ffdff6aSGreg Kroah-Hartman comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 388*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 389*8ffdff6aSGreg Kroah-Hartman } 390*8ffdff6aSGreg Kroah-Hartman 391*8ffdff6aSGreg Kroah-Hartman if (err) 392*8ffdff6aSGreg Kroah-Hartman return 4; 393*8ffdff6aSGreg Kroah-Hartman 394*8ffdff6aSGreg Kroah-Hartman /* step 5: complain about special chanlist considerations */ 395*8ffdff6aSGreg Kroah-Hartman 396*8ffdff6aSGreg Kroah-Hartman if (cmd->chanlist) { 397*8ffdff6aSGreg Kroah-Hartman if (!check_channel_list(dev, s, cmd->chanlist, 398*8ffdff6aSGreg Kroah-Hartman cmd->chanlist_len)) 399*8ffdff6aSGreg Kroah-Hartman return 5; /* incorrect channels list */ 400*8ffdff6aSGreg Kroah-Hartman } 401*8ffdff6aSGreg Kroah-Hartman 402*8ffdff6aSGreg Kroah-Hartman return 0; 403*8ffdff6aSGreg Kroah-Hartman } 404*8ffdff6aSGreg Kroah-Hartman 405*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 406*8ffdff6aSGreg Kroah-Hartman { 407*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 408*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 409*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 410*8ffdff6aSGreg Kroah-Hartman unsigned int ctrl; 411*8ffdff6aSGreg Kroah-Hartman unsigned int seglen; 412*8ffdff6aSGreg Kroah-Hartman 413*8ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_running) 414*8ffdff6aSGreg Kroah-Hartman return -EBUSY; 415*8ffdff6aSGreg Kroah-Hartman 416*8ffdff6aSGreg Kroah-Hartman seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len); 417*8ffdff6aSGreg Kroah-Hartman if (seglen < 1) 418*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 419*8ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen); 420*8ffdff6aSGreg Kroah-Hartman udelay(1); 421*8ffdff6aSGreg Kroah-Hartman 422*8ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 1; 423*8ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = 0; 424*8ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 0; 425*8ffdff6aSGreg Kroah-Hartman 426*8ffdff6aSGreg Kroah-Hartman /* setup and enable dma for the first buffer */ 427*8ffdff6aSGreg Kroah-Hartman dma->cur_dma = 0; 428*8ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_dma(dev, s, 0); 429*8ffdff6aSGreg Kroah-Hartman 430*8ffdff6aSGreg Kroah-Hartman comedi_8254_set_mode(dev->pacer, 0, I8254_MODE1 | I8254_BINARY); 431*8ffdff6aSGreg Kroah-Hartman comedi_8254_write(dev->pacer, 0, 0x0ff); 432*8ffdff6aSGreg Kroah-Hartman udelay(1); 433*8ffdff6aSGreg Kroah-Hartman comedi_8254_update_divisors(dev->pacer); 434*8ffdff6aSGreg Kroah-Hartman comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 435*8ffdff6aSGreg Kroah-Hartman 436*8ffdff6aSGreg Kroah-Hartman ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | 437*8ffdff6aSGreg Kroah-Hartman PCL816_CTRL_DMASRC_SLOT(0); 438*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) 439*8ffdff6aSGreg Kroah-Hartman ctrl |= PCL816_CTRL_PACER_TRIG; 440*8ffdff6aSGreg Kroah-Hartman else /* TRIG_EXT */ 441*8ffdff6aSGreg Kroah-Hartman ctrl |= PCL816_CTRL_EXT_TRIG; 442*8ffdff6aSGreg Kroah-Hartman 443*8ffdff6aSGreg Kroah-Hartman outb(ctrl, dev->iobase + PCL816_CTRL_REG); 444*8ffdff6aSGreg Kroah-Hartman outb((dma->chan << 4) | dev->irq, 445*8ffdff6aSGreg Kroah-Hartman dev->iobase + PCL816_STATUS_REG); 446*8ffdff6aSGreg Kroah-Hartman 447*8ffdff6aSGreg Kroah-Hartman return 0; 448*8ffdff6aSGreg Kroah-Hartman } 449*8ffdff6aSGreg Kroah-Hartman 450*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s) 451*8ffdff6aSGreg Kroah-Hartman { 452*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 453*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 454*8ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc; 455*8ffdff6aSGreg Kroah-Hartman unsigned long flags; 456*8ffdff6aSGreg Kroah-Hartman unsigned int poll; 457*8ffdff6aSGreg Kroah-Hartman int ret; 458*8ffdff6aSGreg Kroah-Hartman 459*8ffdff6aSGreg Kroah-Hartman spin_lock_irqsave(&dev->spinlock, flags); 460*8ffdff6aSGreg Kroah-Hartman 461*8ffdff6aSGreg Kroah-Hartman poll = comedi_isadma_poll(dma); 462*8ffdff6aSGreg Kroah-Hartman poll = comedi_bytes_to_samples(s, poll); 463*8ffdff6aSGreg Kroah-Hartman if (poll > devpriv->ai_poll_ptr) { 464*8ffdff6aSGreg Kroah-Hartman desc = &dma->desc[dma->cur_dma]; 465*8ffdff6aSGreg Kroah-Hartman transfer_from_dma_buf(dev, s, desc->virt_addr, 466*8ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr, 467*8ffdff6aSGreg Kroah-Hartman poll - devpriv->ai_poll_ptr); 468*8ffdff6aSGreg Kroah-Hartman /* new buffer position */ 469*8ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = poll; 470*8ffdff6aSGreg Kroah-Hartman 471*8ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s); 472*8ffdff6aSGreg Kroah-Hartman 473*8ffdff6aSGreg Kroah-Hartman ret = comedi_buf_n_bytes_ready(s); 474*8ffdff6aSGreg Kroah-Hartman } else { 475*8ffdff6aSGreg Kroah-Hartman /* no new samples */ 476*8ffdff6aSGreg Kroah-Hartman ret = 0; 477*8ffdff6aSGreg Kroah-Hartman } 478*8ffdff6aSGreg Kroah-Hartman spin_unlock_irqrestore(&dev->spinlock, flags); 479*8ffdff6aSGreg Kroah-Hartman 480*8ffdff6aSGreg Kroah-Hartman return ret; 481*8ffdff6aSGreg Kroah-Hartman } 482*8ffdff6aSGreg Kroah-Hartman 483*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cancel(struct comedi_device *dev, 484*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 485*8ffdff6aSGreg Kroah-Hartman { 486*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 487*8ffdff6aSGreg Kroah-Hartman 488*8ffdff6aSGreg Kroah-Hartman if (!devpriv->ai_cmd_running) 489*8ffdff6aSGreg Kroah-Hartman return 0; 490*8ffdff6aSGreg Kroah-Hartman 491*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 492*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 493*8ffdff6aSGreg Kroah-Hartman 494*8ffdff6aSGreg Kroah-Hartman comedi_8254_pacer_enable(dev->pacer, 1, 2, false); 495*8ffdff6aSGreg Kroah-Hartman 496*8ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 0; 497*8ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 1; 498*8ffdff6aSGreg Kroah-Hartman 499*8ffdff6aSGreg Kroah-Hartman return 0; 500*8ffdff6aSGreg Kroah-Hartman } 501*8ffdff6aSGreg Kroah-Hartman 502*8ffdff6aSGreg Kroah-Hartman static int pcl816_ai_insn_read(struct comedi_device *dev, 503*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 504*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 505*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 506*8ffdff6aSGreg Kroah-Hartman { 507*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 508*8ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(insn->chanspec); 509*8ffdff6aSGreg Kroah-Hartman int ret = 0; 510*8ffdff6aSGreg Kroah-Hartman int i; 511*8ffdff6aSGreg Kroah-Hartman 512*8ffdff6aSGreg Kroah-Hartman outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG); 513*8ffdff6aSGreg Kroah-Hartman 514*8ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, chan, range); 515*8ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_scan(dev, chan, chan); 516*8ffdff6aSGreg Kroah-Hartman 517*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 518*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 519*8ffdff6aSGreg Kroah-Hartman pcl816_ai_soft_trig(dev); 520*8ffdff6aSGreg Kroah-Hartman 521*8ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0); 522*8ffdff6aSGreg Kroah-Hartman if (ret) 523*8ffdff6aSGreg Kroah-Hartman break; 524*8ffdff6aSGreg Kroah-Hartman 525*8ffdff6aSGreg Kroah-Hartman data[i] = pcl816_ai_get_sample(dev, s); 526*8ffdff6aSGreg Kroah-Hartman } 527*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 528*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 529*8ffdff6aSGreg Kroah-Hartman 530*8ffdff6aSGreg Kroah-Hartman return ret ? ret : insn->n; 531*8ffdff6aSGreg Kroah-Hartman } 532*8ffdff6aSGreg Kroah-Hartman 533*8ffdff6aSGreg Kroah-Hartman static int pcl816_di_insn_bits(struct comedi_device *dev, 534*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 535*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 536*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 537*8ffdff6aSGreg Kroah-Hartman { 538*8ffdff6aSGreg Kroah-Hartman data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) | 539*8ffdff6aSGreg Kroah-Hartman (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8); 540*8ffdff6aSGreg Kroah-Hartman 541*8ffdff6aSGreg Kroah-Hartman return insn->n; 542*8ffdff6aSGreg Kroah-Hartman } 543*8ffdff6aSGreg Kroah-Hartman 544*8ffdff6aSGreg Kroah-Hartman static int pcl816_do_insn_bits(struct comedi_device *dev, 545*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 546*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 547*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 548*8ffdff6aSGreg Kroah-Hartman { 549*8ffdff6aSGreg Kroah-Hartman if (comedi_dio_update_state(s, data)) { 550*8ffdff6aSGreg Kroah-Hartman outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG); 551*8ffdff6aSGreg Kroah-Hartman outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG); 552*8ffdff6aSGreg Kroah-Hartman } 553*8ffdff6aSGreg Kroah-Hartman 554*8ffdff6aSGreg Kroah-Hartman data[1] = s->state; 555*8ffdff6aSGreg Kroah-Hartman 556*8ffdff6aSGreg Kroah-Hartman return insn->n; 557*8ffdff6aSGreg Kroah-Hartman } 558*8ffdff6aSGreg Kroah-Hartman 559*8ffdff6aSGreg Kroah-Hartman static void pcl816_reset(struct comedi_device *dev) 560*8ffdff6aSGreg Kroah-Hartman { 561*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 562*8ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, 0, 0); 563*8ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 564*8ffdff6aSGreg Kroah-Hartman 565*8ffdff6aSGreg Kroah-Hartman /* set all digital outputs low */ 566*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_DO_DI_LSB_REG); 567*8ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_DO_DI_MSB_REG); 568*8ffdff6aSGreg Kroah-Hartman } 569*8ffdff6aSGreg Kroah-Hartman 570*8ffdff6aSGreg Kroah-Hartman static void pcl816_alloc_irq_and_dma(struct comedi_device *dev, 571*8ffdff6aSGreg Kroah-Hartman struct comedi_devconfig *it) 572*8ffdff6aSGreg Kroah-Hartman { 573*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 574*8ffdff6aSGreg Kroah-Hartman unsigned int irq_num = it->options[1]; 575*8ffdff6aSGreg Kroah-Hartman unsigned int dma_chan = it->options[2]; 576*8ffdff6aSGreg Kroah-Hartman 577*8ffdff6aSGreg Kroah-Hartman /* only IRQs 2-7 and DMA channels 3 and 1 are valid */ 578*8ffdff6aSGreg Kroah-Hartman if (!(irq_num >= 2 && irq_num <= 7) || 579*8ffdff6aSGreg Kroah-Hartman !(dma_chan == 3 || dma_chan == 1)) 580*8ffdff6aSGreg Kroah-Hartman return; 581*8ffdff6aSGreg Kroah-Hartman 582*8ffdff6aSGreg Kroah-Hartman if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev)) 583*8ffdff6aSGreg Kroah-Hartman return; 584*8ffdff6aSGreg Kroah-Hartman 585*8ffdff6aSGreg Kroah-Hartman /* DMA uses two 16K buffers */ 586*8ffdff6aSGreg Kroah-Hartman devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan, 587*8ffdff6aSGreg Kroah-Hartman PAGE_SIZE * 4, COMEDI_ISADMA_READ); 588*8ffdff6aSGreg Kroah-Hartman if (!devpriv->dma) 589*8ffdff6aSGreg Kroah-Hartman free_irq(irq_num, dev); 590*8ffdff6aSGreg Kroah-Hartman else 591*8ffdff6aSGreg Kroah-Hartman dev->irq = irq_num; 592*8ffdff6aSGreg Kroah-Hartman } 593*8ffdff6aSGreg Kroah-Hartman 594*8ffdff6aSGreg Kroah-Hartman static void pcl816_free_dma(struct comedi_device *dev) 595*8ffdff6aSGreg Kroah-Hartman { 596*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 597*8ffdff6aSGreg Kroah-Hartman 598*8ffdff6aSGreg Kroah-Hartman if (devpriv) 599*8ffdff6aSGreg Kroah-Hartman comedi_isadma_free(devpriv->dma); 600*8ffdff6aSGreg Kroah-Hartman } 601*8ffdff6aSGreg Kroah-Hartman 602*8ffdff6aSGreg Kroah-Hartman static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it) 603*8ffdff6aSGreg Kroah-Hartman { 604*8ffdff6aSGreg Kroah-Hartman const struct pcl816_board *board = dev->board_ptr; 605*8ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv; 606*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 607*8ffdff6aSGreg Kroah-Hartman int ret; 608*8ffdff6aSGreg Kroah-Hartman 609*8ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 610*8ffdff6aSGreg Kroah-Hartman if (!devpriv) 611*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 612*8ffdff6aSGreg Kroah-Hartman 613*8ffdff6aSGreg Kroah-Hartman ret = comedi_request_region(dev, it->options[0], 0x10); 614*8ffdff6aSGreg Kroah-Hartman if (ret) 615*8ffdff6aSGreg Kroah-Hartman return ret; 616*8ffdff6aSGreg Kroah-Hartman 617*8ffdff6aSGreg Kroah-Hartman /* an IRQ and DMA are required to support async commands */ 618*8ffdff6aSGreg Kroah-Hartman pcl816_alloc_irq_and_dma(dev, it); 619*8ffdff6aSGreg Kroah-Hartman 620*8ffdff6aSGreg Kroah-Hartman dev->pacer = comedi_8254_init(dev->iobase + PCL816_TIMER_BASE, 621*8ffdff6aSGreg Kroah-Hartman I8254_OSC_BASE_10MHZ, I8254_IO8, 0); 622*8ffdff6aSGreg Kroah-Hartman if (!dev->pacer) 623*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 624*8ffdff6aSGreg Kroah-Hartman 625*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 4); 626*8ffdff6aSGreg Kroah-Hartman if (ret) 627*8ffdff6aSGreg Kroah-Hartman return ret; 628*8ffdff6aSGreg Kroah-Hartman 629*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 630*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 631*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_CMD_READ | SDF_DIFF; 632*8ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 633*8ffdff6aSGreg Kroah-Hartman s->maxdata = board->ai_maxdata; 634*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_pcl816; 635*8ffdff6aSGreg Kroah-Hartman s->insn_read = pcl816_ai_insn_read; 636*8ffdff6aSGreg Kroah-Hartman if (dev->irq) { 637*8ffdff6aSGreg Kroah-Hartman dev->read_subdev = s; 638*8ffdff6aSGreg Kroah-Hartman s->subdev_flags |= SDF_CMD_READ; 639*8ffdff6aSGreg Kroah-Hartman s->len_chanlist = board->ai_chanlist; 640*8ffdff6aSGreg Kroah-Hartman s->do_cmdtest = pcl816_ai_cmdtest; 641*8ffdff6aSGreg Kroah-Hartman s->do_cmd = pcl816_ai_cmd; 642*8ffdff6aSGreg Kroah-Hartman s->poll = pcl816_ai_poll; 643*8ffdff6aSGreg Kroah-Hartman s->cancel = pcl816_ai_cancel; 644*8ffdff6aSGreg Kroah-Hartman } 645*8ffdff6aSGreg Kroah-Hartman 646*8ffdff6aSGreg Kroah-Hartman /* Piggyback Slot1 subdevice */ 647*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 648*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 649*8ffdff6aSGreg Kroah-Hartman 650*8ffdff6aSGreg Kroah-Hartman /* Digital Input subdevice */ 651*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2]; 652*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DI; 653*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE; 654*8ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 655*8ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 656*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 657*8ffdff6aSGreg Kroah-Hartman s->insn_bits = pcl816_di_insn_bits; 658*8ffdff6aSGreg Kroah-Hartman 659*8ffdff6aSGreg Kroah-Hartman /* Digital Output subdevice */ 660*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3]; 661*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DO; 662*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 663*8ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 664*8ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 665*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 666*8ffdff6aSGreg Kroah-Hartman s->insn_bits = pcl816_do_insn_bits; 667*8ffdff6aSGreg Kroah-Hartman 668*8ffdff6aSGreg Kroah-Hartman pcl816_reset(dev); 669*8ffdff6aSGreg Kroah-Hartman 670*8ffdff6aSGreg Kroah-Hartman return 0; 671*8ffdff6aSGreg Kroah-Hartman } 672*8ffdff6aSGreg Kroah-Hartman 673*8ffdff6aSGreg Kroah-Hartman static void pcl816_detach(struct comedi_device *dev) 674*8ffdff6aSGreg Kroah-Hartman { 675*8ffdff6aSGreg Kroah-Hartman if (dev->private) { 676*8ffdff6aSGreg Kroah-Hartman pcl816_ai_cancel(dev, dev->read_subdev); 677*8ffdff6aSGreg Kroah-Hartman pcl816_reset(dev); 678*8ffdff6aSGreg Kroah-Hartman } 679*8ffdff6aSGreg Kroah-Hartman pcl816_free_dma(dev); 680*8ffdff6aSGreg Kroah-Hartman comedi_legacy_detach(dev); 681*8ffdff6aSGreg Kroah-Hartman } 682*8ffdff6aSGreg Kroah-Hartman 683*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver pcl816_driver = { 684*8ffdff6aSGreg Kroah-Hartman .driver_name = "pcl816", 685*8ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 686*8ffdff6aSGreg Kroah-Hartman .attach = pcl816_attach, 687*8ffdff6aSGreg Kroah-Hartman .detach = pcl816_detach, 688*8ffdff6aSGreg Kroah-Hartman .board_name = &boardtypes[0].name, 689*8ffdff6aSGreg Kroah-Hartman .num_names = ARRAY_SIZE(boardtypes), 690*8ffdff6aSGreg Kroah-Hartman .offset = sizeof(struct pcl816_board), 691*8ffdff6aSGreg Kroah-Hartman }; 692*8ffdff6aSGreg Kroah-Hartman module_comedi_driver(pcl816_driver); 693*8ffdff6aSGreg Kroah-Hartman 694*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org"); 695*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi low-level driver"); 696*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 697