2005-06-23 09:43:54 +10:00
/*
2005-10-31 20:08:37 -05:00
* IOMMU implementation for Cell Broadband Processor Architecture
2005-06-23 09:43:54 +10:00
*
2008-01-30 11:03:44 +11:00
* ( C ) Copyright IBM Corporation 2006 - 2008
2005-06-23 09:43:54 +10:00
*
2006-11-11 17:25:18 +11:00
* Author : Jeremy Kerr < jk @ ozlabs . org >
2005-06-23 09:43:54 +10:00
*
2006-11-11 17:25:18 +11:00
* 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 0213 9 , USA .
2005-06-23 09:43:54 +10:00
*/
# undef DEBUG
# include <linux/kernel.h>
# include <linux/init.h>
2006-11-11 17:25:18 +11:00
# include <linux/interrupt.h>
# include <linux/notifier.h>
2008-02-08 16:37:02 +11:00
# include <linux/of.h>
2007-11-13 11:10:58 -06:00
# include <linux/of_platform.h>
2008-02-13 16:56:49 -08:00
# include <linux/lmb.h>
2005-06-23 09:43:54 +10:00
# include <asm/prom.h>
2006-11-11 17:25:18 +11:00
# include <asm/iommu.h>
2005-06-23 09:43:54 +10:00
# include <asm/machdep.h>
2006-11-11 17:25:18 +11:00
# include <asm/pci-bridge.h>
2005-12-09 19:04:20 +01:00
# include <asm/udbg.h>
2007-12-04 19:38:24 +11:00
# include <asm/firmware.h>
2007-10-04 15:40:42 +10:00
# include <asm/cell-regs.h>
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
# include "interrupt.h"
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
/* 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 0x4000000000000000ul
# 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 */
/* Page table entries */
# define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
# define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
# define IOPTE_M 0x2000000000000000ul /* coherency required */
# define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
# define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
# define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
# define IOPTE_H 0x0000000000000800ul /* cache hint */
# define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
/* IOMMU sizing */
# define IO_SEGMENT_SHIFT 28
2008-02-29 18:33:27 +11:00
# define IO_PAGENO_BITS(shift) (IO_SEGMENT_SHIFT - (shift))
2006-11-11 17:25:18 +11:00
/* 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 ;
} ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
# 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 ;
} ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
/* 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 ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
static void invalidate_tce_cache ( struct cbe_iommu * iommu , unsigned long * pte ,
long n_ptes )
2005-06-23 09:43:54 +10:00
{
2007-02-09 16:38:15 +00:00
unsigned long __iomem * reg ;
unsigned long val ;
2006-11-11 17:25:18 +11:00
long n ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
reg = iommu - > xlate_regs + IOC_IOPT_CacheInvd ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
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 ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
out_be64 ( reg , val ) ;
while ( in_be64 ( reg ) & IOC_IOPT_CacheInvd_Busy )
;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
n_ptes - = n ;
pte + = n ;
}
2005-06-23 09:43:54 +10:00
}
2006-11-11 17:25:18 +11:00
static void tce_build_cell ( struct iommu_table * tbl , long index , long npages ,
2008-07-16 05:51:47 +10:00
unsigned long uaddr , enum dma_data_direction direction ,
struct dma_attrs * attrs )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
int i ;
unsigned long * io_pte , base_pte ;
struct iommu_window * window =
container_of ( tbl , struct iommu_window , table ) ;
/* implementing proper protection causes problems with the spidernet
* driver - check mapping directions later , but allow read & write by
* default for now . */
# ifdef CELL_IOMMU_STRICT_PROTECTION
/* to avoid referencing a global, we use a trick here to setup the
* protection bit . " prot " is setup to be 3 fields of 4 bits apprended
* together for each of the 3 supported direction values . It is then
* shifted left so that the fields matching the desired direction
* lands on the appropriate bits , and other bits are masked out .
*/
const unsigned long prot = 0xc48 ;
base_pte =
( ( prot < < ( 52 + 4 * direction ) ) & ( IOPTE_PP_W | IOPTE_PP_R ) )
| IOPTE_M | IOPTE_SO_RW | ( window - > ioid & IOPTE_IOID_Mask ) ;
# else
base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW |
( window - > ioid & IOPTE_IOID_Mask ) ;
# endif
2008-07-18 23:03:34 +10:00
if ( unlikely ( dma_get_attr ( DMA_ATTR_WEAK_ORDERING , attrs ) ) )
base_pte & = ~ IOPTE_SO_RW ;
2006-11-11 17:25:18 +11:00
2008-02-29 18:33:23 +11:00
io_pte = ( unsigned long * ) tbl - > it_base + ( index - tbl - > it_offset ) ;
2006-11-11 17:25:18 +11:00
for ( i = 0 ; i < npages ; i + + , uaddr + = IOMMU_PAGE_SIZE )
io_pte [ i ] = base_pte | ( __pa ( uaddr ) & IOPTE_RPN_Mask ) ;
mb ( ) ;
invalidate_tce_cache ( window - > iommu , io_pte , npages ) ;
pr_debug ( " tce_build_cell(index=%lx,n=%lx,dir=%d,base_pte=%lx) \n " ,
index , npages , direction , base_pte ) ;
2005-06-23 09:43:54 +10:00
}
2006-11-11 17:25:18 +11:00
static void tce_free_cell ( struct iommu_table * tbl , long index , long npages )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
int i ;
unsigned long * io_pte , pte ;
struct iommu_window * window =
container_of ( tbl , struct iommu_window , table ) ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
pr_debug ( " tce_free_cell(index=%lx,n=%lx) \n " , index , npages ) ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
# ifdef CELL_IOMMU_REAL_UNMAP
pte = 0 ;
# else
/* spider bridge does PCI reads after freeing - insert a mapping
* to a scratch page instead of an invalid entry */
pte = IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | __pa ( window - > iommu - > pad_page )
| ( window - > ioid & IOPTE_IOID_Mask ) ;
# endif
2005-06-23 09:43:54 +10:00
2008-02-29 18:33:23 +11:00
io_pte = ( unsigned long * ) tbl - > it_base + ( index - tbl - > it_offset ) ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
for ( i = 0 ; i < npages ; i + + )
io_pte [ i ] = pte ;
mb ( ) ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
invalidate_tce_cache ( window - > iommu , io_pte , npages ) ;
2005-06-23 09:43:54 +10:00
}
2006-11-11 17:25:18 +11:00
static irqreturn_t ioc_interrupt ( int irq , void * data )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
unsigned long stat ;
struct cbe_iommu * iommu = data ;
stat = in_be64 ( iommu - > xlate_regs + IOC_IO_ExcpStat ) ;
/* Might want to rate limit it */
printk ( KERN_ERR " iommu: DMA exception 0x%016lx \n " , stat ) ;
printk ( KERN_ERR " V=%d, SPF=[%c%c], RW=%s, IOID=0x%04x \n " ,
! ! ( stat & IOC_IO_ExcpStat_V ) ,
( stat & IOC_IO_ExcpStat_SPF_S ) ? ' S ' : ' ' ,
( stat & IOC_IO_ExcpStat_SPF_P ) ? ' P ' : ' ' ,
( stat & IOC_IO_ExcpStat_RW_Mask ) ? " Read " : " Write " ,
( unsigned int ) ( stat & IOC_IO_ExcpStat_IOID_Mask ) ) ;
printk ( KERN_ERR " page=0x%016lx \n " ,
stat & IOC_IO_ExcpStat_ADDR_Mask ) ;
/* clear interrupt */
stat & = ~ IOC_IO_ExcpStat_V ;
out_be64 ( iommu - > xlate_regs + IOC_IO_ExcpStat , stat ) ;
return IRQ_HANDLED ;
2005-06-23 09:43:54 +10:00
}
2006-11-11 17:25:18 +11:00
static int cell_iommu_find_ioc ( int nid , unsigned long * base )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
struct device_node * np ;
struct resource r ;
* base = 0 ;
/* First look for new style /be nodes */
for_each_node_by_name ( np , " ioc " ) {
if ( of_node_to_nid ( np ) ! = nid )
continue ;
if ( of_address_to_resource ( np , 0 , & r ) ) {
printk ( KERN_ERR " iommu: can't get address for %s \n " ,
np - > full_name ) ;
continue ;
}
* base = r . start ;
of_node_put ( np ) ;
return 0 ;
}
/* Ok, let's try the old way */
for_each_node_by_type ( np , " cpu " ) {
const unsigned int * nidp ;
const unsigned long * tmp ;
2007-04-03 22:26:41 +10:00
nidp = of_get_property ( np , " node-id " , NULL ) ;
2006-11-11 17:25:18 +11:00
if ( nidp & & * nidp = = nid ) {
2007-04-03 22:26:41 +10:00
tmp = of_get_property ( np , " ioc-translation " , NULL ) ;
2006-11-11 17:25:18 +11:00
if ( tmp ) {
* base = * tmp ;
of_node_put ( np ) ;
return 0 ;
}
}
}
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
return - ENODEV ;
2005-06-23 09:43:54 +10:00
}
2008-02-29 18:33:25 +11:00
static void cell_iommu_setup_stab ( struct cbe_iommu * iommu ,
2008-01-30 01:14:01 +11:00
unsigned long dbase , unsigned long dsize ,
unsigned long fbase , unsigned long fsize )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
struct page * page ;
2008-02-29 18:33:25 +11:00
unsigned long segments , stab_size ;
2008-01-30 01:14:01 +11:00
segments = max ( dbase + dsize , fbase + fsize ) > > IO_SEGMENT_SHIFT ;
2006-11-11 17:25:18 +11:00
2008-02-29 18:33:25 +11:00
pr_debug ( " %s: iommu[%d]: segments: %lu \n " ,
2008-03-29 08:21:07 +11:00
__func__ , iommu - > nid , segments ) ;
2006-11-11 17:25:18 +11:00
/* set up the segment table */
2008-01-21 18:01:43 +11:00
stab_size = segments * sizeof ( unsigned long ) ;
page = alloc_pages_node ( iommu - > nid , GFP_KERNEL , get_order ( stab_size ) ) ;
2006-11-11 17:25:18 +11:00
BUG_ON ( ! page ) ;
iommu - > stab = page_address ( page ) ;
2008-02-29 18:33:25 +11:00
memset ( iommu - > stab , 0 , stab_size ) ;
}
static unsigned long * cell_iommu_alloc_ptab ( struct cbe_iommu * iommu ,
unsigned long base , unsigned long size , unsigned long gap_base ,
2008-02-29 18:33:27 +11:00
unsigned long gap_size , unsigned long page_shift )
2008-02-29 18:33:25 +11:00
{
struct page * page ;
int i ;
unsigned long reg , segments , pages_per_segment , ptab_size ,
n_pte_pages , start_seg , * ptab ;
start_seg = base > > IO_SEGMENT_SHIFT ;
segments = size > > IO_SEGMENT_SHIFT ;
2008-02-29 18:33:27 +11:00
pages_per_segment = 1ull < < IO_PAGENO_BITS ( page_shift ) ;
/* PTEs for each segment must start on a 4K bounday */
pages_per_segment = max ( pages_per_segment ,
( 1 < < 12 ) / sizeof ( unsigned long ) ) ;
2006-11-11 17:25:18 +11:00
ptab_size = segments * pages_per_segment * sizeof ( unsigned long ) ;
2008-03-29 08:21:07 +11:00
pr_debug ( " %s: iommu[%d]: ptab_size: %lu, order: %d \n " , __func__ ,
2006-11-11 17:25:18 +11:00
iommu - > nid , ptab_size , get_order ( ptab_size ) ) ;
page = alloc_pages_node ( iommu - > nid , GFP_KERNEL , get_order ( ptab_size ) ) ;
BUG_ON ( ! page ) ;
2008-02-29 18:33:25 +11:00
ptab = page_address ( page ) ;
memset ( ptab , 0 , ptab_size ) ;
2006-11-11 17:25:18 +11:00
2008-02-29 18:33:26 +11:00
/* number of 4K pages needed for a page table */
n_pte_pages = ( pages_per_segment * sizeof ( unsigned long ) ) > > 12 ;
2006-11-11 17:25:18 +11:00
pr_debug ( " %s: iommu[%d]: stab at %p, ptab at %p, n_pte_pages: %lu \n " ,
2008-03-29 08:21:07 +11:00
__func__ , iommu - > nid , iommu - > stab , ptab ,
2006-11-11 17:25:18 +11:00
n_pte_pages ) ;
/* initialise the STEs */
reg = IOSTE_V | ( ( n_pte_pages - 1 ) < < 5 ) ;
2008-02-29 18:33:27 +11:00
switch ( page_shift ) {
case 12 : reg | = IOSTE_PS_4K ; break ;
case 16 : reg | = IOSTE_PS_64K ; break ;
case 20 : reg | = IOSTE_PS_1M ; break ;
case 24 : reg | = IOSTE_PS_16M ; break ;
default : BUG ( ) ;
2006-11-11 17:25:18 +11:00
}
2005-06-23 09:43:54 +10:00
2008-02-29 18:33:25 +11:00
gap_base = gap_base > > IO_SEGMENT_SHIFT ;
gap_size = gap_size > > IO_SEGMENT_SHIFT ;
2006-11-11 17:25:18 +11:00
pr_debug ( " Setting up IOMMU stab: \n " ) ;
2008-02-29 18:33:25 +11:00
for ( i = start_seg ; i < ( start_seg + segments ) ; i + + ) {
if ( i > = gap_base & & i < ( gap_base + gap_size ) ) {
pr_debug ( " \t overlap at %d, skipping \n " , i ) ;
continue ;
}
2008-02-29 18:33:26 +11:00
iommu - > stab [ i ] = reg | ( __pa ( ptab ) + ( n_pte_pages < < 12 ) *
( i - start_seg ) ) ;
2006-11-11 17:25:18 +11:00
pr_debug ( " \t [%d] 0x%016lx \n " , i , iommu - > stab [ i ] ) ;
}
2008-02-29 18:33:25 +11:00
return ptab ;
2008-01-30 01:14:00 +11:00
}
static void cell_iommu_enable_hardware ( struct cbe_iommu * iommu )
{
int ret ;
unsigned long reg , xlate_base ;
unsigned int virq ;
if ( cell_iommu_find_ioc ( iommu - > nid , & xlate_base ) )
panic ( " %s: missing IOC register mappings for node %d \n " ,
2008-03-29 08:21:07 +11:00
__func__ , iommu - > nid ) ;
2008-01-30 01:14:00 +11:00
iommu - > xlate_regs = ioremap ( xlate_base , IOC_Reg_Size ) ;
iommu - > cmd_regs = iommu - > xlate_regs + IOC_IOCmd_Offset ;
2006-11-11 17:25:18 +11:00
/* ensure that the STEs have updated */
mb ( ) ;
/* setup interrupts for the iommu. */
reg = in_be64 ( iommu - > xlate_regs + IOC_IO_ExcpStat ) ;
out_be64 ( iommu - > xlate_regs + IOC_IO_ExcpStat ,
reg & ~ IOC_IO_ExcpStat_V ) ;
out_be64 ( iommu - > xlate_regs + IOC_IO_ExcpMask ,
IOC_IO_ExcpMask_PFE | IOC_IO_ExcpMask_SFE ) ;
virq = irq_create_mapping ( NULL ,
IIC_IRQ_IOEX_ATI | ( iommu - > nid < < IIC_IRQ_NODE_SHIFT ) ) ;
BUG_ON ( virq = = NO_IRQ ) ;
ret = request_irq ( virq , ioc_interrupt , IRQF_DISABLED ,
iommu - > name , iommu ) ;
BUG_ON ( ret ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* set the IOC segment table origin register (and turn on the iommu) */
reg = IOC_IOST_Origin_E | __pa ( iommu - > stab ) | IOC_IOST_Origin_HW ;
out_be64 ( iommu - > xlate_regs + IOC_IOST_Origin , reg ) ;
in_be64 ( iommu - > xlate_regs + IOC_IOST_Origin ) ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
/* turn on IO translation */
reg = in_be64 ( iommu - > cmd_regs + IOC_IOCmd_Cfg ) | IOC_IOCmd_Cfg_TE ;
out_be64 ( iommu - > cmd_regs + IOC_IOCmd_Cfg , reg ) ;
}
2008-01-30 01:14:00 +11:00
static void cell_iommu_setup_hardware ( struct cbe_iommu * iommu ,
unsigned long base , unsigned long size )
{
2008-02-29 18:33:25 +11:00
cell_iommu_setup_stab ( iommu , base , size , 0 , 0 ) ;
2008-02-29 18:33:27 +11:00
iommu - > ptab = cell_iommu_alloc_ptab ( iommu , base , size , 0 , 0 ,
IOMMU_PAGE_SHIFT ) ;
2008-01-30 01:14:00 +11:00
cell_iommu_enable_hardware ( iommu ) ;
}
2006-11-11 17:25:18 +11:00
#if 0 /* Unused for now */
static struct iommu_window * find_window ( struct cbe_iommu * iommu ,
unsigned long offset , unsigned long size )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
struct iommu_window * window ;
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
/* todo: check for overlapping (but not equal) windows) */
list_for_each_entry ( window , & ( iommu - > windows ) , list ) {
if ( window - > offset = = offset & & window - > size = = size )
return window ;
2005-12-09 19:04:20 +01:00
}
2006-11-11 17:25:18 +11:00
return NULL ;
2005-12-09 19:04:20 +01:00
}
2006-11-11 17:25:18 +11:00
# endif
2005-06-23 09:43:54 +10:00
2008-01-30 01:14:02 +11:00
static inline u32 cell_iommu_get_ioid ( struct device_node * np )
{
const u32 * ioid ;
ioid = of_get_property ( np , " ioid " , NULL ) ;
if ( ioid = = NULL ) {
printk ( KERN_WARNING " iommu: missing ioid for %s using 0 \n " ,
np - > full_name ) ;
return 0 ;
}
return * ioid ;
}
2006-11-11 17:25:18 +11:00
static struct iommu_window * __init
cell_iommu_setup_window ( struct cbe_iommu * iommu , struct device_node * np ,
unsigned long offset , unsigned long size ,
unsigned long pte_offset )
2005-12-09 19:04:20 +01:00
{
2006-11-11 17:25:18 +11:00
struct iommu_window * window ;
2008-02-29 18:33:24 +11:00
struct page * page ;
2008-01-30 01:14:02 +11:00
u32 ioid ;
2005-06-23 09:43:54 +10:00
2008-01-30 01:14:02 +11:00
ioid = cell_iommu_get_ioid ( np ) ;
2006-11-11 17:25:18 +11:00
window = kmalloc_node ( sizeof ( * window ) , GFP_KERNEL , iommu - > nid ) ;
BUG_ON ( window = = NULL ) ;
window - > offset = offset ;
window - > size = size ;
2008-01-30 01:14:02 +11:00
window - > ioid = ioid ;
2006-11-11 17:25:18 +11:00
window - > iommu = iommu ;
window - > table . it_blocksize = 16 ;
window - > table . it_base = ( unsigned long ) iommu - > ptab ;
window - > table . it_index = iommu - > nid ;
2008-02-29 18:33:23 +11:00
window - > table . it_offset = ( offset > > IOMMU_PAGE_SHIFT ) + pte_offset ;
2006-11-11 17:25:18 +11:00
window - > table . it_size = size > > IOMMU_PAGE_SHIFT ;
iommu_init_table ( & window - > table , iommu - > nid ) ;
pr_debug ( " \t ioid %d \n " , window - > ioid ) ;
pr_debug ( " \t blocksize %ld \n " , window - > table . it_blocksize ) ;
pr_debug ( " \t base 0x%016lx \n " , window - > table . it_base ) ;
pr_debug ( " \t offset 0x%lx \n " , window - > table . it_offset ) ;
pr_debug ( " \t size %ld \n " , window - > table . it_size ) ;
list_add ( & window - > list , & iommu - > windows ) ;
if ( offset ! = 0 )
return window ;
/* We need to map and reserve the first IOMMU page since it's used
* by the spider workaround . In theory , we only need to do that when
* running on spider but it doesn ' t really matter .
*
* This code also assumes that we have a window that starts at 0 ,
* which is the case on all spider based blades .
*/
2008-02-29 18:33:24 +11:00
page = alloc_pages_node ( iommu - > nid , GFP_KERNEL , 0 ) ;
BUG_ON ( ! page ) ;
iommu - > pad_page = page_address ( page ) ;
clear_page ( iommu - > pad_page ) ;
2006-11-11 17:25:18 +11:00
__set_bit ( 0 , window - > table . it_map ) ;
tce_build_cell ( & window - > table , window - > table . it_offset , 1 ,
2008-07-16 05:51:47 +10:00
( unsigned long ) iommu - > pad_page , DMA_TO_DEVICE , NULL ) ;
2006-11-11 17:25:18 +11:00
window - > table . it_hint = window - > table . it_blocksize ;
return window ;
}
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
static struct cbe_iommu * cell_iommu_for_node ( int nid )
{
int i ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
for ( i = 0 ; i < cbe_nr_iommus ; i + + )
if ( iommus [ i ] . nid = = nid )
return & iommus [ i ] ;
return NULL ;
}
2005-12-09 19:04:20 +01:00
2008-01-21 16:42:45 +11:00
static unsigned long cell_dma_direct_offset ;
2008-01-30 11:03:44 +11:00
static unsigned long dma_iommu_fixed_base ;
2008-07-18 23:03:34 +10:00
/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */
static int iommu_fixed_is_weak ;
2008-01-30 11:03:44 +11:00
2008-07-05 05:05:44 +10:00
static struct iommu_table * cell_get_iommu_table ( struct device * dev )
2006-11-11 17:25:18 +11:00
{
struct iommu_window * window ;
struct cbe_iommu * iommu ;
struct dev_archdata * archdata = & dev - > archdata ;
/* Current implementation uses the first window available in that
* node ' s iommu . We - might - do something smarter later though it may
* never be necessary
*/
iommu = cell_iommu_for_node ( archdata - > numa_node ) ;
if ( iommu = = NULL | | list_empty ( & iommu - > windows ) ) {
printk ( KERN_ERR " iommu: missing iommu for %s (node %d) \n " ,
archdata - > of_node ? archdata - > of_node - > full_name : " ? " ,
archdata - > numa_node ) ;
2008-07-05 05:05:44 +10:00
return NULL ;
2006-11-11 17:25:18 +11:00
}
window = list_entry ( iommu - > windows . next , struct iommu_window , list ) ;
2005-12-09 19:04:20 +01:00
2008-07-05 05:05:44 +10:00
return & window - > table ;
2005-12-09 19:04:20 +01:00
}
2008-07-18 23:03:34 +10:00
/* A coherent allocation implies strong ordering */
static void * dma_fixed_alloc_coherent ( struct device * dev , size_t size ,
dma_addr_t * dma_handle , gfp_t flag )
{
if ( iommu_fixed_is_weak )
return iommu_alloc_coherent ( dev , cell_get_iommu_table ( dev ) ,
size , dma_handle ,
device_to_mask ( dev ) , flag ,
dev - > archdata . numa_node ) ;
else
return dma_direct_ops . alloc_coherent ( dev , size , dma_handle ,
flag ) ;
}
static void dma_fixed_free_coherent ( struct device * dev , size_t size ,
void * vaddr , dma_addr_t dma_handle )
{
if ( iommu_fixed_is_weak )
iommu_free_coherent ( cell_get_iommu_table ( dev ) , size , vaddr ,
dma_handle ) ;
else
dma_direct_ops . free_coherent ( dev , size , vaddr , dma_handle ) ;
}
static dma_addr_t dma_fixed_map_single ( struct device * dev , void * ptr ,
size_t size ,
enum dma_data_direction direction ,
struct dma_attrs * attrs )
{
if ( iommu_fixed_is_weak = = dma_get_attr ( DMA_ATTR_WEAK_ORDERING , attrs ) )
return dma_direct_ops . map_single ( dev , ptr , size , direction ,
attrs ) ;
else
return iommu_map_single ( dev , cell_get_iommu_table ( dev ) , ptr ,
size , device_to_mask ( dev ) , direction ,
attrs ) ;
}
static void dma_fixed_unmap_single ( struct device * dev , dma_addr_t dma_addr ,
size_t size ,
enum dma_data_direction direction ,
struct dma_attrs * attrs )
{
if ( iommu_fixed_is_weak = = dma_get_attr ( DMA_ATTR_WEAK_ORDERING , attrs ) )
dma_direct_ops . unmap_single ( dev , dma_addr , size , direction ,
attrs ) ;
else
iommu_unmap_single ( cell_get_iommu_table ( dev ) , dma_addr , size ,
direction , attrs ) ;
}
static int dma_fixed_map_sg ( struct device * dev , struct scatterlist * sg ,
int nents , enum dma_data_direction direction ,
struct dma_attrs * attrs )
{
if ( iommu_fixed_is_weak = = dma_get_attr ( DMA_ATTR_WEAK_ORDERING , attrs ) )
return dma_direct_ops . map_sg ( dev , sg , nents , direction , attrs ) ;
else
return iommu_map_sg ( dev , cell_get_iommu_table ( dev ) , sg , nents ,
device_to_mask ( dev ) , direction , attrs ) ;
}
static void dma_fixed_unmap_sg ( struct device * dev , struct scatterlist * sg ,
int nents , enum dma_data_direction direction ,
struct dma_attrs * attrs )
{
if ( iommu_fixed_is_weak = = dma_get_attr ( DMA_ATTR_WEAK_ORDERING , attrs ) )
dma_direct_ops . unmap_sg ( dev , sg , nents , direction , attrs ) ;
else
iommu_unmap_sg ( cell_get_iommu_table ( dev ) , sg , nents , direction ,
attrs ) ;
}
static int dma_fixed_dma_supported ( struct device * dev , u64 mask )
{
return mask = = DMA_64BIT_MASK ;
}
static int dma_set_mask_and_switch ( struct device * dev , u64 dma_mask ) ;
struct dma_mapping_ops dma_iommu_fixed_ops = {
. alloc_coherent = dma_fixed_alloc_coherent ,
. free_coherent = dma_fixed_free_coherent ,
. map_single = dma_fixed_map_single ,
. unmap_single = dma_fixed_unmap_single ,
. map_sg = dma_fixed_map_sg ,
. unmap_sg = dma_fixed_unmap_sg ,
. dma_supported = dma_fixed_dma_supported ,
. set_dma_mask = dma_set_mask_and_switch ,
} ;
2008-02-29 18:33:22 +11:00
static void cell_dma_dev_setup_fixed ( struct device * dev ) ;
2008-01-30 11:03:44 +11:00
2008-01-30 01:14:01 +11:00
static void cell_dma_dev_setup ( struct device * dev )
{
struct dev_archdata * archdata = & dev - > archdata ;
2008-01-30 11:03:44 +11:00
/* Order is important here, these are not mutually exclusive */
if ( get_dma_ops ( dev ) = = & dma_iommu_fixed_ops )
2008-02-29 18:33:22 +11:00
cell_dma_dev_setup_fixed ( dev ) ;
2008-01-30 11:03:44 +11:00
else if ( get_pci_dma_ops ( ) = = & dma_iommu_ops )
2008-07-05 05:05:44 +10:00
archdata - > dma_data = cell_get_iommu_table ( dev ) ;
2008-01-30 01:14:01 +11:00
else if ( get_pci_dma_ops ( ) = = & dma_direct_ops )
archdata - > dma_data = ( void * ) cell_dma_direct_offset ;
else
BUG ( ) ;
}
2006-11-11 17:25:18 +11:00
static void cell_pci_dma_dev_setup ( struct pci_dev * dev )
2005-12-09 19:04:20 +01:00
{
2006-11-11 17:25:18 +11:00
cell_dma_dev_setup ( & dev - > dev ) ;
}
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
static int cell_of_bus_notify ( struct notifier_block * nb , unsigned long action ,
void * data )
{
struct device * dev = data ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* We are only intereted in device addition */
if ( action ! = BUS_NOTIFY_ADD_DEVICE )
return 0 ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* We use the PCI DMA ops */
2007-03-04 17:02:41 +11:00
dev - > archdata . dma_ops = get_pci_dma_ops ( ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
cell_dma_dev_setup ( dev ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
return 0 ;
}
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
static struct notifier_block cell_of_bus_notifier = {
. notifier_call = cell_of_bus_notify
} ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
static int __init cell_iommu_get_window ( struct device_node * np ,
unsigned long * base ,
unsigned long * size )
{
const void * dma_window ;
unsigned long index ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* Use ibm,dma-window if available, else, hard code ! */
2007-04-03 22:26:41 +10:00
dma_window = of_get_property ( np , " ibm,dma-window " , NULL ) ;
2006-11-11 17:25:18 +11:00
if ( dma_window = = NULL ) {
* base = 0 ;
* size = 0x80000000u ;
return - ENODEV ;
}
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
of_parse_dma_window ( np , dma_window , & index , base , size ) ;
2005-12-09 19:04:20 +01:00
return 0 ;
2005-06-23 09:43:54 +10:00
}
2008-01-30 01:13:59 +11:00
static struct cbe_iommu * __init cell_iommu_alloc ( struct device_node * np )
2005-12-09 19:04:20 +01:00
{
2006-11-11 17:25:18 +11:00
struct cbe_iommu * iommu ;
int nid , i ;
/* Get node ID */
nid = of_node_to_nid ( np ) ;
if ( nid < 0 ) {
printk ( KERN_ERR " iommu: failed to get node for %s \n " ,
np - > full_name ) ;
2008-01-30 01:13:59 +11:00
return NULL ;
2006-11-11 17:25:18 +11:00
}
pr_debug ( " iommu: setting up iommu for node %d (%s) \n " ,
nid , np - > full_name ) ;
/* XXX todo: If we can have multiple windows on the same IOMMU, which
* isn ' t the case today , we probably want here to check wether the
* iommu for that node is already setup .
* However , there might be issue with getting the size right so let ' s
* ignore that for now . We might want to completely get rid of the
* multiple window support since the cell iommu supports per - page ioids
*/
if ( cbe_nr_iommus > = NR_IOMMUS ) {
printk ( KERN_ERR " iommu: too many IOMMUs detected ! (%s) \n " ,
np - > full_name ) ;
2008-01-30 01:13:59 +11:00
return NULL ;
2006-11-11 17:25:18 +11:00
}
/* Init base fields */
i = cbe_nr_iommus + + ;
iommu = & iommus [ i ] ;
2007-02-09 16:38:15 +00:00
iommu - > stab = NULL ;
2006-11-11 17:25:18 +11:00
iommu - > nid = nid ;
snprintf ( iommu - > name , sizeof ( iommu - > name ) , " iommu%d " , i ) ;
INIT_LIST_HEAD ( & iommu - > windows ) ;
2005-12-09 19:04:20 +01:00
2008-01-30 01:13:59 +11:00
return iommu ;
}
static void __init cell_iommu_init_one ( struct device_node * np ,
unsigned long offset )
{
struct cbe_iommu * iommu ;
unsigned long base , size ;
iommu = cell_iommu_alloc ( np ) ;
if ( ! iommu )
return ;
2006-11-11 17:25:18 +11:00
/* Obtain a window for it */
cell_iommu_get_window ( np , & base , & size ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
pr_debug ( " \t translating window 0x%lx...0x%lx \n " ,
base , base + size - 1 ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* Initialize the hardware */
2008-01-30 01:14:00 +11:00
cell_iommu_setup_hardware ( iommu , base , size ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* Setup the iommu_table */
cell_iommu_setup_window ( iommu , np , base , size ,
offset > > IOMMU_PAGE_SHIFT ) ;
}
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
static void __init cell_disable_iommus ( void )
{
int node ;
unsigned long base , val ;
void __iomem * xregs , * cregs ;
/* Make sure IOC translation is disabled on all nodes */
for_each_online_node ( node ) {
if ( cell_iommu_find_ioc ( node , & base ) )
continue ;
xregs = ioremap ( base , IOC_Reg_Size ) ;
if ( xregs = = NULL )
continue ;
cregs = xregs + IOC_IOCmd_Offset ;
pr_debug ( " iommu: cleaning up iommu on node %d \n " , node ) ;
out_be64 ( xregs + IOC_IOST_Origin , 0 ) ;
( void ) in_be64 ( xregs + IOC_IOST_Origin ) ;
val = in_be64 ( cregs + IOC_IOCmd_Cfg ) ;
val & = ~ IOC_IOCmd_Cfg_TE ;
out_be64 ( cregs + IOC_IOCmd_Cfg , val ) ;
( void ) in_be64 ( cregs + IOC_IOCmd_Cfg ) ;
iounmap ( xregs ) ;
}
}
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
static int __init cell_iommu_init_disabled ( void )
{
struct device_node * np = NULL ;
unsigned long base = 0 , size ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* When no iommu is present, we use direct DMA ops */
2007-03-04 16:58:39 +11:00
set_pci_dma_ops ( & dma_direct_ops ) ;
2005-12-09 19:04:20 +01:00
2006-11-11 17:25:18 +11:00
/* First make sure all IOC translation is turned off */
cell_disable_iommus ( ) ;
/* If we have no Axon, we set up the spider DMA magic offset */
if ( of_find_node_by_name ( NULL , " axon " ) = = NULL )
2008-01-21 16:42:45 +11:00
cell_dma_direct_offset = SPIDER_DMA_OFFSET ;
2006-11-11 17:25:18 +11:00
/* Now we need to check to see where the memory is mapped
* in PCI space . We assume that all busses use the same dma
* window which is always the case so far on Cell , thus we
* pick up the first pci - internal node we can find and check
* the DMA window from there .
*/
for_each_node_by_name ( np , " axon " ) {
if ( np - > parent = = NULL | | np - > parent - > parent ! = NULL )
continue ;
if ( cell_iommu_get_window ( np , & base , & size ) = = 0 )
break ;
}
if ( np = = NULL ) {
for_each_node_by_name ( np , " pci-internal " ) {
if ( np - > parent = = NULL | | np - > parent - > parent ! = NULL )
continue ;
if ( cell_iommu_get_window ( np , & base , & size ) = = 0 )
break ;
}
}
of_node_put ( np ) ;
/* If we found a DMA window, we check if it's big enough to enclose
* all of physical memory . If not , we force enable IOMMU
*/
if ( np & & size < lmb_end_of_DRAM ( ) ) {
printk ( KERN_WARNING " iommu: force-enabled, dma window "
" (%ldMB) smaller than total memory (%ldMB) \n " ,
size > > 20 , lmb_end_of_DRAM ( ) > > 20 ) ;
return - ENODEV ;
2005-12-09 19:04:20 +01:00
}
2008-01-21 16:42:45 +11:00
cell_dma_direct_offset + = base ;
2006-11-11 17:25:18 +11:00
2008-01-21 16:42:45 +11:00
if ( cell_dma_direct_offset ! = 0 )
2008-01-21 16:42:41 +11:00
ppc_md . pci_dma_dev_setup = cell_pci_dma_dev_setup ;
2006-11-11 17:25:18 +11:00
printk ( " iommu: disabled, direct DMA offset is 0x%lx \n " ,
2008-01-21 16:42:45 +11:00
cell_dma_direct_offset ) ;
2006-11-11 17:25:18 +11:00
return 0 ;
2005-12-09 19:04:20 +01:00
}
2008-01-30 11:03:44 +11:00
/*
* Fixed IOMMU mapping support
*
* This code adds support for setting up a fixed IOMMU mapping on certain
* cell machines . For 64 - bit devices this avoids the performance overhead of
* mapping and unmapping pages at runtime . 32 - bit devices are unable to use
* the fixed mapping .
*
* The fixed mapping is established at boot , and maps all of physical memory
* 1 : 1 into device space at some offset . On machines with < 30 GB of memory
* we setup the fixed mapping immediately above the normal IOMMU window .
*
* For example a machine with 4 GB of memory would end up with the normal
* IOMMU window from 0 - 2 GB and the fixed mapping window from 2 GB to 6 GB . In
* this case a 64 - bit device wishing to DMA to 1 GB would be told to DMA to
* 3 GB , plus any offset required by firmware . The firmware offset is encoded
* in the " dma-ranges " property .
*
* On machines with 30 GB or more of memory , we are unable to place the fixed
* mapping above the normal IOMMU window as we would run out of address space .
* Instead we move the normal IOMMU window to coincide with the hash page
* table , this region does not need to be part of the fixed mapping as no
* device should ever be DMA ' ing to it . We then setup the fixed mapping
* from 0 to 32 GB .
*/
static u64 cell_iommu_get_fixed_address ( struct device * dev )
{
2008-03-14 16:47:39 +11:00
u64 cpu_addr , size , best_size , dev_addr = OF_BAD_ADDR ;
2008-02-08 16:37:02 +11:00
struct device_node * np ;
2008-01-30 11:03:44 +11:00
const u32 * ranges = NULL ;
2008-03-14 16:47:39 +11:00
int i , len , best , naddr , nsize , pna , range_size ;
2008-01-30 11:03:44 +11:00
2008-02-08 16:37:02 +11:00
np = of_node_get ( dev - > archdata . of_node ) ;
2008-03-14 16:47:39 +11:00
while ( 1 ) {
naddr = of_n_addr_cells ( np ) ;
nsize = of_n_size_cells ( np ) ;
np = of_get_next_parent ( np ) ;
if ( ! np )
break ;
2008-01-30 11:03:44 +11:00
ranges = of_get_property ( np , " dma-ranges " , & len ) ;
2008-03-14 16:47:39 +11:00
/* Ignore empty ranges, they imply no translation required */
if ( ranges & & len > 0 )
2008-02-08 16:37:02 +11:00
break ;
2008-01-30 11:03:44 +11:00
}
if ( ! ranges ) {
dev_dbg ( dev , " iommu: no dma-ranges found \n " ) ;
goto out ;
}
len / = sizeof ( u32 ) ;
2008-03-14 16:47:39 +11:00
pna = of_n_addr_cells ( np ) ;
range_size = naddr + nsize + pna ;
2008-01-30 11:03:44 +11:00
/* dma-ranges format:
2008-03-14 16:47:39 +11:00
* child addr : naddr cells
* parent addr : pna cells
* size : nsize cells
2008-01-30 11:03:44 +11:00
*/
2008-03-14 16:47:39 +11:00
for ( i = 0 , best = - 1 , best_size = 0 ; i < len ; i + = range_size ) {
cpu_addr = of_translate_dma_address ( np , ranges + i + naddr ) ;
size = of_read_number ( ranges + i + naddr + pna , nsize ) ;
2008-01-30 11:03:44 +11:00
if ( cpu_addr = = 0 & & size > best_size ) {
best = i ;
best_size = size ;
}
}
2008-03-14 16:47:39 +11:00
if ( best > = 0 ) {
dev_addr = of_read_number ( ranges + best , naddr ) ;
} else
2008-01-30 11:03:44 +11:00
dev_dbg ( dev , " iommu: no suitable range found! \n " ) ;
out :
of_node_put ( np ) ;
2008-03-14 16:47:39 +11:00
return dev_addr ;
2008-01-30 11:03:44 +11:00
}
static int dma_set_mask_and_switch ( struct device * dev , u64 dma_mask )
{
if ( ! dev - > dma_mask | | ! dma_supported ( dev , dma_mask ) )
return - EIO ;
2008-02-08 16:37:04 +11:00
if ( dma_mask = = DMA_BIT_MASK ( 64 ) & &
cell_iommu_get_fixed_address ( dev ) ! = OF_BAD_ADDR )
{
dev_dbg ( dev , " iommu: 64-bit OK, using fixed ops \n " ) ;
set_dma_ops ( dev , & dma_iommu_fixed_ops ) ;
2008-01-30 11:03:44 +11:00
} else {
dev_dbg ( dev , " iommu: not 64-bit, using default ops \n " ) ;
set_dma_ops ( dev , get_pci_dma_ops ( ) ) ;
}
2008-02-08 16:37:04 +11:00
cell_dma_dev_setup ( dev ) ;
2008-01-30 11:03:44 +11:00
* dev - > dma_mask = dma_mask ;
return 0 ;
}
2008-02-29 18:33:22 +11:00
static void cell_dma_dev_setup_fixed ( struct device * dev )
2008-01-30 11:03:44 +11:00
{
struct dev_archdata * archdata = & dev - > archdata ;
u64 addr ;
addr = cell_iommu_get_fixed_address ( dev ) + dma_iommu_fixed_base ;
archdata - > dma_data = ( void * ) addr ;
dev_dbg ( dev , " iommu: fixed addr = %lx \n " , addr ) ;
}
2008-02-29 18:33:29 +11:00
static void insert_16M_pte ( unsigned long addr , unsigned long * ptab ,
unsigned long base_pte )
{
unsigned long segment , offset ;
segment = addr > > IO_SEGMENT_SHIFT ;
offset = ( addr > > 24 ) - ( segment < < IO_PAGENO_BITS ( 24 ) ) ;
ptab = ptab + ( segment * ( 1 < < 12 ) / sizeof ( unsigned long ) ) ;
pr_debug ( " iommu: addr %lx ptab %p segment %lx offset %lx \n " ,
addr , ptab , segment , offset ) ;
ptab [ offset ] = base_pte | ( __pa ( addr ) & IOPTE_RPN_Mask ) ;
}
2008-01-30 11:03:44 +11:00
static void cell_iommu_setup_fixed_ptab ( struct cbe_iommu * iommu ,
struct device_node * np , unsigned long dbase , unsigned long dsize ,
unsigned long fbase , unsigned long fsize )
{
2008-02-29 18:33:29 +11:00
unsigned long base_pte , uaddr , ioaddr , * ptab ;
2008-01-30 11:03:44 +11:00
2008-02-29 18:33:29 +11:00
ptab = cell_iommu_alloc_ptab ( iommu , fbase , fsize , dbase , dsize , 24 ) ;
2008-01-30 11:03:44 +11:00
dma_iommu_fixed_base = fbase ;
pr_debug ( " iommu: mapping 0x%lx pages from 0x%lx \n " , fsize , fbase ) ;
2008-07-18 23:03:34 +10:00
base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M
2008-01-30 11:03:44 +11:00
| ( cell_iommu_get_ioid ( np ) & IOPTE_IOID_Mask ) ;
2008-07-18 23:03:34 +10:00
if ( iommu_fixed_is_weak )
pr_info ( " IOMMU: Using weak ordering for fixed mapping \n " ) ;
else {
pr_info ( " IOMMU: Using strong ordering for fixed mapping \n " ) ;
base_pte | = IOPTE_SO_RW ;
}
2008-02-29 18:33:29 +11:00
for ( uaddr = 0 ; uaddr < fsize ; uaddr + = ( 1 < < 24 ) ) {
2008-01-30 11:03:44 +11:00
/* Don't touch the dynamic region */
2008-02-29 18:33:29 +11:00
ioaddr = uaddr + fbase ;
if ( ioaddr > = dbase & & ioaddr < ( dbase + dsize ) ) {
2008-02-29 18:33:22 +11:00
pr_debug ( " iommu: fixed/dynamic overlap, skipping \n " ) ;
2008-01-30 11:03:44 +11:00
continue ;
}
2008-02-29 18:33:29 +11:00
insert_16M_pte ( uaddr , ptab , base_pte ) ;
2008-01-30 11:03:44 +11:00
}
mb ( ) ;
}
static int __init cell_iommu_fixed_mapping_init ( void )
{
unsigned long dbase , dsize , fbase , fsize , hbase , hend ;
struct cbe_iommu * iommu ;
struct device_node * np ;
/* The fixed mapping is only supported on axon machines */
np = of_find_node_by_name ( NULL , " axon " ) ;
if ( ! np ) {
pr_debug ( " iommu: fixed mapping disabled, no axons found \n " ) ;
return - 1 ;
}
2008-02-08 16:37:03 +11:00
/* We must have dma-ranges properties for fixed mapping to work */
for ( np = NULL ; ( np = of_find_all_nodes ( np ) ) ; ) {
if ( of_find_property ( np , " dma-ranges " , NULL ) )
break ;
}
of_node_put ( np ) ;
if ( ! np ) {
pr_debug ( " iommu: no dma-ranges found, no fixed mapping \n " ) ;
return - 1 ;
}
2008-01-30 11:03:44 +11:00
/* The default setup is to have the fixed mapping sit after the
* dynamic region , so find the top of the largest IOMMU window
* on any axon , then add the size of RAM and that ' s our max value .
* If that is > 32 GB we have to do other shennanigans .
*/
fbase = 0 ;
for_each_node_by_name ( np , " axon " ) {
cell_iommu_get_window ( np , & dbase , & dsize ) ;
fbase = max ( fbase , dbase + dsize ) ;
}
fbase = _ALIGN_UP ( fbase , 1 < < IO_SEGMENT_SHIFT ) ;
fsize = lmb_phys_mem_size ( ) ;
if ( ( fbase + fsize ) < = 0x800000000 )
hbase = 0 ; /* use the device tree window */
else {
/* If we're over 32 GB we need to cheat. We can't map all of
* RAM with the fixed mapping , and also fit the dynamic
* region . So try to place the dynamic region where the hash
* table sits , drivers never need to DMA to it , we don ' t
* need a fixed mapping for that area .
*/
if ( ! htab_address ) {
pr_debug ( " iommu: htab is NULL, on LPAR? Huh? \n " ) ;
return - 1 ;
}
hbase = __pa ( htab_address ) ;
hend = hbase + htab_size_bytes ;
/* The window must start and end on a segment boundary */
if ( ( hbase ! = _ALIGN_UP ( hbase , 1 < < IO_SEGMENT_SHIFT ) ) | |
( hend ! = _ALIGN_UP ( hend , 1 < < IO_SEGMENT_SHIFT ) ) ) {
pr_debug ( " iommu: hash window not segment aligned \n " ) ;
return - 1 ;
}
/* Check the hash window fits inside the real DMA window */
for_each_node_by_name ( np , " axon " ) {
cell_iommu_get_window ( np , & dbase , & dsize ) ;
if ( hbase < dbase | | ( hend > ( dbase + dsize ) ) ) {
pr_debug ( " iommu: hash window doesn't fit in "
" real DMA window \n " ) ;
return - 1 ;
}
}
fbase = 0 ;
}
/* Setup the dynamic regions */
for_each_node_by_name ( np , " axon " ) {
iommu = cell_iommu_alloc ( np ) ;
BUG_ON ( ! iommu ) ;
if ( hbase = = 0 )
cell_iommu_get_window ( np , & dbase , & dsize ) ;
else {
dbase = hbase ;
dsize = htab_size_bytes ;
}
2008-02-08 16:37:04 +11:00
printk ( KERN_DEBUG " iommu: node %d, dynamic window 0x%lx-0x%lx "
" fixed window 0x%lx-0x%lx \n " , iommu - > nid , dbase ,
2008-01-30 11:03:44 +11:00
dbase + dsize , fbase , fbase + fsize ) ;
2008-02-29 18:33:25 +11:00
cell_iommu_setup_stab ( iommu , dbase , dsize , fbase , fsize ) ;
2008-02-29 18:33:27 +11:00
iommu - > ptab = cell_iommu_alloc_ptab ( iommu , dbase , dsize , 0 , 0 ,
IOMMU_PAGE_SHIFT ) ;
2008-01-30 11:03:44 +11:00
cell_iommu_setup_fixed_ptab ( iommu , np , dbase , dsize ,
fbase , fsize ) ;
cell_iommu_enable_hardware ( iommu ) ;
cell_iommu_setup_window ( iommu , np , dbase , dsize , 0 ) ;
}
dma_iommu_ops . set_dma_mask = dma_set_mask_and_switch ;
set_pci_dma_ops ( & dma_iommu_ops ) ;
return 0 ;
}
static int iommu_fixed_disabled ;
static int __init setup_iommu_fixed ( char * str )
{
2008-07-24 14:28:48 +10:00
struct device_node * pciep ;
2008-01-30 11:03:44 +11:00
if ( strcmp ( str , " off " ) = = 0 )
iommu_fixed_disabled = 1 ;
2008-07-24 14:28:48 +10:00
/* If we can find a pcie-endpoint in the device tree assume that
* we ' re on a triblade or a CAB so by default the fixed mapping
* should be set to be weakly ordered ; but only if the boot
* option WASN ' T set for strong ordering
*/
pciep = of_find_node_by_type ( NULL , " pcie-endpoint " ) ;
if ( strcmp ( str , " weak " ) = = 0 | | ( pciep & & strcmp ( str , " strong " ) ! = 0 ) )
2008-07-18 23:03:34 +10:00
iommu_fixed_is_weak = 1 ;
2008-07-24 14:28:48 +10:00
of_node_put ( pciep ) ;
2008-01-30 11:03:44 +11:00
return 1 ;
}
__setup ( " iommu_fixed= " , setup_iommu_fixed ) ;
2006-11-11 17:25:18 +11:00
static int __init cell_iommu_init ( void )
2005-06-23 09:43:54 +10:00
{
2006-11-11 17:25:18 +11:00
struct device_node * np ;
/* If IOMMU is disabled or we have little enough RAM to not need
* to enable it , we setup a direct mapping .
*
* Note : should we make sure we have the IOMMU actually disabled ?
*/
if ( iommu_is_off | |
( ! iommu_force_on & & lmb_end_of_DRAM ( ) < = 0x80000000ull ) )
if ( cell_iommu_init_disabled ( ) = = 0 )
goto bail ;
/* Setup various ppc_md. callbacks */
ppc_md . pci_dma_dev_setup = cell_pci_dma_dev_setup ;
ppc_md . tce_build = tce_build_cell ;
ppc_md . tce_free = tce_free_cell ;
2008-01-30 11:03:44 +11:00
if ( ! iommu_fixed_disabled & & cell_iommu_fixed_mapping_init ( ) = = 0 )
goto bail ;
2006-11-11 17:25:18 +11:00
/* Create an iommu for each /axon node. */
for_each_node_by_name ( np , " axon " ) {
if ( np - > parent = = NULL | | np - > parent - > parent ! = NULL )
continue ;
cell_iommu_init_one ( np , 0 ) ;
2005-12-09 19:04:20 +01:00
}
2005-06-23 09:43:54 +10:00
2006-11-11 17:25:18 +11:00
/* Create an iommu for each toplevel /pci-internal node for
* old hardware / firmware
*/
for_each_node_by_name ( np , " pci-internal " ) {
if ( np - > parent = = NULL | | np - > parent - > parent ! = NULL )
continue ;
cell_iommu_init_one ( np , SPIDER_DMA_OFFSET ) ;
}
/* Setup default PCI iommu ops */
2007-03-04 16:58:39 +11:00
set_pci_dma_ops ( & dma_iommu_ops ) ;
2006-11-11 17:25:18 +11:00
bail :
/* Register callbacks on OF platform device addition/removal
* to handle linking them to the right DMA operations
*/
bus_register_notifier ( & of_platform_bus_type , & cell_of_bus_notifier ) ;
return 0 ;
2005-06-23 09:43:54 +10:00
}
2008-01-03 06:14:36 +11:00
machine_arch_initcall ( cell , cell_iommu_init ) ;
machine_arch_initcall ( celleb_native , cell_iommu_init ) ;
2006-11-11 17:25:18 +11:00