/*
comedi/drivers/amplc_dio200.c
- Driver for Amplicon PC272E and PCI272 DIO boards.
+ Driver for various Amplicon 200 series DIO boards.
(Support for other boards in Amplicon 200 series may be added at
a later date, e.g. PCI215.)
- Copyright (C) 2005 MEV Ltd. <http://www.mev.co.uk/>
+ Copyright (C) 2005-2012 MEV Ltd. <http://www.mev.co.uk/>
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
Description: Amplicon 200 Series Digital I/O
Author: Ian Abbott <abbotti@mev.co.uk>
Devices: [Amplicon] PC212E (pc212e), PC214E (pc214e), PC215E (pc215e),
- PCI215 (pci215), PC218E (pc218e), PC272E (pc272e), PCI272 (pci272)
-Updated: Mon, 05 Nov 2007 14:04:04 +0000
+ PCI215 (pci215 or amplc_dio200), PCIe215 (pcie215 or amplc_dio200),
+ PC218E (pc218e), PCIe236 (pcie236 or amplc_dio200), PC272E (pc272e),
+ PCI272 (pci272 or amplc_dio200), PCIe296 (pcie296 or amplc_dio200)
+Updated: Wed, 16 May 2012 13:57:58 +0100
Status: works
Configuration options - PC212E, PC214E, PC215E, PC218E, PC272E:
[0] - I/O port base address
[1] - IRQ (optional, but commands won't work without it)
-Configuration options - PCI215, PCI272:
+Configuration options - PCI215, PCIe215, PCIe236, PCI272, PCIe296:
[0] - PCI bus of device (optional)
[1] - PCI slot of device (optional)
If bus/slot is not specified, the first available PCI device will
SUBDEVICES
- PC218E PC212E PC215E/PCI215
+ PC212E PC214E PC215E/PCI215
------------- ------------- -------------
- Subdevices 7 6 5
- 0 CTR-X1 PPI-X PPI-X
- 1 CTR-X2 CTR-Y1 PPI-Y
- 2 CTR-Y1 CTR-Y2 CTR-Z1
- 3 CTR-Y2 CTR-Z1 CTR-Z2
- 4 CTR-Z1 CTR-Z2 INTERRUPT
- 5 CTR-Z2 INTERRUPT
- 6 INTERRUPT
-
- PC214E PC272E/PCI272
+ Subdevices 6 4 5
+ 0 PPI-X PPI-X PPI-X
+ 1 CTR-Y1 PPI-Y PPI-Y
+ 2 CTR-Y2 CTR-Z1* CTR-Z1
+ 3 CTR-Z1 INTERRUPT* CTR-Z2
+ 4 CTR-Z2 INTERRUPT
+ 5 INTERRUPT
+
+ PCIe215 PC218E PCIe236
+ ------------- ------------- -------------
+ Subdevices 8 7 8
+ 0 PPI-X CTR-X1 PPI-X
+ 1 UNUSED CTR-X2 UNUSED
+ 2 PPI-Y CTR-Y1 UNUSED
+ 3 UNUSED CTR-Y2 UNUSED
+ 4 CTR-Z1 CTR-Z1 CTR-Z1
+ 5 CTR-Z2 CTR-Z2 CTR-Z2
+ 6 TIMER INTERRUPT TIMER
+ 7 INTERRUPT INTERRUPT
+
+ PC272E/PCI272 PCIe296
------------- -------------
- Subdevices 4 4
- 0 PPI-X PPI-X
- 1 PPI-Y PPI-Y
- 2 CTR-Z1* PPI-Z
- 3 INTERRUPT* INTERRUPT
+ Subdevices 4 8
+ 0 PPI-X PPI-X1
+ 1 PPI-Y PPI-X2
+ 2 PPI-Z PPI-Y1
+ 3 INTERRUPT PPI-Y2
+ 4 CTR-Z1
+ 5 CTR-Z2
+ 6 TIMER
+ 7 INTERRUPT
Each PPI is a 8255 chip providing 24 DIO channels. The DIO channels
are configurable as inputs or outputs in four groups:
3. The counter subdevices are connected in a ring, so the highest
counter subdevice precedes the lowest.
+The 'TIMER' subdevice is a free-running 32-bit timer subdevice.
+
The 'INTERRUPT' subdevice pretends to be a digital input subdevice. The
digital inputs come from the interrupt status register. The number of
channels matches the number of interrupt sources. The PC214E does not
INTERRUPT SOURCES
- PC218E PC212E PC215E/PCI215
+ PC212E PC214E PC215E/PCI215
+ ------------- ------------- -------------
+ Sources 6 1 6
+ 0 PPI-X-C0 JUMPER-J5 PPI-X-C0
+ 1 PPI-X-C3 PPI-X-C3
+ 2 CTR-Y1-OUT PPI-Y-C0
+ 3 CTR-Y2-OUT PPI-Y-C3
+ 4 CTR-Z1-OUT CTR-Z1-OUT
+ 5 CTR-Z2-OUT CTR-Z2-OUT
+
+ PCIe215 PC218E PCIe236
------------- ------------- -------------
Sources 6 6 6
- 0 CTR-X1-OUT PPI-X-C0 PPI-X-C0
- 1 CTR-X2-OUT PPI-X-C3 PPI-X-C3
- 2 CTR-Y1-OUT CTR-Y1-OUT PPI-Y-C0
- 3 CTR-Y2-OUT CTR-Y2-OUT PPI-Y-C3
+ 0 PPI-X-C0 CTR-X1-OUT PPI-X-C0
+ 1 PPI-X-C3 CTR-X2-OUT PPI-X-C3
+ 2 PPI-Y-C0 CTR-Y1-OUT unused
+ 3 PPI-Y-C3 CTR-Y2-OUT unused
4 CTR-Z1-OUT CTR-Z1-OUT CTR-Z1-OUT
5 CTR-Z2-OUT CTR-Z2-OUT CTR-Z2-OUT
- PC214E PC272E/PCI272
+ PC272E/PCI272 PCIe296
------------- -------------
- Sources 1 6
- 0 JUMPER-J5 PPI-X-C0
- 1 PPI-X-C3
- 2 PPI-Y-C0
- 3 PPI-Y-C3
- 4 PPI-Z-C0
- 5 PPI-Z-C3
+ Sources 6 6
+ 0 PPI-X-C0 PPI-X1-C0
+ 1 PPI-X-C3 PPI-X1-C3
+ 2 PPI-Y-C0 PPI-Y1-C0
+ 3 PPI-Y-C3 PPI-Y2-C3
+ 4 PPI-Z-C0 CTR-Z1-OUT
+ 5 PPI-Z-C3 CTR-Z2-OUT
When an interrupt source is enabled in the interrupt source enable
register, a rising edge on the source signal latches the corresponding
/* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
#define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
#define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
+#define PCI_DEVICE_ID_AMPLICON_PCIE236 0x0011
+#define PCI_DEVICE_ID_AMPLICON_PCIE215 0x0012
+#define PCI_DEVICE_ID_AMPLICON_PCIE296 0x0014
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+/* 8255 control register bits */
+#define CR_C_LO_IO 0x01
+#define CR_B_IO 0x02
+#define CR_B_MODE 0x04
+#define CR_C_HI_IO 0x08
+#define CR_A_IO 0x10
+#define CR_A_MODE(a) ((a)<<5)
+#define CR_CW 0x80
/* 200 series registers */
#define DIO200_IO_SIZE 0x20
+#define DIO200_PCIE_IO_SIZE 0x4000
#define DIO200_XCLK_SCE 0x18 /* Group X clock selection register */
#define DIO200_YCLK_SCE 0x19 /* Group Y clock selection register */
#define DIO200_ZCLK_SCE 0x1a /* Group Z clock selection register */
#define DIO200_YGAT_SCE 0x1c /* Group Y gate selection register */
#define DIO200_ZGAT_SCE 0x1d /* Group Z gate selection register */
#define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */
+/* Extra registers for new PCIe boards */
+#define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */
+#define DIO200_VERSION 0x24 /* Hardware version */
/*
* Macros for constructing value for DIO_200_?CLK_SCE and
*
* 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
* 'chan' is the channel: 0, 1 or 2.
- * 'source' is the signal source: 0 to 7.
+ * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
*/
-#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
-#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
+#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | \
+ (((source) & 030) << 3) | ((source) & 007))
+#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | \
+ (((source) & 030) << 3) | ((source) & 007))
/*
* Periods of the internal clock sources in nanoseconds.
*/
-static const unsigned clock_period[8] = {
+static const unsigned clock_period[32] = {
0, /* dedicated clock input/output pin */
100, /* 10 MHz */
1000, /* 1 MHz */
100000, /* 10 kHz */
1000000, /* 1 kHz */
0, /* OUT N-1 */
- 0 /* group clock input pin */
+ 0, /* group clock input pin */
+ 0, /* HIGH (VCC) (enhanced) */
+ 0, /* LOW (GND) (enhanced) */
+ 0, /* pattern present (enhanced) */
+ 50, /* 20 MHz (enhanced) */
+ /* remaining clock sources reserved (enhanced) */
+};
+
+/*
+ * Register region.
+ */
+enum dio200_regtype { no_regtype, io_regtype, mmio_regtype };
+struct dio200_region {
+ union {
+ unsigned long iobase; /* I/O base address */
+ unsigned char __iomem *membase; /* Mapped MMIO base address */
+ } u;
+ unsigned char regtype;
+ unsigned char regshift;
};
/*
enum dio200_model {
pc212e_model,
pc214e_model,
- pc215e_model, pci215_model,
+ pc215e_model, pci215_model, pcie215_model,
pc218e_model,
- pc272e_model, pci272_model
+ pcie236_model,
+ pc272e_model, pci272_model,
+ pcie296_model,
+ anypci_model
};
enum dio200_layout {
pc214_layout,
pc215_layout,
pc218_layout,
- pc272_layout
+ pc272_layout,
+#ifdef CONFIG_COMEDI_PCI
+ pcie215_layout,
+ pcie236_layout,
+ pcie296_layout,
+#endif
+ num_layouts
};
typedef struct dio200_board_struct {
const char *name;
+ unsigned short devid;
enum dio200_bustype bustype;
enum dio200_model model;
enum dio200_layout layout;
+ unsigned char mainbar;
+ unsigned char mainshift;
+ unsigned int mainsize;
} dio200_board;
static const dio200_board dio200_boards[] = {
bustype: isa_bustype,
model: pc212e_model,
layout: pc212_layout,
+ mainsize: DIO200_IO_SIZE,
},
{
name: "pc214e",
bustype: isa_bustype,
model: pc214e_model,
layout: pc214_layout,
+ mainsize: DIO200_IO_SIZE,
},
{
name: "pc215e",
bustype: isa_bustype,
model: pc215e_model,
layout: pc215_layout,
+ mainsize: DIO200_IO_SIZE,
},
#ifdef CONFIG_COMEDI_PCI
{
name: "pci215",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI215,
bustype: pci_bustype,
model: pci215_model,
layout: pc215_layout,
+ mainsize: DIO200_IO_SIZE,
+ mainbar: 2,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pcie215",
+ devid: PCI_DEVICE_ID_AMPLICON_PCIE215,
+ bustype: pci_bustype,
+ model: pcie215_model,
+ layout: pcie215_layout,
+ mainsize: DIO200_PCIE_IO_SIZE,
+ mainbar: 1,
+ mainshift: 3,
},
#endif
{
bustype: isa_bustype,
model: pc218e_model,
layout: pc218_layout,
+ mainsize: DIO200_IO_SIZE,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pcie236",
+ devid: PCI_DEVICE_ID_AMPLICON_PCIE236,
+ bustype: pci_bustype,
+ model: pcie236_model,
+ layout: pcie236_layout,
+ mainsize: DIO200_PCIE_IO_SIZE,
+ mainbar: 1,
+ mainshift: 3,
},
+#endif
{
name: "pc272e",
bustype: isa_bustype,
model: pc272e_model,
layout: pc272_layout,
+ mainsize: DIO200_IO_SIZE,
},
#ifdef CONFIG_COMEDI_PCI
{
name: "pci272",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI272,
bustype: pci_bustype,
model: pci272_model,
layout: pc272_layout,
+ mainsize: DIO200_IO_SIZE,
+ mainbar: 2,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pcie296",
+ devid: PCI_DEVICE_ID_AMPLICON_PCIE296,
+ bustype: pci_bustype,
+ model: pcie296_model,
+ layout: pcie296_layout,
+ mainsize: DIO200_PCIE_IO_SIZE,
+ mainbar: 1,
+ mainshift: 3,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: DIO200_DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ bustype: pci_bustype,
+ model: anypci_model, /* wildcard */
},
#endif
};
* layout.
*/
-enum dio200_sdtype { sd_none, sd_intr, sd_8255, sd_8254 };
+enum dio200_sdtype { sd_none, sd_intr, sd_8255, sd_8254, sd_timer };
-#define DIO200_MAX_SUBDEVS 7
+#define DIO200_MAX_SUBDEVS 8
#define DIO200_MAX_ISNS 6
typedef struct dio200_layout_struct {
unsigned char sdinfo[DIO200_MAX_SUBDEVS]; /* depends on sdtype */
char has_int_sce; /* has interrupt enable/status register */
char has_clk_gat_sce; /* has clock/gate selection registers */
+ char has_enhancements; /* has enhanced features */
} dio200_layout;
static const dio200_layout dio200_layouts[] = {
0x3F},
has_int_sce:1,
has_clk_gat_sce:1,
+ has_enhancements:0,
},
[pc214_layout] = {
n_subdevs:4,
sdinfo: {0x00, 0x08, 0x10, 0x01},
has_int_sce:0,
has_clk_gat_sce:0,
+ has_enhancements:0,
},
[pc215_layout] = {
n_subdevs:5,
sdinfo: {0x00, 0x08, 0x10, 0x14, 0x3F},
has_int_sce:1,
has_clk_gat_sce:1,
+ has_enhancements:0,
},
[pc218_layout] = {
n_subdevs:7,
0x3F},
has_int_sce:1,
has_clk_gat_sce:1,
+ has_enhancements:0,
},
[pc272_layout] = {
n_subdevs:4,
sdinfo: {0x00, 0x08, 0x10, 0x3F},
has_int_sce:1,
has_clk_gat_sce:0,
+ has_enhancements:0,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ [pcie215_layout] = {
+ n_subdevs:8,
+ sdtype: {sd_8255, sd_none, sd_8255, sd_none, sd_8254, sd_8254,
+ sd_timer, sd_intr},
+ sdinfo: {0x00, 0x00, 0x08, 0x00, 0x10, 0x14, 0x00, 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ has_enhancements:1,
+ },
+ [pcie236_layout] = {
+ n_subdevs:8,
+ sdtype: {sd_8255, sd_none, sd_none, sd_none, sd_8254, sd_8254,
+ sd_timer, sd_intr},
+ sdinfo: {0x00, 0x00, 0x00, 0x00, 0x10, 0x14, 0x00, 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ has_enhancements:1,
},
+ [pcie296_layout] = {
+ n_subdevs:8,
+ sdtype: {sd_8255, sd_8255, sd_8255, sd_8255, sd_8254, sd_8254,
+ sd_timer, sd_intr},
+ sdinfo: {0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x00, 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ has_enhancements:1,
+ },
+#endif
};
/*
#ifdef CONFIG_COMEDI_PCI
static DEFINE_PCI_DEVICE_TABLE(dio200_pci_table) = {
{PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI215,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci215_model},
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI272,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci272_model},
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCIE236,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCIE215,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCIE296,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0}
};
#ifdef CONFIG_COMEDI_PCI
struct pci_dev *pci_dev; /* PCI device */
#endif
+ struct dio200_region io; /* Register region */
int intr_sd;
} dio200_private;
#define devpriv ((dio200_private *)dev->private)
typedef struct {
- unsigned long iobase; /* Counter base address */
- unsigned long clk_sce_iobase; /* CLK_SCE base address */
- unsigned long gat_sce_iobase; /* GAT_SCE base address */
- int which; /* Bit 5 of CLK_SCE or GAT_SCE */
- int has_clk_gat_sce;
- unsigned clock_src[3]; /* Current clock sources */
- unsigned gate_src[3]; /* Current gate sources */
+ unsigned int ofs; /* Counter base offset */
+ unsigned int clk_sce_ofs; /* CLK_SCE base offset */
+ unsigned int gat_sce_ofs; /* GAT_SCE base offset */
+ int which; /* Bit 5 of CLK_SCE or GAT_SCE */
+ unsigned int clock_src[3]; /* Current clock sources */
+ unsigned int gate_src[3]; /* Current gate sources */
+ spinlock_t spinlock;
} dio200_subdev_8254;
typedef struct {
- unsigned long iobase;
+ unsigned int ofs; /* DIO base offset */
+} dio200_subdev_8255;
+
+typedef struct {
+ unsigned int ofs;
spinlock_t spinlock;
int active;
- int has_int_sce;
unsigned int valid_isns;
unsigned int enabled_isns;
unsigned int stopcount;
num_names:sizeof(dio200_boards) / sizeof(dio200_board),
};
+#ifdef CONFIG_COMEDI_PCI
COMEDI_PCI_INITCLEANUP(driver_amplc_dio200, dio200_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_amplc_dio200);
+#endif
+
+/*
+ * Read 8-bit register.
+ */
+static unsigned char dio200_read8(comedi_device * dev, unsigned int offset)
+{
+ offset <<= devpriv->io.regshift;
+ if (devpriv->io.regtype == io_regtype)
+ return inb(devpriv->io.u.iobase + offset);
+ else
+ return readb(devpriv->io.u.membase + offset);
+}
+
+/*
+ * Write 8-bit register.
+ */
+static void dio200_write8(comedi_device * dev, unsigned int offset,
+ unsigned char val)
+{
+ offset <<= devpriv->io.regshift;
+ if (devpriv->io.regtype == io_regtype)
+ outb(val, devpriv->io.u.iobase + offset);
+ else
+ writeb(val, devpriv->io.u.membase + offset);
+}
/*
* This function looks for a PCI device matching the requested board name,
struct pci_dev **pci_dev_p)
{
struct pci_dev *pci_dev = NULL;
- const struct pci_device_id *pci_id;
*pci_dev_p = NULL;
- /* Look for PCI table entry for this model. */
- for (pci_id = dio200_pci_table; pci_id->vendor != 0; pci_id++) {
- if (pci_id->driver_data == thisboard->model)
- break;
- }
- if (pci_id->vendor == 0) {
- printk(KERN_ERR
- "comedi%d: %s: BUG! cannot determine board type!\n",
- dev->minor, DIO200_DRIVER_NAME);
- return -EINVAL;
- }
-
/* Look for matching PCI device. */
- for (pci_dev = pci_get_device(pci_id->vendor, pci_id->device, NULL);
+ for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
pci_dev != NULL;
- pci_dev = pci_get_device(pci_id->vendor,
- pci_id->device, pci_dev)) {
+ pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
+ PCI_ANY_ID, pci_dev)) {
/* If bus/slot specified, check them. */
if (bus || slot) {
if (bus != pci_dev->bus->number
|| slot != PCI_SLOT(pci_dev->devfn))
continue;
}
-#if 0
- if (pci_id->subvendor != PCI_ANY_ID) {
- if (pci_dev->subsystem_vendor != pci_id->subvendor)
+ if (thisboard->model == anypci_model) {
+ /* Match any supported model. */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dio200_boards); i++) {
+ if (dio200_boards[i].bustype != pci_bustype)
+ continue;
+ if (pci_dev->device == dio200_boards[i].devid) {
+ /* Change board_ptr to matched board. */
+ dev->board_ptr = &dio200_boards[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(dio200_boards))
continue;
- }
- if (pci_id->subdevice != PCI_ANY_ID) {
- if (pci_dev->subsystem_device != pci_id->subdevice)
+ } else {
+ /* Match specific model name. */
+ if (pci_dev->device != thisboard->devid)
continue;
}
-#endif
- if (((pci_dev->class ^ pci_id->class) & pci_id->class_mask) !=
- 0)
- continue;
/* Found a match. */
*pci_dev_p = pci_dev;
{
dio200_subdev_intr *subpriv = s->private;
- if (subpriv->has_int_sce) {
+ if (thislayout->has_int_sce) {
/* Just read the interrupt status register. */
- data[1] = inb(subpriv->iobase) & subpriv->valid_isns;
+ data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
} else {
/* No interrupt status register. */
data[0] = 0;
subpriv->active = 0;
subpriv->enabled_isns = 0;
- if (subpriv->has_int_sce) {
- outb(0, subpriv->iobase);
+ if (thislayout->has_int_sce) {
+ dio200_write8(dev, subpriv->ofs, 0);
}
}
isn_bits &= subpriv->valid_isns;
/* Enable interrupt sources. */
subpriv->enabled_isns = isn_bits;
- if (subpriv->has_int_sce) {
- outb(isn_bits, subpriv->iobase);
+ if (thislayout->has_int_sce) {
+ dio200_write8(dev, subpriv->ofs, isn_bits);
}
}
unsigned cur_enabled;
unsigned int oldevents;
unsigned long flags;
+ unsigned int int_sce_ofs;
+ int_sce_ofs = subpriv->ofs;
triggered = 0;
comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
oldevents = s->async->events;
- if (subpriv->has_int_sce) {
+ if (thislayout->has_int_sce) {
/*
* Collect interrupt sources that have triggered and disable
* them temporarily. Loop around until no extra interrupt
* loop in case of misconfiguration.
*/
cur_enabled = subpriv->enabled_isns;
- while ((intstat = (inb(subpriv->iobase) & subpriv->valid_isns
- & ~triggered)) != 0) {
+ while ((intstat = (dio200_read8(dev, int_sce_ofs)
+ & subpriv->valid_isns & ~triggered))
+ != 0) {
triggered |= intstat;
cur_enabled &= ~triggered;
- outb(cur_enabled, subpriv->iobase);
+ dio200_write8(dev, int_sce_ofs, cur_enabled);
}
} else {
/*
* Reenable them NOW to minimize the time they are disabled.
*/
cur_enabled = subpriv->enabled_isns;
- if (subpriv->has_int_sce) {
- outb(cur_enabled, subpriv->iobase);
+ if (thislayout->has_int_sce) {
+ dio200_write8(dev, int_sce_ofs, cur_enabled);
}
if (subpriv->active) {
*/
static int
dio200_subdev_intr_init(comedi_device * dev, comedi_subdevice * s,
- unsigned long iobase, unsigned valid_isns, int has_int_sce)
+ unsigned int offset, unsigned valid_isns)
{
dio200_subdev_intr *subpriv;
dev->minor);
return -ENOMEM;
}
- subpriv->iobase = iobase;
- subpriv->has_int_sce = has_int_sce;
+ subpriv->ofs = offset;
subpriv->valid_isns = valid_isns;
spin_lock_init(&subpriv->spinlock);
- if (has_int_sce) {
- outb(0, subpriv->iobase); /* Disable interrupt sources. */
+ if (thislayout->has_int_sce) {
+ /* Disable interrupt sources. */
+ dio200_write8(dev, subpriv->ofs, 0);
}
s->private = subpriv;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
- if (has_int_sce) {
+ if (thislayout->has_int_sce) {
s->n_chan = DIO200_MAX_ISNS;
s->len_chanlist = DIO200_MAX_ISNS;
} else {
return IRQ_RETVAL(handled);
}
+/*
+ * Read an '8254' counter subdevice channel.
+ */
+static inline unsigned int
+dio200_subdev_8254_read_chan(comedi_device * dev, comedi_subdevice * s,
+ unsigned int chan)
+{
+ unsigned int i8254_ofs = ((dio200_subdev_8254 *)s->private)->ofs;
+ unsigned int val;
+
+ /* latch counter */
+ val = chan << 6;
+ dio200_write8(dev, i8254_ofs + i8254_control_reg, val);
+
+ /* read lsb */
+ val = dio200_read8(dev, i8254_ofs + chan);
+ /* read msb */
+ val += dio200_read8(dev, i8254_ofs + chan) << 8;
+
+ return val;
+}
+
+/*
+ * Write an '8254' counter subdevice channel.
+ */
+static inline void
+dio200_subdev_8254_write_chan(comedi_device * dev, comedi_subdevice * s,
+ unsigned int chan, unsigned int count)
+{
+ unsigned int i8254_ofs = ((dio200_subdev_8254 *)s->private)->ofs;
+
+ /* write lsb */
+ dio200_write8(dev, i8254_ofs + chan, count & 0xff);
+ /* write msb */
+ dio200_write8(dev, i8254_ofs + chan, (count >> 8) & 0xff);
+}
+
+/*
+ * Set mode of an '8254' counter subdevice channel.
+ */
+static inline void
+dio200_subdev_8254_set_mode(comedi_device * dev, comedi_subdevice * s,
+ unsigned int chan, unsigned int mode)
+{
+ unsigned int i8254_ofs = ((dio200_subdev_8254 *)s->private)->ofs;
+ unsigned int byte;
+
+ byte = chan << 6;
+ byte |= 0x30; /* load lsb then msb */
+ byte |= (mode & 0xf); /* set counter mode and BCD|binary */
+ dio200_write8(dev, i8254_ofs + i8254_control_reg, byte);
+}
+
+/*
+ * Read status byte of an '8254' counter subdevice channel.
+ */
+static inline unsigned int
+dio200_subdev_8254_status(comedi_device * dev, comedi_subdevice * s,
+ unsigned int chan)
+{
+ unsigned int i8254_ofs = ((dio200_subdev_8254 *)s->private)->ofs;
+
+ dio200_write8(dev, i8254_ofs + i8254_control_reg, (0xE0 | 2 << chan));
+ return dio200_read8(dev, i8254_ofs + chan);
+}
+
/*
* Handle 'insn_read' for an '8254' counter subdevice.
*/
{
dio200_subdev_8254 *subpriv = s->private;
int chan = CR_CHAN(insn->chanspec);
+ unsigned long flags;
- data[0] = i8254_read(subpriv->iobase, 0, chan);
+ if (insn->n == 0)
+ return 0;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ data[0] = dio200_subdev_8254_read_chan(dev, s, chan);
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
return 1;
}
{
dio200_subdev_8254 *subpriv = s->private;
int chan = CR_CHAN(insn->chanspec);
+ unsigned long flags;
- i8254_write(subpriv->iobase, 0, chan, data[0]);
+ if (insn->n == 0)
+ return 0;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ dio200_subdev_8254_write_chan(dev, s, chan, data[0]);
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
return 1;
}
* Set gate source for an '8254' counter subdevice channel.
*/
static int
-dio200_set_gate_src(dio200_subdev_8254 * subpriv, unsigned int counter_number,
- unsigned int gate_src)
+dio200_subdev_8254_set_gate_src(comedi_device * dev, comedi_subdevice *s,
+ unsigned int counter_number, unsigned int gate_src)
{
+ dio200_subdev_8254 *subpriv = s->private;
unsigned char byte;
- if (!subpriv->has_clk_gat_sce)
+ if (!thislayout->has_clk_gat_sce)
return -1;
if (counter_number > 2)
return -1;
- if (gate_src > 7)
+ if (gate_src > (thislayout->has_enhancements ? 31 : 7))
return -1;
subpriv->gate_src[counter_number] = gate_src;
byte = GAT_SCE(subpriv->which, counter_number, gate_src);
- outb(byte, subpriv->gat_sce_iobase);
+ dio200_write8(dev, subpriv->gat_sce_ofs, byte);
return 0;
}
* Get gate source for an '8254' counter subdevice channel.
*/
static int
-dio200_get_gate_src(dio200_subdev_8254 * subpriv, unsigned int counter_number)
+dio200_subdev_8254_get_gate_src(comedi_device * dev, comedi_subdevice *s,
+ unsigned int counter_number)
{
- if (!subpriv->has_clk_gat_sce)
+ dio200_subdev_8254 *subpriv = s->private;
+
+ if (!thislayout->has_clk_gat_sce)
return -1;
if (counter_number > 2)
return -1;
* Set clock source for an '8254' counter subdevice channel.
*/
static int
-dio200_set_clock_src(dio200_subdev_8254 * subpriv, unsigned int counter_number,
- unsigned int clock_src)
+dio200_subdev_8254_set_clock_src(comedi_device * dev, comedi_subdevice *s,
+ unsigned int counter_number, unsigned int clock_src)
{
+ dio200_subdev_8254 *subpriv = s->private;
unsigned char byte;
- if (!subpriv->has_clk_gat_sce)
+ if (!thislayout->has_clk_gat_sce)
return -1;
if (counter_number > 2)
return -1;
- if (clock_src > 7)
+ if (clock_src > (thislayout->has_enhancements ? 31 : 7))
return -1;
subpriv->clock_src[counter_number] = clock_src;
byte = CLK_SCE(subpriv->which, counter_number, clock_src);
- outb(byte, subpriv->clk_sce_iobase);
+ dio200_write8(dev, subpriv->clk_sce_ofs, byte);
return 0;
}
* Get clock source for an '8254' counter subdevice channel.
*/
static int
-dio200_get_clock_src(dio200_subdev_8254 * subpriv, unsigned int counter_number,
- lsampl_t * period_ns)
+dio200_subdev_8254_get_clock_src(comedi_device * dev, comedi_subdevice *s,
+ unsigned int counter_number, lsampl_t * period_ns)
{
+ dio200_subdev_8254 *subpriv = s->private;
unsigned clock_src;
- if (!subpriv->has_clk_gat_sce)
+ if (!thislayout->has_clk_gat_sce)
return -1;
if (counter_number > 2)
return -1;
comedi_insn * insn, lsampl_t * data)
{
dio200_subdev_8254 *subpriv = s->private;
- int ret;
+ int ret = 0;
int chan = CR_CHAN(insn->chanspec);
+ unsigned long flags;
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
switch (data[0]) {
case INSN_CONFIG_SET_COUNTER_MODE:
- ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
- if (ret < 0)
- return -EINVAL;
+ if (data[1] > (I8254_MODE5 | I8254_BINARY))
+ ret = -EINVAL;
+ else
+ dio200_subdev_8254_set_mode(dev, s, chan, data[1]);
break;
case INSN_CONFIG_8254_READ_STATUS:
- data[1] = i8254_status(subpriv->iobase, 0, chan);
+ data[1] = dio200_subdev_8254_status(dev, s, chan);
break;
case INSN_CONFIG_SET_GATE_SRC:
- ret = dio200_set_gate_src(subpriv, chan, data[2]);
+ ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]);
if (ret < 0)
- return -EINVAL;
+ ret = -EINVAL;
break;
case INSN_CONFIG_GET_GATE_SRC:
- ret = dio200_get_gate_src(subpriv, chan);
- if (ret < 0)
- return -EINVAL;
+ ret = dio200_subdev_8254_get_gate_src(dev, s, chan);
+ if (ret < 0) {
+ ret = -EINVAL;
+ break;
+ }
data[2] = ret;
break;
case INSN_CONFIG_SET_CLOCK_SRC:
- ret = dio200_set_clock_src(subpriv, chan, data[1]);
+ ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]);
if (ret < 0)
- return -EINVAL;
+ ret = -EINVAL;
break;
case INSN_CONFIG_GET_CLOCK_SRC:
- ret = dio200_get_clock_src(subpriv, chan, &data[2]);
- if (ret < 0)
- return -EINVAL;
+ ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]);
+ if (ret < 0) {
+ ret = -EINVAL;
+ break;
+ }
data[1] = ret;
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
break;
}
- return insn->n;
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+ return ret < 0 ? ret : insn->n;
}
/*
* This function initializes an '8254' counter subdevice.
*
- * Note: iobase is the base address of the board, not the subdevice;
* offset is the offset to the 8254 chip.
*/
static int
dio200_subdev_8254_init(comedi_device * dev, comedi_subdevice * s,
- unsigned long iobase, unsigned offset, int has_clk_gat_sce)
+ unsigned int offset)
{
dio200_subdev_8254 *subpriv;
unsigned int chan;
s->insn_write = dio200_subdev_8254_write;
s->insn_config = dio200_subdev_8254_config;
- subpriv->iobase = offset + iobase;
- subpriv->has_clk_gat_sce = has_clk_gat_sce;
- if (has_clk_gat_sce) {
+ spin_lock_init(&subpriv->spinlock);
+ subpriv->ofs = offset;
+ if (thislayout->has_clk_gat_sce) {
/* Derive CLK_SCE and GAT_SCE register offsets from
* 8254 offset. */
- subpriv->clk_sce_iobase =
- DIO200_XCLK_SCE + (offset >> 3) + iobase;
- subpriv->gat_sce_iobase =
- DIO200_XGAT_SCE + (offset >> 3) + iobase;
+ subpriv->clk_sce_ofs =
+ DIO200_XCLK_SCE + (offset >> 3);
+ subpriv->gat_sce_ofs =
+ DIO200_XGAT_SCE + (offset >> 3);
subpriv->which = (offset >> 2) & 1;
}
/* Initialize channels. */
for (chan = 0; chan < 3; chan++) {
- i8254_set_mode(subpriv->iobase, 0, chan,
- I8254_MODE0 | I8254_BINARY);
- if (subpriv->has_clk_gat_sce) {
+ dio200_subdev_8254_set_mode(dev, s, chan,
+ (I8254_MODE0 | I8254_BINARY));
+ if (thislayout->has_clk_gat_sce) {
/* Gate source 0 is VCC (logic 1). */
- dio200_set_gate_src(subpriv, chan, 0);
+ dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
/* Clock source 0 is the dedicated clock input. */
- dio200_set_clock_src(subpriv, chan, 0);
+ dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
}
}
static void
dio200_subdev_8254_cleanup(comedi_device * dev, comedi_subdevice * s)
{
- dio200_subdev_intr *subpriv = s->private;
+ dio200_subdev_8254 *subpriv = s->private;
if (subpriv) {
kfree(subpriv);
}
}
+/*
+ * This function sets I/O directions for an '8255' DIO subdevice.
+ */
+static void
+dio200_subdev_8255_set_dir(comedi_device * dev, comedi_subdevice * s)
+{
+ dio200_subdev_8255 *subpriv = s->private;
+ int config;
+
+ config = CR_CW;
+ /* 1 in io_bits indicates output, 1 in config indicates input */
+ if (!(s->io_bits & 0x0000ff))
+ config |= CR_A_IO;
+ if (!(s->io_bits & 0x00ff00))
+ config |= CR_B_IO;
+ if (!(s->io_bits & 0x0f0000))
+ config |= CR_C_LO_IO;
+ if (!(s->io_bits & 0xf00000))
+ config |= CR_C_HI_IO;
+
+ dio200_write8(dev, subpriv->ofs + 3, config);
+}
+
+/*
+ * Handle 'insn_bits' for an '8255' DIO subdevice.
+ */
+static int
+dio200_subdev_8255_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ unsigned int i8255_ofs = ((dio200_subdev_8255 *)s->private)->ofs;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ if (data[0] & 0xff) {
+ dio200_write8(dev, i8255_ofs, s->state & 0xff);
+ }
+ if (data[0] & 0xff00) {
+ dio200_write8(dev, i8255_ofs + 1,
+ (s->state >> 8) & 0xff);
+ }
+ if (data[0] & 0xff0000) {
+ dio200_write8(dev, i8255_ofs + 2,
+ (s->state >> 16) & 0xff);
+ }
+ }
+
+ data[1] = dio200_read8(dev, i8255_ofs);
+ data[1] |= dio200_read8(dev, i8255_ofs + 1) << 8;
+ data[1] |= dio200_read8(dev, i8255_ofs + 2) << 16;
+
+ return 2;
+}
+
+/*
+ * Handle 'insn_config' for an '8255' DIO subdevice.
+ */
+static int
+dio200_subdev_8255_config(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ unsigned int mask;
+ unsigned int bits;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & 0x0000ff) {
+ bits = 0x0000ff;
+ } else if (mask & 0x00ff00) {
+ bits = 0x00ff00;
+ } else if (mask & 0x0f0000) {
+ bits = 0x0f0000;
+ } else {
+ bits = 0xf00000;
+ }
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~bits;
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= bits;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dio200_subdev_8255_set_dir(dev, s);
+
+ return 1;
+}
+
+/*
+ * This function initializes an '8255' DIO subdevice.
+ *
+ * offset is the offset to the 8255 chip.
+ */
+static int
+dio200_subdev_8255_init(comedi_device * dev, comedi_subdevice * s,
+ unsigned int offset)
+{
+ dio200_subdev_8255 *subpriv;
+
+ subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+ if (!subpriv) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return -ENOMEM;
+ }
+
+ subpriv->ofs = offset;
+
+ s->private = subpriv;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dio200_subdev_8255_bits;
+ s->insn_config = dio200_subdev_8255_config;
+
+ s->state = 0;
+ s->io_bits = 0;
+ dio200_subdev_8255_set_dir(dev, s);
+
+ return 0;
+}
+
+/*
+ * This function cleans up an '8255' DIO subdevice.
+ */
+static void
+dio200_subdev_8255_cleanup(comedi_device * dev, comedi_subdevice * s)
+{
+ dio200_subdev_8255 *subpriv = s->private;
+
+ if (subpriv) {
+ kfree(subpriv);
+ }
+}
+
+#ifdef CONFIG_COMEDI_PCI
+/*
+ * This function does some special set-up for the PCIe boards
+ * PCIe215, PCIe236, PCIe296.
+ */
+static int
+dio200_pcie_board_setup(comedi_device * dev)
+{
+ struct pci_dev *pci_dev = devpriv->pci_dev;
+ unsigned char __iomem *brbase;
+ resource_size_t brlen;
+
+ /*
+ * The board uses Altera Cyclone IV with PCI-Express hard IP.
+ * The FPGA configuration has the PCI-Express Avalon-MM Bridge
+ * Control registers in PCI BAR 0, offset 0, and the length of
+ * these registers is 0x4000.
+ *
+ * We need to write 0x80 to the "Avalon-MM to PCI-Express Interrupt
+ * Enable" register at offset 0x50 to allow generation of PCIe
+ * interrupts when RXmlrq_i is asserted in the SOPC Builder system.
+ */
+ brlen = pci_resource_len(pci_dev, 0);
+ if (brlen < 0x4000 ||
+ !(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_ERR "comedi%d: error! bad PCI region!\n",
+ dev->minor);
+ return -EINVAL;
+ }
+ brbase = ioremap_nocache(pci_resource_start(pci_dev, 0), brlen);
+ if (!brbase) {
+ printk(KERN_ERR "comedi%d: error! failed to map registers!\n",
+ dev->minor);
+ return -ENOMEM;
+ }
+ writel(0x80, brbase + 0x50);
+ iounmap(brbase);
+ /*
+ * Enable "enhanced" features of board.
+ */
+ dio200_write8(dev, DIO200_ENHANCE, 1);
+ return 0;
+}
+#endif
+
/*
* Attach is called by the Comedi core to configure the driver
* for a particular board. If you specified a board_name array
}
devpriv->intr_sd = -1;
+ devpriv->io.regtype = no_regtype;
+ devpriv->io.regshift = thisboard->mainshift;
/* Enable device and reserve I/O spaces. */
#ifdef CONFIG_COMEDI_PCI
if (pci_dev) {
+ resource_size_t base, len;
+ unsigned int bar;
+
ret = comedi_pci_enable(pci_dev, DIO200_DRIVER_NAME);
if (ret < 0) {
printk(KERN_ERR
dev->minor);
return ret;
}
- iobase = pci_resource_start(pci_dev, 2);
+ bar = thisboard->mainbar;
+ base = pci_resource_start(pci_dev, bar);
+ len = pci_resource_len(pci_dev, bar);
+ if (len < thisboard->mainsize) {
+ printk(KERN_ERR
+ "comedi%d: error! PCI region size too small!\n",
+ dev->minor);
+ return -EINVAL;
+ }
+ if ((pci_resource_flags(pci_dev, bar) & IORESOURCE_MEM) != 0) {
+ devpriv->io.u.membase = ioremap_nocache(base, len);
+ if (!devpriv->io.u.membase) {
+ printk(KERN_ERR
+ "comedi%d: error! cannot remap registers!\n",
+ dev->minor);
+ return -ENOMEM;
+ }
+ devpriv->io.regtype = mmio_regtype;
+ } else {
+ devpriv->io.u.iobase = (unsigned long)base;
+ devpriv->io.regtype = io_regtype;
+ }
irq = pci_dev->irq;
} else
#endif
{
- ret = dio200_request_region(dev->minor, iobase, DIO200_IO_SIZE);
+ ret = dio200_request_region(dev->minor, iobase,
+ thisboard->mainsize);
+ if (ret < 0) {
+ return ret;
+ }
+ devpriv->io.u.iobase = iobase;
+ devpriv->io.regtype = io_regtype;
+ }
+
+ switch (thisboard->model) {
+#ifdef CONFIG_COMEDI_PCI
+ case pcie215_model:
+ case pcie236_model:
+ case pcie296_model:
+ ret = dio200_pcie_board_setup(dev);
if (ret < 0) {
return ret;
}
+ break;
+#endif
+ default:
+ break;
}
- dev->iobase = iobase;
layout = thislayout;
if ((ret = alloc_subdevices(dev, layout->n_subdevs)) < 0) {
switch (layout->sdtype[n]) {
case sd_8254:
/* counter subdevice (8254) */
- ret = dio200_subdev_8254_init(dev, s, iobase,
- layout->sdinfo[n], layout->has_clk_gat_sce);
+ ret = dio200_subdev_8254_init(dev, s,
+ layout->sdinfo[n]);
if (ret < 0) {
return ret;
}
break;
case sd_8255:
/* digital i/o subdevice (8255) */
- ret = subdev_8255_init(dev, s, 0,
- iobase + layout->sdinfo[n]);
+ ret = dio200_subdev_8255_init(dev, s,
+ layout->sdinfo[n]);
if (ret < 0) {
return ret;
}
/* 'INTERRUPT' subdevice */
if (irq) {
ret = dio200_subdev_intr_init(dev, s,
- iobase + DIO200_INT_SCE,
- layout->sdinfo[n], layout->has_int_sce);
+ DIO200_INT_SCE, layout->sdinfo[n]);
if (ret < 0) {
return ret;
}
s->type = COMEDI_SUBD_UNUSED;
}
break;
+ case sd_timer:
+ /* TODO. Fall-thru to default for now. */
default:
s->type = COMEDI_SUBD_UNUSED;
break;
dio200_subdev_8254_cleanup(dev, s);
break;
case sd_8255:
- subdev_8255_cleanup(dev, s);
+ dio200_subdev_8255_cleanup(dev, s);
break;
case sd_intr:
dio200_subdev_intr_cleanup(dev, s);
}
}
if (devpriv) {
+ if (devpriv->io.regtype == mmio_regtype) {
+ iounmap(devpriv->io.u.membase);
+ }
#ifdef CONFIG_COMEDI_PCI
if (devpriv->pci_dev) {
- if (dev->iobase) {
+ if (devpriv->io.regtype != no_regtype) {
comedi_pci_disable(devpriv->pci_dev);
}
pci_dev_put(devpriv->pci_dev);
} else
#endif
{
- if (dev->iobase) {
- release_region(dev->iobase, DIO200_IO_SIZE);
+ if (devpriv->io.regtype == io_regtype) {
+ release_region(devpriv->io.u.iobase,
+ thisboard->mainsize);
}
}
}