18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 28ffdff6aSGreg Kroah-Hartman /* 38ffdff6aSGreg Kroah-Hartman * pcl816.c 48ffdff6aSGreg Kroah-Hartman * Comedi driver for Advantech PCL-816 cards 58ffdff6aSGreg Kroah-Hartman * 68ffdff6aSGreg Kroah-Hartman * Author: Juan Grigera <juan@grigera.com.ar> 78ffdff6aSGreg Kroah-Hartman * based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812 88ffdff6aSGreg Kroah-Hartman */ 98ffdff6aSGreg Kroah-Hartman 108ffdff6aSGreg Kroah-Hartman /* 118ffdff6aSGreg Kroah-Hartman * Driver: pcl816 128ffdff6aSGreg Kroah-Hartman * Description: Advantech PCL-816 cards, PCL-814 138ffdff6aSGreg Kroah-Hartman * Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b) 148ffdff6aSGreg Kroah-Hartman * Author: Juan Grigera <juan@grigera.com.ar> 158ffdff6aSGreg Kroah-Hartman * Status: works 168ffdff6aSGreg Kroah-Hartman * Updated: Tue, 2 Apr 2002 23:15:21 -0800 178ffdff6aSGreg Kroah-Hartman * 188ffdff6aSGreg Kroah-Hartman * PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO. 198ffdff6aSGreg Kroah-Hartman * Differences are at resolution (16 vs 12 bits). 208ffdff6aSGreg Kroah-Hartman * 218ffdff6aSGreg Kroah-Hartman * The driver support AI command mode, other subdevices not written. 228ffdff6aSGreg Kroah-Hartman * 238ffdff6aSGreg Kroah-Hartman * Analog output and digital input and output are not supported. 248ffdff6aSGreg Kroah-Hartman * 258ffdff6aSGreg Kroah-Hartman * Configuration Options: 268ffdff6aSGreg Kroah-Hartman * [0] - IO Base 278ffdff6aSGreg Kroah-Hartman * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) 288ffdff6aSGreg Kroah-Hartman * [2] - DMA (0=disable, 1, 3) 298ffdff6aSGreg Kroah-Hartman * [3] - 0, 10=10MHz clock for 8254 308ffdff6aSGreg Kroah-Hartman * 1= 1MHz clock for 8254 318ffdff6aSGreg Kroah-Hartman */ 328ffdff6aSGreg Kroah-Hartman 338ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 348ffdff6aSGreg Kroah-Hartman #include <linux/gfp.h> 358ffdff6aSGreg Kroah-Hartman #include <linux/delay.h> 368ffdff6aSGreg Kroah-Hartman #include <linux/io.h> 378ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 38df0e68c1SIan Abbott #include <linux/comedi/comedidev.h> 3944fb7affSIan Abbott #include <linux/comedi/comedi_8254.h> 40fe7a4f5bSIan Abbott #include <linux/comedi/comedi_isadma.h> 418ffdff6aSGreg Kroah-Hartman 428ffdff6aSGreg Kroah-Hartman /* 438ffdff6aSGreg Kroah-Hartman * Register I/O map 448ffdff6aSGreg Kroah-Hartman */ 458ffdff6aSGreg Kroah-Hartman #define PCL816_DO_DI_LSB_REG 0x00 468ffdff6aSGreg Kroah-Hartman #define PCL816_DO_DI_MSB_REG 0x01 478ffdff6aSGreg Kroah-Hartman #define PCL816_TIMER_BASE 0x04 488ffdff6aSGreg Kroah-Hartman #define PCL816_AI_LSB_REG 0x08 498ffdff6aSGreg Kroah-Hartman #define PCL816_AI_MSB_REG 0x09 508ffdff6aSGreg Kroah-Hartman #define PCL816_RANGE_REG 0x09 518ffdff6aSGreg Kroah-Hartman #define PCL816_CLRINT_REG 0x0a 528ffdff6aSGreg Kroah-Hartman #define PCL816_MUX_REG 0x0b 538ffdff6aSGreg Kroah-Hartman #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first)) 548ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_REG 0x0c 558ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_SOFT_TRIG BIT(0) 568ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_PACER_TRIG BIT(1) 578ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_EXT_TRIG BIT(2) 588ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_POE BIT(3) 598ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_DMAEN BIT(4) 608ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_INTEN BIT(5) 618ffdff6aSGreg Kroah-Hartman #define PCL816_CTRL_DMASRC_SLOT(x) (((x) & 0x3) << 6) 628ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_REG 0x0d 638ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0) 648ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_SLOT(x) (((x) & 0x3) << 4) 658ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_DMA PCL816_STATUS_INTSRC_SLOT(3) 668ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTSRC_MASK PCL816_STATUS_INTSRC_SLOT(3) 678ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_INTACT BIT(6) 688ffdff6aSGreg Kroah-Hartman #define PCL816_STATUS_DRDY BIT(7) 698ffdff6aSGreg Kroah-Hartman 708ffdff6aSGreg Kroah-Hartman #define MAGIC_DMA_WORD 0x5a5a 718ffdff6aSGreg Kroah-Hartman 728ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_pcl816 = { 738ffdff6aSGreg Kroah-Hartman 8, { 748ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 758ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), 768ffdff6aSGreg Kroah-Hartman BIP_RANGE(2.5), 778ffdff6aSGreg Kroah-Hartman BIP_RANGE(1.25), 788ffdff6aSGreg Kroah-Hartman UNI_RANGE(10), 798ffdff6aSGreg Kroah-Hartman UNI_RANGE(5), 808ffdff6aSGreg Kroah-Hartman UNI_RANGE(2.5), 818ffdff6aSGreg Kroah-Hartman UNI_RANGE(1.25) 828ffdff6aSGreg Kroah-Hartman } 838ffdff6aSGreg Kroah-Hartman }; 848ffdff6aSGreg Kroah-Hartman 858ffdff6aSGreg Kroah-Hartman struct pcl816_board { 868ffdff6aSGreg Kroah-Hartman const char *name; 878ffdff6aSGreg Kroah-Hartman int ai_maxdata; 888ffdff6aSGreg Kroah-Hartman int ai_chanlist; 898ffdff6aSGreg Kroah-Hartman }; 908ffdff6aSGreg Kroah-Hartman 918ffdff6aSGreg Kroah-Hartman static const struct pcl816_board boardtypes[] = { 928ffdff6aSGreg Kroah-Hartman { 938ffdff6aSGreg Kroah-Hartman .name = "pcl816", 948ffdff6aSGreg Kroah-Hartman .ai_maxdata = 0xffff, 958ffdff6aSGreg Kroah-Hartman .ai_chanlist = 1024, 968ffdff6aSGreg Kroah-Hartman }, { 978ffdff6aSGreg Kroah-Hartman .name = "pcl814b", 988ffdff6aSGreg Kroah-Hartman .ai_maxdata = 0x3fff, 998ffdff6aSGreg Kroah-Hartman .ai_chanlist = 1024, 1008ffdff6aSGreg Kroah-Hartman }, 1018ffdff6aSGreg Kroah-Hartman }; 1028ffdff6aSGreg Kroah-Hartman 1038ffdff6aSGreg Kroah-Hartman struct pcl816_private { 1048ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma; 1058ffdff6aSGreg Kroah-Hartman unsigned int ai_poll_ptr; /* how many sampes transfer poll */ 1068ffdff6aSGreg Kroah-Hartman unsigned int ai_cmd_running:1; 1078ffdff6aSGreg Kroah-Hartman unsigned int ai_cmd_canceled:1; 1088ffdff6aSGreg Kroah-Hartman }; 1098ffdff6aSGreg Kroah-Hartman 1108ffdff6aSGreg Kroah-Hartman static void pcl816_ai_setup_dma(struct comedi_device *dev, 1118ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 1128ffdff6aSGreg Kroah-Hartman unsigned int unread_samples) 1138ffdff6aSGreg Kroah-Hartman { 1148ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 1158ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 1168ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; 1178ffdff6aSGreg Kroah-Hartman unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize); 1188ffdff6aSGreg Kroah-Hartman unsigned int nsamples; 1198ffdff6aSGreg Kroah-Hartman 1208ffdff6aSGreg Kroah-Hartman comedi_isadma_disable(dma->chan); 1218ffdff6aSGreg Kroah-Hartman 1228ffdff6aSGreg Kroah-Hartman /* 1238ffdff6aSGreg Kroah-Hartman * Determine dma size based on the buffer maxsize plus the number of 1248ffdff6aSGreg Kroah-Hartman * unread samples and the number of samples remaining in the command. 1258ffdff6aSGreg Kroah-Hartman */ 1268ffdff6aSGreg Kroah-Hartman nsamples = comedi_nsamples_left(s, max_samples + unread_samples); 1278ffdff6aSGreg Kroah-Hartman if (nsamples > unread_samples) { 1288ffdff6aSGreg Kroah-Hartman nsamples -= unread_samples; 1298ffdff6aSGreg Kroah-Hartman desc->size = comedi_samples_to_bytes(s, nsamples); 1308ffdff6aSGreg Kroah-Hartman comedi_isadma_program(desc); 1318ffdff6aSGreg Kroah-Hartman } 1328ffdff6aSGreg Kroah-Hartman } 1338ffdff6aSGreg Kroah-Hartman 1348ffdff6aSGreg Kroah-Hartman static void pcl816_ai_set_chan_range(struct comedi_device *dev, 1358ffdff6aSGreg Kroah-Hartman unsigned int chan, 1368ffdff6aSGreg Kroah-Hartman unsigned int range) 1378ffdff6aSGreg Kroah-Hartman { 1388ffdff6aSGreg Kroah-Hartman outb(chan, dev->iobase + PCL816_MUX_REG); 1398ffdff6aSGreg Kroah-Hartman outb(range, dev->iobase + PCL816_RANGE_REG); 1408ffdff6aSGreg Kroah-Hartman } 1418ffdff6aSGreg Kroah-Hartman 1428ffdff6aSGreg Kroah-Hartman static void pcl816_ai_set_chan_scan(struct comedi_device *dev, 1438ffdff6aSGreg Kroah-Hartman unsigned int first_chan, 1448ffdff6aSGreg Kroah-Hartman unsigned int last_chan) 1458ffdff6aSGreg Kroah-Hartman { 1468ffdff6aSGreg Kroah-Hartman outb(PCL816_MUX_SCAN(first_chan, last_chan), 1478ffdff6aSGreg Kroah-Hartman dev->iobase + PCL816_MUX_REG); 1488ffdff6aSGreg Kroah-Hartman } 1498ffdff6aSGreg Kroah-Hartman 1508ffdff6aSGreg Kroah-Hartman static void pcl816_ai_setup_chanlist(struct comedi_device *dev, 1518ffdff6aSGreg Kroah-Hartman unsigned int *chanlist, 1528ffdff6aSGreg Kroah-Hartman unsigned int seglen) 1538ffdff6aSGreg Kroah-Hartman { 1548ffdff6aSGreg Kroah-Hartman unsigned int first_chan = CR_CHAN(chanlist[0]); 1558ffdff6aSGreg Kroah-Hartman unsigned int last_chan; 1568ffdff6aSGreg Kroah-Hartman unsigned int range; 1578ffdff6aSGreg Kroah-Hartman unsigned int i; 1588ffdff6aSGreg Kroah-Hartman 1598ffdff6aSGreg Kroah-Hartman /* store range list to card */ 1608ffdff6aSGreg Kroah-Hartman for (i = 0; i < seglen; i++) { 1618ffdff6aSGreg Kroah-Hartman last_chan = CR_CHAN(chanlist[i]); 1628ffdff6aSGreg Kroah-Hartman range = CR_RANGE(chanlist[i]); 1638ffdff6aSGreg Kroah-Hartman 1648ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, last_chan, range); 1658ffdff6aSGreg Kroah-Hartman } 1668ffdff6aSGreg Kroah-Hartman 1678ffdff6aSGreg Kroah-Hartman udelay(1); 1688ffdff6aSGreg Kroah-Hartman 1698ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_scan(dev, first_chan, last_chan); 1708ffdff6aSGreg Kroah-Hartman } 1718ffdff6aSGreg Kroah-Hartman 1728ffdff6aSGreg Kroah-Hartman static void pcl816_ai_clear_eoc(struct comedi_device *dev) 1738ffdff6aSGreg Kroah-Hartman { 1748ffdff6aSGreg Kroah-Hartman /* writing any value clears the interrupt request */ 1758ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CLRINT_REG); 1768ffdff6aSGreg Kroah-Hartman } 1778ffdff6aSGreg Kroah-Hartman 1788ffdff6aSGreg Kroah-Hartman static void pcl816_ai_soft_trig(struct comedi_device *dev) 1798ffdff6aSGreg Kroah-Hartman { 1808ffdff6aSGreg Kroah-Hartman /* writing any value triggers a software conversion */ 1818ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_AI_LSB_REG); 1828ffdff6aSGreg Kroah-Hartman } 1838ffdff6aSGreg Kroah-Hartman 1848ffdff6aSGreg Kroah-Hartman static unsigned int pcl816_ai_get_sample(struct comedi_device *dev, 1858ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 1868ffdff6aSGreg Kroah-Hartman { 1878ffdff6aSGreg Kroah-Hartman unsigned int val; 1888ffdff6aSGreg Kroah-Hartman 1898ffdff6aSGreg Kroah-Hartman val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8; 1908ffdff6aSGreg Kroah-Hartman val |= inb(dev->iobase + PCL816_AI_LSB_REG); 1918ffdff6aSGreg Kroah-Hartman 1928ffdff6aSGreg Kroah-Hartman return val & s->maxdata; 1938ffdff6aSGreg Kroah-Hartman } 1948ffdff6aSGreg Kroah-Hartman 1958ffdff6aSGreg Kroah-Hartman static int pcl816_ai_eoc(struct comedi_device *dev, 1968ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 1978ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 1988ffdff6aSGreg Kroah-Hartman unsigned long context) 1998ffdff6aSGreg Kroah-Hartman { 2008ffdff6aSGreg Kroah-Hartman unsigned int status; 2018ffdff6aSGreg Kroah-Hartman 2028ffdff6aSGreg Kroah-Hartman status = inb(dev->iobase + PCL816_STATUS_REG); 2038ffdff6aSGreg Kroah-Hartman if ((status & PCL816_STATUS_DRDY) == 0) 2048ffdff6aSGreg Kroah-Hartman return 0; 2058ffdff6aSGreg Kroah-Hartman return -EBUSY; 2068ffdff6aSGreg Kroah-Hartman } 2078ffdff6aSGreg Kroah-Hartman 2088ffdff6aSGreg Kroah-Hartman static bool pcl816_ai_next_chan(struct comedi_device *dev, 2098ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 2108ffdff6aSGreg Kroah-Hartman { 2118ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 2128ffdff6aSGreg Kroah-Hartman 2138ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT && 2148ffdff6aSGreg Kroah-Hartman s->async->scans_done >= cmd->stop_arg) { 2158ffdff6aSGreg Kroah-Hartman s->async->events |= COMEDI_CB_EOA; 2168ffdff6aSGreg Kroah-Hartman return false; 2178ffdff6aSGreg Kroah-Hartman } 2188ffdff6aSGreg Kroah-Hartman 2198ffdff6aSGreg Kroah-Hartman return true; 2208ffdff6aSGreg Kroah-Hartman } 2218ffdff6aSGreg Kroah-Hartman 2228ffdff6aSGreg Kroah-Hartman static void transfer_from_dma_buf(struct comedi_device *dev, 2238ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2248ffdff6aSGreg Kroah-Hartman unsigned short *ptr, 2258ffdff6aSGreg Kroah-Hartman unsigned int bufptr, unsigned int len) 2268ffdff6aSGreg Kroah-Hartman { 2278ffdff6aSGreg Kroah-Hartman unsigned short val; 2288ffdff6aSGreg Kroah-Hartman int i; 2298ffdff6aSGreg Kroah-Hartman 2308ffdff6aSGreg Kroah-Hartman for (i = 0; i < len; i++) { 2318ffdff6aSGreg Kroah-Hartman val = ptr[bufptr++]; 2328ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, &val, 1); 2338ffdff6aSGreg Kroah-Hartman 2348ffdff6aSGreg Kroah-Hartman if (!pcl816_ai_next_chan(dev, s)) 2358ffdff6aSGreg Kroah-Hartman return; 2368ffdff6aSGreg Kroah-Hartman } 2378ffdff6aSGreg Kroah-Hartman } 2388ffdff6aSGreg Kroah-Hartman 2398ffdff6aSGreg Kroah-Hartman static irqreturn_t pcl816_interrupt(int irq, void *d) 2408ffdff6aSGreg Kroah-Hartman { 2418ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = d; 2428ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s = dev->read_subdev; 2438ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 2448ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 2458ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; 2468ffdff6aSGreg Kroah-Hartman unsigned int nsamples; 2478ffdff6aSGreg Kroah-Hartman unsigned int bufptr; 2488ffdff6aSGreg Kroah-Hartman 2498ffdff6aSGreg Kroah-Hartman if (!dev->attached || !devpriv->ai_cmd_running) { 2508ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 2518ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 2528ffdff6aSGreg Kroah-Hartman } 2538ffdff6aSGreg Kroah-Hartman 2548ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_canceled) { 2558ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 0; 2568ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 2578ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 2588ffdff6aSGreg Kroah-Hartman } 2598ffdff6aSGreg Kroah-Hartman 2608ffdff6aSGreg Kroah-Hartman nsamples = comedi_bytes_to_samples(s, desc->size) - 2618ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr; 2628ffdff6aSGreg Kroah-Hartman bufptr = devpriv->ai_poll_ptr; 2638ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = 0; 2648ffdff6aSGreg Kroah-Hartman 2658ffdff6aSGreg Kroah-Hartman /* restart dma with the next buffer */ 2668ffdff6aSGreg Kroah-Hartman dma->cur_dma = 1 - dma->cur_dma; 2678ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_dma(dev, s, nsamples); 2688ffdff6aSGreg Kroah-Hartman 2698ffdff6aSGreg Kroah-Hartman transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples); 2708ffdff6aSGreg Kroah-Hartman 2718ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 2728ffdff6aSGreg Kroah-Hartman 2738ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s); 2748ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 2758ffdff6aSGreg Kroah-Hartman } 2768ffdff6aSGreg Kroah-Hartman 2778ffdff6aSGreg Kroah-Hartman static int check_channel_list(struct comedi_device *dev, 2788ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2798ffdff6aSGreg Kroah-Hartman unsigned int *chanlist, 2808ffdff6aSGreg Kroah-Hartman unsigned int chanlen) 2818ffdff6aSGreg Kroah-Hartman { 2828ffdff6aSGreg Kroah-Hartman unsigned int chansegment[16]; 2838ffdff6aSGreg Kroah-Hartman unsigned int i, nowmustbechan, seglen; 2848ffdff6aSGreg Kroah-Hartman 2858ffdff6aSGreg Kroah-Hartman /* correct channel and range number check itself comedi/range.c */ 2868ffdff6aSGreg Kroah-Hartman if (chanlen < 1) { 2878ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "range/channel list is empty!\n"); 2888ffdff6aSGreg Kroah-Hartman return 0; 2898ffdff6aSGreg Kroah-Hartman } 2908ffdff6aSGreg Kroah-Hartman 2918ffdff6aSGreg Kroah-Hartman if (chanlen > 1) { 2928ffdff6aSGreg Kroah-Hartman /* first channel is every time ok */ 2938ffdff6aSGreg Kroah-Hartman chansegment[0] = chanlist[0]; 2948ffdff6aSGreg Kroah-Hartman for (i = 1, seglen = 1; i < chanlen; i++, seglen++) { 2958ffdff6aSGreg Kroah-Hartman /* we detect loop, this must by finish */ 2968ffdff6aSGreg Kroah-Hartman if (chanlist[0] == chanlist[i]) 2978ffdff6aSGreg Kroah-Hartman break; 2988ffdff6aSGreg Kroah-Hartman nowmustbechan = 2998ffdff6aSGreg Kroah-Hartman (CR_CHAN(chansegment[i - 1]) + 1) % chanlen; 3008ffdff6aSGreg Kroah-Hartman if (nowmustbechan != CR_CHAN(chanlist[i])) { 3018ffdff6aSGreg Kroah-Hartman /* channel list isn't continuous :-( */ 3028ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, 3038ffdff6aSGreg Kroah-Hartman "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n", 3048ffdff6aSGreg Kroah-Hartman i, CR_CHAN(chanlist[i]), nowmustbechan, 3058ffdff6aSGreg Kroah-Hartman CR_CHAN(chanlist[0])); 3068ffdff6aSGreg Kroah-Hartman return 0; 3078ffdff6aSGreg Kroah-Hartman } 3088ffdff6aSGreg Kroah-Hartman /* well, this is next correct channel in list */ 3098ffdff6aSGreg Kroah-Hartman chansegment[i] = chanlist[i]; 3108ffdff6aSGreg Kroah-Hartman } 3118ffdff6aSGreg Kroah-Hartman 3128ffdff6aSGreg Kroah-Hartman /* check whole chanlist */ 3138ffdff6aSGreg Kroah-Hartman for (i = 0; i < chanlen; i++) { 3148ffdff6aSGreg Kroah-Hartman if (chanlist[i] != chansegment[i % seglen]) { 3158ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, 3168ffdff6aSGreg Kroah-Hartman "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n", 3178ffdff6aSGreg Kroah-Hartman i, CR_CHAN(chansegment[i]), 3188ffdff6aSGreg Kroah-Hartman CR_RANGE(chansegment[i]), 3198ffdff6aSGreg Kroah-Hartman CR_AREF(chansegment[i]), 3208ffdff6aSGreg Kroah-Hartman CR_CHAN(chanlist[i % seglen]), 3218ffdff6aSGreg Kroah-Hartman CR_RANGE(chanlist[i % seglen]), 3228ffdff6aSGreg Kroah-Hartman CR_AREF(chansegment[i % seglen])); 3238ffdff6aSGreg Kroah-Hartman return 0; /* chan/gain list is strange */ 3248ffdff6aSGreg Kroah-Hartman } 3258ffdff6aSGreg Kroah-Hartman } 3268ffdff6aSGreg Kroah-Hartman } else { 3278ffdff6aSGreg Kroah-Hartman seglen = 1; 3288ffdff6aSGreg Kroah-Hartman } 3298ffdff6aSGreg Kroah-Hartman 3308ffdff6aSGreg Kroah-Hartman return seglen; /* we can serve this with MUX logic */ 3318ffdff6aSGreg Kroah-Hartman } 3328ffdff6aSGreg Kroah-Hartman 3338ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cmdtest(struct comedi_device *dev, 3348ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, struct comedi_cmd *cmd) 3358ffdff6aSGreg Kroah-Hartman { 3368ffdff6aSGreg Kroah-Hartman int err = 0; 3378ffdff6aSGreg Kroah-Hartman 3388ffdff6aSGreg Kroah-Hartman /* Step 1 : check if triggers are trivially valid */ 3398ffdff6aSGreg Kroah-Hartman 3408ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 3418ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 3428ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->convert_src, 3438ffdff6aSGreg Kroah-Hartman TRIG_EXT | TRIG_TIMER); 3448ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 3458ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 3468ffdff6aSGreg Kroah-Hartman 3478ffdff6aSGreg Kroah-Hartman if (err) 3488ffdff6aSGreg Kroah-Hartman return 1; 3498ffdff6aSGreg Kroah-Hartman 3508ffdff6aSGreg Kroah-Hartman /* Step 2a : make sure trigger sources are unique */ 3518ffdff6aSGreg Kroah-Hartman 3528ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->convert_src); 3538ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->stop_src); 3548ffdff6aSGreg Kroah-Hartman 3558ffdff6aSGreg Kroah-Hartman /* Step 2b : and mutually compatible */ 3568ffdff6aSGreg Kroah-Hartman 3578ffdff6aSGreg Kroah-Hartman if (err) 3588ffdff6aSGreg Kroah-Hartman return 2; 3598ffdff6aSGreg Kroah-Hartman 3608ffdff6aSGreg Kroah-Hartman /* Step 3: check if arguments are trivially valid */ 3618ffdff6aSGreg Kroah-Hartman 3628ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 3638ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 3648ffdff6aSGreg Kroah-Hartman 3658ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) 3668ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); 3678ffdff6aSGreg Kroah-Hartman else /* TRIG_EXT */ 3688ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 3698ffdff6aSGreg Kroah-Hartman 3708ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 3718ffdff6aSGreg Kroah-Hartman cmd->chanlist_len); 3728ffdff6aSGreg Kroah-Hartman 3738ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) 3748ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 3758ffdff6aSGreg Kroah-Hartman else /* TRIG_NONE */ 3768ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 3778ffdff6aSGreg Kroah-Hartman 3788ffdff6aSGreg Kroah-Hartman if (err) 3798ffdff6aSGreg Kroah-Hartman return 3; 3808ffdff6aSGreg Kroah-Hartman 3818ffdff6aSGreg Kroah-Hartman /* step 4: fix up any arguments */ 3828ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) { 3838ffdff6aSGreg Kroah-Hartman unsigned int arg = cmd->convert_arg; 3848ffdff6aSGreg Kroah-Hartman 3858ffdff6aSGreg Kroah-Hartman comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 3868ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 3878ffdff6aSGreg Kroah-Hartman } 3888ffdff6aSGreg Kroah-Hartman 3898ffdff6aSGreg Kroah-Hartman if (err) 3908ffdff6aSGreg Kroah-Hartman return 4; 3918ffdff6aSGreg Kroah-Hartman 3928ffdff6aSGreg Kroah-Hartman /* step 5: complain about special chanlist considerations */ 3938ffdff6aSGreg Kroah-Hartman 3948ffdff6aSGreg Kroah-Hartman if (cmd->chanlist) { 3958ffdff6aSGreg Kroah-Hartman if (!check_channel_list(dev, s, cmd->chanlist, 3968ffdff6aSGreg Kroah-Hartman cmd->chanlist_len)) 3978ffdff6aSGreg Kroah-Hartman return 5; /* incorrect channels list */ 3988ffdff6aSGreg Kroah-Hartman } 3998ffdff6aSGreg Kroah-Hartman 4008ffdff6aSGreg Kroah-Hartman return 0; 4018ffdff6aSGreg Kroah-Hartman } 4028ffdff6aSGreg Kroah-Hartman 4038ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 4048ffdff6aSGreg Kroah-Hartman { 4058ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 4068ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 4078ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 4088ffdff6aSGreg Kroah-Hartman unsigned int ctrl; 4098ffdff6aSGreg Kroah-Hartman unsigned int seglen; 4108ffdff6aSGreg Kroah-Hartman 4118ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_running) 4128ffdff6aSGreg Kroah-Hartman return -EBUSY; 4138ffdff6aSGreg Kroah-Hartman 4148ffdff6aSGreg Kroah-Hartman seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len); 4158ffdff6aSGreg Kroah-Hartman if (seglen < 1) 4168ffdff6aSGreg Kroah-Hartman return -EINVAL; 4178ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen); 4188ffdff6aSGreg Kroah-Hartman udelay(1); 4198ffdff6aSGreg Kroah-Hartman 4208ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 1; 4218ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = 0; 4228ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 0; 4238ffdff6aSGreg Kroah-Hartman 4248ffdff6aSGreg Kroah-Hartman /* setup and enable dma for the first buffer */ 4258ffdff6aSGreg Kroah-Hartman dma->cur_dma = 0; 4268ffdff6aSGreg Kroah-Hartman pcl816_ai_setup_dma(dev, s, 0); 4278ffdff6aSGreg Kroah-Hartman 4288ffdff6aSGreg Kroah-Hartman comedi_8254_set_mode(dev->pacer, 0, I8254_MODE1 | I8254_BINARY); 4298ffdff6aSGreg Kroah-Hartman comedi_8254_write(dev->pacer, 0, 0x0ff); 4308ffdff6aSGreg Kroah-Hartman udelay(1); 4318ffdff6aSGreg Kroah-Hartman comedi_8254_update_divisors(dev->pacer); 4328ffdff6aSGreg Kroah-Hartman comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 4338ffdff6aSGreg Kroah-Hartman 4348ffdff6aSGreg Kroah-Hartman ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | 4358ffdff6aSGreg Kroah-Hartman PCL816_CTRL_DMASRC_SLOT(0); 4368ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) 4378ffdff6aSGreg Kroah-Hartman ctrl |= PCL816_CTRL_PACER_TRIG; 4388ffdff6aSGreg Kroah-Hartman else /* TRIG_EXT */ 4398ffdff6aSGreg Kroah-Hartman ctrl |= PCL816_CTRL_EXT_TRIG; 4408ffdff6aSGreg Kroah-Hartman 4418ffdff6aSGreg Kroah-Hartman outb(ctrl, dev->iobase + PCL816_CTRL_REG); 4428ffdff6aSGreg Kroah-Hartman outb((dma->chan << 4) | dev->irq, 4438ffdff6aSGreg Kroah-Hartman dev->iobase + PCL816_STATUS_REG); 4448ffdff6aSGreg Kroah-Hartman 4458ffdff6aSGreg Kroah-Hartman return 0; 4468ffdff6aSGreg Kroah-Hartman } 4478ffdff6aSGreg Kroah-Hartman 4488ffdff6aSGreg Kroah-Hartman static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s) 4498ffdff6aSGreg Kroah-Hartman { 4508ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 4518ffdff6aSGreg Kroah-Hartman struct comedi_isadma *dma = devpriv->dma; 4528ffdff6aSGreg Kroah-Hartman struct comedi_isadma_desc *desc; 4538ffdff6aSGreg Kroah-Hartman unsigned long flags; 4548ffdff6aSGreg Kroah-Hartman unsigned int poll; 4558ffdff6aSGreg Kroah-Hartman int ret; 4568ffdff6aSGreg Kroah-Hartman 4578ffdff6aSGreg Kroah-Hartman spin_lock_irqsave(&dev->spinlock, flags); 4588ffdff6aSGreg Kroah-Hartman 4598ffdff6aSGreg Kroah-Hartman poll = comedi_isadma_poll(dma); 4608ffdff6aSGreg Kroah-Hartman poll = comedi_bytes_to_samples(s, poll); 4618ffdff6aSGreg Kroah-Hartman if (poll > devpriv->ai_poll_ptr) { 4628ffdff6aSGreg Kroah-Hartman desc = &dma->desc[dma->cur_dma]; 4638ffdff6aSGreg Kroah-Hartman transfer_from_dma_buf(dev, s, desc->virt_addr, 4648ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr, 4658ffdff6aSGreg Kroah-Hartman poll - devpriv->ai_poll_ptr); 4668ffdff6aSGreg Kroah-Hartman /* new buffer position */ 4678ffdff6aSGreg Kroah-Hartman devpriv->ai_poll_ptr = poll; 4688ffdff6aSGreg Kroah-Hartman 4698ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s); 4708ffdff6aSGreg Kroah-Hartman 4718ffdff6aSGreg Kroah-Hartman ret = comedi_buf_n_bytes_ready(s); 4728ffdff6aSGreg Kroah-Hartman } else { 4738ffdff6aSGreg Kroah-Hartman /* no new samples */ 4748ffdff6aSGreg Kroah-Hartman ret = 0; 4758ffdff6aSGreg Kroah-Hartman } 4768ffdff6aSGreg Kroah-Hartman spin_unlock_irqrestore(&dev->spinlock, flags); 4778ffdff6aSGreg Kroah-Hartman 4788ffdff6aSGreg Kroah-Hartman return ret; 4798ffdff6aSGreg Kroah-Hartman } 4808ffdff6aSGreg Kroah-Hartman 4818ffdff6aSGreg Kroah-Hartman static int pcl816_ai_cancel(struct comedi_device *dev, 4828ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 4838ffdff6aSGreg Kroah-Hartman { 4848ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 4858ffdff6aSGreg Kroah-Hartman 4868ffdff6aSGreg Kroah-Hartman if (!devpriv->ai_cmd_running) 4878ffdff6aSGreg Kroah-Hartman return 0; 4888ffdff6aSGreg Kroah-Hartman 4898ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 4908ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 4918ffdff6aSGreg Kroah-Hartman 4928ffdff6aSGreg Kroah-Hartman comedi_8254_pacer_enable(dev->pacer, 1, 2, false); 4938ffdff6aSGreg Kroah-Hartman 4948ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 0; 4958ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_canceled = 1; 4968ffdff6aSGreg Kroah-Hartman 4978ffdff6aSGreg Kroah-Hartman return 0; 4988ffdff6aSGreg Kroah-Hartman } 4998ffdff6aSGreg Kroah-Hartman 5008ffdff6aSGreg Kroah-Hartman static int pcl816_ai_insn_read(struct comedi_device *dev, 5018ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 5028ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 5038ffdff6aSGreg Kroah-Hartman unsigned int *data) 5048ffdff6aSGreg Kroah-Hartman { 5058ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 5068ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(insn->chanspec); 5078ffdff6aSGreg Kroah-Hartman int ret = 0; 5088ffdff6aSGreg Kroah-Hartman int i; 5098ffdff6aSGreg Kroah-Hartman 5108ffdff6aSGreg Kroah-Hartman outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG); 5118ffdff6aSGreg Kroah-Hartman 5128ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, chan, range); 5138ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_scan(dev, chan, chan); 5148ffdff6aSGreg Kroah-Hartman 5158ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 5168ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 5178ffdff6aSGreg Kroah-Hartman pcl816_ai_soft_trig(dev); 5188ffdff6aSGreg Kroah-Hartman 5198ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0); 5208ffdff6aSGreg Kroah-Hartman if (ret) 5218ffdff6aSGreg Kroah-Hartman break; 5228ffdff6aSGreg Kroah-Hartman 5238ffdff6aSGreg Kroah-Hartman data[i] = pcl816_ai_get_sample(dev, s); 5248ffdff6aSGreg Kroah-Hartman } 5258ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 5268ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 5278ffdff6aSGreg Kroah-Hartman 5288ffdff6aSGreg Kroah-Hartman return ret ? ret : insn->n; 5298ffdff6aSGreg Kroah-Hartman } 5308ffdff6aSGreg Kroah-Hartman 5318ffdff6aSGreg Kroah-Hartman static int pcl816_di_insn_bits(struct comedi_device *dev, 5328ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 5338ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 5348ffdff6aSGreg Kroah-Hartman unsigned int *data) 5358ffdff6aSGreg Kroah-Hartman { 5368ffdff6aSGreg Kroah-Hartman data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) | 5378ffdff6aSGreg Kroah-Hartman (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8); 5388ffdff6aSGreg Kroah-Hartman 5398ffdff6aSGreg Kroah-Hartman return insn->n; 5408ffdff6aSGreg Kroah-Hartman } 5418ffdff6aSGreg Kroah-Hartman 5428ffdff6aSGreg Kroah-Hartman static int pcl816_do_insn_bits(struct comedi_device *dev, 5438ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 5448ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 5458ffdff6aSGreg Kroah-Hartman unsigned int *data) 5468ffdff6aSGreg Kroah-Hartman { 5478ffdff6aSGreg Kroah-Hartman if (comedi_dio_update_state(s, data)) { 5488ffdff6aSGreg Kroah-Hartman outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG); 5498ffdff6aSGreg Kroah-Hartman outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG); 5508ffdff6aSGreg Kroah-Hartman } 5518ffdff6aSGreg Kroah-Hartman 5528ffdff6aSGreg Kroah-Hartman data[1] = s->state; 5538ffdff6aSGreg Kroah-Hartman 5548ffdff6aSGreg Kroah-Hartman return insn->n; 5558ffdff6aSGreg Kroah-Hartman } 5568ffdff6aSGreg Kroah-Hartman 5578ffdff6aSGreg Kroah-Hartman static void pcl816_reset(struct comedi_device *dev) 5588ffdff6aSGreg Kroah-Hartman { 5598ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_CTRL_REG); 5608ffdff6aSGreg Kroah-Hartman pcl816_ai_set_chan_range(dev, 0, 0); 5618ffdff6aSGreg Kroah-Hartman pcl816_ai_clear_eoc(dev); 5628ffdff6aSGreg Kroah-Hartman 5638ffdff6aSGreg Kroah-Hartman /* set all digital outputs low */ 5648ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_DO_DI_LSB_REG); 5658ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + PCL816_DO_DI_MSB_REG); 5668ffdff6aSGreg Kroah-Hartman } 5678ffdff6aSGreg Kroah-Hartman 5688ffdff6aSGreg Kroah-Hartman static void pcl816_alloc_irq_and_dma(struct comedi_device *dev, 5698ffdff6aSGreg Kroah-Hartman struct comedi_devconfig *it) 5708ffdff6aSGreg Kroah-Hartman { 5718ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 5728ffdff6aSGreg Kroah-Hartman unsigned int irq_num = it->options[1]; 5738ffdff6aSGreg Kroah-Hartman unsigned int dma_chan = it->options[2]; 5748ffdff6aSGreg Kroah-Hartman 5758ffdff6aSGreg Kroah-Hartman /* only IRQs 2-7 and DMA channels 3 and 1 are valid */ 5768ffdff6aSGreg Kroah-Hartman if (!(irq_num >= 2 && irq_num <= 7) || 5778ffdff6aSGreg Kroah-Hartman !(dma_chan == 3 || dma_chan == 1)) 5788ffdff6aSGreg Kroah-Hartman return; 5798ffdff6aSGreg Kroah-Hartman 5808ffdff6aSGreg Kroah-Hartman if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev)) 5818ffdff6aSGreg Kroah-Hartman return; 5828ffdff6aSGreg Kroah-Hartman 5838ffdff6aSGreg Kroah-Hartman /* DMA uses two 16K buffers */ 5848ffdff6aSGreg Kroah-Hartman devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan, 5858ffdff6aSGreg Kroah-Hartman PAGE_SIZE * 4, COMEDI_ISADMA_READ); 5868ffdff6aSGreg Kroah-Hartman if (!devpriv->dma) 5878ffdff6aSGreg Kroah-Hartman free_irq(irq_num, dev); 5888ffdff6aSGreg Kroah-Hartman else 5898ffdff6aSGreg Kroah-Hartman dev->irq = irq_num; 5908ffdff6aSGreg Kroah-Hartman } 5918ffdff6aSGreg Kroah-Hartman 5928ffdff6aSGreg Kroah-Hartman static void pcl816_free_dma(struct comedi_device *dev) 5938ffdff6aSGreg Kroah-Hartman { 5948ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv = dev->private; 5958ffdff6aSGreg Kroah-Hartman 5968ffdff6aSGreg Kroah-Hartman if (devpriv) 5978ffdff6aSGreg Kroah-Hartman comedi_isadma_free(devpriv->dma); 5988ffdff6aSGreg Kroah-Hartman } 5998ffdff6aSGreg Kroah-Hartman 6008ffdff6aSGreg Kroah-Hartman static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it) 6018ffdff6aSGreg Kroah-Hartman { 6028ffdff6aSGreg Kroah-Hartman const struct pcl816_board *board = dev->board_ptr; 6038ffdff6aSGreg Kroah-Hartman struct pcl816_private *devpriv; 6048ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 6058ffdff6aSGreg Kroah-Hartman int ret; 6068ffdff6aSGreg Kroah-Hartman 6078ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 6088ffdff6aSGreg Kroah-Hartman if (!devpriv) 6098ffdff6aSGreg Kroah-Hartman return -ENOMEM; 6108ffdff6aSGreg Kroah-Hartman 6118ffdff6aSGreg Kroah-Hartman ret = comedi_request_region(dev, it->options[0], 0x10); 6128ffdff6aSGreg Kroah-Hartman if (ret) 6138ffdff6aSGreg Kroah-Hartman return ret; 6148ffdff6aSGreg Kroah-Hartman 6158ffdff6aSGreg Kroah-Hartman /* an IRQ and DMA are required to support async commands */ 6168ffdff6aSGreg Kroah-Hartman pcl816_alloc_irq_and_dma(dev, it); 6178ffdff6aSGreg Kroah-Hartman 618*fade5e5bSIan Abbott dev->pacer = comedi_8254_io_alloc(dev->iobase + PCL816_TIMER_BASE, 6198ffdff6aSGreg Kroah-Hartman I8254_OSC_BASE_10MHZ, I8254_IO8, 0); 620*fade5e5bSIan Abbott if (IS_ERR(dev->pacer)) 621*fade5e5bSIan Abbott return PTR_ERR(dev->pacer); 6228ffdff6aSGreg Kroah-Hartman 6238ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 4); 6248ffdff6aSGreg Kroah-Hartman if (ret) 6258ffdff6aSGreg Kroah-Hartman return ret; 6268ffdff6aSGreg Kroah-Hartman 6278ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 6288ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 6298ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_CMD_READ | SDF_DIFF; 6308ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 6318ffdff6aSGreg Kroah-Hartman s->maxdata = board->ai_maxdata; 6328ffdff6aSGreg Kroah-Hartman s->range_table = &range_pcl816; 6338ffdff6aSGreg Kroah-Hartman s->insn_read = pcl816_ai_insn_read; 6348ffdff6aSGreg Kroah-Hartman if (dev->irq) { 6358ffdff6aSGreg Kroah-Hartman dev->read_subdev = s; 6368ffdff6aSGreg Kroah-Hartman s->subdev_flags |= SDF_CMD_READ; 6378ffdff6aSGreg Kroah-Hartman s->len_chanlist = board->ai_chanlist; 6388ffdff6aSGreg Kroah-Hartman s->do_cmdtest = pcl816_ai_cmdtest; 6398ffdff6aSGreg Kroah-Hartman s->do_cmd = pcl816_ai_cmd; 6408ffdff6aSGreg Kroah-Hartman s->poll = pcl816_ai_poll; 6418ffdff6aSGreg Kroah-Hartman s->cancel = pcl816_ai_cancel; 6428ffdff6aSGreg Kroah-Hartman } 6438ffdff6aSGreg Kroah-Hartman 6448ffdff6aSGreg Kroah-Hartman /* Piggyback Slot1 subdevice */ 6458ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 6468ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 6478ffdff6aSGreg Kroah-Hartman 6488ffdff6aSGreg Kroah-Hartman /* Digital Input subdevice */ 6498ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2]; 6508ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DI; 6518ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE; 6528ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 6538ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 6548ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 6558ffdff6aSGreg Kroah-Hartman s->insn_bits = pcl816_di_insn_bits; 6568ffdff6aSGreg Kroah-Hartman 6578ffdff6aSGreg Kroah-Hartman /* Digital Output subdevice */ 6588ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3]; 6598ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DO; 6608ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 6618ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 6628ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 6638ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 6648ffdff6aSGreg Kroah-Hartman s->insn_bits = pcl816_do_insn_bits; 6658ffdff6aSGreg Kroah-Hartman 6668ffdff6aSGreg Kroah-Hartman pcl816_reset(dev); 6678ffdff6aSGreg Kroah-Hartman 6688ffdff6aSGreg Kroah-Hartman return 0; 6698ffdff6aSGreg Kroah-Hartman } 6708ffdff6aSGreg Kroah-Hartman 6718ffdff6aSGreg Kroah-Hartman static void pcl816_detach(struct comedi_device *dev) 6728ffdff6aSGreg Kroah-Hartman { 6738ffdff6aSGreg Kroah-Hartman if (dev->private) { 6748ffdff6aSGreg Kroah-Hartman pcl816_ai_cancel(dev, dev->read_subdev); 6758ffdff6aSGreg Kroah-Hartman pcl816_reset(dev); 6768ffdff6aSGreg Kroah-Hartman } 6778ffdff6aSGreg Kroah-Hartman pcl816_free_dma(dev); 6788ffdff6aSGreg Kroah-Hartman comedi_legacy_detach(dev); 6798ffdff6aSGreg Kroah-Hartman } 6808ffdff6aSGreg Kroah-Hartman 6818ffdff6aSGreg Kroah-Hartman static struct comedi_driver pcl816_driver = { 6828ffdff6aSGreg Kroah-Hartman .driver_name = "pcl816", 6838ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 6848ffdff6aSGreg Kroah-Hartman .attach = pcl816_attach, 6858ffdff6aSGreg Kroah-Hartman .detach = pcl816_detach, 6868ffdff6aSGreg Kroah-Hartman .board_name = &boardtypes[0].name, 6878ffdff6aSGreg Kroah-Hartman .num_names = ARRAY_SIZE(boardtypes), 6888ffdff6aSGreg Kroah-Hartman .offset = sizeof(struct pcl816_board), 6898ffdff6aSGreg Kroah-Hartman }; 6908ffdff6aSGreg Kroah-Hartman module_comedi_driver(pcl816_driver); 6918ffdff6aSGreg Kroah-Hartman 6928ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org"); 6938ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi low-level driver"); 6948ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 695