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