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