1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * UIO driver for Hilscher NetX based fieldbus cards (cifX, comX). 4 * See http://www.hilscher.com for details. 5 * 6 * (C) 2007 Hans J. Koch <hjk@hansjkoch.de> 7 * (C) 2008 Manuel Traut <manut@linutronix.de> 8 * 9 */ 10 11 #include <linux/device.h> 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/pci.h> 15 #include <linux/slab.h> 16 #include <linux/uio_driver.h> 17 18 #define PCI_VENDOR_ID_HILSCHER 0x15CF 19 #define PCI_DEVICE_ID_HILSCHER_NETX 0x0000 20 #define PCI_DEVICE_ID_HILSCHER_NETPLC 0x0010 21 #define PCI_SUBDEVICE_ID_NETPLC_RAM 0x0000 22 #define PCI_SUBDEVICE_ID_NETPLC_FLASH 0x0001 23 #define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235 24 #define PCI_SUBDEVICE_ID_NXPCA 0x3335 25 26 #define DPM_HOST_INT_EN0 0xfff0 27 #define DPM_HOST_INT_STAT0 0xffe0 28 29 #define DPM_HOST_INT_MASK 0xe600ffff 30 #define DPM_HOST_INT_GLOBAL_EN 0x80000000 31 32 static irqreturn_t netx_handler(int irq, struct uio_info *dev_info) 33 { 34 void __iomem *int_enable_reg = dev_info->mem[0].internal_addr 35 + DPM_HOST_INT_EN0; 36 void __iomem *int_status_reg = dev_info->mem[0].internal_addr 37 + DPM_HOST_INT_STAT0; 38 39 /* Is one of our interrupts enabled and active ? */ 40 if (!(ioread32(int_enable_reg) & ioread32(int_status_reg) 41 & DPM_HOST_INT_MASK)) 42 return IRQ_NONE; 43 44 /* Disable interrupt */ 45 iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN, 46 int_enable_reg); 47 return IRQ_HANDLED; 48 } 49 50 static int netx_pci_probe(struct pci_dev *dev, 51 const struct pci_device_id *id) 52 { 53 struct uio_info *info; 54 int bar; 55 56 info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 57 if (!info) 58 return -ENOMEM; 59 60 if (pci_enable_device(dev)) 61 goto out_free; 62 63 if (pci_request_regions(dev, "netx")) 64 goto out_disable; 65 66 switch (id->device) { 67 case PCI_DEVICE_ID_HILSCHER_NETX: 68 bar = 0; 69 info->name = "netx"; 70 break; 71 case PCI_DEVICE_ID_HILSCHER_NETPLC: 72 bar = 0; 73 info->name = "netplc"; 74 break; 75 default: 76 bar = 2; 77 info->name = "netx_plx"; 78 } 79 80 /* BAR0 or 2 points to the card's dual port memory */ 81 info->mem[0].addr = pci_resource_start(dev, bar); 82 if (!info->mem[0].addr) 83 goto out_release; 84 info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar), 85 pci_resource_len(dev, bar)); 86 87 if (!info->mem[0].internal_addr) 88 goto out_release; 89 90 info->mem[0].size = pci_resource_len(dev, bar); 91 info->mem[0].memtype = UIO_MEM_PHYS; 92 info->irq = dev->irq; 93 info->irq_flags = IRQF_SHARED; 94 info->handler = netx_handler; 95 info->version = "0.0.1"; 96 97 /* Make sure all interrupts are disabled */ 98 iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 99 100 if (uio_register_device(&dev->dev, info)) 101 goto out_unmap; 102 103 pci_set_drvdata(dev, info); 104 dev_info(&dev->dev, "Found %s card, registered UIO device.\n", 105 info->name); 106 107 return 0; 108 109 out_unmap: 110 iounmap(info->mem[0].internal_addr); 111 out_release: 112 pci_release_regions(dev); 113 out_disable: 114 pci_disable_device(dev); 115 out_free: 116 kfree(info); 117 return -ENODEV; 118 } 119 120 static void netx_pci_remove(struct pci_dev *dev) 121 { 122 struct uio_info *info = pci_get_drvdata(dev); 123 124 /* Disable all interrupts */ 125 iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 126 uio_unregister_device(info); 127 pci_release_regions(dev); 128 pci_disable_device(dev); 129 iounmap(info->mem[0].internal_addr); 130 131 kfree(info); 132 } 133 134 static struct pci_device_id netx_pci_ids[] = { 135 { 136 .vendor = PCI_VENDOR_ID_HILSCHER, 137 .device = PCI_DEVICE_ID_HILSCHER_NETX, 138 .subvendor = 0, 139 .subdevice = 0, 140 }, 141 { 142 .vendor = PCI_VENDOR_ID_HILSCHER, 143 .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 144 .subvendor = PCI_VENDOR_ID_HILSCHER, 145 .subdevice = PCI_SUBDEVICE_ID_NETPLC_RAM, 146 }, 147 { 148 .vendor = PCI_VENDOR_ID_HILSCHER, 149 .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 150 .subvendor = PCI_VENDOR_ID_HILSCHER, 151 .subdevice = PCI_SUBDEVICE_ID_NETPLC_FLASH, 152 }, 153 { 154 .vendor = PCI_VENDOR_ID_PLX, 155 .device = PCI_DEVICE_ID_PLX_9030, 156 .subvendor = PCI_VENDOR_ID_PLX, 157 .subdevice = PCI_SUBDEVICE_ID_NXSB_PCA, 158 }, 159 { 160 .vendor = PCI_VENDOR_ID_PLX, 161 .device = PCI_DEVICE_ID_PLX_9030, 162 .subvendor = PCI_VENDOR_ID_PLX, 163 .subdevice = PCI_SUBDEVICE_ID_NXPCA, 164 }, 165 { 0, } 166 }; 167 168 static struct pci_driver netx_pci_driver = { 169 .name = "netx", 170 .id_table = netx_pci_ids, 171 .probe = netx_pci_probe, 172 .remove = netx_pci_remove, 173 }; 174 175 module_pci_driver(netx_pci_driver); 176 MODULE_DEVICE_TABLE(pci, netx_pci_ids); 177 MODULE_LICENSE("GPL v2"); 178 MODULE_AUTHOR("Hans J. Koch, Manuel Traut"); 179