s526.c revision 5f74ea14c07fee91d3bdbaad88bff6264c6200e6
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, struct comedi_subdevice *s, 251 struct comedi_insn *insn, unsigned int *data); 252static int s526_gpct_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 253 struct comedi_insn *insn, unsigned int *data); 254static int s526_gpct_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 255 struct comedi_insn *insn, unsigned int *data); 256static int s526_ai_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 257 struct comedi_insn *insn, unsigned int *data); 258static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 259 struct comedi_insn *insn, unsigned int *data); 260static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 261 struct comedi_insn *insn, unsigned int *data); 262static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 263 struct comedi_insn *insn, unsigned int *data); 264static int s526_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 265 struct comedi_insn *insn, unsigned int *data); 266static int s526_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 267 struct comedi_insn *insn, unsigned int *data); 268 269/* 270 * Attach is called by the Comedi core to configure the driver 271 * for a particular board. If you specified a board_name array 272 * in the driver structure, dev->board_ptr contains that 273 * address. 274 */ 275static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) 276{ 277 struct comedi_subdevice *s; 278 int iobase; 279 int i, n; 280/* short value; */ 281/* int subdev_channel = 0; */ 282 283 printk("comedi%d: s526: ", dev->minor); 284 285 iobase = it->options[0]; 286 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) { 287 comedi_error(dev, "I/O port conflict"); 288 return -EIO; 289 } 290 dev->iobase = iobase; 291 292 printk("iobase=0x%lx\n", dev->iobase); 293 294 /*** make it a little quieter, exw, 8/29/06 295 for (i = 0; i < S526_NUM_PORTS; i++) { 296 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i]))); 297 } 298 ***/ 299 300/* 301 * Initialize dev->board_name. Note that we can use the "thisboard" 302 * macro now, since we just initialized it in the last line. 303 */ 304 dev->board_ptr = &s526_boards[0]; 305 306 dev->board_name = thisboard->name; 307 308/* 309 * Allocate the private structure area. alloc_private() is a 310 * convenient macro defined in comedidev.h. 311 */ 312 if (alloc_private(dev, sizeof(struct s526_private)) < 0) 313 return -ENOMEM; 314 315/* 316 * Allocate the subdevice structures. alloc_subdevice() is a 317 * convenient macro defined in comedidev.h. 318 */ 319 dev->n_subdevices = 4; 320 if (alloc_subdevices(dev, dev->n_subdevices) < 0) 321 return -ENOMEM; 322 323 s = dev->subdevices + 0; 324 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */ 325 s->type = COMEDI_SUBD_COUNTER; 326 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL; 327 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */ 328 s->n_chan = thisboard->gpct_chans; 329 s->maxdata = 0x00ffffff; /* 24 bit counter */ 330 s->insn_read = s526_gpct_rinsn; 331 s->insn_config = s526_gpct_insn_config; 332 s->insn_write = s526_gpct_winsn; 333 334 /* Command are not implemented yet, however they are necessary to 335 allocate the necessary memory for the comedi_async struct (used 336 to trigger the GPCT in case of pulsegenerator function */ 337 /* s->do_cmd = s526_gpct_cmd; */ 338 /* s->do_cmdtest = s526_gpct_cmdtest; */ 339 /* s->cancel = s526_gpct_cancel; */ 340 341 s = dev->subdevices + 1; 342 /* dev->read_subdev=s; */ 343 /* analog input subdevice */ 344 s->type = COMEDI_SUBD_AI; 345 /* we support differential */ 346 s->subdev_flags = SDF_READABLE | SDF_DIFF; 347 /* channels 0 to 7 are the regular differential inputs */ 348 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */ 349 s->n_chan = 10; 350 s->maxdata = 0xffff; 351 s->range_table = &range_bipolar10; 352 s->len_chanlist = 16; /* This is the maximum chanlist length that 353 the board can handle */ 354 s->insn_read = s526_ai_rinsn; 355 s->insn_config = s526_ai_insn_config; 356 357 s = dev->subdevices + 2; 358 /* analog output subdevice */ 359 s->type = COMEDI_SUBD_AO; 360 s->subdev_flags = SDF_WRITABLE; 361 s->n_chan = 4; 362 s->maxdata = 0xffff; 363 s->range_table = &range_bipolar10; 364 s->insn_write = s526_ao_winsn; 365 s->insn_read = s526_ao_rinsn; 366 367 s = dev->subdevices + 3; 368 /* digital i/o subdevice */ 369 if (thisboard->have_dio) { 370 s->type = COMEDI_SUBD_DIO; 371 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 372 s->n_chan = 2; 373 s->maxdata = 1; 374 s->range_table = &range_digital; 375 s->insn_bits = s526_dio_insn_bits; 376 s->insn_config = s526_dio_insn_config; 377 } else { 378 s->type = COMEDI_SUBD_UNUSED; 379 } 380 381 printk("attached\n"); 382 383 return 1; 384 385#if 0 386 /* Example of Counter Application */ 387 /* One-shot (software trigger) */ 388 cmReg.reg.coutSource = 0; /* out RCAP */ 389 cmReg.reg.coutPolarity = 1; /* Polarity inverted */ 390 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */ 391 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */ 392 cmReg.reg.ctEnableCtrl = 2; /* Hardware */ 393 cmReg.reg.clockSource = 2; /* Internal */ 394 cmReg.reg.countDir = 1; /* Down */ 395 cmReg.reg.countDirCtrl = 1; /* Software */ 396 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 397 cmReg.reg.preloadRegSel = 0; /* PR0 */ 398 cmReg.reg.reserved = 0; 399 400 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 401 402 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 403 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 404 405 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 406 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 407 408 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */ 409 410#else 411 412 /* Set Counter Mode Register */ 413 cmReg.reg.coutSource = 0; /* out RCAP */ 414 cmReg.reg.coutPolarity = 0; /* Polarity inverted */ 415 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 416 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ 417 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ 418 cmReg.reg.clockSource = 3; /* x4 */ 419 cmReg.reg.countDir = 0; /* up */ 420 cmReg.reg.countDirCtrl = 0; /* quadrature */ 421 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 422 cmReg.reg.preloadRegSel = 0; /* PR0 */ 423 cmReg.reg.reserved = 0; 424 425 n = 0; 426 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M, 427 n)); 428 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n)); 429 udelay(1000); 430 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); 431 432 /* Load the pre-laod register high word */ 433/* value = (short) (0x55); */ 434/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */ 435 436 /* Load the pre-laod register low word */ 437/* value = (short)(0xaa55); */ 438/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */ 439 440 /* Write the Counter Control Register */ 441/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */ 442 443 /* Reset the counter if it is software preload */ 444 if (cmReg.reg.autoLoadResetRcap == 0) { 445 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */ 446 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */ 447 } 448 449 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n)); 450 udelay(1000); 451 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); 452 453#endif 454 printk("Current registres:\n"); 455 456 for (i = 0; i < S526_NUM_PORTS; i++) { 457 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]), 458 inw(ADDR_REG(s526_ports[i]))); 459 } 460 return 1; 461} 462 463/* 464 * _detach is called to deconfigure a device. It should deallocate 465 * resources. 466 * This function is also called when _attach() fails, so it should be 467 * careful not to release resources that were not necessarily 468 * allocated by _attach(). dev->private and dev->subdevices are 469 * deallocated automatically by the core. 470 */ 471static int s526_detach(struct comedi_device *dev) 472{ 473 printk("comedi%d: s526: remove\n", dev->minor); 474 475 if (dev->iobase > 0) 476 release_region(dev->iobase, S526_IOSIZE); 477 478 return 0; 479} 480 481static int s526_gpct_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 482 struct comedi_insn *insn, unsigned int *data) 483{ 484 int i; /* counts the Data */ 485 int counter_channel = CR_CHAN(insn->chanspec); 486 unsigned short datalow; 487 unsigned short datahigh; 488 489 /* Check if (n > 0) */ 490 if (insn->n <= 0) { 491 printk("s526: INSN_READ: n should be > 0\n"); 492 return -EINVAL; 493 } 494 /* Read the low word first */ 495 for (i = 0; i < insn->n; i++) { 496 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel)); 497 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel)); 498 data[i] = (int)(datahigh & 0x00FF); 499 data[i] = (data[i] << 16) | (datalow & 0xFFFF); 500/* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */ 501 } 502 return i; 503} 504 505static int s526_gpct_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 506 struct comedi_insn *insn, unsigned int *data) 507{ 508 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ 509 int i; 510 short value; 511 512/* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */ 513 514 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) { 515 devpriv->s526_gpct_config[subdev_channel].data[i] = 516 insn->data[i]; 517/* printk("data[%d]=%x\n", i, insn->data[i]); */ 518 } 519 520 /* Check what type of Counter the user requested, data[0] contains */ 521 /* the Application type */ 522 switch (insn->data[0]) { 523 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 524 /* 525 data[0]: Application Type 526 data[1]: Counter Mode Register Value 527 data[2]: Pre-load Register Value 528 data[3]: Conter Control Register 529 */ 530 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n"); 531 devpriv->s526_gpct_config[subdev_channel].app = 532 PositionMeasurement; 533 534#if 0 535 /* Example of Counter Application */ 536 /* One-shot (software trigger) */ 537 cmReg.reg.coutSource = 0; /* out RCAP */ 538 cmReg.reg.coutPolarity = 1; /* Polarity inverted */ 539 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 540 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */ 541 cmReg.reg.ctEnableCtrl = 2; /* Hardware */ 542 cmReg.reg.clockSource = 2; /* Internal */ 543 cmReg.reg.countDir = 1; /* Down */ 544 cmReg.reg.countDirCtrl = 1; /* Software */ 545 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 546 cmReg.reg.preloadRegSel = 0; /* PR0 */ 547 cmReg.reg.reserved = 0; 548 549 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 550 551 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 552 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 553 554 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 555 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 556 557 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */ 558 559#endif 560 561#if 1 562 /* Set Counter Mode Register */ 563 cmReg.reg.coutSource = 0; /* out RCAP */ 564 cmReg.reg.coutPolarity = 0; /* Polarity inverted */ 565 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ 566 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ 567 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ 568 cmReg.reg.clockSource = 3; /* x4 */ 569 cmReg.reg.countDir = 0; /* up */ 570 cmReg.reg.countDirCtrl = 0; /* quadrature */ 571 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ 572 cmReg.reg.preloadRegSel = 0; /* PR0 */ 573 cmReg.reg.reserved = 0; 574 575 /* Set Counter Mode Register */ 576/* printk("s526: Counter Mode register=%x\n", cmReg.value); */ 577 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 578 579 /* Reset the counter if it is software preload */ 580 if (cmReg.reg.autoLoadResetRcap == 0) { 581 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 582/* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */ 583 } 584#else 585 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */ 586 587 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */ 588 if (insn->data[1] == GPCT_X2) { 589 cmReg.reg.clockSource = 1; 590 } else if (insn->data[1] == GPCT_X4) { 591 cmReg.reg.clockSource = 2; 592 } else { 593 cmReg.reg.clockSource = 0; 594 } 595 596 /* When to take into account the indexpulse: */ 597 if (insn->data[2] == GPCT_IndexPhaseLowLow) { 598 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) { 599 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) { 600 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) { 601 } 602 /* Take into account the index pulse? */ 603 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX) 604 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */ 605 606 /* Set Counter Mode Register */ 607 cmReg.value = (short) (insn->data[1] & 0xFFFF); 608 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 609 610 /* Load the pre-laod register high word */ 611 value = (short) ((insn->data[2] >> 16) & 0xFFFF); 612 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 613 614 /* Load the pre-laod register low word */ 615 value = (short) (insn->data[2] & 0xFFFF); 616 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 617 618 /* Write the Counter Control Register */ 619 if (insn->data[3] != 0) { 620 value = (short) (insn->data[3] & 0xFFFF); 621 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 622 } 623 /* Reset the counter if it is software preload */ 624 if (cmReg.reg.autoLoadResetRcap == 0) { 625 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */ 626 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */ 627 } 628#endif 629 break; 630 631 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 632 /* 633 data[0]: Application Type 634 data[1]: Counter Mode Register Value 635 data[2]: Pre-load Register 0 Value 636 data[3]: Pre-load Register 1 Value 637 data[4]: Conter Control Register 638 */ 639 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n"); 640 devpriv->s526_gpct_config[subdev_channel].app = 641 SinglePulseGeneration; 642 643 /* Set Counter Mode Register */ 644 cmReg.value = (short) (insn->data[1] & 0xFFFF); 645 cmReg.reg.preloadRegSel = 0; /* PR0 */ 646 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 647 648 /* Load the pre-laod register 0 high word */ 649 value = (short) ((insn->data[2] >> 16) & 0xFFFF); 650 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 651 652 /* Load the pre-laod register 0 low word */ 653 value = (short) (insn->data[2] & 0xFFFF); 654 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 655 656 /* Set Counter Mode Register */ 657 cmReg.value = (short) (insn->data[1] & 0xFFFF); 658 cmReg.reg.preloadRegSel = 1; /* PR1 */ 659 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 660 661 /* Load the pre-laod register 1 high word */ 662 value = (short) ((insn->data[3] >> 16) & 0xFFFF); 663 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 664 665 /* Load the pre-laod register 1 low word */ 666 value = (short) (insn->data[3] & 0xFFFF); 667 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 668 669 /* Write the Counter Control Register */ 670 if (insn->data[3] != 0) { 671 value = (short) (insn->data[3] & 0xFFFF); 672 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 673 } 674 break; 675 676 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 677 /* 678 data[0]: Application Type 679 data[1]: Counter Mode Register Value 680 data[2]: Pre-load Register 0 Value 681 data[3]: Pre-load Register 1 Value 682 data[4]: Conter Control Register 683 */ 684 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n"); 685 devpriv->s526_gpct_config[subdev_channel].app = 686 PulseTrainGeneration; 687 688 /* Set Counter Mode Register */ 689 cmReg.value = (short) (insn->data[1] & 0xFFFF); 690 cmReg.reg.preloadRegSel = 0; /* PR0 */ 691 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 692 693 /* Load the pre-laod register 0 high word */ 694 value = (short) ((insn->data[2] >> 16) & 0xFFFF); 695 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 696 697 /* Load the pre-laod register 0 low word */ 698 value = (short) (insn->data[2] & 0xFFFF); 699 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 700 701 /* Set Counter Mode Register */ 702 cmReg.value = (short) (insn->data[1] & 0xFFFF); 703 cmReg.reg.preloadRegSel = 1; /* PR1 */ 704 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); 705 706 /* Load the pre-laod register 1 high word */ 707 value = (short) ((insn->data[3] >> 16) & 0xFFFF); 708 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 709 710 /* Load the pre-laod register 1 low word */ 711 value = (short) (insn->data[3] & 0xFFFF); 712 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 713 714 /* Write the Counter Control Register */ 715 if (insn->data[3] != 0) { 716 value = (short) (insn->data[3] & 0xFFFF); 717 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); 718 } 719 break; 720 721 default: 722 printk("s526: unsupported GPCT_insn_config\n"); 723 return -EINVAL; 724 break; 725 } 726 727 return insn->n; 728} 729 730static int s526_gpct_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 731 struct comedi_insn *insn, unsigned int *data) 732{ 733 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ 734 short value; 735 736 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel); 737 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel)); 738 printk("s526: Counter Mode Register: %x\n", cmReg.value); 739 /* Check what Application of Counter this channel is configured for */ 740 switch (devpriv->s526_gpct_config[subdev_channel].app) { 741 case PositionMeasurement: 742 printk("S526: INSN_WRITE: PM\n"); 743 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, 744 subdev_channel)); 745 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel)); 746 break; 747 748 case SinglePulseGeneration: 749 printk("S526: INSN_WRITE: SPG\n"); 750 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, 751 subdev_channel)); 752 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel)); 753 break; 754 755 case PulseTrainGeneration: 756 /* data[0] contains the PULSE_WIDTH 757 data[1] contains the PULSE_PERIOD 758 @pre PULSE_PERIOD > PULSE_WIDTH > 0 759 The above periods must be expressed as a multiple of the 760 pulse frequency on the selected source 761 */ 762 printk("S526: INSN_WRITE: PTG\n"); 763 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) { 764 (devpriv->s526_gpct_config[subdev_channel]).data[0] = 765 insn->data[0]; 766 (devpriv->s526_gpct_config[subdev_channel]).data[1] = 767 insn->data[1]; 768 } else { 769 printk("%d \t %d\n", insn->data[1], insn->data[2]); 770 printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n"); 771 return -EINVAL; 772 } 773 774 value = (short) ((*data >> 16) & 0xFFFF); 775 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); 776 value = (short) (*data & 0xFFFF); 777 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); 778 break; 779 default: /* Impossible */ 780 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n", devpriv->s526_gpct_config[subdev_channel].app); 781 return -EINVAL; 782 break; 783 } 784 /* return the number of samples written */ 785 return insn->n; 786} 787 788#define ISR_ADC_DONE 0x4 789static int s526_ai_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 790 struct comedi_insn *insn, unsigned int *data) 791{ 792 int result = -EINVAL; 793 794 if (insn->n < 1) 795 return result; 796 797 result = insn->n; 798 799 /* data[0] : channels was set in relevant bits. 800 data[1] : delay 801 */ 802 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to 803 * enable channels here. The channel should be enabled in the 804 * INSN_READ handler. */ 805 806 /* Enable ADC interrupt */ 807 outw(ISR_ADC_DONE, ADDR_REG(REG_IER)); 808/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */ 809 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5; 810 if (data[1] > 0) 811 devpriv->s526_ai_config |= 0x8000; /* set the delay */ 812 813 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */ 814 815 return result; 816} 817 818/* 819 * "instructions" read/write data in "one-shot" or "software-triggered" 820 * mode. 821 */ 822static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 823 struct comedi_insn *insn, unsigned int *data) 824{ 825 int n, i; 826 int chan = CR_CHAN(insn->chanspec); 827 unsigned short value; 828 unsigned int d; 829 unsigned int status; 830 831 /* Set configured delay, enable channel for this channel only, 832 * select "ADC read" channel, set "ADC start" bit. */ 833 value = (devpriv->s526_ai_config & 0x8000) | 834 ((1 << 5) << chan) | (chan << 1) | 0x0001; 835 836 /* convert n samples */ 837 for (n = 0; n < insn->n; n++) { 838 /* trigger conversion */ 839 outw(value, ADDR_REG(REG_ADC)); 840/* printk("s526: Wrote 0x%04x to ADC\n", value); */ 841/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */ 842 843#define TIMEOUT 100 844 /* wait for conversion to end */ 845 for (i = 0; i < TIMEOUT; i++) { 846 status = inw(ADDR_REG(REG_ISR)); 847 if (status & ISR_ADC_DONE) { 848 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR)); 849 break; 850 } 851 } 852 if (i == TIMEOUT) { 853 /* printk() should be used instead of printk() 854 * whenever the code can be called from real-time. */ 855 printk("s526: ADC(0x%04x) timeout\n", 856 inw(ADDR_REG(REG_ISR))); 857 return -ETIMEDOUT; 858 } 859 860 /* read data */ 861 d = inw(ADDR_REG(REG_ADD)); 862/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */ 863 864 /* munge data */ 865 data[n] = d ^ 0x8000; 866 } 867 868 /* return the number of samples read/written */ 869 return n; 870} 871 872static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 873 struct comedi_insn *insn, unsigned int *data) 874{ 875 int i; 876 int chan = CR_CHAN(insn->chanspec); 877 unsigned short val; 878 879/* printk("s526_ao_winsn\n"); */ 880 val = chan << 1; 881/* outw(val, dev->iobase + REG_DAC); */ 882 outw(val, ADDR_REG(REG_DAC)); 883 884 /* Writing a list of values to an AO channel is probably not 885 * very useful, but that's how the interface is defined. */ 886 for (i = 0; i < insn->n; i++) { 887 /* a typical programming sequence */ 888/* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */ 889 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */ 890 devpriv->ao_readback[chan] = data[i]; 891/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */ 892 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */ 893 } 894 895 /* return the number of samples read/written */ 896 return i; 897} 898 899/* AO subdevices should have a read insn as well as a write insn. 900 * Usually this means copying a value stored in devpriv. */ 901static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 902 struct comedi_insn *insn, unsigned int *data) 903{ 904 int i; 905 int chan = CR_CHAN(insn->chanspec); 906 907 for (i = 0; i < insn->n; i++) 908 data[i] = devpriv->ao_readback[chan]; 909 910 return i; 911} 912 913/* DIO devices are slightly special. Although it is possible to 914 * implement the insn_read/insn_write interface, it is much more 915 * useful to applications if you implement the insn_bits interface. 916 * This allows packed reading/writing of the DIO channels. The 917 * comedi core can convert between insn_bits and insn_read/write */ 918static int s526_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 919 struct comedi_insn *insn, unsigned int *data) 920{ 921 if (insn->n != 2) 922 return -EINVAL; 923 924 /* The insn data is a mask in data[0] and the new data 925 * in data[1], each channel cooresponding to a bit. */ 926 if (data[0]) { 927 s->state &= ~data[0]; 928 s->state |= data[0] & data[1]; 929 /* Write out the new digital output lines */ 930 outw(s->state, ADDR_REG(REG_DIO)); 931 } 932 933 /* on return, data[1] contains the value of the digital 934 * input and output lines. */ 935 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */ 936 /* or we could just return the software copy of the output values if 937 * it was a purely digital output subdevice */ 938 /* data[1]=s->state; */ 939 940 return 2; 941} 942 943static int s526_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 944 struct comedi_insn *insn, unsigned int *data) 945{ 946 int chan = CR_CHAN(insn->chanspec); 947 short value; 948 949 printk("S526 DIO insn_config\n"); 950 951 if (insn->n != 1) 952 return -EINVAL; 953 954 value = inw(ADDR_REG(REG_DIO)); 955 956 /* The input or output configuration of each digital line is 957 * configured by a special insn_config instruction. chanspec 958 * contains the channel to be changed, and data[0] contains the 959 * value COMEDI_INPUT or COMEDI_OUTPUT. */ 960 961 if (data[0] == COMEDI_OUTPUT) { 962 value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */ 963 s->io_bits |= (0xF << chan); 964 } else { 965 value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */ 966 s->io_bits &= ~(0xF << chan); 967 } 968 outw(value, ADDR_REG(REG_DIO)); 969 970 return 1; 971} 972 973/* 974 * A convenient macro that defines init_module() and cleanup_module(), 975 * as necessary. 976 */ 977COMEDI_INITCLEANUP(driver_s526); 978