18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Loongson IOMMU Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020-2021 Loongson Technology Ltd.
68c2ecf20Sopenharmony_ci * Author:	Lv Chen <lvchen@loongson.cn>
78c2ecf20Sopenharmony_ci *		Wang Yang <wangyang@loongson.cn>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/iommu.h>
188c2ecf20Sopenharmony_ci#include <linux/list.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/acpi.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci#include <linux/pci_regs.h>
238c2ecf20Sopenharmony_ci#include <linux/printk.h>
248c2ecf20Sopenharmony_ci#include <linux/sizes.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
278c2ecf20Sopenharmony_ci#include "iommu.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define LOOP_TIMEOUT		100000
308c2ecf20Sopenharmony_ci#define IOVA_START		(SZ_256M)
318c2ecf20Sopenharmony_ci#define IOVA_END0		(SZ_2G + SZ_256M)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define IVRS_HEADER_LENGTH		48
348c2ecf20Sopenharmony_ci#define ACPI_IVHD_TYPE_MAX_SUPPORTED	0x40
358c2ecf20Sopenharmony_ci#define IVHD_DEV_ALL                    0x01
368c2ecf20Sopenharmony_ci#define IVHD_DEV_SELECT                 0x02
378c2ecf20Sopenharmony_ci#define IVHD_DEV_SELECT_RANGE_START     0x03
388c2ecf20Sopenharmony_ci#define IVHD_DEV_RANGE_END              0x04
398c2ecf20Sopenharmony_ci#define IVHD_DEV_ALIAS                  0x42
408c2ecf20Sopenharmony_ci#define IVHD_DEV_EXT_SELECT             0x46
418c2ecf20Sopenharmony_ci#define IVHD_DEV_ACPI_HID		0xf0
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define IVHD_HEAD_TYPE10		0x10
448c2ecf20Sopenharmony_ci#define IVHD_HEAD_TYPE11		0x11
458c2ecf20Sopenharmony_ci#define IVHD_HEAD_TYPE40		0x40
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define MAX_BDF_NUM			0xffff
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define RLOOKUP_TABLE_ENTRY_SIZE	(sizeof(void *))
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * structure describing one IOMMU in the ACPI table. Typically followed by one
538c2ecf20Sopenharmony_ci * or more ivhd_entrys.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistruct ivhd_header {
568c2ecf20Sopenharmony_ci	u8 type;
578c2ecf20Sopenharmony_ci	u8 flags;
588c2ecf20Sopenharmony_ci	u16 length;
598c2ecf20Sopenharmony_ci	u16 devid;
608c2ecf20Sopenharmony_ci	u16 cap_ptr;
618c2ecf20Sopenharmony_ci	u64 mmio_phys;
628c2ecf20Sopenharmony_ci	u16 pci_seg;
638c2ecf20Sopenharmony_ci	u16 info;
648c2ecf20Sopenharmony_ci	u32 efr_attr;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Following only valid on IVHD type 11h and 40h */
678c2ecf20Sopenharmony_ci	u64 efr_reg; /* Exact copy of MMIO_EXT_FEATURES */
688c2ecf20Sopenharmony_ci	u64 res;
698c2ecf20Sopenharmony_ci} __attribute__((packed));
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * A device entry describing which devices a specific IOMMU translates and
738c2ecf20Sopenharmony_ci * which requestor ids they use.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistruct ivhd_entry {
768c2ecf20Sopenharmony_ci	u8 type;
778c2ecf20Sopenharmony_ci	u16 devid;
788c2ecf20Sopenharmony_ci	u8 flags;
798c2ecf20Sopenharmony_ci	u32 ext;
808c2ecf20Sopenharmony_ci	u32 hidh;
818c2ecf20Sopenharmony_ci	u64 cid;
828c2ecf20Sopenharmony_ci	u8 uidf;
838c2ecf20Sopenharmony_ci	u8 uidl;
848c2ecf20Sopenharmony_ci	u8 uid;
858c2ecf20Sopenharmony_ci} __attribute__((packed));
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciLIST_HEAD(loongson_iommu_list);			/* list of all IOMMUs in the system */
888c2ecf20Sopenharmony_ciLIST_HEAD(loongson_rlookup_iommu_list);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic u32 rlookup_table_size;			/* size if the rlookup table */
918c2ecf20Sopenharmony_cistatic int loongson_iommu_target_ivhd_type;
928c2ecf20Sopenharmony_ciu16	loongson_iommu_last_bdf;		/* largest PCI device id we have to handle */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciint loongson_iommu_disable;
958c2ecf20Sopenharmony_cistatic struct iommu_ops loongson_iommu_ops;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void iommu_write_regl(loongson_iommu *iommu, unsigned long off, u32 val)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	*(u32 *)(iommu->membase + off) = val;
1008c2ecf20Sopenharmony_ci	iob();
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic u32 iommu_read_regl(loongson_iommu *iommu, unsigned long off)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 val;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	val = *(u32 *)(iommu->membase + off);
1088c2ecf20Sopenharmony_ci	iob();
1098c2ecf20Sopenharmony_ci	return val;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void iommu_translate_disable(loongson_iommu *iommu)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	u32 val;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (iommu == NULL) {
1178c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
1188c2ecf20Sopenharmony_ci		return;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_EIVDB);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Disable */
1248c2ecf20Sopenharmony_ci	val &= ~(1 << 31);
1258c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_EIVDB, val);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Write cmd */
1288c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_CMD);
1298c2ecf20Sopenharmony_ci	val &= 0xfffffffc;
1308c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_CMD, val);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void iommu_translate_enable(loongson_iommu *iommu)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	u32 val = 0;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (iommu == NULL) {
1388c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
1398c2ecf20Sopenharmony_ci		return;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_EIVDB);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Enable */
1458c2ecf20Sopenharmony_ci	val |= (1 << 31);
1468c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_EIVDB, val);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Write cmd */
1498c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_CMD);
1508c2ecf20Sopenharmony_ci	val &= 0xfffffffc;
1518c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_CMD, val);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic bool loongson_iommu_capable(enum iommu_cap cap)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	switch (cap) {
1578c2ecf20Sopenharmony_ci	case IOMMU_CAP_CACHE_COHERENCY:
1588c2ecf20Sopenharmony_ci		return true;
1598c2ecf20Sopenharmony_ci	default:
1608c2ecf20Sopenharmony_ci		return false;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic dom_info *to_dom_info(struct iommu_domain *dom)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return container_of(dom, dom_info, domain);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * Check whether the system has a priv.
1718c2ecf20Sopenharmony_ci * If yes, it returns 1 and if not, it returns 0
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic int has_dom(loongson_iommu *iommu)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	spin_lock(&iommu->dom_info_lock);
1768c2ecf20Sopenharmony_ci	while (!list_empty(&iommu->dom_list)) {
1778c2ecf20Sopenharmony_ci		spin_unlock(&iommu->dom_info_lock);
1788c2ecf20Sopenharmony_ci		return 1;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	spin_unlock(&iommu->dom_info_lock);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int update_dev_table(struct loongson_iommu_dev_data *dev_data, int flag)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	u32 val = 0;
1888c2ecf20Sopenharmony_ci	int index;
1898c2ecf20Sopenharmony_ci	unsigned short bdf;
1908c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
1918c2ecf20Sopenharmony_ci	u16 domain_id;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (dev_data == NULL) {
1948c2ecf20Sopenharmony_ci		pr_err("%s dev_data is NULL", __func__);
1958c2ecf20Sopenharmony_ci		return 0;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (dev_data->iommu == NULL) {
1998c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
2008c2ecf20Sopenharmony_ci		return 0;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (dev_data->iommu_entry == NULL) {
2048c2ecf20Sopenharmony_ci		pr_err("%s iommu_entry is NULL", __func__);
2058c2ecf20Sopenharmony_ci		return 0;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	iommu = dev_data->iommu;
2098c2ecf20Sopenharmony_ci	domain_id = dev_data->iommu_entry->id;
2108c2ecf20Sopenharmony_ci	bdf = dev_data->bdf;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Set device table */
2138c2ecf20Sopenharmony_ci	if (flag) {
2148c2ecf20Sopenharmony_ci		index = find_first_zero_bit(iommu->devtable_bitmap, MAX_ATTACHED_DEV_ID);
2158c2ecf20Sopenharmony_ci		if (index < MAX_ATTACHED_DEV_ID) {
2168c2ecf20Sopenharmony_ci			__set_bit(index, iommu->devtable_bitmap);
2178c2ecf20Sopenharmony_ci			dev_data->index = index;
2188c2ecf20Sopenharmony_ci		} else {
2198c2ecf20Sopenharmony_ci			pr_err("%s get id from dev table failed\n", __func__);
2208c2ecf20Sopenharmony_ci			return 0;
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		pr_info("%s bdf %x domain_id %d index %x"
2248c2ecf20Sopenharmony_ci				" iommu segment %d flag %x\n",
2258c2ecf20Sopenharmony_ci				__func__, bdf, domain_id, index,
2268c2ecf20Sopenharmony_ci				iommu->segment, flag);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		val = bdf & 0xffff;
2298c2ecf20Sopenharmony_ci		val |= ((domain_id & 0xf) << 16);	/* domain id */
2308c2ecf20Sopenharmony_ci		val |= ((index & 0xf) << 24);		/* index */
2318c2ecf20Sopenharmony_ci		val |= (0x1 << 20);			/* valid */
2328c2ecf20Sopenharmony_ci		val |= (0x1 << 31);			/* enable */
2338c2ecf20Sopenharmony_ci		iommu_write_regl(iommu, LA_IOMMU_EIVDB, val);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		val = iommu_read_regl(iommu, LA_IOMMU_CMD);
2368c2ecf20Sopenharmony_ci		val &= 0xfffffffc;
2378c2ecf20Sopenharmony_ci		iommu_write_regl(iommu, LA_IOMMU_CMD, val);
2388c2ecf20Sopenharmony_ci	} else {
2398c2ecf20Sopenharmony_ci		/* Flush device table */
2408c2ecf20Sopenharmony_ci		index = dev_data->index;
2418c2ecf20Sopenharmony_ci		pr_info("%s bdf %x domain_id %d index %x"
2428c2ecf20Sopenharmony_ci				" iommu segment %d flag %x\n",
2438c2ecf20Sopenharmony_ci				__func__, bdf, domain_id, index,
2448c2ecf20Sopenharmony_ci				iommu->segment, flag);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci		val = iommu_read_regl(iommu, LA_IOMMU_EIVDB);
2478c2ecf20Sopenharmony_ci		val &= ~(0x7fffffff);
2488c2ecf20Sopenharmony_ci		val |= ((index & 0xf) << 24);		/* index */
2498c2ecf20Sopenharmony_ci		iommu_write_regl(iommu, LA_IOMMU_EIVDB, val);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		val = iommu_read_regl(iommu, LA_IOMMU_CMD);
2528c2ecf20Sopenharmony_ci		val &= 0xfffffffc;
2538c2ecf20Sopenharmony_ci		iommu_write_regl(iommu, LA_IOMMU_CMD, val);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		if (index < MAX_ATTACHED_DEV_ID)
2568c2ecf20Sopenharmony_ci			__clear_bit(index, iommu->devtable_bitmap);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void flush_iotlb(loongson_iommu *iommu)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	u32 val, cmd;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (iommu == NULL) {
2678c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
2688c2ecf20Sopenharmony_ci		return;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_VBTC);
2728c2ecf20Sopenharmony_ci	val &= ~0x1f;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Flush all tlb */
2758c2ecf20Sopenharmony_ci	val |= 0x5;
2768c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_VBTC, val);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	cmd = iommu_read_regl(iommu, LA_IOMMU_CMD);
2798c2ecf20Sopenharmony_ci	cmd &= 0xfffffffc;
2808c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_CMD, cmd);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int flush_pgtable_is_busy(loongson_iommu *iommu)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	u32 val;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (iommu == NULL) {
2888c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
2898c2ecf20Sopenharmony_ci		return 0;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	val = iommu_read_regl(iommu, LA_IOMMU_VBTC);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return val & IOMMU_PGTABLE_BUSY;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int __iommu_flush_iotlb_all(loongson_iommu *iommu)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	u32 retry = 0;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (iommu == NULL) {
3028c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
3038c2ecf20Sopenharmony_ci		return 0;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	flush_iotlb(iommu);
3078c2ecf20Sopenharmony_ci	while (flush_pgtable_is_busy(iommu)) {
3088c2ecf20Sopenharmony_ci		if (retry == LOOP_TIMEOUT) {
3098c2ecf20Sopenharmony_ci			pr_err("Loongson-IOMMU: iotlb flush busy\n");
3108c2ecf20Sopenharmony_ci			return -EIO;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci		retry++;
3138c2ecf20Sopenharmony_ci		udelay(1);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	iommu_translate_enable(iommu);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic void priv_flush_iotlb_pde(loongson_iommu *iommu)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	if (iommu == NULL) {
3238c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	__iommu_flush_iotlb_all(iommu);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void do_attach(iommu_info *info, struct loongson_iommu_dev_data *dev_data)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	if (!dev_data->count)
3338c2ecf20Sopenharmony_ci		return;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	dev_data->iommu_entry = info;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	spin_lock(&info->devlock);
3388c2ecf20Sopenharmony_ci	list_add(&dev_data->list, &info->dev_list);
3398c2ecf20Sopenharmony_ci	info->dev_cnt += 1;
3408c2ecf20Sopenharmony_ci	spin_unlock(&info->devlock);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	update_dev_table(dev_data, 1);
3438c2ecf20Sopenharmony_ci	if (info->dev_cnt > 0)
3448c2ecf20Sopenharmony_ci		priv_flush_iotlb_pde(dev_data->iommu);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void do_detach(struct loongson_iommu_dev_data *dev_data)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	iommu_info *iommu_entry = NULL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (dev_data == NULL) {
3528c2ecf20Sopenharmony_ci		pr_err("%s dev_data is NULL", __func__);
3538c2ecf20Sopenharmony_ci		return;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (dev_data->count)
3578c2ecf20Sopenharmony_ci		return;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	iommu_entry = dev_data->iommu_entry;
3608c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
3618c2ecf20Sopenharmony_ci		pr_err("%s iommu_entry is NULL", __func__);
3628c2ecf20Sopenharmony_ci		return;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	list_del(&dev_data->list);
3668c2ecf20Sopenharmony_ci	iommu_entry->dev_cnt -= 1;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	update_dev_table(dev_data, 0);
3698c2ecf20Sopenharmony_ci	dev_data->iommu_entry = NULL;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void cleanup_iommu_entry(iommu_info *iommu_entry)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data = NULL;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	spin_lock(&iommu_entry->devlock);
3778c2ecf20Sopenharmony_ci	while (!list_empty(&iommu_entry->dev_list)) {
3788c2ecf20Sopenharmony_ci		dev_data = list_first_entry(&iommu_entry->dev_list,
3798c2ecf20Sopenharmony_ci				struct loongson_iommu_dev_data, list);
3808c2ecf20Sopenharmony_ci		do_detach(dev_data);
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	spin_unlock(&iommu_entry->devlock);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int domain_id_alloc(loongson_iommu *iommu)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	int id = -1;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (iommu == NULL) {
3918c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
3928c2ecf20Sopenharmony_ci		return id;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	spin_lock(&iommu->domain_bitmap_lock);
3968c2ecf20Sopenharmony_ci	id = find_first_zero_bit(iommu->domain_bitmap, MAX_DOMAIN_ID);
3978c2ecf20Sopenharmony_ci	if (id < MAX_DOMAIN_ID)
3988c2ecf20Sopenharmony_ci		__set_bit(id, iommu->domain_bitmap);
3998c2ecf20Sopenharmony_ci	else
4008c2ecf20Sopenharmony_ci		pr_err("Loongson-IOMMU: Alloc domain id over max domain id\n");
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	spin_unlock(&iommu->domain_bitmap_lock);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return id;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void domain_id_free(loongson_iommu *iommu, int id)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	if (iommu == NULL) {
4108c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
4118c2ecf20Sopenharmony_ci		return;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	spin_lock(&iommu->domain_bitmap_lock);
4158c2ecf20Sopenharmony_ci	if ((id >= 0) && (id < MAX_DOMAIN_ID))
4168c2ecf20Sopenharmony_ci		__clear_bit(id, iommu->domain_bitmap);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	spin_unlock(&iommu->domain_bitmap_lock);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci/*
4228c2ecf20Sopenharmony_ci *  This function adds a private domain to the global domain list
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_cistatic void add_domain_to_list(loongson_iommu *iommu, dom_info *priv)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	spin_lock(&iommu->dom_info_lock);
4278c2ecf20Sopenharmony_ci	list_add(&priv->list, &iommu->dom_list);
4288c2ecf20Sopenharmony_ci	spin_unlock(&iommu->dom_info_lock);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void del_domain_from_list(loongson_iommu *iommu, dom_info *priv)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	spin_lock(&iommu->dom_info_lock);
4348c2ecf20Sopenharmony_ci	list_del(&priv->list);
4358c2ecf20Sopenharmony_ci	spin_unlock(&iommu->dom_info_lock);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic spt_entry *iommu_zalloc_page(loongson_iommu *iommu)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int index;
4418c2ecf20Sopenharmony_ci	void *addr;
4428c2ecf20Sopenharmony_ci	spt_entry *shd_entry;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	spin_lock(&iommu->pgtable_bitmap_lock);
4458c2ecf20Sopenharmony_ci	index = find_first_zero_bit(iommu->pgtable_bitmap, iommu->maxpages);
4468c2ecf20Sopenharmony_ci	if (index < iommu->maxpages)
4478c2ecf20Sopenharmony_ci		__set_bit(index, iommu->pgtable_bitmap);
4488c2ecf20Sopenharmony_ci	spin_unlock(&iommu->pgtable_bitmap_lock);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	shd_entry = NULL;
4518c2ecf20Sopenharmony_ci	if (index < iommu->maxpages) {
4528c2ecf20Sopenharmony_ci		shd_entry = kmalloc(sizeof(*shd_entry), GFP_KERNEL);
4538c2ecf20Sopenharmony_ci		if (!shd_entry) {
4548c2ecf20Sopenharmony_ci			pr_err("%s alloc memory for shadow page entry failed\n", __func__);
4558c2ecf20Sopenharmony_ci			goto fail;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		shd_entry->shadow_ptable = (unsigned long *)get_zeroed_page(GFP_KERNEL);
4598c2ecf20Sopenharmony_ci		if (!shd_entry->shadow_ptable) {
4608c2ecf20Sopenharmony_ci			pr_err("Loongson-IOMMU: get zeroed page err\n");
4618c2ecf20Sopenharmony_ci			kfree(shd_entry);
4628c2ecf20Sopenharmony_ci			goto fail;
4638c2ecf20Sopenharmony_ci		}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		addr = iommu->pgtbase + index * IOMMU_PAGE_SIZE;
4668c2ecf20Sopenharmony_ci		memset(addr, 0x0, IOMMU_PAGE_SIZE);
4678c2ecf20Sopenharmony_ci		shd_entry->index = index;
4688c2ecf20Sopenharmony_ci		shd_entry->gmem_ptable = addr;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return shd_entry;
4728c2ecf20Sopenharmony_cifail:
4738c2ecf20Sopenharmony_ci	spin_lock(&iommu->pgtable_bitmap_lock);
4748c2ecf20Sopenharmony_ci	__clear_bit(index, iommu->pgtable_bitmap);
4758c2ecf20Sopenharmony_ci	spin_unlock(&iommu->pgtable_bitmap_lock);
4768c2ecf20Sopenharmony_ci	return NULL;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic void iommu_free_page(loongson_iommu *iommu, spt_entry *shadw_entry)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	void *addr;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (shadw_entry->index < iommu->maxpages) {
4848c2ecf20Sopenharmony_ci		addr = shadw_entry->gmem_ptable;
4858c2ecf20Sopenharmony_ci		memset(addr, 0x0, IOMMU_PAGE_SIZE);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		spin_lock(&iommu->pgtable_bitmap_lock);
4888c2ecf20Sopenharmony_ci		__clear_bit(shadw_entry->index, iommu->pgtable_bitmap);
4898c2ecf20Sopenharmony_ci		spin_unlock(&iommu->pgtable_bitmap_lock);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		shadw_entry->index = -1;
4928c2ecf20Sopenharmony_ci		free_page((unsigned long)shadw_entry->shadow_ptable);
4938c2ecf20Sopenharmony_ci		shadw_entry->shadow_ptable = NULL;
4948c2ecf20Sopenharmony_ci		shadw_entry->gmem_ptable = NULL;
4958c2ecf20Sopenharmony_ci		kfree(shadw_entry);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void free_pagetable_one_level(iommu_info *iommu_entry, spt_entry *shd_entry, int level)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	int i;
5028c2ecf20Sopenharmony_ci	unsigned long *psentry;
5038c2ecf20Sopenharmony_ci	spt_entry *shd_entry_tmp;
5048c2ecf20Sopenharmony_ci	loongson_iommu *iommu = iommu_entry->iommu;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	psentry = (unsigned long *)shd_entry;
5078c2ecf20Sopenharmony_ci	if (level == IOMMU_PT_LEVEL1) {
5088c2ecf20Sopenharmony_ci		if (iommu_pt_present(psentry) && (!iommu_pt_huge(psentry)))
5098c2ecf20Sopenharmony_ci			iommu_free_page(iommu, shd_entry);
5108c2ecf20Sopenharmony_ci		return;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	for (i = 0; i < IOMMU_PTRS_PER_LEVEL; i++) {
5148c2ecf20Sopenharmony_ci		psentry = shd_entry->shadow_ptable + i;
5158c2ecf20Sopenharmony_ci		if (!iommu_pt_present(psentry))
5168c2ecf20Sopenharmony_ci			continue;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		shd_entry_tmp = (spt_entry *)(*psentry);
5198c2ecf20Sopenharmony_ci		free_pagetable_one_level(iommu_entry, shd_entry_tmp, level - 1);
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	iommu_free_page(iommu, shd_entry);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic void free_pagetable(iommu_info *iommu_entry)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	spt_entry *shd_entry;
5288c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	iommu = iommu_entry->iommu;
5318c2ecf20Sopenharmony_ci	shd_entry = iommu_entry->shadow_pgd;
5328c2ecf20Sopenharmony_ci	free_pagetable_one_level(iommu_entry, shd_entry, IOMMU_LEVEL_MAX);
5338c2ecf20Sopenharmony_ci	iommu_entry->shadow_pgd = NULL;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic dom_info *dom_info_alloc(void)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	dom_info *info;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
5418c2ecf20Sopenharmony_ci	if (info == NULL) {
5428c2ecf20Sopenharmony_ci		pr_err("%s alloc memory for info failed\n", __func__);
5438c2ecf20Sopenharmony_ci		return NULL;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/* 0x10000000~0x8fffffff */
5478c2ecf20Sopenharmony_ci	info->mmio_pgd = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 6);
5488c2ecf20Sopenharmony_ci	if (info->mmio_pgd == NULL) {
5498c2ecf20Sopenharmony_ci		pr_err("%s alloc memory for virtio pgtable failed\n", __func__);
5508c2ecf20Sopenharmony_ci		kfree(info);
5518c2ecf20Sopenharmony_ci		return NULL;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->iommu_devlist);
5558c2ecf20Sopenharmony_ci	spin_lock_init(&info->lock);
5568c2ecf20Sopenharmony_ci	return info;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic void dom_info_free(dom_info *info)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	/* 0x10000000~0x8fffffff */
5628c2ecf20Sopenharmony_ci	if (info->mmio_pgd) {
5638c2ecf20Sopenharmony_ci		free_pages((unsigned long)info->mmio_pgd, 6);
5648c2ecf20Sopenharmony_ci		info->mmio_pgd = NULL;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	kfree(info);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic struct iommu_domain *loongson_iommu_domain_alloc(unsigned int type)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	dom_info *info;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	switch (type) {
5758c2ecf20Sopenharmony_ci	case IOMMU_DOMAIN_UNMANAGED:
5768c2ecf20Sopenharmony_ci		info = dom_info_alloc();
5778c2ecf20Sopenharmony_ci		if (info == NULL)
5788c2ecf20Sopenharmony_ci			return NULL;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		info->domain.geometry.aperture_start	= 0;
5818c2ecf20Sopenharmony_ci		info->domain.geometry.aperture_end	= ~0ULL;
5828c2ecf20Sopenharmony_ci		info->domain.geometry.force_aperture	= true;
5838c2ecf20Sopenharmony_ci		break;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	default:
5868c2ecf20Sopenharmony_ci		return NULL;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	return &info->domain;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_civoid domain_deattach_iommu(dom_info *priv, iommu_info *iommu_entry)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (priv == NULL) {
5978c2ecf20Sopenharmony_ci		pr_err("%s priv is NULL", __func__);
5988c2ecf20Sopenharmony_ci		return;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
6028c2ecf20Sopenharmony_ci		pr_err("%s iommu_entry is NULL", __func__);
6038c2ecf20Sopenharmony_ci		return;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (iommu_entry->dev_cnt != 0)
6078c2ecf20Sopenharmony_ci		return;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	iommu = iommu_entry->iommu;
6108c2ecf20Sopenharmony_ci	if (iommu == NULL) {
6118c2ecf20Sopenharmony_ci		pr_err("%s iommu is NULL", __func__);
6128c2ecf20Sopenharmony_ci		return;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	domain_id_free(iommu_entry->iommu, iommu_entry->id);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	mutex_lock(&iommu->loongson_iommu_pgtlock);
6188c2ecf20Sopenharmony_ci	free_pagetable(iommu_entry);
6198c2ecf20Sopenharmony_ci	mutex_unlock(&iommu->loongson_iommu_pgtlock);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
6228c2ecf20Sopenharmony_ci	list_del(&iommu_entry->list);
6238c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	kfree(iommu_entry);
6268c2ecf20Sopenharmony_ci	del_domain_from_list(iommu, priv);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic void loongson_iommu_domain_free(struct iommu_domain *domain)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	dom_info *priv;
6338c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
6348c2ecf20Sopenharmony_ci	struct iommu_info *iommu_entry, *iommu_entry_temp;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	priv = to_dom_info(domain);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
6398c2ecf20Sopenharmony_ci	list_for_each_entry_safe(iommu_entry, iommu_entry_temp, &priv->iommu_devlist, list) {
6408c2ecf20Sopenharmony_ci		iommu = iommu_entry->iommu;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		if (iommu_entry->dev_cnt > 0)
6438c2ecf20Sopenharmony_ci			cleanup_iommu_entry(iommu_entry);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		spin_unlock(&priv->lock);
6468c2ecf20Sopenharmony_ci		domain_deattach_iommu(priv, iommu_entry);
6478c2ecf20Sopenharmony_ci		spin_lock(&priv->lock);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		__iommu_flush_iotlb_all(iommu);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci		if (!has_dom(iommu))
6528c2ecf20Sopenharmony_ci			iommu_translate_disable(iommu);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	dom_info_free(priv);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistruct loongson_iommu_rlookup_entry *lookup_rlooptable(int pcisegment)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	list_for_each_entry(rlookupentry, &loongson_rlookup_iommu_list, list) {
6658c2ecf20Sopenharmony_ci		if (rlookupentry->pcisegment == pcisegment)
6668c2ecf20Sopenharmony_ci			return rlookupentry;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return NULL;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ciloongson_iommu *find_iommu_by_dev(struct pci_dev  *pdev)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	int pcisegment;
6758c2ecf20Sopenharmony_ci	unsigned short devid;
6768c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
6778c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	devid = pdev->devfn & 0xff;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	pcisegment = pci_domain_nr(pdev->bus);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	rlookupentry = lookup_rlooptable(pcisegment);
6848c2ecf20Sopenharmony_ci	if (rlookupentry == NULL) {
6858c2ecf20Sopenharmony_ci		pr_info("%s find segment %d rlookupentry failed\n", __func__,
6868c2ecf20Sopenharmony_ci				pcisegment);
6878c2ecf20Sopenharmony_ci		return iommu;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	iommu = rlookupentry->loongson_iommu_rlookup_table[devid];
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return iommu;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int iommu_init_device(struct device *dev)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	unsigned char busnum;
6988c2ecf20Sopenharmony_ci	unsigned short bdf, devid;
6998c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
7008c2ecf20Sopenharmony_ci	struct pci_bus *bus = pdev->bus;
7018c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data;
7028c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	bdf = pdev->devfn & 0xff;
7058c2ecf20Sopenharmony_ci	busnum = bus->number;
7068c2ecf20Sopenharmony_ci	if (busnum != 0) {
7078c2ecf20Sopenharmony_ci		while (bus->parent->parent)
7088c2ecf20Sopenharmony_ci			bus = bus->parent;
7098c2ecf20Sopenharmony_ci		bdf = bus->self->devfn & 0xff;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (dev_iommu_priv_get(dev)) {
7138c2ecf20Sopenharmony_ci		pr_info("Loongson-IOMMU: bdf:0x%x has added\n", bdf);
7148c2ecf20Sopenharmony_ci		return 0;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
7188c2ecf20Sopenharmony_ci	if (!dev_data)
7198c2ecf20Sopenharmony_ci		return -ENOMEM;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	devid = PCI_DEVID(bus->number, bdf);
7228c2ecf20Sopenharmony_ci	dev_data->bdf = devid;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	pci_info(pdev, "%s devid %x bus %x\n", __func__, devid, busnum);
7258c2ecf20Sopenharmony_ci	iommu = find_iommu_by_dev(pdev);
7268c2ecf20Sopenharmony_ci	if (iommu == NULL)
7278c2ecf20Sopenharmony_ci		pci_info(pdev, "%s find iommu failed by dev\n", __func__);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* The initial state is 0, and 1 is added only when attach dev */
7308c2ecf20Sopenharmony_ci	dev_data->count = 0;
7318c2ecf20Sopenharmony_ci	dev_data->iommu = iommu;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, dev_data);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic struct iommu_device *loongson_iommu_probe_device(struct device *dev)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	int ret = 0;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	ret = iommu_init_device(dev);
7438c2ecf20Sopenharmony_ci	if (ret < 0)
7448c2ecf20Sopenharmony_ci		pr_err("Loongson-IOMMU: unable to alloc memory for dev_data\n");
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic struct iommu_group *loongson_iommu_device_group(struct device *dev)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	struct iommu_group *group;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/*
7548c2ecf20Sopenharmony_ci	 * We don't support devices sharing stream IDs other than PCI RID
7558c2ecf20Sopenharmony_ci	 * aliases, since the necessary ID-to-device lookup becomes rather
7568c2ecf20Sopenharmony_ci	 * impractical given a potential sparse 32-bit stream ID space.
7578c2ecf20Sopenharmony_ci	 */
7588c2ecf20Sopenharmony_ci	if (dev_is_pci(dev))
7598c2ecf20Sopenharmony_ci		group = pci_device_group(dev);
7608c2ecf20Sopenharmony_ci	else
7618c2ecf20Sopenharmony_ci		group = generic_device_group(dev);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return group;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic void loongson_iommu_release_device(struct device *dev)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	dev_data = dev_iommu_priv_get(dev);
7718c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, NULL);
7728c2ecf20Sopenharmony_ci	kfree(dev_data);
7738c2ecf20Sopenharmony_ci}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ciiommu_info *get_first_iommu_entry(dom_info *priv)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	struct iommu_info *iommu_entry;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (priv == NULL) {
7808c2ecf20Sopenharmony_ci		pr_err("%s priv is NULL", __func__);
7818c2ecf20Sopenharmony_ci		return NULL;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	iommu_entry = list_first_entry_or_null(&priv->iommu_devlist,
7858c2ecf20Sopenharmony_ci			struct iommu_info, list);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return iommu_entry;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ciiommu_info *get_iommu_entry(dom_info *priv, loongson_iommu *iommu)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	struct iommu_info *iommu_entry;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
7958c2ecf20Sopenharmony_ci	list_for_each_entry(iommu_entry, &priv->iommu_devlist, list) {
7968c2ecf20Sopenharmony_ci		if (iommu_entry->iommu == iommu) {
7978c2ecf20Sopenharmony_ci			spin_unlock(&priv->lock);
7988c2ecf20Sopenharmony_ci			return iommu_entry;
7998c2ecf20Sopenharmony_ci		}
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	return NULL;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ciiommu_info *domain_attach_iommu(dom_info *priv, loongson_iommu *iommu)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	unsigned long pgd_pa;
8098c2ecf20Sopenharmony_ci	u32 dir_ctrl, pgd_lo, pgd_hi;
8108c2ecf20Sopenharmony_ci	struct iommu_info *iommu_entry = NULL;
8118c2ecf20Sopenharmony_ci	spt_entry *shd_entry = NULL;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	iommu_entry = get_iommu_entry(priv, iommu);
8148c2ecf20Sopenharmony_ci	if (iommu_entry)
8158c2ecf20Sopenharmony_ci		return iommu_entry;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	iommu_entry = kzalloc(sizeof(struct iommu_info), GFP_KERNEL);
8188c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
8198c2ecf20Sopenharmony_ci		pr_info("%s alloc memory for iommu_entry failed\n", __func__);
8208c2ecf20Sopenharmony_ci		return NULL;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iommu_entry->dev_list);
8248c2ecf20Sopenharmony_ci	iommu_entry->iommu = iommu;
8258c2ecf20Sopenharmony_ci	iommu_entry->id = domain_id_alloc(iommu);
8268c2ecf20Sopenharmony_ci	if (iommu_entry->id == -1) {
8278c2ecf20Sopenharmony_ci		pr_info("%s alloc id for domain failed\n", __func__);
8288c2ecf20Sopenharmony_ci		kfree(iommu_entry);
8298c2ecf20Sopenharmony_ci		return NULL;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	shd_entry = iommu_zalloc_page(iommu);
8338c2ecf20Sopenharmony_ci	if (!shd_entry) {
8348c2ecf20Sopenharmony_ci		pr_info("%s alloc shadow page entry err\n", __func__);
8358c2ecf20Sopenharmony_ci		domain_id_free(iommu, iommu_entry->id);
8368c2ecf20Sopenharmony_ci		kfree(iommu_entry);
8378c2ecf20Sopenharmony_ci		return NULL;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	iommu_entry->shadow_pgd = shd_entry;
8418c2ecf20Sopenharmony_ci	dir_ctrl = (IOMMU_LEVEL_STRIDE << 26) | (IOMMU_LEVEL_SHIFT(2) << 20);
8428c2ecf20Sopenharmony_ci	dir_ctrl |= (IOMMU_LEVEL_STRIDE <<  16) | (IOMMU_LEVEL_SHIFT(1) << 10);
8438c2ecf20Sopenharmony_ci	dir_ctrl |= (IOMMU_LEVEL_STRIDE << 6) | IOMMU_LEVEL_SHIFT(0);
8448c2ecf20Sopenharmony_ci	pgd_pa = iommu_pgt_v2p(iommu, shd_entry->gmem_ptable);
8458c2ecf20Sopenharmony_ci	pgd_hi = pgd_pa >> 32;
8468c2ecf20Sopenharmony_ci	pgd_lo = pgd_pa & 0xffffffff;
8478c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_DIR_CTRL(iommu_entry->id), dir_ctrl);
8488c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_PGD_HI(iommu_entry->id), pgd_hi);
8498c2ecf20Sopenharmony_ci	iommu_write_regl(iommu, LA_IOMMU_PGD_LO(iommu_entry->id), pgd_lo);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
8528c2ecf20Sopenharmony_ci	list_add(&iommu_entry->list, &priv->iommu_devlist);
8538c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	add_domain_to_list(iommu, priv);
8568c2ecf20Sopenharmony_ci	pr_info("%s iommu_entry->iommu %lx id %x\n", __func__,
8578c2ecf20Sopenharmony_ci	       (unsigned long)iommu_entry->iommu, iommu_entry->id);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	return iommu_entry;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic struct loongson_iommu_dev_data *iommu_get_devdata(dom_info *info,
8638c2ecf20Sopenharmony_ci		loongson_iommu *iommu, unsigned long bdf)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct iommu_info *entry;
8668c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	entry = get_iommu_entry(info, iommu);
8698c2ecf20Sopenharmony_ci	if (!entry)
8708c2ecf20Sopenharmony_ci		return NULL;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* Find from priv list */
8738c2ecf20Sopenharmony_ci	spin_lock(&entry->devlock);
8748c2ecf20Sopenharmony_ci	list_for_each_entry(dev_data, &entry->dev_list, list) {
8758c2ecf20Sopenharmony_ci		if (dev_data->bdf == bdf) {
8768c2ecf20Sopenharmony_ci			spin_unlock(&entry->devlock);
8778c2ecf20Sopenharmony_ci			return dev_data;
8788c2ecf20Sopenharmony_ci		}
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	spin_unlock(&entry->devlock);
8828c2ecf20Sopenharmony_ci	return NULL;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic int loongson_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	unsigned short bdf;
8888c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
8898c2ecf20Sopenharmony_ci	struct pci_bus *bus = pdev->bus;
8908c2ecf20Sopenharmony_ci	unsigned char busnum = pdev->bus->number;
8918c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data;
8928c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
8938c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
8948c2ecf20Sopenharmony_ci	iommu_info *iommu_entry = NULL;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	bdf = pdev->devfn & 0xff;
8978c2ecf20Sopenharmony_ci	if (busnum != 0) {
8988c2ecf20Sopenharmony_ci		while (bus->parent->parent)
8998c2ecf20Sopenharmony_ci			bus = bus->parent;
9008c2ecf20Sopenharmony_ci		bdf = bus->self->devfn & 0xff;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	dev_data = (struct loongson_iommu_dev_data *)dev_iommu_priv_get(dev);
9048c2ecf20Sopenharmony_ci	if (dev_data == NULL) {
9058c2ecf20Sopenharmony_ci		pci_info(pdev, "%s dev_data is Invalid\n", __func__);
9068c2ecf20Sopenharmony_ci		return 0;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	iommu = dev_data->iommu;
9108c2ecf20Sopenharmony_ci	if (iommu == NULL) {
9118c2ecf20Sopenharmony_ci		pci_info(pdev, "%s iommu is Invalid\n", __func__);
9128c2ecf20Sopenharmony_ci		return 0;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	pci_info(pdev, "%s busnum %x bdf %x priv %lx iommu %lx\n", __func__,
9168c2ecf20Sopenharmony_ci			busnum, bdf, (unsigned long)priv, (unsigned long)iommu);
9178c2ecf20Sopenharmony_ci	dev_data = iommu_get_devdata(priv, iommu, bdf);
9188c2ecf20Sopenharmony_ci	if (dev_data) {
9198c2ecf20Sopenharmony_ci		dev_data->count++;
9208c2ecf20Sopenharmony_ci		pci_info(pdev, "Loongson-IOMMU: bdf 0x%x devfn %x has attached,"
9218c2ecf20Sopenharmony_ci				" count:0x%x\n",
9228c2ecf20Sopenharmony_ci			bdf, pdev->devfn, dev_data->count);
9238c2ecf20Sopenharmony_ci		return 0;
9248c2ecf20Sopenharmony_ci	} else {
9258c2ecf20Sopenharmony_ci		dev_data = (struct loongson_iommu_dev_data *)dev_iommu_priv_get(dev);
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	iommu_entry = domain_attach_iommu(priv, iommu);
9298c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
9308c2ecf20Sopenharmony_ci		pci_info(pdev, "domain attach iommu failed\n");
9318c2ecf20Sopenharmony_ci		return 0;
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	dev_data->count++;
9358c2ecf20Sopenharmony_ci	do_attach(iommu_entry, dev_data);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	return 0;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_cistatic void loongson_iommu_detach_dev(struct iommu_domain *domain,
9418c2ecf20Sopenharmony_ci				 struct device *dev)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	unsigned short bdf;
9448c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
9458c2ecf20Sopenharmony_ci	struct pci_bus *bus = pdev->bus;
9468c2ecf20Sopenharmony_ci	unsigned char busnum = pdev->bus->number;
9478c2ecf20Sopenharmony_ci	struct loongson_iommu_dev_data *dev_data;
9488c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
9498c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
9508c2ecf20Sopenharmony_ci	iommu_info *iommu_entry = NULL;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	bdf = pdev->devfn & 0xff;
9538c2ecf20Sopenharmony_ci	if (busnum != 0) {
9548c2ecf20Sopenharmony_ci		while (bus->parent->parent)
9558c2ecf20Sopenharmony_ci			bus = bus->parent;
9568c2ecf20Sopenharmony_ci		bdf = bus->self->devfn & 0xff;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	dev_data = (struct loongson_iommu_dev_data *)dev_iommu_priv_get(dev);
9608c2ecf20Sopenharmony_ci	if (dev_data == NULL) {
9618c2ecf20Sopenharmony_ci		pci_info(pdev, "%s dev_data is Invalid\n", __func__);
9628c2ecf20Sopenharmony_ci		return;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	iommu = dev_data->iommu;
9668c2ecf20Sopenharmony_ci	if (iommu == NULL) {
9678c2ecf20Sopenharmony_ci		pci_info(pdev, "%s iommu is Invalid\n", __func__);
9688c2ecf20Sopenharmony_ci		return;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	dev_data = iommu_get_devdata(priv, iommu, bdf);
9728c2ecf20Sopenharmony_ci	if (dev_data == NULL) {
9738c2ecf20Sopenharmony_ci		pci_info(pdev, "Loongson-IOMMU: bdf 0x%x devfn %x dev_data is NULL\n",
9748c2ecf20Sopenharmony_ci			bdf, pdev->devfn & 0xff);
9758c2ecf20Sopenharmony_ci			return;
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	iommu = dev_data->iommu;
9798c2ecf20Sopenharmony_ci	dev_data->count--;
9808c2ecf20Sopenharmony_ci	iommu_entry = get_iommu_entry(priv, iommu);
9818c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
9828c2ecf20Sopenharmony_ci		pci_info(pdev, "%s get iommu_entry failed\n", __func__);
9838c2ecf20Sopenharmony_ci		return;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	spin_lock(&iommu_entry->devlock);
9878c2ecf20Sopenharmony_ci	do_detach(dev_data);
9888c2ecf20Sopenharmony_ci	spin_unlock(&iommu_entry->devlock);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	pci_info(pdev, "%s iommu devid  %x sigment %x\n", __func__,
9918c2ecf20Sopenharmony_ci			iommu->devid, iommu->segment);
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic unsigned long *iommu_get_spte(spt_entry *entry, unsigned long iova, int level)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	int i;
9978c2ecf20Sopenharmony_ci	unsigned long *pte;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (level > (IOMMU_LEVEL_MAX - 1))
10008c2ecf20Sopenharmony_ci		return NULL;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	for (i = IOMMU_LEVEL_MAX - 1; i >= level; i--) {
10038c2ecf20Sopenharmony_ci		pte  = iommu_shadow_offset(entry, iova, i);
10048c2ecf20Sopenharmony_ci		if (!iommu_pt_present(pte))
10058c2ecf20Sopenharmony_ci			break;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		if (iommu_pt_huge(pte))
10088c2ecf20Sopenharmony_ci			break;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci		entry = (spt_entry *)(*pte);
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	return pte;
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_cistatic int _iommu_alloc_ptable(loongson_iommu *iommu,
10178c2ecf20Sopenharmony_ci		unsigned long *psentry, unsigned long *phwentry)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	unsigned long pte;
10208c2ecf20Sopenharmony_ci	iommu_pte *new_phwentry;
10218c2ecf20Sopenharmony_ci	spt_entry *new_shd_entry;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (!iommu_pt_present(psentry)) {
10248c2ecf20Sopenharmony_ci		new_shd_entry = iommu_zalloc_page(iommu);
10258c2ecf20Sopenharmony_ci		if (!new_shd_entry) {
10268c2ecf20Sopenharmony_ci			pr_err("Loongson-IOMMU: new_shd_entry alloc err\n");
10278c2ecf20Sopenharmony_ci			return -ENOMEM;
10288c2ecf20Sopenharmony_ci		}
10298c2ecf20Sopenharmony_ci		/* fill shd_entry */
10308c2ecf20Sopenharmony_ci		*psentry = (unsigned long)new_shd_entry;
10318c2ecf20Sopenharmony_ci		/* fill gmem phwentry */
10328c2ecf20Sopenharmony_ci		new_phwentry = (iommu_pte *)new_shd_entry->gmem_ptable;
10338c2ecf20Sopenharmony_ci		pte = iommu_pgt_v2p(iommu, new_phwentry) & IOMMU_PAGE_MASK;
10348c2ecf20Sopenharmony_ci		pte |= IOMMU_PTE_RW;
10358c2ecf20Sopenharmony_ci		*phwentry = pte;
10368c2ecf20Sopenharmony_ci	}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return 0;
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic size_t iommu_ptw_map(loongson_iommu *iommu, spt_entry *shd_entry,
10428c2ecf20Sopenharmony_ci		unsigned long start, unsigned long end, phys_addr_t pa, int level)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	int ret, huge;
10458c2ecf20Sopenharmony_ci	unsigned long pte;
10468c2ecf20Sopenharmony_ci	unsigned long next, old, step;
10478c2ecf20Sopenharmony_ci	unsigned long *psentry, *phwentry;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	old = start;
10508c2ecf20Sopenharmony_ci	psentry = iommu_shadow_offset(shd_entry, start, level);
10518c2ecf20Sopenharmony_ci	phwentry = iommu_ptable_offset(shd_entry->gmem_ptable, start, level);
10528c2ecf20Sopenharmony_ci	if (level == IOMMU_PT_LEVEL0) {
10538c2ecf20Sopenharmony_ci		pa = pa & IOMMU_PAGE_MASK;
10548c2ecf20Sopenharmony_ci		do {
10558c2ecf20Sopenharmony_ci			pte =  pa | IOMMU_PTE_RW;
10568c2ecf20Sopenharmony_ci			*phwentry = pte;
10578c2ecf20Sopenharmony_ci			*psentry = pte;
10588c2ecf20Sopenharmony_ci			psentry++;
10598c2ecf20Sopenharmony_ci			phwentry++;
10608c2ecf20Sopenharmony_ci			start += IOMMU_PAGE_SIZE;
10618c2ecf20Sopenharmony_ci			pa += IOMMU_PAGE_SIZE;
10628c2ecf20Sopenharmony_ci		} while (start < end);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		return start - old;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	do {
10688c2ecf20Sopenharmony_ci		next = iommu_ptable_end(start, end, level);
10698c2ecf20Sopenharmony_ci		step = next - start;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		huge = 0;
10728c2ecf20Sopenharmony_ci		if ((level == IOMMU_PT_LEVEL1) && (step == IOMMU_HPAGE_SIZE))
10738c2ecf20Sopenharmony_ci			if (!iommu_pt_present(psentry) || iommu_pt_huge(psentry))
10748c2ecf20Sopenharmony_ci				huge = 1;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci		if (huge) {
10778c2ecf20Sopenharmony_ci			pte =  (pa & IOMMU_HPAGE_MASK) | IOMMU_PTE_RW | IOMMU_PTE_HP;
10788c2ecf20Sopenharmony_ci			*phwentry = pte;
10798c2ecf20Sopenharmony_ci			*psentry = pte;
10808c2ecf20Sopenharmony_ci		} else {
10818c2ecf20Sopenharmony_ci			ret = _iommu_alloc_ptable(iommu, psentry, phwentry);
10828c2ecf20Sopenharmony_ci			if (ret != 0)
10838c2ecf20Sopenharmony_ci				break;
10848c2ecf20Sopenharmony_ci			iommu_ptw_map(iommu, (spt_entry *)*psentry, start, next, pa, level - 1);
10858c2ecf20Sopenharmony_ci		}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		psentry++;
10888c2ecf20Sopenharmony_ci		phwentry++;
10898c2ecf20Sopenharmony_ci		pa += step;
10908c2ecf20Sopenharmony_ci		start = next;
10918c2ecf20Sopenharmony_ci	} while (start < end);
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	return start - old;
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic int dev_map_page(iommu_info *iommu_entry, unsigned long start,
10978c2ecf20Sopenharmony_ci			phys_addr_t pa, size_t size)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	int ret = 0;
11008c2ecf20Sopenharmony_ci	spt_entry *entry;
11018c2ecf20Sopenharmony_ci	phys_addr_t end;
11028c2ecf20Sopenharmony_ci	size_t map_size;
11038c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	end = start + size;
11068c2ecf20Sopenharmony_ci	iommu = iommu_entry->iommu;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	mutex_lock(&iommu->loongson_iommu_pgtlock);
11098c2ecf20Sopenharmony_ci	entry = iommu_entry->shadow_pgd;
11108c2ecf20Sopenharmony_ci	map_size = iommu_ptw_map(iommu, entry, start, end, pa, IOMMU_LEVEL_MAX - 1);
11118c2ecf20Sopenharmony_ci	if (map_size != size)
11128c2ecf20Sopenharmony_ci		ret = -EFAULT;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (has_dom(iommu))
11158c2ecf20Sopenharmony_ci		__iommu_flush_iotlb_all(iommu);
11168c2ecf20Sopenharmony_ci	mutex_unlock(&iommu->loongson_iommu_pgtlock);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	return ret;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic size_t iommu_ptw_unmap(loongson_iommu *iommu, spt_entry *shd_entry,
11228c2ecf20Sopenharmony_ci		unsigned long start, unsigned long end, int level)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	unsigned long next, old;
11258c2ecf20Sopenharmony_ci	unsigned long *psentry, *phwentry;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	old = start;
11288c2ecf20Sopenharmony_ci	psentry = iommu_shadow_offset(shd_entry, start, level);
11298c2ecf20Sopenharmony_ci	phwentry = iommu_ptable_offset(shd_entry->gmem_ptable, start, level);
11308c2ecf20Sopenharmony_ci	if (level == IOMMU_PT_LEVEL0) {
11318c2ecf20Sopenharmony_ci		do {
11328c2ecf20Sopenharmony_ci			*phwentry++ = 0;
11338c2ecf20Sopenharmony_ci			*psentry++ = 0;
11348c2ecf20Sopenharmony_ci			start += IOMMU_PAGE_SIZE;
11358c2ecf20Sopenharmony_ci		} while (start < end);
11368c2ecf20Sopenharmony_ci	} else {
11378c2ecf20Sopenharmony_ci		do {
11388c2ecf20Sopenharmony_ci			next = iommu_ptable_end(start, end, level);
11398c2ecf20Sopenharmony_ci			if (!iommu_pt_present(psentry))
11408c2ecf20Sopenharmony_ci				continue;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci			if (iommu_pt_huge(psentry)) {
11438c2ecf20Sopenharmony_ci				if ((next - start) != IOMMU_HPAGE_SIZE)
11448c2ecf20Sopenharmony_ci					pr_err("Map pte on hugepage not supported now\n");
11458c2ecf20Sopenharmony_ci				*phwentry = 0;
11468c2ecf20Sopenharmony_ci				*psentry = 0;
11478c2ecf20Sopenharmony_ci			} else
11488c2ecf20Sopenharmony_ci				iommu_ptw_unmap(iommu, (spt_entry *)*psentry, start, next, level - 1);
11498c2ecf20Sopenharmony_ci		} while (psentry++, phwentry++, start = next, start < end);
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	return start - old;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic int iommu_map_page(dom_info *priv, unsigned long start,
11568c2ecf20Sopenharmony_ci			phys_addr_t pa, size_t size, int prot, gfp_t gfp)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	unsigned long *pte;
11608c2ecf20Sopenharmony_ci	int ret = 0;
11618c2ecf20Sopenharmony_ci	iommu_info *iommu_entry = NULL;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* 0x10000000~0x8fffffff */
11648c2ecf20Sopenharmony_ci	if ((start >= IOVA_START) && (start < IOVA_END0)) {
11658c2ecf20Sopenharmony_ci		start -= IOVA_START;
11668c2ecf20Sopenharmony_ci		pte = (unsigned long *)priv->mmio_pgd;
11678c2ecf20Sopenharmony_ci		while (size > 0) {
11688c2ecf20Sopenharmony_ci			pte[start >> LA_VIRTIO_PAGE_SHIFT] =
11698c2ecf20Sopenharmony_ci					pa & LA_VIRTIO_PAGE_MASK;
11708c2ecf20Sopenharmony_ci			size -= IOMMU_PAGE_SIZE;
11718c2ecf20Sopenharmony_ci			start += IOMMU_PAGE_SIZE;
11728c2ecf20Sopenharmony_ci			pa += IOMMU_PAGE_SIZE;
11738c2ecf20Sopenharmony_ci		}
11748c2ecf20Sopenharmony_ci		return 0;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
11788c2ecf20Sopenharmony_ci	list_for_each_entry(iommu_entry, &priv->iommu_devlist, list) {
11798c2ecf20Sopenharmony_ci		ret |= dev_map_page(iommu_entry, start, pa, size);
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	return ret;
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic size_t iommu_unmap_page(iommu_info *iommu_entry, unsigned long start, size_t size)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
11898c2ecf20Sopenharmony_ci	spt_entry *entry;
11908c2ecf20Sopenharmony_ci	size_t unmap_len;
11918c2ecf20Sopenharmony_ci	unsigned long end;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	end = start + size;
11948c2ecf20Sopenharmony_ci	iommu = iommu_entry->iommu;
11958c2ecf20Sopenharmony_ci	mutex_lock(&iommu->loongson_iommu_pgtlock);
11968c2ecf20Sopenharmony_ci	entry = iommu_entry->shadow_pgd;
11978c2ecf20Sopenharmony_ci	unmap_len = iommu_ptw_unmap(iommu, entry, start, end, (IOMMU_LEVEL_MAX - 1));
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (has_dom(iommu))
12008c2ecf20Sopenharmony_ci		__iommu_flush_iotlb_all(iommu);
12018c2ecf20Sopenharmony_ci	mutex_unlock(&iommu->loongson_iommu_pgtlock);
12028c2ecf20Sopenharmony_ci	return unmap_len;
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cistatic size_t domain_unmap_page(dom_info *info, unsigned long start, size_t size)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	unsigned long *pte;
12088c2ecf20Sopenharmony_ci	size_t unmap_len = 0;
12098c2ecf20Sopenharmony_ci	iommu_info *entry;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* 0x10000000~0x8fffffff */
12128c2ecf20Sopenharmony_ci	if ((start >= IOVA_START) && (start < IOVA_END0)) {
12138c2ecf20Sopenharmony_ci		start -= IOVA_START;
12148c2ecf20Sopenharmony_ci		pte = (unsigned long *)info->mmio_pgd;
12158c2ecf20Sopenharmony_ci		while (size > 0) {
12168c2ecf20Sopenharmony_ci			pte[start >> LA_VIRTIO_PAGE_SHIFT] = 0;
12178c2ecf20Sopenharmony_ci			size -= 0x4000;
12188c2ecf20Sopenharmony_ci			unmap_len += 0x4000;
12198c2ecf20Sopenharmony_ci			start += 0x4000;
12208c2ecf20Sopenharmony_ci		}
12218c2ecf20Sopenharmony_ci		unmap_len += size;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci		return unmap_len;
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	spin_lock(&info->lock);
12278c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &info->iommu_devlist, list)
12288c2ecf20Sopenharmony_ci		unmap_len = iommu_unmap_page(entry, start, size);
12298c2ecf20Sopenharmony_ci	spin_unlock(&info->lock);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	return unmap_len;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic int loongson_iommu_map(struct iommu_domain *domain, unsigned long iova,
12358c2ecf20Sopenharmony_ci			      phys_addr_t pa, size_t len, int prot, gfp_t gfp)
12368c2ecf20Sopenharmony_ci{
12378c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	return iommu_map_page(priv, iova, pa, len, prot, GFP_KERNEL);
12408c2ecf20Sopenharmony_ci}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_cistatic size_t loongson_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
12438c2ecf20Sopenharmony_ci				   size_t size, struct iommu_iotlb_gather *gather)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	return domain_unmap_page(priv, iova, size);
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic phys_addr_t loongson_iommu_iova_to_pa(struct iommu_domain *domain,
12518c2ecf20Sopenharmony_ci						dma_addr_t iova)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	unsigned long pa, offset, tmpva, page_size, page_mask;
12548c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
12558c2ecf20Sopenharmony_ci	unsigned long *psentry, *pte;
12568c2ecf20Sopenharmony_ci	int ret = 0;
12578c2ecf20Sopenharmony_ci	spt_entry *entry;
12588c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
12598c2ecf20Sopenharmony_ci	iommu_info *iommu_entry = NULL;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* 0x10000000~0x8fffffff */
12628c2ecf20Sopenharmony_ci	if ((iova >= IOVA_START) && (iova < IOVA_END0)) {
12638c2ecf20Sopenharmony_ci		tmpva = iova & LA_VIRTIO_PAGE_MASK;
12648c2ecf20Sopenharmony_ci		pte = (unsigned long *)priv->mmio_pgd;
12658c2ecf20Sopenharmony_ci		offset = iova & ((1ULL << LA_VIRTIO_PAGE_SHIFT) - 1);
12668c2ecf20Sopenharmony_ci		pa = pte[(tmpva - IOVA_START) >> 14] + offset;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci		return pa;
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	iommu_entry = get_first_iommu_entry(priv);
12728c2ecf20Sopenharmony_ci	if (iommu_entry == NULL) {
12738c2ecf20Sopenharmony_ci		pr_err("%s iova:0x%llx iommu_entry is invalid\n",
12748c2ecf20Sopenharmony_ci				__func__, iova);
12758c2ecf20Sopenharmony_ci		ret = -EFAULT;
12768c2ecf20Sopenharmony_ci		return ret;
12778c2ecf20Sopenharmony_ci	}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	iommu = iommu_entry->iommu;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	mutex_lock(&iommu->loongson_iommu_pgtlock);
12828c2ecf20Sopenharmony_ci	entry = iommu_entry->shadow_pgd;
12838c2ecf20Sopenharmony_ci	psentry = iommu_get_spte(entry, iova, IOMMU_PT_LEVEL0);
12848c2ecf20Sopenharmony_ci	mutex_unlock(&iommu->loongson_iommu_pgtlock);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	if (!psentry || !iommu_pt_present(psentry)) {
12878c2ecf20Sopenharmony_ci		ret = -EFAULT;
12888c2ecf20Sopenharmony_ci		pr_warn_once("Loongson-IOMMU: shadow pte is null or not present with iova %llx \n", iova);
12898c2ecf20Sopenharmony_ci		return ret;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	if (iommu_pt_huge(psentry)) {
12938c2ecf20Sopenharmony_ci		page_size = IOMMU_HPAGE_SIZE;
12948c2ecf20Sopenharmony_ci		page_mask = IOMMU_HPAGE_MASK;
12958c2ecf20Sopenharmony_ci	} else {
12968c2ecf20Sopenharmony_ci		page_size = IOMMU_PAGE_SIZE;
12978c2ecf20Sopenharmony_ci		page_mask = IOMMU_PAGE_MASK;
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	pa = *psentry & page_mask;
13018c2ecf20Sopenharmony_ci	pa |= (iova & (page_size - 1));
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	return (phys_addr_t)pa;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cistatic phys_addr_t loongson_iommu_iova_to_phys(struct iommu_domain *domain,
13078c2ecf20Sopenharmony_ci					dma_addr_t iova)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	phys_addr_t pa;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	pa = loongson_iommu_iova_to_pa(domain, iova);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	return pa;
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic void loongson_iommu_flush_iotlb_all(struct iommu_domain *domain)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	int ret;
13198c2ecf20Sopenharmony_ci	dom_info *priv = to_dom_info(domain);
13208c2ecf20Sopenharmony_ci	iommu_info *iommu_entry;
13218c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
13248c2ecf20Sopenharmony_ci	list_for_each_entry(iommu_entry, &priv->iommu_devlist, list) {
13258c2ecf20Sopenharmony_ci		iommu = iommu_entry->iommu;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci		ret = __iommu_flush_iotlb_all(iommu);
13288c2ecf20Sopenharmony_ci	}
13298c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic void loongson_iommu_iotlb_sync(struct iommu_domain *domain,
13338c2ecf20Sopenharmony_ci				      struct iommu_iotlb_gather *gather)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	loongson_iommu_flush_iotlb_all(domain);
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cistatic struct iommu_ops loongson_iommu_ops = {
13398c2ecf20Sopenharmony_ci	.capable = loongson_iommu_capable,
13408c2ecf20Sopenharmony_ci	.domain_alloc = loongson_iommu_domain_alloc,
13418c2ecf20Sopenharmony_ci	.domain_free = loongson_iommu_domain_free,
13428c2ecf20Sopenharmony_ci	.attach_dev = loongson_iommu_attach_dev,
13438c2ecf20Sopenharmony_ci	.detach_dev = loongson_iommu_detach_dev,
13448c2ecf20Sopenharmony_ci	.map = loongson_iommu_map,
13458c2ecf20Sopenharmony_ci	.unmap = loongson_iommu_unmap,
13468c2ecf20Sopenharmony_ci	.iova_to_phys = loongson_iommu_iova_to_phys,
13478c2ecf20Sopenharmony_ci	.probe_device = loongson_iommu_probe_device,
13488c2ecf20Sopenharmony_ci	.release_device = loongson_iommu_release_device,
13498c2ecf20Sopenharmony_ci	.device_group = loongson_iommu_device_group,
13508c2ecf20Sopenharmony_ci	.pgsize_bitmap = LA_IOMMU_PGSIZE,
13518c2ecf20Sopenharmony_ci	.flush_iotlb_all = loongson_iommu_flush_iotlb_all,
13528c2ecf20Sopenharmony_ci	.iotlb_sync = loongson_iommu_iotlb_sync,
13538c2ecf20Sopenharmony_ci};
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ciloongson_iommu *loongarch_get_iommu(struct pci_dev *pdev)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	int pcisegment;
13588c2ecf20Sopenharmony_ci	unsigned short devid;
13598c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	devid = pdev->devfn & 0xff;
13628c2ecf20Sopenharmony_ci	pcisegment = pci_domain_nr(pdev->bus);
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	list_for_each_entry(iommu, &loongson_iommu_list, list) {
13658c2ecf20Sopenharmony_ci		if ((iommu->segment == pcisegment) &&
13668c2ecf20Sopenharmony_ci		    (iommu->devid == devid)) {
13678c2ecf20Sopenharmony_ci			return iommu;
13688c2ecf20Sopenharmony_ci		}
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	return NULL;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic int loongson_iommu_probe(struct pci_dev *pdev,
13758c2ecf20Sopenharmony_ci				const struct pci_device_id *ent)
13768c2ecf20Sopenharmony_ci{
13778c2ecf20Sopenharmony_ci	int ret = 1;
13788c2ecf20Sopenharmony_ci	int bitmap_sz = 0;
13798c2ecf20Sopenharmony_ci	int tmp;
13808c2ecf20Sopenharmony_ci	struct loongson_iommu *iommu = NULL;
13818c2ecf20Sopenharmony_ci	resource_size_t base, size;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	iommu = loongarch_get_iommu(pdev);
13848c2ecf20Sopenharmony_ci	if (iommu == NULL) {
13858c2ecf20Sopenharmony_ci		pci_info(pdev, "%s can't find iommu\n", __func__);
13868c2ecf20Sopenharmony_ci		return -ENODEV;
13878c2ecf20Sopenharmony_ci	}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	base = pci_resource_start(pdev, 0);
13908c2ecf20Sopenharmony_ci	size = pci_resource_len(pdev, 0);
13918c2ecf20Sopenharmony_ci	if (!request_mem_region(base, size, "loongson_iommu")) {
13928c2ecf20Sopenharmony_ci		pci_err(pdev, "can't reserve mmio registers\n");
13938c2ecf20Sopenharmony_ci		return -ENOMEM;
13948c2ecf20Sopenharmony_ci	}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	iommu->membase = ioremap(base, size);
13978c2ecf20Sopenharmony_ci	if (iommu->membase == NULL) {
13988c2ecf20Sopenharmony_ci		pci_info(pdev, "%s iommu pci dev bar0 is NULL\n", __func__);
13998c2ecf20Sopenharmony_ci		return ret;
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	base = pci_resource_start(pdev, 2);
14038c2ecf20Sopenharmony_ci	size = pci_resource_len(pdev, 2);
14048c2ecf20Sopenharmony_ci	if (!request_mem_region(base, size, "loongson_iommu")) {
14058c2ecf20Sopenharmony_ci		pci_err(pdev, "can't reserve mmio registers\n");
14068c2ecf20Sopenharmony_ci		return -ENOMEM;
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci	iommu->pgtbase = ioremap(base, size);
14098c2ecf20Sopenharmony_ci	if (iommu->pgtbase == NULL)
14108c2ecf20Sopenharmony_ci		return -ENOMEM;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	iommu->maxpages = size / IOMMU_PAGE_SIZE;
14138c2ecf20Sopenharmony_ci	pr_info("iommu membase %p pgtbase %p pgtsize %llx maxpages %lx\n", iommu->membase, iommu->pgtbase, size, iommu->maxpages);
14148c2ecf20Sopenharmony_ci	tmp = MAX_DOMAIN_ID / 8;
14158c2ecf20Sopenharmony_ci	bitmap_sz = (MAX_DOMAIN_ID % 8) ? (tmp + 1) : tmp;
14168c2ecf20Sopenharmony_ci	iommu->domain_bitmap = bitmap_zalloc(bitmap_sz, GFP_KERNEL);
14178c2ecf20Sopenharmony_ci	if (iommu->domain_bitmap == NULL) {
14188c2ecf20Sopenharmony_ci		pr_err("Loongson-IOMMU: domain bitmap alloc err bitmap_sz:%d\n", bitmap_sz);
14198c2ecf20Sopenharmony_ci		goto out_err;
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	tmp = MAX_ATTACHED_DEV_ID / 8;
14238c2ecf20Sopenharmony_ci	bitmap_sz = (MAX_ATTACHED_DEV_ID % 8) ? (tmp + 1) : tmp;
14248c2ecf20Sopenharmony_ci	iommu->devtable_bitmap = bitmap_zalloc(bitmap_sz, GFP_KERNEL);
14258c2ecf20Sopenharmony_ci	if (iommu->devtable_bitmap == NULL) {
14268c2ecf20Sopenharmony_ci		pr_err("Loongson-IOMMU: devtable bitmap alloc err bitmap_sz:%d\n", bitmap_sz);
14278c2ecf20Sopenharmony_ci		goto out_err_1;
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	tmp = iommu->maxpages / 8;
14318c2ecf20Sopenharmony_ci	bitmap_sz = (iommu->maxpages % 8) ? (tmp + 1) : tmp;
14328c2ecf20Sopenharmony_ci	iommu->pgtable_bitmap = bitmap_zalloc(bitmap_sz, GFP_KERNEL);
14338c2ecf20Sopenharmony_ci	if (iommu->pgtable_bitmap == NULL) {
14348c2ecf20Sopenharmony_ci		pr_err("Loongson-IOMMU: pgtable bitmap alloc err bitmap_sz:%d\n", bitmap_sz);
14358c2ecf20Sopenharmony_ci		goto out_err_2;
14368c2ecf20Sopenharmony_ci	}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	bus_set_iommu(&pci_bus_type, &loongson_iommu_ops);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	return 0;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ciout_err_2:
14438c2ecf20Sopenharmony_ci	kfree(iommu->devtable_bitmap);
14448c2ecf20Sopenharmony_ci	iommu->devtable_bitmap = NULL;
14458c2ecf20Sopenharmony_ciout_err_1:
14468c2ecf20Sopenharmony_ci	kfree(iommu->domain_bitmap);
14478c2ecf20Sopenharmony_ci	iommu->domain_bitmap = NULL;
14488c2ecf20Sopenharmony_ciout_err:
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	return ret;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic void loongson_iommu_remove(struct pci_dev *pdev)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	struct  loongson_iommu *iommu = NULL;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	iommu = loongarch_get_iommu(pdev);
14588c2ecf20Sopenharmony_ci	if (iommu == NULL)
14598c2ecf20Sopenharmony_ci		return;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	if (iommu->domain_bitmap != NULL) {
14628c2ecf20Sopenharmony_ci		kfree(iommu->domain_bitmap);
14638c2ecf20Sopenharmony_ci		iommu->domain_bitmap = NULL;
14648c2ecf20Sopenharmony_ci	}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (iommu->devtable_bitmap != NULL) {
14678c2ecf20Sopenharmony_ci		kfree(iommu->devtable_bitmap);
14688c2ecf20Sopenharmony_ci		iommu->devtable_bitmap = NULL;
14698c2ecf20Sopenharmony_ci	}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (iommu->pgtable_bitmap != NULL) {
14728c2ecf20Sopenharmony_ci		kfree(iommu->pgtable_bitmap);
14738c2ecf20Sopenharmony_ci		iommu->pgtable_bitmap = NULL;
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	iommu->membase = NULL;
14778c2ecf20Sopenharmony_ci	iommu->pgtbase = NULL;
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int __init check_ivrs_checksum(struct acpi_table_header *table)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	int i;
14838c2ecf20Sopenharmony_ci	u8 checksum = 0, *p = (u8 *)table;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	for (i = 0; i < table->length; ++i)
14868c2ecf20Sopenharmony_ci		checksum += p[i];
14878c2ecf20Sopenharmony_ci	if (checksum != 0) {
14888c2ecf20Sopenharmony_ci		/* ACPI table corrupt */
14898c2ecf20Sopenharmony_ci		pr_err("IVRS invalid checksum\n");
14908c2ecf20Sopenharmony_ci		return -ENODEV;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	return 0;
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistruct loongson_iommu_rlookup_entry *create_rlookup_entry(int pcisegment)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	rlookupentry = kzalloc(sizeof(struct loongson_iommu_rlookup_entry), GFP_KERNEL);
15018c2ecf20Sopenharmony_ci	if (rlookupentry == NULL)
15028c2ecf20Sopenharmony_ci		return rlookupentry;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	rlookupentry->pcisegment = pcisegment;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	/* IOMMU rlookup table - find the IOMMU for a specific device */
15078c2ecf20Sopenharmony_ci	rlookupentry->loongson_iommu_rlookup_table = (void *)__get_free_pages(
15088c2ecf20Sopenharmony_ci			GFP_KERNEL | __GFP_ZERO, get_order(rlookup_table_size));
15098c2ecf20Sopenharmony_ci	if (rlookupentry->loongson_iommu_rlookup_table == NULL) {
15108c2ecf20Sopenharmony_ci		kfree(rlookupentry);
15118c2ecf20Sopenharmony_ci		rlookupentry = NULL;
15128c2ecf20Sopenharmony_ci	} else {
15138c2ecf20Sopenharmony_ci		list_add(&rlookupentry->list, &loongson_rlookup_iommu_list);
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	return rlookupentry;
15178c2ecf20Sopenharmony_ci}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci/* Writes the specific IOMMU for a device into the rlookup table */
15208c2ecf20Sopenharmony_cistatic void __init set_iommu_for_device(loongson_iommu *iommu,
15218c2ecf20Sopenharmony_ci		u16 devid)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	rlookupentry = lookup_rlooptable(iommu->segment);
15268c2ecf20Sopenharmony_ci	if (rlookupentry == NULL)
15278c2ecf20Sopenharmony_ci		rlookupentry = create_rlookup_entry(iommu->segment);
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	if (rlookupentry != NULL)
15308c2ecf20Sopenharmony_ci		rlookupentry->loongson_iommu_rlookup_table[devid] = iommu;
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_cistatic inline u32 get_ivhd_header_size(struct ivhd_header *h)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	u32 size = 0;
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	switch (h->type) {
15388c2ecf20Sopenharmony_ci	case IVHD_HEAD_TYPE10:
15398c2ecf20Sopenharmony_ci		size = 24;
15408c2ecf20Sopenharmony_ci		break;
15418c2ecf20Sopenharmony_ci	case IVHD_HEAD_TYPE11:
15428c2ecf20Sopenharmony_ci	case IVHD_HEAD_TYPE40:
15438c2ecf20Sopenharmony_ci		size = 40;
15448c2ecf20Sopenharmony_ci		break;
15458c2ecf20Sopenharmony_ci	}
15468c2ecf20Sopenharmony_ci	return size;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic inline void update_last_devid(u16 devid)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	if (devid > loongson_iommu_last_bdf)
15528c2ecf20Sopenharmony_ci		loongson_iommu_last_bdf = devid;
15538c2ecf20Sopenharmony_ci}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci/*
15568c2ecf20Sopenharmony_ci * This function calculates the length of a given IVHD entry
15578c2ecf20Sopenharmony_ci */
15588c2ecf20Sopenharmony_cistatic inline int ivhd_entry_length(u8 *ivhd)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	u32 type = ((struct ivhd_entry *)ivhd)->type;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (type < 0x80) {
15638c2ecf20Sopenharmony_ci		return 0x04 << (*ivhd >> 6);
15648c2ecf20Sopenharmony_ci	} else if (type == IVHD_DEV_ACPI_HID) {
15658c2ecf20Sopenharmony_ci		/* For ACPI_HID, offset 21 is uid len */
15668c2ecf20Sopenharmony_ci		return *((u8 *)ivhd + 21) + 22;
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci	return 0;
15698c2ecf20Sopenharmony_ci}
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci/*
15728c2ecf20Sopenharmony_ci * After reading the highest device id from the IOMMU PCI capability header
15738c2ecf20Sopenharmony_ci * this function looks if there is a higher device id defined in the ACPI table
15748c2ecf20Sopenharmony_ci */
15758c2ecf20Sopenharmony_cistatic int __init find_last_devid_from_ivhd(struct ivhd_header *h)
15768c2ecf20Sopenharmony_ci{
15778c2ecf20Sopenharmony_ci	u8 *p = (void *)h, *end = (void *)h;
15788c2ecf20Sopenharmony_ci	struct ivhd_entry *dev;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	u32 ivhd_size = get_ivhd_header_size(h);
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	if (!ivhd_size) {
15838c2ecf20Sopenharmony_ci		pr_err("loongson-iommu: Unsupported IVHD type %#x\n", h->type);
15848c2ecf20Sopenharmony_ci		return -EINVAL;
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	p += ivhd_size;
15888c2ecf20Sopenharmony_ci	end += h->length;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	while (p < end) {
15918c2ecf20Sopenharmony_ci		dev = (struct ivhd_entry *)p;
15928c2ecf20Sopenharmony_ci		switch (dev->type) {
15938c2ecf20Sopenharmony_ci		case IVHD_DEV_ALL:
15948c2ecf20Sopenharmony_ci			/* Use maximum BDF value for DEV_ALL */
15958c2ecf20Sopenharmony_ci			update_last_devid(MAX_BDF_NUM);
15968c2ecf20Sopenharmony_ci			break;
15978c2ecf20Sopenharmony_ci		case IVHD_DEV_SELECT:
15988c2ecf20Sopenharmony_ci		case IVHD_DEV_RANGE_END:
15998c2ecf20Sopenharmony_ci		case IVHD_DEV_ALIAS:
16008c2ecf20Sopenharmony_ci		case IVHD_DEV_EXT_SELECT:
16018c2ecf20Sopenharmony_ci			/* all the above subfield types refer to device ids */
16028c2ecf20Sopenharmony_ci			update_last_devid(dev->devid);
16038c2ecf20Sopenharmony_ci			break;
16048c2ecf20Sopenharmony_ci		default:
16058c2ecf20Sopenharmony_ci			break;
16068c2ecf20Sopenharmony_ci		}
16078c2ecf20Sopenharmony_ci		p += ivhd_entry_length(p);
16088c2ecf20Sopenharmony_ci	}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	WARN_ON(p != end);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	return 0;
16138c2ecf20Sopenharmony_ci}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci/*
16168c2ecf20Sopenharmony_ci * Iterate over all IVHD entries in the ACPI table and find the highest device
16178c2ecf20Sopenharmony_ci * id which we need to handle. This is the first of three functions which parse
16188c2ecf20Sopenharmony_ci * the ACPI table. So we check the checksum here.
16198c2ecf20Sopenharmony_ci */
16208c2ecf20Sopenharmony_cistatic int __init find_last_devid_acpi(struct acpi_table_header *table)
16218c2ecf20Sopenharmony_ci{
16228c2ecf20Sopenharmony_ci	u8 *p = (u8 *)table, *end = (u8 *)table;
16238c2ecf20Sopenharmony_ci	struct ivhd_header *h;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	p += IVRS_HEADER_LENGTH;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	end += table->length;
16288c2ecf20Sopenharmony_ci	while (p < end) {
16298c2ecf20Sopenharmony_ci		h = (struct ivhd_header *)p;
16308c2ecf20Sopenharmony_ci		if (h->type == loongson_iommu_target_ivhd_type) {
16318c2ecf20Sopenharmony_ci			int ret = find_last_devid_from_ivhd(h);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci			if (ret)
16348c2ecf20Sopenharmony_ci				return ret;
16358c2ecf20Sopenharmony_ci		}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci		if (h->length == 0)
16388c2ecf20Sopenharmony_ci			break;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci		p += h->length;
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	if (p != end)
16448c2ecf20Sopenharmony_ci		return -EINVAL;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	return 0;
16488c2ecf20Sopenharmony_ci}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci/*
16518c2ecf20Sopenharmony_ci * Takes a pointer to an loongarch IOMMU entry in the ACPI table and
16528c2ecf20Sopenharmony_ci * initializes the hardware and our data structures with it.
16538c2ecf20Sopenharmony_ci */
16548c2ecf20Sopenharmony_cistatic int __init init_iommu_from_acpi(loongson_iommu *iommu,
16558c2ecf20Sopenharmony_ci					struct ivhd_header *h)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	u8 *p = (u8 *)h;
16588c2ecf20Sopenharmony_ci	u8 *end = p;
16598c2ecf20Sopenharmony_ci	u16 devid = 0, devid_start = 0;
16608c2ecf20Sopenharmony_ci	u32 dev_i, ivhd_size;
16618c2ecf20Sopenharmony_ci	struct ivhd_entry *e;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	/*
16648c2ecf20Sopenharmony_ci	 * Done. Now parse the device entries
16658c2ecf20Sopenharmony_ci	 */
16668c2ecf20Sopenharmony_ci	ivhd_size = get_ivhd_header_size(h);
16678c2ecf20Sopenharmony_ci	if (!ivhd_size) {
16688c2ecf20Sopenharmony_ci		pr_err("loongarch iommu: Unsupported IVHD type %#x\n", h->type);
16698c2ecf20Sopenharmony_ci		return -EINVAL;
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	if (h->length == 0)
16738c2ecf20Sopenharmony_ci		return -EINVAL;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	p += ivhd_size;
16768c2ecf20Sopenharmony_ci	end += h->length;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	while (p < end) {
16798c2ecf20Sopenharmony_ci		e = (struct ivhd_entry *)p;
16808c2ecf20Sopenharmony_ci		switch (e->type) {
16818c2ecf20Sopenharmony_ci		case IVHD_DEV_ALL:
16828c2ecf20Sopenharmony_ci			for (dev_i = 0; dev_i <= loongson_iommu_last_bdf; ++dev_i)
16838c2ecf20Sopenharmony_ci				set_iommu_for_device(iommu, dev_i);
16848c2ecf20Sopenharmony_ci			break;
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci		case IVHD_DEV_SELECT:
16878c2ecf20Sopenharmony_ci			pr_info("  DEV_SELECT\t\t\t devid: %02x:%02x.%x\n",
16888c2ecf20Sopenharmony_ci				PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid));
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci			devid = e->devid;
16918c2ecf20Sopenharmony_ci			set_iommu_for_device(iommu, devid);
16928c2ecf20Sopenharmony_ci			break;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci		case IVHD_DEV_SELECT_RANGE_START:
16958c2ecf20Sopenharmony_ci			pr_info("  DEV_SELECT_RANGE_START\t devid: %02x:%02x.%x\n",
16968c2ecf20Sopenharmony_ci				PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid));
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci			devid_start = e->devid;
16998c2ecf20Sopenharmony_ci			break;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci		case IVHD_DEV_RANGE_END:
17028c2ecf20Sopenharmony_ci			pr_info("  DEV_RANGE_END\t\t devid: %02x:%02x.%x\n",
17038c2ecf20Sopenharmony_ci				PCI_BUS_NUM(e->devid), PCI_SLOT(e->devid), PCI_FUNC(e->devid));
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci			devid = e->devid;
17068c2ecf20Sopenharmony_ci			for (dev_i = devid_start; dev_i <= devid; ++dev_i)
17078c2ecf20Sopenharmony_ci				set_iommu_for_device(iommu, dev_i);
17088c2ecf20Sopenharmony_ci			break;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci		default:
17118c2ecf20Sopenharmony_ci			break;
17128c2ecf20Sopenharmony_ci		}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci		p += ivhd_entry_length(p);
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	return 0;
17188c2ecf20Sopenharmony_ci}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci/*
17218c2ecf20Sopenharmony_ci * This function clues the initialization function for one IOMMU
17228c2ecf20Sopenharmony_ci * together and also allocates the command buffer and programs the
17238c2ecf20Sopenharmony_ci * hardware. It does NOT enable the IOMMU. This is done afterwards.
17248c2ecf20Sopenharmony_ci */
17258c2ecf20Sopenharmony_cistatic int __init init_iommu_one(loongson_iommu *iommu, struct ivhd_header *h)
17268c2ecf20Sopenharmony_ci{
17278c2ecf20Sopenharmony_ci	int ret;
17288c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->domain_bitmap_lock);
17318c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->dom_info_lock);
17328c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->pgtable_bitmap_lock);
17338c2ecf20Sopenharmony_ci	mutex_init(&iommu->loongson_iommu_pgtlock);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	/* Add IOMMU to internal data structures */
17368c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iommu->dom_list);
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	list_add_tail(&iommu->list, &loongson_iommu_list);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/*
17418c2ecf20Sopenharmony_ci	 * Copy data from ACPI table entry to the iommu struct
17428c2ecf20Sopenharmony_ci	 */
17438c2ecf20Sopenharmony_ci	iommu->devid   = h->devid;
17448c2ecf20Sopenharmony_ci	iommu->segment = h->pci_seg;
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	ret = init_iommu_from_acpi(iommu, h);
17478c2ecf20Sopenharmony_ci	if (ret) {
17488c2ecf20Sopenharmony_ci		pr_err("%s init iommu from acpi failed\n", __func__);
17498c2ecf20Sopenharmony_ci		return ret;
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	rlookupentry = lookup_rlooptable(iommu->segment);
17538c2ecf20Sopenharmony_ci	if (rlookupentry != NULL) {
17548c2ecf20Sopenharmony_ci		/*
17558c2ecf20Sopenharmony_ci		 * Make sure IOMMU is not considered to translate itself.
17568c2ecf20Sopenharmony_ci		 * The IVRS table tells us so, but this is a lie!
17578c2ecf20Sopenharmony_ci		 */
17588c2ecf20Sopenharmony_ci		rlookupentry->loongson_iommu_rlookup_table[iommu->devid] = NULL;
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return 0;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci/*
17658c2ecf20Sopenharmony_ci * Iterates over all IOMMU entries in the ACPI table, allocates the
17668c2ecf20Sopenharmony_ci * IOMMU structure and initializes it with init_iommu_one()
17678c2ecf20Sopenharmony_ci */
17688c2ecf20Sopenharmony_cistatic int __init init_iommu_all(struct acpi_table_header *table)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	int ret;
17718c2ecf20Sopenharmony_ci	u8 *p = (u8 *)table, *end = (u8 *)table;
17728c2ecf20Sopenharmony_ci	struct ivhd_header *h;
17738c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	end += table->length;
17768c2ecf20Sopenharmony_ci	p += IVRS_HEADER_LENGTH;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	while (p < end) {
17798c2ecf20Sopenharmony_ci		h = (struct ivhd_header *)p;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci		if (h->length == 0)
17828c2ecf20Sopenharmony_ci			break;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci		if (*p == loongson_iommu_target_ivhd_type) {
17858c2ecf20Sopenharmony_ci			pr_info("device: %02x:%02x.%01x seg: %d\n",
17868c2ecf20Sopenharmony_ci				PCI_BUS_NUM(h->devid), PCI_SLOT(h->devid), PCI_FUNC(h->devid), h->pci_seg);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci			iommu = kzalloc(sizeof(loongson_iommu), GFP_KERNEL);
17898c2ecf20Sopenharmony_ci			if (iommu == NULL) {
17908c2ecf20Sopenharmony_ci				pr_info("%s alloc memory for iommu failed\n", __func__);
17918c2ecf20Sopenharmony_ci				return -ENOMEM;
17928c2ecf20Sopenharmony_ci			}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci			ret = init_iommu_one(iommu, h);
17958c2ecf20Sopenharmony_ci			if (ret) {
17968c2ecf20Sopenharmony_ci				kfree(iommu);
17978c2ecf20Sopenharmony_ci				pr_info("%s init iommu failed\n", __func__);
17988c2ecf20Sopenharmony_ci				return ret;
17998c2ecf20Sopenharmony_ci			}
18008c2ecf20Sopenharmony_ci		}
18018c2ecf20Sopenharmony_ci		p += h->length;
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	if (p != end)
18058c2ecf20Sopenharmony_ci		return -EINVAL;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	return 0;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci/**
18118c2ecf20Sopenharmony_ci * get_highest_supported_ivhd_type - Look up the appropriate IVHD type
18128c2ecf20Sopenharmony_ci * @ivrs          Pointer to the IVRS header
18138c2ecf20Sopenharmony_ci *
18148c2ecf20Sopenharmony_ci * This function search through all IVDB of the maximum supported IVHD
18158c2ecf20Sopenharmony_ci */
18168c2ecf20Sopenharmony_cistatic u8 get_highest_supported_ivhd_type(struct acpi_table_header *ivrs)
18178c2ecf20Sopenharmony_ci{
18188c2ecf20Sopenharmony_ci	u8 *base = (u8 *)ivrs;
18198c2ecf20Sopenharmony_ci	struct ivhd_header *ivhd = (struct ivhd_header *)(base + IVRS_HEADER_LENGTH);
18208c2ecf20Sopenharmony_ci	u8 last_type = ivhd->type;
18218c2ecf20Sopenharmony_ci	u16 devid = ivhd->devid;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	while (((u8 *)ivhd - base < ivrs->length) &&
18248c2ecf20Sopenharmony_ci	       (ivhd->type <= ACPI_IVHD_TYPE_MAX_SUPPORTED) &&
18258c2ecf20Sopenharmony_ci	       (ivhd->length > 0)) {
18268c2ecf20Sopenharmony_ci		u8 *p = (u8 *) ivhd;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci		if (ivhd->devid == devid)
18298c2ecf20Sopenharmony_ci			last_type = ivhd->type;
18308c2ecf20Sopenharmony_ci		ivhd = (struct ivhd_header *)(p + ivhd->length);
18318c2ecf20Sopenharmony_ci	}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return last_type;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic inline unsigned long tbl_size(int entry_size)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	unsigned int shift = PAGE_SHIFT +
18398c2ecf20Sopenharmony_ci			 get_order(((int)loongson_iommu_last_bdf + 1) * entry_size);
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	return 1UL << shift;
18428c2ecf20Sopenharmony_ci}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_cistatic int __init loongson_iommu_ivrs_init(void)
18458c2ecf20Sopenharmony_ci{
18468c2ecf20Sopenharmony_ci	int ret = 0;
18478c2ecf20Sopenharmony_ci	acpi_status status;
18488c2ecf20Sopenharmony_ci	struct acpi_table_header *ivrs_base;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	status = acpi_get_table("IVRS", 0, &ivrs_base);
18518c2ecf20Sopenharmony_ci	if (status == AE_NOT_FOUND) {
18528c2ecf20Sopenharmony_ci		pr_info("%s get ivrs table failed\n", __func__);
18538c2ecf20Sopenharmony_ci		return -ENODEV;
18548c2ecf20Sopenharmony_ci	}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	/*
18578c2ecf20Sopenharmony_ci	 * Validate checksum here so we don't need to do it when
18588c2ecf20Sopenharmony_ci	 * we actually parse the table
18598c2ecf20Sopenharmony_ci	 */
18608c2ecf20Sopenharmony_ci	ret = check_ivrs_checksum(ivrs_base);
18618c2ecf20Sopenharmony_ci	if (ret)
18628c2ecf20Sopenharmony_ci		goto out;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	loongson_iommu_target_ivhd_type = get_highest_supported_ivhd_type(ivrs_base);
18658c2ecf20Sopenharmony_ci	pr_info("Using IVHD type %#x\n", loongson_iommu_target_ivhd_type);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	/*
18688c2ecf20Sopenharmony_ci	 * First parse ACPI tables to find the largest Bus/Dev/Func
18698c2ecf20Sopenharmony_ci	 * we need to handle. Upon this information the shared data
18708c2ecf20Sopenharmony_ci	 * structures for the IOMMUs in the system will be allocated
18718c2ecf20Sopenharmony_ci	 */
18728c2ecf20Sopenharmony_ci	ret = find_last_devid_acpi(ivrs_base);
18738c2ecf20Sopenharmony_ci	if (ret) {
18748c2ecf20Sopenharmony_ci		pr_err("%s find last devid failed\n", __func__);
18758c2ecf20Sopenharmony_ci		goto out;
18768c2ecf20Sopenharmony_ci	}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	/*
18818c2ecf20Sopenharmony_ci	 * now the data structures are allocated and basically initialized
18828c2ecf20Sopenharmony_ci	 * start the real acpi table scan
18838c2ecf20Sopenharmony_ci	 */
18848c2ecf20Sopenharmony_ci	ret = init_iommu_all(ivrs_base);
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ciout:
18878c2ecf20Sopenharmony_ci	/* Don't leak any ACPI memory */
18888c2ecf20Sopenharmony_ci	acpi_put_table(ivrs_base);
18898c2ecf20Sopenharmony_ci	ivrs_base = NULL;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	return ret;
18928c2ecf20Sopenharmony_ci}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic int __init loongson_iommu_ivrs_init_stub(void)
18958c2ecf20Sopenharmony_ci{
18968c2ecf20Sopenharmony_ci	u32 dev_i;
18978c2ecf20Sopenharmony_ci	loongson_iommu *iommu;
18988c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	/* Use maximum BDF value for DEV_ALL */
19018c2ecf20Sopenharmony_ci	update_last_devid(MAX_BDF_NUM);
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE);
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	iommu = kzalloc(sizeof(loongson_iommu), GFP_KERNEL);
19068c2ecf20Sopenharmony_ci	if (iommu == NULL) {
19078c2ecf20Sopenharmony_ci		pr_info("%s alloc memory for iommu failed\n", __func__);
19088c2ecf20Sopenharmony_ci		return -ENOMEM;
19098c2ecf20Sopenharmony_ci	}
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->domain_bitmap_lock);
19128c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->dom_info_lock);
19138c2ecf20Sopenharmony_ci	spin_lock_init(&iommu->pgtable_bitmap_lock);
19148c2ecf20Sopenharmony_ci	mutex_init(&iommu->loongson_iommu_pgtlock);
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	/* Add IOMMU to internal data structures */
19178c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iommu->dom_list);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	list_add_tail(&iommu->list, &loongson_iommu_list);
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	/*
19228c2ecf20Sopenharmony_ci	 * Copy data from ACPI table entry to the iommu struct
19238c2ecf20Sopenharmony_ci	 */
19248c2ecf20Sopenharmony_ci	iommu->devid = 0xd0;
19258c2ecf20Sopenharmony_ci	iommu->segment = 0;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	for (dev_i = 0; dev_i <= loongson_iommu_last_bdf; ++dev_i)
19288c2ecf20Sopenharmony_ci		set_iommu_for_device(iommu, dev_i);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	rlookupentry = lookup_rlooptable(iommu->segment);
19318c2ecf20Sopenharmony_ci	if (rlookupentry != NULL) {
19328c2ecf20Sopenharmony_ci		/*
19338c2ecf20Sopenharmony_ci		 * Make sure IOMMU is not considered to translate itself.
19348c2ecf20Sopenharmony_ci		 * The IVRS table tells us so, but this is a lie!
19358c2ecf20Sopenharmony_ci		 */
19368c2ecf20Sopenharmony_ci		rlookupentry->loongson_iommu_rlookup_table[iommu->devid] = NULL;
19378c2ecf20Sopenharmony_ci	}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	return 0;
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_cistatic void free_iommu_rlookup_entry(void)
19438c2ecf20Sopenharmony_ci{
19448c2ecf20Sopenharmony_ci	loongson_iommu *iommu = NULL;
19458c2ecf20Sopenharmony_ci	struct loongson_iommu_rlookup_entry *rlookupentry = NULL;
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	while (!list_empty(&loongson_iommu_list)) {
19488c2ecf20Sopenharmony_ci		iommu = list_first_entry(&loongson_iommu_list, loongson_iommu, list);
19498c2ecf20Sopenharmony_ci		list_del(&iommu->list);
19508c2ecf20Sopenharmony_ci		kfree(iommu);
19518c2ecf20Sopenharmony_ci	}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	while (!list_empty(&loongson_rlookup_iommu_list)) {
19548c2ecf20Sopenharmony_ci		rlookupentry = list_first_entry(&loongson_rlookup_iommu_list,
19558c2ecf20Sopenharmony_ci				struct loongson_iommu_rlookup_entry, list);
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci		list_del(&rlookupentry->list);
19588c2ecf20Sopenharmony_ci		if (rlookupentry->loongson_iommu_rlookup_table != NULL) {
19598c2ecf20Sopenharmony_ci			free_pages(
19608c2ecf20Sopenharmony_ci			(unsigned long)rlookupentry->loongson_iommu_rlookup_table,
19618c2ecf20Sopenharmony_ci			get_order(rlookup_table_size));
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci			rlookupentry->loongson_iommu_rlookup_table = NULL;
19648c2ecf20Sopenharmony_ci		}
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci		kfree(rlookupentry);
19678c2ecf20Sopenharmony_ci	}
19688c2ecf20Sopenharmony_ci}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_cistatic int __init loonson_iommu_setup(char *str)
19718c2ecf20Sopenharmony_ci{
19728c2ecf20Sopenharmony_ci	if (!str)
19738c2ecf20Sopenharmony_ci		return -EINVAL;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	while (*str) {
19768c2ecf20Sopenharmony_ci		if (!strncmp(str, "on", 2)) {
19778c2ecf20Sopenharmony_ci			loongson_iommu_disable = 0;
19788c2ecf20Sopenharmony_ci			pr_info("IOMMU enabled\n");
19798c2ecf20Sopenharmony_ci		} else if (!strncmp(str, "off", 3)) {
19808c2ecf20Sopenharmony_ci			loongson_iommu_disable = 1;
19818c2ecf20Sopenharmony_ci			pr_info("IOMMU disabled\n");
19828c2ecf20Sopenharmony_ci		}
19838c2ecf20Sopenharmony_ci		str += strcspn(str, ",");
19848c2ecf20Sopenharmony_ci		while (*str == ',')
19858c2ecf20Sopenharmony_ci			str++;
19868c2ecf20Sopenharmony_ci	}
19878c2ecf20Sopenharmony_ci	return 0;
19888c2ecf20Sopenharmony_ci}
19898c2ecf20Sopenharmony_ci__setup("loongson_iommu=", loonson_iommu_setup);
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cistatic const struct pci_device_id loongson_iommu_pci_tbl[] = {
19928c2ecf20Sopenharmony_ci	{ PCI_DEVICE(0x14, 0x7a1f) },
19938c2ecf20Sopenharmony_ci	{ 0, }
19948c2ecf20Sopenharmony_ci};
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_cistatic struct pci_driver loongson_iommu_driver = {
19978c2ecf20Sopenharmony_ci	.name = "loongson-iommu",
19988c2ecf20Sopenharmony_ci	.probe	= loongson_iommu_probe,
19998c2ecf20Sopenharmony_ci	.remove	= loongson_iommu_remove,
20008c2ecf20Sopenharmony_ci	.id_table = loongson_iommu_pci_tbl,
20018c2ecf20Sopenharmony_ci};
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_cistatic int __init loongson_iommu_driver_init(void)
20048c2ecf20Sopenharmony_ci{
20058c2ecf20Sopenharmony_ci	int ret = 0;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	if (!loongson_iommu_disable) {
20088c2ecf20Sopenharmony_ci		ret = loongson_iommu_ivrs_init();
20098c2ecf20Sopenharmony_ci		if (ret < 0) {
20108c2ecf20Sopenharmony_ci			free_iommu_rlookup_entry();
20118c2ecf20Sopenharmony_ci			pr_err("Failed to init iommu by ivrs\n");
20128c2ecf20Sopenharmony_ci			ret = loongson_iommu_ivrs_init_stub();
20138c2ecf20Sopenharmony_ci			if (ret < 0) {
20148c2ecf20Sopenharmony_ci				free_iommu_rlookup_entry();
20158c2ecf20Sopenharmony_ci				pr_err("Failed to init iommu by stub\n");
20168c2ecf20Sopenharmony_ci				return ret;
20178c2ecf20Sopenharmony_ci			}
20188c2ecf20Sopenharmony_ci		}
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci		ret = pci_register_driver(&loongson_iommu_driver);
20218c2ecf20Sopenharmony_ci		if (ret < 0) {
20228c2ecf20Sopenharmony_ci			pr_err("Failed to register IOMMU driver\n");
20238c2ecf20Sopenharmony_ci			return ret;
20248c2ecf20Sopenharmony_ci		}
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	return ret;
20288c2ecf20Sopenharmony_ci}
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_cistatic void __exit loongson_iommu_driver_exit(void)
20318c2ecf20Sopenharmony_ci{
20328c2ecf20Sopenharmony_ci	if (!loongson_iommu_disable) {
20338c2ecf20Sopenharmony_ci		free_iommu_rlookup_entry();
20348c2ecf20Sopenharmony_ci		pci_unregister_driver(&loongson_iommu_driver);
20358c2ecf20Sopenharmony_ci	}
20368c2ecf20Sopenharmony_ci}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_cimodule_init(loongson_iommu_driver_init);
20398c2ecf20Sopenharmony_cimodule_exit(loongson_iommu_driver_exit);
2040