18f314cfcSHans J. Koch /* 28f314cfcSHans J. Koch * UIO driver for Hilscher NetX based fieldbus cards (cifX, comX). 38f314cfcSHans J. Koch * See http://www.hilscher.com for details. 48f314cfcSHans J. Koch * 5*318af55dSHans J. Koch * (C) 2007 Hans J. Koch <hjk@hansjkoch.de> 68f314cfcSHans J. Koch * (C) 2008 Manuel Traut <manut@linutronix.de> 78f314cfcSHans J. Koch * 88f314cfcSHans J. Koch * Licensed under GPL version 2 only. 98f314cfcSHans J. Koch * 108f314cfcSHans J. Koch */ 118f314cfcSHans J. Koch 128f314cfcSHans J. Koch #include <linux/device.h> 138f314cfcSHans J. Koch #include <linux/io.h> 148f314cfcSHans J. Koch #include <linux/module.h> 158f314cfcSHans J. Koch #include <linux/pci.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 178f314cfcSHans J. Koch #include <linux/uio_driver.h> 188f314cfcSHans J. Koch 198f314cfcSHans J. Koch #define PCI_VENDOR_ID_HILSCHER 0x15CF 208f314cfcSHans J. Koch #define PCI_DEVICE_ID_HILSCHER_NETX 0x0000 218f314cfcSHans J. Koch #define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235 228f314cfcSHans J. Koch #define PCI_SUBDEVICE_ID_NXPCA 0x3335 238f314cfcSHans J. Koch 248f314cfcSHans J. Koch #define DPM_HOST_INT_EN0 0xfff0 258f314cfcSHans J. Koch #define DPM_HOST_INT_STAT0 0xffe0 268f314cfcSHans J. Koch 278f314cfcSHans J. Koch #define DPM_HOST_INT_MASK 0xe600ffff 288f314cfcSHans J. Koch #define DPM_HOST_INT_GLOBAL_EN 0x80000000 298f314cfcSHans J. Koch 308f314cfcSHans J. Koch static irqreturn_t netx_handler(int irq, struct uio_info *dev_info) 318f314cfcSHans J. Koch { 328f314cfcSHans J. Koch void __iomem *int_enable_reg = dev_info->mem[0].internal_addr 338f314cfcSHans J. Koch + DPM_HOST_INT_EN0; 348f314cfcSHans J. Koch void __iomem *int_status_reg = dev_info->mem[0].internal_addr 358f314cfcSHans J. Koch + DPM_HOST_INT_STAT0; 368f314cfcSHans J. Koch 378f314cfcSHans J. Koch /* Is one of our interrupts enabled and active ? */ 388f314cfcSHans J. Koch if (!(ioread32(int_enable_reg) & ioread32(int_status_reg) 398f314cfcSHans J. Koch & DPM_HOST_INT_MASK)) 408f314cfcSHans J. Koch return IRQ_NONE; 418f314cfcSHans J. Koch 428f314cfcSHans J. Koch /* Disable interrupt */ 438f314cfcSHans J. Koch iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN, 448f314cfcSHans J. Koch int_enable_reg); 458f314cfcSHans J. Koch return IRQ_HANDLED; 468f314cfcSHans J. Koch } 478f314cfcSHans J. Koch 488f314cfcSHans J. Koch static int __devinit netx_pci_probe(struct pci_dev *dev, 498f314cfcSHans J. Koch const struct pci_device_id *id) 508f314cfcSHans J. Koch { 518f314cfcSHans J. Koch struct uio_info *info; 528f314cfcSHans J. Koch int bar; 538f314cfcSHans J. Koch 548f314cfcSHans J. Koch info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 558f314cfcSHans J. Koch if (!info) 568f314cfcSHans J. Koch return -ENOMEM; 578f314cfcSHans J. Koch 588f314cfcSHans J. Koch if (pci_enable_device(dev)) 598f314cfcSHans J. Koch goto out_free; 608f314cfcSHans J. Koch 618f314cfcSHans J. Koch if (pci_request_regions(dev, "netx")) 628f314cfcSHans J. Koch goto out_disable; 638f314cfcSHans J. Koch 648f314cfcSHans J. Koch switch (id->device) { 658f314cfcSHans J. Koch case PCI_DEVICE_ID_HILSCHER_NETX: 668f314cfcSHans J. Koch bar = 0; 678f314cfcSHans J. Koch info->name = "netx"; 688f314cfcSHans J. Koch break; 698f314cfcSHans J. Koch default: 708f314cfcSHans J. Koch bar = 2; 718f314cfcSHans J. Koch info->name = "netx_plx"; 728f314cfcSHans J. Koch } 738f314cfcSHans J. Koch 748f314cfcSHans J. Koch /* BAR0 or 2 points to the card's dual port memory */ 758f314cfcSHans J. Koch info->mem[0].addr = pci_resource_start(dev, bar); 768f314cfcSHans J. Koch if (!info->mem[0].addr) 778f314cfcSHans J. Koch goto out_release; 788f314cfcSHans J. Koch info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar), 798f314cfcSHans J. Koch pci_resource_len(dev, bar)); 808f314cfcSHans J. Koch 818f314cfcSHans J. Koch if (!info->mem[0].internal_addr) 828f314cfcSHans J. Koch goto out_release; 838f314cfcSHans J. Koch 848f314cfcSHans J. Koch info->mem[0].size = pci_resource_len(dev, bar); 858f314cfcSHans J. Koch info->mem[0].memtype = UIO_MEM_PHYS; 868f314cfcSHans J. Koch info->irq = dev->irq; 878f314cfcSHans J. Koch info->irq_flags = IRQF_SHARED; 888f314cfcSHans J. Koch info->handler = netx_handler; 898f314cfcSHans J. Koch info->version = "0.0.1"; 908f314cfcSHans J. Koch 918f314cfcSHans J. Koch /* Make sure all interrupts are disabled */ 928f314cfcSHans J. Koch iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 938f314cfcSHans J. Koch 948f314cfcSHans J. Koch if (uio_register_device(&dev->dev, info)) 958f314cfcSHans J. Koch goto out_unmap; 968f314cfcSHans J. Koch 978f314cfcSHans J. Koch pci_set_drvdata(dev, info); 988f314cfcSHans J. Koch dev_info(&dev->dev, "Found %s card, registered UIO device.\n", 998f314cfcSHans J. Koch info->name); 1008f314cfcSHans J. Koch 1018f314cfcSHans J. Koch return 0; 1028f314cfcSHans J. Koch 1038f314cfcSHans J. Koch out_unmap: 1048f314cfcSHans J. Koch iounmap(info->mem[0].internal_addr); 1058f314cfcSHans J. Koch out_release: 1068f314cfcSHans J. Koch pci_release_regions(dev); 1078f314cfcSHans J. Koch out_disable: 1088f314cfcSHans J. Koch pci_disable_device(dev); 1098f314cfcSHans J. Koch out_free: 1108f314cfcSHans J. Koch kfree(info); 1118f314cfcSHans J. Koch return -ENODEV; 1128f314cfcSHans J. Koch } 1138f314cfcSHans J. Koch 1148f314cfcSHans J. Koch static void netx_pci_remove(struct pci_dev *dev) 1158f314cfcSHans J. Koch { 1168f314cfcSHans J. Koch struct uio_info *info = pci_get_drvdata(dev); 1178f314cfcSHans J. Koch 1188f314cfcSHans J. Koch /* Disable all interrupts */ 1198f314cfcSHans J. Koch iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 1208f314cfcSHans J. Koch uio_unregister_device(info); 1218f314cfcSHans J. Koch pci_release_regions(dev); 1228f314cfcSHans J. Koch pci_disable_device(dev); 1238f314cfcSHans J. Koch pci_set_drvdata(dev, NULL); 1248f314cfcSHans J. Koch iounmap(info->mem[0].internal_addr); 1258f314cfcSHans J. Koch 1268f314cfcSHans J. Koch kfree(info); 1278f314cfcSHans J. Koch } 1288f314cfcSHans J. Koch 1298f314cfcSHans J. Koch static struct pci_device_id netx_pci_ids[] = { 1308f314cfcSHans J. Koch { 1318f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_HILSCHER, 1328f314cfcSHans J. Koch .device = PCI_DEVICE_ID_HILSCHER_NETX, 1338f314cfcSHans J. Koch .subvendor = 0, 1348f314cfcSHans J. Koch .subdevice = 0, 1358f314cfcSHans J. Koch }, 1368f314cfcSHans J. Koch { 1378f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_PLX, 1388f314cfcSHans J. Koch .device = PCI_DEVICE_ID_PLX_9030, 1398f314cfcSHans J. Koch .subvendor = PCI_VENDOR_ID_PLX, 1408f314cfcSHans J. Koch .subdevice = PCI_SUBDEVICE_ID_NXSB_PCA, 1418f314cfcSHans J. Koch }, 1428f314cfcSHans J. Koch { 1438f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_PLX, 1448f314cfcSHans J. Koch .device = PCI_DEVICE_ID_PLX_9030, 1458f314cfcSHans J. Koch .subvendor = PCI_VENDOR_ID_PLX, 1468f314cfcSHans J. Koch .subdevice = PCI_SUBDEVICE_ID_NXPCA, 1478f314cfcSHans J. Koch }, 1488f314cfcSHans J. Koch { 0, } 1498f314cfcSHans J. Koch }; 1508f314cfcSHans J. Koch 1518f314cfcSHans J. Koch static struct pci_driver netx_pci_driver = { 1528f314cfcSHans J. Koch .name = "netx", 1538f314cfcSHans J. Koch .id_table = netx_pci_ids, 1548f314cfcSHans J. Koch .probe = netx_pci_probe, 1558f314cfcSHans J. Koch .remove = netx_pci_remove, 1568f314cfcSHans J. Koch }; 1578f314cfcSHans J. Koch 1588f314cfcSHans J. Koch static int __init netx_init_module(void) 1598f314cfcSHans J. Koch { 1608f314cfcSHans J. Koch return pci_register_driver(&netx_pci_driver); 1618f314cfcSHans J. Koch } 1628f314cfcSHans J. Koch 1638f314cfcSHans J. Koch static void __exit netx_exit_module(void) 1648f314cfcSHans J. Koch { 1658f314cfcSHans J. Koch pci_unregister_driver(&netx_pci_driver); 1668f314cfcSHans J. Koch } 1678f314cfcSHans J. Koch 1688f314cfcSHans J. Koch module_init(netx_init_module); 1698f314cfcSHans J. Koch module_exit(netx_exit_module); 1708f314cfcSHans J. Koch 1718f314cfcSHans J. Koch MODULE_DEVICE_TABLE(pci, netx_pci_ids); 1728f314cfcSHans J. Koch MODULE_LICENSE("GPL v2"); 1738f314cfcSHans J. Koch MODULE_AUTHOR("Hans J. Koch, Manuel Traut"); 174