1/* 2 comedi/drivers/adv_pci1724.c 3 This is a driver for the Advantech PCI-1724U card. 4 5 Author: Frank Mori Hess <fmh6jj@gmail.com> 6 Copyright (C) 2013 GnuBIO Inc 7 8 COMEDI - Linux Control and Measurement Device Interface 9 Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20*/ 21 22/* 23 24Driver: adv_1724 25Description: Advantech PCI-1724U 26Author: Frank Mori Hess <fmh6jj@gmail.com> 27Status: works 28Updated: 2013-02-09 29Devices: [Advantech] PCI-1724U (adv_pci1724) 30 31Subdevice 0 is the analog output. 32Subdevice 1 is the offset calibration for the analog output. 33Subdevice 2 is the gain calibration for the analog output. 34 35The calibration offset and gains have quite a large effect 36on the analog output, so it is possible to adjust the analog output to 37have an output range significantly different from the board's 38nominal output ranges. For a calibrated +/- 10V range, the analog 39output's offset will be set somewhere near mid-range (0x2000) and its 40gain will be near maximum (0x3fff). 41 42There is really no difference between the board's documented 0-20mA 43versus 4-20mA output ranges. To pick one or the other is simply a matter 44of adjusting the offset and gain calibration until the board outputs in 45the desired range. 46 47Configuration options: 48 None 49 50Manual configuration of comedi devices is not supported by this driver; 51supported PCI devices are configured as comedi devices automatically. 52 53*/ 54 55#include <linux/module.h> 56#include <linux/delay.h> 57#include <linux/pci.h> 58 59#include "../comedidev.h" 60 61#define PCI_VENDOR_ID_ADVANTECH 0x13fe 62 63#define NUM_AO_CHANNELS 32 64 65/* register offsets */ 66enum board_registers { 67 DAC_CONTROL_REG = 0x0, 68 SYNC_OUTPUT_REG = 0x4, 69 EEPROM_CONTROL_REG = 0x8, 70 SYNC_OUTPUT_TRIGGER_REG = 0xc, 71 BOARD_ID_REG = 0x10 72}; 73 74/* bit definitions for registers */ 75enum dac_control_contents { 76 DAC_DATA_MASK = 0x3fff, 77 DAC_DESTINATION_MASK = 0xc000, 78 DAC_NORMAL_MODE = 0xc000, 79 DAC_OFFSET_MODE = 0x8000, 80 DAC_GAIN_MODE = 0x4000, 81 DAC_CHANNEL_SELECT_MASK = 0xf0000, 82 DAC_GROUP_SELECT_MASK = 0xf00000 83}; 84 85static uint32_t dac_data_bits(uint16_t dac_data) 86{ 87 return dac_data & DAC_DATA_MASK; 88} 89 90static uint32_t dac_channel_select_bits(unsigned channel) 91{ 92 return (channel << 16) & DAC_CHANNEL_SELECT_MASK; 93} 94 95static uint32_t dac_group_select_bits(unsigned group) 96{ 97 return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK; 98} 99 100static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel) 101{ 102 return dac_channel_select_bits(comedi_channel % 8) | 103 dac_group_select_bits(comedi_channel / 8); 104} 105 106enum sync_output_contents { 107 SYNC_MODE = 0x1, 108 DAC_BUSY = 0x2, /* dac state machine is not ready */ 109}; 110 111enum sync_output_trigger_contents { 112 SYNC_TRIGGER_BITS = 0x0 /* any value works */ 113}; 114 115enum board_id_contents { 116 BOARD_ID_MASK = 0xf 117}; 118 119static const struct comedi_lrange ao_ranges_1724 = { 120 4, { 121 BIP_RANGE(10), 122 RANGE_mA(0, 20), 123 RANGE_mA(4, 20), 124 RANGE_unitless(0, 1) 125 } 126}; 127 128/* this structure is for data unique to this hardware driver. */ 129struct adv_pci1724_private { 130 int ao_value[NUM_AO_CHANNELS]; 131 int offset_value[NUM_AO_CHANNELS]; 132 int gain_value[NUM_AO_CHANNELS]; 133}; 134 135static int wait_for_dac_idle(struct comedi_device *dev) 136{ 137 static const int timeout = 10000; 138 int i; 139 140 for (i = 0; i < timeout; ++i) { 141 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0) 142 break; 143 udelay(1); 144 } 145 if (i == timeout) { 146 dev_err(dev->class_dev, 147 "Timed out waiting for dac to become idle\n"); 148 return -EIO; 149 } 150 return 0; 151} 152 153static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel, 154 unsigned data) 155{ 156 int retval; 157 unsigned control_bits; 158 159 retval = wait_for_dac_idle(dev); 160 if (retval < 0) 161 return retval; 162 163 control_bits = mode; 164 control_bits |= dac_channel_and_group_select_bits(channel); 165 control_bits |= dac_data_bits(data); 166 outl(control_bits, dev->iobase + DAC_CONTROL_REG); 167 return 0; 168} 169 170static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 171 struct comedi_insn *insn, unsigned int *data) 172{ 173 struct adv_pci1724_private *devpriv = dev->private; 174 int channel = CR_CHAN(insn->chanspec); 175 int retval; 176 int i; 177 178 /* turn off synchronous mode */ 179 outl(0, dev->iobase + SYNC_OUTPUT_REG); 180 181 for (i = 0; i < insn->n; ++i) { 182 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]); 183 if (retval < 0) 184 return retval; 185 devpriv->ao_value[channel] = data[i]; 186 } 187 return insn->n; 188} 189 190static int ao_readback_insn(struct comedi_device *dev, 191 struct comedi_subdevice *s, 192 struct comedi_insn *insn, unsigned int *data) 193{ 194 struct adv_pci1724_private *devpriv = dev->private; 195 int channel = CR_CHAN(insn->chanspec); 196 int i; 197 198 if (devpriv->ao_value[channel] < 0) { 199 dev_err(dev->class_dev, 200 "Cannot read back channels which have not yet been written to\n"); 201 return -EIO; 202 } 203 for (i = 0; i < insn->n; i++) 204 data[i] = devpriv->ao_value[channel]; 205 206 return insn->n; 207} 208 209static int offset_write_insn(struct comedi_device *dev, 210 struct comedi_subdevice *s, 211 struct comedi_insn *insn, unsigned int *data) 212{ 213 struct adv_pci1724_private *devpriv = dev->private; 214 int channel = CR_CHAN(insn->chanspec); 215 int retval; 216 int i; 217 218 /* turn off synchronous mode */ 219 outl(0, dev->iobase + SYNC_OUTPUT_REG); 220 221 for (i = 0; i < insn->n; ++i) { 222 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]); 223 if (retval < 0) 224 return retval; 225 devpriv->offset_value[channel] = data[i]; 226 } 227 228 return insn->n; 229} 230 231static int offset_read_insn(struct comedi_device *dev, 232 struct comedi_subdevice *s, 233 struct comedi_insn *insn, unsigned int *data) 234{ 235 struct adv_pci1724_private *devpriv = dev->private; 236 unsigned int channel = CR_CHAN(insn->chanspec); 237 int i; 238 239 if (devpriv->offset_value[channel] < 0) { 240 dev_err(dev->class_dev, 241 "Cannot read back channels which have not yet been written to\n"); 242 return -EIO; 243 } 244 for (i = 0; i < insn->n; i++) 245 data[i] = devpriv->offset_value[channel]; 246 247 return insn->n; 248} 249 250static int gain_write_insn(struct comedi_device *dev, 251 struct comedi_subdevice *s, 252 struct comedi_insn *insn, unsigned int *data) 253{ 254 struct adv_pci1724_private *devpriv = dev->private; 255 int channel = CR_CHAN(insn->chanspec); 256 int retval; 257 int i; 258 259 /* turn off synchronous mode */ 260 outl(0, dev->iobase + SYNC_OUTPUT_REG); 261 262 for (i = 0; i < insn->n; ++i) { 263 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]); 264 if (retval < 0) 265 return retval; 266 devpriv->gain_value[channel] = data[i]; 267 } 268 269 return insn->n; 270} 271 272static int gain_read_insn(struct comedi_device *dev, 273 struct comedi_subdevice *s, struct comedi_insn *insn, 274 unsigned int *data) 275{ 276 struct adv_pci1724_private *devpriv = dev->private; 277 unsigned int channel = CR_CHAN(insn->chanspec); 278 int i; 279 280 if (devpriv->gain_value[channel] < 0) { 281 dev_err(dev->class_dev, 282 "Cannot read back channels which have not yet been written to\n"); 283 return -EIO; 284 } 285 for (i = 0; i < insn->n; i++) 286 data[i] = devpriv->gain_value[channel]; 287 288 return insn->n; 289} 290 291/* Allocate and initialize the subdevice structures. 292 */ 293static int setup_subdevices(struct comedi_device *dev) 294{ 295 struct comedi_subdevice *s; 296 int ret; 297 298 ret = comedi_alloc_subdevices(dev, 3); 299 if (ret) 300 return ret; 301 302 /* analog output subdevice */ 303 s = &dev->subdevices[0]; 304 s->type = COMEDI_SUBD_AO; 305 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND; 306 s->n_chan = NUM_AO_CHANNELS; 307 s->maxdata = 0x3fff; 308 s->range_table = &ao_ranges_1724; 309 s->insn_read = ao_readback_insn; 310 s->insn_write = ao_winsn; 311 312 /* offset calibration */ 313 s = &dev->subdevices[1]; 314 s->type = COMEDI_SUBD_CALIB; 315 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; 316 s->n_chan = NUM_AO_CHANNELS; 317 s->insn_read = offset_read_insn; 318 s->insn_write = offset_write_insn; 319 s->maxdata = 0x3fff; 320 321 /* gain calibration */ 322 s = &dev->subdevices[2]; 323 s->type = COMEDI_SUBD_CALIB; 324 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; 325 s->n_chan = NUM_AO_CHANNELS; 326 s->insn_read = gain_read_insn; 327 s->insn_write = gain_write_insn; 328 s->maxdata = 0x3fff; 329 330 return 0; 331} 332 333static int adv_pci1724_auto_attach(struct comedi_device *dev, 334 unsigned long context_unused) 335{ 336 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 337 struct adv_pci1724_private *devpriv; 338 int i; 339 int retval; 340 unsigned int board_id; 341 342 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 343 if (!devpriv) 344 return -ENOMEM; 345 346 /* init software copies of output values to indicate we don't know 347 * what the output value is since it has never been written. */ 348 for (i = 0; i < NUM_AO_CHANNELS; ++i) { 349 devpriv->ao_value[i] = -1; 350 devpriv->offset_value[i] = -1; 351 devpriv->gain_value[i] = -1; 352 } 353 354 retval = comedi_pci_enable(dev); 355 if (retval) 356 return retval; 357 358 dev->iobase = pci_resource_start(pcidev, 2); 359 board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK; 360 dev_info(dev->class_dev, "board id: %d\n", board_id); 361 362 retval = setup_subdevices(dev); 363 if (retval < 0) 364 return retval; 365 366 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n", 367 dev->board_name, pci_name(pcidev), board_id); 368 return 0; 369} 370 371static struct comedi_driver adv_pci1724_driver = { 372 .driver_name = "adv_pci1724", 373 .module = THIS_MODULE, 374 .auto_attach = adv_pci1724_auto_attach, 375 .detach = comedi_pci_detach, 376}; 377 378static int adv_pci1724_pci_probe(struct pci_dev *dev, 379 const struct pci_device_id *id) 380{ 381 return comedi_pci_auto_config(dev, &adv_pci1724_driver, 382 id->driver_data); 383} 384 385static const struct pci_device_id adv_pci1724_pci_table[] = { 386 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) }, 387 { 0 } 388}; 389MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table); 390 391static struct pci_driver adv_pci1724_pci_driver = { 392 .name = "adv_pci1724", 393 .id_table = adv_pci1724_pci_table, 394 .probe = adv_pci1724_pci_probe, 395 .remove = comedi_pci_auto_unconfig, 396}; 397 398module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver); 399 400MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>"); 401MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver"); 402MODULE_LICENSE("GPL"); 403