s526.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
1/* 2 comedi/drivers/s526.c 3 Sensoray s526 Comedi driver 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23/* 24Driver: s526 25Description: Sensoray 526 driver 26Devices: [Sensoray] 526 (s526) 27Author: Richie 28 Everett Wang <everett.wang@everteq.com> 29Updated: Thu, 14 Sep. 2006 30Status: experimental 31 32Encoder works 33Analog input works 34Analog output works 35PWM output works 36Commands are not supported yet. 37 38Configuration Options: 39 40comedi_config /dev/comedi0 s526 0x2C0,0x3 41 42*/ 43 44#include "../comedidev.h" 45#include <linux/ioport.h> 46 47#define S526_SIZE 64 48 49#define S526_START_AI_CONV 0 50#define S526_AI_READ 0 51 52/* Ports */ 53#define S526_IOSIZE 0x40 54#define S526_NUM_PORTS 27 55 56/* registers */ 57#define REG_TCR 0x00 58#define REG_WDC 0x02 59#define REG_DAC 0x04 60#define REG_ADC 0x06 61#define REG_ADD 0x08 62#define REG_DIO 0x0A 63#define REG_IER 0x0C 64#define REG_ISR 0x0E 65#define REG_MSC 0x10 66#define REG_C0L 0x12 67#define REG_C0H 0x14 68#define REG_C0M 0x16 69#define REG_C0C 0x18 70#define REG_C1L 0x1A 71#define REG_C1H 0x1C 72#define REG_C1M 0x1E 73#define REG_C1C 0x20 74#define REG_C2L 0x22 75#define REG_C2H 0x24 76#define REG_C2M 0x26 77#define REG_C2C 0x28 78#define REG_C3L 0x2A 79#define REG_C3H 0x2C 80#define REG_C3M 0x2E 81#define REG_C3C 0x30 82#define REG_EED 0x32 83#define REG_EEC 0x34 84 85static const int s526_ports[] = { 86 REG_TCR, 87 REG_WDC, 88 REG_DAC, 89 REG_ADC, 90 REG_ADD, 91 REG_DIO, 92 REG_IER, 93 REG_ISR, 94 REG_MSC, 95 REG_C0L, 96 REG_C0H, 97 REG_C0M, 98 REG_C0C, 99 REG_C1L, 100 REG_C1H, 101 REG_C1M, 102 REG_C1C, 103 REG_C2L, 104 REG_C2H, 105 REG_C2M, 106 REG_C2C, 107 REG_C3L, 108 REG_C3H, 109 REG_C3M, 110 REG_C3C, 111 REG_EED, 112 REG_EEC 113}; 114 115struct counter_mode_register_t { 116 unsigned short coutSource:1; 117 unsigned short coutPolarity:1; 118 unsigned short autoLoadResetRcap:3; 119 unsigned short hwCtEnableSource:2; 120 unsigned short ctEnableCtrl:2; 121 unsigned short clockSource:2; 122 unsigned short countDir:1; 123 unsigned short countDirCtrl:1; 124 unsigned short outputRegLatchCtrl:1; 125 unsigned short preloadRegSel:1; 126 unsigned short reserved:1; 127}; 128 129union { 130 struct counter_mode_register_t reg; 131 unsigned short value; 132} cmReg; 133 134#define MAX_GPCT_CONFIG_DATA 6 135 136/* Different Application Classes for GPCT Subdevices */ 137/* The list is not exhaustive and needs discussion! */ 138enum S526_GPCT_APP_CLASS { 139 CountingAndTimeMeasurement, 140 SinglePulseGeneration, 141 PulseTrainGeneration, 142 PositionMeasurement, 143 Miscellaneous 144}; 145 146/* Config struct for different GPCT subdevice Application Classes and 147 their options 148*/ 149struct s526GPCTConfig { 150 enum S526_GPCT_APP_CLASS app; 151 int data[MAX_GPCT_CONFIG_DATA]; 152}; 153 154/* 155 * Board descriptions for two imaginary boards. Describing the 156 * boards in this way is optional, and completely driver-dependent. 157 * Some drivers use arrays such as this, other do not. 158 */ 159struct s526_board { 160 const char *name; 161 int gpct_chans; 162 int gpct_bits; 163 int ad_chans; 164 int ad_bits; 165 int da_chans; 166 int da_bits; 167 int have_dio; 168}; 169 170static const struct s526_board s526_boards[] = { 171 { 172 .name = "s526", 173 .gpct_chans = 4, 174 .gpct_bits = 24, 175 .ad_chans = 8, 176 .ad_bits = 16, 177 .da_chans = 4, 178 .da_bits = 16, 179 .have_dio = 1, 180 } 181}; 182 183#define ADDR_REG(reg) (dev->iobase + (reg)) 184#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8) 185 186/* 187 * Useful for shorthand access to the particular board structure 188 */ 189#define thisboard ((const struct s526_board *)dev->board_ptr) 190 191/* this structure is for data unique to this hardware driver. If 192 several hardware drivers keep similar information in this structure, 193 feel free to suggest moving the variable to the struct comedi_device struct. */ 194struct s526_private { 195 196 int data; 197 198 /* would be useful for a PCI device */ 199 struct pci_dev *pci_dev; 200 201 /* Used for AO readback */ 202 unsigned int ao_readback[2]; 203 204 struct s526GPCTConfig s526_gpct_config[4]; 205 unsigned short s526_ai_config; 206}; 207 208/* 209 * most drivers define the following macro to make it easy to 210 * access the private structure. 211 */ 212#define devpriv ((struct s526_private *)dev->private) 213 214/* 215 * The struct comedi_driver structure tells the Comedi core module 216 * which functions to call to configure/deconfigure (attach/detach) 217 * the board, and also about the kernel module that contains 218 * the device code. 219 */ 220static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it); 221static int s526_detach(struct comedi_device *dev); 222static struct comedi_driver driver_s526 = { 223 .driver_name = "s526", 224 .module = THIS_MODULE, 225 .attach = s526_attach, 226 .detach = s526_detach, 227/* It is not necessary to implement the following members if you are 228 * writing a driver for a ISA PnP or PCI card */ 229 /* Most drivers will support multiple types of boards by 230 * having an array of board structures. These were defined 231 * in s526_boards[] above. Note that the element 'name' 232 * was first in the structure -- Comedi uses this fact to 233 * extract the name of the board without knowing any details 234 * about the structure except for its length. 235 * When a device is attached (by comedi_config), the name 236 * of the device is given to Comedi, and Comedi tries to 237 * match it by going through the list of board names. If 238 * there is a match, the address of the pointer is put 239 * into dev->board_ptr and driver->attach() is called. 240 * 241 * Note that these are not necessary if you can determine 242 * the type of board in software. ISA PnP, PCI, and PCMCIA 243 * devices are such boards. 244 */ 245 .board_name = &s526_boards[0].name, 246 .offset = sizeof(struct s526_board), 247 .num_names = ARRAY_SIZE(s526_boards), 248}; 249 250static int s526_gpct_rinsn(struct comedi_device *dev, 251 struct comedi_subdevice *s, struct comedi_insn *insn, 252 unsigned int *data); 253static int s526_gpct_insn_config(struct comedi_device *dev, 254 struct comedi_subdevice *s, 255 struct comedi_insn *insn, unsigned int *data); 256static int s526_gpct_winsn(struct comedi_device *dev, 257 struct comedi_subdevice *s, struct comedi_insn *insn, 258 unsigned int *data); 259static int s526_ai_insn_config(struct comedi_device *dev, 260 struct comedi_subdevice *s, 261 struct comedi_insn *insn, unsigned int *data); 262static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 263 struct comedi_insn *insn, unsigned int *data); 264static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 265 struct comedi_insn *insn, unsigned int *data); 266static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 267 struct comedi_insn *insn, unsigned int *data); 268static int s526_dio_insn_bits(struct comedi_device *dev, 269 struct comedi_subdevice *s, 270 struct comedi_insn *insn, unsigned int *data); 271static int s526_dio_insn_config(struct comedi_device *dev, 272 struct comedi_subdevice *s, 273 struct comedi_insn *insn, unsigned int *data); 274 275/* 276 * Attach is called by the Comedi core to configure the driver 277 * for a particular board. If you specified a board_name array 278 * in the driver structure, dev->board_ptr contains that 279 * address. 280 */ 281static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) 282{ 283 struct comedi_subdevice *s; 284 int iobase; 285 int i, n; 286/* short value; */ 287/* int subdev_channel = 0; */ 288 289 printk("comedi%d: s526: ", dev->minor); 290 291 iobase = it->options[0]; 292 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) { 293 comedi_error(dev, "I/O port conflict"); 294 return -EIO; 295 } 296 dev->iobase = iobase; 297 298 printk("iobase=0x%lx\n", dev->iobase); 299 300 /*** make it a little quieter, exw, 8/29/06 301 for (i = 0; i < S526_NUM_PORTS; i++) { 302 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i]))); 303 } 304 ***/ 305 306/* 307 * Initialize dev->board_name. Note that we can use the "thisboard" 308 * macro now, since we just initialized it in the last line. 309 */ 310 dev->board_ptr = &s526_boards[0]; 311 312 dev->board_name = thisboard->name; 313 314/* 315 * Allocate the private structure area. alloc_private() is a 316 * convenient macro defined in comedidev.h. 317 */ 318 if (alloc_private(dev, sizeof(struct s526_private)) < 0) 319 return -ENOMEM; 320 321/* 322 * Allocate the subdevice structures. alloc_subdevice() is a 323 * convenient macro defined in comedidev.h. 324 */ 325 dev->n_subdevices = 4; 326 if (alloc_subdevices(dev, dev->n_subdevices) < 0) 327 return -ENOMEM; 328 329 s = dev->subdevices + 0; 330 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */ 331 s->type = COMEDI_SUBD_COUNTER; 332 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL; 333 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */ 334 s->n_chan = thisboard->gpct_chans; 335 s->maxdata = 0x00ffffff; /* 24 bit counter */ 336 s->insn_read = s526_gpct_rinsn; 337 s->insn_config = s526_gpct_insn_config; 338 s->insn_write = s526_gpct_winsn; 339 340 /* Command are not implemented yet, however they are necessary to 341 allocate the necessary memory for the comedi_async struct (used 342 to trigger the GPCT in case of pulsegenerator function */ 343 /* s->do_cmd = s526_gpct_cmd; */ 344 /* s->do_cmdtest = s526_gpct_cmdtest; */ 345 /* s->cancel = s526_gpct_cancel; */ 346 347 s = dev->subdevices + 1; 348 /* dev->read_subdev=s; */ 349 /* analog input subdevice */ 350 s->type = COMEDI_SUBD_AI; 351 /* we support differential */ 352 s->subdev_flags = SDF_READABLE | SDF_DIFF; 353 /* channels 0 to 7 are the regular differential inputs */ 354 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */ 355 s->n_chan = 10; 356 s->maxdata = 0xffff; 357 s->range_table = &range_bipolar10; 358 s->len_chanlist = 16; /* This is the maximum chanlist length that 359 the board can handle */ 360 s->insn_read = s526_ai_rinsn; 361 s->insn_config = s526_ai_insn_config; 362 363 s = dev->subdevices + 2; 364 /* analog output subdevice */ 365 s->type = COMEDI_SUBD_AO; 366 s->subdev_flags = SDF_WRITABLE; 367 s->n_chan = 4; 368 s->maxdata = 0xffff; 369 s->range_table = &range_bipolar10; 370 s->insn_write = s526_ao_winsn; 371 s->insn_read = s526_ao_rinsn; 372 373 s = dev->subdevices + 3; 374 /* digital i/o subdevice */ 375 if (thisboard->have_dio) { 376 s->type = COMEDI_SUBD_DIO; 377 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 378 s->n_chan = 2; 379 s->maxdata = 1; 380 s->range_table = &range_digital; 381 s->insn_bits = s526_dio_insn_bits; 382 s->insn_config = s526_dio_insn_config; 383 } else { 384 s->type = COMEDI_SUBD_UNUSED; 385 } 386 387 printk("attached\n"); 388 389 return 1; 390 391#if 0 392 /* Example of Counter Application */ 393 /* One-shot (software trigger) */ 394 cmReg.reg.coutSource = 0; /* out RCAP */ 395 cmReg.reg.coutPolarity = 1; /* Polarity inverted */ 396 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */ 397 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */ 398 cmReg.reg.ctEnableCtrl = 2; /* Hardware */ 399 cmReg.reg.clockSource = 2; /* Internal */ 400 cmReg.reg.countDir = 1; /* Down */ 401 cmReg.reg.countDirCtrl = 1; /* Software */ 402 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 403 cmReg.reg.preloadRegSel = 0; /* PR0 */ 404 cmReg.reg.reserved = 0; 405 406 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 407 408 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 409 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 410 411 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 412 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 413 414 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */ 415 416#else 417 418 /* Set Counter Mode Register */ 419 cmReg.reg.coutSource = 0; /* out RCAP */ 420 cmReg.reg.coutPolarity = 0; /* Polarity inverted */ 421 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 422 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ 423 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ 424 cmReg.reg.clockSource = 3; /* x4 */ 425 cmReg.reg.countDir = 0; /* up */ 426 cmReg.reg.countDirCtrl = 0; /* quadrature */ 427 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 428 cmReg.reg.preloadRegSel = 0; /* PR0 */ 429 cmReg.reg.reserved = 0; 430 431 n = 0; 432 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M, 433 n)); 434 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n)); 435 udelay(1000); 436 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); 437 438 /* Load the pre-laod register high word */ 439/* value = (short) (0x55); */ 440/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */ 441 442 /* Load the pre-laod register low word */ 443/* value = (short)(0xaa55); */ 444/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */ 445 446 /* Write the Counter Control Register */ 447/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */ 448 449 /* Reset the counter if it is software preload */ 450 if (cmReg.reg.autoLoadResetRcap == 0) { 451 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */ 452 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */ 453 } 454 455 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n)); 456 udelay(1000); 457 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); 458 459#endif 460 printk("Current registres:\n"); 461 462 for (i = 0; i < S526_NUM_PORTS; i++) { 463 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]), 464 inw(ADDR_REG(s526_ports[i]))); 465 } 466 return 1; 467} 468 469/* 470 * _detach is called to deconfigure a device. It should deallocate 471 * resources. 472 * This function is also called when _attach() fails, so it should be 473 * careful not to release resources that were not necessarily 474 * allocated by _attach(). dev->private and dev->subdevices are 475 * deallocated automatically by the core. 476 */ 477static int s526_detach(struct comedi_device *dev) 478{ 479 printk("comedi%d: s526: remove\n", dev->minor); 480 481 if (dev->iobase > 0) 482 release_region(dev->iobase, S526_IOSIZE); 483 484 return 0; 485} 486 487static int s526_gpct_rinsn(struct comedi_device *dev, 488 struct comedi_subdevice *s, struct comedi_insn *insn, 489 unsigned int *data) 490{ 491 int i; /* counts the Data */ 492 int counter_channel = CR_CHAN(insn->chanspec); 493 unsigned short datalow; 494 unsigned short datahigh; 495 496 /* Check if (n > 0) */ 497 if (insn->n <= 0) { 498 printk("s526: INSN_READ: n should be > 0\n"); 499 return -EINVAL; 500 } 501 /* Read the low word first */ 502 for (i = 0; i < insn->n; i++) { 503 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel)); 504 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel)); 505 data[i] = (int)(datahigh & 0x00FF); 506 data[i] = (data[i] << 16) | (datalow & 0xFFFF); 507/* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */ 508 } 509 return i; 510} 511 512static int s526_gpct_insn_config(struct comedi_device *dev, 513 struct comedi_subdevice *s, 514 struct comedi_insn *insn, unsigned int *data) 515{ 516 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ 517 int i; 518 short value; 519 520/* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */ 521 522 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) { 523 devpriv->s526_gpct_config[subdev_channel].data[i] = 524 insn->data[i]; 525/* printk("data[%d]=%x\n", i, insn->data[i]); */ 526 } 527 528 /* Check what type of Counter the user requested, data[0] contains */ 529 /* the Application type */ 530 switch (insn->data[0]) { 531 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 532 /* 533 data[0]: Application Type 534 data[1]: Counter Mode Register Value 535 data[2]: Pre-load Register Value 536 data[3]: Conter Control Register 537 */ 538 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n"); 539 devpriv->s526_gpct_config[subdev_channel].app = 540 PositionMeasurement; 541 542#if 0 543 /* Example of Counter Application */ 544 /* One-shot (software trigger) */ 545 cmReg.reg.coutSource = 0; /* out RCAP */ 546 cmReg.reg.coutPolarity = 1; /* Polarity inverted */ 547 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 548 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */ 549 cmReg.reg.ctEnableCtrl = 2; /* Hardware */ 550 cmReg.reg.clockSource = 2; /* Internal */ 551 cmReg.reg.countDir = 1; /* Down */ 552 cmReg.reg.countDirCtrl = 1; /* Software */ 553 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 554 cmReg.reg.preloadRegSel = 0; /* PR0 */ 555 cmReg.reg.reserved = 0; 556 557 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 558 559 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 560 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 561 562 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 563 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 564 565 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */ 566 567#endif 568 569#if 1 570 /* Set Counter Mode Register */ 571 cmReg.reg.coutSource = 0; /* out RCAP */ 572 cmReg.reg.coutPolarity = 0; /* Polarity inverted */ 573 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 574 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ 575 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ 576 cmReg.reg.clockSource = 3; /* x4 */ 577 cmReg.reg.countDir = 0; /* up */ 578 cmReg.reg.countDirCtrl = 0; /* quadrature */ 579 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 580 cmReg.reg.preloadRegSel = 0; /* PR0 */ 581 cmReg.reg.reserved = 0; 582 583 /* Set Counter Mode Register */ 584/* printk("s526: Counter Mode register=%x\n", cmReg.value); */ 585 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 586 587 /* Reset the counter if it is software preload */ 588 if (cmReg.reg.autoLoadResetRcap == 0) { 589 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 590/* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */ 591 } 592#else 593 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */ 594 595 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */ 596 if (insn->data[1] == GPCT_X2) { 597 cmReg.reg.clockSource = 1; 598 } else if (insn->data[1] == GPCT_X4) { 599 cmReg.reg.clockSource = 2; 600 } else { 601 cmReg.reg.clockSource = 0; 602 } 603 604 /* When to take into account the indexpulse: */ 605 if (insn->data[2] == GPCT_IndexPhaseLowLow) { 606 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) { 607 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) { 608 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) { 609 } 610 /* Take into account the index pulse? */ 611 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX) 612 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */ 613 614 /* Set Counter Mode Register */ 615 cmReg.value = (short)(insn->data[1] & 0xFFFF); 616 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 617 618 /* Load the pre-laod register high word */ 619 value = (short)((insn->data[2] >> 16) & 0xFFFF); 620 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 621 622 /* Load the pre-laod register low word */ 623 value = (short)(insn->data[2] & 0xFFFF); 624 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 625 626 /* Write the Counter Control Register */ 627 if (insn->data[3] != 0) { 628 value = (short)(insn->data[3] & 0xFFFF); 629 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 630 } 631 /* Reset the counter if it is software preload */ 632 if (cmReg.reg.autoLoadResetRcap == 0) { 633 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 634 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 635 } 636#endif 637 break; 638 639 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 640 /* 641 data[0]: Application Type 642 data[1]: Counter Mode Register Value 643 data[2]: Pre-load Register 0 Value 644 data[3]: Pre-load Register 1 Value 645 data[4]: Conter Control Register 646 */ 647 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n"); 648 devpriv->s526_gpct_config[subdev_channel].app = 649 SinglePulseGeneration; 650 651 /* Set Counter Mode Register */ 652 cmReg.value = (short)(insn->data[1] & 0xFFFF); 653 cmReg.reg.preloadRegSel = 0; /* PR0 */ 654 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 655 656 /* Load the pre-laod register 0 high word */ 657 value = (short)((insn->data[2] >> 16) & 0xFFFF); 658 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 659 660 /* Load the pre-laod register 0 low word */ 661 value = (short)(insn->data[2] & 0xFFFF); 662 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 663 664 /* Set Counter Mode Register */ 665 cmReg.value = (short)(insn->data[1] & 0xFFFF); 666 cmReg.reg.preloadRegSel = 1; /* PR1 */ 667 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 668 669 /* Load the pre-laod register 1 high word */ 670 value = (short)((insn->data[3] >> 16) & 0xFFFF); 671 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 672 673 /* Load the pre-laod register 1 low word */ 674 value = (short)(insn->data[3] & 0xFFFF); 675 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 676 677 /* Write the Counter Control Register */ 678 if (insn->data[3] != 0) { 679 value = (short)(insn->data[3] & 0xFFFF); 680 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 681 } 682 break; 683 684 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 685 /* 686 data[0]: Application Type 687 data[1]: Counter Mode Register Value 688 data[2]: Pre-load Register 0 Value 689 data[3]: Pre-load Register 1 Value 690 data[4]: Conter Control Register 691 */ 692 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n"); 693 devpriv->s526_gpct_config[subdev_channel].app = 694 PulseTrainGeneration; 695 696 /* Set Counter Mode Register */ 697 cmReg.value = (short)(insn->data[1] & 0xFFFF); 698 cmReg.reg.preloadRegSel = 0; /* PR0 */ 699 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 700 701 /* Load the pre-laod register 0 high word */ 702 value = (short)((insn->data[2] >> 16) & 0xFFFF); 703 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 704 705 /* Load the pre-laod register 0 low word */ 706 value = (short)(insn->data[2] & 0xFFFF); 707 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 708 709 /* Set Counter Mode Register */ 710 cmReg.value = (short)(insn->data[1] & 0xFFFF); 711 cmReg.reg.preloadRegSel = 1; /* PR1 */ 712 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 713 714 /* Load the pre-laod register 1 high word */ 715 value = (short)((insn->data[3] >> 16) & 0xFFFF); 716 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 717 718 /* Load the pre-laod register 1 low word */ 719 value = (short)(insn->data[3] & 0xFFFF); 720 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 721 722 /* Write the Counter Control Register */ 723 if (insn->data[3] != 0) { 724 value = (short)(insn->data[3] & 0xFFFF); 725 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 726 } 727 break; 728 729 default: 730 printk("s526: unsupported GPCT_insn_config\n"); 731 return -EINVAL; 732 break; 733 } 734 735 return insn->n; 736} 737 738static int s526_gpct_winsn(struct comedi_device *dev, 739 struct comedi_subdevice *s, struct comedi_insn *insn, 740 unsigned int *data) 741{ 742 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ 743 short value; 744 745 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel); 746 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel)); 747 printk("s526: Counter Mode Register: %x\n", cmReg.value); 748 /* Check what Application of Counter this channel is configured for */ 749 switch (devpriv->s526_gpct_config[subdev_channel].app) { 750 case PositionMeasurement: 751 printk("S526: INSN_WRITE: PM\n"); 752 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, 753 subdev_channel)); 754 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel)); 755 break; 756 757 case SinglePulseGeneration: 758 printk("S526: INSN_WRITE: SPG\n"); 759 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, 760 subdev_channel)); 761 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel)); 762 break; 763 764 case PulseTrainGeneration: 765 /* data[0] contains the PULSE_WIDTH 766 data[1] contains the PULSE_PERIOD 767 @pre PULSE_PERIOD > PULSE_WIDTH > 0 768 The above periods must be expressed as a multiple of the 769 pulse frequency on the selected source 770 */ 771 printk("S526: INSN_WRITE: PTG\n"); 772 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) { 773 (devpriv->s526_gpct_config[subdev_channel]).data[0] = 774 insn->data[0]; 775 (devpriv->s526_gpct_config[subdev_channel]).data[1] = 776 insn->data[1]; 777 } else { 778 printk("%d \t %d\n", insn->data[1], insn->data[2]); 779 printk 780 ("s526: INSN_WRITE: PTG: Problem with Pulse params\n"); 781 return -EINVAL; 782 } 783 784 value = (short)((*data >> 16) & 0xFFFF); 785 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 786 value = (short)(*data & 0xFFFF); 787 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 788 break; 789 default: /* Impossible */ 790 printk 791 ("s526: INSN_WRITE: Functionality %d not implemented yet\n", 792 devpriv->s526_gpct_config[subdev_channel].app); 793 return -EINVAL; 794 break; 795 } 796 /* return the number of samples written */ 797 return insn->n; 798} 799 800#define ISR_ADC_DONE 0x4 801static int s526_ai_insn_config(struct comedi_device *dev, 802 struct comedi_subdevice *s, 803 struct comedi_insn *insn, unsigned int *data) 804{ 805 int result = -EINVAL; 806 807 if (insn->n < 1) 808 return result; 809 810 result = insn->n; 811 812 /* data[0] : channels was set in relevant bits. 813 data[1] : delay 814 */ 815 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to 816 * enable channels here. The channel should be enabled in the 817 * INSN_READ handler. */ 818 819 /* Enable ADC interrupt */ 820 outw(ISR_ADC_DONE, ADDR_REG(REG_IER)); 821/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */ 822 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5; 823 if (data[1] > 0) 824 devpriv->s526_ai_config |= 0x8000; /* set the delay */ 825 826 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */ 827 828 return result; 829} 830 831/* 832 * "instructions" read/write data in "one-shot" or "software-triggered" 833 * mode. 834 */ 835static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 836 struct comedi_insn *insn, unsigned int *data) 837{ 838 int n, i; 839 int chan = CR_CHAN(insn->chanspec); 840 unsigned short value; 841 unsigned int d; 842 unsigned int status; 843 844 /* Set configured delay, enable channel for this channel only, 845 * select "ADC read" channel, set "ADC start" bit. */ 846 value = (devpriv->s526_ai_config & 0x8000) | 847 ((1 << 5) << chan) | (chan << 1) | 0x0001; 848 849 /* convert n samples */ 850 for (n = 0; n < insn->n; n++) { 851 /* trigger conversion */ 852 outw(value, ADDR_REG(REG_ADC)); 853/* printk("s526: Wrote 0x%04x to ADC\n", value); */ 854/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */ 855 856#define TIMEOUT 100 857 /* wait for conversion to end */ 858 for (i = 0; i < TIMEOUT; i++) { 859 status = inw(ADDR_REG(REG_ISR)); 860 if (status & ISR_ADC_DONE) { 861 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR)); 862 break; 863 } 864 } 865 if (i == TIMEOUT) { 866 /* printk() should be used instead of printk() 867 * whenever the code can be called from real-time. */ 868 printk("s526: ADC(0x%04x) timeout\n", 869 inw(ADDR_REG(REG_ISR))); 870 return -ETIMEDOUT; 871 } 872 873 /* read data */ 874 d = inw(ADDR_REG(REG_ADD)); 875/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */ 876 877 /* munge data */ 878 data[n] = d ^ 0x8000; 879 } 880 881 /* return the number of samples read/written */ 882 return n; 883} 884 885static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 886 struct comedi_insn *insn, unsigned int *data) 887{ 888 int i; 889 int chan = CR_CHAN(insn->chanspec); 890 unsigned short val; 891 892/* printk("s526_ao_winsn\n"); */ 893 val = chan << 1; 894/* outw(val, dev->iobase + REG_DAC); */ 895 outw(val, ADDR_REG(REG_DAC)); 896 897 /* Writing a list of values to an AO channel is probably not 898 * very useful, but that's how the interface is defined. */ 899 for (i = 0; i < insn->n; i++) { 900 /* a typical programming sequence */ 901/* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */ 902 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */ 903 devpriv->ao_readback[chan] = data[i]; 904/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */ 905 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */ 906 } 907 908 /* return the number of samples read/written */ 909 return i; 910} 911 912/* AO subdevices should have a read insn as well as a write insn. 913 * Usually this means copying a value stored in devpriv. */ 914static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 915 struct comedi_insn *insn, unsigned int *data) 916{ 917 int i; 918 int chan = CR_CHAN(insn->chanspec); 919 920 for (i = 0; i < insn->n; i++) 921 data[i] = devpriv->ao_readback[chan]; 922 923 return i; 924} 925 926/* DIO devices are slightly special. Although it is possible to 927 * implement the insn_read/insn_write interface, it is much more 928 * useful to applications if you implement the insn_bits interface. 929 * This allows packed reading/writing of the DIO channels. The 930 * comedi core can convert between insn_bits and insn_read/write */ 931static int s526_dio_insn_bits(struct comedi_device *dev, 932 struct comedi_subdevice *s, 933 struct comedi_insn *insn, unsigned int *data) 934{ 935 if (insn->n != 2) 936 return -EINVAL; 937 938 /* The insn data is a mask in data[0] and the new data 939 * in data[1], each channel cooresponding to a bit. */ 940 if (data[0]) { 941 s->state &= ~data[0]; 942 s->state |= data[0] & data[1]; 943 /* Write out the new digital output lines */ 944 outw(s->state, ADDR_REG(REG_DIO)); 945 } 946 947 /* on return, data[1] contains the value of the digital 948 * input and output lines. */ 949 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */ 950 /* or we could just return the software copy of the output values if 951 * it was a purely digital output subdevice */ 952 /* data[1]=s->state; */ 953 954 return 2; 955} 956 957static int s526_dio_insn_config(struct comedi_device *dev, 958 struct comedi_subdevice *s, 959 struct comedi_insn *insn, unsigned int *data) 960{ 961 int chan = CR_CHAN(insn->chanspec); 962 short value; 963 964 printk("S526 DIO insn_config\n"); 965 966 if (insn->n != 1) 967 return -EINVAL; 968 969 value = inw(ADDR_REG(REG_DIO)); 970 971 /* The input or output configuration of each digital line is 972 * configured by a special insn_config instruction. chanspec 973 * contains the channel to be changed, and data[0] contains the 974 * value COMEDI_INPUT or COMEDI_OUTPUT. */ 975 976 if (data[0] == COMEDI_OUTPUT) { 977 value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */ 978 s->io_bits |= (0xF << chan); 979 } else { 980 value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */ 981 s->io_bits &= ~(0xF << chan); 982 } 983 outw(value, ADDR_REG(REG_DIO)); 984 985 return 1; 986} 987 988/* 989 * A convenient macro that defines init_module() and cleanup_module(), 990 * as necessary. 991 */ 992COMEDI_INITCLEANUP(driver_s526); 993