forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
4.3 KiB
174 lines
4.3 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* UIO driver for Hilscher NetX based fieldbus cards (cifX, comX). |
|
* See http://www.hilscher.com for details. |
|
* |
|
* (C) 2007 Hans J. Koch <[email protected]> |
|
* (C) 2008 Manuel Traut <[email protected]> |
|
* |
|
*/ |
|
|
|
#include <linux/device.h> |
|
#include <linux/io.h> |
|
#include <linux/module.h> |
|
#include <linux/pci.h> |
|
#include <linux/slab.h> |
|
#include <linux/uio_driver.h> |
|
|
|
#define PCI_VENDOR_ID_HILSCHER 0x15CF |
|
#define PCI_DEVICE_ID_HILSCHER_NETX 0x0000 |
|
#define PCI_DEVICE_ID_HILSCHER_NETPLC 0x0010 |
|
#define PCI_SUBDEVICE_ID_NETPLC_RAM 0x0000 |
|
#define PCI_SUBDEVICE_ID_NETPLC_FLASH 0x0001 |
|
#define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235 |
|
#define PCI_SUBDEVICE_ID_NXPCA 0x3335 |
|
|
|
#define DPM_HOST_INT_EN0 0xfff0 |
|
#define DPM_HOST_INT_STAT0 0xffe0 |
|
|
|
#define DPM_HOST_INT_MASK 0xe600ffff |
|
#define DPM_HOST_INT_GLOBAL_EN 0x80000000 |
|
|
|
static irqreturn_t netx_handler(int irq, struct uio_info *dev_info) |
|
{ |
|
void __iomem *int_enable_reg = dev_info->mem[0].internal_addr |
|
+ DPM_HOST_INT_EN0; |
|
void __iomem *int_status_reg = dev_info->mem[0].internal_addr |
|
+ DPM_HOST_INT_STAT0; |
|
|
|
/* Is one of our interrupts enabled and active ? */ |
|
if (!(ioread32(int_enable_reg) & ioread32(int_status_reg) |
|
& DPM_HOST_INT_MASK)) |
|
return IRQ_NONE; |
|
|
|
/* Disable interrupt */ |
|
iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN, |
|
int_enable_reg); |
|
return IRQ_HANDLED; |
|
} |
|
|
|
static int netx_pci_probe(struct pci_dev *dev, |
|
const struct pci_device_id *id) |
|
{ |
|
struct uio_info *info; |
|
int bar; |
|
|
|
info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL); |
|
if (!info) |
|
return -ENOMEM; |
|
|
|
if (pci_enable_device(dev)) |
|
return -ENODEV; |
|
|
|
if (pci_request_regions(dev, "netx")) |
|
goto out_disable; |
|
|
|
switch (id->device) { |
|
case PCI_DEVICE_ID_HILSCHER_NETX: |
|
bar = 0; |
|
info->name = "netx"; |
|
break; |
|
case PCI_DEVICE_ID_HILSCHER_NETPLC: |
|
bar = 0; |
|
info->name = "netplc"; |
|
break; |
|
default: |
|
bar = 2; |
|
info->name = "netx_plx"; |
|
} |
|
|
|
/* BAR0 or 2 points to the card's dual port memory */ |
|
info->mem[0].addr = pci_resource_start(dev, bar); |
|
if (!info->mem[0].addr) |
|
goto out_release; |
|
info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar), |
|
pci_resource_len(dev, bar)); |
|
|
|
if (!info->mem[0].internal_addr) |
|
goto out_release; |
|
|
|
info->mem[0].size = pci_resource_len(dev, bar); |
|
info->mem[0].memtype = UIO_MEM_PHYS; |
|
info->irq = dev->irq; |
|
info->irq_flags = IRQF_SHARED; |
|
info->handler = netx_handler; |
|
info->version = "0.0.1"; |
|
|
|
/* Make sure all interrupts are disabled */ |
|
iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); |
|
|
|
if (uio_register_device(&dev->dev, info)) |
|
goto out_unmap; |
|
|
|
pci_set_drvdata(dev, info); |
|
dev_info(&dev->dev, "Found %s card, registered UIO device.\n", |
|
info->name); |
|
|
|
return 0; |
|
|
|
out_unmap: |
|
iounmap(info->mem[0].internal_addr); |
|
out_release: |
|
pci_release_regions(dev); |
|
out_disable: |
|
pci_disable_device(dev); |
|
return -ENODEV; |
|
} |
|
|
|
static void netx_pci_remove(struct pci_dev *dev) |
|
{ |
|
struct uio_info *info = pci_get_drvdata(dev); |
|
|
|
/* Disable all interrupts */ |
|
iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0); |
|
uio_unregister_device(info); |
|
pci_release_regions(dev); |
|
pci_disable_device(dev); |
|
iounmap(info->mem[0].internal_addr); |
|
} |
|
|
|
static struct pci_device_id netx_pci_ids[] = { |
|
{ |
|
.vendor = PCI_VENDOR_ID_HILSCHER, |
|
.device = PCI_DEVICE_ID_HILSCHER_NETX, |
|
.subvendor = 0, |
|
.subdevice = 0, |
|
}, |
|
{ |
|
.vendor = PCI_VENDOR_ID_HILSCHER, |
|
.device = PCI_DEVICE_ID_HILSCHER_NETPLC, |
|
.subvendor = PCI_VENDOR_ID_HILSCHER, |
|
.subdevice = PCI_SUBDEVICE_ID_NETPLC_RAM, |
|
}, |
|
{ |
|
.vendor = PCI_VENDOR_ID_HILSCHER, |
|
.device = PCI_DEVICE_ID_HILSCHER_NETPLC, |
|
.subvendor = PCI_VENDOR_ID_HILSCHER, |
|
.subdevice = PCI_SUBDEVICE_ID_NETPLC_FLASH, |
|
}, |
|
{ |
|
.vendor = PCI_VENDOR_ID_PLX, |
|
.device = PCI_DEVICE_ID_PLX_9030, |
|
.subvendor = PCI_VENDOR_ID_PLX, |
|
.subdevice = PCI_SUBDEVICE_ID_NXSB_PCA, |
|
}, |
|
{ |
|
.vendor = PCI_VENDOR_ID_PLX, |
|
.device = PCI_DEVICE_ID_PLX_9030, |
|
.subvendor = PCI_VENDOR_ID_PLX, |
|
.subdevice = PCI_SUBDEVICE_ID_NXPCA, |
|
}, |
|
{ 0, } |
|
}; |
|
|
|
static struct pci_driver netx_pci_driver = { |
|
.name = "netx", |
|
.id_table = netx_pci_ids, |
|
.probe = netx_pci_probe, |
|
.remove = netx_pci_remove, |
|
}; |
|
|
|
module_pci_driver(netx_pci_driver); |
|
MODULE_DEVICE_TABLE(pci, netx_pci_ids); |
|
MODULE_LICENSE("GPL v2"); |
|
MODULE_AUTHOR("Hans J. Koch, Manuel Traut");
|
|
|