2010-04-01 12:31:29 +01:00
/*
* arch / arm / plat - spear / include / plat / padmux . c
*
* SPEAr platform specific gpio pads muxing source file
*
* Copyright ( C ) 2009 ST Microelectronics
* Viresh Kumar < viresh . kumar @ st . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/err.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <plat/padmux.h>
/*
* struct pmx : pmx definition structure
*
* base : base address of configuration registers
* mode_reg : mode configurations
* mux_reg : muxing configurations
* active_mode : pointer to current active mode
*/
struct pmx {
u32 base ;
struct pmx_reg mode_reg ;
struct pmx_reg mux_reg ;
struct pmx_mode * active_mode ;
} ;
static struct pmx * pmx ;
/**
* pmx_mode_set - Enables an multiplexing mode
* @ mode - pointer to pmx mode
*
* It will set mode of operation in hardware .
* Returns - ve on Err otherwise 0
*/
static int pmx_mode_set ( struct pmx_mode * mode )
{
u32 val ;
if ( ! mode - > name )
return - EFAULT ;
pmx - > active_mode = mode ;
val = readl ( pmx - > base + pmx - > mode_reg . offset ) ;
val & = ~ pmx - > mode_reg . mask ;
val | = mode - > mask & pmx - > mode_reg . mask ;
writel ( val , pmx - > base + pmx - > mode_reg . offset ) ;
return 0 ;
}
/**
* pmx_devs_enable - Enables list of devices
* @ devs - pointer to pmx device array
* @ count - number of devices to enable
*
* It will enable pads for all required peripherals once and only once .
* If peripheral is not supported by current mode then request is rejected .
* Conflicts between peripherals are not handled and peripherals will be
* enabled in the order they are present in pmx_dev array .
2010-07-13 05:54:08 -04:00
* In case of conflicts last peripheral enabled will be present .
2010-04-01 12:31:29 +01:00
* Returns - ve on Err otherwise 0
*/
static int pmx_devs_enable ( struct pmx_dev * * devs , u8 count )
{
u32 val , i , mask ;
if ( ! count )
return - EINVAL ;
val = readl ( pmx - > base + pmx - > mux_reg . offset ) ;
for ( i = 0 ; i < count ; i + + ) {
u8 j = 0 ;
if ( ! devs [ i ] - > name | | ! devs [ i ] - > modes ) {
printk ( KERN_ERR " padmux: dev name or modes is null \n " ) ;
continue ;
}
/* check if peripheral exists in active mode */
if ( pmx - > active_mode ) {
bool found = false ;
for ( j = 0 ; j < devs [ i ] - > mode_count ; j + + ) {
if ( devs [ i ] - > modes [ j ] . ids &
pmx - > active_mode - > id ) {
found = true ;
break ;
}
}
if ( found = = false ) {
printk ( KERN_ERR " %s device not available in %s " \
" mode \n " , devs [ i ] - > name ,
pmx - > active_mode - > name ) ;
continue ;
}
}
/* enable peripheral */
mask = devs [ i ] - > modes [ j ] . mask & pmx - > mux_reg . mask ;
if ( devs [ i ] - > enb_on_reset )
val & = ~ mask ;
else
val | = mask ;
devs [ i ] - > is_active = true ;
}
writel ( val , pmx - > base + pmx - > mux_reg . offset ) ;
kfree ( pmx ) ;
/* this will ensure that multiplexing can't be changed now */
pmx = ( struct pmx * ) - 1 ;
return 0 ;
}
/**
* pmx_register - registers a platform requesting pad mux feature
* @ driver - pointer to driver structure containing driver specific parameters
*
* Also this must be called only once . This will allocate memory for pmx
* structure , will call pmx_mode_set , will call pmx_devs_enable .
* Returns - ve on Err otherwise 0
*/
int pmx_register ( struct pmx_driver * driver )
{
int ret = 0 ;
if ( pmx )
return - EPERM ;
if ( ! driver - > base | | ! driver - > devs )
return - EFAULT ;
pmx = kzalloc ( sizeof ( * pmx ) , GFP_KERNEL ) ;
if ( ! pmx )
return - ENOMEM ;
pmx - > base = ( u32 ) driver - > base ;
pmx - > mode_reg . offset = driver - > mode_reg . offset ;
pmx - > mode_reg . mask = driver - > mode_reg . mask ;
pmx - > mux_reg . offset = driver - > mux_reg . offset ;
pmx - > mux_reg . mask = driver - > mux_reg . mask ;
/* choose mode to enable */
if ( driver - > mode ) {
ret = pmx_mode_set ( driver - > mode ) ;
if ( ret )
goto pmx_fail ;
}
ret = pmx_devs_enable ( driver - > devs , driver - > devs_count ) ;
if ( ret )
goto pmx_fail ;
return 0 ;
pmx_fail :
return ret ;
}