1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * dt3000.c 4*8ffdff6aSGreg Kroah-Hartman * Data Translation DT3000 series driver 5*8ffdff6aSGreg Kroah-Hartman * 6*8ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 7*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 1999 David A. Schleef <ds@schleef.org> 8*8ffdff6aSGreg Kroah-Hartman */ 9*8ffdff6aSGreg Kroah-Hartman 10*8ffdff6aSGreg Kroah-Hartman /* 11*8ffdff6aSGreg Kroah-Hartman * Driver: dt3000 12*8ffdff6aSGreg Kroah-Hartman * Description: Data Translation DT3000 series 13*8ffdff6aSGreg Kroah-Hartman * Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003, 14*8ffdff6aSGreg Kroah-Hartman * DT3003-PGL, DT3004, DT3005, DT3004-200 15*8ffdff6aSGreg Kroah-Hartman * Author: ds 16*8ffdff6aSGreg Kroah-Hartman * Updated: Mon, 14 Apr 2008 15:41:24 +0100 17*8ffdff6aSGreg Kroah-Hartman * Status: works 18*8ffdff6aSGreg Kroah-Hartman * 19*8ffdff6aSGreg Kroah-Hartman * Configuration Options: not applicable, uses PCI auto config 20*8ffdff6aSGreg Kroah-Hartman * 21*8ffdff6aSGreg Kroah-Hartman * There is code to support AI commands, but it may not work. 22*8ffdff6aSGreg Kroah-Hartman * 23*8ffdff6aSGreg Kroah-Hartman * AO commands are not supported. 24*8ffdff6aSGreg Kroah-Hartman */ 25*8ffdff6aSGreg Kroah-Hartman 26*8ffdff6aSGreg Kroah-Hartman /* 27*8ffdff6aSGreg Kroah-Hartman * The DT3000 series is Data Translation's attempt to make a PCI 28*8ffdff6aSGreg Kroah-Hartman * data acquisition board. The design of this series is very nice, 29*8ffdff6aSGreg Kroah-Hartman * since each board has an on-board DSP (Texas Instruments TMS320C52). 30*8ffdff6aSGreg Kroah-Hartman * However, a few details are a little annoying. The boards lack 31*8ffdff6aSGreg Kroah-Hartman * bus-mastering DMA, which eliminates them from serious work. 32*8ffdff6aSGreg Kroah-Hartman * They also are not capable of autocalibration, which is a common 33*8ffdff6aSGreg Kroah-Hartman * feature in modern hardware. The default firmware is pretty bad, 34*8ffdff6aSGreg Kroah-Hartman * making it nearly impossible to write an RT compatible driver. 35*8ffdff6aSGreg Kroah-Hartman * It would make an interesting project to write a decent firmware 36*8ffdff6aSGreg Kroah-Hartman * for these boards. 37*8ffdff6aSGreg Kroah-Hartman * 38*8ffdff6aSGreg Kroah-Hartman * Data Translation originally wanted an NDA for the documentation 39*8ffdff6aSGreg Kroah-Hartman * for the 3k series. However, if you ask nicely, they might send 40*8ffdff6aSGreg Kroah-Hartman * you the docs without one, also. 41*8ffdff6aSGreg Kroah-Hartman */ 42*8ffdff6aSGreg Kroah-Hartman 43*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 44*8ffdff6aSGreg Kroah-Hartman #include <linux/delay.h> 45*8ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 46*8ffdff6aSGreg Kroah-Hartman 47*8ffdff6aSGreg Kroah-Hartman #include "../comedi_pci.h" 48*8ffdff6aSGreg Kroah-Hartman 49*8ffdff6aSGreg Kroah-Hartman /* 50*8ffdff6aSGreg Kroah-Hartman * PCI BAR0 - dual-ported RAM location definitions (dev->mmio) 51*8ffdff6aSGreg Kroah-Hartman */ 52*8ffdff6aSGreg Kroah-Hartman #define DPR_DAC_BUFFER (4 * 0x000) 53*8ffdff6aSGreg Kroah-Hartman #define DPR_ADC_BUFFER (4 * 0x800) 54*8ffdff6aSGreg Kroah-Hartman #define DPR_COMMAND (4 * 0xfd3) 55*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS (4 * 0xfd3) 56*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_AI 0 57*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_AO 1 58*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_DIN 2 59*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_DOUT 3 60*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_MEM 4 61*8ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_CT 5 62*8ffdff6aSGreg Kroah-Hartman #define DPR_ENCODE (4 * 0xfd4) 63*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAMS(x) (4 * (0xfd5 + (x))) 64*8ffdff6aSGreg Kroah-Hartman #define DPR_TICK_REG_LO (4 * 0xff5) 65*8ffdff6aSGreg Kroah-Hartman #define DPR_TICK_REG_HI (4 * 0xff6) 66*8ffdff6aSGreg Kroah-Hartman #define DPR_DA_BUF_FRONT (4 * 0xff7) 67*8ffdff6aSGreg Kroah-Hartman #define DPR_DA_BUF_REAR (4 * 0xff8) 68*8ffdff6aSGreg Kroah-Hartman #define DPR_AD_BUF_FRONT (4 * 0xff9) 69*8ffdff6aSGreg Kroah-Hartman #define DPR_AD_BUF_REAR (4 * 0xffa) 70*8ffdff6aSGreg Kroah-Hartman #define DPR_INT_MASK (4 * 0xffb) 71*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_FLAG (4 * 0xffc) 72*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_CMDONE BIT(7) 73*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_CTDONE BIT(6) 74*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DAHWERR BIT(5) 75*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DASWERR BIT(4) 76*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DAEMPTY BIT(3) 77*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADHWERR BIT(2) 78*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADSWERR BIT(1) 79*8ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADFULL BIT(0) 80*8ffdff6aSGreg Kroah-Hartman #define DPR_RESPONSE_MBX (4 * 0xffe) 81*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_MBX (4 * 0xfff) 82*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_COMPLETION(x) ((x) << 8) 83*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOTPROCESSED DPR_CMD_COMPLETION(0x00) 84*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOERROR DPR_CMD_COMPLETION(0x55) 85*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_ERROR DPR_CMD_COMPLETION(0xaa) 86*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOTSUPPORTED DPR_CMD_COMPLETION(0xff) 87*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_COMPLETION_MASK DPR_CMD_COMPLETION(0xff) 88*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD(x) ((x) << 0) 89*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_GETBRDINFO DPR_CMD(0) 90*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_CONFIG DPR_CMD(1) 91*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_GETCONFIG DPR_CMD(2) 92*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_START DPR_CMD(3) 93*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_STOP DPR_CMD(4) 94*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READSINGLE DPR_CMD(5) 95*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITESINGLE DPR_CMD(6) 96*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_CALCCLOCK DPR_CMD(7) 97*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READEVENTS DPR_CMD(8) 98*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECTCTRL DPR_CMD(16) 99*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCTCTRL DPR_CMD(17) 100*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECT DPR_CMD(18) 101*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCT DPR_CMD(19) 102*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITEDATA DPR_CMD(32) 103*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READDATA DPR_CMD(33) 104*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITEIO DPR_CMD(34) 105*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READIO DPR_CMD(35) 106*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECODE DPR_CMD(36) 107*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCODE DPR_CMD(37) 108*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_EXECUTE DPR_CMD(38) 109*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_HALT DPR_CMD(48) 110*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_MASK DPR_CMD(0xff) 111*8ffdff6aSGreg Kroah-Hartman 112*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG(x) (((x) & 0x7) << 2) 113*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT DPR_PARAM5_AD_TRIG(0) 114*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_EXT DPR_PARAM5_AD_TRIG(1) 115*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT_RETRIG DPR_PARAM5_AD_TRIG(2) 116*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_EXT_RETRIG DPR_PARAM5_AD_TRIG(3) 117*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT_RETRIG2 DPR_PARAM5_AD_TRIG(4) 118*8ffdff6aSGreg Kroah-Hartman 119*8ffdff6aSGreg Kroah-Hartman #define DPR_PARAM6_AD_DIFF BIT(0) 120*8ffdff6aSGreg Kroah-Hartman 121*8ffdff6aSGreg Kroah-Hartman #define DPR_AI_FIFO_DEPTH 2003 122*8ffdff6aSGreg Kroah-Hartman #define DPR_AO_FIFO_DEPTH 2048 123*8ffdff6aSGreg Kroah-Hartman 124*8ffdff6aSGreg Kroah-Hartman #define DPR_EXTERNAL_CLOCK 1 125*8ffdff6aSGreg Kroah-Hartman #define DPR_RISING_EDGE 2 126*8ffdff6aSGreg Kroah-Hartman 127*8ffdff6aSGreg Kroah-Hartman #define DPR_TMODE_MASK 0x1c 128*8ffdff6aSGreg Kroah-Hartman 129*8ffdff6aSGreg Kroah-Hartman #define DPR_CMD_TIMEOUT 100 130*8ffdff6aSGreg Kroah-Hartman 131*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_dt3000_ai = { 132*8ffdff6aSGreg Kroah-Hartman 4, { 133*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 134*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), 135*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(2.5), 136*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(1.25) 137*8ffdff6aSGreg Kroah-Hartman } 138*8ffdff6aSGreg Kroah-Hartman }; 139*8ffdff6aSGreg Kroah-Hartman 140*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_dt3000_ai_pgl = { 141*8ffdff6aSGreg Kroah-Hartman 4, { 142*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 143*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(1), 144*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.1), 145*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.02) 146*8ffdff6aSGreg Kroah-Hartman } 147*8ffdff6aSGreg Kroah-Hartman }; 148*8ffdff6aSGreg Kroah-Hartman 149*8ffdff6aSGreg Kroah-Hartman enum dt3k_boardid { 150*8ffdff6aSGreg Kroah-Hartman BOARD_DT3001, 151*8ffdff6aSGreg Kroah-Hartman BOARD_DT3001_PGL, 152*8ffdff6aSGreg Kroah-Hartman BOARD_DT3002, 153*8ffdff6aSGreg Kroah-Hartman BOARD_DT3003, 154*8ffdff6aSGreg Kroah-Hartman BOARD_DT3003_PGL, 155*8ffdff6aSGreg Kroah-Hartman BOARD_DT3004, 156*8ffdff6aSGreg Kroah-Hartman BOARD_DT3005, 157*8ffdff6aSGreg Kroah-Hartman }; 158*8ffdff6aSGreg Kroah-Hartman 159*8ffdff6aSGreg Kroah-Hartman struct dt3k_boardtype { 160*8ffdff6aSGreg Kroah-Hartman const char *name; 161*8ffdff6aSGreg Kroah-Hartman int adchan; 162*8ffdff6aSGreg Kroah-Hartman int ai_speed; 163*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange *adrange; 164*8ffdff6aSGreg Kroah-Hartman unsigned int ai_is_16bit:1; 165*8ffdff6aSGreg Kroah-Hartman unsigned int has_ao:1; 166*8ffdff6aSGreg Kroah-Hartman }; 167*8ffdff6aSGreg Kroah-Hartman 168*8ffdff6aSGreg Kroah-Hartman static const struct dt3k_boardtype dt3k_boardtypes[] = { 169*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3001] = { 170*8ffdff6aSGreg Kroah-Hartman .name = "dt3001", 171*8ffdff6aSGreg Kroah-Hartman .adchan = 16, 172*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai, 173*8ffdff6aSGreg Kroah-Hartman .ai_speed = 3000, 174*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 175*8ffdff6aSGreg Kroah-Hartman }, 176*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3001_PGL] = { 177*8ffdff6aSGreg Kroah-Hartman .name = "dt3001-pgl", 178*8ffdff6aSGreg Kroah-Hartman .adchan = 16, 179*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai_pgl, 180*8ffdff6aSGreg Kroah-Hartman .ai_speed = 3000, 181*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 182*8ffdff6aSGreg Kroah-Hartman }, 183*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3002] = { 184*8ffdff6aSGreg Kroah-Hartman .name = "dt3002", 185*8ffdff6aSGreg Kroah-Hartman .adchan = 32, 186*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai, 187*8ffdff6aSGreg Kroah-Hartman .ai_speed = 3000, 188*8ffdff6aSGreg Kroah-Hartman }, 189*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3003] = { 190*8ffdff6aSGreg Kroah-Hartman .name = "dt3003", 191*8ffdff6aSGreg Kroah-Hartman .adchan = 64, 192*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai, 193*8ffdff6aSGreg Kroah-Hartman .ai_speed = 3000, 194*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 195*8ffdff6aSGreg Kroah-Hartman }, 196*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3003_PGL] = { 197*8ffdff6aSGreg Kroah-Hartman .name = "dt3003-pgl", 198*8ffdff6aSGreg Kroah-Hartman .adchan = 64, 199*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai_pgl, 200*8ffdff6aSGreg Kroah-Hartman .ai_speed = 3000, 201*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 202*8ffdff6aSGreg Kroah-Hartman }, 203*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3004] = { 204*8ffdff6aSGreg Kroah-Hartman .name = "dt3004", 205*8ffdff6aSGreg Kroah-Hartman .adchan = 16, 206*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai, 207*8ffdff6aSGreg Kroah-Hartman .ai_speed = 10000, 208*8ffdff6aSGreg Kroah-Hartman .ai_is_16bit = 1, 209*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 210*8ffdff6aSGreg Kroah-Hartman }, 211*8ffdff6aSGreg Kroah-Hartman [BOARD_DT3005] = { 212*8ffdff6aSGreg Kroah-Hartman .name = "dt3005", /* a.k.a. 3004-200 */ 213*8ffdff6aSGreg Kroah-Hartman .adchan = 16, 214*8ffdff6aSGreg Kroah-Hartman .adrange = &range_dt3000_ai, 215*8ffdff6aSGreg Kroah-Hartman .ai_speed = 5000, 216*8ffdff6aSGreg Kroah-Hartman .ai_is_16bit = 1, 217*8ffdff6aSGreg Kroah-Hartman .has_ao = 1, 218*8ffdff6aSGreg Kroah-Hartman }, 219*8ffdff6aSGreg Kroah-Hartman }; 220*8ffdff6aSGreg Kroah-Hartman 221*8ffdff6aSGreg Kroah-Hartman struct dt3k_private { 222*8ffdff6aSGreg Kroah-Hartman unsigned int lock; 223*8ffdff6aSGreg Kroah-Hartman unsigned int ai_front; 224*8ffdff6aSGreg Kroah-Hartman unsigned int ai_rear; 225*8ffdff6aSGreg Kroah-Hartman }; 226*8ffdff6aSGreg Kroah-Hartman 227*8ffdff6aSGreg Kroah-Hartman static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd) 228*8ffdff6aSGreg Kroah-Hartman { 229*8ffdff6aSGreg Kroah-Hartman int i; 230*8ffdff6aSGreg Kroah-Hartman unsigned int status = 0; 231*8ffdff6aSGreg Kroah-Hartman 232*8ffdff6aSGreg Kroah-Hartman writew(cmd, dev->mmio + DPR_CMD_MBX); 233*8ffdff6aSGreg Kroah-Hartman 234*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < DPR_CMD_TIMEOUT; i++) { 235*8ffdff6aSGreg Kroah-Hartman status = readw(dev->mmio + DPR_CMD_MBX); 236*8ffdff6aSGreg Kroah-Hartman status &= DPR_CMD_COMPLETION_MASK; 237*8ffdff6aSGreg Kroah-Hartman if (status != DPR_CMD_NOTPROCESSED) 238*8ffdff6aSGreg Kroah-Hartman break; 239*8ffdff6aSGreg Kroah-Hartman udelay(1); 240*8ffdff6aSGreg Kroah-Hartman } 241*8ffdff6aSGreg Kroah-Hartman 242*8ffdff6aSGreg Kroah-Hartman if (status != DPR_CMD_NOERROR) 243*8ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n", 244*8ffdff6aSGreg Kroah-Hartman __func__, status); 245*8ffdff6aSGreg Kroah-Hartman } 246*8ffdff6aSGreg Kroah-Hartman 247*8ffdff6aSGreg Kroah-Hartman static unsigned int dt3k_readsingle(struct comedi_device *dev, 248*8ffdff6aSGreg Kroah-Hartman unsigned int subsys, unsigned int chan, 249*8ffdff6aSGreg Kroah-Hartman unsigned int gain) 250*8ffdff6aSGreg Kroah-Hartman { 251*8ffdff6aSGreg Kroah-Hartman writew(subsys, dev->mmio + DPR_SUBSYS); 252*8ffdff6aSGreg Kroah-Hartman 253*8ffdff6aSGreg Kroah-Hartman writew(chan, dev->mmio + DPR_PARAMS(0)); 254*8ffdff6aSGreg Kroah-Hartman writew(gain, dev->mmio + DPR_PARAMS(1)); 255*8ffdff6aSGreg Kroah-Hartman 256*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_READSINGLE); 257*8ffdff6aSGreg Kroah-Hartman 258*8ffdff6aSGreg Kroah-Hartman return readw(dev->mmio + DPR_PARAMS(2)); 259*8ffdff6aSGreg Kroah-Hartman } 260*8ffdff6aSGreg Kroah-Hartman 261*8ffdff6aSGreg Kroah-Hartman static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys, 262*8ffdff6aSGreg Kroah-Hartman unsigned int chan, unsigned int data) 263*8ffdff6aSGreg Kroah-Hartman { 264*8ffdff6aSGreg Kroah-Hartman writew(subsys, dev->mmio + DPR_SUBSYS); 265*8ffdff6aSGreg Kroah-Hartman 266*8ffdff6aSGreg Kroah-Hartman writew(chan, dev->mmio + DPR_PARAMS(0)); 267*8ffdff6aSGreg Kroah-Hartman writew(0, dev->mmio + DPR_PARAMS(1)); 268*8ffdff6aSGreg Kroah-Hartman writew(data, dev->mmio + DPR_PARAMS(2)); 269*8ffdff6aSGreg Kroah-Hartman 270*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_WRITESINGLE); 271*8ffdff6aSGreg Kroah-Hartman } 272*8ffdff6aSGreg Kroah-Hartman 273*8ffdff6aSGreg Kroah-Hartman static void dt3k_ai_empty_fifo(struct comedi_device *dev, 274*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 275*8ffdff6aSGreg Kroah-Hartman { 276*8ffdff6aSGreg Kroah-Hartman struct dt3k_private *devpriv = dev->private; 277*8ffdff6aSGreg Kroah-Hartman int front; 278*8ffdff6aSGreg Kroah-Hartman int rear; 279*8ffdff6aSGreg Kroah-Hartman int count; 280*8ffdff6aSGreg Kroah-Hartman int i; 281*8ffdff6aSGreg Kroah-Hartman unsigned short data; 282*8ffdff6aSGreg Kroah-Hartman 283*8ffdff6aSGreg Kroah-Hartman front = readw(dev->mmio + DPR_AD_BUF_FRONT); 284*8ffdff6aSGreg Kroah-Hartman count = front - devpriv->ai_front; 285*8ffdff6aSGreg Kroah-Hartman if (count < 0) 286*8ffdff6aSGreg Kroah-Hartman count += DPR_AI_FIFO_DEPTH; 287*8ffdff6aSGreg Kroah-Hartman 288*8ffdff6aSGreg Kroah-Hartman rear = devpriv->ai_rear; 289*8ffdff6aSGreg Kroah-Hartman 290*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < count; i++) { 291*8ffdff6aSGreg Kroah-Hartman data = readw(dev->mmio + DPR_ADC_BUFFER + rear); 292*8ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, &data, 1); 293*8ffdff6aSGreg Kroah-Hartman rear++; 294*8ffdff6aSGreg Kroah-Hartman if (rear >= DPR_AI_FIFO_DEPTH) 295*8ffdff6aSGreg Kroah-Hartman rear = 0; 296*8ffdff6aSGreg Kroah-Hartman } 297*8ffdff6aSGreg Kroah-Hartman 298*8ffdff6aSGreg Kroah-Hartman devpriv->ai_rear = rear; 299*8ffdff6aSGreg Kroah-Hartman writew(rear, dev->mmio + DPR_AD_BUF_REAR); 300*8ffdff6aSGreg Kroah-Hartman } 301*8ffdff6aSGreg Kroah-Hartman 302*8ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cancel(struct comedi_device *dev, 303*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 304*8ffdff6aSGreg Kroah-Hartman { 305*8ffdff6aSGreg Kroah-Hartman writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS); 306*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_STOP); 307*8ffdff6aSGreg Kroah-Hartman 308*8ffdff6aSGreg Kroah-Hartman writew(0, dev->mmio + DPR_INT_MASK); 309*8ffdff6aSGreg Kroah-Hartman 310*8ffdff6aSGreg Kroah-Hartman return 0; 311*8ffdff6aSGreg Kroah-Hartman } 312*8ffdff6aSGreg Kroah-Hartman 313*8ffdff6aSGreg Kroah-Hartman static int debug_n_ints; 314*8ffdff6aSGreg Kroah-Hartman 315*8ffdff6aSGreg Kroah-Hartman /* FIXME! Assumes shared interrupt is for this card. */ 316*8ffdff6aSGreg Kroah-Hartman /* What's this debug_n_ints stuff? Obviously needs some work... */ 317*8ffdff6aSGreg Kroah-Hartman static irqreturn_t dt3k_interrupt(int irq, void *d) 318*8ffdff6aSGreg Kroah-Hartman { 319*8ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = d; 320*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s = dev->read_subdev; 321*8ffdff6aSGreg Kroah-Hartman unsigned int status; 322*8ffdff6aSGreg Kroah-Hartman 323*8ffdff6aSGreg Kroah-Hartman if (!dev->attached) 324*8ffdff6aSGreg Kroah-Hartman return IRQ_NONE; 325*8ffdff6aSGreg Kroah-Hartman 326*8ffdff6aSGreg Kroah-Hartman status = readw(dev->mmio + DPR_INTR_FLAG); 327*8ffdff6aSGreg Kroah-Hartman 328*8ffdff6aSGreg Kroah-Hartman if (status & DPR_INTR_ADFULL) 329*8ffdff6aSGreg Kroah-Hartman dt3k_ai_empty_fifo(dev, s); 330*8ffdff6aSGreg Kroah-Hartman 331*8ffdff6aSGreg Kroah-Hartman if (status & (DPR_INTR_ADSWERR | DPR_INTR_ADHWERR)) 332*8ffdff6aSGreg Kroah-Hartman s->async->events |= COMEDI_CB_ERROR; 333*8ffdff6aSGreg Kroah-Hartman 334*8ffdff6aSGreg Kroah-Hartman debug_n_ints++; 335*8ffdff6aSGreg Kroah-Hartman if (debug_n_ints >= 10) 336*8ffdff6aSGreg Kroah-Hartman s->async->events |= COMEDI_CB_EOA; 337*8ffdff6aSGreg Kroah-Hartman 338*8ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s); 339*8ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED; 340*8ffdff6aSGreg Kroah-Hartman } 341*8ffdff6aSGreg Kroah-Hartman 342*8ffdff6aSGreg Kroah-Hartman static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, 343*8ffdff6aSGreg Kroah-Hartman unsigned int flags) 344*8ffdff6aSGreg Kroah-Hartman { 345*8ffdff6aSGreg Kroah-Hartman unsigned int divider, base, prescale; 346*8ffdff6aSGreg Kroah-Hartman 347*8ffdff6aSGreg Kroah-Hartman /* This function needs improvement */ 348*8ffdff6aSGreg Kroah-Hartman /* Don't know if divider==0 works. */ 349*8ffdff6aSGreg Kroah-Hartman 350*8ffdff6aSGreg Kroah-Hartman for (prescale = 0; prescale < 16; prescale++) { 351*8ffdff6aSGreg Kroah-Hartman base = timer_base * (prescale + 1); 352*8ffdff6aSGreg Kroah-Hartman switch (flags & CMDF_ROUND_MASK) { 353*8ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_NEAREST: 354*8ffdff6aSGreg Kroah-Hartman default: 355*8ffdff6aSGreg Kroah-Hartman divider = DIV_ROUND_CLOSEST(*nanosec, base); 356*8ffdff6aSGreg Kroah-Hartman break; 357*8ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_DOWN: 358*8ffdff6aSGreg Kroah-Hartman divider = (*nanosec) / base; 359*8ffdff6aSGreg Kroah-Hartman break; 360*8ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_UP: 361*8ffdff6aSGreg Kroah-Hartman divider = DIV_ROUND_UP(*nanosec, base); 362*8ffdff6aSGreg Kroah-Hartman break; 363*8ffdff6aSGreg Kroah-Hartman } 364*8ffdff6aSGreg Kroah-Hartman if (divider < 65536) { 365*8ffdff6aSGreg Kroah-Hartman *nanosec = divider * base; 366*8ffdff6aSGreg Kroah-Hartman return (prescale << 16) | (divider); 367*8ffdff6aSGreg Kroah-Hartman } 368*8ffdff6aSGreg Kroah-Hartman } 369*8ffdff6aSGreg Kroah-Hartman 370*8ffdff6aSGreg Kroah-Hartman prescale = 15; 371*8ffdff6aSGreg Kroah-Hartman base = timer_base * (prescale + 1); 372*8ffdff6aSGreg Kroah-Hartman divider = 65535; 373*8ffdff6aSGreg Kroah-Hartman *nanosec = divider * base; 374*8ffdff6aSGreg Kroah-Hartman return (prescale << 16) | (divider); 375*8ffdff6aSGreg Kroah-Hartman } 376*8ffdff6aSGreg Kroah-Hartman 377*8ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cmdtest(struct comedi_device *dev, 378*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, struct comedi_cmd *cmd) 379*8ffdff6aSGreg Kroah-Hartman { 380*8ffdff6aSGreg Kroah-Hartman const struct dt3k_boardtype *board = dev->board_ptr; 381*8ffdff6aSGreg Kroah-Hartman int err = 0; 382*8ffdff6aSGreg Kroah-Hartman unsigned int arg; 383*8ffdff6aSGreg Kroah-Hartman 384*8ffdff6aSGreg Kroah-Hartman /* Step 1 : check if triggers are trivially valid */ 385*8ffdff6aSGreg Kroah-Hartman 386*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 387*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER); 388*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); 389*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 390*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT); 391*8ffdff6aSGreg Kroah-Hartman 392*8ffdff6aSGreg Kroah-Hartman if (err) 393*8ffdff6aSGreg Kroah-Hartman return 1; 394*8ffdff6aSGreg Kroah-Hartman 395*8ffdff6aSGreg Kroah-Hartman /* Step 2a : make sure trigger sources are unique */ 396*8ffdff6aSGreg Kroah-Hartman /* Step 2b : and mutually compatible */ 397*8ffdff6aSGreg Kroah-Hartman 398*8ffdff6aSGreg Kroah-Hartman /* Step 3: check if arguments are trivially valid */ 399*8ffdff6aSGreg Kroah-Hartman 400*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 401*8ffdff6aSGreg Kroah-Hartman 402*8ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) { 403*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 404*8ffdff6aSGreg Kroah-Hartman board->ai_speed); 405*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 406*8ffdff6aSGreg Kroah-Hartman 100 * 16 * 65535); 407*8ffdff6aSGreg Kroah-Hartman } 408*8ffdff6aSGreg Kroah-Hartman 409*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) { 410*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 411*8ffdff6aSGreg Kroah-Hartman board->ai_speed); 412*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_max(&cmd->convert_arg, 413*8ffdff6aSGreg Kroah-Hartman 50 * 16 * 65535); 414*8ffdff6aSGreg Kroah-Hartman } 415*8ffdff6aSGreg Kroah-Hartman 416*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 417*8ffdff6aSGreg Kroah-Hartman cmd->chanlist_len); 418*8ffdff6aSGreg Kroah-Hartman 419*8ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) 420*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); 421*8ffdff6aSGreg Kroah-Hartman else /* TRIG_NONE */ 422*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 423*8ffdff6aSGreg Kroah-Hartman 424*8ffdff6aSGreg Kroah-Hartman if (err) 425*8ffdff6aSGreg Kroah-Hartman return 3; 426*8ffdff6aSGreg Kroah-Hartman 427*8ffdff6aSGreg Kroah-Hartman /* step 4: fix up any arguments */ 428*8ffdff6aSGreg Kroah-Hartman 429*8ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) { 430*8ffdff6aSGreg Kroah-Hartman arg = cmd->scan_begin_arg; 431*8ffdff6aSGreg Kroah-Hartman dt3k_ns_to_timer(100, &arg, cmd->flags); 432*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); 433*8ffdff6aSGreg Kroah-Hartman } 434*8ffdff6aSGreg Kroah-Hartman 435*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) { 436*8ffdff6aSGreg Kroah-Hartman arg = cmd->convert_arg; 437*8ffdff6aSGreg Kroah-Hartman dt3k_ns_to_timer(50, &arg, cmd->flags); 438*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 439*8ffdff6aSGreg Kroah-Hartman 440*8ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) { 441*8ffdff6aSGreg Kroah-Hartman arg = cmd->convert_arg * cmd->scan_end_arg; 442*8ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min( 443*8ffdff6aSGreg Kroah-Hartman &cmd->scan_begin_arg, arg); 444*8ffdff6aSGreg Kroah-Hartman } 445*8ffdff6aSGreg Kroah-Hartman } 446*8ffdff6aSGreg Kroah-Hartman 447*8ffdff6aSGreg Kroah-Hartman if (err) 448*8ffdff6aSGreg Kroah-Hartman return 4; 449*8ffdff6aSGreg Kroah-Hartman 450*8ffdff6aSGreg Kroah-Hartman return 0; 451*8ffdff6aSGreg Kroah-Hartman } 452*8ffdff6aSGreg Kroah-Hartman 453*8ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 454*8ffdff6aSGreg Kroah-Hartman { 455*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 456*8ffdff6aSGreg Kroah-Hartman int i; 457*8ffdff6aSGreg Kroah-Hartman unsigned int chan, range, aref; 458*8ffdff6aSGreg Kroah-Hartman unsigned int divider; 459*8ffdff6aSGreg Kroah-Hartman unsigned int tscandiv; 460*8ffdff6aSGreg Kroah-Hartman 461*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < cmd->chanlist_len; i++) { 462*8ffdff6aSGreg Kroah-Hartman chan = CR_CHAN(cmd->chanlist[i]); 463*8ffdff6aSGreg Kroah-Hartman range = CR_RANGE(cmd->chanlist[i]); 464*8ffdff6aSGreg Kroah-Hartman 465*8ffdff6aSGreg Kroah-Hartman writew((range << 6) | chan, dev->mmio + DPR_ADC_BUFFER + i); 466*8ffdff6aSGreg Kroah-Hartman } 467*8ffdff6aSGreg Kroah-Hartman aref = CR_AREF(cmd->chanlist[0]); 468*8ffdff6aSGreg Kroah-Hartman 469*8ffdff6aSGreg Kroah-Hartman writew(cmd->scan_end_arg, dev->mmio + DPR_PARAMS(0)); 470*8ffdff6aSGreg Kroah-Hartman 471*8ffdff6aSGreg Kroah-Hartman if (cmd->convert_src == TRIG_TIMER) { 472*8ffdff6aSGreg Kroah-Hartman divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags); 473*8ffdff6aSGreg Kroah-Hartman writew((divider >> 16), dev->mmio + DPR_PARAMS(1)); 474*8ffdff6aSGreg Kroah-Hartman writew((divider & 0xffff), dev->mmio + DPR_PARAMS(2)); 475*8ffdff6aSGreg Kroah-Hartman } 476*8ffdff6aSGreg Kroah-Hartman 477*8ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) { 478*8ffdff6aSGreg Kroah-Hartman tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg, 479*8ffdff6aSGreg Kroah-Hartman cmd->flags); 480*8ffdff6aSGreg Kroah-Hartman writew((tscandiv >> 16), dev->mmio + DPR_PARAMS(3)); 481*8ffdff6aSGreg Kroah-Hartman writew((tscandiv & 0xffff), dev->mmio + DPR_PARAMS(4)); 482*8ffdff6aSGreg Kroah-Hartman } 483*8ffdff6aSGreg Kroah-Hartman 484*8ffdff6aSGreg Kroah-Hartman writew(DPR_PARAM5_AD_TRIG_INT_RETRIG, dev->mmio + DPR_PARAMS(5)); 485*8ffdff6aSGreg Kroah-Hartman writew((aref == AREF_DIFF) ? DPR_PARAM6_AD_DIFF : 0, 486*8ffdff6aSGreg Kroah-Hartman dev->mmio + DPR_PARAMS(6)); 487*8ffdff6aSGreg Kroah-Hartman 488*8ffdff6aSGreg Kroah-Hartman writew(DPR_AI_FIFO_DEPTH / 2, dev->mmio + DPR_PARAMS(7)); 489*8ffdff6aSGreg Kroah-Hartman 490*8ffdff6aSGreg Kroah-Hartman writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS); 491*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_CONFIG); 492*8ffdff6aSGreg Kroah-Hartman 493*8ffdff6aSGreg Kroah-Hartman writew(DPR_INTR_ADFULL | DPR_INTR_ADSWERR | DPR_INTR_ADHWERR, 494*8ffdff6aSGreg Kroah-Hartman dev->mmio + DPR_INT_MASK); 495*8ffdff6aSGreg Kroah-Hartman 496*8ffdff6aSGreg Kroah-Hartman debug_n_ints = 0; 497*8ffdff6aSGreg Kroah-Hartman 498*8ffdff6aSGreg Kroah-Hartman writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS); 499*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_START); 500*8ffdff6aSGreg Kroah-Hartman 501*8ffdff6aSGreg Kroah-Hartman return 0; 502*8ffdff6aSGreg Kroah-Hartman } 503*8ffdff6aSGreg Kroah-Hartman 504*8ffdff6aSGreg Kroah-Hartman static int dt3k_ai_insn_read(struct comedi_device *dev, 505*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 506*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 507*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 508*8ffdff6aSGreg Kroah-Hartman { 509*8ffdff6aSGreg Kroah-Hartman int i; 510*8ffdff6aSGreg Kroah-Hartman unsigned int chan, gain; 511*8ffdff6aSGreg Kroah-Hartman 512*8ffdff6aSGreg Kroah-Hartman chan = CR_CHAN(insn->chanspec); 513*8ffdff6aSGreg Kroah-Hartman gain = CR_RANGE(insn->chanspec); 514*8ffdff6aSGreg Kroah-Hartman /* XXX docs don't explain how to select aref */ 515*8ffdff6aSGreg Kroah-Hartman 516*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) 517*8ffdff6aSGreg Kroah-Hartman data[i] = dt3k_readsingle(dev, DPR_SUBSYS_AI, chan, gain); 518*8ffdff6aSGreg Kroah-Hartman 519*8ffdff6aSGreg Kroah-Hartman return i; 520*8ffdff6aSGreg Kroah-Hartman } 521*8ffdff6aSGreg Kroah-Hartman 522*8ffdff6aSGreg Kroah-Hartman static int dt3k_ao_insn_write(struct comedi_device *dev, 523*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 524*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 525*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 526*8ffdff6aSGreg Kroah-Hartman { 527*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 528*8ffdff6aSGreg Kroah-Hartman unsigned int val = s->readback[chan]; 529*8ffdff6aSGreg Kroah-Hartman int i; 530*8ffdff6aSGreg Kroah-Hartman 531*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 532*8ffdff6aSGreg Kroah-Hartman val = data[i]; 533*8ffdff6aSGreg Kroah-Hartman dt3k_writesingle(dev, DPR_SUBSYS_AO, chan, val); 534*8ffdff6aSGreg Kroah-Hartman } 535*8ffdff6aSGreg Kroah-Hartman s->readback[chan] = val; 536*8ffdff6aSGreg Kroah-Hartman 537*8ffdff6aSGreg Kroah-Hartman return insn->n; 538*8ffdff6aSGreg Kroah-Hartman } 539*8ffdff6aSGreg Kroah-Hartman 540*8ffdff6aSGreg Kroah-Hartman static void dt3k_dio_config(struct comedi_device *dev, int bits) 541*8ffdff6aSGreg Kroah-Hartman { 542*8ffdff6aSGreg Kroah-Hartman /* XXX */ 543*8ffdff6aSGreg Kroah-Hartman writew(DPR_SUBSYS_DOUT, dev->mmio + DPR_SUBSYS); 544*8ffdff6aSGreg Kroah-Hartman 545*8ffdff6aSGreg Kroah-Hartman writew(bits, dev->mmio + DPR_PARAMS(0)); 546*8ffdff6aSGreg Kroah-Hartman 547*8ffdff6aSGreg Kroah-Hartman /* XXX write 0 to DPR_PARAMS(1) and DPR_PARAMS(2) ? */ 548*8ffdff6aSGreg Kroah-Hartman 549*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_CONFIG); 550*8ffdff6aSGreg Kroah-Hartman } 551*8ffdff6aSGreg Kroah-Hartman 552*8ffdff6aSGreg Kroah-Hartman static int dt3k_dio_insn_config(struct comedi_device *dev, 553*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 554*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 555*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 556*8ffdff6aSGreg Kroah-Hartman { 557*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 558*8ffdff6aSGreg Kroah-Hartman unsigned int mask; 559*8ffdff6aSGreg Kroah-Hartman int ret; 560*8ffdff6aSGreg Kroah-Hartman 561*8ffdff6aSGreg Kroah-Hartman if (chan < 4) 562*8ffdff6aSGreg Kroah-Hartman mask = 0x0f; 563*8ffdff6aSGreg Kroah-Hartman else 564*8ffdff6aSGreg Kroah-Hartman mask = 0xf0; 565*8ffdff6aSGreg Kroah-Hartman 566*8ffdff6aSGreg Kroah-Hartman ret = comedi_dio_insn_config(dev, s, insn, data, mask); 567*8ffdff6aSGreg Kroah-Hartman if (ret) 568*8ffdff6aSGreg Kroah-Hartman return ret; 569*8ffdff6aSGreg Kroah-Hartman 570*8ffdff6aSGreg Kroah-Hartman dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3)); 571*8ffdff6aSGreg Kroah-Hartman 572*8ffdff6aSGreg Kroah-Hartman return insn->n; 573*8ffdff6aSGreg Kroah-Hartman } 574*8ffdff6aSGreg Kroah-Hartman 575*8ffdff6aSGreg Kroah-Hartman static int dt3k_dio_insn_bits(struct comedi_device *dev, 576*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 577*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 578*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 579*8ffdff6aSGreg Kroah-Hartman { 580*8ffdff6aSGreg Kroah-Hartman if (comedi_dio_update_state(s, data)) 581*8ffdff6aSGreg Kroah-Hartman dt3k_writesingle(dev, DPR_SUBSYS_DOUT, 0, s->state); 582*8ffdff6aSGreg Kroah-Hartman 583*8ffdff6aSGreg Kroah-Hartman data[1] = dt3k_readsingle(dev, DPR_SUBSYS_DIN, 0, 0); 584*8ffdff6aSGreg Kroah-Hartman 585*8ffdff6aSGreg Kroah-Hartman return insn->n; 586*8ffdff6aSGreg Kroah-Hartman } 587*8ffdff6aSGreg Kroah-Hartman 588*8ffdff6aSGreg Kroah-Hartman static int dt3k_mem_insn_read(struct comedi_device *dev, 589*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 590*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 591*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 592*8ffdff6aSGreg Kroah-Hartman { 593*8ffdff6aSGreg Kroah-Hartman unsigned int addr = CR_CHAN(insn->chanspec); 594*8ffdff6aSGreg Kroah-Hartman int i; 595*8ffdff6aSGreg Kroah-Hartman 596*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 597*8ffdff6aSGreg Kroah-Hartman writew(DPR_SUBSYS_MEM, dev->mmio + DPR_SUBSYS); 598*8ffdff6aSGreg Kroah-Hartman writew(addr, dev->mmio + DPR_PARAMS(0)); 599*8ffdff6aSGreg Kroah-Hartman writew(1, dev->mmio + DPR_PARAMS(1)); 600*8ffdff6aSGreg Kroah-Hartman 601*8ffdff6aSGreg Kroah-Hartman dt3k_send_cmd(dev, DPR_CMD_READCODE); 602*8ffdff6aSGreg Kroah-Hartman 603*8ffdff6aSGreg Kroah-Hartman data[i] = readw(dev->mmio + DPR_PARAMS(2)); 604*8ffdff6aSGreg Kroah-Hartman } 605*8ffdff6aSGreg Kroah-Hartman 606*8ffdff6aSGreg Kroah-Hartman return i; 607*8ffdff6aSGreg Kroah-Hartman } 608*8ffdff6aSGreg Kroah-Hartman 609*8ffdff6aSGreg Kroah-Hartman static int dt3000_auto_attach(struct comedi_device *dev, 610*8ffdff6aSGreg Kroah-Hartman unsigned long context) 611*8ffdff6aSGreg Kroah-Hartman { 612*8ffdff6aSGreg Kroah-Hartman struct pci_dev *pcidev = comedi_to_pci_dev(dev); 613*8ffdff6aSGreg Kroah-Hartman const struct dt3k_boardtype *board = NULL; 614*8ffdff6aSGreg Kroah-Hartman struct dt3k_private *devpriv; 615*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 616*8ffdff6aSGreg Kroah-Hartman int ret = 0; 617*8ffdff6aSGreg Kroah-Hartman 618*8ffdff6aSGreg Kroah-Hartman if (context < ARRAY_SIZE(dt3k_boardtypes)) 619*8ffdff6aSGreg Kroah-Hartman board = &dt3k_boardtypes[context]; 620*8ffdff6aSGreg Kroah-Hartman if (!board) 621*8ffdff6aSGreg Kroah-Hartman return -ENODEV; 622*8ffdff6aSGreg Kroah-Hartman dev->board_ptr = board; 623*8ffdff6aSGreg Kroah-Hartman dev->board_name = board->name; 624*8ffdff6aSGreg Kroah-Hartman 625*8ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 626*8ffdff6aSGreg Kroah-Hartman if (!devpriv) 627*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 628*8ffdff6aSGreg Kroah-Hartman 629*8ffdff6aSGreg Kroah-Hartman ret = comedi_pci_enable(dev); 630*8ffdff6aSGreg Kroah-Hartman if (ret < 0) 631*8ffdff6aSGreg Kroah-Hartman return ret; 632*8ffdff6aSGreg Kroah-Hartman 633*8ffdff6aSGreg Kroah-Hartman dev->mmio = pci_ioremap_bar(pcidev, 0); 634*8ffdff6aSGreg Kroah-Hartman if (!dev->mmio) 635*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 636*8ffdff6aSGreg Kroah-Hartman 637*8ffdff6aSGreg Kroah-Hartman if (pcidev->irq) { 638*8ffdff6aSGreg Kroah-Hartman ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED, 639*8ffdff6aSGreg Kroah-Hartman dev->board_name, dev); 640*8ffdff6aSGreg Kroah-Hartman if (ret == 0) 641*8ffdff6aSGreg Kroah-Hartman dev->irq = pcidev->irq; 642*8ffdff6aSGreg Kroah-Hartman } 643*8ffdff6aSGreg Kroah-Hartman 644*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 4); 645*8ffdff6aSGreg Kroah-Hartman if (ret) 646*8ffdff6aSGreg Kroah-Hartman return ret; 647*8ffdff6aSGreg Kroah-Hartman 648*8ffdff6aSGreg Kroah-Hartman /* Analog Input subdevice */ 649*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 650*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 651*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 652*8ffdff6aSGreg Kroah-Hartman s->n_chan = board->adchan; 653*8ffdff6aSGreg Kroah-Hartman s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; 654*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_dt3000_ai; /* XXX */ 655*8ffdff6aSGreg Kroah-Hartman s->insn_read = dt3k_ai_insn_read; 656*8ffdff6aSGreg Kroah-Hartman if (dev->irq) { 657*8ffdff6aSGreg Kroah-Hartman dev->read_subdev = s; 658*8ffdff6aSGreg Kroah-Hartman s->subdev_flags |= SDF_CMD_READ; 659*8ffdff6aSGreg Kroah-Hartman s->len_chanlist = 512; 660*8ffdff6aSGreg Kroah-Hartman s->do_cmd = dt3k_ai_cmd; 661*8ffdff6aSGreg Kroah-Hartman s->do_cmdtest = dt3k_ai_cmdtest; 662*8ffdff6aSGreg Kroah-Hartman s->cancel = dt3k_ai_cancel; 663*8ffdff6aSGreg Kroah-Hartman } 664*8ffdff6aSGreg Kroah-Hartman 665*8ffdff6aSGreg Kroah-Hartman /* Analog Output subdevice */ 666*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 667*8ffdff6aSGreg Kroah-Hartman if (board->has_ao) { 668*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AO; 669*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 670*8ffdff6aSGreg Kroah-Hartman s->n_chan = 2; 671*8ffdff6aSGreg Kroah-Hartman s->maxdata = 0x0fff; 672*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_bipolar10; 673*8ffdff6aSGreg Kroah-Hartman s->insn_write = dt3k_ao_insn_write; 674*8ffdff6aSGreg Kroah-Hartman 675*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdev_readback(s); 676*8ffdff6aSGreg Kroah-Hartman if (ret) 677*8ffdff6aSGreg Kroah-Hartman return ret; 678*8ffdff6aSGreg Kroah-Hartman 679*8ffdff6aSGreg Kroah-Hartman } else { 680*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED; 681*8ffdff6aSGreg Kroah-Hartman } 682*8ffdff6aSGreg Kroah-Hartman 683*8ffdff6aSGreg Kroah-Hartman /* Digital I/O subdevice */ 684*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2]; 685*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DIO; 686*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 687*8ffdff6aSGreg Kroah-Hartman s->n_chan = 8; 688*8ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 689*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 690*8ffdff6aSGreg Kroah-Hartman s->insn_config = dt3k_dio_insn_config; 691*8ffdff6aSGreg Kroah-Hartman s->insn_bits = dt3k_dio_insn_bits; 692*8ffdff6aSGreg Kroah-Hartman 693*8ffdff6aSGreg Kroah-Hartman /* Memory subdevice */ 694*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3]; 695*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_MEMORY; 696*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE; 697*8ffdff6aSGreg Kroah-Hartman s->n_chan = 0x1000; 698*8ffdff6aSGreg Kroah-Hartman s->maxdata = 0xff; 699*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_unknown; 700*8ffdff6aSGreg Kroah-Hartman s->insn_read = dt3k_mem_insn_read; 701*8ffdff6aSGreg Kroah-Hartman 702*8ffdff6aSGreg Kroah-Hartman return 0; 703*8ffdff6aSGreg Kroah-Hartman } 704*8ffdff6aSGreg Kroah-Hartman 705*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver dt3000_driver = { 706*8ffdff6aSGreg Kroah-Hartman .driver_name = "dt3000", 707*8ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 708*8ffdff6aSGreg Kroah-Hartman .auto_attach = dt3000_auto_attach, 709*8ffdff6aSGreg Kroah-Hartman .detach = comedi_pci_detach, 710*8ffdff6aSGreg Kroah-Hartman }; 711*8ffdff6aSGreg Kroah-Hartman 712*8ffdff6aSGreg Kroah-Hartman static int dt3000_pci_probe(struct pci_dev *dev, 713*8ffdff6aSGreg Kroah-Hartman const struct pci_device_id *id) 714*8ffdff6aSGreg Kroah-Hartman { 715*8ffdff6aSGreg Kroah-Hartman return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data); 716*8ffdff6aSGreg Kroah-Hartman } 717*8ffdff6aSGreg Kroah-Hartman 718*8ffdff6aSGreg Kroah-Hartman static const struct pci_device_id dt3000_pci_table[] = { 719*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 }, 720*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 }, 721*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 }, 722*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 }, 723*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 }, 724*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL }, 725*8ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL }, 726*8ffdff6aSGreg Kroah-Hartman { 0 } 727*8ffdff6aSGreg Kroah-Hartman }; 728*8ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, dt3000_pci_table); 729*8ffdff6aSGreg Kroah-Hartman 730*8ffdff6aSGreg Kroah-Hartman static struct pci_driver dt3000_pci_driver = { 731*8ffdff6aSGreg Kroah-Hartman .name = "dt3000", 732*8ffdff6aSGreg Kroah-Hartman .id_table = dt3000_pci_table, 733*8ffdff6aSGreg Kroah-Hartman .probe = dt3000_pci_probe, 734*8ffdff6aSGreg Kroah-Hartman .remove = comedi_pci_auto_unconfig, 735*8ffdff6aSGreg Kroah-Hartman }; 736*8ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver); 737*8ffdff6aSGreg Kroah-Hartman 738*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org"); 739*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for Data Translation DT3000 series boards"); 740*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 741