162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IOMMU implementation for Cell Broadband Processor Architecture 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright IBM Corporation 2006-2008 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Jeremy Kerr <jk@ozlabs.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#undef DEBUG 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irqdomain.h> 1662306a36Sopenharmony_ci#include <linux/notifier.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/memblock.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/prom.h> 2462306a36Sopenharmony_ci#include <asm/iommu.h> 2562306a36Sopenharmony_ci#include <asm/machdep.h> 2662306a36Sopenharmony_ci#include <asm/pci-bridge.h> 2762306a36Sopenharmony_ci#include <asm/udbg.h> 2862306a36Sopenharmony_ci#include <asm/firmware.h> 2962306a36Sopenharmony_ci#include <asm/cell-regs.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "cell.h" 3262306a36Sopenharmony_ci#include "interrupt.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Define CELL_IOMMU_REAL_UNMAP to actually unmap non-used pages 3562306a36Sopenharmony_ci * instead of leaving them mapped to some dummy page. This can be 3662306a36Sopenharmony_ci * enabled once the appropriate workarounds for spider bugs have 3762306a36Sopenharmony_ci * been enabled 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci#define CELL_IOMMU_REAL_UNMAP 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Define CELL_IOMMU_STRICT_PROTECTION to enforce protection of 4262306a36Sopenharmony_ci * IO PTEs based on the transfer direction. That can be enabled 4362306a36Sopenharmony_ci * once spider-net has been fixed to pass the correct direction 4462306a36Sopenharmony_ci * to the DMA mapping functions 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci#define CELL_IOMMU_STRICT_PROTECTION 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define NR_IOMMUS 2 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* IOC mmap registers */ 5262306a36Sopenharmony_ci#define IOC_Reg_Size 0x2000 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define IOC_IOPT_CacheInvd 0x908 5562306a36Sopenharmony_ci#define IOC_IOPT_CacheInvd_NE_Mask 0xffe0000000000000ul 5662306a36Sopenharmony_ci#define IOC_IOPT_CacheInvd_IOPTE_Mask 0x000003fffffffff8ul 5762306a36Sopenharmony_ci#define IOC_IOPT_CacheInvd_Busy 0x0000000000000001ul 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define IOC_IOST_Origin 0x918 6062306a36Sopenharmony_ci#define IOC_IOST_Origin_E 0x8000000000000000ul 6162306a36Sopenharmony_ci#define IOC_IOST_Origin_HW 0x0000000000000800ul 6262306a36Sopenharmony_ci#define IOC_IOST_Origin_HL 0x0000000000000400ul 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define IOC_IO_ExcpStat 0x920 6562306a36Sopenharmony_ci#define IOC_IO_ExcpStat_V 0x8000000000000000ul 6662306a36Sopenharmony_ci#define IOC_IO_ExcpStat_SPF_Mask 0x6000000000000000ul 6762306a36Sopenharmony_ci#define IOC_IO_ExcpStat_SPF_S 0x6000000000000000ul 6862306a36Sopenharmony_ci#define IOC_IO_ExcpStat_SPF_P 0x2000000000000000ul 6962306a36Sopenharmony_ci#define IOC_IO_ExcpStat_ADDR_Mask 0x00000007fffff000ul 7062306a36Sopenharmony_ci#define IOC_IO_ExcpStat_RW_Mask 0x0000000000000800ul 7162306a36Sopenharmony_ci#define IOC_IO_ExcpStat_IOID_Mask 0x00000000000007fful 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define IOC_IO_ExcpMask 0x928 7462306a36Sopenharmony_ci#define IOC_IO_ExcpMask_SFE 0x4000000000000000ul 7562306a36Sopenharmony_ci#define IOC_IO_ExcpMask_PFE 0x2000000000000000ul 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define IOC_IOCmd_Offset 0x1000 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define IOC_IOCmd_Cfg 0xc00 8062306a36Sopenharmony_ci#define IOC_IOCmd_Cfg_TE 0x0000800000000000ul 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Segment table entries */ 8462306a36Sopenharmony_ci#define IOSTE_V 0x8000000000000000ul /* valid */ 8562306a36Sopenharmony_ci#define IOSTE_H 0x4000000000000000ul /* cache hint */ 8662306a36Sopenharmony_ci#define IOSTE_PT_Base_RPN_Mask 0x3ffffffffffff000ul /* base RPN of IOPT */ 8762306a36Sopenharmony_ci#define IOSTE_NPPT_Mask 0x0000000000000fe0ul /* no. pages in IOPT */ 8862306a36Sopenharmony_ci#define IOSTE_PS_Mask 0x0000000000000007ul /* page size */ 8962306a36Sopenharmony_ci#define IOSTE_PS_4K 0x0000000000000001ul /* - 4kB */ 9062306a36Sopenharmony_ci#define IOSTE_PS_64K 0x0000000000000003ul /* - 64kB */ 9162306a36Sopenharmony_ci#define IOSTE_PS_1M 0x0000000000000005ul /* - 1MB */ 9262306a36Sopenharmony_ci#define IOSTE_PS_16M 0x0000000000000007ul /* - 16MB */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* IOMMU sizing */ 9662306a36Sopenharmony_ci#define IO_SEGMENT_SHIFT 28 9762306a36Sopenharmony_ci#define IO_PAGENO_BITS(shift) (IO_SEGMENT_SHIFT - (shift)) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* The high bit needs to be set on every DMA address */ 10062306a36Sopenharmony_ci#define SPIDER_DMA_OFFSET 0x80000000ul 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct iommu_window { 10362306a36Sopenharmony_ci struct list_head list; 10462306a36Sopenharmony_ci struct cbe_iommu *iommu; 10562306a36Sopenharmony_ci unsigned long offset; 10662306a36Sopenharmony_ci unsigned long size; 10762306a36Sopenharmony_ci unsigned int ioid; 10862306a36Sopenharmony_ci struct iommu_table table; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define NAMESIZE 8 11262306a36Sopenharmony_cistruct cbe_iommu { 11362306a36Sopenharmony_ci int nid; 11462306a36Sopenharmony_ci char name[NAMESIZE]; 11562306a36Sopenharmony_ci void __iomem *xlate_regs; 11662306a36Sopenharmony_ci void __iomem *cmd_regs; 11762306a36Sopenharmony_ci unsigned long *stab; 11862306a36Sopenharmony_ci unsigned long *ptab; 11962306a36Sopenharmony_ci void *pad_page; 12062306a36Sopenharmony_ci struct list_head windows; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Static array of iommus, one per node 12462306a36Sopenharmony_ci * each contains a list of windows, keyed from dma_window property 12562306a36Sopenharmony_ci * - on bus setup, look for a matching window, or create one 12662306a36Sopenharmony_ci * - on dev setup, assign iommu_table ptr 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic struct cbe_iommu iommus[NR_IOMMUS]; 12962306a36Sopenharmony_cistatic int cbe_nr_iommus; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte, 13262306a36Sopenharmony_ci long n_ptes) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u64 __iomem *reg; 13562306a36Sopenharmony_ci u64 val; 13662306a36Sopenharmony_ci long n; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci reg = iommu->xlate_regs + IOC_IOPT_CacheInvd; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci while (n_ptes > 0) { 14162306a36Sopenharmony_ci /* we can invalidate up to 1 << 11 PTEs at once */ 14262306a36Sopenharmony_ci n = min(n_ptes, 1l << 11); 14362306a36Sopenharmony_ci val = (((n /*- 1*/) << 53) & IOC_IOPT_CacheInvd_NE_Mask) 14462306a36Sopenharmony_ci | (__pa(pte) & IOC_IOPT_CacheInvd_IOPTE_Mask) 14562306a36Sopenharmony_ci | IOC_IOPT_CacheInvd_Busy; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci out_be64(reg, val); 14862306a36Sopenharmony_ci while (in_be64(reg) & IOC_IOPT_CacheInvd_Busy) 14962306a36Sopenharmony_ci ; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci n_ptes -= n; 15262306a36Sopenharmony_ci pte += n; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int tce_build_cell(struct iommu_table *tbl, long index, long npages, 15762306a36Sopenharmony_ci unsigned long uaddr, enum dma_data_direction direction, 15862306a36Sopenharmony_ci unsigned long attrs) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci unsigned long *io_pte, base_pte; 16262306a36Sopenharmony_ci struct iommu_window *window = 16362306a36Sopenharmony_ci container_of(tbl, struct iommu_window, table); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* implementing proper protection causes problems with the spidernet 16662306a36Sopenharmony_ci * driver - check mapping directions later, but allow read & write by 16762306a36Sopenharmony_ci * default for now.*/ 16862306a36Sopenharmony_ci#ifdef CELL_IOMMU_STRICT_PROTECTION 16962306a36Sopenharmony_ci /* to avoid referencing a global, we use a trick here to setup the 17062306a36Sopenharmony_ci * protection bit. "prot" is setup to be 3 fields of 4 bits appended 17162306a36Sopenharmony_ci * together for each of the 3 supported direction values. It is then 17262306a36Sopenharmony_ci * shifted left so that the fields matching the desired direction 17362306a36Sopenharmony_ci * lands on the appropriate bits, and other bits are masked out. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci const unsigned long prot = 0xc48; 17662306a36Sopenharmony_ci base_pte = 17762306a36Sopenharmony_ci ((prot << (52 + 4 * direction)) & 17862306a36Sopenharmony_ci (CBE_IOPTE_PP_W | CBE_IOPTE_PP_R)) | 17962306a36Sopenharmony_ci CBE_IOPTE_M | CBE_IOPTE_SO_RW | 18062306a36Sopenharmony_ci (window->ioid & CBE_IOPTE_IOID_Mask); 18162306a36Sopenharmony_ci#else 18262306a36Sopenharmony_ci base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M | 18362306a36Sopenharmony_ci CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci if (unlikely(attrs & DMA_ATTR_WEAK_ORDERING)) 18662306a36Sopenharmony_ci base_pte &= ~CBE_IOPTE_SO_RW; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (i = 0; i < npages; i++, uaddr += (1 << tbl->it_page_shift)) 19162306a36Sopenharmony_ci io_pte[i] = base_pte | (__pa(uaddr) & CBE_IOPTE_RPN_Mask); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mb(); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci invalidate_tce_cache(window->iommu, io_pte, npages); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci pr_debug("tce_build_cell(index=%lx,n=%lx,dir=%d,base_pte=%lx)\n", 19862306a36Sopenharmony_ci index, npages, direction, base_pte); 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void tce_free_cell(struct iommu_table *tbl, long index, long npages) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci unsigned long *io_pte, pte; 20762306a36Sopenharmony_ci struct iommu_window *window = 20862306a36Sopenharmony_ci container_of(tbl, struct iommu_window, table); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci pr_debug("tce_free_cell(index=%lx,n=%lx)\n", index, npages); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci#ifdef CELL_IOMMU_REAL_UNMAP 21362306a36Sopenharmony_ci pte = 0; 21462306a36Sopenharmony_ci#else 21562306a36Sopenharmony_ci /* spider bridge does PCI reads after freeing - insert a mapping 21662306a36Sopenharmony_ci * to a scratch page instead of an invalid entry */ 21762306a36Sopenharmony_ci pte = CBE_IOPTE_PP_R | CBE_IOPTE_M | CBE_IOPTE_SO_RW | 21862306a36Sopenharmony_ci __pa(window->iommu->pad_page) | 21962306a36Sopenharmony_ci (window->ioid & CBE_IOPTE_IOID_Mask); 22062306a36Sopenharmony_ci#endif 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci for (i = 0; i < npages; i++) 22562306a36Sopenharmony_ci io_pte[i] = pte; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci mb(); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci invalidate_tce_cache(window->iommu, io_pte, npages); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic irqreturn_t ioc_interrupt(int irq, void *data) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci unsigned long stat, spf; 23562306a36Sopenharmony_ci struct cbe_iommu *iommu = data; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci stat = in_be64(iommu->xlate_regs + IOC_IO_ExcpStat); 23862306a36Sopenharmony_ci spf = stat & IOC_IO_ExcpStat_SPF_Mask; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Might want to rate limit it */ 24162306a36Sopenharmony_ci printk(KERN_ERR "iommu: DMA exception 0x%016lx\n", stat); 24262306a36Sopenharmony_ci printk(KERN_ERR " V=%d, SPF=[%c%c], RW=%s, IOID=0x%04x\n", 24362306a36Sopenharmony_ci !!(stat & IOC_IO_ExcpStat_V), 24462306a36Sopenharmony_ci (spf == IOC_IO_ExcpStat_SPF_S) ? 'S' : ' ', 24562306a36Sopenharmony_ci (spf == IOC_IO_ExcpStat_SPF_P) ? 'P' : ' ', 24662306a36Sopenharmony_ci (stat & IOC_IO_ExcpStat_RW_Mask) ? "Read" : "Write", 24762306a36Sopenharmony_ci (unsigned int)(stat & IOC_IO_ExcpStat_IOID_Mask)); 24862306a36Sopenharmony_ci printk(KERN_ERR " page=0x%016lx\n", 24962306a36Sopenharmony_ci stat & IOC_IO_ExcpStat_ADDR_Mask); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* clear interrupt */ 25262306a36Sopenharmony_ci stat &= ~IOC_IO_ExcpStat_V; 25362306a36Sopenharmony_ci out_be64(iommu->xlate_regs + IOC_IO_ExcpStat, stat); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return IRQ_HANDLED; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int __init cell_iommu_find_ioc(int nid, unsigned long *base) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct device_node *np; 26162306a36Sopenharmony_ci struct resource r; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci *base = 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* First look for new style /be nodes */ 26662306a36Sopenharmony_ci for_each_node_by_name(np, "ioc") { 26762306a36Sopenharmony_ci if (of_node_to_nid(np) != nid) 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &r)) { 27062306a36Sopenharmony_ci printk(KERN_ERR "iommu: can't get address for %pOF\n", 27162306a36Sopenharmony_ci np); 27262306a36Sopenharmony_ci continue; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci *base = r.start; 27562306a36Sopenharmony_ci of_node_put(np); 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Ok, let's try the old way */ 28062306a36Sopenharmony_ci for_each_node_by_type(np, "cpu") { 28162306a36Sopenharmony_ci const unsigned int *nidp; 28262306a36Sopenharmony_ci const unsigned long *tmp; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci nidp = of_get_property(np, "node-id", NULL); 28562306a36Sopenharmony_ci if (nidp && *nidp == nid) { 28662306a36Sopenharmony_ci tmp = of_get_property(np, "ioc-translation", NULL); 28762306a36Sopenharmony_ci if (tmp) { 28862306a36Sopenharmony_ci *base = *tmp; 28962306a36Sopenharmony_ci of_node_put(np); 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return -ENODEV; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void __init cell_iommu_setup_stab(struct cbe_iommu *iommu, 29962306a36Sopenharmony_ci unsigned long dbase, unsigned long dsize, 30062306a36Sopenharmony_ci unsigned long fbase, unsigned long fsize) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct page *page; 30362306a36Sopenharmony_ci unsigned long segments, stab_size; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci segments = max(dbase + dsize, fbase + fsize) >> IO_SEGMENT_SHIFT; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci pr_debug("%s: iommu[%d]: segments: %lu\n", 30862306a36Sopenharmony_ci __func__, iommu->nid, segments); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* set up the segment table */ 31162306a36Sopenharmony_ci stab_size = segments * sizeof(unsigned long); 31262306a36Sopenharmony_ci page = alloc_pages_node(iommu->nid, GFP_KERNEL, get_order(stab_size)); 31362306a36Sopenharmony_ci BUG_ON(!page); 31462306a36Sopenharmony_ci iommu->stab = page_address(page); 31562306a36Sopenharmony_ci memset(iommu->stab, 0, stab_size); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic unsigned long *__init cell_iommu_alloc_ptab(struct cbe_iommu *iommu, 31962306a36Sopenharmony_ci unsigned long base, unsigned long size, unsigned long gap_base, 32062306a36Sopenharmony_ci unsigned long gap_size, unsigned long page_shift) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct page *page; 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci unsigned long reg, segments, pages_per_segment, ptab_size, 32562306a36Sopenharmony_ci n_pte_pages, start_seg, *ptab; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci start_seg = base >> IO_SEGMENT_SHIFT; 32862306a36Sopenharmony_ci segments = size >> IO_SEGMENT_SHIFT; 32962306a36Sopenharmony_ci pages_per_segment = 1ull << IO_PAGENO_BITS(page_shift); 33062306a36Sopenharmony_ci /* PTEs for each segment must start on a 4K boundary */ 33162306a36Sopenharmony_ci pages_per_segment = max(pages_per_segment, 33262306a36Sopenharmony_ci (1 << 12) / sizeof(unsigned long)); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ptab_size = segments * pages_per_segment * sizeof(unsigned long); 33562306a36Sopenharmony_ci pr_debug("%s: iommu[%d]: ptab_size: %lu, order: %d\n", __func__, 33662306a36Sopenharmony_ci iommu->nid, ptab_size, get_order(ptab_size)); 33762306a36Sopenharmony_ci page = alloc_pages_node(iommu->nid, GFP_KERNEL, get_order(ptab_size)); 33862306a36Sopenharmony_ci BUG_ON(!page); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ptab = page_address(page); 34162306a36Sopenharmony_ci memset(ptab, 0, ptab_size); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* number of 4K pages needed for a page table */ 34462306a36Sopenharmony_ci n_pte_pages = (pages_per_segment * sizeof(unsigned long)) >> 12; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci pr_debug("%s: iommu[%d]: stab at %p, ptab at %p, n_pte_pages: %lu\n", 34762306a36Sopenharmony_ci __func__, iommu->nid, iommu->stab, ptab, 34862306a36Sopenharmony_ci n_pte_pages); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* initialise the STEs */ 35162306a36Sopenharmony_ci reg = IOSTE_V | ((n_pte_pages - 1) << 5); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci switch (page_shift) { 35462306a36Sopenharmony_ci case 12: reg |= IOSTE_PS_4K; break; 35562306a36Sopenharmony_ci case 16: reg |= IOSTE_PS_64K; break; 35662306a36Sopenharmony_ci case 20: reg |= IOSTE_PS_1M; break; 35762306a36Sopenharmony_ci case 24: reg |= IOSTE_PS_16M; break; 35862306a36Sopenharmony_ci default: BUG(); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci gap_base = gap_base >> IO_SEGMENT_SHIFT; 36262306a36Sopenharmony_ci gap_size = gap_size >> IO_SEGMENT_SHIFT; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci pr_debug("Setting up IOMMU stab:\n"); 36562306a36Sopenharmony_ci for (i = start_seg; i < (start_seg + segments); i++) { 36662306a36Sopenharmony_ci if (i >= gap_base && i < (gap_base + gap_size)) { 36762306a36Sopenharmony_ci pr_debug("\toverlap at %d, skipping\n", i); 36862306a36Sopenharmony_ci continue; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci iommu->stab[i] = reg | (__pa(ptab) + (n_pte_pages << 12) * 37162306a36Sopenharmony_ci (i - start_seg)); 37262306a36Sopenharmony_ci pr_debug("\t[%d] 0x%016lx\n", i, iommu->stab[i]); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return ptab; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void __init cell_iommu_enable_hardware(struct cbe_iommu *iommu) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci unsigned long reg, xlate_base; 38262306a36Sopenharmony_ci unsigned int virq; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (cell_iommu_find_ioc(iommu->nid, &xlate_base)) 38562306a36Sopenharmony_ci panic("%s: missing IOC register mappings for node %d\n", 38662306a36Sopenharmony_ci __func__, iommu->nid); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci iommu->xlate_regs = ioremap(xlate_base, IOC_Reg_Size); 38962306a36Sopenharmony_ci iommu->cmd_regs = iommu->xlate_regs + IOC_IOCmd_Offset; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* ensure that the STEs have updated */ 39262306a36Sopenharmony_ci mb(); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* setup interrupts for the iommu. */ 39562306a36Sopenharmony_ci reg = in_be64(iommu->xlate_regs + IOC_IO_ExcpStat); 39662306a36Sopenharmony_ci out_be64(iommu->xlate_regs + IOC_IO_ExcpStat, 39762306a36Sopenharmony_ci reg & ~IOC_IO_ExcpStat_V); 39862306a36Sopenharmony_ci out_be64(iommu->xlate_regs + IOC_IO_ExcpMask, 39962306a36Sopenharmony_ci IOC_IO_ExcpMask_PFE | IOC_IO_ExcpMask_SFE); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci virq = irq_create_mapping(NULL, 40262306a36Sopenharmony_ci IIC_IRQ_IOEX_ATI | (iommu->nid << IIC_IRQ_NODE_SHIFT)); 40362306a36Sopenharmony_ci BUG_ON(!virq); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = request_irq(virq, ioc_interrupt, 0, iommu->name, iommu); 40662306a36Sopenharmony_ci BUG_ON(ret); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* set the IOC segment table origin register (and turn on the iommu) */ 40962306a36Sopenharmony_ci reg = IOC_IOST_Origin_E | __pa(iommu->stab) | IOC_IOST_Origin_HW; 41062306a36Sopenharmony_ci out_be64(iommu->xlate_regs + IOC_IOST_Origin, reg); 41162306a36Sopenharmony_ci in_be64(iommu->xlate_regs + IOC_IOST_Origin); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* turn on IO translation */ 41462306a36Sopenharmony_ci reg = in_be64(iommu->cmd_regs + IOC_IOCmd_Cfg) | IOC_IOCmd_Cfg_TE; 41562306a36Sopenharmony_ci out_be64(iommu->cmd_regs + IOC_IOCmd_Cfg, reg); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void __init cell_iommu_setup_hardware(struct cbe_iommu *iommu, 41962306a36Sopenharmony_ci unsigned long base, unsigned long size) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci cell_iommu_setup_stab(iommu, base, size, 0, 0); 42262306a36Sopenharmony_ci iommu->ptab = cell_iommu_alloc_ptab(iommu, base, size, 0, 0, 42362306a36Sopenharmony_ci IOMMU_PAGE_SHIFT_4K); 42462306a36Sopenharmony_ci cell_iommu_enable_hardware(iommu); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci#if 0/* Unused for now */ 42862306a36Sopenharmony_cistatic struct iommu_window *find_window(struct cbe_iommu *iommu, 42962306a36Sopenharmony_ci unsigned long offset, unsigned long size) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct iommu_window *window; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* todo: check for overlapping (but not equal) windows) */ 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci list_for_each_entry(window, &(iommu->windows), list) { 43662306a36Sopenharmony_ci if (window->offset == offset && window->size == size) 43762306a36Sopenharmony_ci return window; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return NULL; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci#endif 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic inline u32 cell_iommu_get_ioid(struct device_node *np) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci const u32 *ioid; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ioid = of_get_property(np, "ioid", NULL); 44962306a36Sopenharmony_ci if (ioid == NULL) { 45062306a36Sopenharmony_ci printk(KERN_WARNING "iommu: missing ioid for %pOF using 0\n", 45162306a36Sopenharmony_ci np); 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return *ioid; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct iommu_table_ops cell_iommu_ops = { 45962306a36Sopenharmony_ci .set = tce_build_cell, 46062306a36Sopenharmony_ci .clear = tce_free_cell 46162306a36Sopenharmony_ci}; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct iommu_window * __init 46462306a36Sopenharmony_cicell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np, 46562306a36Sopenharmony_ci unsigned long offset, unsigned long size, 46662306a36Sopenharmony_ci unsigned long pte_offset) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct iommu_window *window; 46962306a36Sopenharmony_ci struct page *page; 47062306a36Sopenharmony_ci u32 ioid; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ioid = cell_iommu_get_ioid(np); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci window = kzalloc_node(sizeof(*window), GFP_KERNEL, iommu->nid); 47562306a36Sopenharmony_ci BUG_ON(window == NULL); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci window->offset = offset; 47862306a36Sopenharmony_ci window->size = size; 47962306a36Sopenharmony_ci window->ioid = ioid; 48062306a36Sopenharmony_ci window->iommu = iommu; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci window->table.it_blocksize = 16; 48362306a36Sopenharmony_ci window->table.it_base = (unsigned long)iommu->ptab; 48462306a36Sopenharmony_ci window->table.it_index = iommu->nid; 48562306a36Sopenharmony_ci window->table.it_page_shift = IOMMU_PAGE_SHIFT_4K; 48662306a36Sopenharmony_ci window->table.it_offset = 48762306a36Sopenharmony_ci (offset >> window->table.it_page_shift) + pte_offset; 48862306a36Sopenharmony_ci window->table.it_size = size >> window->table.it_page_shift; 48962306a36Sopenharmony_ci window->table.it_ops = &cell_iommu_ops; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!iommu_init_table(&window->table, iommu->nid, 0, 0)) 49262306a36Sopenharmony_ci panic("Failed to initialize iommu table"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci pr_debug("\tioid %d\n", window->ioid); 49562306a36Sopenharmony_ci pr_debug("\tblocksize %ld\n", window->table.it_blocksize); 49662306a36Sopenharmony_ci pr_debug("\tbase 0x%016lx\n", window->table.it_base); 49762306a36Sopenharmony_ci pr_debug("\toffset 0x%lx\n", window->table.it_offset); 49862306a36Sopenharmony_ci pr_debug("\tsize %ld\n", window->table.it_size); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci list_add(&window->list, &iommu->windows); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (offset != 0) 50362306a36Sopenharmony_ci return window; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* We need to map and reserve the first IOMMU page since it's used 50662306a36Sopenharmony_ci * by the spider workaround. In theory, we only need to do that when 50762306a36Sopenharmony_ci * running on spider but it doesn't really matter. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * This code also assumes that we have a window that starts at 0, 51062306a36Sopenharmony_ci * which is the case on all spider based blades. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci page = alloc_pages_node(iommu->nid, GFP_KERNEL, 0); 51362306a36Sopenharmony_ci BUG_ON(!page); 51462306a36Sopenharmony_ci iommu->pad_page = page_address(page); 51562306a36Sopenharmony_ci clear_page(iommu->pad_page); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci __set_bit(0, window->table.it_map); 51862306a36Sopenharmony_ci tce_build_cell(&window->table, window->table.it_offset, 1, 51962306a36Sopenharmony_ci (unsigned long)iommu->pad_page, DMA_TO_DEVICE, 0); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return window; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic struct cbe_iommu *cell_iommu_for_node(int nid) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int i; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci for (i = 0; i < cbe_nr_iommus; i++) 52962306a36Sopenharmony_ci if (iommus[i].nid == nid) 53062306a36Sopenharmony_ci return &iommus[i]; 53162306a36Sopenharmony_ci return NULL; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic unsigned long cell_dma_nommu_offset; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic unsigned long dma_iommu_fixed_base; 53762306a36Sopenharmony_cistatic bool cell_iommu_enabled; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */ 54062306a36Sopenharmony_cibool iommu_fixed_is_weak; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct iommu_table *cell_get_iommu_table(struct device *dev) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct iommu_window *window; 54562306a36Sopenharmony_ci struct cbe_iommu *iommu; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Current implementation uses the first window available in that 54862306a36Sopenharmony_ci * node's iommu. We -might- do something smarter later though it may 54962306a36Sopenharmony_ci * never be necessary 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci iommu = cell_iommu_for_node(dev_to_node(dev)); 55262306a36Sopenharmony_ci if (iommu == NULL || list_empty(&iommu->windows)) { 55362306a36Sopenharmony_ci dev_err(dev, "iommu: missing iommu for %pOF (node %d)\n", 55462306a36Sopenharmony_ci dev->of_node, dev_to_node(dev)); 55562306a36Sopenharmony_ci return NULL; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci window = list_entry(iommu->windows.next, struct iommu_window, list); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return &window->table; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic u64 cell_iommu_get_fixed_address(struct device *dev); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic void cell_dma_dev_setup(struct device *dev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci if (cell_iommu_enabled) { 56762306a36Sopenharmony_ci u64 addr = cell_iommu_get_fixed_address(dev); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (addr != OF_BAD_ADDR) 57062306a36Sopenharmony_ci dev->archdata.dma_offset = addr + dma_iommu_fixed_base; 57162306a36Sopenharmony_ci set_iommu_table_base(dev, cell_get_iommu_table(dev)); 57262306a36Sopenharmony_ci } else { 57362306a36Sopenharmony_ci dev->archdata.dma_offset = cell_dma_nommu_offset; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void cell_pci_dma_dev_setup(struct pci_dev *dev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci cell_dma_dev_setup(&dev->dev); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int cell_of_bus_notify(struct notifier_block *nb, unsigned long action, 58362306a36Sopenharmony_ci void *data) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct device *dev = data; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* We are only interested in device addition */ 58862306a36Sopenharmony_ci if (action != BUS_NOTIFY_ADD_DEVICE) 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (cell_iommu_enabled) 59262306a36Sopenharmony_ci dev->dma_ops = &dma_iommu_ops; 59362306a36Sopenharmony_ci cell_dma_dev_setup(dev); 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic struct notifier_block cell_of_bus_notifier = { 59862306a36Sopenharmony_ci .notifier_call = cell_of_bus_notify 59962306a36Sopenharmony_ci}; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int __init cell_iommu_get_window(struct device_node *np, 60262306a36Sopenharmony_ci unsigned long *base, 60362306a36Sopenharmony_ci unsigned long *size) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci const __be32 *dma_window; 60662306a36Sopenharmony_ci unsigned long index; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Use ibm,dma-window if available, else, hard code ! */ 60962306a36Sopenharmony_ci dma_window = of_get_property(np, "ibm,dma-window", NULL); 61062306a36Sopenharmony_ci if (dma_window == NULL) { 61162306a36Sopenharmony_ci *base = 0; 61262306a36Sopenharmony_ci *size = 0x80000000u; 61362306a36Sopenharmony_ci return -ENODEV; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci of_parse_dma_window(np, dma_window, &index, base, size); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic struct cbe_iommu * __init cell_iommu_alloc(struct device_node *np) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct cbe_iommu *iommu; 62362306a36Sopenharmony_ci int nid, i; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* Get node ID */ 62662306a36Sopenharmony_ci nid = of_node_to_nid(np); 62762306a36Sopenharmony_ci if (nid < 0) { 62862306a36Sopenharmony_ci printk(KERN_ERR "iommu: failed to get node for %pOF\n", 62962306a36Sopenharmony_ci np); 63062306a36Sopenharmony_ci return NULL; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci pr_debug("iommu: setting up iommu for node %d (%pOF)\n", 63362306a36Sopenharmony_ci nid, np); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* XXX todo: If we can have multiple windows on the same IOMMU, which 63662306a36Sopenharmony_ci * isn't the case today, we probably want here to check whether the 63762306a36Sopenharmony_ci * iommu for that node is already setup. 63862306a36Sopenharmony_ci * However, there might be issue with getting the size right so let's 63962306a36Sopenharmony_ci * ignore that for now. We might want to completely get rid of the 64062306a36Sopenharmony_ci * multiple window support since the cell iommu supports per-page ioids 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (cbe_nr_iommus >= NR_IOMMUS) { 64462306a36Sopenharmony_ci printk(KERN_ERR "iommu: too many IOMMUs detected ! (%pOF)\n", 64562306a36Sopenharmony_ci np); 64662306a36Sopenharmony_ci return NULL; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Init base fields */ 65062306a36Sopenharmony_ci i = cbe_nr_iommus++; 65162306a36Sopenharmony_ci iommu = &iommus[i]; 65262306a36Sopenharmony_ci iommu->stab = NULL; 65362306a36Sopenharmony_ci iommu->nid = nid; 65462306a36Sopenharmony_ci snprintf(iommu->name, sizeof(iommu->name), "iommu%d", i); 65562306a36Sopenharmony_ci INIT_LIST_HEAD(&iommu->windows); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return iommu; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic void __init cell_iommu_init_one(struct device_node *np, 66162306a36Sopenharmony_ci unsigned long offset) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct cbe_iommu *iommu; 66462306a36Sopenharmony_ci unsigned long base, size; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci iommu = cell_iommu_alloc(np); 66762306a36Sopenharmony_ci if (!iommu) 66862306a36Sopenharmony_ci return; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Obtain a window for it */ 67162306a36Sopenharmony_ci cell_iommu_get_window(np, &base, &size); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci pr_debug("\ttranslating window 0x%lx...0x%lx\n", 67462306a36Sopenharmony_ci base, base + size - 1); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Initialize the hardware */ 67762306a36Sopenharmony_ci cell_iommu_setup_hardware(iommu, base, size); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Setup the iommu_table */ 68062306a36Sopenharmony_ci cell_iommu_setup_window(iommu, np, base, size, 68162306a36Sopenharmony_ci offset >> IOMMU_PAGE_SHIFT_4K); 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void __init cell_disable_iommus(void) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int node; 68762306a36Sopenharmony_ci unsigned long base, val; 68862306a36Sopenharmony_ci void __iomem *xregs, *cregs; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Make sure IOC translation is disabled on all nodes */ 69162306a36Sopenharmony_ci for_each_online_node(node) { 69262306a36Sopenharmony_ci if (cell_iommu_find_ioc(node, &base)) 69362306a36Sopenharmony_ci continue; 69462306a36Sopenharmony_ci xregs = ioremap(base, IOC_Reg_Size); 69562306a36Sopenharmony_ci if (xregs == NULL) 69662306a36Sopenharmony_ci continue; 69762306a36Sopenharmony_ci cregs = xregs + IOC_IOCmd_Offset; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci pr_debug("iommu: cleaning up iommu on node %d\n", node); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci out_be64(xregs + IOC_IOST_Origin, 0); 70262306a36Sopenharmony_ci (void)in_be64(xregs + IOC_IOST_Origin); 70362306a36Sopenharmony_ci val = in_be64(cregs + IOC_IOCmd_Cfg); 70462306a36Sopenharmony_ci val &= ~IOC_IOCmd_Cfg_TE; 70562306a36Sopenharmony_ci out_be64(cregs + IOC_IOCmd_Cfg, val); 70662306a36Sopenharmony_ci (void)in_be64(cregs + IOC_IOCmd_Cfg); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci iounmap(xregs); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int __init cell_iommu_init_disabled(void) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct device_node *np = NULL; 71562306a36Sopenharmony_ci unsigned long base = 0, size; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* When no iommu is present, we use direct DMA ops */ 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* First make sure all IOC translation is turned off */ 72062306a36Sopenharmony_ci cell_disable_iommus(); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* If we have no Axon, we set up the spider DMA magic offset */ 72362306a36Sopenharmony_ci np = of_find_node_by_name(NULL, "axon"); 72462306a36Sopenharmony_ci if (!np) 72562306a36Sopenharmony_ci cell_dma_nommu_offset = SPIDER_DMA_OFFSET; 72662306a36Sopenharmony_ci of_node_put(np); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Now we need to check to see where the memory is mapped 72962306a36Sopenharmony_ci * in PCI space. We assume that all busses use the same dma 73062306a36Sopenharmony_ci * window which is always the case so far on Cell, thus we 73162306a36Sopenharmony_ci * pick up the first pci-internal node we can find and check 73262306a36Sopenharmony_ci * the DMA window from there. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci for_each_node_by_name(np, "axon") { 73562306a36Sopenharmony_ci if (np->parent == NULL || np->parent->parent != NULL) 73662306a36Sopenharmony_ci continue; 73762306a36Sopenharmony_ci if (cell_iommu_get_window(np, &base, &size) == 0) 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci if (np == NULL) { 74162306a36Sopenharmony_ci for_each_node_by_name(np, "pci-internal") { 74262306a36Sopenharmony_ci if (np->parent == NULL || np->parent->parent != NULL) 74362306a36Sopenharmony_ci continue; 74462306a36Sopenharmony_ci if (cell_iommu_get_window(np, &base, &size) == 0) 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci of_node_put(np); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* If we found a DMA window, we check if it's big enough to enclose 75162306a36Sopenharmony_ci * all of physical memory. If not, we force enable IOMMU 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci if (np && size < memblock_end_of_DRAM()) { 75462306a36Sopenharmony_ci printk(KERN_WARNING "iommu: force-enabled, dma window" 75562306a36Sopenharmony_ci " (%ldMB) smaller than total memory (%lldMB)\n", 75662306a36Sopenharmony_ci size >> 20, memblock_end_of_DRAM() >> 20); 75762306a36Sopenharmony_ci return -ENODEV; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci cell_dma_nommu_offset += base; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (cell_dma_nommu_offset != 0) 76362306a36Sopenharmony_ci cell_pci_controller_ops.dma_dev_setup = cell_pci_dma_dev_setup; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci printk("iommu: disabled, direct DMA offset is 0x%lx\n", 76662306a36Sopenharmony_ci cell_dma_nommu_offset); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* 77262306a36Sopenharmony_ci * Fixed IOMMU mapping support 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * This code adds support for setting up a fixed IOMMU mapping on certain 77562306a36Sopenharmony_ci * cell machines. For 64-bit devices this avoids the performance overhead of 77662306a36Sopenharmony_ci * mapping and unmapping pages at runtime. 32-bit devices are unable to use 77762306a36Sopenharmony_ci * the fixed mapping. 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * The fixed mapping is established at boot, and maps all of physical memory 78062306a36Sopenharmony_ci * 1:1 into device space at some offset. On machines with < 30 GB of memory 78162306a36Sopenharmony_ci * we setup the fixed mapping immediately above the normal IOMMU window. 78262306a36Sopenharmony_ci * 78362306a36Sopenharmony_ci * For example a machine with 4GB of memory would end up with the normal 78462306a36Sopenharmony_ci * IOMMU window from 0-2GB and the fixed mapping window from 2GB to 6GB. In 78562306a36Sopenharmony_ci * this case a 64-bit device wishing to DMA to 1GB would be told to DMA to 78662306a36Sopenharmony_ci * 3GB, plus any offset required by firmware. The firmware offset is encoded 78762306a36Sopenharmony_ci * in the "dma-ranges" property. 78862306a36Sopenharmony_ci * 78962306a36Sopenharmony_ci * On machines with 30GB or more of memory, we are unable to place the fixed 79062306a36Sopenharmony_ci * mapping above the normal IOMMU window as we would run out of address space. 79162306a36Sopenharmony_ci * Instead we move the normal IOMMU window to coincide with the hash page 79262306a36Sopenharmony_ci * table, this region does not need to be part of the fixed mapping as no 79362306a36Sopenharmony_ci * device should ever be DMA'ing to it. We then setup the fixed mapping 79462306a36Sopenharmony_ci * from 0 to 32GB. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic u64 cell_iommu_get_fixed_address(struct device *dev) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci u64 cpu_addr, size, best_size, dev_addr = OF_BAD_ADDR; 80062306a36Sopenharmony_ci struct device_node *np; 80162306a36Sopenharmony_ci const u32 *ranges = NULL; 80262306a36Sopenharmony_ci int i, len, best, naddr, nsize, pna, range_size; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* We can be called for platform devices that have no of_node */ 80562306a36Sopenharmony_ci np = of_node_get(dev->of_node); 80662306a36Sopenharmony_ci if (!np) 80762306a36Sopenharmony_ci goto out; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci while (1) { 81062306a36Sopenharmony_ci naddr = of_n_addr_cells(np); 81162306a36Sopenharmony_ci nsize = of_n_size_cells(np); 81262306a36Sopenharmony_ci np = of_get_next_parent(np); 81362306a36Sopenharmony_ci if (!np) 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ranges = of_get_property(np, "dma-ranges", &len); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Ignore empty ranges, they imply no translation required */ 81962306a36Sopenharmony_ci if (ranges && len > 0) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!ranges) { 82462306a36Sopenharmony_ci dev_dbg(dev, "iommu: no dma-ranges found\n"); 82562306a36Sopenharmony_ci goto out; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci len /= sizeof(u32); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci pna = of_n_addr_cells(np); 83162306a36Sopenharmony_ci range_size = naddr + nsize + pna; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* dma-ranges format: 83462306a36Sopenharmony_ci * child addr : naddr cells 83562306a36Sopenharmony_ci * parent addr : pna cells 83662306a36Sopenharmony_ci * size : nsize cells 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci for (i = 0, best = -1, best_size = 0; i < len; i += range_size) { 83962306a36Sopenharmony_ci cpu_addr = of_translate_dma_address(np, ranges + i + naddr); 84062306a36Sopenharmony_ci size = of_read_number(ranges + i + naddr + pna, nsize); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (cpu_addr == 0 && size > best_size) { 84362306a36Sopenharmony_ci best = i; 84462306a36Sopenharmony_ci best_size = size; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (best >= 0) { 84962306a36Sopenharmony_ci dev_addr = of_read_number(ranges + best, naddr); 85062306a36Sopenharmony_ci } else 85162306a36Sopenharmony_ci dev_dbg(dev, "iommu: no suitable range found!\n"); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ciout: 85462306a36Sopenharmony_ci of_node_put(np); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return dev_addr; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic bool cell_pci_iommu_bypass_supported(struct pci_dev *pdev, u64 mask) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci return mask == DMA_BIT_MASK(64) && 86262306a36Sopenharmony_ci cell_iommu_get_fixed_address(&pdev->dev) != OF_BAD_ADDR; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void __init insert_16M_pte(unsigned long addr, unsigned long *ptab, 86662306a36Sopenharmony_ci unsigned long base_pte) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci unsigned long segment, offset; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci segment = addr >> IO_SEGMENT_SHIFT; 87162306a36Sopenharmony_ci offset = (addr >> 24) - (segment << IO_PAGENO_BITS(24)); 87262306a36Sopenharmony_ci ptab = ptab + (segment * (1 << 12) / sizeof(unsigned long)); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci pr_debug("iommu: addr %lx ptab %p segment %lx offset %lx\n", 87562306a36Sopenharmony_ci addr, ptab, segment, offset); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci ptab[offset] = base_pte | (__pa(addr) & CBE_IOPTE_RPN_Mask); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic void __init cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu, 88162306a36Sopenharmony_ci struct device_node *np, unsigned long dbase, unsigned long dsize, 88262306a36Sopenharmony_ci unsigned long fbase, unsigned long fsize) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci unsigned long base_pte, uaddr, ioaddr, *ptab; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci ptab = cell_iommu_alloc_ptab(iommu, fbase, fsize, dbase, dsize, 24); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci dma_iommu_fixed_base = fbase; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M | 89362306a36Sopenharmony_ci (cell_iommu_get_ioid(np) & CBE_IOPTE_IOID_Mask); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (iommu_fixed_is_weak) 89662306a36Sopenharmony_ci pr_info("IOMMU: Using weak ordering for fixed mapping\n"); 89762306a36Sopenharmony_ci else { 89862306a36Sopenharmony_ci pr_info("IOMMU: Using strong ordering for fixed mapping\n"); 89962306a36Sopenharmony_ci base_pte |= CBE_IOPTE_SO_RW; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { 90362306a36Sopenharmony_ci /* Don't touch the dynamic region */ 90462306a36Sopenharmony_ci ioaddr = uaddr + fbase; 90562306a36Sopenharmony_ci if (ioaddr >= dbase && ioaddr < (dbase + dsize)) { 90662306a36Sopenharmony_ci pr_debug("iommu: fixed/dynamic overlap, skipping\n"); 90762306a36Sopenharmony_ci continue; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci insert_16M_pte(uaddr, ptab, base_pte); 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mb(); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int __init cell_iommu_fixed_mapping_init(void) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci unsigned long dbase, dsize, fbase, fsize, hbase, hend; 91962306a36Sopenharmony_ci struct cbe_iommu *iommu; 92062306a36Sopenharmony_ci struct device_node *np; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* The fixed mapping is only supported on axon machines */ 92362306a36Sopenharmony_ci np = of_find_node_by_name(NULL, "axon"); 92462306a36Sopenharmony_ci of_node_put(np); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!np) { 92762306a36Sopenharmony_ci pr_debug("iommu: fixed mapping disabled, no axons found\n"); 92862306a36Sopenharmony_ci return -1; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* We must have dma-ranges properties for fixed mapping to work */ 93262306a36Sopenharmony_ci np = of_find_node_with_property(NULL, "dma-ranges"); 93362306a36Sopenharmony_ci of_node_put(np); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!np) { 93662306a36Sopenharmony_ci pr_debug("iommu: no dma-ranges found, no fixed mapping\n"); 93762306a36Sopenharmony_ci return -1; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* The default setup is to have the fixed mapping sit after the 94162306a36Sopenharmony_ci * dynamic region, so find the top of the largest IOMMU window 94262306a36Sopenharmony_ci * on any axon, then add the size of RAM and that's our max value. 94362306a36Sopenharmony_ci * If that is > 32GB we have to do other shennanigans. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci fbase = 0; 94662306a36Sopenharmony_ci for_each_node_by_name(np, "axon") { 94762306a36Sopenharmony_ci cell_iommu_get_window(np, &dbase, &dsize); 94862306a36Sopenharmony_ci fbase = max(fbase, dbase + dsize); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci fbase = ALIGN(fbase, 1 << IO_SEGMENT_SHIFT); 95262306a36Sopenharmony_ci fsize = memblock_phys_mem_size(); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if ((fbase + fsize) <= 0x800000000ul) 95562306a36Sopenharmony_ci hbase = 0; /* use the device tree window */ 95662306a36Sopenharmony_ci else { 95762306a36Sopenharmony_ci /* If we're over 32 GB we need to cheat. We can't map all of 95862306a36Sopenharmony_ci * RAM with the fixed mapping, and also fit the dynamic 95962306a36Sopenharmony_ci * region. So try to place the dynamic region where the hash 96062306a36Sopenharmony_ci * table sits, drivers never need to DMA to it, we don't 96162306a36Sopenharmony_ci * need a fixed mapping for that area. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci if (!htab_address) { 96462306a36Sopenharmony_ci pr_debug("iommu: htab is NULL, on LPAR? Huh?\n"); 96562306a36Sopenharmony_ci return -1; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci hbase = __pa(htab_address); 96862306a36Sopenharmony_ci hend = hbase + htab_size_bytes; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* The window must start and end on a segment boundary */ 97162306a36Sopenharmony_ci if ((hbase != ALIGN(hbase, 1 << IO_SEGMENT_SHIFT)) || 97262306a36Sopenharmony_ci (hend != ALIGN(hend, 1 << IO_SEGMENT_SHIFT))) { 97362306a36Sopenharmony_ci pr_debug("iommu: hash window not segment aligned\n"); 97462306a36Sopenharmony_ci return -1; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Check the hash window fits inside the real DMA window */ 97862306a36Sopenharmony_ci for_each_node_by_name(np, "axon") { 97962306a36Sopenharmony_ci cell_iommu_get_window(np, &dbase, &dsize); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (hbase < dbase || (hend > (dbase + dsize))) { 98262306a36Sopenharmony_ci pr_debug("iommu: hash window doesn't fit in" 98362306a36Sopenharmony_ci "real DMA window\n"); 98462306a36Sopenharmony_ci of_node_put(np); 98562306a36Sopenharmony_ci return -1; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci fbase = 0; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Setup the dynamic regions */ 99362306a36Sopenharmony_ci for_each_node_by_name(np, "axon") { 99462306a36Sopenharmony_ci iommu = cell_iommu_alloc(np); 99562306a36Sopenharmony_ci BUG_ON(!iommu); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (hbase == 0) 99862306a36Sopenharmony_ci cell_iommu_get_window(np, &dbase, &dsize); 99962306a36Sopenharmony_ci else { 100062306a36Sopenharmony_ci dbase = hbase; 100162306a36Sopenharmony_ci dsize = htab_size_bytes; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci printk(KERN_DEBUG "iommu: node %d, dynamic window 0x%lx-0x%lx " 100562306a36Sopenharmony_ci "fixed window 0x%lx-0x%lx\n", iommu->nid, dbase, 100662306a36Sopenharmony_ci dbase + dsize, fbase, fbase + fsize); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci cell_iommu_setup_stab(iommu, dbase, dsize, fbase, fsize); 100962306a36Sopenharmony_ci iommu->ptab = cell_iommu_alloc_ptab(iommu, dbase, dsize, 0, 0, 101062306a36Sopenharmony_ci IOMMU_PAGE_SHIFT_4K); 101162306a36Sopenharmony_ci cell_iommu_setup_fixed_ptab(iommu, np, dbase, dsize, 101262306a36Sopenharmony_ci fbase, fsize); 101362306a36Sopenharmony_ci cell_iommu_enable_hardware(iommu); 101462306a36Sopenharmony_ci cell_iommu_setup_window(iommu, np, dbase, dsize, 0); 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci cell_pci_controller_ops.iommu_bypass_supported = 101862306a36Sopenharmony_ci cell_pci_iommu_bypass_supported; 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int iommu_fixed_disabled; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int __init setup_iommu_fixed(char *str) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct device_node *pciep; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (strcmp(str, "off") == 0) 102962306a36Sopenharmony_ci iommu_fixed_disabled = 1; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci /* If we can find a pcie-endpoint in the device tree assume that 103262306a36Sopenharmony_ci * we're on a triblade or a CAB so by default the fixed mapping 103362306a36Sopenharmony_ci * should be set to be weakly ordered; but only if the boot 103462306a36Sopenharmony_ci * option WASN'T set for strong ordering 103562306a36Sopenharmony_ci */ 103662306a36Sopenharmony_ci pciep = of_find_node_by_type(NULL, "pcie-endpoint"); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0)) 103962306a36Sopenharmony_ci iommu_fixed_is_weak = true; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci of_node_put(pciep); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci return 1; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci__setup("iommu_fixed=", setup_iommu_fixed); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic int __init cell_iommu_init(void) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct device_node *np; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* If IOMMU is disabled or we have little enough RAM to not need 105262306a36Sopenharmony_ci * to enable it, we setup a direct mapping. 105362306a36Sopenharmony_ci * 105462306a36Sopenharmony_ci * Note: should we make sure we have the IOMMU actually disabled ? 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci if (iommu_is_off || 105762306a36Sopenharmony_ci (!iommu_force_on && memblock_end_of_DRAM() <= 0x80000000ull)) 105862306a36Sopenharmony_ci if (cell_iommu_init_disabled() == 0) 105962306a36Sopenharmony_ci goto bail; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* Setup various callbacks */ 106262306a36Sopenharmony_ci cell_pci_controller_ops.dma_dev_setup = cell_pci_dma_dev_setup; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (!iommu_fixed_disabled && cell_iommu_fixed_mapping_init() == 0) 106562306a36Sopenharmony_ci goto done; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Create an iommu for each /axon node. */ 106862306a36Sopenharmony_ci for_each_node_by_name(np, "axon") { 106962306a36Sopenharmony_ci if (np->parent == NULL || np->parent->parent != NULL) 107062306a36Sopenharmony_ci continue; 107162306a36Sopenharmony_ci cell_iommu_init_one(np, 0); 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* Create an iommu for each toplevel /pci-internal node for 107562306a36Sopenharmony_ci * old hardware/firmware 107662306a36Sopenharmony_ci */ 107762306a36Sopenharmony_ci for_each_node_by_name(np, "pci-internal") { 107862306a36Sopenharmony_ci if (np->parent == NULL || np->parent->parent != NULL) 107962306a36Sopenharmony_ci continue; 108062306a36Sopenharmony_ci cell_iommu_init_one(np, SPIDER_DMA_OFFSET); 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci done: 108362306a36Sopenharmony_ci /* Setup default PCI iommu ops */ 108462306a36Sopenharmony_ci set_pci_dma_ops(&dma_iommu_ops); 108562306a36Sopenharmony_ci cell_iommu_enabled = true; 108662306a36Sopenharmony_ci bail: 108762306a36Sopenharmony_ci /* Register callbacks on OF platform device addition/removal 108862306a36Sopenharmony_ci * to handle linking them to the right DMA operations 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ci bus_register_notifier(&platform_bus_type, &cell_of_bus_notifier); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_cimachine_arch_initcall(cell, cell_iommu_init); 1095