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