summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorIan Abbott <abbotti@mev.co.uk>2012-10-24 16:48:01 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-24 15:25:17 -0700
commit71b3e9e8dc218133417d3c167632a61f9fed3651 (patch)
treed5f9d14c72713d25fccee0b13f973af23394498c /drivers
parent9bfa0d548c0ca427cba4a1c26658f870cfda64ca (diff)
downloadlwn-71b3e9e8dc218133417d3c167632a61f9fed3651.tar.gz
lwn-71b3e9e8dc218133417d3c167632a61f9fed3651.zip
staging: comedi: amplc_dio200: support memory-mapped I/O
The boards currently supported by this module all use port I/O. Support memory-mapped I/O as well for future PCI/PCIe cards. Define `struct dio200_region` to hold the type of register access and either the port I/O base address or an ioremapped MMIO address. Add a member `io` to the comedi device private data (`struct dio200_private`) to hold this. Use this instead of `dev->iobase`. Memory-mapped registers are mapped in `dio200_pci_attach()` and unmapped in `dio200_detach()`. `dio200_detach()` now uses the private data pointer `devpriv` set to `dev->private` but can return early if it is `NULL` because no clean-up needs to be done in that case. Signed-off-by: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.c61
1 files changed, 53 insertions, 8 deletions
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
index 1b734dc51b33..39fb82b4eca8 100644
--- a/drivers/staging/comedi/drivers/amplc_dio200.c
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -265,6 +265,18 @@ static const unsigned clock_period[8] = {
};
/*
+ * Register region.
+ */
+enum dio200_regtype { no_regtype = 0, io_regtype, mmio_regtype };
+struct dio200_region {
+ union {
+ unsigned long iobase; /* I/O base address */
+ unsigned char __iomem *membase; /* mapped MMIO base address */
+ } u;
+ enum dio200_regtype regtype;
+};
+
+/*
* Board descriptions.
*/
@@ -425,6 +437,7 @@ static const struct dio200_layout dio200_layouts[] = {
feel free to suggest moving the variable to the struct comedi_device struct.
*/
struct dio200_private {
+ struct dio200_region io; /* Register region */
int intr_sd;
};
@@ -480,7 +493,12 @@ static inline bool is_isa_board(const struct dio200_board *board)
static unsigned char dio200_read8(struct comedi_device *dev,
unsigned int offset)
{
- return inb(dev->iobase + offset);
+ struct dio200_private *devpriv = dev->private;
+
+ if (devpriv->io.regtype == io_regtype)
+ return inb(devpriv->io.u.iobase + offset);
+ else
+ return readb(devpriv->io.u.membase + offset);
}
/*
@@ -489,7 +507,12 @@ static unsigned char dio200_read8(struct comedi_device *dev,
static void dio200_write8(struct comedi_device *dev, unsigned int offset,
unsigned char val)
{
- outb(val, dev->iobase + offset);
+ struct dio200_private *devpriv = dev->private;
+
+ if (devpriv->io.regtype == io_regtype)
+ outb(val, devpriv->io.u.iobase + offset);
+ else
+ writeb(val, devpriv->io.u.membase + offset);
}
/*
@@ -1405,13 +1428,14 @@ static void dio200_subdev_8255_cleanup(struct comedi_device *dev,
static void dio200_report_attach(struct comedi_device *dev, unsigned int irq)
{
const struct dio200_board *thisboard = comedi_board(dev);
+ struct dio200_private *devpriv = dev->private;
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
char tmpbuf[60];
int tmplen;
if (is_isa_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
- "(base %#lx) ", dev->iobase);
+ "(base %#lx) ", devpriv->io.u.iobase);
else if (is_pci_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
"(pci %s) ", pci_name(pcidev));
@@ -1526,7 +1550,8 @@ static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it)
ret = dio200_request_region(dev, iobase, DIO200_IO_SIZE);
if (ret < 0)
return ret;
- dev->iobase = iobase;
+ devpriv->io.u.iobase = iobase;
+ devpriv->io.regtype = io_regtype;
return dio200_common_attach(dev, irq, 0);
} else if (is_pci_board(thisboard)) {
dev_err(dev->class_dev,
@@ -1549,6 +1574,7 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
struct pci_dev *pci_dev)
{
struct dio200_private *devpriv;
+ resource_size_t base;
int ret;
if (!DO_PCI)
@@ -1573,16 +1599,32 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
"error! cannot enable PCI device and request regions!\n");
return ret;
}
- dev->iobase = pci_resource_start(pci_dev, 2);
+ base = pci_resource_start(pci_dev, 2);
+ if ((pci_resource_flags(pci_dev, 2) & IORESOURCE_MEM) != 0) {
+ resource_size_t len = pci_resource_len(pci_dev, 2);
+ devpriv->io.u.membase = ioremap_nocache(base, len);
+ if (!devpriv->io.u.membase) {
+ dev_err(dev->class_dev,
+ "error! cannot remap registers\n");
+ return -ENOMEM;
+ }
+ devpriv->io.regtype = mmio_regtype;
+ } else {
+ devpriv->io.u.iobase = (unsigned long)base;
+ devpriv->io.regtype = io_regtype;
+ }
return dio200_common_attach(dev, pci_dev->irq, IRQF_SHARED);
}
static void dio200_detach(struct comedi_device *dev)
{
const struct dio200_board *thisboard = comedi_board(dev);
+ struct dio200_private *devpriv = dev->private;
const struct dio200_layout *layout;
unsigned n;
+ if (!thisboard || !devpriv)
+ return;
if (dev->irq)
free_irq(dev->irq, dev);
if (dev->subdevices) {
@@ -1605,13 +1647,16 @@ static void dio200_detach(struct comedi_device *dev)
}
}
if (is_isa_board(thisboard)) {
- if (dev->iobase)
- release_region(dev->iobase, DIO200_IO_SIZE);
+ if (devpriv->io.regtype == io_regtype)
+ release_region(devpriv->io.u.iobase, DIO200_IO_SIZE);
} else if (is_pci_board(thisboard)) {
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
if (pcidev) {
- if (dev->iobase)
+ if (devpriv->io.regtype != no_regtype) {
+ if (devpriv->io.regtype == mmio_regtype)
+ iounmap(devpriv->io.u.membase);
comedi_pci_disable(pcidev);
+ }
}
}
}