2008-05-19 16:53:02 -07:00
/*
2005-04-16 15:20:36 -07:00
* io - unit . c : IO - UNIT specific routines for memory management .
*
* Copyright ( C ) 1997 , 1998 Jakub Jelinek ( jj @ sunsite . mff . cuni . cz )
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/mm.h>
# include <linux/highmem.h> /* pte_offset_map => kmap_atomic */
# include <linux/bitops.h>
2007-05-14 15:44:38 +02:00
# include <linux/scatterlist.h>
2008-08-27 19:54:01 -07:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-16 15:20:36 -07:00
# include <asm/pgalloc.h>
# include <asm/pgtable.h>
# include <asm/io.h>
# include <asm/io-unit.h>
# include <asm/mxcc.h>
# include <asm/cacheflush.h>
# include <asm/tlbflush.h>
# include <asm/dma.h>
2006-11-30 17:11:26 -08:00
# include <asm/oplib.h>
2005-04-16 15:20:36 -07:00
/* #define IOUNIT_DEBUG */
# ifdef IOUNIT_DEBUG
# define IOD(x) printk(x)
# else
# define IOD(x) do { } while (0)
# endif
# define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
# define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
2008-08-27 04:54:04 -07:00
static void __init iounit_iommu_init ( struct of_device * op )
2005-04-16 15:20:36 -07:00
{
struct iounit_struct * iounit ;
2008-08-25 22:47:20 -07:00
iopte_t * xpt , * xptend ;
2005-04-16 15:20:36 -07:00
2006-11-30 17:07:04 -08:00
iounit = kzalloc ( sizeof ( struct iounit_struct ) , GFP_ATOMIC ) ;
2006-11-30 17:11:26 -08:00
if ( ! iounit ) {
prom_printf ( " SUN4D: Cannot alloc iounit, halting. \n " ) ;
prom_halt ( ) ;
}
2005-04-16 15:20:36 -07:00
iounit - > limit [ 0 ] = IOUNIT_BMAP1_START ;
iounit - > limit [ 1 ] = IOUNIT_BMAP2_START ;
iounit - > limit [ 2 ] = IOUNIT_BMAPM_START ;
iounit - > limit [ 3 ] = IOUNIT_BMAPM_END ;
iounit - > rotor [ 1 ] = IOUNIT_BMAP2_START ;
iounit - > rotor [ 2 ] = IOUNIT_BMAPM_START ;
2008-08-25 22:47:20 -07:00
xpt = of_ioremap ( & op - > resource [ 2 ] , 0 , PAGE_SIZE * 16 , " XPT " ) ;
if ( ! xpt ) {
prom_printf ( " SUN4D: Cannot map External Page Table. " ) ;
prom_halt ( ) ;
2005-04-16 15:20:36 -07:00
}
2008-08-25 22:47:20 -07:00
op - > dev . archdata . iommu = iounit ;
2005-04-16 15:20:36 -07:00
iounit - > page_table = xpt ;
2006-07-17 21:40:27 -07:00
spin_lock_init ( & iounit - > lock ) ;
2005-04-16 15:20:36 -07:00
for ( xptend = iounit - > page_table + ( 16 * PAGE_SIZE ) / sizeof ( iopte_t ) ;
xpt < xptend ; )
iopte_val ( * xpt + + ) = 0 ;
}
2008-08-27 04:54:04 -07:00
static int __init iounit_init ( void )
{
extern void sun4d_init_sbi_irq ( void ) ;
struct device_node * dp ;
for_each_node_by_name ( dp , " sbi " ) {
struct of_device * op = of_find_device_by_node ( dp ) ;
iounit_iommu_init ( op ) ;
of_propagate_archdata ( op ) ;
}
sun4d_init_sbi_irq ( ) ;
return 0 ;
}
subsys_initcall ( iounit_init ) ;
2005-04-16 15:20:36 -07:00
/* One has to hold iounit->lock to call this */
static unsigned long iounit_get_area ( struct iounit_struct * iounit , unsigned long vaddr , int size )
{
int i , j , k , npages ;
unsigned long rotor , scan , limit ;
iopte_t iopte ;
npages = ( ( vaddr & ~ PAGE_MASK ) + size + ( PAGE_SIZE - 1 ) ) > > PAGE_SHIFT ;
/* A tiny bit of magic ingredience :) */
switch ( npages ) {
case 1 : i = 0x0231 ; break ;
case 2 : i = 0x0132 ; break ;
default : i = 0x0213 ; break ;
}
IOD ( ( " iounit_get_area(%08lx,%d[%d])= " , vaddr , size , npages ) ) ;
next : j = ( i & 15 ) ;
rotor = iounit - > rotor [ j - 1 ] ;
limit = iounit - > limit [ j ] ;
scan = rotor ;
nexti : scan = find_next_zero_bit ( iounit - > bmap , limit , scan ) ;
if ( scan + npages > limit ) {
if ( limit ! = rotor ) {
limit = rotor ;
scan = iounit - > limit [ j - 1 ] ;
goto nexti ;
}
i > > = 4 ;
if ( ! ( i & 15 ) )
panic ( " iounit_get_area: Couldn't find free iopte slots for (%08lx,%d) \n " , vaddr , size ) ;
goto next ;
}
for ( k = 1 , scan + + ; k < npages ; k + + )
if ( test_bit ( scan + + , iounit - > bmap ) )
goto nexti ;
iounit - > rotor [ j - 1 ] = ( scan < limit ) ? scan : iounit - > limit [ j - 1 ] ;
scan - = npages ;
iopte = MKIOPTE ( __pa ( vaddr & PAGE_MASK ) ) ;
vaddr = IOUNIT_DMA_BASE + ( scan < < PAGE_SHIFT ) + ( vaddr & ~ PAGE_MASK ) ;
for ( k = 0 ; k < npages ; k + + , iopte = __iopte ( iopte_val ( iopte ) + 0x100 ) , scan + + ) {
set_bit ( scan , iounit - > bmap ) ;
iounit - > page_table [ scan ] = iopte ;
}
IOD ( ( " %08lx \n " , vaddr ) ) ;
return vaddr ;
}
2008-08-26 23:00:58 -07:00
static __u32 iounit_get_scsi_one ( struct device * dev , char * vaddr , unsigned long len )
2005-04-16 15:20:36 -07:00
{
2008-08-26 23:00:58 -07:00
struct iounit_struct * iounit = dev - > archdata . iommu ;
2005-04-16 15:20:36 -07:00
unsigned long ret , flags ;
spin_lock_irqsave ( & iounit - > lock , flags ) ;
ret = iounit_get_area ( iounit , ( unsigned long ) vaddr , len ) ;
spin_unlock_irqrestore ( & iounit - > lock , flags ) ;
return ret ;
}
2008-08-26 23:00:58 -07:00
static void iounit_get_scsi_sgl ( struct device * dev , struct scatterlist * sg , int sz )
2005-04-16 15:20:36 -07:00
{
2008-08-26 23:00:58 -07:00
struct iounit_struct * iounit = dev - > archdata . iommu ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
/* FIXME: Cache some resolved pages - often several sg entries are to the same page */
spin_lock_irqsave ( & iounit - > lock , flags ) ;
while ( sz ! = 0 ) {
- - sz ;
2008-12-11 20:24:58 -08:00
sg - > dma_address = iounit_get_area ( iounit , ( unsigned long ) sg_virt ( sg ) , sg - > length ) ;
sg - > dma_length = sg - > length ;
2007-05-14 15:44:38 +02:00
sg = sg_next ( sg ) ;
2005-04-16 15:20:36 -07:00
}
spin_unlock_irqrestore ( & iounit - > lock , flags ) ;
}
2008-08-26 23:00:58 -07:00
static void iounit_release_scsi_one ( struct device * dev , __u32 vaddr , unsigned long len )
2005-04-16 15:20:36 -07:00
{
2008-08-26 23:00:58 -07:00
struct iounit_struct * iounit = dev - > archdata . iommu ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
spin_lock_irqsave ( & iounit - > lock , flags ) ;
len = ( ( vaddr & ~ PAGE_MASK ) + len + ( PAGE_SIZE - 1 ) ) > > PAGE_SHIFT ;
vaddr = ( vaddr - IOUNIT_DMA_BASE ) > > PAGE_SHIFT ;
IOD ( ( " iounit_release %08lx-%08lx \n " , ( long ) vaddr , ( long ) len + vaddr ) ) ;
for ( len + = vaddr ; vaddr < len ; vaddr + + )
clear_bit ( vaddr , iounit - > bmap ) ;
spin_unlock_irqrestore ( & iounit - > lock , flags ) ;
}
2008-08-26 23:00:58 -07:00
static void iounit_release_scsi_sgl ( struct device * dev , struct scatterlist * sg , int sz )
2005-04-16 15:20:36 -07:00
{
2008-08-26 23:00:58 -07:00
struct iounit_struct * iounit = dev - > archdata . iommu ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
unsigned long vaddr , len ;
spin_lock_irqsave ( & iounit - > lock , flags ) ;
while ( sz ! = 0 ) {
- - sz ;
2008-12-11 20:24:58 -08:00
len = ( ( sg - > dma_address & ~ PAGE_MASK ) + sg - > length + ( PAGE_SIZE - 1 ) ) > > PAGE_SHIFT ;
vaddr = ( sg - > dma_address - IOUNIT_DMA_BASE ) > > PAGE_SHIFT ;
2005-04-16 15:20:36 -07:00
IOD ( ( " iounit_release %08lx-%08lx \n " , ( long ) vaddr , ( long ) len + vaddr ) ) ;
for ( len + = vaddr ; vaddr < len ; vaddr + + )
clear_bit ( vaddr , iounit - > bmap ) ;
2007-05-14 15:44:38 +02:00
sg = sg_next ( sg ) ;
2005-04-16 15:20:36 -07:00
}
spin_unlock_irqrestore ( & iounit - > lock , flags ) ;
}
# ifdef CONFIG_SBUS
2008-08-27 18:40:38 -07:00
static int iounit_map_dma_area ( struct device * dev , dma_addr_t * pba , unsigned long va , __u32 addr , int len )
2005-04-16 15:20:36 -07:00
{
2008-08-27 18:40:38 -07:00
struct iounit_struct * iounit = dev - > archdata . iommu ;
2005-04-16 15:20:36 -07:00
unsigned long page , end ;
pgprot_t dvma_prot ;
iopte_t * iopte ;
* pba = addr ;
dvma_prot = __pgprot ( SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV ) ;
end = PAGE_ALIGN ( ( addr + len ) ) ;
while ( addr < end ) {
page = va ;
{
pgd_t * pgdp ;
pmd_t * pmdp ;
pte_t * ptep ;
long i ;
pgdp = pgd_offset ( & init_mm , addr ) ;
pmdp = pmd_offset ( pgdp , addr ) ;
ptep = pte_offset_map ( pmdp , addr ) ;
set_pte ( ptep , mk_pte ( virt_to_page ( page ) , dvma_prot ) ) ;
i = ( ( addr - IOUNIT_DMA_BASE ) > > PAGE_SHIFT ) ;
2008-08-27 18:40:38 -07:00
iopte = ( iopte_t * ) ( iounit - > page_table + i ) ;
* iopte = MKIOPTE ( __pa ( page ) ) ;
2005-04-16 15:20:36 -07:00
}
addr + = PAGE_SIZE ;
va + = PAGE_SIZE ;
}
flush_cache_all ( ) ;
flush_tlb_all ( ) ;
return 0 ;
}
2008-08-27 18:40:38 -07:00
static void iounit_unmap_dma_area ( struct device * dev , unsigned long addr , int len )
2005-04-16 15:20:36 -07:00
{
/* XXX Somebody please fill this in */
}
# endif
static char * iounit_lockarea ( char * vaddr , unsigned long len )
{
/* FIXME: Write this */
return vaddr ;
}
static void iounit_unlockarea ( char * vaddr , unsigned long len )
{
/* FIXME: Write this */
}
void __init ld_mmu_iounit ( void )
{
BTFIXUPSET_CALL ( mmu_lockarea , iounit_lockarea , BTFIXUPCALL_RETO0 ) ;
BTFIXUPSET_CALL ( mmu_unlockarea , iounit_unlockarea , BTFIXUPCALL_NOP ) ;
BTFIXUPSET_CALL ( mmu_get_scsi_one , iounit_get_scsi_one , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( mmu_get_scsi_sgl , iounit_get_scsi_sgl , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( mmu_release_scsi_one , iounit_release_scsi_one , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( mmu_release_scsi_sgl , iounit_release_scsi_sgl , BTFIXUPCALL_NORM ) ;
# ifdef CONFIG_SBUS
BTFIXUPSET_CALL ( mmu_map_dma_area , iounit_map_dma_area , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( mmu_unmap_dma_area , iounit_unmap_dma_area , BTFIXUPCALL_NORM ) ;
# endif
}