lkml.org 
[lkml]   [2008]   [Oct]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 45/46] UIO: add automata sercos3 pci card support
    Date
    From: John Ogness <john.ogness@linutronix.de>

    Here is a new version of the patch to support the Automata Sercos III
    PCI card driver. I now check that the IRQ is enabled before accepting
    the interrupt.

    I still use a logical OR to store the enabled interrupts and I've
    added a second use of a logical OR when restoring the enabled
    interrupts. I added an explanation of why I do this in comments at the
    top of the source file.

    Since I use a logical OR, I also removed the extra checks if the
    Interrupt Enable Register and ier0_cache are 0.

    Signed-off-by: John Ogness <john.ogness@linutronix.de>
    Signed-off-by: Hans J. Koch <hjk@linutronix.de>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
    ---
    drivers/uio/Kconfig | 13 +++
    drivers/uio/Makefile | 1 +
    drivers/uio/uio_sercos3.c | 243 +++++++++++++++++++++++++++++++++++++++++++++
    3 files changed, 257 insertions(+), 0 deletions(-)
    create mode 100644 drivers/uio/uio_sercos3.c

    diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
    index 4190be6..04b954c 100644
    --- a/drivers/uio/Kconfig
    +++ b/drivers/uio/Kconfig
    @@ -58,4 +58,17 @@ config UIO_SMX

    If you compile this as a module, it will be called uio_smx.

    +config UIO_SERCOS3
    + tristate "Automata Sercos III PCI card driver"
    + default n
    + help
    + Userspace I/O interface for the Sercos III PCI card from
    + Automata GmbH. The userspace part of this driver will be
    + available for download from the Automata GmbH web site.
    +
    + Automata GmbH: http://www.automataweb.com
    + Sercos III interface: http://www.sercos.com
    +
    + If you compile this as a module, it will be called uio_sercos3.
    +
    endif
    diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
    index 8667bbd..e695581 100644
    --- a/drivers/uio/Makefile
    +++ b/drivers/uio/Makefile
    @@ -3,3 +3,4 @@ obj-$(CONFIG_UIO_CIF) += uio_cif.o
    obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
    obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
    obj-$(CONFIG_UIO_SMX) += uio_smx.o
    +obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
    diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c
    new file mode 100644
    index 0000000..a6d1b2b
    --- /dev/null
    +++ b/drivers/uio/uio_sercos3.c
    @@ -0,0 +1,243 @@
    +/* sercos3: UIO driver for the Automata Sercos III PCI card
    +
    + Copyright (C) 2008 Linutronix GmbH
    + Author: John Ogness <john.ogness@linutronix.de>
    +
    + This is a straight-forward UIO driver, where interrupts are disabled
    + by the interrupt handler and re-enabled via a write to the UIO device
    + by the userspace-part.
    +
    + The only part that may seem odd is the use of a logical OR when
    + storing and restoring enabled interrupts. This is done because the
    + userspace-part could directly modify the Interrupt Enable Register
    + at any time. To reduce possible conflicts, the kernel driver uses
    + a logical OR to make more controlled changes (rather than blindly
    + overwriting previous values).
    +
    + Race conditions exist if the userspace-part directly modifies the
    + Interrupt Enable Register while in operation. The consequences are
    + that certain interrupts would fail to be enabled or disabled. For
    + this reason, the userspace-part should only directly modify the
    + Interrupt Enable Register at the beginning (to get things going).
    + The userspace-part can safely disable interrupts at any time using
    + a write to the UIO device.
    +*/
    +
    +#include <linux/device.h>
    +#include <linux/module.h>
    +#include <linux/pci.h>
    +#include <linux/uio_driver.h>
    +#include <linux/io.h>
    +
    +/* ID's for SERCOS III PCI card (PLX 9030) */
    +#define SERCOS_SUB_VENDOR_ID 0x1971
    +#define SERCOS_SUB_SYSID_3530 0x3530
    +#define SERCOS_SUB_SYSID_3535 0x3535
    +#define SERCOS_SUB_SYSID_3780 0x3780
    +
    +/* Interrupt Enable Register */
    +#define IER0_OFFSET 0x08
    +
    +/* Interrupt Status Register */
    +#define ISR0_OFFSET 0x18
    +
    +struct sercos3_priv {
    + u32 ier0_cache;
    + spinlock_t ier0_cache_lock;
    +};
    +
    +/* this function assumes ier0_cache_lock is locked! */
    +static void sercos3_disable_interrupts(struct uio_info *info,
    + struct sercos3_priv *priv)
    +{
    + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
    +
    + /* add enabled interrupts to cache */
    + priv->ier0_cache |= ioread32(ier0);
    +
    + /* disable interrupts */
    + iowrite32(0, ier0);
    +}
    +
    +/* this function assumes ier0_cache_lock is locked! */
    +static void sercos3_enable_interrupts(struct uio_info *info,
    + struct sercos3_priv *priv)
    +{
    + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
    +
    + /* restore previously enabled interrupts */
    + iowrite32(ioread32(ier0) | priv->ier0_cache, ier0);
    + priv->ier0_cache = 0;
    +}
    +
    +static irqreturn_t sercos3_handler(int irq, struct uio_info *info)
    +{
    + struct sercos3_priv *priv = info->priv;
    + void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET;
    + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
    +
    + if (!(ioread32(isr0) & ioread32(ier0)))
    + return IRQ_NONE;
    +
    + spin_lock(&priv->ier0_cache_lock);
    + sercos3_disable_interrupts(info, priv);
    + spin_unlock(&priv->ier0_cache_lock);
    +
    + return IRQ_HANDLED;
    +}
    +
    +static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on)
    +{
    + struct sercos3_priv *priv = info->priv;
    +
    + spin_lock_irq(&priv->ier0_cache_lock);
    + if (irq_on)
    + sercos3_enable_interrupts(info, priv);
    + else
    + sercos3_disable_interrupts(info, priv);
    + spin_unlock_irq(&priv->ier0_cache_lock);
    +
    + return 0;
    +}
    +
    +static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
    + int n, int pci_bar)
    +{
    + info->mem[n].addr = pci_resource_start(dev, pci_bar);
    + if (!info->mem[n].addr)
    + return -1;
    + info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar),
    + pci_resource_len(dev, pci_bar));
    + if (!info->mem[n].internal_addr)
    + return -1;
    + info->mem[n].size = pci_resource_len(dev, pci_bar);
    + info->mem[n].memtype = UIO_MEM_PHYS;
    + return 0;
    +}
    +
    +static int __devinit sercos3_pci_probe(struct pci_dev *dev,
    + const struct pci_device_id *id)
    +{
    + struct uio_info *info;
    + struct sercos3_priv *priv;
    + int i;
    +
    + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
    + if (!info)
    + return -ENOMEM;
    +
    + priv = kzalloc(sizeof(struct sercos3_priv), GFP_KERNEL);
    + if (!priv)
    + goto out_free;
    +
    + if (pci_enable_device(dev))
    + goto out_free_priv;
    +
    + if (pci_request_regions(dev, "sercos3"))
    + goto out_disable;
    +
    + /* we only need PCI BAR's 0, 2, 3, 4, 5 */
    + if (sercos3_setup_iomem(dev, info, 0, 0))
    + goto out_unmap;
    + if (sercos3_setup_iomem(dev, info, 1, 2))
    + goto out_unmap;
    + if (sercos3_setup_iomem(dev, info, 2, 3))
    + goto out_unmap;
    + if (sercos3_setup_iomem(dev, info, 3, 4))
    + goto out_unmap;
    + if (sercos3_setup_iomem(dev, info, 4, 5))
    + goto out_unmap;
    +
    + spin_lock_init(&priv->ier0_cache_lock);
    + info->priv = priv;
    + info->name = "Sercos_III_PCI";
    + info->version = "0.0.1";
    + info->irq = dev->irq;
    + info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
    + info->handler = sercos3_handler;
    + info->irqcontrol = sercos3_irqcontrol;
    +
    + pci_set_drvdata(dev, info);
    +
    + if (uio_register_device(&dev->dev, info))
    + goto out_unmap;
    +
    + return 0;
    +
    +out_unmap:
    + for (i = 0; i < 5; i++) {
    + if (info->mem[i].internal_addr)
    + iounmap(info->mem[i].internal_addr);
    + }
    + pci_release_regions(dev);
    +out_disable:
    + pci_disable_device(dev);
    +out_free_priv:
    + kfree(priv);
    +out_free:
    + kfree(info);
    + return -ENODEV;
    +}
    +
    +static void sercos3_pci_remove(struct pci_dev *dev)
    +{
    + struct uio_info *info = pci_get_drvdata(dev);
    + int i;
    +
    + uio_unregister_device(info);
    + pci_release_regions(dev);
    + pci_disable_device(dev);
    + pci_set_drvdata(dev, NULL);
    + for (i = 0; i < 5; i++) {
    + if (info->mem[i].internal_addr)
    + iounmap(info->mem[i].internal_addr);
    + }
    + kfree(info->priv);
    + kfree(info);
    +}
    +
    +static struct pci_device_id sercos3_pci_ids[] __devinitdata = {
    + {
    + .vendor = PCI_VENDOR_ID_PLX,
    + .device = PCI_DEVICE_ID_PLX_9030,
    + .subvendor = SERCOS_SUB_VENDOR_ID,
    + .subdevice = SERCOS_SUB_SYSID_3530,
    + },
    + {
    + .vendor = PCI_VENDOR_ID_PLX,
    + .device = PCI_DEVICE_ID_PLX_9030,
    + .subvendor = SERCOS_SUB_VENDOR_ID,
    + .subdevice = SERCOS_SUB_SYSID_3535,
    + },
    + {
    + .vendor = PCI_VENDOR_ID_PLX,
    + .device = PCI_DEVICE_ID_PLX_9030,
    + .subvendor = SERCOS_SUB_VENDOR_ID,
    + .subdevice = SERCOS_SUB_SYSID_3780,
    + },
    + { 0, }
    +};
    +
    +static struct pci_driver sercos3_pci_driver = {
    + .name = "sercos3",
    + .id_table = sercos3_pci_ids,
    + .probe = sercos3_pci_probe,
    + .remove = sercos3_pci_remove,
    +};
    +
    +static int __init sercos3_init_module(void)
    +{
    + return pci_register_driver(&sercos3_pci_driver);
    +}
    +
    +static void __exit sercos3_exit_module(void)
    +{
    + pci_unregister_driver(&sercos3_pci_driver);
    +}
    +
    +module_init(sercos3_init_module);
    +module_exit(sercos3_exit_module);
    +
    +MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
    +MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
    +MODULE_LICENSE("GPL v2");
    --
    1.6.0.2


    \
     
     \ /
      Last update: 2008-10-16 19:35    [W:2.800 / U:1.108 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site