2008-07-05 12:02:50 +04:00
/*
* Copyright 2004 - 2006 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright ( C ) 2008 by Sascha Hauer < kernel @ pengutronix . de >
2009-01-28 17:13:50 +03:00
* Copyright ( C ) 2009 by Valentin Longchamp < valentin . longchamp @ epfl . ch >
2008-07-05 12:02:50 +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 .
* 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 .
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
2011-07-26 13:53:52 +04:00
# include <linux/gpio.h>
2008-07-05 12:02:50 +04:00
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/io.h>
2009-01-28 17:13:50 +03:00
# include <linux/kernel.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
# include <mach/iomux-mx3.h>
2008-07-05 12:02:50 +04:00
/*
* IOMUX register ( base ) addresses
*/
2009-12-16 21:06:12 +03:00
# define IOMUX_BASE MX31_IO_ADDRESS(MX31_IOMUXC_BASE_ADDR)
2008-07-05 12:02:50 +04:00
# define IOMUXINT_OBS1 (IOMUX_BASE + 0x000)
# define IOMUXINT_OBS2 (IOMUX_BASE + 0x004)
# define IOMUXGPR (IOMUX_BASE + 0x008)
# define IOMUXSW_MUX_CTL (IOMUX_BASE + 0x00C)
# define IOMUXSW_PAD_CTL (IOMUX_BASE + 0x154)
static DEFINE_SPINLOCK ( gpio_mux_lock ) ;
# define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3)
2009-01-28 17:13:50 +03:00
unsigned long mxc_pin_alloc_map [ NB_PORTS * 32 / BITS_PER_LONG ] ;
2008-07-05 12:02:50 +04:00
/*
* set the mode for a IOMUX pin .
*/
int mxc_iomux_mode ( unsigned int pin_mode )
{
2008-09-09 12:19:43 +04:00
u32 field , l , mode , ret = 0 ;
void __iomem * reg ;
2008-07-05 12:02:50 +04:00
reg = IOMUXSW_MUX_CTL + ( pin_mode & IOMUX_REG_MASK ) ;
field = pin_mode & 0x3 ;
mode = ( pin_mode & IOMUX_MODE_MASK ) > > IOMUX_MODE_SHIFT ;
spin_lock ( & gpio_mux_lock ) ;
l = __raw_readl ( reg ) ;
l & = ~ ( 0xff < < ( field * 8 ) ) ;
l | = mode < < ( field * 8 ) ;
__raw_writel ( l , reg ) ;
spin_unlock ( & gpio_mux_lock ) ;
return ret ;
}
EXPORT_SYMBOL ( mxc_iomux_mode ) ;
/*
* This function configures the pad value for a IOMUX pin .
*/
void mxc_iomux_set_pad ( enum iomux_pins pin , u32 config )
{
2008-09-09 12:19:43 +04:00
u32 field , l ;
void __iomem * reg ;
2008-07-05 12:02:50 +04:00
2008-11-13 14:20:49 +03:00
pin & = IOMUX_PADNUM_MASK ;
reg = IOMUXSW_PAD_CTL + ( pin + 2 ) / 3 * 4 ;
2008-07-05 12:02:50 +04:00
field = ( pin + 2 ) % 3 ;
2008-11-13 14:20:49 +03:00
pr_debug ( " %s: reg offset = 0x%x, field = %d \n " ,
2008-07-05 12:02:50 +04:00
__func__ , ( pin + 2 ) / 3 , field ) ;
spin_lock ( & gpio_mux_lock ) ;
l = __raw_readl ( reg ) ;
2008-11-13 14:20:49 +03:00
l & = ~ ( 0x1ff < < ( field * 10 ) ) ;
l | = config < < ( field * 10 ) ;
2008-07-05 12:02:50 +04:00
__raw_writel ( l , reg ) ;
spin_unlock ( & gpio_mux_lock ) ;
}
EXPORT_SYMBOL ( mxc_iomux_set_pad ) ;
2009-01-28 17:13:50 +03:00
/*
2009-05-06 13:44:20 +04:00
* allocs a single pin :
2009-01-28 17:13:50 +03:00
* - reserves the pin so that it is not claimed by another driver
* - setups the iomux according to the configuration
*/
2011-03-02 12:59:48 +03:00
int mxc_iomux_alloc_pin ( unsigned int pin , const char * label )
2009-01-28 17:13:50 +03:00
{
unsigned pad = pin & IOMUX_PADNUM_MASK ;
if ( pad > = ( PIN_MAX + 1 ) ) {
printk ( KERN_ERR " mxc_iomux: Attempt to request nonexistant pin %u for \" %s \" \n " ,
pad , label ? label : " ? " ) ;
return - EINVAL ;
}
if ( test_and_set_bit ( pad , mxc_pin_alloc_map ) ) {
printk ( KERN_ERR " mxc_iomux: pin %u already used. Allocation for \" %s \" failed \n " ,
pad , label ? label : " ? " ) ;
2009-05-06 13:44:20 +04:00
return - EBUSY ;
2009-01-28 17:13:50 +03:00
}
mxc_iomux_mode ( pin ) ;
return 0 ;
}
2009-05-06 13:44:20 +04:00
EXPORT_SYMBOL ( mxc_iomux_alloc_pin ) ;
2009-01-28 17:13:50 +03:00
2011-03-02 12:59:48 +03:00
int mxc_iomux_setup_multiple_pins ( const unsigned int * pin_list , unsigned count ,
2009-01-28 17:13:50 +03:00
const char * label )
{
2011-03-02 12:59:48 +03:00
const unsigned int * p = pin_list ;
2009-01-28 17:13:50 +03:00
int i ;
int ret = - EINVAL ;
for ( i = 0 ; i < count ; i + + ) {
2009-05-06 13:44:20 +04:00
ret = mxc_iomux_alloc_pin ( * p , label ) ;
if ( ret )
2009-01-28 17:13:50 +03:00
goto setup_error ;
p + + ;
}
return 0 ;
setup_error :
mxc_iomux_release_multiple_pins ( pin_list , i ) ;
return ret ;
}
EXPORT_SYMBOL ( mxc_iomux_setup_multiple_pins ) ;
2011-03-02 12:59:48 +03:00
void mxc_iomux_release_pin ( unsigned int pin )
2009-01-28 17:13:50 +03:00
{
unsigned pad = pin & IOMUX_PADNUM_MASK ;
if ( pad < ( PIN_MAX + 1 ) )
clear_bit ( pad , mxc_pin_alloc_map ) ;
}
EXPORT_SYMBOL ( mxc_iomux_release_pin ) ;
2011-03-02 12:59:48 +03:00
void mxc_iomux_release_multiple_pins ( const unsigned int * pin_list , int count )
2009-01-28 17:13:50 +03:00
{
2011-03-02 12:59:48 +03:00
const unsigned int * p = pin_list ;
2009-01-28 17:13:50 +03:00
int i ;
for ( i = 0 ; i < count ; i + + ) {
mxc_iomux_release_pin ( * p ) ;
p + + ;
}
}
EXPORT_SYMBOL ( mxc_iomux_release_multiple_pins ) ;
2008-07-05 12:02:50 +04:00
/*
* This function enables / disables the general purpose function for a particular
* signal .
*/
void mxc_iomux_set_gpr ( enum iomux_gp_func gp , bool en )
{
u32 l ;
spin_lock ( & gpio_mux_lock ) ;
l = __raw_readl ( IOMUXGPR ) ;
if ( en )
l | = gp ;
else
l & = ~ gp ;
__raw_writel ( l , IOMUXGPR ) ;
spin_unlock ( & gpio_mux_lock ) ;
}
EXPORT_SYMBOL ( mxc_iomux_set_gpr ) ;