2008-10-08 15:41:43 +04:00
/*
* Pinmuxed GPIO support for SuperH .
*
* Copyright ( C ) 2008 Magnus Damm
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/bitops.h>
# include <linux/gpio.h>
static int enum_in_range ( pinmux_enum_t enum_id , struct pinmux_range * r )
{
if ( enum_id < r - > begin )
return 0 ;
if ( enum_id > r - > end )
return 0 ;
return 1 ;
}
2008-12-25 12:17:26 +03:00
static unsigned long gpio_read_raw_reg ( unsigned long reg ,
unsigned long reg_width )
{
switch ( reg_width ) {
case 8 :
2009-11-30 06:10:41 +03:00
return __raw_readb ( reg ) ;
2008-12-25 12:17:26 +03:00
case 16 :
2009-11-30 06:10:41 +03:00
return __raw_readw ( reg ) ;
2008-12-25 12:17:26 +03:00
case 32 :
2009-11-30 06:10:41 +03:00
return __raw_readl ( reg ) ;
2008-12-25 12:17:26 +03:00
}
BUG ( ) ;
return 0 ;
}
static void gpio_write_raw_reg ( unsigned long reg ,
unsigned long reg_width ,
unsigned long data )
{
switch ( reg_width ) {
case 8 :
2009-11-30 06:10:41 +03:00
__raw_writeb ( data , reg ) ;
2008-12-25 12:17:26 +03:00
return ;
case 16 :
2009-11-30 06:10:41 +03:00
__raw_writew ( data , reg ) ;
2008-12-25 12:17:26 +03:00
return ;
case 32 :
2009-11-30 06:10:41 +03:00
__raw_writel ( data , reg ) ;
2008-12-25 12:17:26 +03:00
return ;
}
BUG ( ) ;
}
static void gpio_write_bit ( struct pinmux_data_reg * dr ,
unsigned long in_pos , unsigned long value )
{
unsigned long pos ;
pos = dr - > reg_width - ( in_pos + 1 ) ;
2009-12-09 09:51:27 +03:00
pr_debug ( " write_bit addr = %lx, value = %d, pos = %ld, "
2009-11-30 06:15:04 +03:00
" r_width = %ld \n " ,
dr - > reg , ! ! value , pos , dr - > reg_width ) ;
2008-12-25 12:17:26 +03:00
if ( value )
set_bit ( pos , & dr - > reg_shadow ) ;
else
clear_bit ( pos , & dr - > reg_shadow ) ;
gpio_write_raw_reg ( dr - > reg , dr - > reg_width , dr - > reg_shadow ) ;
}
2008-12-25 12:17:18 +03:00
static int gpio_read_reg ( unsigned long reg , unsigned long reg_width ,
unsigned long field_width , unsigned long in_pos )
2008-10-08 15:41:43 +04:00
{
unsigned long data , mask , pos ;
data = 0 ;
mask = ( 1 < < field_width ) - 1 ;
pos = reg_width - ( ( in_pos + 1 ) * field_width ) ;
2009-11-30 06:15:04 +03:00
pr_debug ( " read_reg: addr = %lx, pos = %ld, "
" r_width = %ld, f_width = %ld \n " ,
reg , pos , reg_width , field_width ) ;
2008-10-08 15:41:43 +04:00
2008-12-25 12:17:26 +03:00
data = gpio_read_raw_reg ( reg , reg_width ) ;
2008-12-25 12:17:18 +03:00
return ( data > > pos ) & mask ;
}
static void gpio_write_reg ( unsigned long reg , unsigned long reg_width ,
unsigned long field_width , unsigned long in_pos ,
unsigned long value )
{
unsigned long mask , pos ;
mask = ( 1 < < field_width ) - 1 ;
pos = reg_width - ( ( in_pos + 1 ) * field_width ) ;
2008-10-08 15:41:43 +04:00
2009-11-30 06:15:04 +03:00
pr_debug ( " write_reg addr = %lx, value = %ld, pos = %ld, "
" r_width = %ld, f_width = %ld \n " ,
reg , value , pos , reg_width , field_width ) ;
2008-12-25 12:17:18 +03:00
mask = ~ ( mask < < pos ) ;
value = value < < pos ;
2008-10-08 15:41:43 +04:00
switch ( reg_width ) {
case 8 :
2009-11-30 06:10:41 +03:00
__raw_writeb ( ( __raw_readb ( reg ) & mask ) | value , reg ) ;
2008-10-08 15:41:43 +04:00
break ;
case 16 :
2009-11-30 06:10:41 +03:00
__raw_writew ( ( __raw_readw ( reg ) & mask ) | value , reg ) ;
2008-10-08 15:41:43 +04:00
break ;
case 32 :
2009-11-30 06:10:41 +03:00
__raw_writel ( ( __raw_readl ( reg ) & mask ) | value , reg ) ;
2008-10-08 15:41:43 +04:00
break ;
}
}
2008-12-25 12:17:09 +03:00
static int setup_data_reg ( struct pinmux_info * gpioc , unsigned gpio )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:09 +03:00
struct pinmux_gpio * gpiop = & gpioc - > gpios [ gpio ] ;
2008-10-08 15:41:43 +04:00
struct pinmux_data_reg * data_reg ;
int k , n ;
2008-12-25 12:17:09 +03:00
if ( ! enum_in_range ( gpiop - > enum_id , & gpioc - > data ) )
2008-10-08 15:41:43 +04:00
return - 1 ;
k = 0 ;
while ( 1 ) {
data_reg = gpioc - > data_regs + k ;
if ( ! data_reg - > reg_width )
break ;
for ( n = 0 ; n < data_reg - > reg_width ; n + + ) {
2008-12-25 12:17:09 +03:00
if ( data_reg - > enum_ids [ n ] = = gpiop - > enum_id ) {
gpiop - > flags & = ~ PINMUX_FLAG_DREG ;
gpiop - > flags | = ( k < < PINMUX_FLAG_DREG_SHIFT ) ;
gpiop - > flags & = ~ PINMUX_FLAG_DBIT ;
gpiop - > flags | = ( n < < PINMUX_FLAG_DBIT_SHIFT ) ;
2008-10-08 15:41:43 +04:00
return 0 ;
}
}
k + + ;
}
2008-12-25 12:17:09 +03:00
BUG ( ) ;
2008-10-08 15:41:43 +04:00
return - 1 ;
}
2008-12-25 12:17:26 +03:00
static void setup_data_regs ( struct pinmux_info * gpioc )
{
struct pinmux_data_reg * drp ;
int k ;
for ( k = gpioc - > first_gpio ; k < = gpioc - > last_gpio ; k + + )
setup_data_reg ( gpioc , k ) ;
k = 0 ;
while ( 1 ) {
drp = gpioc - > data_regs + k ;
if ( ! drp - > reg_width )
break ;
drp - > reg_shadow = gpio_read_raw_reg ( drp - > reg , drp - > reg_width ) ;
k + + ;
}
}
2008-12-25 12:17:09 +03:00
static int get_data_reg ( struct pinmux_info * gpioc , unsigned gpio ,
struct pinmux_data_reg * * drp , int * bitp )
{
struct pinmux_gpio * gpiop = & gpioc - > gpios [ gpio ] ;
int k , n ;
if ( ! enum_in_range ( gpiop - > enum_id , & gpioc - > data ) )
return - 1 ;
k = ( gpiop - > flags & PINMUX_FLAG_DREG ) > > PINMUX_FLAG_DREG_SHIFT ;
n = ( gpiop - > flags & PINMUX_FLAG_DBIT ) > > PINMUX_FLAG_DBIT_SHIFT ;
* drp = gpioc - > data_regs + k ;
* bitp = n ;
return 0 ;
}
2008-10-08 15:41:43 +04:00
static int get_config_reg ( struct pinmux_info * gpioc , pinmux_enum_t enum_id ,
struct pinmux_cfg_reg * * crp , int * indexp ,
unsigned long * * cntp )
{
struct pinmux_cfg_reg * config_reg ;
unsigned long r_width , f_width ;
int k , n ;
k = 0 ;
while ( 1 ) {
config_reg = gpioc - > cfg_regs + k ;
r_width = config_reg - > reg_width ;
f_width = config_reg - > field_width ;
if ( ! r_width )
break ;
for ( n = 0 ; n < ( r_width / f_width ) * 1 < < f_width ; n + + ) {
if ( config_reg - > enum_ids [ n ] = = enum_id ) {
* crp = config_reg ;
* indexp = n ;
* cntp = & config_reg - > cnt [ n / ( 1 < < f_width ) ] ;
return 0 ;
}
}
k + + ;
}
return - 1 ;
}
static int get_gpio_enum_id ( struct pinmux_info * gpioc , unsigned gpio ,
int pos , pinmux_enum_t * enum_idp )
{
pinmux_enum_t enum_id = gpioc - > gpios [ gpio ] . enum_id ;
pinmux_enum_t * data = gpioc - > gpio_data ;
int k ;
if ( ! enum_in_range ( enum_id , & gpioc - > data ) ) {
if ( ! enum_in_range ( enum_id , & gpioc - > mark ) ) {
pr_err ( " non data/mark enum_id for gpio %d \n " , gpio ) ;
return - 1 ;
}
}
if ( pos ) {
* enum_idp = data [ pos + 1 ] ;
return pos + 1 ;
}
for ( k = 0 ; k < gpioc - > gpio_data_size ; k + + ) {
if ( data [ k ] = = enum_id ) {
* enum_idp = data [ k + 1 ] ;
return k + 1 ;
}
}
pr_err ( " cannot locate data/mark enum_id for gpio %d \n " , gpio ) ;
return - 1 ;
}
2008-12-25 12:17:18 +03:00
static void write_config_reg ( struct pinmux_info * gpioc ,
struct pinmux_cfg_reg * crp ,
int index )
2008-10-08 15:41:43 +04:00
{
unsigned long ncomb , pos , value ;
ncomb = 1 < < crp - > field_width ;
pos = index / ncomb ;
value = index % ncomb ;
2008-12-25 12:17:18 +03:00
gpio_write_reg ( crp - > reg , crp - > reg_width , crp - > field_width , pos , value ) ;
2008-10-08 15:41:43 +04:00
}
static int check_config_reg ( struct pinmux_info * gpioc ,
struct pinmux_cfg_reg * crp ,
int index )
{
unsigned long ncomb , pos , value ;
ncomb = 1 < < crp - > field_width ;
pos = index / ncomb ;
value = index % ncomb ;
2008-12-25 12:17:18 +03:00
if ( gpio_read_reg ( crp - > reg , crp - > reg_width ,
crp - > field_width , pos ) = = value )
2008-10-08 15:41:43 +04:00
return 0 ;
return - 1 ;
}
enum { GPIO_CFG_DRYRUN , GPIO_CFG_REQ , GPIO_CFG_FREE } ;
2008-12-25 12:17:18 +03:00
static int pinmux_config_gpio ( struct pinmux_info * gpioc , unsigned gpio ,
int pinmux_type , int cfg_mode )
2008-10-08 15:41:43 +04:00
{
struct pinmux_cfg_reg * cr = NULL ;
pinmux_enum_t enum_id ;
struct pinmux_range * range ;
int in_range , pos , index ;
unsigned long * cntp ;
switch ( pinmux_type ) {
case PINMUX_TYPE_FUNCTION :
range = NULL ;
break ;
case PINMUX_TYPE_OUTPUT :
range = & gpioc - > output ;
break ;
case PINMUX_TYPE_INPUT :
range = & gpioc - > input ;
break ;
case PINMUX_TYPE_INPUT_PULLUP :
range = & gpioc - > input_pu ;
break ;
case PINMUX_TYPE_INPUT_PULLDOWN :
range = & gpioc - > input_pd ;
break ;
default :
goto out_err ;
}
pos = 0 ;
enum_id = 0 ;
index = 0 ;
while ( 1 ) {
pos = get_gpio_enum_id ( gpioc , gpio , pos , & enum_id ) ;
if ( pos < = 0 )
goto out_err ;
if ( ! enum_id )
break ;
in_range = enum_in_range ( enum_id , & gpioc - > function ) ;
2008-10-22 13:29:17 +04:00
if ( ! in_range & & range ) {
2008-10-08 15:41:43 +04:00
in_range = enum_in_range ( enum_id , range ) ;
2008-10-22 13:29:17 +04:00
if ( in_range & & enum_id = = range - > force )
continue ;
}
2008-10-08 15:41:43 +04:00
if ( ! in_range )
continue ;
if ( get_config_reg ( gpioc , enum_id , & cr , & index , & cntp ) ! = 0 )
goto out_err ;
switch ( cfg_mode ) {
case GPIO_CFG_DRYRUN :
if ( ! * cntp | | ! check_config_reg ( gpioc , cr , index ) )
continue ;
break ;
case GPIO_CFG_REQ :
2008-12-25 12:17:18 +03:00
write_config_reg ( gpioc , cr , index ) ;
2008-10-08 15:41:43 +04:00
* cntp = * cntp + 1 ;
break ;
case GPIO_CFG_FREE :
* cntp = * cntp - 1 ;
break ;
}
}
return 0 ;
out_err :
return - 1 ;
}
static DEFINE_SPINLOCK ( gpio_lock ) ;
2008-12-25 12:17:34 +03:00
static struct pinmux_info * chip_to_pinmux ( struct gpio_chip * chip )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:34 +03:00
return container_of ( chip , struct pinmux_info , chip ) ;
}
static int sh_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
struct pinmux_info * gpioc = chip_to_pinmux ( chip ) ;
2008-10-08 15:41:43 +04:00
struct pinmux_data_reg * dummy ;
unsigned long flags ;
int i , ret , pinmux_type ;
ret = - EINVAL ;
if ( ! gpioc )
goto err_out ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
2008-12-25 12:17:34 +03:00
if ( ( gpioc - > gpios [ offset ] . flags & PINMUX_FLAG_TYPE ) ! = PINMUX_TYPE_NONE )
2008-10-08 15:41:43 +04:00
goto err_unlock ;
/* setup pin function here if no data is associated with pin */
2008-12-25 12:17:34 +03:00
if ( get_data_reg ( gpioc , offset , & dummy , & i ) ! = 0 )
2008-10-08 15:41:43 +04:00
pinmux_type = PINMUX_TYPE_FUNCTION ;
else
pinmux_type = PINMUX_TYPE_GPIO ;
if ( pinmux_type = = PINMUX_TYPE_FUNCTION ) {
2008-12-25 12:17:34 +03:00
if ( pinmux_config_gpio ( gpioc , offset ,
2008-10-08 15:41:43 +04:00
pinmux_type ,
GPIO_CFG_DRYRUN ) ! = 0 )
goto err_unlock ;
2008-12-25 12:17:34 +03:00
if ( pinmux_config_gpio ( gpioc , offset ,
2008-10-08 15:41:43 +04:00
pinmux_type ,
GPIO_CFG_REQ ) ! = 0 )
BUG ( ) ;
}
2008-12-25 12:17:34 +03:00
gpioc - > gpios [ offset ] . flags & = ~ PINMUX_FLAG_TYPE ;
gpioc - > gpios [ offset ] . flags | = pinmux_type ;
2008-10-08 15:41:43 +04:00
ret = 0 ;
err_unlock :
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
err_out :
return ret ;
}
2008-12-25 12:17:34 +03:00
static void sh_gpio_free ( struct gpio_chip * chip , unsigned offset )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:34 +03:00
struct pinmux_info * gpioc = chip_to_pinmux ( chip ) ;
2008-10-08 15:41:43 +04:00
unsigned long flags ;
int pinmux_type ;
if ( ! gpioc )
return ;
spin_lock_irqsave ( & gpio_lock , flags ) ;
2008-12-25 12:17:34 +03:00
pinmux_type = gpioc - > gpios [ offset ] . flags & PINMUX_FLAG_TYPE ;
pinmux_config_gpio ( gpioc , offset , pinmux_type , GPIO_CFG_FREE ) ;
gpioc - > gpios [ offset ] . flags & = ~ PINMUX_FLAG_TYPE ;
gpioc - > gpios [ offset ] . flags | = PINMUX_TYPE_NONE ;
2008-10-08 15:41:43 +04:00
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
}
static int pinmux_direction ( struct pinmux_info * gpioc ,
unsigned gpio , int new_pinmux_type )
{
2008-12-25 12:17:18 +03:00
int pinmux_type ;
int ret = - EINVAL ;
if ( ! gpioc )
goto err_out ;
2008-10-08 15:41:43 +04:00
pinmux_type = gpioc - > gpios [ gpio ] . flags & PINMUX_FLAG_TYPE ;
switch ( pinmux_type ) {
case PINMUX_TYPE_GPIO :
break ;
case PINMUX_TYPE_OUTPUT :
case PINMUX_TYPE_INPUT :
case PINMUX_TYPE_INPUT_PULLUP :
case PINMUX_TYPE_INPUT_PULLDOWN :
pinmux_config_gpio ( gpioc , gpio , pinmux_type , GPIO_CFG_FREE ) ;
break ;
default :
goto err_out ;
}
if ( pinmux_config_gpio ( gpioc , gpio ,
new_pinmux_type ,
GPIO_CFG_DRYRUN ) ! = 0 )
goto err_out ;
if ( pinmux_config_gpio ( gpioc , gpio ,
new_pinmux_type ,
GPIO_CFG_REQ ) ! = 0 )
BUG ( ) ;
2008-12-25 12:17:09 +03:00
gpioc - > gpios [ gpio ] . flags & = ~ PINMUX_FLAG_TYPE ;
gpioc - > gpios [ gpio ] . flags | = new_pinmux_type ;
2008-10-08 15:41:43 +04:00
ret = 0 ;
err_out :
return ret ;
}
2008-12-25 12:17:34 +03:00
static int sh_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:34 +03:00
struct pinmux_info * gpioc = chip_to_pinmux ( chip ) ;
2008-10-08 15:41:43 +04:00
unsigned long flags ;
2008-12-25 12:17:18 +03:00
int ret ;
2008-10-08 15:41:43 +04:00
spin_lock_irqsave ( & gpio_lock , flags ) ;
2008-12-25 12:17:34 +03:00
ret = pinmux_direction ( gpioc , offset , PINMUX_TYPE_INPUT ) ;
2008-10-08 15:41:43 +04:00
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
2008-12-25 12:17:18 +03:00
2008-10-08 15:41:43 +04:00
return ret ;
}
2008-12-25 12:17:34 +03:00
static void sh_gpio_set_value ( struct pinmux_info * gpioc ,
2008-12-25 12:17:18 +03:00
unsigned gpio , int value )
2008-10-08 15:41:43 +04:00
{
struct pinmux_data_reg * dr = NULL ;
int bit = 0 ;
2008-12-25 12:17:18 +03:00
if ( ! gpioc | | get_data_reg ( gpioc , gpio , & dr , & bit ) ! = 0 )
2008-10-08 15:41:43 +04:00
BUG ( ) ;
else
2008-12-25 12:17:26 +03:00
gpio_write_bit ( dr , bit , value ) ;
2008-10-08 15:41:43 +04:00
}
2008-12-25 12:17:34 +03:00
static int sh_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:34 +03:00
struct pinmux_info * gpioc = chip_to_pinmux ( chip ) ;
2008-10-08 15:41:43 +04:00
unsigned long flags ;
2008-12-25 12:17:18 +03:00
int ret ;
2008-10-08 15:41:43 +04:00
2008-12-25 12:17:34 +03:00
sh_gpio_set_value ( gpioc , offset , value ) ;
2008-12-25 12:17:26 +03:00
spin_lock_irqsave ( & gpio_lock , flags ) ;
2008-12-25 12:17:34 +03:00
ret = pinmux_direction ( gpioc , offset , PINMUX_TYPE_OUTPUT ) ;
2008-10-08 15:41:43 +04:00
spin_unlock_irqrestore ( & gpio_lock , flags ) ;
2008-12-25 12:17:18 +03:00
2008-10-08 15:41:43 +04:00
return ret ;
}
2008-12-25 12:17:34 +03:00
static int sh_gpio_get_value ( struct pinmux_info * gpioc , unsigned gpio )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:18 +03:00
struct pinmux_data_reg * dr = NULL ;
int bit = 0 ;
2008-10-08 15:41:43 +04:00
2008-12-25 12:17:18 +03:00
if ( ! gpioc | | get_data_reg ( gpioc , gpio , & dr , & bit ) ! = 0 ) {
2008-10-08 15:41:43 +04:00
BUG ( ) ;
2008-12-25 12:17:18 +03:00
return 0 ;
2008-10-08 15:41:43 +04:00
}
2008-12-25 12:17:18 +03:00
return gpio_read_reg ( dr - > reg , dr - > reg_width , 1 , bit ) ;
}
2008-12-25 12:17:34 +03:00
static int sh_gpio_get ( struct gpio_chip * chip , unsigned offset )
2008-12-25 12:17:18 +03:00
{
2008-12-25 12:17:34 +03:00
return sh_gpio_get_value ( chip_to_pinmux ( chip ) , offset ) ;
2008-10-08 15:41:43 +04:00
}
2008-12-25 12:17:34 +03:00
static void sh_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
2008-10-08 15:41:43 +04:00
{
2008-12-25 12:17:34 +03:00
sh_gpio_set_value ( chip_to_pinmux ( chip ) , offset , value ) ;
2008-10-08 15:41:43 +04:00
}
int register_pinmux ( struct pinmux_info * pip )
{
2008-12-25 12:17:34 +03:00
struct gpio_chip * chip = & pip - > chip ;
pr_info ( " sh pinmux: %s handling gpio %d -> %d \n " ,
2008-10-08 15:41:43 +04:00
pip - > name , pip - > first_gpio , pip - > last_gpio ) ;
2008-12-25 12:17:34 +03:00
setup_data_regs ( pip ) ;
chip - > request = sh_gpio_request ;
chip - > free = sh_gpio_free ;
chip - > direction_input = sh_gpio_direction_input ;
chip - > get = sh_gpio_get ;
chip - > direction_output = sh_gpio_direction_output ;
chip - > set = sh_gpio_set ;
WARN_ON ( pip - > first_gpio ! = 0 ) ; /* needs testing */
chip - > label = pip - > name ;
chip - > owner = THIS_MODULE ;
chip - > base = pip - > first_gpio ;
chip - > ngpio = ( pip - > last_gpio - pip - > first_gpio ) + 1 ;
return gpiochip_add ( chip ) ;
2008-10-08 15:41:43 +04:00
}