| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * linux/drivers/misc/xillybus_pcie.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2011 Xillybus Ltd, http://xillybus.com
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Driver for the Xillybus FPGA/host framework using PCI Express. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the smems of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; version 2 of the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/pci-aspm.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include "xillybus.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Xillybus driver for PCIe"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); | 
					
						
							|  |  |  | MODULE_VERSION("1.06"); | 
					
						
							|  |  |  | MODULE_ALIAS("xillybus_pcie"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL v2"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define PCI_DEVICE_ID_XILLYBUS		0xebeb
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define PCI_VENDOR_ID_ALTERA		0x1172
 | 
					
						
							|  |  |  | #define PCI_VENDOR_ID_ACTEL		0x11aa
 | 
					
						
							|  |  |  | #define PCI_VENDOR_ID_LATTICE		0x1204
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 00:24:00 +03:00
										 |  |  | static const char xillyname[] = "xillybus_pcie"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-03 08:26:00 +09:00
										 |  |  | static const struct pci_device_id xillyids[] = { | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	{PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)}, | 
					
						
							|  |  |  | 	{PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)}, | 
					
						
							|  |  |  | 	{PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)}, | 
					
						
							|  |  |  | 	{PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)}, | 
					
						
							|  |  |  | 	{ /* End: all zeroes */ } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int xilly_pci_direction(int direction) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (direction) { | 
					
						
							|  |  |  | 	case DMA_TO_DEVICE: | 
					
						
							|  |  |  | 		return PCI_DMA_TODEVICE; | 
					
						
							|  |  |  | 	case DMA_FROM_DEVICE: | 
					
						
							|  |  |  | 		return PCI_DMA_FROMDEVICE; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return PCI_DMA_BIDIRECTIONAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, | 
					
						
							|  |  |  | 					      dma_addr_t dma_handle, | 
					
						
							|  |  |  | 					      size_t size, | 
					
						
							|  |  |  | 					      int direction) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pci_dma_sync_single_for_cpu(ep->pdev, | 
					
						
							|  |  |  | 				    dma_handle, | 
					
						
							|  |  |  | 				    size, | 
					
						
							|  |  |  | 				    xilly_pci_direction(direction)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, | 
					
						
							|  |  |  | 						 dma_addr_t dma_handle, | 
					
						
							|  |  |  | 						 size_t size, | 
					
						
							|  |  |  | 						 int direction) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pci_dma_sync_single_for_device(ep->pdev, | 
					
						
							|  |  |  | 				       dma_handle, | 
					
						
							|  |  |  | 				       size, | 
					
						
							|  |  |  | 				       xilly_pci_direction(direction)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | static void xilly_pci_unmap(void *ptr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct xilly_mapping *data = ptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_unmap_single(data->device, data->dma_addr, | 
					
						
							|  |  |  | 			 data->size, data->direction); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kfree(ptr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Map either through the PCI DMA mapper or the non_PCI one. Behind the | 
					
						
							|  |  |  |  * scenes exactly the same functions are called with the same parameters, | 
					
						
							|  |  |  |  * but that can change. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | static int xilly_map_single_pci(struct xilly_endpoint *ep, | 
					
						
							|  |  |  | 				void *ptr, | 
					
						
							|  |  |  | 				size_t size, | 
					
						
							|  |  |  | 				int direction, | 
					
						
							|  |  |  | 				dma_addr_t *ret_dma_handle | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int pci_direction; | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	dma_addr_t addr; | 
					
						
							|  |  |  | 	struct xilly_mapping *this; | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:48 +03:00
										 |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	this = kzalloc(sizeof(*this), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	if (!this) | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pci_direction = xilly_pci_direction(direction); | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	addr = pci_map_single(ep->pdev, ptr, size, pci_direction); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pci_dma_mapping_error(ep->pdev, addr)) { | 
					
						
							|  |  |  | 		kfree(this); | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	this->device = ep->pdev; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	this->dma_addr = addr; | 
					
						
							|  |  |  | 	this->size = size; | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	this->direction = pci_direction; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	*ret_dma_handle = addr; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	rc = devm_add_action(ep->dev, xilly_pci_unmap, this); | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							|  |  |  | 		pci_unmap_single(ep->pdev, addr, size, pci_direction); | 
					
						
							|  |  |  | 		kfree(this); | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:48 +03:00
										 |  |  | 		return rc; | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:48 +03:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct xilly_endpoint_hardware pci_hw = { | 
					
						
							|  |  |  | 	.owner = THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2013-07-31 11:22:43 +03:00
										 |  |  | 	.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, | 
					
						
							|  |  |  | 	.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	.map_single = xilly_map_single_pci, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int xilly_probe(struct pci_dev *pdev, | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:56 +03:00
										 |  |  | 		       const struct pci_device_id *ent) | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct xilly_endpoint *endpoint; | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:48 +03:00
										 |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:25 +03:00
										 |  |  | 	endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!endpoint) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_set_drvdata(pdev, endpoint); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 	rc = pcim_enable_device(pdev); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	if (rc) { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 			"pcim_enable_device() failed. Aborting.\n"); | 
					
						
							|  |  |  | 		return rc; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 	/* L0s has caused packet drops. No power saving, thank you. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, | 
					
						
							|  |  |  | 			"Incorrect BAR configuration. Aborting.\n"); | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 	rc = pcim_iomap_regions(pdev, 0x01, xillyname); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	if (rc) { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 			"pcim_iomap_regions() failed. Aborting.\n"); | 
					
						
							|  |  |  | 		return rc; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 	endpoint->registers = pcim_iomap_table(pdev)[0]; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pci_set_master(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up a single MSI interrupt */ | 
					
						
							|  |  |  | 	if (pci_enable_msi(pdev)) { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, | 
					
						
							|  |  |  | 			"Failed to enable MSI interrupts. Aborting.\n"); | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 	rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, | 
					
						
							|  |  |  | 			      xillyname, endpoint); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	if (rc) { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, | 
					
						
							|  |  |  | 			"Failed to register MSI handler. Aborting.\n"); | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1 | 
					
						
							|  |  |  | 	 * is the right thing. But some unclever PCIe drivers report it's OK | 
					
						
							|  |  |  | 	 * when the hardware drops those 64-bit PCIe packets. So trust | 
					
						
							|  |  |  | 	 * nobody and use 32 bits DMA addressing in any case. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:51 +03:00
										 |  |  | 	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 		endpoint->dma_using_dac = 0; | 
					
						
							| 
									
										
										
										
											2014-09-04 17:47:51 +03:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-10-19 01:02:26 +03:00
										 |  |  | 		dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); | 
					
						
							| 
									
										
										
										
											2014-05-11 19:53:46 +03:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-21 14:07:12 +03:00
										 |  |  | 	return xillybus_endpoint_discovery(endpoint); | 
					
						
							| 
									
										
										
										
											2013-06-24 18:55:47 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void xilly_remove(struct pci_dev *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct xilly_endpoint *endpoint = pci_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	xillybus_endpoint_remove(endpoint); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(pci, xillyids); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct pci_driver xillybus_driver = { | 
					
						
							|  |  |  | 	.name = xillyname, | 
					
						
							|  |  |  | 	.id_table = xillyids, | 
					
						
							|  |  |  | 	.probe = xilly_probe, | 
					
						
							|  |  |  | 	.remove = xilly_remove, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_pci_driver(xillybus_driver); |