staging: comedi: adv_pci1710: separate out PCI-1720 support as a new driver

The PCI-1710 series boards are multifunction data acquisition boards with
analog inputs and outputs, digital inputs and outputs, and counter/timer
functions.

The PCI-1720 is a simple 4 channel analog output board. It also uses a
unique register map.

Separate out the PCI-1720 support as a new driver, adv_pci1720, to ease
maintainability.

Fix some issues with the PCI-1720 support in the new driver:
  1) the registers are all 8-bit
  2) remove the analog output "reset" when the driver attaches/detaches
  3) disable "synchronized output" to simplify the analog outputs
  4) remove the need for the private data
  5) add support for the BoardID register to allow multiple cards

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
H Hartley Sweeten 2015-10-30 11:10:05 -07:00 committed by Greg Kroah-Hartman
parent 2aadecb60a
commit b2cb068604
4 changed files with 212 additions and 105 deletions

View File

@ -737,15 +737,23 @@ config COMEDI_ADL_PCI9118
called adl_pci9118.
config COMEDI_ADV_PCI1710
tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
tristate "Advantech PCI-171x and PCI-1731 support"
select COMEDI_8254
---help---
Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
PCI-1713, PCI-1720 and PCI-1731
PCI-1713 and PCI-1731
To compile this driver as a module, choose M here: the module will be
called adv_pci1710.
config COMEDI_ADV_PCI1720
tristate "Advantech PCI-1720 support"
---help---
Enable support for Advantech PCI-1720 Analog Output board.
To compile this driver as a module, choose M here: the module will be
called adv_pci1720.
config COMEDI_ADV_PCI1723
tristate "Advantech PCI-1723 support"
---help---

View File

@ -78,6 +78,7 @@ obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o
obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
obj-$(CONFIG_COMEDI_ADL_PCI9118) += adl_pci9118.o
obj-$(CONFIG_COMEDI_ADV_PCI1710) += adv_pci1710.o
obj-$(CONFIG_COMEDI_ADV_PCI1720) += adv_pci1720.o
obj-$(CONFIG_COMEDI_ADV_PCI1723) += adv_pci1723.o
obj-$(CONFIG_COMEDI_ADV_PCI1724) += adv_pci1724.o
obj-$(CONFIG_COMEDI_ADV_PCI_DIO) += adv_pci_dio.o

View File

@ -11,8 +11,9 @@
* Driver: adv_pci1710
* Description: Comedi driver for Advantech PCI-1710 series boards
* Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
* PCI-1713, PCI-1720, PCI-1731
* PCI-1713, PCI-1731
* Author: Michal Dobes <dobes@tesnet.cz>
* Updated: Fri, 29 Oct 2015 17:19:35 -0700
* Status: works
*
* Configuration options: not applicable, uses PCI auto config
@ -62,16 +63,6 @@
#define PCI171X_DO_REG 0x10 /* W: digital outputs */
#define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */
/*
* PCI-1720 only has analog outputs and has a different
* register map (dev->iobase)
*/
#define PCI1720_DA_REG(x) (0x00 + ((x) * 2)) /* W: D/A registers */
#define PCI1720_RANGE_REG 0x08 /* R/W: D/A range register */
#define PCI1720_SYNC_REG 0x09 /* W: D/A synchronized output */
#define PCI1720_SYNC_CTRL_REG 0x0f /* R/W: D/A synchronized control */
#define PCI1720_SYNC_CTRL_SC0 BIT(0) /* set synchronous output mode */
static const struct comedi_lrange range_pci1710_3 = {
9, {
BIP_RANGE(5),
@ -122,15 +113,6 @@ static const struct comedi_lrange range_pci17x1 = {
static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
static const struct comedi_lrange pci1720_ao_range = {
4, {
UNI_RANGE(5),
UNI_RANGE(10),
BIP_RANGE(5),
BIP_RANGE(10)
}
};
static const struct comedi_lrange pci171x_ao_range = {
2, {
UNI_RANGE(5),
@ -143,7 +125,6 @@ enum pci1710_boardid {
BOARD_PCI1710HG,
BOARD_PCI1711,
BOARD_PCI1713,
BOARD_PCI1720,
BOARD_PCI1731,
};
@ -153,7 +134,6 @@ struct boardtype {
const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
const char *rangecode_ai; /* range codes for programming */
unsigned int is_pci1713:1;
unsigned int is_pci1720:1;
unsigned int has_irq:1;
unsigned int has_large_fifo:1; /* 4K or 1K FIFO */
unsigned int has_diff_ai:1;
@ -207,11 +187,6 @@ static const struct boardtype boardtypes[] = {
.has_large_fifo = 1,
.has_diff_ai = 1,
},
[BOARD_PCI1720] = {
.name = "pci1720",
.is_pci1720 = 1,
.has_ao = 1,
},
[BOARD_PCI1731] = {
.name = "pci1731",
.n_aichan = 16,
@ -465,36 +440,6 @@ static int pci171x_do_insn_bits(struct comedi_device *dev,
return insn->n;
}
static int pci1720_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct pci1710_private *devpriv = dev->private;
unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int range = CR_RANGE(insn->chanspec);
unsigned int val;
int i;
val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
val |= (range << (chan << 1));
if (val != devpriv->da_ranges) {
outb(val, dev->iobase + PCI1720_RANGE_REG);
devpriv->da_ranges = val;
}
val = s->readback[chan];
for (i = 0; i < insn->n; i++) {
val = data[i];
outw(val, dev->iobase + PCI1720_DA_REG(chan));
outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
}
s->readback[chan] = val;
return insn->n;
}
static int pci171x_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
@ -790,7 +735,7 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
return insn->n;
}
static int pci171x_reset(struct comedi_device *dev)
static int pci1710_reset(struct comedi_device *dev)
{
const struct boardtype *board = dev->board_ptr;
struct pci1710_private *devpriv = dev->private;
@ -815,33 +760,6 @@ static int pci171x_reset(struct comedi_device *dev)
return 0;
}
static int pci1720_reset(struct comedi_device *dev)
{
struct pci1710_private *devpriv = dev->private;
/* set synchronous output mode */
outb(PCI1720_SYNC_CTRL_SC0, dev->iobase + PCI1720_SYNC_CTRL_REG);
devpriv->da_ranges = 0xAA;
/* set all ranges to +/-5V and outputs to 0V */
outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE_REG);
outw(0x0800, dev->iobase + PCI1720_DA_REG(0));
outw(0x0800, dev->iobase + PCI1720_DA_REG(1));
outw(0x0800, dev->iobase + PCI1720_DA_REG(2));
outw(0x0800, dev->iobase + PCI1720_DA_REG(3));
outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
return 0;
}
static int pci1710_reset(struct comedi_device *dev)
{
const struct boardtype *board = dev->board_ptr;
if (board->is_pci1720)
return pci1720_reset(dev);
return pci171x_reset(dev);
}
static int pci1710_auto_attach(struct comedi_device *dev,
unsigned long context)
{
@ -922,29 +840,15 @@ static int pci1710_auto_attach(struct comedi_device *dev,
s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = 2;
s->maxdata = 0x0fff;
if (board->is_pci1720) {
s->n_chan = 4;
s->range_table = &pci1720_ao_range;
s->insn_write = pci1720_ao_insn_write;
} else {
s->n_chan = 2;
s->range_table = &pci171x_ao_range;
s->insn_write = pci171x_ao_insn_write;
}
s->range_table = &pci171x_ao_range;
s->insn_write = pci171x_ao_insn_write;
ret = comedi_alloc_subdev_readback(s);
if (ret)
return ret;
/* initialize the readback values to match the board reset */
if (board->is_pci1720) {
int i;
for (i = 0; i < s->n_chan; i++)
s->readback[i] = 0x0800;
}
subdev++;
}
@ -1063,7 +967,6 @@ static const struct pci_device_id adv_pci1710_pci_table[] = {
},
{ PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
{ PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
{ PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
{ PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
{ 0 }
};

View File

@ -0,0 +1,195 @@
/*
* COMEDI driver for Advantech PCI-1720U
* Copyright (c) 2015 H Hartley Sweeten <hsweeten@visionengravers.com>
*
* Separated from the adv_pci1710 driver written by:
* Michal Dobes <dobes@tesnet.cz>
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 2000 David A. Schleef <ds@schleef.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Driver: adv_pci1720
* Description: 4-channel Isolated D/A Output board
* Devices: [Advantech] PCI-7120U (adv_pci1720)
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
* Updated: Fri, 29 Oct 2015 17:19:35 -0700
* Status: untested
*
* Configuration options: not applicable, uses PCI auto config
*
* The PCI-1720 has 4 isolated 12-bit analog output channels with multiple
* output ranges. It also has a BoardID switch to allow differentiating
* multiple boards in the system.
*
* The analog outputs can operate in two modes, immediate and synchronized.
* This driver currently does not support the synchronized output mode.
*
* Jumpers JP1 to JP4 are used to set the current sink ranges for each
* analog output channel. In order to use the current sink ranges, the
* unipolar 5V range must be used. The voltage output and sink output for
* each channel is available on the connector as separate pins.
*
* Jumper JP5 controls the "hot" reset state of the analog outputs.
* Depending on its setting, the analog outputs will either keep the
* last settings and output values or reset to the default state after
* a "hot" reset. The default state for all channels is uniploar 5V range
* and all the output values are 0V. To allow this feature to work, the
* analog outputs are not "reset" when the driver attaches.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include "../comedi_pci.h"
/*
* PCI BAR2 Register map (dev->iobase)
*/
#define PCI1720_AO_LSB_REG(x) (0x00 + ((x) * 2))
#define PCI1720_AO_MSB_REG(x) (0x01 + ((x) * 2))
#define PCI1720_AO_RANGE_REG 0x08
#define PCI1720_AO_RANGE(c, r) (((r) & 0x3) << ((c) * 2))
#define PCI1720_AO_RANGE_MASK(c) PCI1720_AO_RANGE((c), 0x3)
#define PCI1720_SYNC_REG 0x09
#define PCI1720_SYNC_CTRL_REG 0x0f
#define PCI1720_SYNC_CTRL_SC0 BIT(0)
#define PCI1720_BOARDID_REG 0x14
static const struct comedi_lrange pci1720_ao_range = {
4, {
UNI_RANGE(5),
UNI_RANGE(10),
BIP_RANGE(5),
BIP_RANGE(10)
}
};
static int pci1720_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int range = CR_RANGE(insn->chanspec);
unsigned int val;
int i;
/* set the channel range and polarity */
val = inb(dev->iobase + PCI1720_AO_RANGE_REG);
val &= ~PCI1720_AO_RANGE_MASK(chan);
val |= PCI1720_AO_RANGE(chan, range);
outb(val, dev->iobase + PCI1720_AO_RANGE_REG);
val = s->readback[chan];
for (i = 0; i < insn->n; i++) {
val = data[i];
outb(val & 0xff, dev->iobase + PCI1720_AO_LSB_REG(chan));
outb((val >> 8) & 0xff, dev->iobase + PCI1720_AO_MSB_REG(chan));
/* conversion time is 2us (500 kHz throughput) */
usleep_range(2, 100);
}
s->readback[chan] = val;
return insn->n;
}
static int pci1720_di_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
data[1] = inb(dev->iobase + PCI1720_BOARDID_REG);
return insn->n;
}
static int pci1720_auto_attach(struct comedi_device *dev,
unsigned long context)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
struct comedi_subdevice *s;
int ret;
ret = comedi_pci_enable(dev);
if (ret)
return ret;
dev->iobase = pci_resource_start(pcidev, 2);
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
/* Analog Output subdevice */
s = &dev->subdevices[0];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 4;
s->maxdata = 0x0fff;
s->range_table = &pci1720_ao_range;
s->insn_write = pci1720_ao_insn_write;
ret = comedi_alloc_subdev_readback(s);
if (ret)
return ret;
/* Digital Input subdevice (BoardID SW1) */
s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 4;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = pci1720_di_insn_bits;
/* disable synchronized output, channels update when written */
outb(0, dev->iobase + PCI1720_SYNC_CTRL_REG);
return 0;
}
static struct comedi_driver adv_pci1720_driver = {
.driver_name = "adv_pci1720",
.module = THIS_MODULE,
.auto_attach = pci1720_auto_attach,
.detach = comedi_pci_detach,
};
static int adv_pci1720_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
return comedi_pci_auto_config(dev, &adv_pci1720_driver,
id->driver_data);
}
static const struct pci_device_id adv_pci1720_pci_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1720) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, adv_pci1720_pci_table);
static struct pci_driver adv_pci1720_pci_driver = {
.name = "adv_pci1720",
.id_table = adv_pci1720_pci_table,
.probe = adv_pci1720_pci_probe,
.remove = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(adv_pci1720_driver, adv_pci1720_pci_driver);
MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("Comedi driver for Advantech PCI-1720 Analog Output board");
MODULE_LICENSE("GPL");