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), PC218E (pc218e), PC272E (pc272e),
+ PCI272 (pci272 or amplc_dio200)
+Updated: Wed, 22 Oct 2008 13:36:02 +0100
Status: works
Configuration options - PC212E, PC214E, PC215E, PC218E, PC272E:
/* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
#define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
#define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
+#define PCI_DEVICE_ID_INVALID 0xffff
/* 200 series registers */
#define DIO200_IO_SIZE 0x20
pc214e_model,
pc215e_model, pci215_model,
pc218e_model,
- pc272e_model, pci272_model
+ pc272e_model, pci272_model,
+ anypci_model
};
enum dio200_layout {
typedef struct dio200_board_struct {
const char *name;
+ unsigned short devid;
enum dio200_bustype bustype;
enum dio200_model model;
enum dio200_layout layout;
model: pc215e_model,
layout: pc215_layout,
},
+#ifdef CONFIG_COMEDI_PCI
{
name: "pci215",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI215,
bustype: pci_bustype,
model: pci215_model,
layout: pc215_layout,
},
+#endif
{
name: "pc218e",
bustype: isa_bustype,
model: pc272e_model,
layout: pc272_layout,
},
+#ifdef CONFIG_COMEDI_PCI
{
name: "pci272",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI272,
bustype: pci_bustype,
model: pci272_model,
layout: pc272_layout,
},
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: DIO200_DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ bustype: pci_bustype,
+ model: anypci_model, /* wildcard */
+ },
+#endif
};
/*
* PCI driver table.
*/
-static struct pci_device_id dio200_pci_table[] __devinitdata = {
+#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},
{0}
};
MODULE_DEVICE_TABLE(pci, dio200_pci_table);
+#endif /* CONFIG_COMEDI_PCI */
/*
* Useful for shorthand access to the particular board structure
several hardware drivers keep similar information in this structure,
feel free to suggest moving the variable to the comedi_device struct. */
typedef struct {
+#ifdef CONFIG_COMEDI_PCI
struct pci_dev *pci_dev; /* PCI device */
+#endif
int intr_sd;
} dio200_private;
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 */
+ spinlock_t spinlock;
} dio200_subdev_8254;
typedef struct {
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
/*
* This function looks for a PCI device matching the requested board name,
* bus and slot.
*/
+#ifdef CONFIG_COMEDI_PCI
static int
dio200_find_pci(comedi_device * dev, int bus, int slot,
struct pci_dev **pci_dev_p)
{
struct pci_dev *pci_dev = NULL;
- 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;
}
return -EIO;
}
+#endif
/*
* This function checks and requests an I/O region, reporting an error
{
dio200_subdev_8254 *subpriv = s->private;
int chan = CR_CHAN(insn->chanspec);
+ unsigned long flags;
+
+ if (insn->n == 0)
+ return 0;
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
data[0] = i8254_read(subpriv->iobase, 0, 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;
+ if (insn->n == 0)
+ return 0;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
i8254_write(subpriv->iobase, 0, 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;
* 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;
* 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;
+ ret = -EINVAL;
break;
case INSN_CONFIG_8254_READ_STATUS:
data[1] = i8254_status(subpriv->iobase, 0, 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;
}
/*
*/
static int
dio200_subdev_8254_init(comedi_device * dev, comedi_subdevice * s,
- unsigned long iobase, unsigned offset, int has_clk_gat_sce)
+ unsigned long iobase, unsigned offset)
{
dio200_subdev_8254 *subpriv;
unsigned int chan;
s->insn_write = dio200_subdev_8254_write;
s->insn_config = dio200_subdev_8254_config;
+ spin_lock_init(&subpriv->spinlock);
subpriv->iobase = offset + iobase;
- subpriv->has_clk_gat_sce = has_clk_gat_sce;
- if (has_clk_gat_sce) {
+ if (thislayout->has_clk_gat_sce) {
/* Derive CLK_SCE and GAT_SCE register offsets from
* 8254 offset. */
subpriv->clk_sce_iobase =
for (chan = 0; chan < 3; chan++) {
i8254_set_mode(subpriv->iobase, 0, chan,
I8254_MODE0 | I8254_BINARY);
- if (subpriv->has_clk_gat_sce) {
+ 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);
static int dio200_attach(comedi_device * dev, comedi_devconfig * it)
{
comedi_subdevice *s;
- struct pci_dev *pci_dev = NULL;
unsigned long iobase = 0;
unsigned int irq = 0;
+#ifdef CONFIG_COMEDI_PCI
+ struct pci_dev *pci_dev = NULL;
int bus = 0, slot = 0;
+#endif
const dio200_layout *layout;
int share_irq = 0;
int sdx;
irq = it->options[1];
share_irq = 0;
break;
+#ifdef CONFIG_COMEDI_PCI
case pci_bustype:
bus = it->options[0];
slot = it->options[1];
return ret;
devpriv->pci_dev = pci_dev;
break;
+#endif
default:
printk(KERN_ERR
"comedi%d: %s: BUG! cannot determine board type!\n",
devpriv->intr_sd = -1;
/* Enable device and reserve I/O spaces. */
+#ifdef CONFIG_COMEDI_PCI
if (pci_dev) {
ret = comedi_pci_enable(pci_dev, DIO200_DRIVER_NAME);
if (ret < 0) {
}
iobase = pci_resource_start(pci_dev, 2);
irq = pci_dev->irq;
- } else {
+ } else
+#endif
+ {
ret = dio200_request_region(dev->minor, iobase, DIO200_IO_SIZE);
if (ret < 0) {
return ret;
case sd_8254:
/* counter subdevice (8254) */
ret = dio200_subdev_8254_init(dev, s, iobase,
- layout->sdinfo[n], layout->has_clk_gat_sce);
+ layout->sdinfo[n]);
if (ret < 0) {
return ret;
}
if (thisboard->bustype == isa_bustype) {
printk("(base %#lx) ", iobase);
} else {
+#ifdef CONFIG_COMEDI_PCI
printk("(pci %s) ", pci_name(pci_dev));
+#endif
}
if (irq) {
printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
}
}
if (devpriv) {
+#ifdef CONFIG_COMEDI_PCI
if (devpriv->pci_dev) {
if (dev->iobase) {
comedi_pci_disable(devpriv->pci_dev);
}
pci_dev_put(devpriv->pci_dev);
- } else if (dev->iobase) {
- release_region(dev->iobase, DIO200_IO_SIZE);
+ } else
+#endif
+ {
+ if (dev->iobase) {
+ release_region(dev->iobase, DIO200_IO_SIZE);
+ }
}
}
if (dev->board_name) {