162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2008, PA Semi, Inc 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Maintained by: Olof Johansson <olof@lixom.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#undef DEBUG 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <asm/iommu.h> 1662306a36Sopenharmony_ci#include <asm/machdep.h> 1762306a36Sopenharmony_ci#include <asm/firmware.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "pasemi.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define IOBMAP_PAGE_SHIFT 12 2262306a36Sopenharmony_ci#define IOBMAP_PAGE_SIZE (1 << IOBMAP_PAGE_SHIFT) 2362306a36Sopenharmony_ci#define IOBMAP_PAGE_MASK (IOBMAP_PAGE_SIZE - 1) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define IOB_BASE 0xe0000000 2662306a36Sopenharmony_ci#define IOB_SIZE 0x3000 2762306a36Sopenharmony_ci/* Configuration registers */ 2862306a36Sopenharmony_ci#define IOBCAP_REG 0x40 2962306a36Sopenharmony_ci#define IOBCOM_REG 0x100 3062306a36Sopenharmony_ci/* Enable IOB address translation */ 3162306a36Sopenharmony_ci#define IOBCOM_ATEN 0x00000100 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Address decode configuration register */ 3462306a36Sopenharmony_ci#define IOB_AD_REG 0x14c 3562306a36Sopenharmony_ci/* IOBCOM_AD_REG fields */ 3662306a36Sopenharmony_ci#define IOB_AD_VGPRT 0x00000e00 3762306a36Sopenharmony_ci#define IOB_AD_VGAEN 0x00000100 3862306a36Sopenharmony_ci/* Direct mapping settings */ 3962306a36Sopenharmony_ci#define IOB_AD_MPSEL_MASK 0x00000030 4062306a36Sopenharmony_ci#define IOB_AD_MPSEL_B38 0x00000000 4162306a36Sopenharmony_ci#define IOB_AD_MPSEL_B40 0x00000010 4262306a36Sopenharmony_ci#define IOB_AD_MPSEL_B42 0x00000020 4362306a36Sopenharmony_ci/* Translation window size / enable */ 4462306a36Sopenharmony_ci#define IOB_AD_TRNG_MASK 0x00000003 4562306a36Sopenharmony_ci#define IOB_AD_TRNG_256M 0x00000000 4662306a36Sopenharmony_ci#define IOB_AD_TRNG_2G 0x00000001 4762306a36Sopenharmony_ci#define IOB_AD_TRNG_128G 0x00000003 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define IOB_TABLEBASE_REG 0x154 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Base of the 64 4-byte L1 registers */ 5262306a36Sopenharmony_ci#define IOB_XLT_L1_REGBASE 0x2b00 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Register to invalidate TLB entries */ 5562306a36Sopenharmony_ci#define IOB_AT_INVAL_TLB_REG 0x2d00 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* The top two bits of the level 1 entry contains valid and type flags */ 5862306a36Sopenharmony_ci#define IOBMAP_L1E_V 0x40000000 5962306a36Sopenharmony_ci#define IOBMAP_L1E_V_B 0x80000000 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* For big page entries, the bottom two bits contains flags */ 6262306a36Sopenharmony_ci#define IOBMAP_L1E_BIG_CACHED 0x00000002 6362306a36Sopenharmony_ci#define IOBMAP_L1E_BIG_PRIORITY 0x00000001 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* For regular level 2 entries, top 2 bits contain valid and cache flags */ 6662306a36Sopenharmony_ci#define IOBMAP_L2E_V 0x80000000 6762306a36Sopenharmony_ci#define IOBMAP_L2E_V_CACHED 0xc0000000 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __iomem *iob; 7062306a36Sopenharmony_cistatic u32 iob_l1_emptyval; 7162306a36Sopenharmony_cistatic u32 iob_l2_emptyval; 7262306a36Sopenharmony_cistatic u32 *iob_l2_base; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct iommu_table iommu_table_iobmap; 7562306a36Sopenharmony_cistatic int iommu_table_iobmap_inited; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int iobmap_build(struct iommu_table *tbl, long index, 7862306a36Sopenharmony_ci long npages, unsigned long uaddr, 7962306a36Sopenharmony_ci enum dma_data_direction direction, 8062306a36Sopenharmony_ci unsigned long attrs) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci u32 *ip; 8362306a36Sopenharmony_ci u32 rpn; 8462306a36Sopenharmony_ci unsigned long bus_addr; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci pr_debug("iobmap: build at: %lx, %lx, addr: %lx\n", index, npages, uaddr); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci bus_addr = (tbl->it_offset + index) << IOBMAP_PAGE_SHIFT; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ip = ((u32 *)tbl->it_base) + index; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci while (npages--) { 9362306a36Sopenharmony_ci rpn = __pa(uaddr) >> IOBMAP_PAGE_SHIFT; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci *(ip++) = IOBMAP_L2E_V | rpn; 9662306a36Sopenharmony_ci /* invalidate tlb, can be optimized more */ 9762306a36Sopenharmony_ci out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci uaddr += IOBMAP_PAGE_SIZE; 10062306a36Sopenharmony_ci bus_addr += IOBMAP_PAGE_SIZE; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void iobmap_free(struct iommu_table *tbl, long index, 10762306a36Sopenharmony_ci long npages) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 *ip; 11062306a36Sopenharmony_ci unsigned long bus_addr; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci pr_debug("iobmap: free at: %lx, %lx\n", index, npages); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci bus_addr = (tbl->it_offset + index) << IOBMAP_PAGE_SHIFT; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ip = ((u32 *)tbl->it_base) + index; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci while (npages--) { 11962306a36Sopenharmony_ci *(ip++) = iob_l2_emptyval; 12062306a36Sopenharmony_ci /* invalidate tlb, can be optimized more */ 12162306a36Sopenharmony_ci out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); 12262306a36Sopenharmony_ci bus_addr += IOBMAP_PAGE_SIZE; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic struct iommu_table_ops iommu_table_iobmap_ops = { 12762306a36Sopenharmony_ci .set = iobmap_build, 12862306a36Sopenharmony_ci .clear = iobmap_free 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void iommu_table_iobmap_setup(void) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci pr_debug(" -> %s\n", __func__); 13462306a36Sopenharmony_ci iommu_table_iobmap.it_busno = 0; 13562306a36Sopenharmony_ci iommu_table_iobmap.it_offset = 0; 13662306a36Sopenharmony_ci iommu_table_iobmap.it_page_shift = IOBMAP_PAGE_SHIFT; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* it_size is in number of entries */ 13962306a36Sopenharmony_ci iommu_table_iobmap.it_size = 14062306a36Sopenharmony_ci 0x80000000 >> iommu_table_iobmap.it_page_shift; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Initialize the common IOMMU code */ 14362306a36Sopenharmony_ci iommu_table_iobmap.it_base = (unsigned long)iob_l2_base; 14462306a36Sopenharmony_ci iommu_table_iobmap.it_index = 0; 14562306a36Sopenharmony_ci /* XXXOJN tune this to avoid IOB cache invals. 14662306a36Sopenharmony_ci * Should probably be 8 (64 bytes) 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci iommu_table_iobmap.it_blocksize = 4; 14962306a36Sopenharmony_ci iommu_table_iobmap.it_ops = &iommu_table_iobmap_ops; 15062306a36Sopenharmony_ci if (!iommu_init_table(&iommu_table_iobmap, 0, 0, 0)) 15162306a36Sopenharmony_ci panic("Failed to initialize iommu table"); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci pr_debug(" <- %s\n", __func__); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void pci_dma_bus_setup_pasemi(struct pci_bus *bus) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci pr_debug("pci_dma_bus_setup, bus %p, bus->self %p\n", bus, bus->self); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!iommu_table_iobmap_inited) { 16362306a36Sopenharmony_ci iommu_table_iobmap_inited = 1; 16462306a36Sopenharmony_ci iommu_table_iobmap_setup(); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void pci_dma_dev_setup_pasemi(struct pci_dev *dev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci pr_debug("pci_dma_dev_setup, dev %p (%s)\n", dev, pci_name(dev)); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#if !defined(CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE) 17462306a36Sopenharmony_ci /* For non-LPAR environment, don't translate anything for the DMA 17562306a36Sopenharmony_ci * engine. The exception to this is if the user has enabled 17662306a36Sopenharmony_ci * CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE at build time. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci if (dev->vendor == 0x1959 && dev->device == 0xa007 && 17962306a36Sopenharmony_ci !firmware_has_feature(FW_FEATURE_LPAR)) { 18062306a36Sopenharmony_ci dev->dev.dma_ops = NULL; 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Set the coherent DMA mask to prevent the iommu 18362306a36Sopenharmony_ci * being used unnecessarily 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci dev->dev.coherent_dma_mask = DMA_BIT_MASK(44); 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci#endif 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci set_iommu_table_base(&dev->dev, &iommu_table_iobmap); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int __init iob_init(struct device_node *dn) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci unsigned long tmp; 19662306a36Sopenharmony_ci u32 regword; 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci pr_debug(" -> %s\n", __func__); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* For 2G space, 8x64 pages (2^21 bytes) is max total l2 size */ 20262306a36Sopenharmony_ci iob_l2_base = memblock_alloc_try_nid_raw(1UL << 21, 1UL << 21, 20362306a36Sopenharmony_ci MEMBLOCK_LOW_LIMIT, 0x80000000, 20462306a36Sopenharmony_ci NUMA_NO_NODE); 20562306a36Sopenharmony_ci if (!iob_l2_base) 20662306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx max_addr=%x\n", 20762306a36Sopenharmony_ci __func__, 1UL << 21, 1UL << 21, 0x80000000); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci pr_info("IOBMAP L2 allocated at: %p\n", iob_l2_base); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Allocate a spare page to map all invalid IOTLB pages. */ 21262306a36Sopenharmony_ci tmp = memblock_phys_alloc(IOBMAP_PAGE_SIZE, IOBMAP_PAGE_SIZE); 21362306a36Sopenharmony_ci if (!tmp) 21462306a36Sopenharmony_ci panic("IOBMAP: Cannot allocate spare page!"); 21562306a36Sopenharmony_ci /* Empty l1 is marked invalid */ 21662306a36Sopenharmony_ci iob_l1_emptyval = 0; 21762306a36Sopenharmony_ci /* Empty l2 is mapped to dummy page */ 21862306a36Sopenharmony_ci iob_l2_emptyval = IOBMAP_L2E_V | (tmp >> IOBMAP_PAGE_SHIFT); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci iob = ioremap(IOB_BASE, IOB_SIZE); 22162306a36Sopenharmony_ci if (!iob) 22262306a36Sopenharmony_ci panic("IOBMAP: Cannot map registers!"); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* setup direct mapping of the L1 entries */ 22562306a36Sopenharmony_ci for (i = 0; i < 64; i++) { 22662306a36Sopenharmony_ci /* Each L1 covers 32MB, i.e. 8K entries = 32K of ram */ 22762306a36Sopenharmony_ci regword = IOBMAP_L1E_V | (__pa(iob_l2_base + i*0x2000) >> 12); 22862306a36Sopenharmony_ci out_le32(iob+IOB_XLT_L1_REGBASE+i*4, regword); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* set 2GB translation window, based at 0 */ 23262306a36Sopenharmony_ci regword = in_le32(iob+IOB_AD_REG); 23362306a36Sopenharmony_ci regword &= ~IOB_AD_TRNG_MASK; 23462306a36Sopenharmony_ci regword |= IOB_AD_TRNG_2G; 23562306a36Sopenharmony_ci out_le32(iob+IOB_AD_REG, regword); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Enable translation */ 23862306a36Sopenharmony_ci regword = in_le32(iob+IOBCOM_REG); 23962306a36Sopenharmony_ci regword |= IOBCOM_ATEN; 24062306a36Sopenharmony_ci out_le32(iob+IOBCOM_REG, regword); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pr_debug(" <- %s\n", __func__); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* These are called very early. */ 24962306a36Sopenharmony_civoid __init iommu_init_early_pasemi(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int iommu_off; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#ifndef CONFIG_PPC_PASEMI_IOMMU 25462306a36Sopenharmony_ci iommu_off = 1; 25562306a36Sopenharmony_ci#else 25662306a36Sopenharmony_ci iommu_off = of_chosen && 25762306a36Sopenharmony_ci of_property_read_bool(of_chosen, "linux,iommu-off"); 25862306a36Sopenharmony_ci#endif 25962306a36Sopenharmony_ci if (iommu_off) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci iob_init(NULL); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci pasemi_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pasemi; 26562306a36Sopenharmony_ci pasemi_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pasemi; 26662306a36Sopenharmony_ci set_pci_dma_ops(&dma_iommu_ops); 26762306a36Sopenharmony_ci} 268