Commit 71b3e9e8 authored by Ian Abbott's avatar Ian Abbott Committed by Greg Kroah-Hartman
Browse files

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: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9bfa0d54
Loading
Loading
Loading
Loading
+53 −8
Original line number Diff line number Diff line
@@ -264,6 +264,18 @@ static const unsigned clock_period[8] = {
	0			/* group clock input pin */
};

/*
 * 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,16 +1647,19 @@ 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);
			}
		}
	}
}

/*
 * The struct comedi_driver structure tells the Comedi core module