/*
* IOMMU implementation for Cell Broadband Processor Architecture
*
* (C) Copyright IBM Corporation 2006-2008
*
* Author: Jeremy Kerr <jk@ozlabs.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/lmb.h>
#include <asm/prom.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
#include <asm/pci-bridge.h>
#include <asm/udbg.h>
#include <asm/firmware.h>
#include <asm/cell-regs.h>
#include "interrupt.h"
/* Define CELL_IOMMU_REAL_UNMAP to actually unmap non-used pages
* instead of leaving them mapped to some dummy page. This can be
* enabled once the appropriate workarounds for spider bugs have
* been enabled
*/
#define CELL_IOMMU_REAL_UNMAP
/* Define CELL_IOMMU_STRICT_PROTECTION to enforce protection of
* IO PTEs based on the transfer direction. That can be enabled
* once spider-net has been fixed to pass the correct direction
* to the DMA mapping functions
*/
#define CELL_IOMMU_STRICT_PROTECTION
#define NR_IOMMUS 2
/* IOC mmap registers */
#define IOC_Reg_Size 0x2000
#define IOC_IOPT_CacheInvd 0x908
#define IOC_IOPT_CacheInvd_NE_Mask 0xffe0000000000000ul
#define IOC_IOPT_CacheInvd_IOPTE_Mask 0x000003fffffffff8ul
#define IOC_IOPT_CacheInvd_Busy 0x0000000000000001ul
#define IOC_IOST_Origin 0x918
#define IOC_IOST_Origin_E 0x8000000000000000ul
#define IOC_IOST_Origin_HW 0x0000000000000800ul
#define IOC_IOST_Origin_HL 0x0000000000000400ul
#define IOC_IO_ExcpStat 0x920
#define IOC_IO_ExcpStat_V 0x8000000000000000ul
#define IOC_IO_ExcpStat_SPF_Mask 0x6000000000000000ul
#define IOC_IO_ExcpStat_SPF_S 0x6000000000000000ul
#define IOC_IO_ExcpStat_SPF_P 0x2000000000000000ul
#define IOC_IO_ExcpStat_ADDR_Mask 0x00000007fffff000ul
#define IOC_IO_ExcpStat_RW_Mask 0x0000000000000800ul
#define IOC_IO_ExcpStat_IOID_Mask 0x00000000000007fful
#define IOC_IO_ExcpMask 0x928
#define IOC_IO_ExcpMask_SFE 0x4000000000000000ul
#define IOC_IO_ExcpMask_PFE 0x2000000000000000ul
#define IOC_IOCmd_Offset 0x1000
#define IOC_IOCmd_Cfg 0xc00
#define IOC_IOCmd_Cfg_TE 0x0000800000000000ul
/* Segment table entries */
#define IOSTE_V 0x8000000000000000ul /* valid */
#define IOSTE_H 0x4000000000000000ul /* cache hint */
#define IOSTE_PT_Base_RPN_Mask 0x3ffffffffffff000ul /* base RPN of IOPT */
#define IOSTE_NPPT_Mask 0x0000000000000fe0ul /* no. pages in IOPT */
#define IOSTE_PS_Mask 0x0000000000000007ul /* page size */
#define IOSTE_PS_4K 0x0000000000000001ul /* - 4kB */
#define IOSTE_PS_64K 0x0000000000000003ul /* - 64kB */
#define IOSTE_PS_1M 0x0000000000000005ul /* - 1MB */
#define IOSTE_PS_16M 0x0000000000000007ul /* - 16MB */
/* IOMMU sizing */
#define IO_SEGMENT_SHIFT 28
#define IO_PAGENO_BITS(shift) (IO_SEGMENT_SHIFT - (shift))
/* The high bit needs to be set on every DMA address */
#define SPIDER_DMA_OFFSET 0x80000000ul
struct iommu_window {
struct list_head list;
struct cbe_iommu *iommu;
unsigned long offset;
unsigned long size;
unsigned int ioid;
struct iommu_table table;
};
#define NAMESIZE 8
struct cbe_iommu {
int nid;
char name[NAMESIZE];
void __iomem *xlate_regs;
void __iomem *cmd_regs;
unsigned long *stab;
unsigned long *ptab;
void *pad_page;
struct list_head windows;
};
/* Static array of iommus, one per node
* each contains a list of windows, keyed from dma_window property
* - on bus setup, look for a matching window, or create one
* - on dev setup, assign iommu_table ptr
*/
static struct cbe_iommu iommus[NR_IOMMUS];
static int cbe_nr_iommus;
static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte,
long n_ptes)
{
u64 __iomem *reg;
u64 val;
long n;
reg = iommu->xlate_regs + IOC_IOPT_CacheInvd;
while (n_ptes > 0) {
/* we can invalidate up to 1 << 11 PTEs at once */
n = min(n_ptes, 1l << 11);
val = (((n /*- 1*/) << 53) & IOC_IOPT_CacheInvd_NE_Mask)
| (__pa(pte) & IOC_IOPT_CacheInvd_IOPTE_Mask)
| IOC_IOPT_CacheInvd_Busy;
out_be64(reg, val);
while (in_be64(reg) & IOC_IOPT_CacheInvd_Busy)
;
n_ptes -= n;
pte += n;