1/* 2 * addi_apci_3501.c 3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 4 * Project manager: Eric Stolz 5 * 6 * ADDI-DATA GmbH 7 * Dieselstrasse 3 8 * D-77833 Ottersweier 9 * Tel: +19(0)7223/9493-0 10 * Fax: +49(0)7223/9493-92 11 * http://www.addi-data.com 12 * info@addi-data.com 13 * 14 * This program is free software; you can redistribute it and/or modify it 15 * under the terms of the GNU General Public License as published by the 16 * Free Software Foundation; either version 2 of the License, or (at your 17 * option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, but WITHOUT 20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 22 * more details. 23 */ 24 25#include <linux/module.h> 26#include <linux/pci.h> 27#include <linux/interrupt.h> 28#include <linux/sched.h> 29 30#include "../comedidev.h" 31#include "comedi_fc.h" 32#include "amcc_s5933.h" 33 34/* 35 * PCI bar 1 register I/O map 36 */ 37#define APCI3501_AO_CTRL_STATUS_REG 0x00 38#define APCI3501_AO_CTRL_BIPOLAR (1 << 0) 39#define APCI3501_AO_STATUS_READY (1 << 8) 40#define APCI3501_AO_DATA_REG 0x04 41#define APCI3501_AO_DATA_CHAN(x) ((x) << 0) 42#define APCI3501_AO_DATA_VAL(x) ((x) << 8) 43#define APCI3501_AO_DATA_BIPOLAR (1 << 31) 44#define APCI3501_AO_TRIG_SCS_REG 0x08 45#define APCI3501_TIMER_SYNC_REG 0x20 46#define APCI3501_TIMER_RELOAD_REG 0x24 47#define APCI3501_TIMER_TIMEBASE_REG 0x28 48#define APCI3501_TIMER_CTRL_REG 0x2c 49#define APCI3501_TIMER_STATUS_REG 0x30 50#define APCI3501_TIMER_IRQ_REG 0x34 51#define APCI3501_TIMER_WARN_RELOAD_REG 0x38 52#define APCI3501_TIMER_WARN_TIMEBASE_REG 0x3c 53#define APCI3501_DO_REG 0x40 54#define APCI3501_DI_REG 0x50 55 56/* 57 * AMCC S5933 NVRAM 58 */ 59#define NVRAM_USER_DATA_START 0x100 60 61#define NVCMD_BEGIN_READ (0x7 << 5) 62#define NVCMD_LOAD_LOW (0x4 << 5) 63#define NVCMD_LOAD_HIGH (0x5 << 5) 64 65/* 66 * Function types stored in the eeprom 67 */ 68#define EEPROM_DIGITALINPUT 0 69#define EEPROM_DIGITALOUTPUT 1 70#define EEPROM_ANALOGINPUT 2 71#define EEPROM_ANALOGOUTPUT 3 72#define EEPROM_TIMER 4 73#define EEPROM_WATCHDOG 5 74#define EEPROM_TIMER_WATCHDOG_COUNTER 10 75 76struct apci3501_private { 77 int i_IobaseAmcc; 78 struct task_struct *tsk_Current; 79 unsigned char b_TimerSelectMode; 80}; 81 82static struct comedi_lrange apci3501_ao_range = { 83 2, { 84 BIP_RANGE(10), 85 UNI_RANGE(10) 86 } 87}; 88 89static int apci3501_wait_for_dac(struct comedi_device *dev) 90{ 91 unsigned int status; 92 93 do { 94 status = inl(dev->iobase + APCI3501_AO_CTRL_STATUS_REG); 95 } while (!(status & APCI3501_AO_STATUS_READY)); 96 97 return 0; 98} 99 100static int apci3501_ao_insn_write(struct comedi_device *dev, 101 struct comedi_subdevice *s, 102 struct comedi_insn *insn, 103 unsigned int *data) 104{ 105 unsigned int chan = CR_CHAN(insn->chanspec); 106 unsigned int range = CR_RANGE(insn->chanspec); 107 unsigned int cfg = APCI3501_AO_DATA_CHAN(chan); 108 int ret; 109 int i; 110 111 /* 112 * All analog output channels have the same output range. 113 * 14-bit bipolar: 0-10V 114 * 13-bit unipolar: +/-10V 115 * Changing the range of one channel changes all of them! 116 */ 117 if (range) { 118 outl(0, dev->iobase + APCI3501_AO_CTRL_STATUS_REG); 119 } else { 120 cfg |= APCI3501_AO_DATA_BIPOLAR; 121 outl(APCI3501_AO_CTRL_BIPOLAR, 122 dev->iobase + APCI3501_AO_CTRL_STATUS_REG); 123 } 124 125 for (i = 0; i < insn->n; i++) { 126 unsigned int val = data[i]; 127 128 if (range == 1) { 129 if (data[i] > 0x1fff) { 130 dev_err(dev->class_dev, 131 "Unipolar resolution is only 13-bits\n"); 132 return -EINVAL; 133 } 134 } 135 136 ret = apci3501_wait_for_dac(dev); 137 if (ret) 138 return ret; 139 140 outl(cfg | APCI3501_AO_DATA_VAL(val), 141 dev->iobase + APCI3501_AO_DATA_REG); 142 143 s->readback[chan] = val; 144 } 145 146 return insn->n; 147} 148 149#include "addi-data/hwdrv_apci3501.c" 150 151static int apci3501_di_insn_bits(struct comedi_device *dev, 152 struct comedi_subdevice *s, 153 struct comedi_insn *insn, 154 unsigned int *data) 155{ 156 data[1] = inl(dev->iobase + APCI3501_DI_REG) & 0x3; 157 158 return insn->n; 159} 160 161static int apci3501_do_insn_bits(struct comedi_device *dev, 162 struct comedi_subdevice *s, 163 struct comedi_insn *insn, 164 unsigned int *data) 165{ 166 s->state = inl(dev->iobase + APCI3501_DO_REG); 167 168 if (comedi_dio_update_state(s, data)) 169 outl(s->state, dev->iobase + APCI3501_DO_REG); 170 171 data[1] = s->state; 172 173 return insn->n; 174} 175 176static void apci3501_eeprom_wait(unsigned long iobase) 177{ 178 unsigned char val; 179 180 do { 181 val = inb(iobase + AMCC_OP_REG_MCSR_NVCMD); 182 } while (val & 0x80); 183} 184 185static unsigned short apci3501_eeprom_readw(unsigned long iobase, 186 unsigned short addr) 187{ 188 unsigned short val = 0; 189 unsigned char tmp; 190 unsigned char i; 191 192 /* Add the offset to the start of the user data */ 193 addr += NVRAM_USER_DATA_START; 194 195 for (i = 0; i < 2; i++) { 196 /* Load the low 8 bit address */ 197 outb(NVCMD_LOAD_LOW, iobase + AMCC_OP_REG_MCSR_NVCMD); 198 apci3501_eeprom_wait(iobase); 199 outb((addr + i) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA); 200 apci3501_eeprom_wait(iobase); 201 202 /* Load the high 8 bit address */ 203 outb(NVCMD_LOAD_HIGH, iobase + AMCC_OP_REG_MCSR_NVCMD); 204 apci3501_eeprom_wait(iobase); 205 outb(((addr + i) >> 8) & 0xff, 206 iobase + AMCC_OP_REG_MCSR_NVDATA); 207 apci3501_eeprom_wait(iobase); 208 209 /* Read the eeprom data byte */ 210 outb(NVCMD_BEGIN_READ, iobase + AMCC_OP_REG_MCSR_NVCMD); 211 apci3501_eeprom_wait(iobase); 212 tmp = inb(iobase + AMCC_OP_REG_MCSR_NVDATA); 213 apci3501_eeprom_wait(iobase); 214 215 if (i == 0) 216 val |= tmp; 217 else 218 val |= (tmp << 8); 219 } 220 221 return val; 222} 223 224static int apci3501_eeprom_get_ao_n_chan(struct comedi_device *dev) 225{ 226 struct apci3501_private *devpriv = dev->private; 227 unsigned long iobase = devpriv->i_IobaseAmcc; 228 unsigned char nfuncs; 229 int i; 230 231 nfuncs = apci3501_eeprom_readw(iobase, 10) & 0xff; 232 233 /* Read functionality details */ 234 for (i = 0; i < nfuncs; i++) { 235 unsigned short offset = i * 4; 236 unsigned short addr; 237 unsigned char func; 238 unsigned short val; 239 240 func = apci3501_eeprom_readw(iobase, 12 + offset) & 0x3f; 241 addr = apci3501_eeprom_readw(iobase, 14 + offset); 242 243 if (func == EEPROM_ANALOGOUTPUT) { 244 val = apci3501_eeprom_readw(iobase, addr + 10); 245 return (val >> 4) & 0x3ff; 246 } 247 } 248 return 0; 249} 250 251static int apci3501_eeprom_insn_read(struct comedi_device *dev, 252 struct comedi_subdevice *s, 253 struct comedi_insn *insn, 254 unsigned int *data) 255{ 256 struct apci3501_private *devpriv = dev->private; 257 unsigned short addr = CR_CHAN(insn->chanspec); 258 259 data[0] = apci3501_eeprom_readw(devpriv->i_IobaseAmcc, 2 * addr); 260 261 return insn->n; 262} 263 264static irqreturn_t apci3501_interrupt(int irq, void *d) 265{ 266 struct comedi_device *dev = d; 267 struct apci3501_private *devpriv = dev->private; 268 unsigned int ui_Timer_AOWatchdog; 269 unsigned long ul_Command1; 270 int i_temp; 271 272 /* Disable Interrupt */ 273 ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG); 274 ul_Command1 = (ul_Command1 & 0xFFFFF9FDul); 275 outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG); 276 277 ui_Timer_AOWatchdog = inl(dev->iobase + APCI3501_TIMER_IRQ_REG) & 0x1; 278 if ((!ui_Timer_AOWatchdog)) { 279 dev_err(dev->class_dev, "IRQ from unknown source\n"); 280 return IRQ_NONE; 281 } 282 283 /* Enable Interrupt Send a signal to from kernel to user space */ 284 send_sig(SIGIO, devpriv->tsk_Current, 0); 285 ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG); 286 ul_Command1 = ((ul_Command1 & 0xFFFFF9FDul) | 1 << 1); 287 outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG); 288 i_temp = inl(dev->iobase + APCI3501_TIMER_STATUS_REG) & 0x1; 289 290 return IRQ_HANDLED; 291} 292 293static int apci3501_reset(struct comedi_device *dev) 294{ 295 unsigned int val; 296 int chan; 297 int ret; 298 299 /* Reset all digital outputs to "0" */ 300 outl(0x0, dev->iobase + APCI3501_DO_REG); 301 302 /* Default all analog outputs to 0V (bipolar) */ 303 outl(APCI3501_AO_CTRL_BIPOLAR, 304 dev->iobase + APCI3501_AO_CTRL_STATUS_REG); 305 val = APCI3501_AO_DATA_BIPOLAR | APCI3501_AO_DATA_VAL(0); 306 307 /* Set all analog output channels */ 308 for (chan = 0; chan < 8; chan++) { 309 ret = apci3501_wait_for_dac(dev); 310 if (ret) { 311 dev_warn(dev->class_dev, 312 "%s: DAC not-ready for channel %i\n", 313 __func__, chan); 314 } else { 315 outl(val | APCI3501_AO_DATA_CHAN(chan), 316 dev->iobase + APCI3501_AO_DATA_REG); 317 } 318 } 319 320 return 0; 321} 322 323static int apci3501_auto_attach(struct comedi_device *dev, 324 unsigned long context_unused) 325{ 326 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 327 struct apci3501_private *devpriv; 328 struct comedi_subdevice *s; 329 int ao_n_chan; 330 int ret; 331 332 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 333 if (!devpriv) 334 return -ENOMEM; 335 336 ret = comedi_pci_enable(dev); 337 if (ret) 338 return ret; 339 340 dev->iobase = pci_resource_start(pcidev, 1); 341 devpriv->i_IobaseAmcc = pci_resource_start(pcidev, 0); 342 343 ao_n_chan = apci3501_eeprom_get_ao_n_chan(dev); 344 345 if (pcidev->irq > 0) { 346 ret = request_irq(pcidev->irq, apci3501_interrupt, IRQF_SHARED, 347 dev->board_name, dev); 348 if (ret == 0) 349 dev->irq = pcidev->irq; 350 } 351 352 ret = comedi_alloc_subdevices(dev, 5); 353 if (ret) 354 return ret; 355 356 /* Initialize the analog output subdevice */ 357 s = &dev->subdevices[0]; 358 if (ao_n_chan) { 359 s->type = COMEDI_SUBD_AO; 360 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 361 s->n_chan = ao_n_chan; 362 s->maxdata = 0x3fff; 363 s->range_table = &apci3501_ao_range; 364 s->insn_write = apci3501_ao_insn_write; 365 s->insn_read = comedi_readback_insn_read; 366 367 ret = comedi_alloc_subdev_readback(s); 368 if (ret) 369 return ret; 370 } else { 371 s->type = COMEDI_SUBD_UNUSED; 372 } 373 374 /* Initialize the digital input subdevice */ 375 s = &dev->subdevices[1]; 376 s->type = COMEDI_SUBD_DI; 377 s->subdev_flags = SDF_READABLE; 378 s->n_chan = 2; 379 s->maxdata = 1; 380 s->range_table = &range_digital; 381 s->insn_bits = apci3501_di_insn_bits; 382 383 /* Initialize the digital output subdevice */ 384 s = &dev->subdevices[2]; 385 s->type = COMEDI_SUBD_DO; 386 s->subdev_flags = SDF_WRITEABLE; 387 s->n_chan = 2; 388 s->maxdata = 1; 389 s->range_table = &range_digital; 390 s->insn_bits = apci3501_do_insn_bits; 391 392 /* Initialize the timer/watchdog subdevice */ 393 s = &dev->subdevices[3]; 394 s->type = COMEDI_SUBD_TIMER; 395 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 396 s->n_chan = 1; 397 s->maxdata = 0; 398 s->len_chanlist = 1; 399 s->range_table = &range_digital; 400 s->insn_write = apci3501_write_insn_timer; 401 s->insn_read = apci3501_read_insn_timer; 402 s->insn_config = apci3501_config_insn_timer; 403 404 /* Initialize the eeprom subdevice */ 405 s = &dev->subdevices[4]; 406 s->type = COMEDI_SUBD_MEMORY; 407 s->subdev_flags = SDF_READABLE | SDF_INTERNAL; 408 s->n_chan = 256; 409 s->maxdata = 0xffff; 410 s->insn_read = apci3501_eeprom_insn_read; 411 412 apci3501_reset(dev); 413 return 0; 414} 415 416static void apci3501_detach(struct comedi_device *dev) 417{ 418 if (dev->iobase) 419 apci3501_reset(dev); 420 comedi_pci_detach(dev); 421} 422 423static struct comedi_driver apci3501_driver = { 424 .driver_name = "addi_apci_3501", 425 .module = THIS_MODULE, 426 .auto_attach = apci3501_auto_attach, 427 .detach = apci3501_detach, 428}; 429 430static int apci3501_pci_probe(struct pci_dev *dev, 431 const struct pci_device_id *id) 432{ 433 return comedi_pci_auto_config(dev, &apci3501_driver, id->driver_data); 434} 435 436static const struct pci_device_id apci3501_pci_table[] = { 437 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x3001) }, 438 { 0 } 439}; 440MODULE_DEVICE_TABLE(pci, apci3501_pci_table); 441 442static struct pci_driver apci3501_pci_driver = { 443 .name = "addi_apci_3501", 444 .id_table = apci3501_pci_table, 445 .probe = apci3501_pci_probe, 446 .remove = comedi_pci_auto_unconfig, 447}; 448module_comedi_pci_driver(apci3501_driver, apci3501_pci_driver); 449 450MODULE_DESCRIPTION("ADDI-DATA APCI-3501 Analog output board"); 451MODULE_AUTHOR("Comedi http://www.comedi.org"); 452MODULE_LICENSE("GPL"); 453