@ -12,10 +12,17 @@
*/
# include <common.h>
# include <dm.h>
# include <malloc.h>
# include <errno.h>
# include <fdtdec.h>
# include <asm/io.h>
# include <asm/bitops.h>
# include <asm/arch/tegra.h>
# include <asm/gpio.h>
# include <dm/device-internal.h>
DECLARE_GLOBAL_DATA_PTR ;
enum {
TEGRA_CMD_INFO ,
@ -24,14 +31,18 @@ enum {
TEGRA_CMD_INPUT ,
} ;
static struct gpio_names {
char name [ GPIO_NAME_SIZE ] ;
} gpio_names [ MAX_NUM_GPIOS ] ;
struct tegra_gpio_platdata {
struct gpio_ctlr_bank * bank ;
const char * port_name ; /* Name of port, e.g. "B" */
int base_gpio ; /* Port number for this port (0, 1,.., n-1) */
} ;
static char * get_name ( int i )
{
return * gpio_names [ i ] . name ? gpio_names [ i ] . name : " UNKNOWN " ;
}
/* Information about each port at run-time */
struct tegra_port_info {
char label [ TEGRA_GPIOS_PER_PORT ] [ GPIO_NAME_SIZE ] ;
struct gpio_ctlr_bank * bank ;
int base_gpio ; /* Port number for this port (0, 1,.., n-1) */
} ;
/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */
static int get_config ( unsigned gpio )
@ -121,38 +132,72 @@ static void set_level(unsigned gpio, int high)
writel ( u , & bank - > gpio_out [ GPIO_PORT ( gpio ) ] ) ;
}
static int check_reserved ( struct udevice * dev , unsigned offset ,
const char * func )
{
struct tegra_port_info * state = dev_get_priv ( dev ) ;
struct gpio_dev_priv * uc_priv = dev - > uclass_priv ;
if ( ! * state - > label [ offset ] ) {
printf ( " tegra_gpio: %s: error: gpio %s%d not reserved \n " ,
func , uc_priv - > bank_name , offset ) ;
return - EBUSY ;
}
return 0 ;
}
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
int tegra_spl_gpio_direction_output ( int gpio , int value )
{
/* Configure as a GPIO */
set_config ( gpio , 1 ) ;
/* Configure GPIO output value. */
set_level ( gpio , value ) ;
/* Configure GPIO direction as output. */
set_direction ( gpio , 1 ) ;
return 0 ;
}
/*
* Generic_GPIO primitives .
*/
int gpio_request ( unsigned gpio , const char * label )
static int tegra_gpio_request ( struct udevice * dev , unsigned offset ,
const char * label )
{
if ( gpio > = MAX_NUM_GPIOS )
return - 1 ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
if ( label ! = NULL ) {
strncpy ( gpio_names [ gpio ] . name , label , GPIO_NAME_SIZE ) ;
gpio_names [ gpio ] . name [ GPIO_NAME_SIZE - 1 ] = ' \0 ' ;
}
if ( * state - > label [ offset ] )
return - EBUSY ;
strncpy ( state - > label [ offset ] , label , GPIO_NAME_SIZE ) ;
state - > label [ offset ] [ GPIO_NAME_SIZE - 1 ] = ' \0 ' ;
/* Configure as a GPIO */
set_config ( gpio , 1 ) ;
set_config ( state - > base_ gpio + offset , 1 ) ;
return 0 ;
}
int gpio_free ( unsigned gpio )
static int tegra_ gpio_free( struct udevice * dev , unsigned offset )
{
if ( gpio > = MAX_NUM_GPIOS )
return - 1 ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int ret ;
ret = check_reserved ( dev , offset , __func__ ) ;
if ( ret )
return ret ;
state - > label [ offset ] [ 0 ] = ' \0 ' ;
gpio_names [ gpio ] . name [ 0 ] = ' \0 ' ;
/* Do not configure as input or change pin mux here */
return 0 ;
}
/* read GPIO OUT value of pin 'gpio' */
static int gpio_get_output_value ( unsigned gpio )
static int tegra_ gpio_get_output_value( unsigned gpio )
{
struct gpio_ctlr * ctlr = ( struct gpio_ctlr * ) NV_PA_GPIO_BASE ;
struct gpio_ctlr_bank * bank = & ctlr - > gpio_bank [ GPIO_BANK ( gpio ) ] ;
@ -166,24 +211,34 @@ static int gpio_get_output_value(unsigned gpio)
return ( val > > GPIO_BIT ( gpio ) ) & 1 ;
}
/* set GPIO pin 'gpio' as an input */
int gpio_direction_input ( unsigned gpi o)
static int tegra_ gpio_direction_input( struct udevice * dev , unsigned offset )
{
debug ( " gpio_direction_input: pin = %d (port %d:bit %d) \n " ,
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) ) ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int ret ;
ret = check_reserved ( dev , offset , __func__ ) ;
if ( ret )
return ret ;
/* Configure GPIO direction as input. */
set_direction ( gpio , 0 ) ;
set_direction ( state - > base_ gpio + offset , 0 ) ;
return 0 ;
}
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
int gpio_direction_output ( unsigned gpio , int value )
static int tegra_gpio_direction_output ( struct udevice * dev , unsigned offset ,
int value )
{
debug ( " gpio_direction_output: pin = %d (port %d:bit %d) = %s \n " ,
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) ,
value ? " HIGH " : " LOW " ) ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int gpio = state - > base_gpio + offset ;
int ret ;
ret = check_reserved ( dev , offset , __func__ ) ;
if ( ret )
return ret ;
/* Configure GPIO output value. */
set_level ( gpio , value ) ;
@ -195,25 +250,38 @@ int gpio_direction_output(unsigned gpio, int value)
}
/* read GPIO IN value of pin 'gpio' */
int gpio_get_value ( unsigned gpi o)
static int tegra_ gpio_get_value( struct udevice * dev , unsigned offset )
{
struct gpio_ctlr * ctlr = ( struct gpio_ctlr * ) NV_PA_GPIO_BASE ;
struct gpio_ctlr_bank * bank = & ctlr - > gpio_bank [ GPIO_BANK ( gpio ) ] ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int gpio = state - > base_gpio + offset ;
int ret ;
int val ;
debug ( " gpio_get_value: pin = %d (port %d:bit %d) \n " ,
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) ) ;
ret = check_reserved ( dev , offset , __func__ ) ;
if ( ret )
return ret ;
debug ( " %s: pin = %d (port %d:bit %d) \n " , __func__ ,
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) ) ;
val = readl ( & bank - > gpio_in [ GPIO_PORT ( gpio ) ] ) ;
val = readl ( & state - > bank - > gpio_in [ GPIO_PORT ( gpio ) ] ) ;
return ( val > > GPIO_BIT ( gpio ) ) & 1 ;
}
/* write GPIO OUT value to pin 'gpio' */
int gpio_set_value ( unsigned gpi o, int value )
static int tegra_ gpio_set_value( struct udevice * dev , unsigned offset , int value )
{
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int gpio = state - > base_gpio + offset ;
int ret ;
ret = check_reserved ( dev , offset , __func__ ) ;
if ( ret )
return ret ;
debug ( " gpio_set_value: pin = %d (port %d:bit %d), value = %d \n " ,
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) , value ) ;
gpio , GPIO_FULLPORT ( gpio ) , GPIO_BIT ( gpio ) , value ) ;
/* Configure GPIO output value. */
set_level ( gpio , value ) ;
@ -241,26 +309,175 @@ void gpio_config_table(const struct tegra_gpio_config *config, int len)
}
}
/*
* Display Tegra GPIO information
static int tegra_gpio_get_function ( struct udevice * dev , unsigned offset )
{
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int gpio = state - > base_gpio + offset ;
if ( ! * state - > label [ offset ] )
return GPIOF_UNUSED ;
if ( ! get_config ( gpio ) )
return GPIOF_FUNC ;
else if ( get_direction ( gpio ) )
return GPIOF_OUTPUT ;
else
return GPIOF_INPUT ;
}
static int tegra_gpio_get_state ( struct udevice * dev , unsigned int offset ,
char * buf , int bufsize )
{
struct gpio_dev_priv * uc_priv = dev - > uclass_priv ;
struct tegra_port_info * state = dev_get_priv ( dev ) ;
int gpio = state - > base_gpio + offset ;
const char * label ;
int is_output ;
int is_gpio ;
int size ;
label = state - > label [ offset ] ;
is_gpio = get_config ( gpio ) ; /* GPIO, not SFPIO */
size = snprintf ( buf , bufsize , " %s%d: " ,
uc_priv - > bank_name ? uc_priv - > bank_name : " " , offset ) ;
buf + = size ;
bufsize - = size ;
if ( is_gpio ) {
is_output = get_direction ( gpio ) ;
snprintf ( buf , bufsize , " %s: %d [%c]%s%s " ,
is_output ? " out " : " in " ,
is_output ?
tegra_gpio_get_output_value ( gpio ) :
tegra_gpio_get_value ( dev , offset ) ,
* label ? ' x ' : ' ' ,
* label ? " " : " " ,
label ) ;
} else {
snprintf ( buf , bufsize , " sfpio " ) ;
}
return 0 ;
}
static const struct dm_gpio_ops gpio_tegra_ops = {
. request = tegra_gpio_request ,
. free = tegra_gpio_free ,
. direction_input = tegra_gpio_direction_input ,
. direction_output = tegra_gpio_direction_output ,
. get_value = tegra_gpio_get_value ,
. set_value = tegra_gpio_set_value ,
. get_function = tegra_gpio_get_function ,
. get_state = tegra_gpio_get_state ,
} ;
/**
* Returns the name of a GPIO port
*
* GPIOs are named A , B , C , . . . , Z , AA , BB , CC , . . .
*
* @ base_port : Base port number ( 0 , 1. . n - 1 )
* @ return allocated string containing the name
*/
void gpio_info ( void )
static char * gpio_port_name ( int base_port )
{
unsigned c ;
int type ;
char * name , * s ;
name = malloc ( 3 ) ;
if ( name ) {
s = name ;
* s + + = ' A ' + ( base_port % 26 ) ;
if ( base_port > = 26 )
* s + + = * name ;
* s = ' \0 ' ;
}
for ( c = 0 ; c < MAX_NUM_GPIOS ; c + + ) {
type = get_config ( c ) ; /* GPIO, not SFPIO */
if ( type ) {
printf ( " GPIO_%d: \t %s is an %s, " , c ,
get_name ( c ) ,
get_direction ( c ) ? " OUTPUT " : " INPUT " ) ;
if ( get_direction ( c ) )
printf ( " value = %d " , gpio_get_output_value ( c ) ) ;
else
printf ( " value = %d " , gpio_get_value ( c ) ) ;
printf ( " \n " ) ;
} else
continue ;
return name ;
}
static const struct udevice_id tegra_gpio_ids [ ] = {
{ . compatible = " nvidia,tegra30-gpio " } ,
{ . compatible = " nvidia,tegra20-gpio " } ,
{ }
} ;
static int gpio_tegra_probe ( struct udevice * dev )
{
struct gpio_dev_priv * uc_priv = dev - > uclass_priv ;
struct tegra_port_info * priv = dev - > priv ;
struct tegra_gpio_platdata * plat = dev - > platdata ;
/* Only child devices have ports */
if ( ! plat )
return 0 ;
priv - > bank = plat - > bank ;
priv - > base_gpio = plat - > base_gpio ;
uc_priv - > gpio_count = TEGRA_GPIOS_PER_PORT ;
uc_priv - > bank_name = plat - > port_name ;
return 0 ;
}
/**
* We have a top - level GPIO device with no actual GPIOs . It has a child
* device for each Tegra port .
*/
static int gpio_tegra_bind ( struct udevice * parent )
{
struct tegra_gpio_platdata * plat = parent - > platdata ;
struct gpio_ctlr * ctlr ;
int bank_count ;
int bank ;
int ret ;
int len ;
/* If this is a child device, there is nothing to do here */
if ( plat )
return 0 ;
/*
* This driver does not make use of interrupts , other than to figure
* out the number of GPIO banks
*/
if ( ! fdt_getprop ( gd - > fdt_blob , parent - > of_offset , " interrupts " , & len ) )
return - EINVAL ;
bank_count = len / 3 / sizeof ( u32 ) ;
ctlr = ( struct gpio_ctlr * ) fdtdec_get_addr ( gd - > fdt_blob ,
parent - > of_offset , " reg " ) ;
for ( bank = 0 ; bank < bank_count ; bank + + ) {
int port ;
for ( port = 0 ; port < TEGRA_PORTS_PER_BANK ; port + + ) {
struct tegra_gpio_platdata * plat ;
struct udevice * dev ;
int base_port ;
plat = calloc ( 1 , sizeof ( * plat ) ) ;
if ( ! plat )
return - ENOMEM ;
plat - > bank = & ctlr - > gpio_bank [ bank ] ;
base_port = bank * TEGRA_PORTS_PER_BANK + port ;
plat - > base_gpio = TEGRA_GPIOS_PER_PORT * base_port ;
plat - > port_name = gpio_port_name ( base_port ) ;
ret = device_bind ( parent , parent - > driver ,
plat - > port_name , plat , - 1 , & dev ) ;
if ( ret )
return ret ;
dev - > of_offset = parent - > of_offset ;
}
}
return 0 ;
}
U_BOOT_DRIVER ( gpio_tegra ) = {
. name = " gpio_tegra " ,
. id = UCLASS_GPIO ,
. of_match = tegra_gpio_ids ,
. bind = gpio_tegra_bind ,
. probe = gpio_tegra_probe ,
. priv_auto_alloc_size = sizeof ( struct tegra_port_info ) ,
. ops = & gpio_tegra_ops ,
} ;