/*
* Copyright ( C ) 2016 Socionext Inc .
* Author : Masahiro Yamada < yamada . masahiro @ socionext . com >
*
* SPDX - License - Identifier : GPL - 2.0 +
*/
# include <common.h>
# include <dm/device.h>
# include <linux/bitops.h>
# include <linux/io.h>
# include <linux/sizes.h>
# include <linux/errno.h>
# include <asm/gpio.h>
# define UNIPHIER_GPIO_PORTS_PER_BANK 8
# define UNIPHIER_GPIO_REG_DATA 0 /* data */
# define UNIPHIER_GPIO_REG_DIR 4 /* direction (1:in, 0:out) */
struct uniphier_gpio_priv {
void __iomem * base ;
char bank_name [ 16 ] ;
} ;
static void uniphier_gpio_offset_write ( struct udevice * dev , unsigned offset ,
unsigned reg , int value )
{
struct uniphier_gpio_priv * priv = dev_get_priv ( dev ) ;
u32 tmp ;
tmp = readl ( priv - > base + reg ) ;
if ( value )
tmp | = BIT ( offset ) ;
else
tmp & = ~ BIT ( offset ) ;
writel ( tmp , priv - > base + reg ) ;
}
static int uniphier_gpio_offset_read ( struct udevice * dev , unsigned offset ,
unsigned reg )
{
struct uniphier_gpio_priv * priv = dev_get_priv ( dev ) ;
return ! ! ( readl ( priv - > base + reg ) & BIT ( offset ) ) ;
}
static int uniphier_gpio_direction_input ( struct udevice * dev , unsigned offset )
{
uniphier_gpio_offset_write ( dev , offset , UNIPHIER_GPIO_REG_DIR , 1 ) ;
return 0 ;
}
static int uniphier_gpio_direction_output ( struct udevice * dev , unsigned offset ,
int value )
{
uniphier_gpio_offset_write ( dev , offset , UNIPHIER_GPIO_REG_DATA , value ) ;
uniphier_gpio_offset_write ( dev , offset , UNIPHIER_GPIO_REG_DIR , 0 ) ;
return 0 ;
}
static int uniphier_gpio_get_value ( struct udevice * dev , unsigned offset )
{
return uniphier_gpio_offset_read ( dev , offset , UNIPHIER_GPIO_REG_DATA ) ;
}
static int uniphier_gpio_set_value ( struct udevice * dev , unsigned offset ,
int value )
{
uniphier_gpio_offset_write ( dev , offset , UNIPHIER_GPIO_REG_DATA , value ) ;
return 0 ;
}
static int uniphier_gpio_get_function ( struct udevice * dev , unsigned offset )
{
return uniphier_gpio_offset_read ( dev , offset , UNIPHIER_GPIO_REG_DIR ) ?
GPIOF_INPUT : GPIOF_OUTPUT ;
}
static const struct dm_gpio_ops uniphier_gpio_ops = {
. direction_input = uniphier_gpio_direction_input ,
. direction_output = uniphier_gpio_direction_output ,
. get_value = uniphier_gpio_get_value ,
. set_value = uniphier_gpio_set_value ,
. get_function = uniphier_gpio_get_function ,
} ;
static int uniphier_gpio_probe ( struct udevice * dev )
{
struct uniphier_gpio_priv * priv = dev_get_priv ( dev ) ;
struct gpio_dev_priv * uc_priv = dev_get_uclass_priv ( dev ) ;
fdt_addr_t addr ;
unsigned int tmp ;
addr = dev_get_addr ( dev ) ;
if ( addr = = FDT_ADDR_T_NONE )
return - EINVAL ;
priv - > base = devm_ioremap ( dev , addr , SZ_8 ) ;
if ( ! priv - > base )
return - ENOMEM ;
uc_priv - > gpio_count = UNIPHIER_GPIO_PORTS_PER_BANK ;
tmp = ( addr & 0xfff ) ;
/* Unfortunately, there is a register hole at offset 0x90-0x9f. */
if ( tmp > 0x90 )
tmp - = 0x10 ;
snprintf ( priv - > bank_name , sizeof ( priv - > bank_name ) - 1 ,
" port%d- " , ( tmp - 8 ) / 8 ) ;
uc_priv - > bank_name = priv - > bank_name ;
return 0 ;
}
/* .data = the number of GPIO banks */
static const struct udevice_id uniphier_gpio_match [ ] = {
{ . compatible = " socionext,uniphier-gpio " } ,
{ /* sentinel */ }
} ;
U_BOOT_DRIVER ( uniphier_gpio ) = {
. name = " uniphier_gpio " ,
. id = UCLASS_GPIO ,
. of_match = uniphier_gpio_match ,
. probe = uniphier_gpio_probe ,
. priv_auto_alloc_size = sizeof ( struct uniphier_gpio_priv ) ,
. ops = & uniphier_gpio_ops ,
} ;