2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2001 Mike Corrigan & Dave Engebretsen , IBM Corporation
*
2006-04-29 07:51:59 +04:00
* Rewrite , cleanup :
2005-04-17 02:20:36 +04:00
*
2005-11-21 11:12:32 +03:00
* Copyright ( C ) 2004 Olof Johansson < olof @ lixom . net > , IBM Corporation
2006-04-29 07:51:59 +04:00
* Copyright ( C ) 2006 Olof Johansson < olof @ lixom . net >
2005-04-17 02:20:36 +04:00
*
* Dynamic DMA mapping support , pSeries - specific parts , both SMP and LPAR .
*
2006-04-29 07:51:59 +04:00
*
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
*
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
*
2005-04-17 02:20:36 +04: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 08:35:25 +04:00
# include <asm/firmware.h>
2005-09-20 07:45:41 +04:00
# include <asm/tce.h>
2005-09-27 20:50:25 +04:00
# include <asm/ppc-pci.h>
2005-11-07 05:18:13 +03:00
# include <asm/udbg.h>
2005-04-17 02:20:36 +04:00
2005-11-03 07:33:31 +03:00
# include "plpar_wrappers.h"
2005-04-17 02:20:36 +04:00
# define DBG(fmt...)
2006-04-29 07:51:59 +04:00
static void tce_build_pSeries ( struct iommu_table * tbl , long index ,
long npages , unsigned long uaddr ,
2005-04-17 02:20:36 +04:00
enum dma_data_direction direction )
{
2006-04-29 07:51:59 +04:00
u64 proto_tce ;
u64 * tcep ;
u64 rpn ;
2005-04-17 02:20:36 +04:00
2006-04-29 07:51:59 +04:00
proto_tce = TCE_PCI_READ ; // Read allowed
2005-04-17 02:20:36 +04:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-29 07:51:59 +04:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-17 02:20:36 +04:00
2006-04-29 07:51:59 +04:00
tcep = ( ( u64 * ) tbl - > it_base ) + index ;
2005-04-17 02:20:36 +04:00
while ( npages - - ) {
/* can't move this out since we might cross LMB boundary */
2006-04-29 07:51:59 +04:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
* tcep = proto_tce | ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
2005-04-17 02:20:36 +04:00
2005-09-20 07:46:44 +04:00
uaddr + = TCE_PAGE_SIZE ;
2006-04-29 07:51:59 +04:00
tcep + + ;
2005-04-17 02:20:36 +04:00
}
}
static void tce_free_pSeries ( struct iommu_table * tbl , long index , long npages )
{
2006-04-29 07:51:59 +04:00
u64 * tcep ;
2005-04-17 02:20:36 +04:00
2006-04-29 07:51:59 +04:00
tcep = ( ( u64 * ) tbl - > it_base ) + index ;
while ( npages - - )
* ( tcep + + ) = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 10:35:10 +04: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-17 02:20:36 +04: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-29 07:51:59 +04:00
u64 proto_tce , tce ;
u64 rpn ;
2005-04-17 02:20:36 +04:00
2006-04-29 07:51:59 +04:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
proto_tce = TCE_PCI_READ ;
2005-04-17 02:20:36 +04:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-29 07:51:59 +04:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-17 02:20:36 +04:00
while ( npages - - ) {
2006-04-29 07:51:59 +04: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-17 02:20:36 +04: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-29 07:51:59 +04:00
printk ( " \t tce val = 0x%lx \n " , tce ) ;
2005-04-17 02:20:36 +04:00
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
2006-04-29 07:51:59 +04:00
2005-04-17 02:20:36 +04:00
tcenum + + ;
2006-04-29 07:51:59 +04:00
rpn + + ;
2005-04-17 02:20:36 +04:00
}
}
2006-04-29 07:51:59 +04:00
static DEFINE_PER_CPU ( u64 * , tce_page ) = NULL ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
u64 proto_tce ;
u64 * tcep ;
u64 rpn ;
2005-04-17 02:20:36 +04:00
long l , limit ;
2006-10-30 08:15:59 +03:00
if ( npages = = 1 )
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
tcep = ( u64 * ) __get_free_page ( GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
rpn = ( virt_to_abs ( uaddr ) ) > > TCE_SHIFT ;
proto_tce = TCE_PCI_READ ;
2005-04-17 02:20:36 +04:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-29 07:51:59 +04:00
proto_tce | = TCE_PCI_WRITE ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
limit = min_t ( long , npages , 4096 / TCE_ENTRY_SIZE ) ;
2005-04-17 02:20:36 +04:00
for ( l = 0 ; l < limit ; l + + ) {
2006-04-29 07:51:59 +04:00
tcep [ l ] = proto_tce | ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
rpn + + ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
printk ( " \t tce[0] val = 0x%lx \n " , tcep [ 0 ] ) ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
rc = plpar_tce_put ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , 0 ) ;
2005-04-17 02:20:36 +04: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-29 07:51:59 +04:00
rc = plpar_tce_stuff ( ( u64 ) tbl - > it_index , ( u64 ) tcenum < < 12 , 0 , npages ) ;
2005-04-17 02:20:36 +04: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-23 10:35:10 +04: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 ;
}
2005-04-17 02:20:36 +04:00
static void iommu_table_setparms ( struct pci_controller * phb ,
struct device_node * dn ,
2006-04-29 07:51:59 +04:00
struct iommu_table * tbl )
2005-04-17 02:20:36 +04:00
{
struct device_node * node ;
2006-10-05 07:28:00 +04:00
const unsigned long * basep ;
const u32 * sizep ;
2005-04-17 02:20:36 +04:00
node = ( struct device_node * ) phb - > arch_data ;
2006-07-12 09:39:43 +04:00
basep = get_property ( node , " linux,tce-base " , NULL ) ;
sizep = get_property ( node , " linux,tce-size " , NULL ) ;
2005-04-17 02:20:36 +04: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-23 10:35:10 +04:00
# ifndef CONFIG_CRASH_DUMP
2005-04-17 02:20:36 +04:00
memset ( ( void * ) tbl - > it_base , 0 , * sizep ) ;
2006-06-23 10:35:10 +04:00
# endif
2005-04-17 02:20:36 +04:00
tbl - > it_busno = phb - > bus - > number ;
2006-04-29 07:51:59 +04:00
2005-04-17 02:20:36 +04:00
/* Units of tce entries */
2006-10-30 08:15:59 +03:00
tbl - > it_offset = phb - > dma_window_base_cur > > IOMMU_PAGE_SHIFT ;
2006-04-29 07:51:59 +04:00
2005-04-17 02:20:36 +04:00
/* Test if we are going over 2GB of DMA space */
2005-09-21 20:55:31 +04: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-29 07:51:59 +04:00
panic ( " PCI_DMA: Unexpected number of IOAs under this PHB. \n " ) ;
2005-09-21 20:55:31 +04:00
}
2006-04-29 07:51:59 +04:00
2005-04-17 02:20:36 +04:00
phb - > dma_window_base_cur + = phb - > dma_window_size ;
/* Set the tce table size - measured in entries */
2006-10-30 08:15:59 +03:00
tbl - > it_size = phb - > dma_window_size > > IOMMU_PAGE_SHIFT ;
2005-04-17 02:20:36 +04: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 09:39:43 +04:00
const void * dma_window )
2005-04-17 02:20:36 +04:00
{
2006-05-18 12:06:37 +04:00
unsigned long offset , size ;
2005-09-06 07:17:54 +04:00
tbl - > it_busno = PCI_DN ( dn ) - > bussubno ;
2006-05-18 12:06:37 +04:00
of_parse_dma_window ( dn , dma_window , & tbl - > it_index , & offset , & size ) ;
2005-04-17 02:20:36 +04:00
tbl - > it_base = 0 ;
tbl - > it_blocksize = 16 ;
tbl - > it_type = TCE_PCI ;
2006-10-30 08:15:59 +03:00
tbl - > it_offset = offset > > IOMMU_PAGE_SHIFT ;
tbl - > it_size = size > > IOMMU_PAGE_SHIFT ;
2005-04-17 02:20:36 +04:00
}
static void iommu_bus_setup_pSeries ( struct pci_bus * bus )
{
2005-09-21 20:55:31 +04:00
struct device_node * dn ;
2005-04-17 02:20:36 +04:00
struct iommu_table * tbl ;
2005-09-21 20:55:31 +04:00
struct device_node * isa_dn , * isa_dn_orig ;
struct device_node * tmp ;
struct pci_dn * pci ;
int children ;
2005-04-17 02:20:36 +04:00
DBG ( " iommu_bus_setup_pSeries, bus %p, bus->self %p \n " , bus , bus - > self ) ;
2005-09-21 20:55:31 +04:00
dn = pci_bus_to_OF_node ( bus ) ;
pci = PCI_DN ( dn ) ;
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 ;
}
/* Check if the ISA bus on the system is under
* this PHB .
2005-04-17 02:20:36 +04:00
*/
2005-09-21 20:55:31 +04:00
isa_dn = isa_dn_orig = of_find_node_by_type ( NULL , " isa " ) ;
2005-04-17 02:20:36 +04:00
2005-09-21 20:55:31 +04:00
while ( isa_dn & & isa_dn ! = dn )
isa_dn = isa_dn - > parent ;
if ( isa_dn_orig )
of_node_put ( isa_dn_orig ) ;
2005-04-17 02:20:36 +04:00
2006-06-20 12:00:30 +04:00
/* Count number of direct PCI children of the PHB. */
2005-09-21 20:55:31 +04:00
for ( children = 0 , tmp = dn - > child ; tmp ; tmp = tmp - > sibling )
2006-06-20 12:00:30 +04:00
children + + ;
2005-04-17 02:20:36 +04:00
2005-09-21 20:55:31 +04:00
DBG ( " Children: %d \n " , children ) ;
2005-04-17 02:20:36 +04:00
2005-09-21 20:55:31 +04: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-17 02:20:36 +04:00
2005-09-21 20:55:31 +04: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-23 08:44:05 +04:00
DBG ( " No ISA/IDE, window size is 0x%lx \n " ,
pci - > phb - > dma_window_size ) ;
2005-09-21 20:55:31 +04:00
pci - > phb - > dma_window_base_cur = 0 ;
return ;
2005-04-17 02:20:36 +04:00
}
2005-09-21 20:55:31 +04: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 14:58:08 +04:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
pci - > phb - > node ) ;
2005-09-21 20:55:31 +04:00
iommu_table_setparms ( pci - > phb , dn , tbl ) ;
2006-06-10 14:58:08 +04:00
pci - > iommu_table = iommu_init_table ( tbl , pci - > phb - > node ) ;
2005-09-21 20:55:31 +04: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-23 08:44:05 +04:00
DBG ( " ISA/IDE, window size is 0x%lx \n " , pci - > phb - > dma_window_size ) ;
2005-09-21 20:55:31 +04:00
2005-04-17 02:20:36 +04:00
}
static void iommu_bus_setup_pSeriesLP ( struct pci_bus * bus )
{
struct iommu_table * tbl ;
struct device_node * dn , * pdn ;
2005-09-06 07:17:54 +04:00
struct pci_dn * ppci ;
2006-07-12 09:39:43 +04:00
const void * dma_window = NULL ;
2005-04-17 02:20:36 +04:00
DBG ( " iommu_bus_setup_pSeriesLP, bus %p, bus->self %p \n " , bus , bus - > self ) ;
dn = pci_bus_to_OF_node ( bus ) ;
/* Find nearest ibm,dma-window, walking up the device tree */
for ( pdn = dn ; pdn ! = NULL ; pdn = pdn - > parent ) {
2006-05-18 12:06:37 +04:00
dma_window = get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( dma_window ! = NULL )
break ;
}
if ( dma_window = = NULL ) {
DBG ( " iommu_bus_setup_pSeriesLP: bus %s seems to have no ibm,dma-window property \n " , dn - > full_name ) ;
return ;
}
2005-12-06 04:37:35 +03:00
ppci = PCI_DN ( pdn ) ;
2005-09-06 07:17:54 +04:00
if ( ! ppci - > iommu_table ) {
2005-04-17 02:20:36 +04:00
/* Bussubno hasn't been copied yet.
* Do it now because iommu_table_setparms_lpar needs it .
*/
2005-09-06 07:17:54 +04:00
ppci - > bussubno = bus - > number ;
2005-04-17 02:20:36 +04:00
2006-06-10 14:58:08 +04:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
ppci - > phb - > node ) ;
2006-04-29 07:51:59 +04:00
2005-09-06 07:17:54 +04:00
iommu_table_setparms_lpar ( ppci - > phb , pdn , tbl , dma_window ) ;
2005-04-17 02:20:36 +04:00
2006-06-10 14:58:08 +04:00
ppci - > iommu_table = iommu_init_table ( tbl , ppci - > phb - > node ) ;
2005-04-17 02:20:36 +04:00
}
if ( pdn ! = dn )
2005-09-06 07:17:54 +04:00
PCI_DN ( dn ) - > iommu_table = ppci - > iommu_table ;
2005-04-17 02:20:36 +04:00
}
static void iommu_dev_setup_pSeries ( struct pci_dev * dev )
{
struct device_node * dn , * mydn ;
2005-09-21 20:55:31 +04:00
struct iommu_table * tbl ;
2005-04-17 02:20:36 +04:00
2005-09-23 08:44:05 +04:00
DBG ( " iommu_dev_setup_pSeries, dev %p (%s) \n " , dev , pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
mydn = dn = pci_device_to_OF_node ( dev ) ;
2005-09-21 20:55:31 +04: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 ) {
DBG ( " --> first child, no bridge. Allocating iommu table. \n " ) ;
2006-06-10 14:58:08 +04:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
PCI_DN ( dn ) - > phb - > node ) ;
2005-09-21 20:55:31 +04:00
iommu_table_setparms ( PCI_DN ( dn ) - > phb , dn , tbl ) ;
2006-06-10 14:58:08 +04:00
PCI_DN ( dn ) - > iommu_table = iommu_init_table ( tbl ,
PCI_DN ( dn ) - > phb - > node ) ;
2005-09-21 20:55:31 +04: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-06 04:37:35 +03:00
while ( dn & & PCI_DN ( dn ) & & PCI_DN ( dn ) - > iommu_table = = NULL )
2005-04-17 02:20:36 +04:00
dn = dn - > parent ;
2005-12-06 04:37:35 +03:00
if ( dn & & PCI_DN ( dn ) ) {
2005-09-06 07:17:54 +04:00
PCI_DN ( mydn ) - > iommu_table = PCI_DN ( dn ) - > iommu_table ;
2005-04-17 02:20:36 +04:00
} else {
2005-09-23 08:44:05 +04:00
DBG ( " iommu_dev_setup_pSeries, dev %p (%s) has no iommu table \n " , dev , pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
}
}
static int iommu_reconfig_notifier ( struct notifier_block * nb , unsigned long action , void * node )
{
int err = NOTIFY_OK ;
struct device_node * np = node ;
2005-12-06 04:37:35 +03:00
struct pci_dn * pci = PCI_DN ( np ) ;
2005-04-17 02:20:36 +04:00
switch ( action ) {
case PSERIES_RECONFIG_REMOVE :
2005-11-02 19:29:55 +03:00
if ( pci & & pci - > iommu_table & &
2005-04-17 02:20:36 +04:00
get_property ( np , " ibm,dma-window " , NULL ) )
iommu_free_table ( np ) ;
break ;
default :
err = NOTIFY_DONE ;
break ;
}
return err ;
}
static struct notifier_block iommu_reconfig_nb = {
. notifier_call = iommu_reconfig_notifier ,
} ;
static void iommu_dev_setup_pSeriesLP ( struct pci_dev * dev )
{
struct device_node * pdn , * dn ;
struct iommu_table * tbl ;
2006-07-12 09:39:43 +04:00
const void * dma_window = NULL ;
2005-09-06 07:17:54 +04:00
struct pci_dn * pci ;
2005-04-17 02:20:36 +04: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-10-30 08:15:59 +03:00
DBG ( " iommu_dev_setup_pSeriesLP, dev %p (%s) %s \n " ,
dev , pci_name ( dev ) , dn - > full_name ) ;
2005-12-06 04:37:35 +03:00
for ( pdn = dn ; pdn & & PCI_DN ( pdn ) & & ! PCI_DN ( pdn ) - > iommu_table ;
2005-09-06 07:17:54 +04:00
pdn = pdn - > parent ) {
2006-05-18 12:06:37 +04:00
dma_window = get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( dma_window )
break ;
}
/* 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 ) {
2005-09-23 08:44:04 +04:00
DBG ( " No dma window for device, linking to parent \n " ) ;
PCI_DN ( dn ) - > iommu_table = PCI_DN ( pdn ) - > iommu_table ;
2005-04-17 02:20:36 +04:00
return ;
} else {
DBG ( " Found DMA window, allocating table \n " ) ;
}
2005-12-06 04:37:35 +03:00
pci = PCI_DN ( pdn ) ;
2005-09-06 07:17:54 +04:00
if ( ! pci - > iommu_table ) {
2005-04-17 02:20:36 +04:00
/* iommu_table_setparms_lpar needs bussubno. */
2005-09-06 07:17:54 +04:00
pci - > bussubno = pci - > phb - > bus - > number ;
2005-04-17 02:20:36 +04:00
2006-06-10 14:58:08 +04:00
tbl = kmalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
pci - > phb - > node ) ;
2005-04-17 02:20:36 +04:00
2005-09-06 07:17:54 +04:00
iommu_table_setparms_lpar ( pci - > phb , pdn , tbl , dma_window ) ;
2005-04-17 02:20:36 +04:00
2006-06-10 14:58:08 +04:00
pci - > iommu_table = iommu_init_table ( tbl , pci - > phb - > node ) ;
2005-04-17 02:20:36 +04:00
}
if ( pdn ! = dn )
2005-09-06 07:17:54 +04:00
PCI_DN ( dn ) - > iommu_table = pci - > iommu_table ;
2005-04-17 02:20:36 +04:00
}
static void iommu_bus_setup_null ( struct pci_bus * b ) { }
static void iommu_dev_setup_null ( struct pci_dev * d ) { }
/* These are called very early. */
void iommu_init_early_pSeries ( void )
{
if ( of_chosen & & get_property ( of_chosen , " linux,iommu-off " , NULL ) ) {
/* Direct I/O, IOMMU off */
ppc_md . iommu_dev_setup = iommu_dev_setup_null ;
ppc_md . iommu_bus_setup = iommu_bus_setup_null ;
pci_direct_iommu_init ( ) ;
return ;
}
2006-03-21 12:45:59 +03:00
if ( firmware_has_feature ( FW_FEATURE_LPAR ) ) {
2005-08-03 08:35:25 +04:00
if ( firmware_has_feature ( FW_FEATURE_MULTITCE ) ) {
2005-04-17 02:20:36 +04: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-23 10:35:10 +04:00
ppc_md . tce_get = tce_get_pSeriesLP ;
2005-04-17 02:20:36 +04:00
ppc_md . iommu_bus_setup = iommu_bus_setup_pSeriesLP ;
ppc_md . iommu_dev_setup = iommu_dev_setup_pSeriesLP ;
} else {
ppc_md . tce_build = tce_build_pSeries ;
ppc_md . tce_free = tce_free_pSeries ;
2006-06-23 10:35:10 +04:00
ppc_md . tce_get = tce_get_pseries ;
2005-04-17 02:20:36 +04:00
ppc_md . iommu_bus_setup = iommu_bus_setup_pSeries ;
ppc_md . iommu_dev_setup = iommu_dev_setup_pSeries ;
}
pSeries_reconfig_notifier_register ( & iommu_reconfig_nb ) ;
pci_iommu_init ( ) ;
}