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>
2008-10-23 00:39:04 +04:00
# include <linux/crash_dump.h>
2005-04-17 02:20:36 +04:00
# 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
2008-07-23 22:31:16 +04:00
static int tce_build_pSeries ( struct iommu_table * tbl , long index ,
2006-04-29 07:51:59 +04:00
long npages , unsigned long uaddr ,
2008-07-15 23:51:47 +04:00
enum dma_data_direction direction ,
struct dma_attrs * attrs )
2005-04-17 02:20:36 +04:00
{
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 - - ) {
2010-07-12 08:36:09 +04:00
/* can't move this out since we might cross MEMBLOCK 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
}
2008-07-23 22:31:16 +04:00
return 0 ;
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
2008-07-23 22:31:16 +04:00
static void tce_free_pSeriesLP ( struct iommu_table * , long , long ) ;
static void tce_freemulti_pSeriesLP ( struct iommu_table * , long , long ) ;
static int tce_build_pSeriesLP ( struct iommu_table * tbl , long tcenum ,
2005-04-17 02:20:36 +04:00
long npages , unsigned long uaddr ,
2008-07-15 23:51:47 +04:00
enum dma_data_direction direction ,
struct dma_attrs * attrs )
2005-04-17 02:20:36 +04:00
{
2008-07-23 22:31:16 +04:00
u64 rc = 0 ;
2006-04-29 07:51:59 +04:00
u64 proto_tce , tce ;
u64 rpn ;
2008-07-23 22:31:16 +04:00
int ret = 0 ;
long tcenum_start = tcenum , npages_start = npages ;
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 ) ;
2008-07-23 22:31:16 +04:00
if ( unlikely ( rc = = H_NOT_ENOUGH_RESOURCES ) ) {
ret = ( int ) rc ;
tce_free_pSeriesLP ( tbl , tcenum_start ,
( npages_start - ( npages + 1 ) ) ) ;
break ;
}
2005-04-17 02:20:36 +04:00
if ( rc & & printk_ratelimit ( ) ) {
2009-01-06 17:26:03 +03:00
printk ( " tce_build_pSeriesLP: plpar_tce_put failed. rc=%lld \n " , rc ) ;
printk ( " \t index = 0x%llx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%llx \n " , ( u64 ) tcenum ) ;
printk ( " \t tce val = 0x%llx \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
}
2008-07-23 22:31:16 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2010-10-18 11:27:03 +04:00
static DEFINE_PER_CPU ( u64 * , tce_page ) ;
2005-04-17 02:20:36 +04:00
2008-07-23 22:31:16 +04:00
static int tce_buildmulti_pSeriesLP ( struct iommu_table * tbl , long tcenum ,
2005-04-17 02:20:36 +04:00
long npages , unsigned long uaddr ,
2008-07-15 23:51:47 +04:00
enum dma_data_direction direction ,
struct dma_attrs * attrs )
2005-04-17 02:20:36 +04:00
{
2008-07-23 22:31:16 +04:00
u64 rc = 0 ;
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 ;
2008-07-23 22:31:16 +04:00
long tcenum_start = tcenum , npages_start = npages ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2008-05-08 08:27:23 +04:00
if ( npages = = 1 ) {
2008-07-23 22:31:16 +04:00
return tce_build_pSeriesLP ( tbl , tcenum , npages , uaddr ,
direction , attrs ) ;
2008-05-08 08:27:23 +04:00
}
2005-04-17 02:20:36 +04:00
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 */
2008-05-08 08:27:23 +04:00
if ( ! tcep ) {
2008-07-23 22:31:16 +04:00
return tce_build_pSeriesLP ( tbl , tcenum , npages , uaddr ,
2008-07-15 23:51:47 +04:00
direction , attrs ) ;
2008-05-08 08:27:23 +04:00
}
2005-04-17 02:20:36 +04:00
__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 ) ;
2008-07-23 22:31:16 +04:00
if ( unlikely ( rc = = H_NOT_ENOUGH_RESOURCES ) ) {
ret = ( int ) rc ;
tce_freemulti_pSeriesLP ( tbl , tcenum_start ,
( npages_start - ( npages + limit ) ) ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
if ( rc & & printk_ratelimit ( ) ) {
2009-01-06 17:26:03 +03:00
printk ( " tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%lld \n " , rc ) ;
printk ( " \t index = 0x%llx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t npages = 0x%llx \n " , ( u64 ) npages ) ;
printk ( " \t tce[0] val = 0x%llx \n " , tcep [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
2008-07-23 22:31:16 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
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 ( ) ) {
2009-01-06 17:26:03 +03:00
printk ( " tce_free_pSeriesLP: plpar_tce_put failed. rc=%lld \n " , rc ) ;
printk ( " \t index = 0x%llx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%llx \n " , ( u64 ) tcenum ) ;
2005-04-17 02:20:36 +04:00
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 " ) ;
2009-01-06 17:26:03 +03:00
printk ( " \t rc = %lld \n " , rc ) ;
printk ( " \t index = 0x%llx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t npages = 0x%llx \n " , ( u64 ) npages ) ;
2005-04-17 02:20:36 +04:00
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 ( ) ) {
2009-01-06 17:26:03 +03:00
printk ( " tce_get_pSeriesLP: plpar_tce_get failed. rc=%lld \n " , rc ) ;
printk ( " \t index = 0x%llx \n " , ( u64 ) tbl - > it_index ) ;
printk ( " \t tcenum = 0x%llx \n " , ( u64 ) tcenum ) ;
2006-06-23 10:35:10 +04:00
show_stack ( current , ( unsigned long * ) __get_SP ( ) ) ;
}
return tce_ret ;
}
2007-03-04 09:04:44 +03:00
# ifdef CONFIG_PCI
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
2007-12-10 06:33:21 +03:00
node = phb - > dn ;
2005-04-17 02:20:36 +04:00
2007-04-03 16:26:41 +04:00
basep = of_get_property ( node , " linux,tce-base " , NULL ) ;
sizep = of_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
2008-10-23 00:39:04 +04:00
if ( ! is_kdump_kernel ( ) )
2008-10-21 21:38:10 +04:00
memset ( ( void * ) tbl - > it_base , 0 , * sizep ) ;
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 ,
2010-12-09 07:24:01 +03: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 ;
of_parse_dma_window ( dn , dma_window , & tbl - > it_index , & offset , & size ) ;
2005-04-17 02:20:36 +04:00
2010-12-09 07:24:01 +03:00
tbl - > it_busno = phb - > bus - > number ;
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
}
2006-11-11 09:25:02 +03:00
static void pci_dma_bus_setup_pSeries ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
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
2005-09-21 20:55:31 +04:00
dn = pci_bus_to_OF_node ( bus ) ;
2006-11-11 09:25:02 +03:00
2008-04-24 09:13:19 +04:00
pr_debug ( " pci_dma_bus_setup_pSeries: setting up bus %s \n " , dn - > full_name ) ;
2005-09-21 20:55:31 +04: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 09:25:02 +03:00
pci = PCI_DN ( dn ) ;
2005-09-21 20:55:31 +04:00
/* 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
2008-04-24 09:13:19 +04:00
pr_debug ( " 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 ;
2009-06-02 22:21:30 +04:00
pr_debug ( " No ISA/IDE, window size is 0x%llx \n " ,
2008-04-24 09:13:19 +04:00
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 ;
2010-08-11 20:42:48 +04:00
tbl = kzalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
2006-06-10 14:58:08 +04:00
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 ;
2009-06-02 22:21:30 +04:00
pr_debug ( " ISA/IDE, window size is 0x%llx \n " , pci - > phb - > dma_window_size ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-11 09:25:02 +03:00
static void pci_dma_bus_setup_pSeriesLP ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
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
dn = pci_bus_to_OF_node ( bus ) ;
2008-04-24 09:13:19 +04:00
pr_debug ( " pci_dma_bus_setup_pSeriesLP: setting up bus %s \n " ,
dn - > full_name ) ;
2006-11-11 09:25:02 +03:00
2005-04-17 02:20:36 +04:00
/* Find nearest ibm,dma-window, walking up the device tree */
for ( pdn = dn ; pdn ! = NULL ; pdn = pdn - > parent ) {
2007-04-03 16:26:41 +04:00
dma_window = of_get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( dma_window ! = NULL )
break ;
}
if ( dma_window = = NULL ) {
2008-04-24 09:13:19 +04:00
pr_debug ( " no ibm,dma-window property ! \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2005-12-06 04:37:35 +03:00
ppci = PCI_DN ( pdn ) ;
2006-11-11 09:25:02 +03:00
2008-04-24 09:13:19 +04:00
pr_debug ( " parent is %s, iommu_table: 0x%p \n " ,
pdn - > full_name , ppci - > iommu_table ) ;
2006-11-11 09:25:02 +03:00
2005-09-06 07:17:54 +04:00
if ( ! ppci - > iommu_table ) {
2010-08-11 20:42:48 +04:00
tbl = kzalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
2006-06-10 14:58:08 +04:00
ppci - > phb - > node ) ;
2010-12-09 07:24:01 +03:00
iommu_table_setparms_lpar ( ppci - > phb , pdn , tbl , dma_window ) ;
2006-06-10 14:58:08 +04:00
ppci - > iommu_table = iommu_init_table ( tbl , ppci - > phb - > node ) ;
2008-04-24 09:13:19 +04:00
pr_debug ( " created table: %p \n " , ppci - > iommu_table ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-11-11 09:25:02 +03:00
static void pci_dma_dev_setup_pSeries ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2006-11-11 09:25:02 +03:00
struct device_node * dn ;
2005-09-21 20:55:31 +04:00
struct iommu_table * tbl ;
2005-04-17 02:20:36 +04:00
2008-04-24 09:13:19 +04:00
pr_debug ( " pci_dma_dev_setup_pSeries: %s \n " , pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
2010-04-14 03:12:56 +04:00
dn = dev - > dev . of_node ;
2005-04-17 02:20:36 +04:00
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 ) {
2006-11-11 09:25:02 +03:00
struct pci_controller * phb = PCI_DN ( dn ) - > phb ;
2008-04-24 09:13:19 +04:00
pr_debug ( " --> first child, no bridge. Allocating iommu table. \n " ) ;
2010-08-11 20:42:48 +04:00
tbl = kzalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
2006-11-11 09:25:02 +03:00
phb - > node ) ;
iommu_table_setparms ( phb , dn , tbl ) ;
2007-01-11 04:16:29 +03:00
PCI_DN ( dn ) - > iommu_table = iommu_init_table ( tbl , phb - > node ) ;
2009-09-21 12:26:35 +04:00
set_iommu_table_base ( & dev - > dev , PCI_DN ( dn ) - > iommu_table ) ;
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 ;
2006-11-11 09:25:02 +03:00
if ( dn & & PCI_DN ( dn ) )
2009-09-21 12:26:35 +04:00
set_iommu_table_base ( & dev - > dev , PCI_DN ( dn ) - > iommu_table ) ;
2006-11-11 09:25:02 +03:00
else
printk ( KERN_WARNING " iommu: Device %s has no iommu table \n " ,
pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-11 09:25:02 +03:00
static void pci_dma_dev_setup_pSeriesLP ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
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
2008-04-24 09:13:19 +04:00
pr_debug ( " pci_dma_dev_setup_pSeriesLP: %s \n " , pci_name ( dev ) ) ;
2006-11-11 09:25:02 +03:00
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 ) ;
2008-04-24 09:13:19 +04:00
pr_debug ( " node is %s \n " , dn - > full_name ) ;
2006-10-30 08:15:59 +03:00
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 ) {
2007-04-03 16:26:41 +04:00
dma_window = of_get_property ( pdn , " ibm,dma-window " , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( dma_window )
break ;
}
2007-04-11 00:11:23 +04: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 ;
}
2008-04-24 09:13:19 +04:00
pr_debug ( " parent is %s \n " , pdn - > full_name ) ;
2006-11-11 09:25:02 +03:00
2005-12-06 04:37:35 +03:00
pci = PCI_DN ( pdn ) ;
2005-09-06 07:17:54 +04:00
if ( ! pci - > iommu_table ) {
2010-08-11 20:42:48 +04:00
tbl = kzalloc_node ( sizeof ( struct iommu_table ) , GFP_KERNEL ,
2006-06-10 14:58:08 +04:00
pci - > phb - > node ) ;
2010-12-09 07:24:01 +03:00
iommu_table_setparms_lpar ( pci - > phb , pdn , tbl , dma_window ) ;
2006-06-10 14:58:08 +04:00
pci - > iommu_table = iommu_init_table ( tbl , pci - > phb - > node ) ;
2008-04-24 09:13:19 +04:00
pr_debug ( " created table: %p \n " , pci - > iommu_table ) ;
2007-05-10 09:16:27 +04:00
} else {
2008-04-24 09:13:19 +04:00
pr_debug ( " found DMA window, table: %p \n " , pci - > iommu_table ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-21 12:26:35 +04:00
set_iommu_table_base ( & dev - > dev , pci - > iommu_table ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-04 09:04:44 +03: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 :
2010-10-26 21:35:13 +04:00
if ( pci & & pci - > iommu_table )
2007-12-06 05:39:19 +03:00
iommu_free_table ( pci - > iommu_table , np - > full_name ) ;
2007-03-04 09:04:44 +03:00
break ;
default :
err = NOTIFY_DONE ;
break ;
}
return err ;
}
static struct notifier_block iommu_reconfig_nb = {
. notifier_call = iommu_reconfig_notifier ,
} ;
2005-04-17 02:20:36 +04:00
/* These are called very early. */
void iommu_init_early_pSeries ( void )
{
2010-10-18 11:27:03 +04:00
if ( of_chosen & & of_get_property ( of_chosen , " linux,iommu-off " , NULL ) )
2005-04-17 02:20:36 +04:00
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 ;
2006-11-11 09:25:02 +03: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-17 02:20:36 +04:00
} 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 ;
2006-11-11 09:25:02 +03: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-17 02:20:36 +04:00
}
pSeries_reconfig_notifier_register ( & iommu_reconfig_nb ) ;
2007-03-04 08:58:39 +03:00
set_pci_dma_ops ( & dma_iommu_ops ) ;
2005-04-17 02:20:36 +04:00
}
2010-09-28 19:33:12 +04:00
static int __init disable_multitce ( char * str )
{
if ( strcmp ( str , " off " ) = = 0 & &
firmware_has_feature ( FW_FEATURE_LPAR ) & &
firmware_has_feature ( FW_FEATURE_MULTITCE ) ) {
printk ( KERN_INFO " Disabling MULTITCE firmware feature \n " ) ;
ppc_md . tce_build = tce_build_pSeriesLP ;
ppc_md . tce_free = tce_free_pSeriesLP ;
powerpc_firmware_features & = ~ FW_FEATURE_MULTITCE ;
}
return 1 ;
}
__setup ( " multitce= " , disable_multitce ) ;