2005-04-17 02:20:36 +04:00
/*
2005-12-14 05:10:10 +03:00
* arch / powerpc / sysdev / dart_iommu . c
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
2005-12-14 05:10:10 +03:00
* Copyright ( C ) 2005 Benjamin Herrenschmidt < benh @ kernel . crashing . org > ,
* IBM Corporation
2005-04-17 02:20:36 +04:00
*
* Based on pSeries_iommu . c :
* Copyright ( C ) 2001 Mike Corrigan & Dave Engebretsen , IBM Corporation
2005-11-21 11:12:32 +03:00
* Copyright ( C ) 2004 Olof Johansson < olof @ lixom . net > , IBM Corporation
2005-04-17 02:20:36 +04:00
*
2005-12-14 05:10:10 +03:00
* Dynamic DMA mapping support , Apple U3 , U4 & IBM CPC925 " DART " iommu .
*
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 .
2005-12-14 05:10:10 +03: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 .
2005-12-14 05:10:10 +03: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 <linux/vmalloc.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/iommu.h>
# include <asm/pci-bridge.h>
# include <asm/machdep.h>
# include <asm/abs_addr.h>
# include <asm/cacheflush.h>
# include <asm/lmb.h>
2005-09-27 20:50:25 +04:00
# include <asm/ppc-pci.h>
2005-04-17 02:20:36 +04:00
2005-11-02 07:13:20 +03:00
# include "dart.h"
2005-04-17 02:20:36 +04:00
/* Physical base address and size of the DART table */
unsigned long dart_tablebase ; /* exported to htab_initialize */
static unsigned long dart_tablesize ;
/* Virtual base address of the DART table */
static u32 * dart_vbase ;
/* Mapped base address for the dart */
2006-02-01 15:28:02 +03:00
static unsigned int __iomem * dart ;
2005-04-17 02:20:36 +04:00
/* Dummy val that entries are set to when unused */
static unsigned int dart_emptyval ;
2005-12-14 05:10:10 +03:00
static struct iommu_table iommu_table_dart ;
static int iommu_table_dart_inited ;
2005-04-17 02:20:36 +04:00
static int dart_dirty ;
2005-12-14 05:10:10 +03:00
static int dart_is_u4 ;
2005-04-17 02:20:36 +04:00
# define DBG(...)
static inline void dart_tlb_invalidate_all ( void )
{
unsigned long l = 0 ;
2005-12-14 05:10:10 +03:00
unsigned int reg , inv_bit ;
2005-04-17 02:20:36 +04:00
unsigned long limit ;
DBG ( " dart: flush \n " ) ;
/* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the
* control register and wait for it to clear .
*
* Gotcha : Sometimes , the DART won ' t detect that the bit gets
* set . If so , clear it and set it again .
2005-12-14 05:10:10 +03:00
*/
2005-04-17 02:20:36 +04:00
limit = 0 ;
2005-12-14 05:10:10 +03:00
inv_bit = dart_is_u4 ? DART_CNTL_U4_FLUSHTLB : DART_CNTL_U3_FLUSHTLB ;
2005-04-17 02:20:36 +04:00
retry :
l = 0 ;
2005-12-14 05:10:10 +03:00
reg = DART_IN ( DART_CNTL ) ;
reg | = inv_bit ;
DART_OUT ( DART_CNTL , reg ) ;
while ( ( DART_IN ( DART_CNTL ) & inv_bit ) & & l < ( 1L < < limit ) )
2005-04-17 02:20:36 +04:00
l + + ;
2005-12-14 05:10:10 +03:00
if ( l = = ( 1L < < limit ) ) {
2005-04-17 02:20:36 +04:00
if ( limit < 4 ) {
limit + + ;
2006-06-28 13:50:36 +04:00
reg = DART_IN ( DART_CNTL ) ;
reg & = ~ inv_bit ;
2005-12-14 05:10:10 +03:00
DART_OUT ( DART_CNTL , reg ) ;
2005-04-17 02:20:36 +04:00
goto retry ;
} else
2005-12-14 05:10:10 +03:00
panic ( " DART: TLB did not flush after waiting a long "
2005-04-17 02:20:36 +04:00
" time. Buggy U3 ? " ) ;
}
}
2006-06-28 13:50:36 +04:00
static inline void dart_tlb_invalidate_one ( unsigned long bus_rpn )
{
unsigned int reg ;
unsigned int l , limit ;
reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
( bus_rpn & DART_CNTL_U4_IONE_MASK ) ;
DART_OUT ( DART_CNTL , reg ) ;
limit = 0 ;
wait_more :
l = 0 ;
while ( ( DART_IN ( DART_CNTL ) & DART_CNTL_U4_IONE ) & & l < ( 1L < < limit ) ) {
rmb ( ) ;
l + + ;
}
if ( l = = ( 1L < < limit ) ) {
if ( limit < 4 ) {
limit + + ;
goto wait_more ;
} else
panic ( " DART: TLB did not flush after waiting a long "
" time. Buggy U4 ? " ) ;
}
}
2005-04-17 02:20:36 +04:00
static void dart_flush ( struct iommu_table * tbl )
{
2006-09-13 16:12:52 +04:00
mb ( ) ;
2006-06-28 13:50:36 +04:00
if ( dart_dirty ) {
2005-04-17 02:20:36 +04:00
dart_tlb_invalidate_all ( ) ;
2006-06-28 13:50:36 +04:00
dart_dirty = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2005-12-14 05:10:10 +03:00
static void dart_build ( struct iommu_table * tbl , long index ,
2005-04-17 02:20:36 +04:00
long npages , unsigned long uaddr ,
enum dma_data_direction direction )
{
unsigned int * dp ;
unsigned int rpn ;
2006-06-28 13:50:36 +04:00
long l ;
2005-04-17 02:20:36 +04:00
DBG ( " dart: build at: %lx, %lx, addr: %x \n " , index , npages , uaddr ) ;
dp = ( ( unsigned int * ) tbl - > it_base ) + index ;
2005-12-14 05:10:10 +03:00
2005-04-17 02:20:36 +04:00
/* On U3, all memory is contigous, so we can move this
* out of the loop .
*/
2006-06-28 13:50:36 +04:00
l = npages ;
while ( l - - ) {
2005-09-20 07:46:44 +04:00
rpn = virt_to_abs ( uaddr ) > > DART_PAGE_SHIFT ;
2005-04-17 02:20:36 +04:00
* ( dp + + ) = DARTMAP_VALID | ( rpn & DARTMAP_RPNMASK ) ;
2005-09-20 07:46:44 +04:00
uaddr + = DART_PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 16:12:52 +04:00
/* make sure all updates have reached memory */
mb ( ) ;
in_be32 ( ( unsigned __iomem * ) dp ) ;
mb ( ) ;
2006-06-28 13:50:36 +04:00
if ( dart_is_u4 ) {
rpn = index ;
while ( npages - - )
dart_tlb_invalidate_one ( rpn + + ) ;
} else {
dart_dirty = 1 ;
}
2005-04-17 02:20:36 +04:00
}
static void dart_free ( struct iommu_table * tbl , long index , long npages )
{
unsigned int * dp ;
2005-12-14 05:10:10 +03:00
2005-04-17 02:20:36 +04:00
/* We don't worry about flushing the TLB cache. The only drawback of
* not doing it is that we won ' t catch buggy device drivers doing
* bad DMAs , but then no 32 - bit architecture ever does either .
*/
DBG ( " dart: free at: %lx, %lx \n " , index , npages ) ;
dp = ( ( unsigned int * ) tbl - > it_base ) + index ;
2005-12-14 05:10:10 +03:00
2005-04-17 02:20:36 +04:00
while ( npages - - )
* ( dp + + ) = dart_emptyval ;
}
static int dart_init ( struct device_node * dart_node )
{
unsigned int i ;
2005-12-14 05:10:10 +03:00
unsigned long tmp , base , size ;
struct resource r ;
2005-04-17 02:20:36 +04:00
if ( dart_tablebase = = 0 | | dart_tablesize = = 0 ) {
2005-12-14 05:10:10 +03:00
printk ( KERN_INFO " DART: table not allocated, using "
" direct DMA \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2005-12-14 05:10:10 +03:00
if ( of_address_to_resource ( dart_node , 0 , & r ) )
panic ( " DART: can't get register base ! " ) ;
2005-04-17 02:20:36 +04:00
/* Make sure nothing from the DART range remains in the CPU cache
* from a previous mapping that existed before the kernel took
* over
*/
2005-12-14 05:10:10 +03:00
flush_dcache_phys_range ( dart_tablebase ,
dart_tablebase + dart_tablesize ) ;
2005-04-17 02:20:36 +04:00
/* Allocate a spare page to map all invalid DART pages. We need to do
* that to work around what looks like a problem with the HT bridge
* prefetching into invalid pages and corrupting data
*/
2005-09-20 07:46:44 +04:00
tmp = lmb_alloc ( DART_PAGE_SIZE , DART_PAGE_SIZE ) ;
2005-12-14 05:10:10 +03:00
dart_emptyval = DARTMAP_VALID | ( ( tmp > > DART_PAGE_SHIFT ) &
DARTMAP_RPNMASK ) ;
2005-04-17 02:20:36 +04:00
2005-12-14 05:10:10 +03:00
/* Map in DART registers */
dart = ioremap ( r . start , r . end - r . start + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( dart = = NULL )
2005-12-14 05:10:10 +03:00
panic ( " DART: Cannot map registers! " ) ;
2005-04-17 02:20:36 +04:00
2005-12-14 05:10:10 +03:00
/* Map in DART table */
2005-04-17 02:20:36 +04:00
dart_vbase = ioremap ( virt_to_abs ( dart_tablebase ) , dart_tablesize ) ;
/* Fill initial table */
for ( i = 0 ; i < dart_tablesize / 4 ; i + + )
dart_vbase [ i ] = dart_emptyval ;
/* Initialize DART with table base and enable it. */
2005-12-14 05:10:10 +03:00
base = dart_tablebase > > DART_PAGE_SHIFT ;
size = dart_tablesize > > DART_PAGE_SHIFT ;
if ( dart_is_u4 ) {
2005-12-19 08:49:07 +03:00
size & = DART_SIZE_U4_SIZE_MASK ;
2005-12-14 05:10:10 +03:00
DART_OUT ( DART_BASE_U4 , base ) ;
DART_OUT ( DART_SIZE_U4 , size ) ;
DART_OUT ( DART_CNTL , DART_CNTL_U4_ENABLE ) ;
} else {
2005-12-19 08:49:07 +03:00
size & = DART_CNTL_U3_SIZE_MASK ;
2005-12-14 05:10:10 +03:00
DART_OUT ( DART_CNTL ,
DART_CNTL_U3_ENABLE |
( base < < DART_CNTL_U3_BASE_SHIFT ) |
( size < < DART_CNTL_U3_SIZE_SHIFT ) ) ;
}
2005-04-17 02:20:36 +04:00
/* Invalidate DART to get rid of possible stale TLBs */
dart_tlb_invalidate_all ( ) ;
2005-12-14 05:10:10 +03:00
printk ( KERN_INFO " DART IOMMU initialized for %s type chipset \n " ,
dart_is_u4 ? " U4 " : " U3 " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-12-14 05:10:10 +03:00
static void iommu_table_dart_setup ( void )
2005-04-17 02:20:36 +04:00
{
2005-12-14 05:10:10 +03:00
iommu_table_dart . it_busno = 0 ;
iommu_table_dart . it_offset = 0 ;
2005-04-17 02:20:36 +04:00
/* it_size is in number of entries */
2006-10-30 08:15:59 +03:00
iommu_table_dart . it_size = dart_tablesize / sizeof ( u32 ) ;
2005-04-17 02:20:36 +04:00
/* Initialize the common IOMMU code */
2005-12-14 05:10:10 +03:00
iommu_table_dart . it_base = ( unsigned long ) dart_vbase ;
iommu_table_dart . it_index = 0 ;
iommu_table_dart . it_blocksize = 1 ;
2006-06-10 14:58:08 +04:00
iommu_init_table ( & iommu_table_dart , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Reserve the last page of the DART to avoid possible prefetch
* past the DART mapped area
*/
2005-12-14 05:10:10 +03:00
set_bit ( iommu_table_dart . it_size - 1 , iommu_table_dart . it_map ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-11 09:25:02 +03:00
static void pci_dma_dev_setup_dart ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
/* We only have one iommu table on the mac for now, which makes
* things simple . Setup all PCI devices to point to this table
*/
2006-11-11 09:25:02 +03:00
dev - > dev . archdata . dma_data = & iommu_table_dart ;
2005-04-17 02:20:36 +04:00
}
2006-11-11 09:25:02 +03:00
static void pci_dma_bus_setup_dart ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct device_node * dn ;
2005-12-14 05:10:10 +03:00
if ( ! iommu_table_dart_inited ) {
iommu_table_dart_inited = 1 ;
iommu_table_dart_setup ( ) ;
2005-04-17 02:20:36 +04:00
}
dn = pci_bus_to_OF_node ( bus ) ;
if ( dn )
2005-12-14 05:10:10 +03:00
PCI_DN ( dn ) - > iommu_table = & iommu_table_dart ;
2005-04-17 02:20:36 +04:00
}
2005-12-14 05:10:10 +03:00
void iommu_init_early_dart ( void )
2005-04-17 02:20:36 +04:00
{
struct device_node * dn ;
/* Find the DART in the device-tree */
dn = of_find_compatible_node ( NULL , " dart " , " u3-dart " ) ;
2005-12-14 05:10:10 +03:00
if ( dn = = NULL ) {
dn = of_find_compatible_node ( NULL , " dart " , " u4-dart " ) ;
if ( dn = = NULL )
goto bail ;
dart_is_u4 = 1 ;
}
2005-04-17 02:20:36 +04:00
/* Setup low level TCE operations for the core IOMMU code */
ppc_md . tce_build = dart_build ;
ppc_md . tce_free = dart_free ;
ppc_md . tce_flush = dart_flush ;
/* Initialize the DART HW */
2005-12-14 05:10:10 +03:00
if ( dart_init ( dn ) = = 0 ) {
2006-11-11 09:25:02 +03:00
ppc_md . pci_dma_dev_setup = pci_dma_dev_setup_dart ;
ppc_md . pci_dma_bus_setup = pci_dma_bus_setup_dart ;
2005-04-17 02:20:36 +04:00
/* Setup pci_dma ops */
2006-11-11 09:25:02 +03:00
pci_dma_ops = & dma_iommu_ops ;
2005-12-14 05:10:10 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-12-14 05:10:10 +03:00
bail :
/* If init failed, use direct iommu and null setup functions */
2006-11-11 09:25:02 +03:00
ppc_md . pci_dma_dev_setup = NULL ;
ppc_md . pci_dma_bus_setup = NULL ;
2005-12-14 05:10:10 +03:00
/* Setup pci_dma ops */
2006-11-11 09:25:02 +03:00
pci_dma_ops = & dma_direct_ops ;
2005-04-17 02:20:36 +04:00
}
2005-12-14 05:10:10 +03:00
void __init alloc_dart_table ( void )
2005-04-17 02:20:36 +04:00
{
2006-04-13 06:52:33 +04:00
/* Only reserve DART space if machine has more than 1GB of RAM
2005-04-17 02:20:36 +04:00
* or if requested with iommu = on on cmdline .
2006-04-13 06:52:33 +04:00
*
* 1 GB of RAM is picked as limit because some default devices
* ( i . e . Airport Extreme ) have 30 bit address range limits .
2005-04-17 02:20:36 +04:00
*/
2006-04-13 06:52:33 +04:00
if ( iommu_is_off )
return ;
if ( ! iommu_force_on & & lmb_end_of_DRAM ( ) < = 0x40000000ull )
2005-04-17 02:20:36 +04:00
return ;
/* 512 pages (2MB) is max DART tablesize. */
dart_tablesize = 1UL < < 21 ;
/* 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we
* will blow up an entire large page anyway in the kernel mapping
*/
dart_tablebase = ( unsigned long )
abs_to_virt ( lmb_alloc_base ( 1UL < < 24 , 1UL < < 24 , 0x80000000L ) ) ;
2005-12-14 05:10:10 +03:00
printk ( KERN_INFO " DART table allocated at: %lx \n " , dart_tablebase ) ;
2005-04-17 02:20:36 +04:00
}