Commit 4332dbb0 authored by leoliu-oc's avatar leoliu-oc
Browse files

Add kh40000_direct_dma_ops for KH-40000 platform

zhaoxin inclusion
category: other
bugzilla: https://gitee.com/openeuler/kernel/issues/I9ARTM


CVE: NA

--------------------------------------------

Add 'kh40000_direct_dma_ops' to replace 'direct_dma_ops' for KH-40000
platform.
For coherent DMA access, memory can be allocated only from the memory node
of the node where the device resides.
For streaming DMA access, add a PCI read operation at the end of DMA
access.

Signed-off-by: default avatarleoliu-oc <leoliu-oc@zhaoxin.com>
parent 0f97a828
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -9,4 +9,11 @@ static inline const struct dma_map_ops *get_arch_dma_ops(void)
	return dma_ops;
}

#ifdef CONFIG_PCI

extern bool is_zhaoxin_kh40000;
extern const struct dma_map_ops kh40000_dma_direct_ops;

#endif

#endif
+1 −0
Original line number Diff line number Diff line
@@ -160,4 +160,5 @@ ifeq ($(CONFIG_X86_64),y)

	obj-$(CONFIG_MMCONF_FAM10H)	+= mmconf-fam10h_64.o
	obj-y				+= vsmp_64.o
	obj-$(CONFIG_PCI)		+= zhaoxin_kh40000.o
endif
+17 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <asm/gart.h>
#include <asm/irq_remapping.h>
#include <asm/early_ioremap.h>
#include <asm/dma-mapping.h>

static void __init early_pci_clear_msi(int bus, int slot, int func)
{
@@ -716,6 +717,18 @@ static void __init apple_airport_reset(int bus, int slot, int func)
	early_iounmap(mmio, BCM4331_MMIO_SIZE);
}

static void quirk_zhaoxin_dma_patch(int num, int slot, int func)
{
	u8 revision;

	revision = read_pci_config_byte(num, slot, func, PCI_REVISION_ID);
	if (revision == 0x10) {
		is_zhaoxin_kh40000 = true;
		dma_ops = &kh40000_dma_direct_ops;
		pr_info("zhaoxin direct dma patch enabled\n");
	}
}

#define QFLAG_APPLY_ONCE 	0x1
#define QFLAG_APPLIED		0x2
#define QFLAG_DONE		(QFLAG_APPLY_ONCE|QFLAG_APPLIED)
@@ -759,6 +772,10 @@ static struct chipset early_qrk[] __initdata = {
		PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet},
	{ PCI_VENDOR_ID_BROADCOM, 0x4331,
	  PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset},
	{ PCI_VENDOR_ID_ZHAOXIN, 0x1001, PCI_CLASS_BRIDGE_HOST,
	  PCI_BASE_CLASS_BRIDGE, QFLAG_APPLY_ONCE, quirk_zhaoxin_dma_patch },
	{ PCI_VENDOR_ID_ZHAOXIN, 0x345B, PCI_CLASS_BRIDGE_HOST,
	  PCI_BASE_CLASS_BRIDGE, QFLAG_APPLY_ONCE, quirk_zhaoxin_dma_patch },
	{ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, early_pci_clear_msi},
	{}
};
+178 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/dma-direct.h>
#include <linux/dma-map-ops.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/kstrtox.h>
#include <linux/pci.h>
#include <linux/pfn.h>
#include <linux/printk.h>
#include <linux/scatterlist.h>
#include <linux/types.h>

#include <asm/dma-mapping.h>

#include "../../../kernel/dma/direct.h"

bool is_zhaoxin_kh40000;

/***
 * usage:
 *  set "zhaoxin_patch_bitmask=<value>" in cmdline
 * value description:
 *  bit 0: enable(1) node check or not(0). default 1
 */
enum {
	ZHAOXIN_P2CW_NODE_CHECK = BIT(0),
	ZHAOXIN_PATCH_CODE_MAX  = ZHAOXIN_P2CW_NODE_CHECK,
};

#define ZHAOXIN_PATCH_CODE_DEFAULT	ZHAOXIN_P2CW_NODE_CHECK

unsigned long zhaoxin_patch_code = ZHAOXIN_PATCH_CODE_DEFAULT;

static int __init zhaoxin_patch_code_setup(char *str)
{
	int err = kstrtoul(str, 0, &zhaoxin_patch_code);

	if (err || (zhaoxin_patch_code > ZHAOXIN_PATCH_CODE_MAX)) {
		pr_err("cmdline 'zhaoxin_patch_bitmask=%s' inappropriate\n", str);
		zhaoxin_patch_code = ZHAOXIN_PATCH_CODE_DEFAULT;
		return err;
	}

	if (ZHAOXIN_P2CW_NODE_CHECK | zhaoxin_patch_code)
		pr_info("zhaoxin dma patch node check is enabled\n");

	return 0;
}
__setup("zhaoxin_patch_bitmask=", zhaoxin_patch_code_setup);

static struct pci_dev *kh40000_get_pci_dev(struct device *dev)
{
	if (dev_is_pci(dev))
		return to_pci_dev(dev);

	if (dev->parent)
		return kh40000_get_pci_dev(dev->parent);

	return NULL;
}

static void kh40000_sync_single_dma_for_cpu(struct device *dev, dma_addr_t paddr,
		enum dma_data_direction dir, bool is_iommu)
{
	u8 vid;
	struct pci_dev *pci;
	u64 dma_mask = *dev->dma_mask;

	/* check direction */
	if ((dir != DMA_FROM_DEVICE) && (dir != DMA_BIDIRECTIONAL))
		return;

	/* check dma capability */
	if (dma_mask <= DMA_BIT_MASK(32))
		return;

	/* check device type */
	pci = kh40000_get_pci_dev(dev);
	if (pci == NULL)
		return;

	/* get real physical address */
	if (is_iommu) {
		struct iommu_domain *domain = iommu_get_dma_domain(dev);

		paddr = iommu_iova_to_phys(domain, paddr);
		if (!paddr)
			return;
	}

	/* check node or not */
	if ((zhaoxin_patch_code & ZHAOXIN_P2CW_NODE_CHECK)
	   && pfn_to_nid(PFN_DOWN(paddr)) == dev_to_node(dev))
		return;

	/* flush data by one pci read cycle */
	pci_read_config_byte(pci, PCI_VENDOR_ID, &vid);
}

/* zhaoxin kh-40000 direct dma ops */
static void *kh40000_dma_direct_alloc(struct device *dev, size_t size,
		dma_addr_t *addr, gfp_t gfp, unsigned long attrs)
{
	if (dev->coherent_dma_mask > DMA_BIT_MASK(32))
		gfp |= __GFP_THISNODE;

	return dma_direct_alloc(dev, size, addr, gfp, attrs);
}

static void kh40000_dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	kh40000_sync_single_dma_for_cpu(dev, addr, dir, 0);
	dma_direct_unmap_page(dev, addr, size, dir, attrs);
}

static void kh40000_dma_direct_sync_sg_for_cpu(struct device *dev,
		struct scatterlist *sgl, int nents, enum dma_data_direction dir)
{
	struct scatterlist *sg;
	int i;

	for_each_sg(sgl,  sg, nents, i)
		kh40000_sync_single_dma_for_cpu(dev, sg_dma_address(sg), dir, 0);

	dma_direct_sync_sg_for_cpu(dev, sgl, nents, dir);
}

static void kh40000_dma_direct_sync_single_for_cpu(struct device *dev,
		dma_addr_t addr, size_t size, enum dma_data_direction dir)
{
	kh40000_sync_single_dma_for_cpu(dev, addr, dir, 0);
	dma_direct_sync_single_for_cpu(dev, addr, size, dir);
}

static void kh40000_dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl,
		int nents, enum dma_data_direction dir, unsigned long attrs)
{
	struct scatterlist *sg;
	int i;

	for_each_sg(sgl,  sg, nents, i)
		kh40000_sync_single_dma_for_cpu(dev, sg_dma_address(sg), dir, 0);

	dma_direct_unmap_sg(dev, sgl, nents, dir, attrs);
}

static void kh40000_dma_direct_unmap_resource(struct device *dev, dma_addr_t addr,
		size_t size, enum dma_data_direction dir, unsigned long attrs)
{
	kh40000_sync_single_dma_for_cpu(dev, addr, dir, 0);
}

const struct dma_map_ops kh40000_dma_direct_ops = {
	.flags			= DMA_F_PCI_P2PDMA_SUPPORTED,
	.alloc			= kh40000_dma_direct_alloc,
	.sync_sg_for_cpu	= kh40000_dma_direct_sync_sg_for_cpu,
	.unmap_page		= kh40000_dma_direct_unmap_page,
	.sync_single_for_cpu	= kh40000_dma_direct_sync_single_for_cpu,
	.unmap_sg		= kh40000_dma_direct_unmap_sg,
	.unmap_resource		= kh40000_dma_direct_unmap_resource,
	.dma_supported		= dma_direct_supported,
	.free			= dma_direct_free,
	.alloc_pages		= dma_direct_alloc_pages,
	.free_pages		= dma_direct_free_pages,
	.sync_single_for_device	= dma_direct_sync_single_for_device,
	.sync_sg_for_device	= dma_direct_sync_sg_for_device,
	.get_required_mask	= dma_direct_get_required_mask,
	.max_mapping_size	= dma_direct_max_mapping_size,
	.mmap			= dma_direct_mmap,
	.get_sgtable		= dma_direct_get_sgtable,
	.map_page		= dma_direct_map_page,
	.map_sg			= dma_direct_map_sg,
	.map_resource		= dma_direct_map_resource,
};
+4 −0
Original line number Diff line number Diff line
@@ -224,6 +224,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)

	dma_numa_cma_reserve();

#if defined(CONFIG_X86_64) && defined(CONFIG_PCI)
	if (is_zhaoxin_kh40000)
		return;
#endif
	pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);

	if (size_cmdline != -1) {