2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2001 Mike Corrigan & Dave Engebretsen , IBM Corporation
*
2006-04-28 22:51:59 -05:00
* Rewrite , cleanup :
2005-04-16 15:20:36 -07:00
*
2005-11-21 02:12:32 -06:00
* Copyright ( C ) 2004 Olof Johansson < olof @ lixom . net > , IBM Corporation
2006-04-28 22:51:59 -05:00
* Copyright ( C ) 2006 Olof Johansson < olof @ lixom . net >
2005-04-16 15:20:36 -07:00
*
* Dynamic DMA mapping support , pSeries - specific parts , both SMP and LPAR .
*
2006-04-28 22:51:59 -05:00
*
2005-04-16 15:20:36 -07: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 of the License , or
* ( at your option ) any later version .
2006-04-28 22:51:59 -05:00
*
2005-04-16 15:20:36 -07:00
* 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 .
2006-04-28 22:51:59 -05:00
*
2005-04-16 15:20:36 -07:00
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/spinlock.h>
# include <linux/string.h>
# include <linux/pci.h>
# include <linux/dma-mapping.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/rtas.h>
# include <asm/iommu.h>
# include <asm/pci-bridge.h>
# include <asm/machdep.h>
# include <asm/abs_addr.h>
# include <asm/pSeries_reconfig.h>
2005-08-03 14:35:25 +10:00
# include <asm/firmware.h>
2005-09-20 13:45:41 +10:00
# include <asm/tce.h>
2005-09-28 02:50:25 +10:00
# include <asm/ppc-pci.h>
2005-11-07 13:18:13 +11:00
# include <asm/udbg.h>
2005-04-16 15:20:36 -07:00
2005-11-03 15:33:31 +11:00
# include "plpar_wrappers.h"
2005-04-16 15:20:36 -07:00
# define DBG(fmt...)
2006-04-28 22:51:59 -05:00
static void tce_build_pSeries ( struct iommu_table * tbl , long index ,
long npages , unsigned long uaddr ,
2005-04-16 15:20:36 -07:00
enum dma_data_direction direction )
{
2006-04-28 22:51:59 -05:00
u64 proto_tce ;
u64 * tcep ;
u64 rpn ;
2005-04-16 15:20:36 -07:00
2006-04-28 22:51:59 -05:00
proto_tce = TCE_PCI_READ ; // Read allowed
2005-04-16 15:20:36 -07:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-28 22:51:59 -05:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-16 15:20:36 -07:00
2006-04-28 22:51:59 -05:00
tcep = ( ( u64 * ) tbl - > it_base ) + index ;
2005-04-16 15:20:36 -07:00
while ( npages - - ) {
/* can't move this out since we might cross LMB boundary */
2006-04-28 22:51:59 -05:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
* tcep = proto_tce | ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
2005-04-16 15:20:36 -07:00
2005-09-20 13:46:44 +10:00
uaddr + = TCE_PAGE_SIZE ;
2006-04-28 22:51:59 -05:00
tcep + + ;
2005-04-16 15:20:36 -07:00
}
}
static void tce_free_pSeries ( struct iommu_table * tbl , long index , long npages )
{
2006-04-28 22:51:59 -05:00
u64 * tcep ;
2005-04-16 15:20:36 -07:00
2006-04-28 22:51:59 -05:00
tcep = ( ( u64 * ) tbl - > it_base ) + index ;
while ( npages - - )
* ( tcep + + ) = 0 ;
2005-04-16 15:20:36 -07:00
}
2006-06-22 23:35:10 -07:00
static unsigned long tce_get_pseries ( struct iommu_table * tbl , long index )
{
u64 * tcep ;
tcep = ( ( u64 * ) tbl - > it_base ) + index ;
return * tcep ;
}
2005-04-16 15:20:36 -07:00
static void tce_build_pSeriesLP ( struct iommu_table * tbl , long tcenum ,
long npages , unsigned long uaddr ,
enum dma_data_direction direction )
{
u64 rc ;
2006-04-28 22:51:59 -05:00
u64 proto_tce , tce ;
u64 rpn ;
2005-04-16 15:20:36 -07:00
2006-04-28 22:51:59 -05:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
proto_tce = TCE_PCI_READ ;
2005-04-16 15:20:36 -07:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-28 22:51:59 -05:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-16 15:20:36 -07:00
while ( npages - - ) {
2006-04-28 22:51:59 -05:00
tce = proto_tce | ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
rc = plpar_tce_put ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , tce ) ;
2005-04-16 15:20:36 -07:00
if ( rc & & printk_ratelimit ( ) ) {
printk ( " tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld \n " , rc ) ;
printk ( " \t index = 0x%lx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%lx \n " , ( u64 ) tcenum ) ;
2006-04-28 22:51:59 -05:00
printk ( " \t tce val = 0x%lx \n " , tce ) ;
2005-04-16 15:20:36 -07:00
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
2006-04-28 22:51:59 -05:00
2005-04-16 15:20:36 -07:00
tcenum + + ;
2006-04-28 22:51:59 -05:00
rpn + + ;
2005-04-16 15:20:36 -07:00
}
}
2006-04-28 22:51:59 -05:00
static DEFINE_PER_CPU ( u64 * , tce_page ) = NULL ;
2005-04-16 15:20:36 -07:00
static void tce_buildmulti_pSeriesLP ( struct iommu_table * tbl , long tcenum ,
long npages , unsigned long uaddr ,
enum dma_data_direction direction )
{
u64 rc ;
2006-04-28 22:51:59 -05:00
u64 proto_tce ;
u64 * tcep ;
u64 rpn ;
2005-04-16 15:20:36 -07:00
long l , limit ;
2006-10-30 16:15:59 +11:00
if ( npages = = 1 )
2005-04-16 15:20:36 -07:00
return tce_build_pSeriesLP ( tbl , tcenum , npages , uaddr ,
direction ) ;
tcep = __get_cpu_var ( tce_page ) ;
/* This is safe to do since interrupts are off when we're called
* from iommu_alloc { , _sg } ( )
*/
if ( ! tcep ) {
2006-04-28 22:51:59 -05:00
tcep = ( u64 * ) __get_free_page ( GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
/* If allocation fails, fall back to the loop implementation */
if ( ! tcep )
return tce_build_pSeriesLP ( tbl , tcenum , npages ,
uaddr , direction ) ;
__get_cpu_var ( tce_page ) = tcep ;
}
2006-04-28 22:51:59 -05:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
proto_tce = TCE_PCI_READ ;
2005-04-16 15:20:36 -07:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-28 22:51:59 -05:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-16 15:20:36 -07:00
/* We can map max one pageful of TCEs at a time */
do {
/*
* Set up the page with TCE data , looping through and setting
* the values .
*/
2006-04-28 22:51:59 -05:00
limit = min_t ( long , npages , 4096 / TCE_ENTRY_SIZE ) ;
2005-04-16 15:20:36 -07:00
for ( l = 0 ; l < limit ; l + + ) {
2006-04-28 22:51:59 -05:00
tcep [ l ] = proto_tce | ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
rpn + + ;
2005-04-16 15:20:36 -07:00
}
rc = plpar_tce_put_indirect ( ( u64 ) tbl - > it_index ,
( u64 ) tcenum < < 12 ,
( u64 ) virt_to_abs ( tcep ) ,
limit ) ;
npages - = limit ;
tcenum + = limit ;
} while ( npages > 0 & & ! rc ) ;
if ( rc & & printk_ratelimit ( ) ) {
printk ( " tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%ld \n " , rc ) ;
printk ( " \t index = 0x%lx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t npages = 0x%lx \n " , ( u64 ) npages ) ;
2006-04-28 22:51:59 -05:00
printk ( " \t tce[0] val = 0x%lx \n " , tcep [ 0 ] ) ;
2005-04-16 15:20:36 -07:00
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
}
static void tce_free_pSeriesLP ( struct iommu_table * tbl , long tcenum , long npages )
{
u64 rc ;
while ( npages - - ) {
2006-04-28 22:51:59 -05:00
rc = plpar_tce_put ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( rc & & printk_ratelimit ( ) ) {
printk ( " tce_free_pSeriesLP: plpar_tce_put failed. rc=%ld \n " , rc ) ;
printk ( " \t index = 0x%lx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%lx \n " , ( u64 ) tcenum ) ;
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
tcenum + + ;
}
}
static void tce_freemulti_pSeriesLP ( struct iommu_table * tbl , long tcenum , long npages )
{
u64 rc ;
2006-04-28 22:51:59 -05:00
rc = plpar_tce_stuff ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , 0 , npages ) ;
2005-04-16 15:20:36 -07:00
if ( rc & & printk_ratelimit ( ) ) {
printk ( " tce_freemulti_pSeriesLP: plpar_tce_stuff failed \n " ) ;
printk ( " \t rc = %ld \n " , rc ) ;
printk ( " \t index = 0x%lx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t npages = 0x%lx \n " , ( u64 ) npages ) ;
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
}
2006-06-22 23:35:10 -07:00
static unsigned long tce_get_pSeriesLP ( struct iommu_table * tbl , long tcenum )
{
u64 rc ;
unsigned long tce_ret ;
rc = plpar_tce_get ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , & tce_ret ) ;
if ( rc & & printk_ratelimit ( ) ) {
printk ( " tce_get_pSeriesLP: plpar_tce_get failed. rc=%ld \n " ,
rc ) ;
printk ( " \t index = 0x%lx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%lx \n " , ( u64 ) tcenum ) ;
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
return tce_ret ;
}
2007-03-04 17:04:44 +11:00
# ifdef CONFIG_PCI
2005-04-16 15:20:36 -07:00
static void iommu_table_setparms ( struct pci_controller * phb ,
struct device_node * dn ,
2006-04-28 22:51:59 -05:00
struct iommu_table * tbl )
2005-04-16 15:20:36 -07:00
{
struct device_node * node ;
2006-10-04 22:28:00 -05:00
const unsigned long * basep ;
const u32 * sizep ;
2005-04-16 15:20:36 -07:00
node = ( struct device_node * ) phb - > arch_data ;
2007-04-03 22:26:41 +10:00
basep = of_get_property ( node , " linux,tce-base " , NULL ) ;
sizep = of_get_property ( node , " linux,tce-size " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( basep = = NULL | | sizep = = NULL ) {
printk ( KERN_ERR " PCI_DMA: iommu_table_setparms: %s has "
" missing tce entries ! \n " , dn - > full_name ) ;
return ;
}
tbl - > it_base = ( unsigned long ) __va ( * basep ) ;
2006-06-22 23:35:10 -07:00
# ifndef CONFIG_CRASH_DUMP
2005-04-16 15:20:36 -07:00
memset ( ( void * ) tbl - > it_base , 0 , * sizep ) ;
2006-06-22 23:35:10 -07:00
# endif
2005-04-16 15:20:36 -07:00
tbl - > it_busno = phb - > bus - > number ;
2006-04-28 22:51:59 -05:00
2005-04-16 15:20:36 -07:00
/* Units of tce entries */
2006-10-30 16:15:59 +11:00
tbl - > it_offset = phb - > dma_window_base_cur > > IOMMU_PAGE_SHIFT ;
2006-04-28 22:51:59 -05:00
2005-04-16 15:20:36 -07:00
/* Test if we are going over 2GB of DMA space */
2005-09-21 09:55:31 -07:00
if ( phb - > dma_window_base_cur + phb - > dma_window_size > 0x80000000ul ) {
udbg_printf ( " PCI_DMA: Unexpected number of IOAs under this PHB. \n " ) ;
2006-04-28 22:51:59 -05:00
panic ( " PCI_DMA: Unexpected number of IOAs under this PHB. \n " ) ;
2005-09-21 09:55:31 -07:00
}
2006-04-28 22:51:59 -05:00
2005-04-16 15:20:36 -07:00
phb - > dma_window_base_cur + = phb - > dma_window_size ;
/* Set the tce table size - measured in entries */
2006-10-30 16:15:59 +11:00
tbl - > it_size = phb - > dma_window_size > > IOMMU_PAGE_SHIFT ;
2005-04-16 15:20:36 -07:00
tbl - > it_index = 0 ;
tbl - > it_blocksize = 16 ;
tbl - > it_type = TCE_PCI ;
}
/*
* iommu_table_setparms_lpar
*
* Function : On pSeries LPAR systems , return TCE table info , given a pci bus .
*/
static void iommu_table_setparms_lpar ( struct pci_controller * phb ,
struct device_node * dn ,
struct iommu_table * tbl ,
2006-07-12 15:39:43 +10:00
const void * dma_window )
2005-04-16 15:20:36 -07:00
{
2006-05-18 18:06:37 +10:00
unsigned long offset , size ;
2005-09-06 13:17:54 +10:00
tbl - > it_busno = PCI_DN ( dn ) - > bussubno ;
2006-05-18 18:06:37 +10:00
of_parse_dma_window ( dn , dma_window , & tbl - > it_index , & offset , & size ) ;
2005-04-16 15:20:36 -07:00
tbl - > it_base = 0 ;
tbl - > it_blocksize = 16 ;
tbl - > it_type = TCE_PCI ;
2006-10-30 16:15:59 +11:00
tbl - > it_offset = offset > > IOMMU_PAGE_SHIFT ;
tbl - > it_size = size > > IOMMU_PAGE_SHIFT ;
2005-04-16 15:20:36 -07:00
}
2006-11-11 17:25:02 +11:00
static void pci_dma_bus_setup_pSeries ( struct pci_bus * bus )
2005-04-16 15:20:36 -07:00
{
2005-09-21 09:55:31 -07:00
struct device_node * dn ;
2005-04-16 15:20:36 -07:00
struct iommu_table * tbl ;
2005-09-21 09:55:31 -07:00
struct device_node * isa_dn , * isa_dn_orig ;
struct device_node * tmp ;
struct pci_dn * pci ;
int children ;
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
dn = pci_bus_to_OF_node ( bus ) ;
2006-11-11 17:25:02 +11:00
DBG ( " pci_dma_bus_setup_pSeries: setting up bus %s \n " , dn - > full_name ) ;
2005-09-21 09:55:31 -07:00
if ( bus - > self ) {
/* This is not a root bus, any setup will be done for the
* device - side of the bridge in iommu_dev_setup_pSeries ( ) .
*/
return ;
}
2006-11-11 17:25:02 +11:00
pci = PCI_DN ( dn ) ;
2005-09-21 09:55:31 -07:00
/* Check if the ISA bus on the system is under
* this PHB .
2005-04-16 15:20:36 -07:00
*/
2005-09-21 09:55:31 -07:00
isa_dn = isa_dn_orig = of_find_node_by_type ( NULL , " isa " ) ;
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
while ( isa_dn & & isa_dn ! = dn )
isa_dn = isa_dn - > parent ;
if ( isa_dn_orig )
of_node_put ( isa_dn_orig ) ;
2005-04-16 15:20:36 -07:00
2006-06-20 18:00:30 +10:00
/* Count number of direct PCI children of the PHB. */
2005-09-21 09:55:31 -07:00
for ( children = 0 , tmp = dn - > child ; tmp ; tmp = tmp - > sibling )
2006-06-20 18:00:30 +10:00
children + + ;
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
DBG ( " Children: %d \n " , children ) ;
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
/* Calculate amount of DMA window per slot. Each window must be
* a power of two ( due to pci_alloc_consistent requirements ) .
*
* Keep 256 MB aside for PHBs with ISA .
*/
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
if ( ! isa_dn ) {
/* No ISA/IDE - just set window size and return */
pci - > phb - > dma_window_size = 0x80000000ul ; /* To be divided */
while ( pci - > phb - > dma_window_size * children > 0x80000000ul )
pci - > phb - > dma_window_size > > = 1 ;
2005-09-22 21:44:05 -07:00
DBG ( " No ISA/IDE, window size is 0x%lx \n " ,
pci - > phb - > dma_window_size ) ;
2005-09-21 09:55:31 -07:00
pci - > phb - > dma_window_base_cur = 0 ;
return ;
2005-04-16 15:20:36 -07:00
}
2005-09-21 09:55:31 -07:00
/* If we have ISA, then we probably have an IDE
* controller too . Allocate a 128 MB table but
* skip the first 128 MB to avoid stepping on ISA
* space .
*/
pci - > phb - > dma_window_size = 0x8000000ul ;
pci - > phb - > dma_window_base_cur = 0x8000000ul ;
2006-06-10 20:58:08 +10:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
pci - > phb - > node ) ;
2005-09-21 09:55:31 -07:00
iommu_table_setparms ( pci - > phb , dn , tbl ) ;
2006-06-10 20:58:08 +10:00
pci - > iommu_table = iommu_init_table ( tbl , pci - > phb - > node ) ;
2005-09-21 09:55:31 -07:00
/* Divide the rest (1.75GB) among the children */
pci - > phb - > dma_window_size = 0x80000000ul ;
while ( pci - > phb - > dma_window_size * children > 0x70000000ul )
pci - > phb - > dma_window_size > > = 1 ;
2005-09-22 21:44:05 -07:00
DBG ( " ISA/IDE, window size is 0x%lx \n " , pci - > phb - > dma_window_size ) ;
2005-09-21 09:55:31 -07:00
2005-04-16 15:20:36 -07:00
}
2006-11-11 17:25:02 +11:00
static void pci_dma_bus_setup_pSeriesLP ( struct pci_bus * bus )
2005-04-16 15:20:36 -07:00
{
struct iommu_table * tbl ;
struct device_node * dn , * pdn ;
2005-09-06 13:17:54 +10:00
struct pci_dn * ppci ;
2006-07-12 15:39:43 +10:00
const void * dma_window = NULL ;
2005-04-16 15:20:36 -07:00
dn = pci_bus_to_OF_node ( bus ) ;
2006-11-11 17:25:02 +11:00
DBG ( " pci_dma_bus_setup_pSeriesLP: setting up bus %s \n " , dn - > full_name ) ;
2005-04-16 15:20:36 -07:00
/* Find nearest ibm,dma-window, walking up the device tree */
for ( pdn = dn ; pdn ! = NULL ; pdn = pdn - > parent ) {
2007-04-03 22:26:41 +10:00
dma_window = of_get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( dma_window ! = NULL )
break ;
}
if ( dma_window = = NULL ) {
2006-11-11 17:25:02 +11:00
DBG ( " no ibm,dma-window property ! \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2005-12-05 19:37:35 -06:00
ppci = PCI_DN ( pdn ) ;
2006-11-11 17:25:02 +11:00
DBG ( " parent is %s, iommu_table: 0x%p \n " ,
pdn - > full_name , ppci - > iommu_table ) ;
2005-09-06 13:17:54 +10:00
if ( ! ppci - > iommu_table ) {
2005-04-16 15:20:36 -07:00
/* Bussubno hasn't been copied yet.
* Do it now because iommu_table_setparms_lpar needs it .
*/
2005-09-06 13:17:54 +10:00
ppci - > bussubno = bus - > number ;
2005-04-16 15:20:36 -07:00
2006-06-10 20:58:08 +10:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
ppci - > phb - > node ) ;
2006-04-28 22:51:59 -05:00
2005-09-06 13:17:54 +10:00
iommu_table_setparms_lpar ( ppci - > phb , pdn , tbl , dma_window ) ;
2005-04-16 15:20:36 -07:00
2006-06-10 20:58:08 +10:00
ppci - > iommu_table = iommu_init_table ( tbl , ppci - > phb - > node ) ;
2006-11-11 17:25:02 +11:00
DBG ( " created table: %p \n " , ppci - > iommu_table ) ;
2005-04-16 15:20:36 -07:00
}
if ( pdn ! = dn )
2005-09-06 13:17:54 +10:00
PCI_DN ( dn ) - > iommu_table = ppci - > iommu_table ;
2005-04-16 15:20:36 -07:00
}
2006-11-11 17:25:02 +11:00
static void pci_dma_dev_setup_pSeries ( struct pci_dev * dev )
2005-04-16 15:20:36 -07:00
{
2006-11-11 17:25:02 +11:00
struct device_node * dn ;
2005-09-21 09:55:31 -07:00
struct iommu_table * tbl ;
2005-04-16 15:20:36 -07:00
2006-11-11 17:25:02 +11:00
DBG ( " pci_dma_dev_setup_pSeries: %s \n " , pci_name ( dev ) ) ;
2005-04-16 15:20:36 -07:00
2006-11-11 17:25:02 +11:00
dn = dev - > dev . archdata . of_node ;
2005-04-16 15:20:36 -07:00
2005-09-21 09:55:31 -07:00
/* If we're the direct child of a root bus, then we need to allocate
* an iommu table ourselves . The bus setup code should have setup
* the window sizes already .
*/
if ( ! dev - > bus - > self ) {
2006-11-11 17:25:02 +11:00
struct pci_controller * phb = PCI_DN ( dn ) - > phb ;
2005-09-21 09:55:31 -07:00
DBG ( " --> first child, no bridge. Allocating iommu table. \n " ) ;
2006-06-10 20:58:08 +10:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
2006-11-11 17:25:02 +11:00
phb - > node ) ;
iommu_table_setparms ( phb , dn , tbl ) ;
2007-01-10 19:16:29 -06:00
PCI_DN ( dn ) - > iommu_table = iommu_init_table ( tbl , phb - > node ) ;
dev - > dev . archdata . dma_data = PCI_DN ( dn ) - > iommu_table ;
2005-09-21 09:55:31 -07:00
return ;
}
/* If this device is further down the bus tree, search upwards until
* an already allocated iommu table is found and use that .
*/
2005-12-05 19:37:35 -06:00
while ( dn & & PCI_DN ( dn ) & & PCI_DN ( dn ) - > iommu_table = = NULL )
2005-04-16 15:20:36 -07:00
dn = dn - > parent ;
2006-11-11 17:25:02 +11:00
if ( dn & & PCI_DN ( dn ) )
dev - > dev . archdata . dma_data = PCI_DN ( dn ) - > iommu_table ;
else
printk ( KERN_WARNING " iommu: Device %s has no iommu table \n " ,
pci_name ( dev ) ) ;
2005-04-16 15:20:36 -07:00
}
2006-11-11 17:25:02 +11:00
static void pci_dma_dev_setup_pSeriesLP ( struct pci_dev * dev )
2005-04-16 15:20:36 -07:00
{
struct device_node * pdn , * dn ;
struct iommu_table * tbl ;
2006-07-12 15:39:43 +10:00
const void * dma_window = NULL ;
2005-09-06 13:17:54 +10:00
struct pci_dn * pci ;
2005-04-16 15:20:36 -07:00
2006-11-11 17:25:02 +11:00
DBG ( " pci_dma_dev_setup_pSeriesLP: %s \n " , pci_name ( dev ) ) ;
2005-04-16 15:20:36 -07:00
/* dev setup for LPAR is a little tricky, since the device tree might
* contain the dma - window properties per - device and not neccesarily
* for the bus . So we need to search upwards in the tree until we
* either hit a dma - window property , OR find a parent with a table
* already allocated .
*/
dn = pci_device_to_OF_node ( dev ) ;
2006-11-11 17:25:02 +11:00
DBG ( " node is %s \n " , dn - > full_name ) ;
2006-10-30 16:15:59 +11:00
2005-12-05 19:37:35 -06:00
for ( pdn = dn ; pdn & & PCI_DN ( pdn ) & & ! PCI_DN ( pdn ) - > iommu_table ;
2005-09-06 13:17:54 +10:00
pdn = pdn - > parent ) {
2007-04-03 22:26:41 +10:00
dma_window = of_get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( dma_window )
break ;
}
2007-04-11 06:11:23 +10:00
if ( ! pdn | | ! PCI_DN ( pdn ) ) {
printk ( KERN_WARNING " pci_dma_dev_setup_pSeriesLP: "
" no DMA window found for pci dev=%s dn=%s \n " ,
pci_name ( dev ) , dn ? dn - > full_name : " <null> " ) ;
return ;
}
2006-11-11 17:25:02 +11:00
DBG ( " parent is %s \n " , pdn - > full_name ) ;
2005-04-16 15:20:36 -07:00
/* Check for parent == NULL so we don't try to setup the empty EADS
* slots on POWER4 machines .
*/
if ( dma_window = = NULL | | pdn - > parent = = NULL ) {
2006-11-11 17:25:02 +11:00
DBG ( " no dma window for device, linking to parent \n " ) ;
dev - > dev . archdata . dma_data = PCI_DN ( pdn ) - > iommu_table ;
2005-04-16 15:20:36 -07:00
return ;
}
2005-12-05 19:37:35 -06:00
pci = PCI_DN ( pdn ) ;
2005-09-06 13:17:54 +10:00
if ( ! pci - > iommu_table ) {
2005-04-16 15:20:36 -07:00
/* iommu_table_setparms_lpar needs bussubno. */
2005-09-06 13:17:54 +10:00
pci - > bussubno = pci - > phb - > bus - > number ;
2005-04-16 15:20:36 -07:00
2006-06-10 20:58:08 +10:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
pci - > phb - > node ) ;
2005-04-16 15:20:36 -07:00
2005-09-06 13:17:54 +10:00
iommu_table_setparms_lpar ( pci - > phb , pdn , tbl , dma_window ) ;
2005-04-16 15:20:36 -07:00
2006-06-10 20:58:08 +10:00
pci - > iommu_table = iommu_init_table ( tbl , pci - > phb - > node ) ;
2006-11-11 17:25:02 +11:00
DBG ( " created table: %p \n " , pci - > iommu_table ) ;
2007-05-10 15:16:27 +10:00
} else {
DBG ( " found DMA window, table: %p \n " , pci - > iommu_table ) ;
2005-04-16 15:20:36 -07:00
}
2006-11-11 17:25:02 +11:00
dev - > dev . archdata . dma_data = pci - > iommu_table ;
2005-04-16 15:20:36 -07:00
}
2007-03-04 17:04:44 +11:00
# else /* CONFIG_PCI */
# define pci_dma_bus_setup_pSeries NULL
# define pci_dma_dev_setup_pSeries NULL
# define pci_dma_bus_setup_pSeriesLP NULL
# define pci_dma_dev_setup_pSeriesLP NULL
# endif /* !CONFIG_PCI */
static int iommu_reconfig_notifier ( struct notifier_block * nb , unsigned long action , void * node )
{
int err = NOTIFY_OK ;
struct device_node * np = node ;
struct pci_dn * pci = PCI_DN ( np ) ;
switch ( action ) {
case PSERIES_RECONFIG_REMOVE :
if ( pci & & pci - > iommu_table & &
2007-04-03 22:26:41 +10:00
of_get_property ( np , " ibm,dma-window " , NULL ) )
2007-03-04 17:04:44 +11:00
iommu_free_table ( np ) ;
break ;
default :
err = NOTIFY_DONE ;
break ;
}
return err ;
}
static struct notifier_block iommu_reconfig_nb = {
. notifier_call = iommu_reconfig_notifier ,
} ;
2005-04-16 15:20:36 -07:00
/* These are called very early. */
void iommu_init_early_pSeries ( void )
{
2007-04-03 22:26:41 +10:00
if ( of_chosen & & of_get_property ( of_chosen , " linux,iommu-off " , NULL ) ) {
2005-04-16 15:20:36 -07:00
/* Direct I/O, IOMMU off */
2006-11-11 17:25:02 +11:00
ppc_md . pci_dma_dev_setup = NULL ;
ppc_md . pci_dma_bus_setup = NULL ;
2007-03-04 16:58:39 +11:00
set_pci_dma_ops ( & dma_direct_ops ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2006-03-21 20:45:59 +11:00
if ( firmware_has_feature ( FW_FEATURE_LPAR ) ) {
2005-08-03 14:35:25 +10:00
if ( firmware_has_feature ( FW_FEATURE_MULTITCE ) ) {
2005-04-16 15:20:36 -07:00
ppc_md . tce_build = tce_buildmulti_pSeriesLP ;
ppc_md . tce_free = tce_freemulti_pSeriesLP ;
} else {
ppc_md . tce_build = tce_build_pSeriesLP ;
ppc_md . tce_free = tce_free_pSeriesLP ;
}
2006-06-22 23:35:10 -07:00
ppc_md . tce_get = tce_get_pSeriesLP ;
2006-11-11 17:25:02 +11:00
ppc_md . pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP ;
ppc_md . pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP ;
2005-04-16 15:20:36 -07:00
} else {
ppc_md . tce_build = tce_build_pSeries ;
ppc_md . tce_free = tce_free_pSeries ;
2006-06-22 23:35:10 -07:00
ppc_md . tce_get = tce_get_pseries ;
2006-11-11 17:25:02 +11:00
ppc_md . pci_dma_bus_setup = pci_dma_bus_setup_pSeries ;
ppc_md . pci_dma_dev_setup = pci_dma_dev_setup_pSeries ;
2005-04-16 15:20:36 -07:00
}
pSeries_reconfig_notifier_register ( & iommu_reconfig_nb ) ;
2007-03-04 16:58:39 +11:00
set_pci_dma_ops ( & dma_iommu_ops ) ;
2005-04-16 15:20:36 -07:00
}