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