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