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