18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel I/OAT DMA Linux driver 48c2ecf20Sopenharmony_ci * Copyright(c) 2004 - 2015 Intel Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 178c2ecf20Sopenharmony_ci#include <linux/dca.h> 188c2ecf20Sopenharmony_ci#include <linux/aer.h> 198c2ecf20Sopenharmony_ci#include <linux/sizes.h> 208c2ecf20Sopenharmony_ci#include "dma.h" 218c2ecf20Sopenharmony_ci#include "registers.h" 228c2ecf20Sopenharmony_ci#include "hw.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "../dmaengine.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_VERSION(IOAT_DMA_VERSION); 278c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const struct pci_device_id ioat_pci_tbl[] = { 318c2ecf20Sopenharmony_ci /* I/OAT v3 platforms */ 328c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, 338c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, 348c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, 358c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, 368c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, 378c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, 388c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, 398c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* I/OAT v3.2 platforms */ 428c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF0) }, 438c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF1) }, 448c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF2) }, 458c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF3) }, 468c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF4) }, 478c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF5) }, 488c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF6) }, 498c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF7) }, 508c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) }, 518c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) }, 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB0) }, 548c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB1) }, 558c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB2) }, 568c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB3) }, 578c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB4) }, 588c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB5) }, 598c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB6) }, 608c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB7) }, 618c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB8) }, 628c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB9) }, 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB0) }, 658c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB1) }, 668c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB2) }, 678c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB3) }, 688c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB4) }, 698c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB5) }, 708c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB6) }, 718c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB7) }, 728c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB8) }, 738c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB9) }, 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW0) }, 768c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW1) }, 778c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW2) }, 788c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW3) }, 798c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW4) }, 808c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW5) }, 818c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW6) }, 828c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW7) }, 838c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW8) }, 848c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW9) }, 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX0) }, 878c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX1) }, 888c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX2) }, 898c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX3) }, 908c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX4) }, 918c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX5) }, 928c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX6) }, 938c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX7) }, 948c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX8) }, 958c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDX9) }, 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SKX) }, 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* I/OAT v3.3 platforms */ 1008c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD0) }, 1018c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD1) }, 1028c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD2) }, 1038c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD3) }, 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE0) }, 1068c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE1) }, 1078c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE2) }, 1088c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE3) }, 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* I/OAT v3.4 platforms */ 1118c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_ICX) }, 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci { 0, } 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ioat_pci_tbl); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); 1188c2ecf20Sopenharmony_cistatic void ioat_remove(struct pci_dev *pdev); 1198c2ecf20Sopenharmony_cistatic void 1208c2ecf20Sopenharmony_ciioat_init_channel(struct ioatdma_device *ioat_dma, 1218c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan, int idx); 1228c2ecf20Sopenharmony_cistatic void ioat_intr_quirk(struct ioatdma_device *ioat_dma); 1238c2ecf20Sopenharmony_cistatic void ioat_enumerate_channels(struct ioatdma_device *ioat_dma); 1248c2ecf20Sopenharmony_cistatic int ioat3_dma_self_test(struct ioatdma_device *ioat_dma); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int ioat_dca_enabled = 1; 1278c2ecf20Sopenharmony_cimodule_param(ioat_dca_enabled, int, 0644); 1288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)"); 1298c2ecf20Sopenharmony_ciint ioat_pending_level = 7; 1308c2ecf20Sopenharmony_cimodule_param(ioat_pending_level, int, 0644); 1318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ioat_pending_level, 1328c2ecf20Sopenharmony_ci "high-water mark for pushing ioat descriptors (default: 7)"); 1338c2ecf20Sopenharmony_cistatic char ioat_interrupt_style[32] = "msix"; 1348c2ecf20Sopenharmony_cimodule_param_string(ioat_interrupt_style, ioat_interrupt_style, 1358c2ecf20Sopenharmony_ci sizeof(ioat_interrupt_style), 0644); 1368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ioat_interrupt_style, 1378c2ecf20Sopenharmony_ci "set ioat interrupt style: msix (default), msi, intx"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct kmem_cache *ioat_cache; 1408c2ecf20Sopenharmony_cistruct kmem_cache *ioat_sed_cache; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic bool is_jf_ioat(struct pci_dev *pdev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci switch (pdev->device) { 1458c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF0: 1468c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF1: 1478c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF2: 1488c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF3: 1498c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF4: 1508c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF5: 1518c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF6: 1528c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF7: 1538c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF8: 1548c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_JSF9: 1558c2ecf20Sopenharmony_ci return true; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci return false; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic bool is_snb_ioat(struct pci_dev *pdev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci switch (pdev->device) { 1648c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB0: 1658c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB1: 1668c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB2: 1678c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB3: 1688c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB4: 1698c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB5: 1708c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB6: 1718c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB7: 1728c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB8: 1738c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_SNB9: 1748c2ecf20Sopenharmony_ci return true; 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci return false; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic bool is_ivb_ioat(struct pci_dev *pdev) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci switch (pdev->device) { 1838c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB0: 1848c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB1: 1858c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB2: 1868c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB3: 1878c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB4: 1888c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB5: 1898c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB6: 1908c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB7: 1918c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB8: 1928c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_IVB9: 1938c2ecf20Sopenharmony_ci return true; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return false; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic bool is_hsw_ioat(struct pci_dev *pdev) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci switch (pdev->device) { 2038c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW0: 2048c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW1: 2058c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW2: 2068c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW3: 2078c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW4: 2088c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW5: 2098c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW6: 2108c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW7: 2118c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW8: 2128c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_HSW9: 2138c2ecf20Sopenharmony_ci return true; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci return false; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic bool is_bdx_ioat(struct pci_dev *pdev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci switch (pdev->device) { 2238c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX0: 2248c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX1: 2258c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX2: 2268c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX3: 2278c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX4: 2288c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX5: 2298c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX6: 2308c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX7: 2318c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX8: 2328c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDX9: 2338c2ecf20Sopenharmony_ci return true; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci return false; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline bool is_skx_ioat(struct pci_dev *pdev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return (pdev->device == PCI_DEVICE_ID_INTEL_IOAT_SKX) ? true : false; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic bool is_xeon_cb32(struct pci_dev *pdev) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) || 2478c2ecf20Sopenharmony_ci is_hsw_ioat(pdev) || is_bdx_ioat(pdev) || is_skx_ioat(pdev); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cibool is_bwd_ioat(struct pci_dev *pdev) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci switch (pdev->device) { 2538c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD0: 2548c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD1: 2558c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD2: 2568c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD3: 2578c2ecf20Sopenharmony_ci /* even though not Atom, BDX-DE has same DMA silicon */ 2588c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE0: 2598c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE1: 2608c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE2: 2618c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE3: 2628c2ecf20Sopenharmony_ci return true; 2638c2ecf20Sopenharmony_ci default: 2648c2ecf20Sopenharmony_ci return false; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic bool is_bwd_noraid(struct pci_dev *pdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci switch (pdev->device) { 2718c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD2: 2728c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BWD3: 2738c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE0: 2748c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE1: 2758c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE2: 2768c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_IOAT_BDXDE3: 2778c2ecf20Sopenharmony_ci return true; 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci return false; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * Perform a IOAT transaction to verify the HW works. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci#define IOAT_TEST_SIZE 2000 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void ioat_dma_test_callback(void *dma_async_param) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct completion *cmp = dma_async_param; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci complete(cmp); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/** 2978c2ecf20Sopenharmony_ci * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. 2988c2ecf20Sopenharmony_ci * @ioat_dma: dma device to be tested 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic int ioat_dma_self_test(struct ioatdma_device *ioat_dma) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci int i; 3038c2ecf20Sopenharmony_ci u8 *src; 3048c2ecf20Sopenharmony_ci u8 *dest; 3058c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 3068c2ecf20Sopenharmony_ci struct device *dev = &ioat_dma->pdev->dev; 3078c2ecf20Sopenharmony_ci struct dma_chan *dma_chan; 3088c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 3098c2ecf20Sopenharmony_ci dma_addr_t dma_dest, dma_src; 3108c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3118c2ecf20Sopenharmony_ci int err = 0; 3128c2ecf20Sopenharmony_ci struct completion cmp; 3138c2ecf20Sopenharmony_ci unsigned long tmo; 3148c2ecf20Sopenharmony_ci unsigned long flags; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci src = kzalloc(IOAT_TEST_SIZE, GFP_KERNEL); 3178c2ecf20Sopenharmony_ci if (!src) 3188c2ecf20Sopenharmony_ci return -ENOMEM; 3198c2ecf20Sopenharmony_ci dest = kzalloc(IOAT_TEST_SIZE, GFP_KERNEL); 3208c2ecf20Sopenharmony_ci if (!dest) { 3218c2ecf20Sopenharmony_ci kfree(src); 3228c2ecf20Sopenharmony_ci return -ENOMEM; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Fill in src buffer */ 3268c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_TEST_SIZE; i++) 3278c2ecf20Sopenharmony_ci src[i] = (u8)i; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Start copy, using first DMA channel */ 3308c2ecf20Sopenharmony_ci dma_chan = container_of(dma->channels.next, struct dma_chan, 3318c2ecf20Sopenharmony_ci device_node); 3328c2ecf20Sopenharmony_ci if (dma->device_alloc_chan_resources(dma_chan) < 1) { 3338c2ecf20Sopenharmony_ci dev_err(dev, "selftest cannot allocate chan resource\n"); 3348c2ecf20Sopenharmony_ci err = -ENODEV; 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); 3398c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_src)) { 3408c2ecf20Sopenharmony_ci dev_err(dev, "mapping src buffer failed\n"); 3418c2ecf20Sopenharmony_ci err = -ENOMEM; 3428c2ecf20Sopenharmony_ci goto free_resources; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); 3458c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_dest)) { 3468c2ecf20Sopenharmony_ci dev_err(dev, "mapping dest buffer failed\n"); 3478c2ecf20Sopenharmony_ci err = -ENOMEM; 3488c2ecf20Sopenharmony_ci goto unmap_src; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci flags = DMA_PREP_INTERRUPT; 3518c2ecf20Sopenharmony_ci tx = ioat_dma->dma_dev.device_prep_dma_memcpy(dma_chan, dma_dest, 3528c2ecf20Sopenharmony_ci dma_src, IOAT_TEST_SIZE, 3538c2ecf20Sopenharmony_ci flags); 3548c2ecf20Sopenharmony_ci if (!tx) { 3558c2ecf20Sopenharmony_ci dev_err(dev, "Self-test prep failed, disabling\n"); 3568c2ecf20Sopenharmony_ci err = -ENODEV; 3578c2ecf20Sopenharmony_ci goto unmap_dma; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci async_tx_ack(tx); 3618c2ecf20Sopenharmony_ci init_completion(&cmp); 3628c2ecf20Sopenharmony_ci tx->callback = ioat_dma_test_callback; 3638c2ecf20Sopenharmony_ci tx->callback_param = &cmp; 3648c2ecf20Sopenharmony_ci cookie = tx->tx_submit(tx); 3658c2ecf20Sopenharmony_ci if (cookie < 0) { 3668c2ecf20Sopenharmony_ci dev_err(dev, "Self-test setup failed, disabling\n"); 3678c2ecf20Sopenharmony_ci err = -ENODEV; 3688c2ecf20Sopenharmony_ci goto unmap_dma; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci dma->device_issue_pending(dma_chan); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (tmo == 0 || 3758c2ecf20Sopenharmony_ci dma->device_tx_status(dma_chan, cookie, NULL) 3768c2ecf20Sopenharmony_ci != DMA_COMPLETE) { 3778c2ecf20Sopenharmony_ci dev_err(dev, "Self-test copy timed out, disabling\n"); 3788c2ecf20Sopenharmony_ci err = -ENODEV; 3798c2ecf20Sopenharmony_ci goto unmap_dma; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci if (memcmp(src, dest, IOAT_TEST_SIZE)) { 3828c2ecf20Sopenharmony_ci dev_err(dev, "Self-test copy failed compare, disabling\n"); 3838c2ecf20Sopenharmony_ci err = -ENODEV; 3848c2ecf20Sopenharmony_ci goto unmap_dma; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciunmap_dma: 3888c2ecf20Sopenharmony_ci dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); 3898c2ecf20Sopenharmony_ciunmap_src: 3908c2ecf20Sopenharmony_ci dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE); 3918c2ecf20Sopenharmony_cifree_resources: 3928c2ecf20Sopenharmony_ci dma->device_free_chan_resources(dma_chan); 3938c2ecf20Sopenharmony_ciout: 3948c2ecf20Sopenharmony_ci kfree(src); 3958c2ecf20Sopenharmony_ci kfree(dest); 3968c2ecf20Sopenharmony_ci return err; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * ioat_dma_setup_interrupts - setup interrupt handler 4018c2ecf20Sopenharmony_ci * @ioat_dma: ioat dma device 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ciint ioat_dma_setup_interrupts(struct ioatdma_device *ioat_dma) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 4068c2ecf20Sopenharmony_ci struct pci_dev *pdev = ioat_dma->pdev; 4078c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4088c2ecf20Sopenharmony_ci struct msix_entry *msix; 4098c2ecf20Sopenharmony_ci int i, j, msixcnt; 4108c2ecf20Sopenharmony_ci int err = -EINVAL; 4118c2ecf20Sopenharmony_ci u8 intrctrl = 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!strcmp(ioat_interrupt_style, "msix")) 4148c2ecf20Sopenharmony_ci goto msix; 4158c2ecf20Sopenharmony_ci if (!strcmp(ioat_interrupt_style, "msi")) 4168c2ecf20Sopenharmony_ci goto msi; 4178c2ecf20Sopenharmony_ci if (!strcmp(ioat_interrupt_style, "intx")) 4188c2ecf20Sopenharmony_ci goto intx; 4198c2ecf20Sopenharmony_ci dev_err(dev, "invalid ioat_interrupt_style %s\n", ioat_interrupt_style); 4208c2ecf20Sopenharmony_ci goto err_no_irq; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cimsix: 4238c2ecf20Sopenharmony_ci /* The number of MSI-X vectors should equal the number of channels */ 4248c2ecf20Sopenharmony_ci msixcnt = ioat_dma->dma_dev.chancnt; 4258c2ecf20Sopenharmony_ci for (i = 0; i < msixcnt; i++) 4268c2ecf20Sopenharmony_ci ioat_dma->msix_entries[i].entry = i; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci err = pci_enable_msix_exact(pdev, ioat_dma->msix_entries, msixcnt); 4298c2ecf20Sopenharmony_ci if (err) 4308c2ecf20Sopenharmony_ci goto msi; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci for (i = 0; i < msixcnt; i++) { 4338c2ecf20Sopenharmony_ci msix = &ioat_dma->msix_entries[i]; 4348c2ecf20Sopenharmony_ci ioat_chan = ioat_chan_by_index(ioat_dma, i); 4358c2ecf20Sopenharmony_ci err = devm_request_irq(dev, msix->vector, 4368c2ecf20Sopenharmony_ci ioat_dma_do_interrupt_msix, 0, 4378c2ecf20Sopenharmony_ci "ioat-msix", ioat_chan); 4388c2ecf20Sopenharmony_ci if (err) { 4398c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 4408c2ecf20Sopenharmony_ci msix = &ioat_dma->msix_entries[j]; 4418c2ecf20Sopenharmony_ci ioat_chan = ioat_chan_by_index(ioat_dma, j); 4428c2ecf20Sopenharmony_ci devm_free_irq(dev, msix->vector, ioat_chan); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci goto msi; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; 4488c2ecf20Sopenharmony_ci ioat_dma->irq_mode = IOAT_MSIX; 4498c2ecf20Sopenharmony_ci goto done; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cimsi: 4528c2ecf20Sopenharmony_ci err = pci_enable_msi(pdev); 4538c2ecf20Sopenharmony_ci if (err) 4548c2ecf20Sopenharmony_ci goto intx; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, 0, 4578c2ecf20Sopenharmony_ci "ioat-msi", ioat_dma); 4588c2ecf20Sopenharmony_ci if (err) { 4598c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 4608c2ecf20Sopenharmony_ci goto intx; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci ioat_dma->irq_mode = IOAT_MSI; 4638c2ecf20Sopenharmony_ci goto done; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ciintx: 4668c2ecf20Sopenharmony_ci err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, 4678c2ecf20Sopenharmony_ci IRQF_SHARED, "ioat-intx", ioat_dma); 4688c2ecf20Sopenharmony_ci if (err) 4698c2ecf20Sopenharmony_ci goto err_no_irq; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ioat_dma->irq_mode = IOAT_INTX; 4728c2ecf20Sopenharmony_cidone: 4738c2ecf20Sopenharmony_ci if (is_bwd_ioat(pdev)) 4748c2ecf20Sopenharmony_ci ioat_intr_quirk(ioat_dma); 4758c2ecf20Sopenharmony_ci intrctrl |= IOAT_INTRCTRL_MASTER_INT_EN; 4768c2ecf20Sopenharmony_ci writeb(intrctrl, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cierr_no_irq: 4808c2ecf20Sopenharmony_ci /* Disable all interrupt generation */ 4818c2ecf20Sopenharmony_ci writeb(0, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); 4828c2ecf20Sopenharmony_ci ioat_dma->irq_mode = IOAT_NOIRQ; 4838c2ecf20Sopenharmony_ci dev_err(dev, "no usable interrupts\n"); 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void ioat_disable_interrupts(struct ioatdma_device *ioat_dma) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci /* Disable all interrupt generation */ 4908c2ecf20Sopenharmony_ci writeb(0, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int ioat_probe(struct ioatdma_device *ioat_dma) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci int err = -ENODEV; 4968c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 4978c2ecf20Sopenharmony_ci struct pci_dev *pdev = ioat_dma->pdev; 4988c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ioat_dma->completion_pool = dma_pool_create("completion_pool", dev, 5018c2ecf20Sopenharmony_ci sizeof(u64), 5028c2ecf20Sopenharmony_ci SMP_CACHE_BYTES, 5038c2ecf20Sopenharmony_ci SMP_CACHE_BYTES); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!ioat_dma->completion_pool) { 5068c2ecf20Sopenharmony_ci err = -ENOMEM; 5078c2ecf20Sopenharmony_ci goto err_out; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ioat_enumerate_channels(ioat_dma); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma->cap_mask); 5138c2ecf20Sopenharmony_ci dma->dev = &pdev->dev; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (!dma->chancnt) { 5168c2ecf20Sopenharmony_ci dev_err(dev, "channel enumeration error\n"); 5178c2ecf20Sopenharmony_ci goto err_setup_interrupts; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci err = ioat_dma_setup_interrupts(ioat_dma); 5218c2ecf20Sopenharmony_ci if (err) 5228c2ecf20Sopenharmony_ci goto err_setup_interrupts; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci err = ioat3_dma_self_test(ioat_dma); 5258c2ecf20Sopenharmony_ci if (err) 5268c2ecf20Sopenharmony_ci goto err_self_test; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cierr_self_test: 5318c2ecf20Sopenharmony_ci ioat_disable_interrupts(ioat_dma); 5328c2ecf20Sopenharmony_cierr_setup_interrupts: 5338c2ecf20Sopenharmony_ci dma_pool_destroy(ioat_dma->completion_pool); 5348c2ecf20Sopenharmony_cierr_out: 5358c2ecf20Sopenharmony_ci return err; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int ioat_register(struct ioatdma_device *ioat_dma) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci int err = dma_async_device_register(&ioat_dma->dma_dev); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (err) { 5438c2ecf20Sopenharmony_ci ioat_disable_interrupts(ioat_dma); 5448c2ecf20Sopenharmony_ci dma_pool_destroy(ioat_dma->completion_pool); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return err; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void ioat_dma_remove(struct ioatdma_device *ioat_dma) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ioat_disable_interrupts(ioat_dma); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ioat_kobject_del(ioat_dma); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dma_async_device_unregister(dma); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * ioat_enumerate_channels - find and initialize the device's channels 5638c2ecf20Sopenharmony_ci * @ioat_dma: the ioat dma device to be enumerated 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_cistatic void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 5688c2ecf20Sopenharmony_ci struct device *dev = &ioat_dma->pdev->dev; 5698c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 5708c2ecf20Sopenharmony_ci u8 xfercap_log; 5718c2ecf20Sopenharmony_ci int i; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma->channels); 5748c2ecf20Sopenharmony_ci dma->chancnt = readb(ioat_dma->reg_base + IOAT_CHANCNT_OFFSET); 5758c2ecf20Sopenharmony_ci dma->chancnt &= 0x1f; /* bits [4:0] valid */ 5768c2ecf20Sopenharmony_ci if (dma->chancnt > ARRAY_SIZE(ioat_dma->idx)) { 5778c2ecf20Sopenharmony_ci dev_warn(dev, "(%d) exceeds max supported channels (%zu)\n", 5788c2ecf20Sopenharmony_ci dma->chancnt, ARRAY_SIZE(ioat_dma->idx)); 5798c2ecf20Sopenharmony_ci dma->chancnt = ARRAY_SIZE(ioat_dma->idx); 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci xfercap_log = readb(ioat_dma->reg_base + IOAT_XFERCAP_OFFSET); 5828c2ecf20Sopenharmony_ci xfercap_log &= 0x1f; /* bits [4:0] valid */ 5838c2ecf20Sopenharmony_ci if (xfercap_log == 0) 5848c2ecf20Sopenharmony_ci return; 5858c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci for (i = 0; i < dma->chancnt; i++) { 5888c2ecf20Sopenharmony_ci ioat_chan = kzalloc(sizeof(*ioat_chan), GFP_KERNEL); 5898c2ecf20Sopenharmony_ci if (!ioat_chan) 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci ioat_init_channel(ioat_dma, ioat_chan, i); 5938c2ecf20Sopenharmony_ci ioat_chan->xfercap_log = xfercap_log; 5948c2ecf20Sopenharmony_ci spin_lock_init(&ioat_chan->prep_lock); 5958c2ecf20Sopenharmony_ci if (ioat_reset_hw(ioat_chan)) { 5968c2ecf20Sopenharmony_ci i = 0; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci dma->chancnt = i; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/** 6048c2ecf20Sopenharmony_ci * ioat_free_chan_resources - release all the descriptors 6058c2ecf20Sopenharmony_ci * @c: the channel to be cleaned 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_cistatic void ioat_free_chan_resources(struct dma_chan *c) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 6108c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 6118c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 6128c2ecf20Sopenharmony_ci const int total_descs = 1 << ioat_chan->alloc_order; 6138c2ecf20Sopenharmony_ci int descs; 6148c2ecf20Sopenharmony_ci int i; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Before freeing channel resources first check 6178c2ecf20Sopenharmony_ci * if they have been previously allocated for this channel. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci if (!ioat_chan->ring) 6208c2ecf20Sopenharmony_ci return; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ioat_stop(ioat_chan); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!test_bit(IOAT_CHAN_DOWN, &ioat_chan->state)) { 6258c2ecf20Sopenharmony_ci ioat_reset_hw(ioat_chan); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Put LTR to idle */ 6288c2ecf20Sopenharmony_ci if (ioat_dma->version >= IOAT_VER_3_4) 6298c2ecf20Sopenharmony_ci writeb(IOAT_CHAN_LTR_SWSEL_IDLE, 6308c2ecf20Sopenharmony_ci ioat_chan->reg_base + 6318c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_SWSEL_OFFSET); 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->cleanup_lock); 6358c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 6368c2ecf20Sopenharmony_ci descs = ioat_ring_space(ioat_chan); 6378c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "freeing %d idle descriptors\n", descs); 6388c2ecf20Sopenharmony_ci for (i = 0; i < descs; i++) { 6398c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->head + i); 6408c2ecf20Sopenharmony_ci ioat_free_ring_ent(desc, c); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (descs < total_descs) 6448c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Freeing %d in use descriptors!\n", 6458c2ecf20Sopenharmony_ci total_descs - descs); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci for (i = 0; i < total_descs - descs; i++) { 6488c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail + i); 6498c2ecf20Sopenharmony_ci dump_desc_dbg(ioat_chan, desc); 6508c2ecf20Sopenharmony_ci ioat_free_ring_ent(desc, c); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci for (i = 0; i < ioat_chan->desc_chunks; i++) { 6548c2ecf20Sopenharmony_ci dma_free_coherent(to_dev(ioat_chan), IOAT_CHUNK_SIZE, 6558c2ecf20Sopenharmony_ci ioat_chan->descs[i].virt, 6568c2ecf20Sopenharmony_ci ioat_chan->descs[i].hw); 6578c2ecf20Sopenharmony_ci ioat_chan->descs[i].virt = NULL; 6588c2ecf20Sopenharmony_ci ioat_chan->descs[i].hw = 0; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci ioat_chan->desc_chunks = 0; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci kfree(ioat_chan->ring); 6638c2ecf20Sopenharmony_ci ioat_chan->ring = NULL; 6648c2ecf20Sopenharmony_ci ioat_chan->alloc_order = 0; 6658c2ecf20Sopenharmony_ci dma_pool_free(ioat_dma->completion_pool, ioat_chan->completion, 6668c2ecf20Sopenharmony_ci ioat_chan->completion_dma); 6678c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 6688c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->cleanup_lock); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ioat_chan->last_completion = 0; 6718c2ecf20Sopenharmony_ci ioat_chan->completion_dma = 0; 6728c2ecf20Sopenharmony_ci ioat_chan->dmacount = 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* ioat_alloc_chan_resources - allocate/initialize ioat descriptor ring 6768c2ecf20Sopenharmony_ci * @chan: channel to be initialized 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_cistatic int ioat_alloc_chan_resources(struct dma_chan *c) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 6818c2ecf20Sopenharmony_ci struct ioat_ring_ent **ring; 6828c2ecf20Sopenharmony_ci u64 status; 6838c2ecf20Sopenharmony_ci int order; 6848c2ecf20Sopenharmony_ci int i = 0; 6858c2ecf20Sopenharmony_ci u32 chanerr; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* have we already been set up? */ 6888c2ecf20Sopenharmony_ci if (ioat_chan->ring) 6898c2ecf20Sopenharmony_ci return 1 << ioat_chan->alloc_order; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Setup register to interrupt and write completion status on error */ 6928c2ecf20Sopenharmony_ci writew(IOAT_CHANCTRL_RUN, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* allocate a completion writeback area */ 6958c2ecf20Sopenharmony_ci /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ 6968c2ecf20Sopenharmony_ci ioat_chan->completion = 6978c2ecf20Sopenharmony_ci dma_pool_zalloc(ioat_chan->ioat_dma->completion_pool, 6988c2ecf20Sopenharmony_ci GFP_NOWAIT, &ioat_chan->completion_dma); 6998c2ecf20Sopenharmony_ci if (!ioat_chan->completion) 7008c2ecf20Sopenharmony_ci return -ENOMEM; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci writel(((u64)ioat_chan->completion_dma) & 0x00000000FFFFFFFF, 7038c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); 7048c2ecf20Sopenharmony_ci writel(((u64)ioat_chan->completion_dma) >> 32, 7058c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci order = IOAT_MAX_ORDER; 7088c2ecf20Sopenharmony_ci ring = ioat_alloc_ring(c, order, GFP_NOWAIT); 7098c2ecf20Sopenharmony_ci if (!ring) 7108c2ecf20Sopenharmony_ci return -ENOMEM; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->cleanup_lock); 7138c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 7148c2ecf20Sopenharmony_ci ioat_chan->ring = ring; 7158c2ecf20Sopenharmony_ci ioat_chan->head = 0; 7168c2ecf20Sopenharmony_ci ioat_chan->issued = 0; 7178c2ecf20Sopenharmony_ci ioat_chan->tail = 0; 7188c2ecf20Sopenharmony_ci ioat_chan->alloc_order = order; 7198c2ecf20Sopenharmony_ci set_bit(IOAT_RUN, &ioat_chan->state); 7208c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 7218c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->cleanup_lock); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Setting up LTR values for 3.4 or later */ 7248c2ecf20Sopenharmony_ci if (ioat_chan->ioat_dma->version >= IOAT_VER_3_4) { 7258c2ecf20Sopenharmony_ci u32 lat_val; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci lat_val = IOAT_CHAN_LTR_ACTIVE_SNVAL | 7288c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_ACTIVE_SNLATSCALE | 7298c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_ACTIVE_SNREQMNT; 7308c2ecf20Sopenharmony_ci writel(lat_val, ioat_chan->reg_base + 7318c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_ACTIVE_OFFSET); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci lat_val = IOAT_CHAN_LTR_IDLE_SNVAL | 7348c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_IDLE_SNLATSCALE | 7358c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_IDLE_SNREQMNT; 7368c2ecf20Sopenharmony_ci writel(lat_val, ioat_chan->reg_base + 7378c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_IDLE_OFFSET); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Select to active */ 7408c2ecf20Sopenharmony_ci writeb(IOAT_CHAN_LTR_SWSEL_ACTIVE, 7418c2ecf20Sopenharmony_ci ioat_chan->reg_base + 7428c2ecf20Sopenharmony_ci IOAT_CHAN_LTR_SWSEL_OFFSET); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ioat_start_null_desc(ioat_chan); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* check that we got off the ground */ 7488c2ecf20Sopenharmony_ci do { 7498c2ecf20Sopenharmony_ci udelay(1); 7508c2ecf20Sopenharmony_ci status = ioat_chansts(ioat_chan); 7518c2ecf20Sopenharmony_ci } while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (is_ioat_active(status) || is_ioat_idle(status)) 7548c2ecf20Sopenharmony_ci return 1 << ioat_chan->alloc_order; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci dev_WARN(to_dev(ioat_chan), 7598c2ecf20Sopenharmony_ci "failed to start channel chanerr: %#x\n", chanerr); 7608c2ecf20Sopenharmony_ci ioat_free_chan_resources(c); 7618c2ecf20Sopenharmony_ci return -EFAULT; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* common channel initialization */ 7658c2ecf20Sopenharmony_cistatic void 7668c2ecf20Sopenharmony_ciioat_init_channel(struct ioatdma_device *ioat_dma, 7678c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan, int idx) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ioat_chan->ioat_dma = ioat_dma; 7728c2ecf20Sopenharmony_ci ioat_chan->reg_base = ioat_dma->reg_base + (0x80 * (idx + 1)); 7738c2ecf20Sopenharmony_ci spin_lock_init(&ioat_chan->cleanup_lock); 7748c2ecf20Sopenharmony_ci ioat_chan->dma_chan.device = dma; 7758c2ecf20Sopenharmony_ci dma_cookie_init(&ioat_chan->dma_chan); 7768c2ecf20Sopenharmony_ci list_add_tail(&ioat_chan->dma_chan.device_node, &dma->channels); 7778c2ecf20Sopenharmony_ci ioat_dma->idx[idx] = ioat_chan; 7788c2ecf20Sopenharmony_ci timer_setup(&ioat_chan->timer, ioat_timer_event, 0); 7798c2ecf20Sopenharmony_ci tasklet_setup(&ioat_chan->cleanup_task, ioat_cleanup_event); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci#define IOAT_NUM_SRC_TEST 6 /* must be <= 8 */ 7838c2ecf20Sopenharmony_cistatic int ioat_xor_val_self_test(struct ioatdma_device *ioat_dma) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci int i, src_idx; 7868c2ecf20Sopenharmony_ci struct page *dest; 7878c2ecf20Sopenharmony_ci struct page *xor_srcs[IOAT_NUM_SRC_TEST]; 7888c2ecf20Sopenharmony_ci struct page *xor_val_srcs[IOAT_NUM_SRC_TEST + 1]; 7898c2ecf20Sopenharmony_ci dma_addr_t dma_srcs[IOAT_NUM_SRC_TEST + 1]; 7908c2ecf20Sopenharmony_ci dma_addr_t dest_dma; 7918c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 7928c2ecf20Sopenharmony_ci struct dma_chan *dma_chan; 7938c2ecf20Sopenharmony_ci dma_cookie_t cookie; 7948c2ecf20Sopenharmony_ci u8 cmp_byte = 0; 7958c2ecf20Sopenharmony_ci u32 cmp_word; 7968c2ecf20Sopenharmony_ci u32 xor_val_result; 7978c2ecf20Sopenharmony_ci int err = 0; 7988c2ecf20Sopenharmony_ci struct completion cmp; 7998c2ecf20Sopenharmony_ci unsigned long tmo; 8008c2ecf20Sopenharmony_ci struct device *dev = &ioat_dma->pdev->dev; 8018c2ecf20Sopenharmony_ci struct dma_device *dma = &ioat_dma->dma_dev; 8028c2ecf20Sopenharmony_ci u8 op = 0; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (!dma_has_cap(DMA_XOR, dma->cap_mask)) 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) { 8108c2ecf20Sopenharmony_ci xor_srcs[src_idx] = alloc_page(GFP_KERNEL); 8118c2ecf20Sopenharmony_ci if (!xor_srcs[src_idx]) { 8128c2ecf20Sopenharmony_ci while (src_idx--) 8138c2ecf20Sopenharmony_ci __free_page(xor_srcs[src_idx]); 8148c2ecf20Sopenharmony_ci return -ENOMEM; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci dest = alloc_page(GFP_KERNEL); 8198c2ecf20Sopenharmony_ci if (!dest) { 8208c2ecf20Sopenharmony_ci while (src_idx--) 8218c2ecf20Sopenharmony_ci __free_page(xor_srcs[src_idx]); 8228c2ecf20Sopenharmony_ci return -ENOMEM; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Fill in src buffers */ 8268c2ecf20Sopenharmony_ci for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) { 8278c2ecf20Sopenharmony_ci u8 *ptr = page_address(xor_srcs[src_idx]); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci for (i = 0; i < PAGE_SIZE; i++) 8308c2ecf20Sopenharmony_ci ptr[i] = (1 << src_idx); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) 8348c2ecf20Sopenharmony_ci cmp_byte ^= (u8) (1 << src_idx); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci cmp_word = (cmp_byte << 24) | (cmp_byte << 16) | 8378c2ecf20Sopenharmony_ci (cmp_byte << 8) | cmp_byte; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci memset(page_address(dest), 0, PAGE_SIZE); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci dma_chan = container_of(dma->channels.next, struct dma_chan, 8428c2ecf20Sopenharmony_ci device_node); 8438c2ecf20Sopenharmony_ci if (dma->device_alloc_chan_resources(dma_chan) < 1) { 8448c2ecf20Sopenharmony_ci err = -ENODEV; 8458c2ecf20Sopenharmony_ci goto out; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* test xor */ 8498c2ecf20Sopenharmony_ci op = IOAT_OP_XOR; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); 8528c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dest_dma)) { 8538c2ecf20Sopenharmony_ci err = -ENOMEM; 8548c2ecf20Sopenharmony_ci goto free_resources; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST; i++) { 8588c2ecf20Sopenharmony_ci dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE, 8598c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 8608c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_srcs[i])) { 8618c2ecf20Sopenharmony_ci err = -ENOMEM; 8628c2ecf20Sopenharmony_ci goto dma_unmap; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs, 8668c2ecf20Sopenharmony_ci IOAT_NUM_SRC_TEST, PAGE_SIZE, 8678c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!tx) { 8708c2ecf20Sopenharmony_ci dev_err(dev, "Self-test xor prep failed\n"); 8718c2ecf20Sopenharmony_ci err = -ENODEV; 8728c2ecf20Sopenharmony_ci goto dma_unmap; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci async_tx_ack(tx); 8768c2ecf20Sopenharmony_ci init_completion(&cmp); 8778c2ecf20Sopenharmony_ci tx->callback = ioat_dma_test_callback; 8788c2ecf20Sopenharmony_ci tx->callback_param = &cmp; 8798c2ecf20Sopenharmony_ci cookie = tx->tx_submit(tx); 8808c2ecf20Sopenharmony_ci if (cookie < 0) { 8818c2ecf20Sopenharmony_ci dev_err(dev, "Self-test xor setup failed\n"); 8828c2ecf20Sopenharmony_ci err = -ENODEV; 8838c2ecf20Sopenharmony_ci goto dma_unmap; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci dma->device_issue_pending(dma_chan); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (tmo == 0 || 8908c2ecf20Sopenharmony_ci dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { 8918c2ecf20Sopenharmony_ci dev_err(dev, "Self-test xor timed out\n"); 8928c2ecf20Sopenharmony_ci err = -ENODEV; 8938c2ecf20Sopenharmony_ci goto dma_unmap; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST; i++) 8978c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); 9008c2ecf20Sopenharmony_ci for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { 9018c2ecf20Sopenharmony_ci u32 *ptr = page_address(dest); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (ptr[i] != cmp_word) { 9048c2ecf20Sopenharmony_ci dev_err(dev, "Self-test xor failed compare\n"); 9058c2ecf20Sopenharmony_ci err = -ENODEV; 9068c2ecf20Sopenharmony_ci goto free_resources; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* skip validate if the capability is not present */ 9148c2ecf20Sopenharmony_ci if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) 9158c2ecf20Sopenharmony_ci goto free_resources; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci op = IOAT_OP_XOR_VAL; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* validate the sources with the destintation page */ 9208c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST; i++) 9218c2ecf20Sopenharmony_ci xor_val_srcs[i] = xor_srcs[i]; 9228c2ecf20Sopenharmony_ci xor_val_srcs[i] = dest; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci xor_val_result = 1; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) { 9278c2ecf20Sopenharmony_ci dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, 9288c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9298c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_srcs[i])) { 9308c2ecf20Sopenharmony_ci err = -ENOMEM; 9318c2ecf20Sopenharmony_ci goto dma_unmap; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, 9358c2ecf20Sopenharmony_ci IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, 9368c2ecf20Sopenharmony_ci &xor_val_result, DMA_PREP_INTERRUPT); 9378c2ecf20Sopenharmony_ci if (!tx) { 9388c2ecf20Sopenharmony_ci dev_err(dev, "Self-test zero prep failed\n"); 9398c2ecf20Sopenharmony_ci err = -ENODEV; 9408c2ecf20Sopenharmony_ci goto dma_unmap; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci async_tx_ack(tx); 9448c2ecf20Sopenharmony_ci init_completion(&cmp); 9458c2ecf20Sopenharmony_ci tx->callback = ioat_dma_test_callback; 9468c2ecf20Sopenharmony_ci tx->callback_param = &cmp; 9478c2ecf20Sopenharmony_ci cookie = tx->tx_submit(tx); 9488c2ecf20Sopenharmony_ci if (cookie < 0) { 9498c2ecf20Sopenharmony_ci dev_err(dev, "Self-test zero setup failed\n"); 9508c2ecf20Sopenharmony_ci err = -ENODEV; 9518c2ecf20Sopenharmony_ci goto dma_unmap; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci dma->device_issue_pending(dma_chan); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (tmo == 0 || 9588c2ecf20Sopenharmony_ci dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { 9598c2ecf20Sopenharmony_ci dev_err(dev, "Self-test validate timed out\n"); 9608c2ecf20Sopenharmony_ci err = -ENODEV; 9618c2ecf20Sopenharmony_ci goto dma_unmap; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) 9658c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (xor_val_result != 0) { 9688c2ecf20Sopenharmony_ci dev_err(dev, "Self-test validate failed compare\n"); 9698c2ecf20Sopenharmony_ci err = -ENODEV; 9708c2ecf20Sopenharmony_ci goto free_resources; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci memset(page_address(dest), 0, PAGE_SIZE); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* test for non-zero parity sum */ 9768c2ecf20Sopenharmony_ci op = IOAT_OP_XOR_VAL; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci xor_val_result = 0; 9798c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) { 9808c2ecf20Sopenharmony_ci dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, 9818c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9828c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_srcs[i])) { 9838c2ecf20Sopenharmony_ci err = -ENOMEM; 9848c2ecf20Sopenharmony_ci goto dma_unmap; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, 9888c2ecf20Sopenharmony_ci IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, 9898c2ecf20Sopenharmony_ci &xor_val_result, DMA_PREP_INTERRUPT); 9908c2ecf20Sopenharmony_ci if (!tx) { 9918c2ecf20Sopenharmony_ci dev_err(dev, "Self-test 2nd zero prep failed\n"); 9928c2ecf20Sopenharmony_ci err = -ENODEV; 9938c2ecf20Sopenharmony_ci goto dma_unmap; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci async_tx_ack(tx); 9978c2ecf20Sopenharmony_ci init_completion(&cmp); 9988c2ecf20Sopenharmony_ci tx->callback = ioat_dma_test_callback; 9998c2ecf20Sopenharmony_ci tx->callback_param = &cmp; 10008c2ecf20Sopenharmony_ci cookie = tx->tx_submit(tx); 10018c2ecf20Sopenharmony_ci if (cookie < 0) { 10028c2ecf20Sopenharmony_ci dev_err(dev, "Self-test 2nd zero setup failed\n"); 10038c2ecf20Sopenharmony_ci err = -ENODEV; 10048c2ecf20Sopenharmony_ci goto dma_unmap; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci dma->device_issue_pending(dma_chan); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (tmo == 0 || 10118c2ecf20Sopenharmony_ci dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { 10128c2ecf20Sopenharmony_ci dev_err(dev, "Self-test 2nd validate timed out\n"); 10138c2ecf20Sopenharmony_ci err = -ENODEV; 10148c2ecf20Sopenharmony_ci goto dma_unmap; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (xor_val_result != SUM_CHECK_P_RESULT) { 10188c2ecf20Sopenharmony_ci dev_err(dev, "Self-test validate failed compare\n"); 10198c2ecf20Sopenharmony_ci err = -ENODEV; 10208c2ecf20Sopenharmony_ci goto dma_unmap; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) 10248c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci goto free_resources; 10278c2ecf20Sopenharmony_cidma_unmap: 10288c2ecf20Sopenharmony_ci if (op == IOAT_OP_XOR) { 10298c2ecf20Sopenharmony_ci while (--i >= 0) 10308c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, 10318c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 10328c2ecf20Sopenharmony_ci dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); 10338c2ecf20Sopenharmony_ci } else if (op == IOAT_OP_XOR_VAL) { 10348c2ecf20Sopenharmony_ci while (--i >= 0) 10358c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, 10368c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_cifree_resources: 10398c2ecf20Sopenharmony_ci dma->device_free_chan_resources(dma_chan); 10408c2ecf20Sopenharmony_ciout: 10418c2ecf20Sopenharmony_ci src_idx = IOAT_NUM_SRC_TEST; 10428c2ecf20Sopenharmony_ci while (src_idx--) 10438c2ecf20Sopenharmony_ci __free_page(xor_srcs[src_idx]); 10448c2ecf20Sopenharmony_ci __free_page(dest); 10458c2ecf20Sopenharmony_ci return err; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic int ioat3_dma_self_test(struct ioatdma_device *ioat_dma) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci int rc; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci rc = ioat_dma_self_test(ioat_dma); 10538c2ecf20Sopenharmony_ci if (rc) 10548c2ecf20Sopenharmony_ci return rc; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci rc = ioat_xor_val_self_test(ioat_dma); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci return rc; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic void ioat_intr_quirk(struct ioatdma_device *ioat_dma) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct dma_device *dma; 10648c2ecf20Sopenharmony_ci struct dma_chan *c; 10658c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 10668c2ecf20Sopenharmony_ci u32 errmask; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci dma = &ioat_dma->dma_dev; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* 10718c2ecf20Sopenharmony_ci * if we have descriptor write back error status, we mask the 10728c2ecf20Sopenharmony_ci * error interrupts 10738c2ecf20Sopenharmony_ci */ 10748c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_DWBES) { 10758c2ecf20Sopenharmony_ci list_for_each_entry(c, &dma->channels, device_node) { 10768c2ecf20Sopenharmony_ci ioat_chan = to_ioat_chan(c); 10778c2ecf20Sopenharmony_ci errmask = readl(ioat_chan->reg_base + 10788c2ecf20Sopenharmony_ci IOAT_CHANERR_MASK_OFFSET); 10798c2ecf20Sopenharmony_ci errmask |= IOAT_CHANERR_XOR_P_OR_CRC_ERR | 10808c2ecf20Sopenharmony_ci IOAT_CHANERR_XOR_Q_ERR; 10818c2ecf20Sopenharmony_ci writel(errmask, ioat_chan->reg_base + 10828c2ecf20Sopenharmony_ci IOAT_CHANERR_MASK_OFFSET); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int ioat3_dma_probe(struct ioatdma_device *ioat_dma, int dca) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct pci_dev *pdev = ioat_dma->pdev; 10908c2ecf20Sopenharmony_ci int dca_en = system_has_dca_enabled(pdev); 10918c2ecf20Sopenharmony_ci struct dma_device *dma; 10928c2ecf20Sopenharmony_ci struct dma_chan *c; 10938c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 10948c2ecf20Sopenharmony_ci int err; 10958c2ecf20Sopenharmony_ci u16 val16; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci dma = &ioat_dma->dma_dev; 10988c2ecf20Sopenharmony_ci dma->device_prep_dma_memcpy = ioat_dma_prep_memcpy_lock; 10998c2ecf20Sopenharmony_ci dma->device_issue_pending = ioat_issue_pending; 11008c2ecf20Sopenharmony_ci dma->device_alloc_chan_resources = ioat_alloc_chan_resources; 11018c2ecf20Sopenharmony_ci dma->device_free_chan_resources = ioat_free_chan_resources; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERRUPT, dma->cap_mask); 11048c2ecf20Sopenharmony_ci dma->device_prep_dma_interrupt = ioat_prep_interrupt_lock; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ioat_dma->cap = readl(ioat_dma->reg_base + IOAT_DMA_CAP_OFFSET); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (is_xeon_cb32(pdev) || is_bwd_noraid(pdev)) 11098c2ecf20Sopenharmony_ci ioat_dma->cap &= 11108c2ecf20Sopenharmony_ci ~(IOAT_CAP_XOR | IOAT_CAP_PQ | IOAT_CAP_RAID16SS); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* dca is incompatible with raid operations */ 11138c2ecf20Sopenharmony_ci if (dca_en && (ioat_dma->cap & (IOAT_CAP_XOR|IOAT_CAP_PQ))) 11148c2ecf20Sopenharmony_ci ioat_dma->cap &= ~(IOAT_CAP_XOR|IOAT_CAP_PQ); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_XOR) { 11178c2ecf20Sopenharmony_ci dma->max_xor = 8; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR, dma->cap_mask); 11208c2ecf20Sopenharmony_ci dma->device_prep_dma_xor = ioat_prep_xor; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR_VAL, dma->cap_mask); 11238c2ecf20Sopenharmony_ci dma->device_prep_dma_xor_val = ioat_prep_xor_val; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_PQ) { 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci dma->device_prep_dma_pq = ioat_prep_pq; 11298c2ecf20Sopenharmony_ci dma->device_prep_dma_pq_val = ioat_prep_pq_val; 11308c2ecf20Sopenharmony_ci dma_cap_set(DMA_PQ, dma->cap_mask); 11318c2ecf20Sopenharmony_ci dma_cap_set(DMA_PQ_VAL, dma->cap_mask); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_RAID16SS) 11348c2ecf20Sopenharmony_ci dma_set_maxpq(dma, 16, 0); 11358c2ecf20Sopenharmony_ci else 11368c2ecf20Sopenharmony_ci dma_set_maxpq(dma, 8, 0); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (!(ioat_dma->cap & IOAT_CAP_XOR)) { 11398c2ecf20Sopenharmony_ci dma->device_prep_dma_xor = ioat_prep_pqxor; 11408c2ecf20Sopenharmony_ci dma->device_prep_dma_xor_val = ioat_prep_pqxor_val; 11418c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR, dma->cap_mask); 11428c2ecf20Sopenharmony_ci dma_cap_set(DMA_XOR_VAL, dma->cap_mask); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_RAID16SS) 11458c2ecf20Sopenharmony_ci dma->max_xor = 16; 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci dma->max_xor = 8; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci dma->device_tx_status = ioat_tx_status; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* starting with CB3.3 super extended descriptors are supported */ 11548c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_RAID16SS) { 11558c2ecf20Sopenharmony_ci char pool_name[14]; 11568c2ecf20Sopenharmony_ci int i; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SED_POOLS; i++) { 11598c2ecf20Sopenharmony_ci snprintf(pool_name, 14, "ioat_hw%d_sed", i); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* allocate SED DMA pool */ 11628c2ecf20Sopenharmony_ci ioat_dma->sed_hw_pool[i] = dmam_pool_create(pool_name, 11638c2ecf20Sopenharmony_ci &pdev->dev, 11648c2ecf20Sopenharmony_ci SED_SIZE * (i + 1), 64, 0); 11658c2ecf20Sopenharmony_ci if (!ioat_dma->sed_hw_pool[i]) 11668c2ecf20Sopenharmony_ci return -ENOMEM; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (!(ioat_dma->cap & (IOAT_CAP_XOR | IOAT_CAP_PQ))) 11728c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, dma->cap_mask); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci err = ioat_probe(ioat_dma); 11758c2ecf20Sopenharmony_ci if (err) 11768c2ecf20Sopenharmony_ci return err; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci list_for_each_entry(c, &dma->channels, device_node) { 11798c2ecf20Sopenharmony_ci ioat_chan = to_ioat_chan(c); 11808c2ecf20Sopenharmony_ci writel(IOAT_DMA_DCA_ANY_CPU, 11818c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_DCACTRL_OFFSET); 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci err = ioat_register(ioat_dma); 11858c2ecf20Sopenharmony_ci if (err) 11868c2ecf20Sopenharmony_ci return err; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci ioat_kobject_add(ioat_dma, &ioat_ktype); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (dca) 11918c2ecf20Sopenharmony_ci ioat_dma->dca = ioat_dca_init(pdev, ioat_dma->reg_base); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* disable relaxed ordering */ 11948c2ecf20Sopenharmony_ci err = pcie_capability_read_word(pdev, IOAT_DEVCTRL_OFFSET, &val16); 11958c2ecf20Sopenharmony_ci if (err) 11968c2ecf20Sopenharmony_ci return pcibios_err_to_errno(err); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci /* clear relaxed ordering enable */ 11998c2ecf20Sopenharmony_ci val16 &= ~IOAT_DEVCTRL_ROE; 12008c2ecf20Sopenharmony_ci err = pcie_capability_write_word(pdev, IOAT_DEVCTRL_OFFSET, val16); 12018c2ecf20Sopenharmony_ci if (err) 12028c2ecf20Sopenharmony_ci return pcibios_err_to_errno(err); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_DPS) 12058c2ecf20Sopenharmony_ci writeb(ioat_pending_level + 1, 12068c2ecf20Sopenharmony_ci ioat_dma->reg_base + IOAT_PREFETCH_LIMIT_OFFSET); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic void ioat_shutdown(struct pci_dev *pdev) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = pci_get_drvdata(pdev); 12148c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 12158c2ecf20Sopenharmony_ci int i; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (!ioat_dma) 12188c2ecf20Sopenharmony_ci return; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_MAX_CHANS; i++) { 12218c2ecf20Sopenharmony_ci ioat_chan = ioat_dma->idx[i]; 12228c2ecf20Sopenharmony_ci if (!ioat_chan) 12238c2ecf20Sopenharmony_ci continue; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 12268c2ecf20Sopenharmony_ci set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); 12278c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 12288c2ecf20Sopenharmony_ci /* 12298c2ecf20Sopenharmony_ci * Synchronization rule for del_timer_sync(): 12308c2ecf20Sopenharmony_ci * - The caller must not hold locks which would prevent 12318c2ecf20Sopenharmony_ci * completion of the timer's handler. 12328c2ecf20Sopenharmony_ci * So prep_lock cannot be held before calling it. 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci del_timer_sync(&ioat_chan->timer); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci /* this should quiesce then reset */ 12378c2ecf20Sopenharmony_ci ioat_reset_hw(ioat_chan); 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci ioat_disable_interrupts(ioat_dma); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic void ioat_resume(struct ioatdma_device *ioat_dma) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 12468c2ecf20Sopenharmony_ci u32 chanerr; 12478c2ecf20Sopenharmony_ci int i; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_MAX_CHANS; i++) { 12508c2ecf20Sopenharmony_ci ioat_chan = ioat_dma->idx[i]; 12518c2ecf20Sopenharmony_ci if (!ioat_chan) 12528c2ecf20Sopenharmony_ci continue; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 12558c2ecf20Sopenharmony_ci clear_bit(IOAT_CHAN_DOWN, &ioat_chan->state); 12568c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 12598c2ecf20Sopenharmony_ci writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci /* no need to reset as shutdown already did that */ 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci#define DRV_NAME "ioatdma" 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic pci_ers_result_t ioat_pcie_error_detected(struct pci_dev *pdev, 12688c2ecf20Sopenharmony_ci pci_channel_state_t error) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s: PCIe AER error %d\n", DRV_NAME, error); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* quiesce and block I/O */ 12738c2ecf20Sopenharmony_ci ioat_shutdown(pdev); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s post reset handling\n", DRV_NAME); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (pci_enable_device_mem(pdev) < 0) { 12858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 12868c2ecf20Sopenharmony_ci "Failed to enable PCIe device after reset.\n"); 12878c2ecf20Sopenharmony_ci result = PCI_ERS_RESULT_DISCONNECT; 12888c2ecf20Sopenharmony_ci } else { 12898c2ecf20Sopenharmony_ci pci_set_master(pdev); 12908c2ecf20Sopenharmony_ci pci_restore_state(pdev); 12918c2ecf20Sopenharmony_ci pci_save_state(pdev); 12928c2ecf20Sopenharmony_ci pci_wake_from_d3(pdev, false); 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return result; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void ioat_pcie_error_resume(struct pci_dev *pdev) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = pci_get_drvdata(pdev); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s: AER handling resuming\n", DRV_NAME); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* initialize and bring everything back */ 13058c2ecf20Sopenharmony_ci ioat_resume(ioat_dma); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic const struct pci_error_handlers ioat_err_handler = { 13098c2ecf20Sopenharmony_ci .error_detected = ioat_pcie_error_detected, 13108c2ecf20Sopenharmony_ci .slot_reset = ioat_pcie_error_slot_reset, 13118c2ecf20Sopenharmony_ci .resume = ioat_pcie_error_resume, 13128c2ecf20Sopenharmony_ci}; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic struct pci_driver ioat_pci_driver = { 13158c2ecf20Sopenharmony_ci .name = DRV_NAME, 13168c2ecf20Sopenharmony_ci .id_table = ioat_pci_tbl, 13178c2ecf20Sopenharmony_ci .probe = ioat_pci_probe, 13188c2ecf20Sopenharmony_ci .remove = ioat_remove, 13198c2ecf20Sopenharmony_ci .shutdown = ioat_shutdown, 13208c2ecf20Sopenharmony_ci .err_handler = &ioat_err_handler, 13218c2ecf20Sopenharmony_ci}; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic void release_ioatdma(struct dma_device *device) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci struct ioatdma_device *d = to_ioatdma_device(device); 13268c2ecf20Sopenharmony_ci int i; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci for (i = 0; i < IOAT_MAX_CHANS; i++) 13298c2ecf20Sopenharmony_ci kfree(d->idx[i]); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci dma_pool_destroy(d->completion_pool); 13328c2ecf20Sopenharmony_ci kfree(d); 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic struct ioatdma_device * 13368c2ecf20Sopenharmony_cialloc_ioatdma(struct pci_dev *pdev, void __iomem *iobase) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct ioatdma_device *d = kzalloc(sizeof(*d), GFP_KERNEL); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (!d) 13418c2ecf20Sopenharmony_ci return NULL; 13428c2ecf20Sopenharmony_ci d->pdev = pdev; 13438c2ecf20Sopenharmony_ci d->reg_base = iobase; 13448c2ecf20Sopenharmony_ci d->dma_dev.device_release = release_ioatdma; 13458c2ecf20Sopenharmony_ci return d; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci void __iomem * const *iomap; 13518c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13528c2ecf20Sopenharmony_ci struct ioatdma_device *device; 13538c2ecf20Sopenharmony_ci int err; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci err = pcim_enable_device(pdev); 13568c2ecf20Sopenharmony_ci if (err) 13578c2ecf20Sopenharmony_ci return err; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci err = pcim_iomap_regions(pdev, 1 << IOAT_MMIO_BAR, DRV_NAME); 13608c2ecf20Sopenharmony_ci if (err) 13618c2ecf20Sopenharmony_ci return err; 13628c2ecf20Sopenharmony_ci iomap = pcim_iomap_table(pdev); 13638c2ecf20Sopenharmony_ci if (!iomap) 13648c2ecf20Sopenharmony_ci return -ENOMEM; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 13678c2ecf20Sopenharmony_ci if (err) 13688c2ecf20Sopenharmony_ci err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 13698c2ecf20Sopenharmony_ci if (err) 13708c2ecf20Sopenharmony_ci return err; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 13738c2ecf20Sopenharmony_ci if (err) 13748c2ecf20Sopenharmony_ci err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 13758c2ecf20Sopenharmony_ci if (err) 13768c2ecf20Sopenharmony_ci return err; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci device = alloc_ioatdma(pdev, iomap[IOAT_MMIO_BAR]); 13798c2ecf20Sopenharmony_ci if (!device) 13808c2ecf20Sopenharmony_ci return -ENOMEM; 13818c2ecf20Sopenharmony_ci pci_set_master(pdev); 13828c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, device); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci device->version = readb(device->reg_base + IOAT_VER_OFFSET); 13858c2ecf20Sopenharmony_ci if (device->version >= IOAT_VER_3_4) 13868c2ecf20Sopenharmony_ci ioat_dca_enabled = 0; 13878c2ecf20Sopenharmony_ci if (device->version >= IOAT_VER_3_0) { 13888c2ecf20Sopenharmony_ci if (is_skx_ioat(pdev)) 13898c2ecf20Sopenharmony_ci device->version = IOAT_VER_3_2; 13908c2ecf20Sopenharmony_ci err = ioat3_dma_probe(device, ioat_dca_enabled); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (device->version >= IOAT_VER_3_3) 13938c2ecf20Sopenharmony_ci pci_enable_pcie_error_reporting(pdev); 13948c2ecf20Sopenharmony_ci } else 13958c2ecf20Sopenharmony_ci return -ENODEV; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (err) { 13988c2ecf20Sopenharmony_ci dev_err(dev, "Intel(R) I/OAT DMA Engine init failed\n"); 13998c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 14008c2ecf20Sopenharmony_ci return -ENODEV; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci return 0; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic void ioat_remove(struct pci_dev *pdev) 14078c2ecf20Sopenharmony_ci{ 14088c2ecf20Sopenharmony_ci struct ioatdma_device *device = pci_get_drvdata(pdev); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (!device) 14118c2ecf20Sopenharmony_ci return; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci ioat_shutdown(pdev); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Removing dma and dca services\n"); 14168c2ecf20Sopenharmony_ci if (device->dca) { 14178c2ecf20Sopenharmony_ci unregister_dca_provider(device->dca, &pdev->dev); 14188c2ecf20Sopenharmony_ci free_dca_provider(device->dca); 14198c2ecf20Sopenharmony_ci device->dca = NULL; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 14238c2ecf20Sopenharmony_ci ioat_dma_remove(device); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int __init ioat_init_module(void) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci int err = -ENOMEM; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci pr_info("%s: Intel(R) QuickData Technology Driver %s\n", 14318c2ecf20Sopenharmony_ci DRV_NAME, IOAT_DMA_VERSION); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci ioat_cache = kmem_cache_create("ioat", sizeof(struct ioat_ring_ent), 14348c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, NULL); 14358c2ecf20Sopenharmony_ci if (!ioat_cache) 14368c2ecf20Sopenharmony_ci return -ENOMEM; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci ioat_sed_cache = KMEM_CACHE(ioat_sed_ent, 0); 14398c2ecf20Sopenharmony_ci if (!ioat_sed_cache) 14408c2ecf20Sopenharmony_ci goto err_ioat_cache; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci err = pci_register_driver(&ioat_pci_driver); 14438c2ecf20Sopenharmony_ci if (err) 14448c2ecf20Sopenharmony_ci goto err_ioat3_cache; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci return 0; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci err_ioat3_cache: 14498c2ecf20Sopenharmony_ci kmem_cache_destroy(ioat_sed_cache); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci err_ioat_cache: 14528c2ecf20Sopenharmony_ci kmem_cache_destroy(ioat_cache); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return err; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_cimodule_init(ioat_init_module); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_cistatic void __exit ioat_exit_module(void) 14598c2ecf20Sopenharmony_ci{ 14608c2ecf20Sopenharmony_ci pci_unregister_driver(&ioat_pci_driver); 14618c2ecf20Sopenharmony_ci kmem_cache_destroy(ioat_cache); 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_cimodule_exit(ioat_exit_module); 1464