2009-04-23 21:15:04 +01:00
/*
*
* arch / arm / mach - u300 / padmux . c
*
*
* Copyright ( C ) 2009 ST - Ericsson AB
* License terms : GNU General Public License ( GPL ) version 2
* U300 PADMUX functions
2009-08-10 12:52:40 +01:00
* Author : Martin Persson < martin . persson @ stericsson . com >
2009-04-23 21:15:04 +01:00
*/
2009-08-10 12:52:40 +01:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/device.h>
2009-04-23 21:15:04 +01:00
# include <linux/err.h>
2009-08-10 12:52:40 +01:00
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/mutex.h>
# include <linux/string.h>
# include <linux/bug.h>
# include <linux/debugfs.h>
# include <linux/seq_file.h>
2009-04-23 21:15:04 +01:00
# include <mach/u300-regs.h>
# include <mach/syscon.h>
# include "padmux.h"
2009-08-10 12:52:40 +01:00
static DEFINE_MUTEX ( pmx_mutex ) ;
const u32 pmx_registers [ ] = {
( U300_SYSCON_VBASE + U300_SYSCON_PMC1LR ) ,
( U300_SYSCON_VBASE + U300_SYSCON_PMC1HR ) ,
( U300_SYSCON_VBASE + U300_SYSCON_PMC2R ) ,
( U300_SYSCON_VBASE + U300_SYSCON_PMC3R ) ,
( U300_SYSCON_VBASE + U300_SYSCON_PMC4R )
} ;
/* High level functionality */
/* Lazy dog:
* onmask = {
* { " PMC1LR " mask , " PMC1LR " value } ,
* { " PMC1HR " mask , " PMC1HR " value } ,
* { " PMC2R " mask , " PMC2R " value } ,
* { " PMC3R " mask , " PMC3R " value } ,
* { " PMC4R " mask , " PMC4R " value }
* }
*/
static struct pmx mmc_setting = {
. setting = U300_APP_PMX_MMC_SETTING ,
. default_on = false ,
. activated = false ,
. name = " MMC " ,
. onmask = {
{ U300_SYSCON_PMC1LR_MMCSD_MASK ,
U300_SYSCON_PMC1LR_MMCSD_MMCSD } ,
{ 0 , 0 } ,
{ 0 , 0 } ,
{ 0 , 0 } ,
{ U300_SYSCON_PMC4R_APP_MISC_12_MASK ,
U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO }
} ,
} ;
static struct pmx spi_setting = {
. setting = U300_APP_PMX_SPI_SETTING ,
. default_on = false ,
. activated = false ,
. name = " SPI " ,
. onmask = { { 0 , 0 } ,
{ U300_SYSCON_PMC1HR_APP_SPI_2_MASK |
U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK |
U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK ,
U300_SYSCON_PMC1HR_APP_SPI_2_SPI |
U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI |
U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI } ,
{ 0 , 0 } ,
{ 0 , 0 } ,
{ 0 , 0 }
} ,
} ;
/* Available padmux settings */
static struct pmx * pmx_settings [ ] = {
& mmc_setting ,
& spi_setting ,
} ;
static void update_registers ( struct pmx * pmx , bool activate )
{
u16 regval , val , mask ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_registers ) ; i + + ) {
if ( activate )
val = pmx - > onmask [ i ] . val ;
else
val = 0 ;
mask = pmx - > onmask [ i ] . mask ;
if ( mask ! = 0 ) {
regval = readw ( pmx_registers [ i ] ) ;
regval & = ~ mask ;
regval | = val ;
writew ( regval , pmx_registers [ i ] ) ;
}
}
}
struct pmx * pmx_get ( struct device * dev , enum pmx_settings setting )
{
int i ;
struct pmx * pmx = ERR_PTR ( - ENOENT ) ;
if ( dev = = NULL )
return ERR_PTR ( - EINVAL ) ;
mutex_lock ( & pmx_mutex ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
if ( setting = = pmx_settings [ i ] - > setting ) {
if ( pmx_settings [ i ] - > dev ! = NULL ) {
WARN ( 1 , " padmux: required setting "
" in use by another consumer \n " ) ;
} else {
pmx = pmx_settings [ i ] ;
pmx - > dev = dev ;
dev_dbg ( dev , " padmux: setting nr %d is now "
" bound to %s and ready to use \n " ,
setting , dev_name ( dev ) ) ;
break ;
}
}
}
mutex_unlock ( & pmx_mutex ) ;
return pmx ;
}
EXPORT_SYMBOL ( pmx_get ) ;
int pmx_put ( struct device * dev , struct pmx * pmx )
{
int i ;
int ret = - ENOENT ;
if ( pmx = = NULL | | dev = = NULL )
return - EINVAL ;
mutex_lock ( & pmx_mutex ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
if ( pmx - > setting = = pmx_settings [ i ] - > setting ) {
if ( dev ! = pmx - > dev ) {
WARN ( 1 , " padmux: cannot release handle as "
" it is bound to another consumer \n " ) ;
ret = - EINVAL ;
break ;
} else {
pmx_settings [ i ] - > dev = NULL ;
ret = 0 ;
break ;
}
}
}
mutex_unlock ( & pmx_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( pmx_put ) ;
int pmx_activate ( struct device * dev , struct pmx * pmx )
{
int i , j , ret ;
ret = 0 ;
if ( pmx = = NULL | | dev = = NULL )
return - EINVAL ;
mutex_lock ( & pmx_mutex ) ;
/* Make sure the required bits are not used */
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
if ( pmx_settings [ i ] - > dev = = NULL | | pmx_settings [ i ] = = pmx )
continue ;
for ( j = 0 ; j < ARRAY_SIZE ( pmx_registers ) ; j + + ) {
if ( pmx_settings [ i ] - > onmask [ j ] . mask & pmx - >
onmask [ j ] . mask ) {
/* More than one entry on the same bits */
WARN ( 1 , " padmux: cannot activate "
" setting. Bit conflict with "
" an active setting \n " ) ;
ret = - EUSERS ;
goto exit ;
}
}
}
update_registers ( pmx , true ) ;
pmx - > activated = true ;
dev_dbg ( dev , " padmux: setting nr %d is activated \n " ,
pmx - > setting ) ;
exit :
mutex_unlock ( & pmx_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( pmx_activate ) ;
int pmx_deactivate ( struct device * dev , struct pmx * pmx )
{
int i ;
int ret = - ENOENT ;
if ( pmx = = NULL | | dev = = NULL )
return - EINVAL ;
mutex_lock ( & pmx_mutex ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
if ( pmx_settings [ i ] - > dev = = NULL )
continue ;
if ( pmx - > setting = = pmx_settings [ i ] - > setting ) {
if ( dev ! = pmx - > dev ) {
WARN ( 1 , " padmux: cannot deactivate "
" pmx setting as it was activated "
" by another consumer \n " ) ;
ret = - EBUSY ;
continue ;
} else {
update_registers ( pmx , false ) ;
pmx_settings [ i ] - > dev = NULL ;
pmx - > activated = false ;
ret = 0 ;
dev_dbg ( dev , " padmux: setting nr %d is deactivated " ,
pmx - > setting ) ;
break ;
}
}
}
mutex_unlock ( & pmx_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( pmx_deactivate ) ;
/*
* For internal use only . If it is to be exported ,
* it should be reentrant . Notice that pmx_activate
* ( i . e . runtime settings ) always override default settings .
*/
static int pmx_set_default ( void )
{
/* Used to identify several entries on the same bits */
u16 modbits [ ARRAY_SIZE ( pmx_registers ) ] ;
int i , j ;
memset ( modbits , 0 , ARRAY_SIZE ( pmx_registers ) * sizeof ( u16 ) ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
if ( ! pmx_settings [ i ] - > default_on )
continue ;
for ( j = 0 ; j < ARRAY_SIZE ( pmx_registers ) ; j + + ) {
/* Make sure there is only one entry on the same bits */
if ( modbits [ j ] & pmx_settings [ i ] - > onmask [ j ] . mask ) {
BUG ( ) ;
return - EUSERS ;
}
modbits [ j ] | = pmx_settings [ i ] - > onmask [ j ] . mask ;
}
update_registers ( pmx_settings [ i ] , true ) ;
}
return 0 ;
2009-04-23 21:15:04 +01:00
}
2009-08-10 12:52:40 +01:00
# if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG))
static int pmx_show ( struct seq_file * s , void * data )
{
int i ;
seq_printf ( s , " ------------------------------------------------- \n " ) ;
seq_printf ( s , " SETTING BOUND TO DEVICE STATE \n " ) ;
seq_printf ( s , " ------------------------------------------------- \n " ) ;
mutex_lock ( & pmx_mutex ) ;
for ( i = 0 ; i < ARRAY_SIZE ( pmx_settings ) ; i + + ) {
/* Format pmx and device name nicely */
char cdp [ 33 ] ;
int chars ;
chars = snprintf ( & cdp [ 0 ] , 17 , " %s " , pmx_settings [ i ] - > name ) ;
while ( chars < 16 ) {
cdp [ chars ] = ' ' ;
chars + + ;
}
chars = snprintf ( & cdp [ 16 ] , 17 , " %s " , pmx_settings [ i ] - > dev ?
dev_name ( pmx_settings [ i ] - > dev ) : " N/A " ) ;
while ( chars < 16 ) {
cdp [ chars + 16 ] = ' ' ;
chars + + ;
}
cdp [ 32 ] = ' \0 ' ;
seq_printf ( s ,
" %s \t %s \n " ,
& cdp [ 0 ] ,
pmx_settings [ i ] - > activated ?
" ACTIVATED " : " DEACTIVATED "
) ;
}
mutex_unlock ( & pmx_mutex ) ;
return 0 ;
}
static int pmx_open ( struct inode * inode , struct file * file )
{
return single_open ( file , pmx_show , NULL ) ;
}
static const struct file_operations pmx_operations = {
. owner = THIS_MODULE ,
. open = pmx_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int __init init_pmx_read_debugfs ( void )
{
/* Expose a simple debugfs interface to view pmx settings */
( void ) debugfs_create_file ( " padmux " , S_IFREG | S_IRUGO ,
NULL , NULL ,
& pmx_operations ) ;
return 0 ;
}
/*
* This needs to come in after the core_initcall ( ) ,
* because debugfs is not available until
* the subsystems come up .
*/
module_init ( init_pmx_read_debugfs ) ;
# endif
static int __init pmx_init ( void )
{
int ret ;
ret = pmx_set_default ( ) ;
if ( IS_ERR_VALUE ( ret ) )
pr_crit ( " padmux: default settings could not be set \n " ) ;
return 0 ;
}
/* Should be initialized before consumers */
core_initcall ( pmx_init ) ;