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