2009-01-26 16:13:45 +03:00
/*
* omap iommu : omap2 / 3 architecture specific functions
*
* Copyright ( C ) 2008 - 2009 Nokia Corporation
*
* Written by Hiroshi DOYU < Hiroshi . DOYU @ nokia . com > ,
* Paul Mundt and Toshihiro Kobayashi
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/err.h>
# include <linux/device.h>
# include <linux/jiffies.h>
# include <linux/module.h>
# include <linux/stringify.h>
# include <mach/iommu.h>
/*
* omap2 architecture specific register bit definitions
*/
# define IOMMU_ARCH_VERSION 0x00000011
/* SYSCONF */
# define MMU_SYS_IDLE_SHIFT 3
# define MMU_SYS_IDLE_FORCE (0 << MMU_SYS_IDLE_SHIFT)
# define MMU_SYS_IDLE_NONE (1 << MMU_SYS_IDLE_SHIFT)
# define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT)
# define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT)
# define MMU_SYS_SOFTRESET (1 << 1)
# define MMU_SYS_AUTOIDLE 1
/* SYSSTATUS */
# define MMU_SYS_RESETDONE 1
/* IRQSTATUS & IRQENABLE */
# define MMU_IRQ_MULTIHITFAULT (1 << 4)
# define MMU_IRQ_TABLEWALKFAULT (1 << 3)
# define MMU_IRQ_EMUMISS (1 << 2)
# define MMU_IRQ_TRANSLATIONFAULT (1 << 1)
# define MMU_IRQ_TLBMISS (1 << 0)
# define MMU_IRQ_MASK \
( MMU_IRQ_MULTIHITFAULT | MMU_IRQ_TABLEWALKFAULT | MMU_IRQ_EMUMISS | \
MMU_IRQ_TRANSLATIONFAULT )
/* MMU_CNTL */
# define MMU_CNTL_SHIFT 1
# define MMU_CNTL_MASK (7 << MMU_CNTL_SHIFT)
# define MMU_CNTL_EML_TLB (1 << 3)
# define MMU_CNTL_TWL_EN (1 << 2)
# define MMU_CNTL_MMU_EN (1 << 1)
# define get_cam_va_mask(pgsz) \
( ( ( pgsz ) = = MMU_CAM_PGSZ_16M ) ? 0xff000000 : \
( ( pgsz ) = = MMU_CAM_PGSZ_1M ) ? 0xfff00000 : \
( ( pgsz ) = = MMU_CAM_PGSZ_64K ) ? 0xffff0000 : \
( ( pgsz ) = = MMU_CAM_PGSZ_4K ) ? 0xfffff000 : 0 )
static int omap2_iommu_enable ( struct iommu * obj )
{
u32 l , pa ;
unsigned long timeout ;
if ( ! obj - > iopgd | | ! IS_ALIGNED ( ( u32 ) obj - > iopgd , SZ_16K ) )
return - EINVAL ;
pa = virt_to_phys ( obj - > iopgd ) ;
if ( ! IS_ALIGNED ( pa , SZ_16K ) )
return - EINVAL ;
iommu_write_reg ( obj , MMU_SYS_SOFTRESET , MMU_SYSCONFIG ) ;
timeout = jiffies + msecs_to_jiffies ( 20 ) ;
do {
l = iommu_read_reg ( obj , MMU_SYSSTATUS ) ;
if ( l & MMU_SYS_RESETDONE )
break ;
2009-09-28 20:21:26 +04:00
} while ( ! time_after ( jiffies , timeout ) ) ;
2009-01-26 16:13:45 +03:00
if ( ! ( l & MMU_SYS_RESETDONE ) ) {
dev_err ( obj - > dev , " can't take mmu out of reset \n " ) ;
return - ENODEV ;
}
l = iommu_read_reg ( obj , MMU_REVISION ) ;
dev_info ( obj - > dev , " %s: version %d.%d \n " , obj - > name ,
( l > > 4 ) & 0xf , l & 0xf ) ;
l = iommu_read_reg ( obj , MMU_SYSCONFIG ) ;
l & = ~ MMU_SYS_IDLE_MASK ;
l | = ( MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE ) ;
iommu_write_reg ( obj , l , MMU_SYSCONFIG ) ;
iommu_write_reg ( obj , MMU_IRQ_MASK , MMU_IRQENABLE ) ;
iommu_write_reg ( obj , pa , MMU_TTB ) ;
l = iommu_read_reg ( obj , MMU_CNTL ) ;
l & = ~ MMU_CNTL_MASK ;
l | = ( MMU_CNTL_MMU_EN | MMU_CNTL_TWL_EN ) ;
iommu_write_reg ( obj , l , MMU_CNTL ) ;
return 0 ;
}
static void omap2_iommu_disable ( struct iommu * obj )
{
u32 l = iommu_read_reg ( obj , MMU_CNTL ) ;
l & = ~ MMU_CNTL_MASK ;
iommu_write_reg ( obj , l , MMU_CNTL ) ;
iommu_write_reg ( obj , MMU_SYS_IDLE_FORCE , MMU_SYSCONFIG ) ;
dev_dbg ( obj - > dev , " %s is shutting down \n " , obj - > name ) ;
}
static u32 omap2_iommu_fault_isr ( struct iommu * obj , u32 * ra )
{
int i ;
u32 stat , da ;
const char * err_msg [ ] = {
" tlb miss " ,
" translation fault " ,
" emulation miss " ,
" table walk fault " ,
" multi hit fault " ,
} ;
stat = iommu_read_reg ( obj , MMU_IRQSTATUS ) ;
stat & = MMU_IRQ_MASK ;
if ( ! stat )
return 0 ;
da = iommu_read_reg ( obj , MMU_FAULT_AD ) ;
* ra = da ;
dev_err ( obj - > dev , " %s: \t da:%08x " , __func__ , da ) ;
for ( i = 0 ; i < ARRAY_SIZE ( err_msg ) ; i + + ) {
if ( stat & ( 1 < < i ) )
printk ( " %s " , err_msg [ i ] ) ;
}
printk ( " \n " ) ;
iommu_write_reg ( obj , stat , MMU_IRQSTATUS ) ;
return stat ;
}
static void omap2_tlb_read_cr ( struct iommu * obj , struct cr_regs * cr )
{
cr - > cam = iommu_read_reg ( obj , MMU_READ_CAM ) ;
cr - > ram = iommu_read_reg ( obj , MMU_READ_RAM ) ;
}
static void omap2_tlb_load_cr ( struct iommu * obj , struct cr_regs * cr )
{
iommu_write_reg ( obj , cr - > cam | MMU_CAM_V , MMU_CAM ) ;
iommu_write_reg ( obj , cr - > ram , MMU_RAM ) ;
}
static u32 omap2_cr_to_virt ( struct cr_regs * cr )
{
u32 page_size = cr - > cam & MMU_CAM_PGSZ_MASK ;
u32 mask = get_cam_va_mask ( cr - > cam & page_size ) ;
return cr - > cam & mask ;
}
static struct cr_regs * omap2_alloc_cr ( struct iommu * obj , struct iotlb_entry * e )
{
struct cr_regs * cr ;
if ( e - > da & ~ ( get_cam_va_mask ( e - > pgsz ) ) ) {
dev_err ( obj - > dev , " %s: \t wrong alignment: %08x \n " , __func__ ,
e - > da ) ;
return ERR_PTR ( - EINVAL ) ;
}
cr = kmalloc ( sizeof ( * cr ) , GFP_KERNEL ) ;
if ( ! cr )
return ERR_PTR ( - ENOMEM ) ;
cr - > cam = ( e - > da & MMU_CAM_VATAG_MASK ) | e - > prsvd | e - > pgsz ;
cr - > ram = e - > pa | e - > endian | e - > elsz | e - > mixed ;
return cr ;
}
static inline int omap2_cr_valid ( struct cr_regs * cr )
{
return cr - > cam & MMU_CAM_V ;
}
static u32 omap2_get_pte_attr ( struct iotlb_entry * e )
{
u32 attr ;
attr = e - > mixed < < 5 ;
attr | = e - > endian ;
attr | = e - > elsz > > 3 ;
attr < < = ( ( e - > pgsz & MMU_CAM_PGSZ_4K ) ? 0 : 6 ) ;
return attr ;
}
static ssize_t omap2_dump_cr ( struct iommu * obj , struct cr_regs * cr , char * buf )
{
char * p = buf ;
/* FIXME: Need more detail analysis of cam/ram */
p + = sprintf ( p , " %08x %08x \n " , cr - > cam , cr - > ram ) ;
return p - buf ;
}
# define pr_reg(name) \
2009-08-28 21:54:41 +04:00
do { \
ssize_t bytes ; \
const char * str = " %20s: %08x \n " ; \
const int maxcol = 32 ; \
bytes = snprintf ( p , maxcol , str , __stringify ( name ) , \
iommu_read_reg ( obj , MMU_ # # name ) ) ; \
p + = bytes ; \
len - = bytes ; \
if ( len < maxcol ) \
goto out ; \
} while ( 0 )
static ssize_t omap2_iommu_dump_ctx ( struct iommu * obj , char * buf , ssize_t len )
2009-01-26 16:13:45 +03:00
{
char * p = buf ;
pr_reg ( REVISION ) ;
pr_reg ( SYSCONFIG ) ;
pr_reg ( SYSSTATUS ) ;
pr_reg ( IRQSTATUS ) ;
pr_reg ( IRQENABLE ) ;
pr_reg ( WALKING_ST ) ;
pr_reg ( CNTL ) ;
pr_reg ( FAULT_AD ) ;
pr_reg ( TTB ) ;
pr_reg ( LOCK ) ;
pr_reg ( LD_TLB ) ;
pr_reg ( CAM ) ;
pr_reg ( RAM ) ;
pr_reg ( GFLUSH ) ;
pr_reg ( FLUSH_ENTRY ) ;
pr_reg ( READ_CAM ) ;
pr_reg ( READ_RAM ) ;
pr_reg ( EMU_FAULT_AD ) ;
2009-08-28 21:54:41 +04:00
out :
2009-01-26 16:13:45 +03:00
return p - buf ;
}
static void omap2_iommu_save_ctx ( struct iommu * obj )
{
int i ;
u32 * p = obj - > ctx ;
for ( i = 0 ; i < ( MMU_REG_SIZE / sizeof ( u32 ) ) ; i + + ) {
p [ i ] = iommu_read_reg ( obj , i * sizeof ( u32 ) ) ;
dev_dbg ( obj - > dev , " %s \t [%02d] %08x \n " , __func__ , i , p [ i ] ) ;
}
BUG_ON ( p [ 0 ] ! = IOMMU_ARCH_VERSION ) ;
}
static void omap2_iommu_restore_ctx ( struct iommu * obj )
{
int i ;
u32 * p = obj - > ctx ;
for ( i = 0 ; i < ( MMU_REG_SIZE / sizeof ( u32 ) ) ; i + + ) {
iommu_write_reg ( obj , p [ i ] , i * sizeof ( u32 ) ) ;
dev_dbg ( obj - > dev , " %s \t [%02d] %08x \n " , __func__ , i , p [ i ] ) ;
}
BUG_ON ( p [ 0 ] ! = IOMMU_ARCH_VERSION ) ;
}
static void omap2_cr_to_e ( struct cr_regs * cr , struct iotlb_entry * e )
{
e - > da = cr - > cam & MMU_CAM_VATAG_MASK ;
e - > pa = cr - > ram & MMU_RAM_PADDR_MASK ;
e - > valid = cr - > cam & MMU_CAM_V ;
e - > pgsz = cr - > cam & MMU_CAM_PGSZ_MASK ;
e - > endian = cr - > ram & MMU_RAM_ENDIAN_MASK ;
e - > elsz = cr - > ram & MMU_RAM_ELSZ_MASK ;
e - > mixed = cr - > ram & MMU_RAM_MIXED ;
}
static const struct iommu_functions omap2_iommu_ops = {
. version = IOMMU_ARCH_VERSION ,
. enable = omap2_iommu_enable ,
. disable = omap2_iommu_disable ,
. fault_isr = omap2_iommu_fault_isr ,
. tlb_read_cr = omap2_tlb_read_cr ,
. tlb_load_cr = omap2_tlb_load_cr ,
. cr_to_e = omap2_cr_to_e ,
. cr_to_virt = omap2_cr_to_virt ,
. alloc_cr = omap2_alloc_cr ,
. cr_valid = omap2_cr_valid ,
. dump_cr = omap2_dump_cr ,
. get_pte_attr = omap2_get_pte_attr ,
. save_ctx = omap2_iommu_save_ctx ,
. restore_ctx = omap2_iommu_restore_ctx ,
. dump_ctx = omap2_iommu_dump_ctx ,
} ;
static int __init omap2_iommu_init ( void )
{
return install_iommu_arch ( & omap2_iommu_ops ) ;
}
module_init ( omap2_iommu_init ) ;
static void __exit omap2_iommu_exit ( void )
{
uninstall_iommu_arch ( & omap2_iommu_ops ) ;
}
module_exit ( omap2_iommu_exit ) ;
MODULE_AUTHOR ( " Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi " ) ;
MODULE_DESCRIPTION ( " omap iommu: omap2/3 architecture specific functions " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;