Commit a6030fcc608bd333c80eab3bfc72f63906476c61

Authored by John Ogness
Committed by Greg Kroah-Hartman
1 parent a6fcc3a196

UIO: add automata sercos3 pci card support

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>

Showing 3 changed files with 257 additions and 0 deletions Side-by-side Diff

... ... @@ -58,5 +58,18 @@
58 58  
59 59 If you compile this as a module, it will be called uio_smx.
60 60  
  61 +config UIO_SERCOS3
  62 + tristate "Automata Sercos III PCI card driver"
  63 + default n
  64 + help
  65 + Userspace I/O interface for the Sercos III PCI card from
  66 + Automata GmbH. The userspace part of this driver will be
  67 + available for download from the Automata GmbH web site.
  68 +
  69 + Automata GmbH: http://www.automataweb.com
  70 + Sercos III interface: http://www.sercos.com
  71 +
  72 + If you compile this as a module, it will be called uio_sercos3.
  73 +
61 74 endif
drivers/uio/Makefile
... ... @@ -3,4 +3,5 @@
3 3 obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
4 4 obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
5 5 obj-$(CONFIG_UIO_SMX) += uio_smx.o
  6 +obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
drivers/uio/uio_sercos3.c
  1 +/* sercos3: UIO driver for the Automata Sercos III PCI card
  2 +
  3 + Copyright (C) 2008 Linutronix GmbH
  4 + Author: John Ogness <john.ogness@linutronix.de>
  5 +
  6 + This is a straight-forward UIO driver, where interrupts are disabled
  7 + by the interrupt handler and re-enabled via a write to the UIO device
  8 + by the userspace-part.
  9 +
  10 + The only part that may seem odd is the use of a logical OR when
  11 + storing and restoring enabled interrupts. This is done because the
  12 + userspace-part could directly modify the Interrupt Enable Register
  13 + at any time. To reduce possible conflicts, the kernel driver uses
  14 + a logical OR to make more controlled changes (rather than blindly
  15 + overwriting previous values).
  16 +
  17 + Race conditions exist if the userspace-part directly modifies the
  18 + Interrupt Enable Register while in operation. The consequences are
  19 + that certain interrupts would fail to be enabled or disabled. For
  20 + this reason, the userspace-part should only directly modify the
  21 + Interrupt Enable Register at the beginning (to get things going).
  22 + The userspace-part can safely disable interrupts at any time using
  23 + a write to the UIO device.
  24 +*/
  25 +
  26 +#include <linux/device.h>
  27 +#include <linux/module.h>
  28 +#include <linux/pci.h>
  29 +#include <linux/uio_driver.h>
  30 +#include <linux/io.h>
  31 +
  32 +/* ID's for SERCOS III PCI card (PLX 9030) */
  33 +#define SERCOS_SUB_VENDOR_ID 0x1971
  34 +#define SERCOS_SUB_SYSID_3530 0x3530
  35 +#define SERCOS_SUB_SYSID_3535 0x3535
  36 +#define SERCOS_SUB_SYSID_3780 0x3780
  37 +
  38 +/* Interrupt Enable Register */
  39 +#define IER0_OFFSET 0x08
  40 +
  41 +/* Interrupt Status Register */
  42 +#define ISR0_OFFSET 0x18
  43 +
  44 +struct sercos3_priv {
  45 + u32 ier0_cache;
  46 + spinlock_t ier0_cache_lock;
  47 +};
  48 +
  49 +/* this function assumes ier0_cache_lock is locked! */
  50 +static void sercos3_disable_interrupts(struct uio_info *info,
  51 + struct sercos3_priv *priv)
  52 +{
  53 + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
  54 +
  55 + /* add enabled interrupts to cache */
  56 + priv->ier0_cache |= ioread32(ier0);
  57 +
  58 + /* disable interrupts */
  59 + iowrite32(0, ier0);
  60 +}
  61 +
  62 +/* this function assumes ier0_cache_lock is locked! */
  63 +static void sercos3_enable_interrupts(struct uio_info *info,
  64 + struct sercos3_priv *priv)
  65 +{
  66 + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
  67 +
  68 + /* restore previously enabled interrupts */
  69 + iowrite32(ioread32(ier0) | priv->ier0_cache, ier0);
  70 + priv->ier0_cache = 0;
  71 +}
  72 +
  73 +static irqreturn_t sercos3_handler(int irq, struct uio_info *info)
  74 +{
  75 + struct sercos3_priv *priv = info->priv;
  76 + void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET;
  77 + void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
  78 +
  79 + if (!(ioread32(isr0) & ioread32(ier0)))
  80 + return IRQ_NONE;
  81 +
  82 + spin_lock(&priv->ier0_cache_lock);
  83 + sercos3_disable_interrupts(info, priv);
  84 + spin_unlock(&priv->ier0_cache_lock);
  85 +
  86 + return IRQ_HANDLED;
  87 +}
  88 +
  89 +static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on)
  90 +{
  91 + struct sercos3_priv *priv = info->priv;
  92 +
  93 + spin_lock_irq(&priv->ier0_cache_lock);
  94 + if (irq_on)
  95 + sercos3_enable_interrupts(info, priv);
  96 + else
  97 + sercos3_disable_interrupts(info, priv);
  98 + spin_unlock_irq(&priv->ier0_cache_lock);
  99 +
  100 + return 0;
  101 +}
  102 +
  103 +static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
  104 + int n, int pci_bar)
  105 +{
  106 + info->mem[n].addr = pci_resource_start(dev, pci_bar);
  107 + if (!info->mem[n].addr)
  108 + return -1;
  109 + info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar),
  110 + pci_resource_len(dev, pci_bar));
  111 + if (!info->mem[n].internal_addr)
  112 + return -1;
  113 + info->mem[n].size = pci_resource_len(dev, pci_bar);
  114 + info->mem[n].memtype = UIO_MEM_PHYS;
  115 + return 0;
  116 +}
  117 +
  118 +static int __devinit sercos3_pci_probe(struct pci_dev *dev,
  119 + const struct pci_device_id *id)
  120 +{
  121 + struct uio_info *info;
  122 + struct sercos3_priv *priv;
  123 + int i;
  124 +
  125 + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
  126 + if (!info)
  127 + return -ENOMEM;
  128 +
  129 + priv = kzalloc(sizeof(struct sercos3_priv), GFP_KERNEL);
  130 + if (!priv)
  131 + goto out_free;
  132 +
  133 + if (pci_enable_device(dev))
  134 + goto out_free_priv;
  135 +
  136 + if (pci_request_regions(dev, "sercos3"))
  137 + goto out_disable;
  138 +
  139 + /* we only need PCI BAR's 0, 2, 3, 4, 5 */
  140 + if (sercos3_setup_iomem(dev, info, 0, 0))
  141 + goto out_unmap;
  142 + if (sercos3_setup_iomem(dev, info, 1, 2))
  143 + goto out_unmap;
  144 + if (sercos3_setup_iomem(dev, info, 2, 3))
  145 + goto out_unmap;
  146 + if (sercos3_setup_iomem(dev, info, 3, 4))
  147 + goto out_unmap;
  148 + if (sercos3_setup_iomem(dev, info, 4, 5))
  149 + goto out_unmap;
  150 +
  151 + spin_lock_init(&priv->ier0_cache_lock);
  152 + info->priv = priv;
  153 + info->name = "Sercos_III_PCI";
  154 + info->version = "0.0.1";
  155 + info->irq = dev->irq;
  156 + info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
  157 + info->handler = sercos3_handler;
  158 + info->irqcontrol = sercos3_irqcontrol;
  159 +
  160 + pci_set_drvdata(dev, info);
  161 +
  162 + if (uio_register_device(&dev->dev, info))
  163 + goto out_unmap;
  164 +
  165 + return 0;
  166 +
  167 +out_unmap:
  168 + for (i = 0; i < 5; i++) {
  169 + if (info->mem[i].internal_addr)
  170 + iounmap(info->mem[i].internal_addr);
  171 + }
  172 + pci_release_regions(dev);
  173 +out_disable:
  174 + pci_disable_device(dev);
  175 +out_free_priv:
  176 + kfree(priv);
  177 +out_free:
  178 + kfree(info);
  179 + return -ENODEV;
  180 +}
  181 +
  182 +static void sercos3_pci_remove(struct pci_dev *dev)
  183 +{
  184 + struct uio_info *info = pci_get_drvdata(dev);
  185 + int i;
  186 +
  187 + uio_unregister_device(info);
  188 + pci_release_regions(dev);
  189 + pci_disable_device(dev);
  190 + pci_set_drvdata(dev, NULL);
  191 + for (i = 0; i < 5; i++) {
  192 + if (info->mem[i].internal_addr)
  193 + iounmap(info->mem[i].internal_addr);
  194 + }
  195 + kfree(info->priv);
  196 + kfree(info);
  197 +}
  198 +
  199 +static struct pci_device_id sercos3_pci_ids[] __devinitdata = {
  200 + {
  201 + .vendor = PCI_VENDOR_ID_PLX,
  202 + .device = PCI_DEVICE_ID_PLX_9030,
  203 + .subvendor = SERCOS_SUB_VENDOR_ID,
  204 + .subdevice = SERCOS_SUB_SYSID_3530,
  205 + },
  206 + {
  207 + .vendor = PCI_VENDOR_ID_PLX,
  208 + .device = PCI_DEVICE_ID_PLX_9030,
  209 + .subvendor = SERCOS_SUB_VENDOR_ID,
  210 + .subdevice = SERCOS_SUB_SYSID_3535,
  211 + },
  212 + {
  213 + .vendor = PCI_VENDOR_ID_PLX,
  214 + .device = PCI_DEVICE_ID_PLX_9030,
  215 + .subvendor = SERCOS_SUB_VENDOR_ID,
  216 + .subdevice = SERCOS_SUB_SYSID_3780,
  217 + },
  218 + { 0, }
  219 +};
  220 +
  221 +static struct pci_driver sercos3_pci_driver = {
  222 + .name = "sercos3",
  223 + .id_table = sercos3_pci_ids,
  224 + .probe = sercos3_pci_probe,
  225 + .remove = sercos3_pci_remove,
  226 +};
  227 +
  228 +static int __init sercos3_init_module(void)
  229 +{
  230 + return pci_register_driver(&sercos3_pci_driver);
  231 +}
  232 +
  233 +static void __exit sercos3_exit_module(void)
  234 +{
  235 + pci_unregister_driver(&sercos3_pci_driver);
  236 +}
  237 +
  238 +module_init(sercos3_init_module);
  239 +module_exit(sercos3_exit_module);
  240 +
  241 +MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
  242 +MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
  243 +MODULE_LICENSE("GPL v2");