Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* sercos3: UIO driver for the Automata Sercos III PCI card
0003 
0004    Copyright (C) 2008 Linutronix GmbH
0005      Author: John Ogness <john.ogness@linutronix.de>
0006 
0007    This is a straight-forward UIO driver, where interrupts are disabled
0008    by the interrupt handler and re-enabled via a write to the UIO device
0009    by the userspace-part.
0010 
0011    The only part that may seem odd is the use of a logical OR when
0012    storing and restoring enabled interrupts. This is done because the
0013    userspace-part could directly modify the Interrupt Enable Register
0014    at any time. To reduce possible conflicts, the kernel driver uses
0015    a logical OR to make more controlled changes (rather than blindly
0016    overwriting previous values).
0017 
0018    Race conditions exist if the userspace-part directly modifies the
0019    Interrupt Enable Register while in operation. The consequences are
0020    that certain interrupts would fail to be enabled or disabled. For
0021    this reason, the userspace-part should only directly modify the
0022    Interrupt Enable Register at the beginning (to get things going).
0023    The userspace-part can safely disable interrupts at any time using
0024    a write to the UIO device.
0025 */
0026 
0027 #include <linux/device.h>
0028 #include <linux/module.h>
0029 #include <linux/pci.h>
0030 #include <linux/uio_driver.h>
0031 #include <linux/io.h>
0032 #include <linux/slab.h>
0033 
0034 /* ID's for SERCOS III PCI card (PLX 9030) */
0035 #define SERCOS_SUB_VENDOR_ID  0x1971
0036 #define SERCOS_SUB_SYSID_3530 0x3530
0037 #define SERCOS_SUB_SYSID_3535 0x3535
0038 #define SERCOS_SUB_SYSID_3780 0x3780
0039 
0040 /* Interrupt Enable Register */
0041 #define IER0_OFFSET 0x08
0042 
0043 /* Interrupt Status Register */
0044 #define ISR0_OFFSET 0x18
0045 
0046 struct sercos3_priv {
0047     u32 ier0_cache;
0048     spinlock_t ier0_cache_lock;
0049 };
0050 
0051 /* this function assumes ier0_cache_lock is locked! */
0052 static void sercos3_disable_interrupts(struct uio_info *info,
0053                        struct sercos3_priv *priv)
0054 {
0055     void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
0056 
0057     /* add enabled interrupts to cache */
0058     priv->ier0_cache |= ioread32(ier0);
0059 
0060     /* disable interrupts */
0061     iowrite32(0, ier0);
0062 }
0063 
0064 /* this function assumes ier0_cache_lock is locked! */
0065 static void sercos3_enable_interrupts(struct uio_info *info,
0066                       struct sercos3_priv *priv)
0067 {
0068     void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
0069 
0070     /* restore previously enabled interrupts */
0071     iowrite32(ioread32(ier0) | priv->ier0_cache, ier0);
0072     priv->ier0_cache = 0;
0073 }
0074 
0075 static irqreturn_t sercos3_handler(int irq, struct uio_info *info)
0076 {
0077     struct sercos3_priv *priv = info->priv;
0078     void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET;
0079     void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
0080 
0081     if (!(ioread32(isr0) & ioread32(ier0)))
0082         return IRQ_NONE;
0083 
0084     spin_lock(&priv->ier0_cache_lock);
0085     sercos3_disable_interrupts(info, priv);
0086     spin_unlock(&priv->ier0_cache_lock);
0087 
0088     return IRQ_HANDLED;
0089 }
0090 
0091 static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on)
0092 {
0093     struct sercos3_priv *priv = info->priv;
0094 
0095     spin_lock_irq(&priv->ier0_cache_lock);
0096     if (irq_on)
0097         sercos3_enable_interrupts(info, priv);
0098     else
0099         sercos3_disable_interrupts(info, priv);
0100     spin_unlock_irq(&priv->ier0_cache_lock);
0101 
0102     return 0;
0103 }
0104 
0105 static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
0106                    int n, int pci_bar)
0107 {
0108     info->mem[n].addr = pci_resource_start(dev, pci_bar);
0109     if (!info->mem[n].addr)
0110         return -1;
0111     info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar),
0112                          pci_resource_len(dev, pci_bar));
0113     if (!info->mem[n].internal_addr)
0114         return -1;
0115     info->mem[n].size = pci_resource_len(dev, pci_bar);
0116     info->mem[n].memtype = UIO_MEM_PHYS;
0117     return 0;
0118 }
0119 
0120 static int sercos3_pci_probe(struct pci_dev *dev,
0121                        const struct pci_device_id *id)
0122 {
0123     struct uio_info *info;
0124     struct sercos3_priv *priv;
0125     int i;
0126 
0127     info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
0128     if (!info)
0129         return -ENOMEM;
0130 
0131     priv = devm_kzalloc(&dev->dev, sizeof(struct sercos3_priv), GFP_KERNEL);
0132     if (!priv)
0133         return -ENOMEM;
0134 
0135     if (pci_enable_device(dev))
0136         return -ENODEV;
0137 
0138     if (pci_request_regions(dev, "sercos3"))
0139         goto out_disable;
0140 
0141     /* we only need PCI BAR's 0, 2, 3, 4, 5 */
0142     if (sercos3_setup_iomem(dev, info, 0, 0))
0143         goto out_unmap;
0144     if (sercos3_setup_iomem(dev, info, 1, 2))
0145         goto out_unmap;
0146     if (sercos3_setup_iomem(dev, info, 2, 3))
0147         goto out_unmap;
0148     if (sercos3_setup_iomem(dev, info, 3, 4))
0149         goto out_unmap;
0150     if (sercos3_setup_iomem(dev, info, 4, 5))
0151         goto out_unmap;
0152 
0153     spin_lock_init(&priv->ier0_cache_lock);
0154     info->priv = priv;
0155     info->name = "Sercos_III_PCI";
0156     info->version = "0.0.1";
0157     info->irq = dev->irq;
0158     info->irq_flags = IRQF_SHARED;
0159     info->handler = sercos3_handler;
0160     info->irqcontrol = sercos3_irqcontrol;
0161 
0162     pci_set_drvdata(dev, info);
0163 
0164     if (uio_register_device(&dev->dev, info))
0165         goto out_unmap;
0166 
0167     return 0;
0168 
0169 out_unmap:
0170     for (i = 0; i < 5; i++) {
0171         if (info->mem[i].internal_addr)
0172             iounmap(info->mem[i].internal_addr);
0173     }
0174     pci_release_regions(dev);
0175 out_disable:
0176     pci_disable_device(dev);
0177     return -ENODEV;
0178 }
0179 
0180 static void sercos3_pci_remove(struct pci_dev *dev)
0181 {
0182     struct uio_info *info = pci_get_drvdata(dev);
0183     int i;
0184 
0185     uio_unregister_device(info);
0186     pci_release_regions(dev);
0187     pci_disable_device(dev);
0188     for (i = 0; i < 5; i++) {
0189         if (info->mem[i].internal_addr)
0190             iounmap(info->mem[i].internal_addr);
0191     }
0192 }
0193 
0194 static struct pci_device_id sercos3_pci_ids[] = {
0195     {
0196         .vendor =       PCI_VENDOR_ID_PLX,
0197         .device =       PCI_DEVICE_ID_PLX_9030,
0198         .subvendor =    SERCOS_SUB_VENDOR_ID,
0199         .subdevice =    SERCOS_SUB_SYSID_3530,
0200     },
0201     {
0202         .vendor =       PCI_VENDOR_ID_PLX,
0203         .device =       PCI_DEVICE_ID_PLX_9030,
0204         .subvendor =    SERCOS_SUB_VENDOR_ID,
0205         .subdevice =    SERCOS_SUB_SYSID_3535,
0206     },
0207     {
0208         .vendor =       PCI_VENDOR_ID_PLX,
0209         .device =       PCI_DEVICE_ID_PLX_9030,
0210         .subvendor =    SERCOS_SUB_VENDOR_ID,
0211         .subdevice =    SERCOS_SUB_SYSID_3780,
0212     },
0213     { 0, }
0214 };
0215 
0216 static struct pci_driver sercos3_pci_driver = {
0217     .name = "sercos3",
0218     .id_table = sercos3_pci_ids,
0219     .probe = sercos3_pci_probe,
0220     .remove = sercos3_pci_remove,
0221 };
0222 
0223 module_pci_driver(sercos3_pci_driver);
0224 MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
0225 MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
0226 MODULE_LICENSE("GPL v2");