2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2001 Mike Corrigan & Dave Engebretsen , IBM Corporation
*
* Rewrite , cleanup :
*
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 , iSeries - specific parts .
*
*
* 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 .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/types.h>
# include <linux/dma-mapping.h>
# include <linux/list.h>
2006-11-11 17:25:02 +11:00
# include <linux/pci.h>
2005-04-16 15:20:36 -07:00
# include <asm/iommu.h>
2005-09-20 13:45:41 +10:00
# include <asm/tce.h>
2005-04-16 15:20:36 -07:00
# include <asm/machdep.h>
2005-10-14 14:51:42 +10:00
# include <asm/abs_addr.h>
2006-05-19 16:48:47 +10:00
# include <asm/prom.h>
2005-10-14 15:06:10 +10:00
# include <asm/pci-bridge.h>
2005-11-02 11:41:12 +11:00
# include <asm/iseries/hv_call_xm.h>
2006-04-27 17:23:32 +10:00
# include <asm/iseries/iommu.h>
2006-01-12 13:58:19 +11:00
2005-04-16 15:20:36 -07:00
static void tce_build_iSeries ( struct iommu_table * tbl , long index , long npages ,
unsigned long uaddr , enum dma_data_direction direction )
{
u64 rc ;
2006-04-28 22:51:59 -05:00
u64 tce , rpn ;
2005-04-16 15:20:36 -07:00
while ( npages - - ) {
2006-04-28 22:51:59 -05:00
rpn = virt_to_abs ( uaddr ) > > TCE_SHIFT ;
tce = ( rpn & TCE_RPN_MASK ) < < TCE_RPN_SHIFT ;
2005-04-16 15:20:36 -07:00
if ( tbl - > it_type = = TCE_VB ) {
/* Virtual Bus */
2006-04-28 22:51:59 -05:00
tce | = TCE_VALID | TCE_ALLIO ;
2005-04-16 15:20:36 -07:00
if ( direction ! = DMA_TO_DEVICE )
2006-04-28 22:51:59 -05:00
tce | = TCE_VB_WRITE ;
2005-04-16 15:20:36 -07:00
} else {
/* PCI Bus */
2006-04-28 22:51:59 -05:00
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
tce | = TCE_PCI_WRITE ;
2005-04-16 15:20:36 -07:00
}
2006-04-28 22:51:59 -05:00
rc = HvCallXm_setTce ( ( u64 ) tbl - > it_index , ( u64 ) index , tce ) ;
2005-04-16 15:20:36 -07:00
if ( rc )
panic ( " PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx \n " ,
rc ) ;
index + + ;
2005-11-07 11:06:55 +11:00
uaddr + = TCE_PAGE_SIZE ;
2005-04-16 15:20:36 -07:00
}
}
static void tce_free_iSeries ( struct iommu_table * tbl , long index , long npages )
{
u64 rc ;
while ( npages - - ) {
rc = HvCallXm_setTce ( ( u64 ) tbl - > it_index , ( u64 ) index , 0 ) ;
if ( rc )
panic ( " PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx \n " ,
rc ) ;
index + + ;
}
}
2006-07-14 14:25:33 +10:00
/*
* Structure passed to HvCallXm_getTceTableParms
*/
struct iommu_table_cb {
unsigned long itc_busno ; /* Bus number for this tce table */
unsigned long itc_start ; /* Will be NULL for secondary */
unsigned long itc_totalsize ; /* Size (in pages) of whole table */
unsigned long itc_offset ; /* Index into real tce table of the
start of our section */
unsigned long itc_size ; /* Size (in pages) of our section */
unsigned long itc_index ; /* Index of this tce table */
unsigned short itc_maxtables ; /* Max num of tables for partition */
unsigned char itc_virtbus ; /* Flag to indicate virtual bus */
unsigned char itc_slotno ; /* IOA Tce Slot Index */
unsigned char itc_rsvd [ 4 ] ;
} ;
2005-04-16 15:20:36 -07:00
/*
* Call Hv with the architected data structure to get TCE table info .
* info . Put the returned data into the Linux representation of the
* TCE table data .
* The Hardware Tce table comes in three flavors .
* 1. TCE table shared between Buses .
* 2. TCE table per Bus .
* 3. TCE Table per IOA .
*/
2005-11-07 11:06:55 +11:00
void iommu_table_getparms_iSeries ( unsigned long busno ,
unsigned char slotno ,
unsigned char virtbus ,
struct iommu_table * tbl )
2005-04-16 15:20:36 -07:00
{
struct iommu_table_cb * parms ;
2006-12-02 13:26:57 +02:00
parms = kzalloc ( sizeof ( * parms ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( parms = = NULL )
panic ( " PCI_DMA: TCE Table Allocation failed. " ) ;
2005-11-07 11:06:55 +11:00
parms - > itc_busno = busno ;
parms - > itc_slotno = slotno ;
parms - > itc_virtbus = virtbus ;
2005-04-16 15:20:36 -07:00
2005-10-14 14:51:42 +10:00
HvCallXm_getTceTableParms ( iseries_hv_addr ( parms ) ) ;
2005-04-16 15:20:36 -07:00
if ( parms - > itc_size = = 0 )
panic ( " PCI_DMA: parms->size is zero, parms is 0x%p " , parms ) ;
/* itc_size is in pages worth of table, it_size is in # of entries */
2006-10-30 16:15:59 +11:00
tbl - > it_size = ( parms - > itc_size * TCE_PAGE_SIZE ) / TCE_ENTRY_SIZE ;
2005-04-16 15:20:36 -07:00
tbl - > it_busno = parms - > itc_busno ;
2006-10-30 16:15:59 +11:00
tbl - > it_offset = parms - > itc_offset ;
2005-04-16 15:20:36 -07:00
tbl - > it_index = parms - > itc_index ;
tbl - > it_blocksize = 1 ;
2005-11-07 11:06:55 +11:00
tbl - > it_type = virtbus ? TCE_VB : TCE_PCI ;
2005-04-16 15:20:36 -07:00
kfree ( parms ) ;
}
2005-11-07 11:06:55 +11:00
# ifdef CONFIG_PCI
/*
* This function compares the known tables to find an iommu_table
* that has already been built for hardware TCEs .
*/
static struct iommu_table * iommu_table_find ( struct iommu_table * tbl )
{
2006-05-19 16:48:47 +10:00
struct device_node * node ;
for ( node = NULL ; ( node = of_find_all_nodes ( node ) ) ; ) {
struct pci_dn * pdn = PCI_DN ( node ) ;
struct iommu_table * it ;
2005-11-07 11:06:55 +11:00
2006-05-19 16:48:47 +10:00
if ( pdn = = NULL )
continue ;
it = pdn - > iommu_table ;
2005-11-07 11:06:55 +11:00
if ( ( it ! = NULL ) & &
( it - > it_type = = TCE_PCI ) & &
( it - > it_offset = = tbl - > it_offset ) & &
( it - > it_index = = tbl - > it_index ) & &
( it - > it_size = = tbl - > it_size ) )
return it ;
}
return NULL ;
}
2006-11-11 17:25:02 +11:00
void iommu_devnode_init_iSeries ( struct pci_dev * pdev , struct device_node * dn )
2005-04-16 15:20:36 -07:00
{
struct iommu_table * tbl ;
2005-09-28 14:40:40 +10:00
struct pci_dn * pdn = PCI_DN ( dn ) ;
2006-07-12 15:39:42 +10:00
const u32 * lsn = get_property ( dn , " linux,logical-slot-number " , NULL ) ;
2006-05-19 16:51:57 +10:00
BUG_ON ( lsn = = NULL ) ;
2005-04-16 15:20:36 -07:00
tbl = kmalloc ( sizeof ( struct iommu_table ) , GFP_KERNEL ) ;
2006-05-19 16:51:57 +10:00
iommu_table_getparms_iSeries ( pdn - > busno , * lsn , 0 , tbl ) ;
2005-04-16 15:20:36 -07:00
/* Look for existing tce table */
2005-09-28 14:40:40 +10:00
pdn - > iommu_table = iommu_table_find ( tbl ) ;
if ( pdn - > iommu_table = = NULL )
2006-06-10 20:58:08 +10:00
pdn - > iommu_table = iommu_init_table ( tbl , - 1 ) ;
2005-04-16 15:20:36 -07:00
else
kfree ( tbl ) ;
2006-11-11 17:25:02 +11:00
pdev - > dev . archdata . dma_data = pdn - > iommu_table ;
2005-04-16 15:20:36 -07:00
}
2005-06-21 17:15:52 -07:00
# endif
2005-04-16 15:20:36 -07:00
void iommu_init_early_iSeries ( void )
{
ppc_md . tce_build = tce_build_iSeries ;
ppc_md . tce_free = tce_free_iSeries ;
2007-03-04 16:58:39 +11:00
set_pci_dma_ops ( & dma_iommu_ops ) ;
2005-04-16 15:20:36 -07:00
}