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