162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * UIO driver for Hilscher NetX based fieldbus cards (cifX, comX). 462306a36Sopenharmony_ci * See http://www.hilscher.com for details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) 2007 Hans J. Koch <hjk@hansjkoch.de> 762306a36Sopenharmony_ci * (C) 2008 Manuel Traut <manut@linutronix.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/uio_driver.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define PCI_VENDOR_ID_HILSCHER 0x15CF 1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_HILSCHER_NETX 0x0000 2062306a36Sopenharmony_ci#define PCI_DEVICE_ID_HILSCHER_NETPLC 0x0010 2162306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_NETPLC_RAM 0x0000 2262306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_NETPLC_FLASH 0x0001 2362306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235 2462306a36Sopenharmony_ci#define PCI_SUBDEVICE_ID_NXPCA 0x3335 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DPM_HOST_INT_EN0 0xfff0 2762306a36Sopenharmony_ci#define DPM_HOST_INT_STAT0 0xffe0 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DPM_HOST_INT_MASK 0xe600ffff 3062306a36Sopenharmony_ci#define DPM_HOST_INT_GLOBAL_EN 0x80000000 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic irqreturn_t netx_handler(int irq, struct uio_info *dev_info) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci void __iomem *int_enable_reg = dev_info->mem[0].internal_addr 3562306a36Sopenharmony_ci + DPM_HOST_INT_EN0; 3662306a36Sopenharmony_ci void __iomem *int_status_reg = dev_info->mem[0].internal_addr 3762306a36Sopenharmony_ci + DPM_HOST_INT_STAT0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Is one of our interrupts enabled and active ? */ 4062306a36Sopenharmony_ci if (!(ioread32(int_enable_reg) & ioread32(int_status_reg) 4162306a36Sopenharmony_ci & DPM_HOST_INT_MASK)) 4262306a36Sopenharmony_ci return IRQ_NONE; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* Disable interrupt */ 4562306a36Sopenharmony_ci iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN, 4662306a36Sopenharmony_ci int_enable_reg); 4762306a36Sopenharmony_ci return IRQ_HANDLED; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int netx_pci_probe(struct pci_dev *dev, 5162306a36Sopenharmony_ci const struct pci_device_id *id) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct uio_info *info; 5462306a36Sopenharmony_ci int bar; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL); 5762306a36Sopenharmony_ci if (!info) 5862306a36Sopenharmony_ci return -ENOMEM; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (pci_enable_device(dev)) 6162306a36Sopenharmony_ci return -ENODEV; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (pci_request_regions(dev, "netx")) 6462306a36Sopenharmony_ci goto out_disable; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci switch (id->device) { 6762306a36Sopenharmony_ci case PCI_DEVICE_ID_HILSCHER_NETX: 6862306a36Sopenharmony_ci bar = 0; 6962306a36Sopenharmony_ci info->name = "netx"; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci case PCI_DEVICE_ID_HILSCHER_NETPLC: 7262306a36Sopenharmony_ci bar = 0; 7362306a36Sopenharmony_ci info->name = "netplc"; 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci default: 7662306a36Sopenharmony_ci bar = 2; 7762306a36Sopenharmony_ci info->name = "netx_plx"; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* BAR0 or 2 points to the card's dual port memory */ 8162306a36Sopenharmony_ci info->mem[0].addr = pci_resource_start(dev, bar); 8262306a36Sopenharmony_ci if (!info->mem[0].addr) 8362306a36Sopenharmony_ci goto out_release; 8462306a36Sopenharmony_ci info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar), 8562306a36Sopenharmony_ci pci_resource_len(dev, bar)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!info->mem[0].internal_addr) 8862306a36Sopenharmony_ci goto out_release; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci info->mem[0].size = pci_resource_len(dev, bar); 9162306a36Sopenharmony_ci info->mem[0].memtype = UIO_MEM_PHYS; 9262306a36Sopenharmony_ci info->irq = dev->irq; 9362306a36Sopenharmony_ci info->irq_flags = IRQF_SHARED; 9462306a36Sopenharmony_ci info->handler = netx_handler; 9562306a36Sopenharmony_ci info->version = "0.0.1"; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Make sure all interrupts are disabled */ 9862306a36Sopenharmony_ci iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (uio_register_device(&dev->dev, info)) 10162306a36Sopenharmony_ci goto out_unmap; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pci_set_drvdata(dev, info); 10462306a36Sopenharmony_ci dev_info(&dev->dev, "Found %s card, registered UIO device.\n", 10562306a36Sopenharmony_ci info->name); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciout_unmap: 11062306a36Sopenharmony_ci iounmap(info->mem[0].internal_addr); 11162306a36Sopenharmony_ciout_release: 11262306a36Sopenharmony_ci pci_release_regions(dev); 11362306a36Sopenharmony_ciout_disable: 11462306a36Sopenharmony_ci pci_disable_device(dev); 11562306a36Sopenharmony_ci return -ENODEV; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void netx_pci_remove(struct pci_dev *dev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct uio_info *info = pci_get_drvdata(dev); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Disable all interrupts */ 12362306a36Sopenharmony_ci iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); 12462306a36Sopenharmony_ci uio_unregister_device(info); 12562306a36Sopenharmony_ci pci_release_regions(dev); 12662306a36Sopenharmony_ci pci_disable_device(dev); 12762306a36Sopenharmony_ci iounmap(info->mem[0].internal_addr); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic struct pci_device_id netx_pci_ids[] = { 13162306a36Sopenharmony_ci { 13262306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_HILSCHER, 13362306a36Sopenharmony_ci .device = PCI_DEVICE_ID_HILSCHER_NETX, 13462306a36Sopenharmony_ci .subvendor = 0, 13562306a36Sopenharmony_ci .subdevice = 0, 13662306a36Sopenharmony_ci }, 13762306a36Sopenharmony_ci { 13862306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_HILSCHER, 13962306a36Sopenharmony_ci .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 14062306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_HILSCHER, 14162306a36Sopenharmony_ci .subdevice = PCI_SUBDEVICE_ID_NETPLC_RAM, 14262306a36Sopenharmony_ci }, 14362306a36Sopenharmony_ci { 14462306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_HILSCHER, 14562306a36Sopenharmony_ci .device = PCI_DEVICE_ID_HILSCHER_NETPLC, 14662306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_HILSCHER, 14762306a36Sopenharmony_ci .subdevice = PCI_SUBDEVICE_ID_NETPLC_FLASH, 14862306a36Sopenharmony_ci }, 14962306a36Sopenharmony_ci { 15062306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 15162306a36Sopenharmony_ci .device = PCI_DEVICE_ID_PLX_9030, 15262306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_PLX, 15362306a36Sopenharmony_ci .subdevice = PCI_SUBDEVICE_ID_NXSB_PCA, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci { 15662306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 15762306a36Sopenharmony_ci .device = PCI_DEVICE_ID_PLX_9030, 15862306a36Sopenharmony_ci .subvendor = PCI_VENDOR_ID_PLX, 15962306a36Sopenharmony_ci .subdevice = PCI_SUBDEVICE_ID_NXPCA, 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci { 0, } 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic struct pci_driver netx_pci_driver = { 16562306a36Sopenharmony_ci .name = "netx", 16662306a36Sopenharmony_ci .id_table = netx_pci_ids, 16762306a36Sopenharmony_ci .probe = netx_pci_probe, 16862306a36Sopenharmony_ci .remove = netx_pci_remove, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cimodule_pci_driver(netx_pci_driver); 17262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, netx_pci_ids); 17362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 17462306a36Sopenharmony_ciMODULE_AUTHOR("Hans J. Koch, Manuel Traut"); 175