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