2008-01-30 13:33:36 +01:00
/*
2008-03-26 22:39:15 +01:00
* GPIO support for RDC SoC R3210 / R8610
*
* Copyright ( C ) 2007 , Florian Fainelli < florian @ openwrt . org >
* Copyright ( C ) 2008 , Volker Weiss < dev @ tintuc . de >
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
2008-01-30 13:33:36 +01:00
*
*/
2008-03-26 22:39:15 +01:00
# include <linux/spinlock.h>
2008-01-30 13:33:36 +01:00
# include <linux/io.h>
# include <linux/types.h>
# include <linux/module.h>
2008-03-26 22:39:15 +01:00
# include <asm/gpio.h>
2008-01-30 13:33:36 +01:00
# include <asm/mach-rdc321x/rdc321x_defs.h>
2008-03-26 22:39:15 +01:00
/* spin lock to protect our private copy of GPIO data register plus
the access to PCI conf registers . */
static DEFINE_SPINLOCK ( gpio_lock ) ;
/* copy of GPIO data registers */
static u32 gpio_data_reg1 ;
static u32 gpio_data_reg2 ;
static u32 gpio_request_data [ 2 ] ;
static inline void rdc321x_conf_write ( unsigned addr , u32 value )
2008-01-30 13:33:36 +01:00
{
2008-03-26 22:39:15 +01:00
outl ( ( 1 < < 31 ) | ( 7 < < 11 ) | addr , RDC3210_CFGREG_ADDR ) ;
outl ( value , RDC3210_CFGREG_DATA ) ;
2008-01-30 13:33:36 +01:00
}
2008-03-26 22:39:15 +01:00
static inline void rdc321x_conf_or ( unsigned addr , u32 value )
2008-01-30 13:33:36 +01:00
{
2008-03-26 22:39:15 +01:00
outl ( ( 1 < < 31 ) | ( 7 < < 11 ) | addr , RDC3210_CFGREG_ADDR ) ;
value | = inl ( RDC3210_CFGREG_DATA ) ;
outl ( value , RDC3210_CFGREG_DATA ) ;
2008-01-30 13:33:36 +01:00
}
2008-03-26 22:39:15 +01:00
static inline u32 rdc321x_conf_read ( unsigned addr )
2008-01-30 13:33:36 +01:00
{
2008-03-26 22:39:15 +01:00
outl ( ( 1 < < 31 ) | ( 7 < < 11 ) | addr , RDC3210_CFGREG_ADDR ) ;
return inl ( RDC3210_CFGREG_DATA ) ;
2008-01-30 13:33:36 +01:00
}
2008-03-26 22:39:15 +01:00
/* configure pin as GPIO */
static void rdc321x_configure_gpio ( unsigned gpio )
{
unsigned long flags ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
rdc321x_conf_or ( gpio < 32
? RDC321X_GPIO_CTRL_REG1 : RDC321X_GPIO_CTRL_REG2 ,
1 < < ( gpio & 0x1f ) ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
}
/* initially setup the 2 copies of the gpio data registers.
This function must be called by the platform setup code . */
void __init rdc321x_gpio_setup ( )
{
/* this might not be, what others (BIOS, bootloader, etc.)
wrote to these registers before , but it ' s a good guess . Still
better than just using 0xffffffff . */
gpio_data_reg1 = rdc321x_conf_read ( RDC321X_GPIO_DATA_REG1 ) ;
gpio_data_reg2 = rdc321x_conf_read ( RDC321X_GPIO_DATA_REG2 ) ;
}
/* determine, if gpio number is valid */
static inline int rdc321x_is_gpio ( unsigned gpio )
{
return gpio < = RDC321X_MAX_GPIO ;
}
/* request GPIO */
int rdc_gpio_request ( unsigned gpio , const char * label )
2008-01-30 13:33:36 +01:00
{
2008-03-26 22:39:15 +01:00
unsigned long flags ;
if ( ! rdc321x_is_gpio ( gpio ) )
2008-01-30 13:33:36 +01:00
return - EINVAL ;
2008-03-26 22:39:15 +01:00
spin_lock_irqsave ( & gpio_lock , flags ) ;
if ( gpio_request_data [ ( gpio & 0x20 ) ? 1 : 0 ] & ( 1 < < ( gpio & 0x1f ) ) )
goto inuse ;
gpio_request_data [ ( gpio & 0x20 ) ? 1 : 0 ] | = ( 1 < < ( gpio & 0x1f ) ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
return 0 ;
inuse :
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
return - EINVAL ;
2008-01-30 13:33:36 +01:00
}
2008-03-26 22:39:15 +01:00
EXPORT_SYMBOL ( rdc_gpio_request ) ;
2008-01-30 13:33:36 +01:00
2008-03-26 22:39:15 +01:00
/* release previously-claimed GPIO */
void rdc_gpio_free ( unsigned gpio )
2008-01-30 13:33:36 +01:00
{
2008-03-26 22:39:15 +01:00
unsigned long flags ;
2008-01-30 13:33:36 +01:00
2008-03-26 22:39:15 +01:00
if ( ! rdc321x_is_gpio ( gpio ) )
2008-01-30 13:33:36 +01:00
return ;
2008-03-26 22:39:15 +01:00
spin_lock_irqsave ( & gpio_lock , flags ) ;
gpio_request_data [ ( gpio & 0x20 ) ? 1 : 0 ] & = ~ ( 1 < < ( gpio & 0x1f ) ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
}
EXPORT_SYMBOL ( rdc_gpio_free ) ;
/* read GPIO pin */
int rdc_gpio_get_value ( unsigned gpio )
{
u32 reg ;
unsigned long flags ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
reg = rdc321x_conf_read ( gpio < 32
? RDC321X_GPIO_DATA_REG1 : RDC321X_GPIO_DATA_REG2 ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
2008-01-30 13:33:36 +01:00
2008-03-26 22:39:15 +01:00
return ( 1 < < ( gpio & 0x1f ) ) & reg ? 1 : 0 ;
}
EXPORT_SYMBOL ( rdc_gpio_get_value ) ;
2008-01-30 13:33:36 +01:00
2008-03-26 22:39:15 +01:00
/* set GPIO pin to value */
void rdc_gpio_set_value ( unsigned gpio , int value )
{
unsigned long flags ;
u32 reg ;
reg = 1 < < ( gpio & 0x1f ) ;
if ( gpio < 32 ) {
spin_lock_irqsave ( & gpio_lock , flags ) ;
if ( value )
gpio_data_reg1 | = reg ;
else
gpio_data_reg1 & = ~ reg ;
rdc321x_conf_write ( RDC321X_GPIO_DATA_REG1 , gpio_data_reg1 ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
} else {
spin_lock_irqsave ( & gpio_lock , flags ) ;
if ( value )
gpio_data_reg2 | = reg ;
else
gpio_data_reg2 & = ~ reg ;
rdc321x_conf_write ( RDC321X_GPIO_DATA_REG2 , gpio_data_reg2 ) ;
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
}
2008-01-30 13:33:36 +01:00
}
EXPORT_SYMBOL ( rdc_gpio_set_value ) ;
2008-03-26 22:39:15 +01:00
/* configure GPIO pin as input */
2008-01-30 13:33:36 +01:00
int rdc_gpio_direction_input ( unsigned gpio )
{
2008-03-26 22:39:15 +01:00
if ( ! rdc321x_is_gpio ( gpio ) )
return - EINVAL ;
rdc321x_configure_gpio ( gpio ) ;
2008-01-30 13:33:36 +01:00
return 0 ;
}
EXPORT_SYMBOL ( rdc_gpio_direction_input ) ;
2008-03-26 22:39:15 +01:00
/* configure GPIO pin as output and set value */
2008-01-30 13:33:36 +01:00
int rdc_gpio_direction_output ( unsigned gpio , int value )
{
2008-03-26 22:39:15 +01:00
if ( ! rdc321x_is_gpio ( gpio ) )
return - EINVAL ;
gpio_set_value ( gpio , value ) ;
rdc321x_configure_gpio ( gpio ) ;
2008-01-30 13:33:36 +01:00
return 0 ;
}
EXPORT_SYMBOL ( rdc_gpio_direction_output ) ;