18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/misc/xillybus_pcie.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Xillybus Ltd, http://xillybus.com
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Driver for the Xillybus FPGA/host framework using PCI Express.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include "xillybus.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xillybus driver for PCIe");
168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
178c2ecf20Sopenharmony_ciMODULE_VERSION("1.06");
188c2ecf20Sopenharmony_ciMODULE_ALIAS("xillybus_pcie");
198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_XILLYBUS		0xebeb
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_ACTEL		0x11aa
248c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_LATTICE		0x1204
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const char xillyname[] = "xillybus_pcie";
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const struct pci_device_id xillyids[] = {
298c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)},
308c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)},
318c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)},
328c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)},
338c2ecf20Sopenharmony_ci	{ /* End: all zeroes */ }
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int xilly_pci_direction(int direction)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	switch (direction) {
398c2ecf20Sopenharmony_ci	case DMA_TO_DEVICE:
408c2ecf20Sopenharmony_ci		return PCI_DMA_TODEVICE;
418c2ecf20Sopenharmony_ci	case DMA_FROM_DEVICE:
428c2ecf20Sopenharmony_ci		return PCI_DMA_FROMDEVICE;
438c2ecf20Sopenharmony_ci	default:
448c2ecf20Sopenharmony_ci		return PCI_DMA_BIDIRECTIONAL;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep,
498c2ecf20Sopenharmony_ci					      dma_addr_t dma_handle,
508c2ecf20Sopenharmony_ci					      size_t size,
518c2ecf20Sopenharmony_ci					      int direction)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	pci_dma_sync_single_for_cpu(ep->pdev,
548c2ecf20Sopenharmony_ci				    dma_handle,
558c2ecf20Sopenharmony_ci				    size,
568c2ecf20Sopenharmony_ci				    xilly_pci_direction(direction));
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
608c2ecf20Sopenharmony_ci						 dma_addr_t dma_handle,
618c2ecf20Sopenharmony_ci						 size_t size,
628c2ecf20Sopenharmony_ci						 int direction)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	pci_dma_sync_single_for_device(ep->pdev,
658c2ecf20Sopenharmony_ci				       dma_handle,
668c2ecf20Sopenharmony_ci				       size,
678c2ecf20Sopenharmony_ci				       xilly_pci_direction(direction));
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void xilly_pci_unmap(void *ptr)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct xilly_mapping *data = ptr;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	pci_unmap_single(data->device, data->dma_addr,
758c2ecf20Sopenharmony_ci			 data->size, data->direction);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	kfree(ptr);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Map either through the PCI DMA mapper or the non_PCI one. Behind the
828c2ecf20Sopenharmony_ci * scenes exactly the same functions are called with the same parameters,
838c2ecf20Sopenharmony_ci * but that can change.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int xilly_map_single_pci(struct xilly_endpoint *ep,
878c2ecf20Sopenharmony_ci				void *ptr,
888c2ecf20Sopenharmony_ci				size_t size,
898c2ecf20Sopenharmony_ci				int direction,
908c2ecf20Sopenharmony_ci				dma_addr_t *ret_dma_handle
918c2ecf20Sopenharmony_ci	)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int pci_direction;
948c2ecf20Sopenharmony_ci	dma_addr_t addr;
958c2ecf20Sopenharmony_ci	struct xilly_mapping *this;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	this = kzalloc(sizeof(*this), GFP_KERNEL);
988c2ecf20Sopenharmony_ci	if (!this)
998c2ecf20Sopenharmony_ci		return -ENOMEM;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	pci_direction = xilly_pci_direction(direction);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (pci_dma_mapping_error(ep->pdev, addr)) {
1068c2ecf20Sopenharmony_ci		kfree(this);
1078c2ecf20Sopenharmony_ci		return -ENODEV;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	this->device = ep->pdev;
1118c2ecf20Sopenharmony_ci	this->dma_addr = addr;
1128c2ecf20Sopenharmony_ci	this->size = size;
1138c2ecf20Sopenharmony_ci	this->direction = pci_direction;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	*ret_dma_handle = addr;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct xilly_endpoint_hardware pci_hw = {
1218c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1228c2ecf20Sopenharmony_ci	.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
1238c2ecf20Sopenharmony_ci	.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
1248c2ecf20Sopenharmony_ci	.map_single = xilly_map_single_pci,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int xilly_probe(struct pci_dev *pdev,
1288c2ecf20Sopenharmony_ci		       const struct pci_device_id *ent)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct xilly_endpoint *endpoint;
1318c2ecf20Sopenharmony_ci	int rc;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!endpoint)
1368c2ecf20Sopenharmony_ci		return -ENOMEM;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, endpoint);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	rc = pcim_enable_device(pdev);
1418c2ecf20Sopenharmony_ci	if (rc) {
1428c2ecf20Sopenharmony_ci		dev_err(endpoint->dev,
1438c2ecf20Sopenharmony_ci			"pcim_enable_device() failed. Aborting.\n");
1448c2ecf20Sopenharmony_ci		return rc;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* L0s has caused packet drops. No power saving, thank you. */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
1528c2ecf20Sopenharmony_ci		dev_err(endpoint->dev,
1538c2ecf20Sopenharmony_ci			"Incorrect BAR configuration. Aborting.\n");
1548c2ecf20Sopenharmony_ci		return -ENODEV;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	rc = pcim_iomap_regions(pdev, 0x01, xillyname);
1588c2ecf20Sopenharmony_ci	if (rc) {
1598c2ecf20Sopenharmony_ci		dev_err(endpoint->dev,
1608c2ecf20Sopenharmony_ci			"pcim_iomap_regions() failed. Aborting.\n");
1618c2ecf20Sopenharmony_ci		return rc;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	endpoint->registers = pcim_iomap_table(pdev)[0];
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pci_set_master(pdev);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* Set up a single MSI interrupt */
1698c2ecf20Sopenharmony_ci	if (pci_enable_msi(pdev)) {
1708c2ecf20Sopenharmony_ci		dev_err(endpoint->dev,
1718c2ecf20Sopenharmony_ci			"Failed to enable MSI interrupts. Aborting.\n");
1728c2ecf20Sopenharmony_ci		return -ENODEV;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
1758c2ecf20Sopenharmony_ci			      xillyname, endpoint);
1768c2ecf20Sopenharmony_ci	if (rc) {
1778c2ecf20Sopenharmony_ci		dev_err(endpoint->dev,
1788c2ecf20Sopenharmony_ci			"Failed to register MSI handler. Aborting.\n");
1798c2ecf20Sopenharmony_ci		return -ENODEV;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/*
1838c2ecf20Sopenharmony_ci	 * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
1848c2ecf20Sopenharmony_ci	 * even when the PCIe driver claims that a 64-bit mask is OK. On the
1858c2ecf20Sopenharmony_ci	 * other hand, on some architectures, 64-bit addressing is mandatory.
1868c2ecf20Sopenharmony_ci	 * So go for the 64-bit mask only when failing is the other option.
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
1908c2ecf20Sopenharmony_ci		endpoint->dma_using_dac = 0;
1918c2ecf20Sopenharmony_ci	} else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
1928c2ecf20Sopenharmony_ci		endpoint->dma_using_dac = 1;
1938c2ecf20Sopenharmony_ci	} else {
1948c2ecf20Sopenharmony_ci		dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n");
1958c2ecf20Sopenharmony_ci		return -ENODEV;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return xillybus_endpoint_discovery(endpoint);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void xilly_remove(struct pci_dev *pdev)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	xillybus_endpoint_remove(endpoint);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, xillyids);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic struct pci_driver xillybus_driver = {
2118c2ecf20Sopenharmony_ci	.name = xillyname,
2128c2ecf20Sopenharmony_ci	.id_table = xillyids,
2138c2ecf20Sopenharmony_ci	.probe = xilly_probe,
2148c2ecf20Sopenharmony_ci	.remove = xilly_remove,
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cimodule_pci_driver(xillybus_driver);
218