1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * ii_pci20kc.c 4*8ffdff6aSGreg Kroah-Hartman * Driver for Intelligent Instruments PCI-20001C carrier board and modules. 5*8ffdff6aSGreg Kroah-Hartman * 6*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de> 7*8ffdff6aSGreg Kroah-Hartman * with suggestions from David Schleef 16.06.2000 8*8ffdff6aSGreg Kroah-Hartman */ 9*8ffdff6aSGreg Kroah-Hartman 10*8ffdff6aSGreg Kroah-Hartman /* 11*8ffdff6aSGreg Kroah-Hartman * Driver: ii_pci20kc 12*8ffdff6aSGreg Kroah-Hartman * Description: Intelligent Instruments PCI-20001C carrier board 13*8ffdff6aSGreg Kroah-Hartman * Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc) 14*8ffdff6aSGreg Kroah-Hartman * Author: Markus Kempf <kempf@matsci.uni-sb.de> 15*8ffdff6aSGreg Kroah-Hartman * Status: works 16*8ffdff6aSGreg Kroah-Hartman * 17*8ffdff6aSGreg Kroah-Hartman * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The 18*8ffdff6aSGreg Kroah-Hartman * -2a version has 32 on-board DIO channels. Three add-on modules 19*8ffdff6aSGreg Kroah-Hartman * can be added to the carrier board for additional functionality. 20*8ffdff6aSGreg Kroah-Hartman * 21*8ffdff6aSGreg Kroah-Hartman * Supported add-on modules: 22*8ffdff6aSGreg Kroah-Hartman * PCI-20006M-1 1 channel, 16-bit analog output module 23*8ffdff6aSGreg Kroah-Hartman * PCI-20006M-2 2 channel, 16-bit analog output module 24*8ffdff6aSGreg Kroah-Hartman * PCI-20341M-1A 4 channel, 16-bit analog input module 25*8ffdff6aSGreg Kroah-Hartman * 26*8ffdff6aSGreg Kroah-Hartman * Options: 27*8ffdff6aSGreg Kroah-Hartman * 0 Board base address 28*8ffdff6aSGreg Kroah-Hartman * 1 IRQ (not-used) 29*8ffdff6aSGreg Kroah-Hartman */ 30*8ffdff6aSGreg Kroah-Hartman 31*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 32*8ffdff6aSGreg Kroah-Hartman #include <linux/io.h> 33*8ffdff6aSGreg Kroah-Hartman #include "../comedidev.h" 34*8ffdff6aSGreg Kroah-Hartman 35*8ffdff6aSGreg Kroah-Hartman /* 36*8ffdff6aSGreg Kroah-Hartman * Register I/O map 37*8ffdff6aSGreg Kroah-Hartman */ 38*8ffdff6aSGreg Kroah-Hartman #define II20K_SIZE 0x400 39*8ffdff6aSGreg Kroah-Hartman #define II20K_MOD_OFFSET 0x100 40*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_REG 0x00 41*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_MOD1_EMPTY BIT(7) 42*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_MOD2_EMPTY BIT(6) 43*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_MOD3_EMPTY BIT(5) 44*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_MASK 0x1f 45*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_PCI20001C_1A 0x1b /* no on-board DIO */ 46*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_PCI20001C_2A 0x1d /* on-board DIO */ 47*8ffdff6aSGreg Kroah-Hartman #define II20K_MOD_STATUS_REG 0x40 48*8ffdff6aSGreg Kroah-Hartman #define II20K_MOD_STATUS_IRQ_MOD1 BIT(7) 49*8ffdff6aSGreg Kroah-Hartman #define II20K_MOD_STATUS_IRQ_MOD2 BIT(6) 50*8ffdff6aSGreg Kroah-Hartman #define II20K_MOD_STATUS_IRQ_MOD3 BIT(5) 51*8ffdff6aSGreg Kroah-Hartman #define II20K_DIO0_REG 0x80 52*8ffdff6aSGreg Kroah-Hartman #define II20K_DIO1_REG 0x81 53*8ffdff6aSGreg Kroah-Hartman #define II20K_DIR_ENA_REG 0x82 54*8ffdff6aSGreg Kroah-Hartman #define II20K_DIR_DIO3_OUT BIT(7) 55*8ffdff6aSGreg Kroah-Hartman #define II20K_DIR_DIO2_OUT BIT(6) 56*8ffdff6aSGreg Kroah-Hartman #define II20K_BUF_DISAB_DIO3 BIT(5) 57*8ffdff6aSGreg Kroah-Hartman #define II20K_BUF_DISAB_DIO2 BIT(4) 58*8ffdff6aSGreg Kroah-Hartman #define II20K_DIR_DIO1_OUT BIT(3) 59*8ffdff6aSGreg Kroah-Hartman #define II20K_DIR_DIO0_OUT BIT(2) 60*8ffdff6aSGreg Kroah-Hartman #define II20K_BUF_DISAB_DIO1 BIT(1) 61*8ffdff6aSGreg Kroah-Hartman #define II20K_BUF_DISAB_DIO0 BIT(0) 62*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL01_REG 0x83 63*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL01_SET BIT(7) 64*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL01_DIO0_IN BIT(4) 65*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL01_DIO1_IN BIT(1) 66*8ffdff6aSGreg Kroah-Hartman #define II20K_DIO2_REG 0xc0 67*8ffdff6aSGreg Kroah-Hartman #define II20K_DIO3_REG 0xc1 68*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL23_REG 0xc3 69*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL23_SET BIT(7) 70*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL23_DIO2_IN BIT(4) 71*8ffdff6aSGreg Kroah-Hartman #define II20K_CTRL23_DIO3_IN BIT(1) 72*8ffdff6aSGreg Kroah-Hartman 73*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_PCI20006M_1 0xe2 /* 1 AO channels */ 74*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_PCI20006M_2 0xe3 /* 2 AO channels */ 75*8ffdff6aSGreg Kroah-Hartman #define II20K_AO_STRB_REG(x) (0x0b + ((x) * 0x08)) 76*8ffdff6aSGreg Kroah-Hartman #define II20K_AO_LSB_REG(x) (0x0d + ((x) * 0x08)) 77*8ffdff6aSGreg Kroah-Hartman #define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08)) 78*8ffdff6aSGreg Kroah-Hartman #define II20K_AO_STRB_BOTH_REG 0x1b 79*8ffdff6aSGreg Kroah-Hartman 80*8ffdff6aSGreg Kroah-Hartman #define II20K_ID_PCI20341M_1 0x77 /* 4 AI channels */ 81*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_CMD_REG 0x01 82*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_CMD_BUSY BIT(7) 83*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_CMD_HW_ENA BIT(1) 84*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_CMD_EXT_START BIT(0) 85*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_LSB_REG 0x02 86*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_MSB_REG 0x03 87*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_PACER_RESET_REG 0x04 88*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_16BIT_DATA_REG 0x06 89*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CONF_REG 0x10 90*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CONF_ENA BIT(2) 91*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_OPT_REG 0x11 92*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_OPT_TRIG_ENA BIT(5) 93*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_OPT_TRIG_INV BIT(4) 94*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1) 95*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_OPT_BURST_MODE BIT(0) 96*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_REG 0x12 97*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_INT BIT(7) 98*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_TRIG BIT(6) 99*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_TRIG_ENA BIT(5) 100*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_PACER_ERR BIT(2) 101*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_DATA_ERR BIT(1) 102*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_STATUS_SET_TIME_ERR BIT(0) 103*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_LAST_CHAN_ADDR_REG 0x13 104*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CUR_ADDR_REG 0x14 105*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_SET_TIME_REG 0x15 106*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_DELAY_LSB_REG 0x16 107*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_DELAY_MSB_REG 0x17 108*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHAN_ADV_REG 0x18 109*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHAN_RESET_REG 0x19 110*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_START_TRIG_REG 0x1a 111*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_COUNT_RESET_REG 0x1b 112*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_REG 0x80 113*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_ONBOARD_ONLY BIT(5) 114*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3) 115*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_MUX_ENA BIT(2) 116*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0) 117*8ffdff6aSGreg Kroah-Hartman #define II20K_AI_CHANLIST_LEN 0x80 118*8ffdff6aSGreg Kroah-Hartman 119*8ffdff6aSGreg Kroah-Hartman /* the AO range is set by jumpers on the 20006M module */ 120*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange ii20k_ao_ranges = { 121*8ffdff6aSGreg Kroah-Hartman 3, { 122*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), /* Chan 0 - W1/W3 in Chan 1 - W2/W4 in */ 123*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(10), /* Chan 0 - W1/W3 out Chan 1 - W2/W4 in */ 124*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */ 125*8ffdff6aSGreg Kroah-Hartman } 126*8ffdff6aSGreg Kroah-Hartman }; 127*8ffdff6aSGreg Kroah-Hartman 128*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange ii20k_ai_ranges = { 129*8ffdff6aSGreg Kroah-Hartman 4, { 130*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), /* gain 1 */ 131*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.5), /* gain 10 */ 132*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.05), /* gain 100 */ 133*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.025) /* gain 200 */ 134*8ffdff6aSGreg Kroah-Hartman }, 135*8ffdff6aSGreg Kroah-Hartman }; 136*8ffdff6aSGreg Kroah-Hartman 137*8ffdff6aSGreg Kroah-Hartman static void __iomem *ii20k_module_iobase(struct comedi_device *dev, 138*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 139*8ffdff6aSGreg Kroah-Hartman { 140*8ffdff6aSGreg Kroah-Hartman return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET; 141*8ffdff6aSGreg Kroah-Hartman } 142*8ffdff6aSGreg Kroah-Hartman 143*8ffdff6aSGreg Kroah-Hartman static int ii20k_ao_insn_write(struct comedi_device *dev, 144*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 145*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 146*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 147*8ffdff6aSGreg Kroah-Hartman { 148*8ffdff6aSGreg Kroah-Hartman void __iomem *iobase = ii20k_module_iobase(dev, s); 149*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 150*8ffdff6aSGreg Kroah-Hartman int i; 151*8ffdff6aSGreg Kroah-Hartman 152*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 153*8ffdff6aSGreg Kroah-Hartman unsigned int val = data[i]; 154*8ffdff6aSGreg Kroah-Hartman 155*8ffdff6aSGreg Kroah-Hartman s->readback[chan] = val; 156*8ffdff6aSGreg Kroah-Hartman 157*8ffdff6aSGreg Kroah-Hartman /* munge the offset binary data to 2's complement */ 158*8ffdff6aSGreg Kroah-Hartman val = comedi_offset_munge(s, val); 159*8ffdff6aSGreg Kroah-Hartman 160*8ffdff6aSGreg Kroah-Hartman writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan)); 161*8ffdff6aSGreg Kroah-Hartman writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan)); 162*8ffdff6aSGreg Kroah-Hartman writeb(0x00, iobase + II20K_AO_STRB_REG(chan)); 163*8ffdff6aSGreg Kroah-Hartman } 164*8ffdff6aSGreg Kroah-Hartman 165*8ffdff6aSGreg Kroah-Hartman return insn->n; 166*8ffdff6aSGreg Kroah-Hartman } 167*8ffdff6aSGreg Kroah-Hartman 168*8ffdff6aSGreg Kroah-Hartman static int ii20k_ai_eoc(struct comedi_device *dev, 169*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 170*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 171*8ffdff6aSGreg Kroah-Hartman unsigned long context) 172*8ffdff6aSGreg Kroah-Hartman { 173*8ffdff6aSGreg Kroah-Hartman void __iomem *iobase = ii20k_module_iobase(dev, s); 174*8ffdff6aSGreg Kroah-Hartman unsigned char status; 175*8ffdff6aSGreg Kroah-Hartman 176*8ffdff6aSGreg Kroah-Hartman status = readb(iobase + II20K_AI_STATUS_REG); 177*8ffdff6aSGreg Kroah-Hartman if ((status & II20K_AI_STATUS_INT) == 0) 178*8ffdff6aSGreg Kroah-Hartman return 0; 179*8ffdff6aSGreg Kroah-Hartman return -EBUSY; 180*8ffdff6aSGreg Kroah-Hartman } 181*8ffdff6aSGreg Kroah-Hartman 182*8ffdff6aSGreg Kroah-Hartman static void ii20k_ai_setup(struct comedi_device *dev, 183*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 184*8ffdff6aSGreg Kroah-Hartman unsigned int chanspec) 185*8ffdff6aSGreg Kroah-Hartman { 186*8ffdff6aSGreg Kroah-Hartman void __iomem *iobase = ii20k_module_iobase(dev, s); 187*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(chanspec); 188*8ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(chanspec); 189*8ffdff6aSGreg Kroah-Hartman unsigned char val; 190*8ffdff6aSGreg Kroah-Hartman 191*8ffdff6aSGreg Kroah-Hartman /* initialize module */ 192*8ffdff6aSGreg Kroah-Hartman writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG); 193*8ffdff6aSGreg Kroah-Hartman 194*8ffdff6aSGreg Kroah-Hartman /* software conversion */ 195*8ffdff6aSGreg Kroah-Hartman writeb(0, iobase + II20K_AI_STATUS_CMD_REG); 196*8ffdff6aSGreg Kroah-Hartman 197*8ffdff6aSGreg Kroah-Hartman /* set the time base for the settling time counter based on the gain */ 198*8ffdff6aSGreg Kroah-Hartman val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2); 199*8ffdff6aSGreg Kroah-Hartman writeb(val, iobase + II20K_AI_OPT_REG); 200*8ffdff6aSGreg Kroah-Hartman 201*8ffdff6aSGreg Kroah-Hartman /* set the settling time counter based on the gain */ 202*8ffdff6aSGreg Kroah-Hartman val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99; 203*8ffdff6aSGreg Kroah-Hartman writeb(val, iobase + II20K_AI_SET_TIME_REG); 204*8ffdff6aSGreg Kroah-Hartman 205*8ffdff6aSGreg Kroah-Hartman /* set number of input channels */ 206*8ffdff6aSGreg Kroah-Hartman writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG); 207*8ffdff6aSGreg Kroah-Hartman 208*8ffdff6aSGreg Kroah-Hartman /* set the channel list byte */ 209*8ffdff6aSGreg Kroah-Hartman val = II20K_AI_CHANLIST_ONBOARD_ONLY | 210*8ffdff6aSGreg Kroah-Hartman II20K_AI_CHANLIST_MUX_ENA | 211*8ffdff6aSGreg Kroah-Hartman II20K_AI_CHANLIST_GAIN(range) | 212*8ffdff6aSGreg Kroah-Hartman II20K_AI_CHANLIST_CHAN(chan); 213*8ffdff6aSGreg Kroah-Hartman writeb(val, iobase + II20K_AI_CHANLIST_REG); 214*8ffdff6aSGreg Kroah-Hartman 215*8ffdff6aSGreg Kroah-Hartman /* reset settling time counter and trigger delay counter */ 216*8ffdff6aSGreg Kroah-Hartman writeb(0, iobase + II20K_AI_COUNT_RESET_REG); 217*8ffdff6aSGreg Kroah-Hartman 218*8ffdff6aSGreg Kroah-Hartman /* reset channel scanner */ 219*8ffdff6aSGreg Kroah-Hartman writeb(0, iobase + II20K_AI_CHAN_RESET_REG); 220*8ffdff6aSGreg Kroah-Hartman } 221*8ffdff6aSGreg Kroah-Hartman 222*8ffdff6aSGreg Kroah-Hartman static int ii20k_ai_insn_read(struct comedi_device *dev, 223*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 224*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 225*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 226*8ffdff6aSGreg Kroah-Hartman { 227*8ffdff6aSGreg Kroah-Hartman void __iomem *iobase = ii20k_module_iobase(dev, s); 228*8ffdff6aSGreg Kroah-Hartman int ret; 229*8ffdff6aSGreg Kroah-Hartman int i; 230*8ffdff6aSGreg Kroah-Hartman 231*8ffdff6aSGreg Kroah-Hartman ii20k_ai_setup(dev, s, insn->chanspec); 232*8ffdff6aSGreg Kroah-Hartman 233*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 234*8ffdff6aSGreg Kroah-Hartman unsigned int val; 235*8ffdff6aSGreg Kroah-Hartman 236*8ffdff6aSGreg Kroah-Hartman /* generate a software start convert signal */ 237*8ffdff6aSGreg Kroah-Hartman readb(iobase + II20K_AI_PACER_RESET_REG); 238*8ffdff6aSGreg Kroah-Hartman 239*8ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0); 240*8ffdff6aSGreg Kroah-Hartman if (ret) 241*8ffdff6aSGreg Kroah-Hartman return ret; 242*8ffdff6aSGreg Kroah-Hartman 243*8ffdff6aSGreg Kroah-Hartman val = readb(iobase + II20K_AI_LSB_REG); 244*8ffdff6aSGreg Kroah-Hartman val |= (readb(iobase + II20K_AI_MSB_REG) << 8); 245*8ffdff6aSGreg Kroah-Hartman 246*8ffdff6aSGreg Kroah-Hartman /* munge the 2's complement data to offset binary */ 247*8ffdff6aSGreg Kroah-Hartman data[i] = comedi_offset_munge(s, val); 248*8ffdff6aSGreg Kroah-Hartman } 249*8ffdff6aSGreg Kroah-Hartman 250*8ffdff6aSGreg Kroah-Hartman return insn->n; 251*8ffdff6aSGreg Kroah-Hartman } 252*8ffdff6aSGreg Kroah-Hartman 253*8ffdff6aSGreg Kroah-Hartman static void ii20k_dio_config(struct comedi_device *dev, 254*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 255*8ffdff6aSGreg Kroah-Hartman { 256*8ffdff6aSGreg Kroah-Hartman unsigned char ctrl01 = 0; 257*8ffdff6aSGreg Kroah-Hartman unsigned char ctrl23 = 0; 258*8ffdff6aSGreg Kroah-Hartman unsigned char dir_ena = 0; 259*8ffdff6aSGreg Kroah-Hartman 260*8ffdff6aSGreg Kroah-Hartman /* port 0 - channels 0-7 */ 261*8ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0x000000ff) { 262*8ffdff6aSGreg Kroah-Hartman /* output port */ 263*8ffdff6aSGreg Kroah-Hartman ctrl01 &= ~II20K_CTRL01_DIO0_IN; 264*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_BUF_DISAB_DIO0; 265*8ffdff6aSGreg Kroah-Hartman dir_ena |= II20K_DIR_DIO0_OUT; 266*8ffdff6aSGreg Kroah-Hartman } else { 267*8ffdff6aSGreg Kroah-Hartman /* input port */ 268*8ffdff6aSGreg Kroah-Hartman ctrl01 |= II20K_CTRL01_DIO0_IN; 269*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_DIR_DIO0_OUT; 270*8ffdff6aSGreg Kroah-Hartman } 271*8ffdff6aSGreg Kroah-Hartman 272*8ffdff6aSGreg Kroah-Hartman /* port 1 - channels 8-15 */ 273*8ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0x0000ff00) { 274*8ffdff6aSGreg Kroah-Hartman /* output port */ 275*8ffdff6aSGreg Kroah-Hartman ctrl01 &= ~II20K_CTRL01_DIO1_IN; 276*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_BUF_DISAB_DIO1; 277*8ffdff6aSGreg Kroah-Hartman dir_ena |= II20K_DIR_DIO1_OUT; 278*8ffdff6aSGreg Kroah-Hartman } else { 279*8ffdff6aSGreg Kroah-Hartman /* input port */ 280*8ffdff6aSGreg Kroah-Hartman ctrl01 |= II20K_CTRL01_DIO1_IN; 281*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_DIR_DIO1_OUT; 282*8ffdff6aSGreg Kroah-Hartman } 283*8ffdff6aSGreg Kroah-Hartman 284*8ffdff6aSGreg Kroah-Hartman /* port 2 - channels 16-23 */ 285*8ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0x00ff0000) { 286*8ffdff6aSGreg Kroah-Hartman /* output port */ 287*8ffdff6aSGreg Kroah-Hartman ctrl23 &= ~II20K_CTRL23_DIO2_IN; 288*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_BUF_DISAB_DIO2; 289*8ffdff6aSGreg Kroah-Hartman dir_ena |= II20K_DIR_DIO2_OUT; 290*8ffdff6aSGreg Kroah-Hartman } else { 291*8ffdff6aSGreg Kroah-Hartman /* input port */ 292*8ffdff6aSGreg Kroah-Hartman ctrl23 |= II20K_CTRL23_DIO2_IN; 293*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_DIR_DIO2_OUT; 294*8ffdff6aSGreg Kroah-Hartman } 295*8ffdff6aSGreg Kroah-Hartman 296*8ffdff6aSGreg Kroah-Hartman /* port 3 - channels 24-31 */ 297*8ffdff6aSGreg Kroah-Hartman if (s->io_bits & 0xff000000) { 298*8ffdff6aSGreg Kroah-Hartman /* output port */ 299*8ffdff6aSGreg Kroah-Hartman ctrl23 &= ~II20K_CTRL23_DIO3_IN; 300*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_BUF_DISAB_DIO3; 301*8ffdff6aSGreg Kroah-Hartman dir_ena |= II20K_DIR_DIO3_OUT; 302*8ffdff6aSGreg Kroah-Hartman } else { 303*8ffdff6aSGreg Kroah-Hartman /* input port */ 304*8ffdff6aSGreg Kroah-Hartman ctrl23 |= II20K_CTRL23_DIO3_IN; 305*8ffdff6aSGreg Kroah-Hartman dir_ena &= ~II20K_DIR_DIO3_OUT; 306*8ffdff6aSGreg Kroah-Hartman } 307*8ffdff6aSGreg Kroah-Hartman 308*8ffdff6aSGreg Kroah-Hartman ctrl23 |= II20K_CTRL01_SET; 309*8ffdff6aSGreg Kroah-Hartman ctrl23 |= II20K_CTRL23_SET; 310*8ffdff6aSGreg Kroah-Hartman 311*8ffdff6aSGreg Kroah-Hartman /* order is important */ 312*8ffdff6aSGreg Kroah-Hartman writeb(ctrl01, dev->mmio + II20K_CTRL01_REG); 313*8ffdff6aSGreg Kroah-Hartman writeb(ctrl23, dev->mmio + II20K_CTRL23_REG); 314*8ffdff6aSGreg Kroah-Hartman writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG); 315*8ffdff6aSGreg Kroah-Hartman } 316*8ffdff6aSGreg Kroah-Hartman 317*8ffdff6aSGreg Kroah-Hartman static int ii20k_dio_insn_config(struct comedi_device *dev, 318*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 319*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 320*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 321*8ffdff6aSGreg Kroah-Hartman { 322*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 323*8ffdff6aSGreg Kroah-Hartman unsigned int mask; 324*8ffdff6aSGreg Kroah-Hartman int ret; 325*8ffdff6aSGreg Kroah-Hartman 326*8ffdff6aSGreg Kroah-Hartman if (chan < 8) 327*8ffdff6aSGreg Kroah-Hartman mask = 0x000000ff; 328*8ffdff6aSGreg Kroah-Hartman else if (chan < 16) 329*8ffdff6aSGreg Kroah-Hartman mask = 0x0000ff00; 330*8ffdff6aSGreg Kroah-Hartman else if (chan < 24) 331*8ffdff6aSGreg Kroah-Hartman mask = 0x00ff0000; 332*8ffdff6aSGreg Kroah-Hartman else 333*8ffdff6aSGreg Kroah-Hartman mask = 0xff000000; 334*8ffdff6aSGreg Kroah-Hartman 335*8ffdff6aSGreg Kroah-Hartman ret = comedi_dio_insn_config(dev, s, insn, data, mask); 336*8ffdff6aSGreg Kroah-Hartman if (ret) 337*8ffdff6aSGreg Kroah-Hartman return ret; 338*8ffdff6aSGreg Kroah-Hartman 339*8ffdff6aSGreg Kroah-Hartman ii20k_dio_config(dev, s); 340*8ffdff6aSGreg Kroah-Hartman 341*8ffdff6aSGreg Kroah-Hartman return insn->n; 342*8ffdff6aSGreg Kroah-Hartman } 343*8ffdff6aSGreg Kroah-Hartman 344*8ffdff6aSGreg Kroah-Hartman static int ii20k_dio_insn_bits(struct comedi_device *dev, 345*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 346*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 347*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 348*8ffdff6aSGreg Kroah-Hartman { 349*8ffdff6aSGreg Kroah-Hartman unsigned int mask; 350*8ffdff6aSGreg Kroah-Hartman 351*8ffdff6aSGreg Kroah-Hartman mask = comedi_dio_update_state(s, data); 352*8ffdff6aSGreg Kroah-Hartman if (mask) { 353*8ffdff6aSGreg Kroah-Hartman if (mask & 0x000000ff) 354*8ffdff6aSGreg Kroah-Hartman writeb((s->state >> 0) & 0xff, 355*8ffdff6aSGreg Kroah-Hartman dev->mmio + II20K_DIO0_REG); 356*8ffdff6aSGreg Kroah-Hartman if (mask & 0x0000ff00) 357*8ffdff6aSGreg Kroah-Hartman writeb((s->state >> 8) & 0xff, 358*8ffdff6aSGreg Kroah-Hartman dev->mmio + II20K_DIO1_REG); 359*8ffdff6aSGreg Kroah-Hartman if (mask & 0x00ff0000) 360*8ffdff6aSGreg Kroah-Hartman writeb((s->state >> 16) & 0xff, 361*8ffdff6aSGreg Kroah-Hartman dev->mmio + II20K_DIO2_REG); 362*8ffdff6aSGreg Kroah-Hartman if (mask & 0xff000000) 363*8ffdff6aSGreg Kroah-Hartman writeb((s->state >> 24) & 0xff, 364*8ffdff6aSGreg Kroah-Hartman dev->mmio + II20K_DIO3_REG); 365*8ffdff6aSGreg Kroah-Hartman } 366*8ffdff6aSGreg Kroah-Hartman 367*8ffdff6aSGreg Kroah-Hartman data[1] = readb(dev->mmio + II20K_DIO0_REG); 368*8ffdff6aSGreg Kroah-Hartman data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8; 369*8ffdff6aSGreg Kroah-Hartman data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16; 370*8ffdff6aSGreg Kroah-Hartman data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24; 371*8ffdff6aSGreg Kroah-Hartman 372*8ffdff6aSGreg Kroah-Hartman return insn->n; 373*8ffdff6aSGreg Kroah-Hartman } 374*8ffdff6aSGreg Kroah-Hartman 375*8ffdff6aSGreg Kroah-Hartman static int ii20k_init_module(struct comedi_device *dev, 376*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 377*8ffdff6aSGreg Kroah-Hartman { 378*8ffdff6aSGreg Kroah-Hartman void __iomem *iobase = ii20k_module_iobase(dev, s); 379*8ffdff6aSGreg Kroah-Hartman unsigned char id; 380*8ffdff6aSGreg Kroah-Hartman int ret; 381*8ffdff6aSGreg Kroah-Hartman 382*8ffdff6aSGreg Kroah-Hartman id = readb(iobase + II20K_ID_REG); 383*8ffdff6aSGreg Kroah-Hartman switch (id) { 384*8ffdff6aSGreg Kroah-Hartman case II20K_ID_PCI20006M_1: 385*8ffdff6aSGreg Kroah-Hartman case II20K_ID_PCI20006M_2: 386*8ffdff6aSGreg Kroah-Hartman /* Analog Output subdevice */ 387*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AO; 388*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 389*8ffdff6aSGreg Kroah-Hartman s->n_chan = (id == II20K_ID_PCI20006M_2) ? 2 : 1; 390*8ffdff6aSGreg Kroah-Hartman s->maxdata = 0xffff; 391*8ffdff6aSGreg Kroah-Hartman s->range_table = &ii20k_ao_ranges; 392*8ffdff6aSGreg Kroah-Hartman s->insn_write = ii20k_ao_insn_write; 393*8ffdff6aSGreg Kroah-Hartman 394*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdev_readback(s); 395*8ffdff6aSGreg Kroah-Hartman if (ret) 396*8ffdff6aSGreg Kroah-Hartman return ret; 397*8ffdff6aSGreg Kroah-Hartman break; 398*8ffdff6aSGreg Kroah-Hartman case II20K_ID_PCI20341M_1: 399*8ffdff6aSGreg Kroah-Hartman /* Analog Input subdevice */ 400*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 401*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_DIFF; 402*8ffdff6aSGreg Kroah-Hartman s->n_chan = 4; 403*8ffdff6aSGreg Kroah-Hartman s->maxdata = 0xffff; 404*8ffdff6aSGreg Kroah-Hartman s->range_table = &ii20k_ai_ranges; 405*8ffdff6aSGreg Kroah-Hartman s->insn_read = ii20k_ai_insn_read; 406*8ffdff6aSGreg Kroah-Hartman break; 407*8ffdff6aSGreg Kroah-Hartman default: 408*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 409*8ffdff6aSGreg Kroah-Hartman break; 410*8ffdff6aSGreg Kroah-Hartman } 411*8ffdff6aSGreg Kroah-Hartman 412*8ffdff6aSGreg Kroah-Hartman return 0; 413*8ffdff6aSGreg Kroah-Hartman } 414*8ffdff6aSGreg Kroah-Hartman 415*8ffdff6aSGreg Kroah-Hartman static int ii20k_attach(struct comedi_device *dev, 416*8ffdff6aSGreg Kroah-Hartman struct comedi_devconfig *it) 417*8ffdff6aSGreg Kroah-Hartman { 418*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 419*8ffdff6aSGreg Kroah-Hartman unsigned int membase; 420*8ffdff6aSGreg Kroah-Hartman unsigned char id; 421*8ffdff6aSGreg Kroah-Hartman bool has_dio; 422*8ffdff6aSGreg Kroah-Hartman int ret; 423*8ffdff6aSGreg Kroah-Hartman 424*8ffdff6aSGreg Kroah-Hartman membase = it->options[0]; 425*8ffdff6aSGreg Kroah-Hartman if (!membase || (membase & ~(0x100000 - II20K_SIZE))) { 426*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 427*8ffdff6aSGreg Kroah-Hartman "%s: invalid memory address specified\n", 428*8ffdff6aSGreg Kroah-Hartman dev->board_name); 429*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 430*8ffdff6aSGreg Kroah-Hartman } 431*8ffdff6aSGreg Kroah-Hartman 432*8ffdff6aSGreg Kroah-Hartman if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) { 433*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n", 434*8ffdff6aSGreg Kroah-Hartman dev->board_name, membase, II20K_SIZE); 435*8ffdff6aSGreg Kroah-Hartman return -EIO; 436*8ffdff6aSGreg Kroah-Hartman } 437*8ffdff6aSGreg Kroah-Hartman dev->iobase = membase; /* actually, a memory address */ 438*8ffdff6aSGreg Kroah-Hartman 439*8ffdff6aSGreg Kroah-Hartman dev->mmio = ioremap(membase, II20K_SIZE); 440*8ffdff6aSGreg Kroah-Hartman if (!dev->mmio) 441*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 442*8ffdff6aSGreg Kroah-Hartman 443*8ffdff6aSGreg Kroah-Hartman id = readb(dev->mmio + II20K_ID_REG); 444*8ffdff6aSGreg Kroah-Hartman switch (id & II20K_ID_MASK) { 445*8ffdff6aSGreg Kroah-Hartman case II20K_ID_PCI20001C_1A: 446*8ffdff6aSGreg Kroah-Hartman has_dio = false; 447*8ffdff6aSGreg Kroah-Hartman break; 448*8ffdff6aSGreg Kroah-Hartman case II20K_ID_PCI20001C_2A: 449*8ffdff6aSGreg Kroah-Hartman has_dio = true; 450*8ffdff6aSGreg Kroah-Hartman break; 451*8ffdff6aSGreg Kroah-Hartman default: 452*8ffdff6aSGreg Kroah-Hartman return -ENODEV; 453*8ffdff6aSGreg Kroah-Hartman } 454*8ffdff6aSGreg Kroah-Hartman 455*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 4); 456*8ffdff6aSGreg Kroah-Hartman if (ret) 457*8ffdff6aSGreg Kroah-Hartman return ret; 458*8ffdff6aSGreg Kroah-Hartman 459*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 460*8ffdff6aSGreg Kroah-Hartman if (id & II20K_ID_MOD1_EMPTY) { 461*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 462*8ffdff6aSGreg Kroah-Hartman } else { 463*8ffdff6aSGreg Kroah-Hartman ret = ii20k_init_module(dev, s); 464*8ffdff6aSGreg Kroah-Hartman if (ret) 465*8ffdff6aSGreg Kroah-Hartman return ret; 466*8ffdff6aSGreg Kroah-Hartman } 467*8ffdff6aSGreg Kroah-Hartman 468*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 469*8ffdff6aSGreg Kroah-Hartman if (id & II20K_ID_MOD2_EMPTY) { 470*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 471*8ffdff6aSGreg Kroah-Hartman } else { 472*8ffdff6aSGreg Kroah-Hartman ret = ii20k_init_module(dev, s); 473*8ffdff6aSGreg Kroah-Hartman if (ret) 474*8ffdff6aSGreg Kroah-Hartman return ret; 475*8ffdff6aSGreg Kroah-Hartman } 476*8ffdff6aSGreg Kroah-Hartman 477*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2]; 478*8ffdff6aSGreg Kroah-Hartman if (id & II20K_ID_MOD3_EMPTY) { 479*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 480*8ffdff6aSGreg Kroah-Hartman } else { 481*8ffdff6aSGreg Kroah-Hartman ret = ii20k_init_module(dev, s); 482*8ffdff6aSGreg Kroah-Hartman if (ret) 483*8ffdff6aSGreg Kroah-Hartman return ret; 484*8ffdff6aSGreg Kroah-Hartman } 485*8ffdff6aSGreg Kroah-Hartman 486*8ffdff6aSGreg Kroah-Hartman /* Digital I/O subdevice */ 487*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3]; 488*8ffdff6aSGreg Kroah-Hartman if (has_dio) { 489*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DIO; 490*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 491*8ffdff6aSGreg Kroah-Hartman s->n_chan = 32; 492*8ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 493*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 494*8ffdff6aSGreg Kroah-Hartman s->insn_bits = ii20k_dio_insn_bits; 495*8ffdff6aSGreg Kroah-Hartman s->insn_config = ii20k_dio_insn_config; 496*8ffdff6aSGreg Kroah-Hartman 497*8ffdff6aSGreg Kroah-Hartman /* default all channels to input */ 498*8ffdff6aSGreg Kroah-Hartman ii20k_dio_config(dev, s); 499*8ffdff6aSGreg Kroah-Hartman } else { 500*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 501*8ffdff6aSGreg Kroah-Hartman } 502*8ffdff6aSGreg Kroah-Hartman 503*8ffdff6aSGreg Kroah-Hartman return 0; 504*8ffdff6aSGreg Kroah-Hartman } 505*8ffdff6aSGreg Kroah-Hartman 506*8ffdff6aSGreg Kroah-Hartman static void ii20k_detach(struct comedi_device *dev) 507*8ffdff6aSGreg Kroah-Hartman { 508*8ffdff6aSGreg Kroah-Hartman if (dev->mmio) 509*8ffdff6aSGreg Kroah-Hartman iounmap(dev->mmio); 510*8ffdff6aSGreg Kroah-Hartman if (dev->iobase) /* actually, a memory address */ 511*8ffdff6aSGreg Kroah-Hartman release_mem_region(dev->iobase, II20K_SIZE); 512*8ffdff6aSGreg Kroah-Hartman } 513*8ffdff6aSGreg Kroah-Hartman 514*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver ii20k_driver = { 515*8ffdff6aSGreg Kroah-Hartman .driver_name = "ii_pci20kc", 516*8ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 517*8ffdff6aSGreg Kroah-Hartman .attach = ii20k_attach, 518*8ffdff6aSGreg Kroah-Hartman .detach = ii20k_detach, 519*8ffdff6aSGreg Kroah-Hartman }; 520*8ffdff6aSGreg Kroah-Hartman module_comedi_driver(ii20k_driver); 521*8ffdff6aSGreg Kroah-Hartman 522*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org"); 523*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for Intelligent Instruments PCI-20001C"); 524*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 525