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 * 5318af55dSHans 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 21d8408aefSDaniel Trautmann #define PCI_DEVICE_ID_HILSCHER_NETPLC 0x0010 22d8408aefSDaniel Trautmann #define PCI_SUBDEVICE_ID_NETPLC_RAM 0x0000 23d8408aefSDaniel Trautmann #define PCI_SUBDEVICE_ID_NETPLC_FLASH 0x0001 248f314cfcSHans J. Koch #define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235 258f314cfcSHans J. Koch #define PCI_SUBDEVICE_ID_NXPCA 0x3335 268f314cfcSHans J. Koch 278f314cfcSHans J. Koch #define DPM_HOST_INT_EN0 0xfff0 288f314cfcSHans J. Koch #define DPM_HOST_INT_STAT0 0xffe0 298f314cfcSHans J. Koch 308f314cfcSHans J. Koch #define DPM_HOST_INT_MASK 0xe600ffff 318f314cfcSHans J. Koch #define DPM_HOST_INT_GLOBAL_EN 0x80000000 328f314cfcSHans J. Koch 338f314cfcSHans J. Koch static irqreturn_t netx_handler(int irq, struct uio_info *dev_info) 348f314cfcSHans J. Koch { 358f314cfcSHans J. Koch void __iomem *int_enable_reg = dev_info->mem[0].internal_addr 368f314cfcSHans J. Koch + DPM_HOST_INT_EN0; 378f314cfcSHans J. Koch void __iomem *int_status_reg = dev_info->mem[0].internal_addr 388f314cfcSHans J. Koch + DPM_HOST_INT_STAT0; 398f314cfcSHans J. Koch 408f314cfcSHans J. Koch /* Is one of our interrupts enabled and active ? */ 418f314cfcSHans J. Koch if (!(ioread32(int_enable_reg) & ioread32(int_status_reg) 428f314cfcSHans J. Koch & DPM_HOST_INT_MASK)) 438f314cfcSHans J. Koch return IRQ_NONE; 448f314cfcSHans J. Koch 458f314cfcSHans J. Koch /* Disable interrupt */ 468f314cfcSHans J. Koch iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN, 478f314cfcSHans J. Koch int_enable_reg); 488f314cfcSHans J. Koch return IRQ_HANDLED; 498f314cfcSHans J. Koch } 508f314cfcSHans J. Koch 51*b17b75bbSBill Pemberton static int netx_pci_probe(struct pci_dev *dev, 528f314cfcSHans J. Koch const struct pci_device_id *id) 538f314cfcSHans J. Koch { 548f314cfcSHans J. Koch struct uio_info *info; 558f314cfcSHans J. Koch int bar; 568f314cfcSHans J. Koch 578f314cfcSHans J. Koch info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); 588f314cfcSHans J. Koch if (!info) 598f314cfcSHans J. Koch return -ENOMEM; 608f314cfcSHans J. Koch 618f314cfcSHans J. Koch if (pci_enable_device(dev)) 628f314cfcSHans J. Koch goto out_free; 638f314cfcSHans J. Koch 648f314cfcSHans J. Koch if (pci_request_regions(dev, "netx")) 658f314cfcSHans J. Koch goto out_disable; 668f314cfcSHans J. Koch 678f314cfcSHans J. Koch switch (id->device) { 688f314cfcSHans J. Koch case PCI_DEVICE_ID_HILSCHER_NETX: 698f314cfcSHans J. Koch bar = 0; 708f314cfcSHans J. Koch info->name = "netx"; 718f314cfcSHans J. Koch break; 72d8408aefSDaniel Trautmann case PCI_DEVICE_ID_HILSCHER_NETPLC: 73d8408aefSDaniel Trautmann bar = 0; 74d8408aefSDaniel Trautmann info->name = "netplc"; 75d8408aefSDaniel Trautmann break; 768f314cfcSHans J. Koch default: 778f314cfcSHans J. Koch bar = 2; 788f314cfcSHans J. Koch info->name = "netx_plx"; 798f314cfcSHans J. Koch } 808f314cfcSHans J. Koch 818f314cfcSHans J. Koch /* BAR0 or 2 points to the card's dual port memory */ 828f314cfcSHans J. Koch info->mem[0].addr = pci_resource_start(dev, bar); 838f314cfcSHans J. Koch if (!info->mem[0].addr) 848f314cfcSHans J. Koch goto out_release; 858f314cfcSHans J. Koch info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar), 868f314cfcSHans J. Koch pci_resource_len(dev, bar)); 878f314cfcSHans J. Koch 888f314cfcSHans J. Koch if (!info->mem[0].internal_addr) 898f314cfcSHans J. Koch goto out_release; 908f314cfcSHans J. Koch 918f314cfcSHans J. Koch info->mem[0].size = pci_resource_len(dev, bar); 928f314cfcSHans J. Koch info->mem[0].memtype = UIO_MEM_PHYS; 938f314cfcSHans J. Koch info->irq = dev->irq; 948f314cfcSHans J. Koch info->irq_flags = IRQF_SHARED; 958f314cfcSHans J. Koch info->handler = netx_handler; 968f314cfcSHans J. Koch info->version = "0.0.1"; 978f314cfcSHans J. Koch 988f314cfcSHans J. Koch /* Make sure all interrupts are disabled */ 998f314cfcSHans J. Koch iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 1008f314cfcSHans J. Koch 1018f314cfcSHans J. Koch if (uio_register_device(&dev->dev, info)) 1028f314cfcSHans J. Koch goto out_unmap; 1038f314cfcSHans J. Koch 1048f314cfcSHans J. Koch pci_set_drvdata(dev, info); 1058f314cfcSHans J. Koch dev_info(&dev->dev, "Found %s card, registered UIO device.\n", 1068f314cfcSHans J. Koch info->name); 1078f314cfcSHans J. Koch 1088f314cfcSHans J. Koch return 0; 1098f314cfcSHans J. Koch 1108f314cfcSHans J. Koch out_unmap: 1118f314cfcSHans J. Koch iounmap(info->mem[0].internal_addr); 1128f314cfcSHans J. Koch out_release: 1138f314cfcSHans J. Koch pci_release_regions(dev); 1148f314cfcSHans J. Koch out_disable: 1158f314cfcSHans J. Koch pci_disable_device(dev); 1168f314cfcSHans J. Koch out_free: 1178f314cfcSHans J. Koch kfree(info); 1188f314cfcSHans J. Koch return -ENODEV; 1198f314cfcSHans J. Koch } 1208f314cfcSHans J. Koch 1218f314cfcSHans J. Koch static void netx_pci_remove(struct pci_dev *dev) 1228f314cfcSHans J. Koch { 1238f314cfcSHans J. Koch struct uio_info *info = pci_get_drvdata(dev); 1248f314cfcSHans J. Koch 1258f314cfcSHans J. Koch /* Disable all interrupts */ 1268f314cfcSHans J. Koch iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 1278f314cfcSHans J. Koch uio_unregister_device(info); 1288f314cfcSHans J. Koch pci_release_regions(dev); 1298f314cfcSHans J. Koch pci_disable_device(dev); 1308f314cfcSHans J. Koch pci_set_drvdata(dev, NULL); 1318f314cfcSHans J. Koch iounmap(info->mem[0].internal_addr); 1328f314cfcSHans J. Koch 1338f314cfcSHans J. Koch kfree(info); 1348f314cfcSHans J. Koch } 1358f314cfcSHans J. Koch 1368f314cfcSHans J. Koch static struct pci_device_id netx_pci_ids[] = { 1378f314cfcSHans J. Koch { 1388f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_HILSCHER, 1398f314cfcSHans J. Koch .device = PCI_DEVICE_ID_HILSCHER_NETX, 1408f314cfcSHans J. Koch .subvendor = 0, 1418f314cfcSHans J. Koch .subdevice = 0, 1428f314cfcSHans J. Koch }, 1438f314cfcSHans J. Koch { 144d8408aefSDaniel Trautmann .vendor = PCI_VENDOR_ID_HILSCHER, 145d8408aefSDaniel Trautmann .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 146d8408aefSDaniel Trautmann .subvendor = PCI_VENDOR_ID_HILSCHER, 147d8408aefSDaniel Trautmann .subdevice = PCI_SUBDEVICE_ID_NETPLC_RAM, 148d8408aefSDaniel Trautmann }, 149d8408aefSDaniel Trautmann { 150d8408aefSDaniel Trautmann .vendor = PCI_VENDOR_ID_HILSCHER, 151d8408aefSDaniel Trautmann .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 152d8408aefSDaniel Trautmann .subvendor = PCI_VENDOR_ID_HILSCHER, 153d8408aefSDaniel Trautmann .subdevice = PCI_SUBDEVICE_ID_NETPLC_FLASH, 154d8408aefSDaniel Trautmann }, 155d8408aefSDaniel Trautmann { 1568f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_PLX, 1578f314cfcSHans J. Koch .device = PCI_DEVICE_ID_PLX_9030, 1588f314cfcSHans J. Koch .subvendor = PCI_VENDOR_ID_PLX, 1598f314cfcSHans J. Koch .subdevice = PCI_SUBDEVICE_ID_NXSB_PCA, 1608f314cfcSHans J. Koch }, 1618f314cfcSHans J. Koch { 1628f314cfcSHans J. Koch .vendor = PCI_VENDOR_ID_PLX, 1638f314cfcSHans J. Koch .device = PCI_DEVICE_ID_PLX_9030, 1648f314cfcSHans J. Koch .subvendor = PCI_VENDOR_ID_PLX, 1658f314cfcSHans J. Koch .subdevice = PCI_SUBDEVICE_ID_NXPCA, 1668f314cfcSHans J. Koch }, 1678f314cfcSHans J. Koch { 0, } 1688f314cfcSHans J. Koch }; 1698f314cfcSHans J. Koch 1708f314cfcSHans J. Koch static struct pci_driver netx_pci_driver = { 1718f314cfcSHans J. Koch .name = "netx", 1728f314cfcSHans J. Koch .id_table = netx_pci_ids, 1738f314cfcSHans J. Koch .probe = netx_pci_probe, 1748f314cfcSHans J. Koch .remove = netx_pci_remove, 1758f314cfcSHans J. Koch }; 1768f314cfcSHans J. Koch 1778f314cfcSHans J. Koch static int __init netx_init_module(void) 1788f314cfcSHans J. Koch { 1798f314cfcSHans J. Koch return pci_register_driver(&netx_pci_driver); 1808f314cfcSHans J. Koch } 1818f314cfcSHans J. Koch 1828f314cfcSHans J. Koch static void __exit netx_exit_module(void) 1838f314cfcSHans J. Koch { 1848f314cfcSHans J. Koch pci_unregister_driver(&netx_pci_driver); 1858f314cfcSHans J. Koch } 1868f314cfcSHans J. Koch 1878f314cfcSHans J. Koch module_init(netx_init_module); 1888f314cfcSHans J. Koch module_exit(netx_exit_module); 1898f314cfcSHans J. Koch 1908f314cfcSHans J. Koch MODULE_DEVICE_TABLE(pci, netx_pci_ids); 1918f314cfcSHans J. Koch MODULE_LICENSE("GPL v2"); 1928f314cfcSHans J. Koch MODULE_AUTHOR("Hans J. Koch, Manuel Traut"); 193