1/* 2 * linux/drivers/misc/xillybus_pcie.c 3 * 4 * Copyright 2011 Xillybus Ltd, http://xillybus.com 5 * 6 * Driver for the Xillybus FPGA/host framework using PCI Express. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the smems of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 */ 12 13#include <linux/module.h> 14#include <linux/pci.h> 15#include <linux/pci-aspm.h> 16#include <linux/slab.h> 17#include "xillybus.h" 18 19MODULE_DESCRIPTION("Xillybus driver for PCIe"); 20MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); 21MODULE_VERSION("1.06"); 22MODULE_ALIAS("xillybus_pcie"); 23MODULE_LICENSE("GPL v2"); 24 25#define PCI_DEVICE_ID_XILLYBUS 0xebeb 26 27#define PCI_VENDOR_ID_ALTERA 0x1172 28#define PCI_VENDOR_ID_ACTEL 0x11aa 29#define PCI_VENDOR_ID_LATTICE 0x1204 30 31static const char xillyname[] = "xillybus_pcie"; 32 33static const struct pci_device_id xillyids[] = { 34 {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)}, 35 {PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)}, 36 {PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)}, 37 {PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)}, 38 { /* End: all zeroes */ } 39}; 40 41static int xilly_pci_direction(int direction) 42{ 43 switch (direction) { 44 case DMA_TO_DEVICE: 45 return PCI_DMA_TODEVICE; 46 case DMA_FROM_DEVICE: 47 return PCI_DMA_FROMDEVICE; 48 default: 49 return PCI_DMA_BIDIRECTIONAL; 50 } 51} 52 53static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, 54 dma_addr_t dma_handle, 55 size_t size, 56 int direction) 57{ 58 pci_dma_sync_single_for_cpu(ep->pdev, 59 dma_handle, 60 size, 61 xilly_pci_direction(direction)); 62} 63 64static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, 65 dma_addr_t dma_handle, 66 size_t size, 67 int direction) 68{ 69 pci_dma_sync_single_for_device(ep->pdev, 70 dma_handle, 71 size, 72 xilly_pci_direction(direction)); 73} 74 75static void xilly_pci_unmap(void *ptr) 76{ 77 struct xilly_mapping *data = ptr; 78 79 pci_unmap_single(data->device, data->dma_addr, 80 data->size, data->direction); 81 82 kfree(ptr); 83} 84 85/* 86 * Map either through the PCI DMA mapper or the non_PCI one. Behind the 87 * scenes exactly the same functions are called with the same parameters, 88 * but that can change. 89 */ 90 91static int xilly_map_single_pci(struct xilly_endpoint *ep, 92 void *ptr, 93 size_t size, 94 int direction, 95 dma_addr_t *ret_dma_handle 96 ) 97{ 98 int pci_direction; 99 dma_addr_t addr; 100 struct xilly_mapping *this; 101 int rc; 102 103 this = kzalloc(sizeof(*this), GFP_KERNEL); 104 if (!this) 105 return -ENOMEM; 106 107 pci_direction = xilly_pci_direction(direction); 108 109 addr = pci_map_single(ep->pdev, ptr, size, pci_direction); 110 111 if (pci_dma_mapping_error(ep->pdev, addr)) { 112 kfree(this); 113 return -ENODEV; 114 } 115 116 this->device = ep->pdev; 117 this->dma_addr = addr; 118 this->size = size; 119 this->direction = pci_direction; 120 121 *ret_dma_handle = addr; 122 123 rc = devm_add_action(ep->dev, xilly_pci_unmap, this); 124 if (rc) { 125 pci_unmap_single(ep->pdev, addr, size, pci_direction); 126 kfree(this); 127 return rc; 128 } 129 130 return 0; 131} 132 133static struct xilly_endpoint_hardware pci_hw = { 134 .owner = THIS_MODULE, 135 .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, 136 .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, 137 .map_single = xilly_map_single_pci, 138}; 139 140static int xilly_probe(struct pci_dev *pdev, 141 const struct pci_device_id *ent) 142{ 143 struct xilly_endpoint *endpoint; 144 int rc; 145 146 endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); 147 148 if (!endpoint) 149 return -ENOMEM; 150 151 pci_set_drvdata(pdev, endpoint); 152 153 rc = pcim_enable_device(pdev); 154 if (rc) { 155 dev_err(endpoint->dev, 156 "pcim_enable_device() failed. Aborting.\n"); 157 return rc; 158 } 159 160 /* L0s has caused packet drops. No power saving, thank you. */ 161 162 pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); 163 164 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 165 dev_err(endpoint->dev, 166 "Incorrect BAR configuration. Aborting.\n"); 167 return -ENODEV; 168 } 169 170 rc = pcim_iomap_regions(pdev, 0x01, xillyname); 171 if (rc) { 172 dev_err(endpoint->dev, 173 "pcim_iomap_regions() failed. Aborting.\n"); 174 return rc; 175 } 176 177 endpoint->registers = pcim_iomap_table(pdev)[0]; 178 179 pci_set_master(pdev); 180 181 /* Set up a single MSI interrupt */ 182 if (pci_enable_msi(pdev)) { 183 dev_err(endpoint->dev, 184 "Failed to enable MSI interrupts. Aborting.\n"); 185 return -ENODEV; 186 } 187 rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, 188 xillyname, endpoint); 189 if (rc) { 190 dev_err(endpoint->dev, 191 "Failed to register MSI handler. Aborting.\n"); 192 return -ENODEV; 193 } 194 195 /* 196 * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1 197 * is the right thing. But some unclever PCIe drivers report it's OK 198 * when the hardware drops those 64-bit PCIe packets. So trust 199 * nobody and use 32 bits DMA addressing in any case. 200 */ 201 202 if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { 203 endpoint->dma_using_dac = 0; 204 } else { 205 dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); 206 return -ENODEV; 207 } 208 209 return xillybus_endpoint_discovery(endpoint); 210} 211 212static void xilly_remove(struct pci_dev *pdev) 213{ 214 struct xilly_endpoint *endpoint = pci_get_drvdata(pdev); 215 216 xillybus_endpoint_remove(endpoint); 217} 218 219MODULE_DEVICE_TABLE(pci, xillyids); 220 221static struct pci_driver xillybus_driver = { 222 .name = xillyname, 223 .id_table = xillyids, 224 .probe = xilly_probe, 225 .remove = xilly_remove, 226}; 227 228module_pci_driver(xillybus_driver); 229