2023-06-07 17:39:31 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* GPIO driver for TI TPS65219 PMICs
*
* Copyright ( C ) 2022 Texas Instruments Incorporated - http : //www.ti.com/
*/
# include <linux/bits.h>
# include <linux/gpio/driver.h>
# include <linux/mfd/tps65219.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# define TPS65219_GPIO0_DIR_MASK BIT(3)
# define TPS65219_GPIO0_OFFSET 2
# define TPS65219_GPIO0_IDX 0
# define TPS65219_GPIO_DIR_IN 1
# define TPS65219_GPIO_DIR_OUT 0
struct tps65219_gpio {
struct gpio_chip gpio_chip ;
struct tps65219 * tps ;
} ;
static int tps65219_gpio_get_direction ( struct gpio_chip * gc , unsigned int offset )
{
struct tps65219_gpio * gpio = gpiochip_get_data ( gc ) ;
int ret , val ;
if ( offset ! = TPS65219_GPIO0_IDX )
return GPIO_LINE_DIRECTION_OUT ;
ret = regmap_read ( gpio - > tps - > regmap , TPS65219_REG_MFP_1_CONFIG , & val ) ;
if ( ret )
return ret ;
return ! ! ( val & TPS65219_GPIO0_DIR_MASK ) ;
}
static int tps65219_gpio_get ( struct gpio_chip * gc , unsigned int offset )
{
struct tps65219_gpio * gpio = gpiochip_get_data ( gc ) ;
struct device * dev = gpio - > tps - > dev ;
int ret , val ;
if ( offset ! = TPS65219_GPIO0_IDX ) {
dev_err ( dev , " GPIO%d is output only, cannot get \n " , offset ) ;
return - ENOTSUPP ;
}
ret = regmap_read ( gpio - > tps - > regmap , TPS65219_REG_MFP_CTRL , & val ) ;
if ( ret )
return ret ;
ret = ! ! ( val & BIT ( TPS65219_MFP_GPIO_STATUS_MASK ) ) ;
dev_warn ( dev , " GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO \n " , offset , ret ) ;
/*
* Depending on NVM config , return an error if direction is output , otherwise the GPIO0
* status bit .
*/
if ( tps65219_gpio_get_direction ( gc , offset ) = = TPS65219_GPIO_DIR_OUT )
return - ENOTSUPP ;
return ret ;
}
static void tps65219_gpio_set ( struct gpio_chip * gc , unsigned int offset , int value )
{
struct tps65219_gpio * gpio = gpiochip_get_data ( gc ) ;
struct device * dev = gpio - > tps - > dev ;
int v , mask , bit ;
bit = ( offset = = TPS65219_GPIO0_IDX ) ? TPS65219_GPIO0_OFFSET : offset - 1 ;
mask = BIT ( bit ) ;
v = value ? mask : 0 ;
if ( regmap_update_bits ( gpio - > tps - > regmap , TPS65219_REG_GENERAL_CONFIG , mask , v ) )
dev_err ( dev , " GPIO%d, set to value %d failed. \n " , offset , value ) ;
}
static int tps65219_gpio_change_direction ( struct gpio_chip * gc , unsigned int offset ,
unsigned int direction )
{
struct tps65219_gpio * gpio = gpiochip_get_data ( gc ) ;
struct device * dev = gpio - > tps - > dev ;
/*
* Documentation is stating that GPIO0 direction must not be changed in Linux :
* Table 8 - 34. MFP_1_CONFIG ( 3 ) : MULTI_DEVICE_ENABLE , should only be changed in INITIALIZE
* state ( prior to ON Request ) .
* Set statically by NVM , changing direction in application can cause a hang .
* Below can be used for test purpose only .
*/
2023-12-21 21:57:02 +03:00
#if 0
int ret = regmap_update_bits ( gpio - > tps - > regmap , TPS65219_REG_MFP_1_CONFIG ,
TPS65219_GPIO0_DIR_MASK , direction ) ;
if ( ret ) {
dev_err ( dev ,
" GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d. \n " ,
direction , offset ) ;
return ret ;
2023-06-07 17:39:31 +03:00
}
2023-12-21 21:57:02 +03:00
# endif
2023-06-07 17:39:31 +03:00
dev_err ( dev ,
" GPIO%d direction set by NVM, change to %u failed, not allowed by specification \n " ,
offset , direction ) ;
return - ENOTSUPP ;
}
static int tps65219_gpio_direction_input ( struct gpio_chip * gc , unsigned int offset )
{
struct tps65219_gpio * gpio = gpiochip_get_data ( gc ) ;
struct device * dev = gpio - > tps - > dev ;
if ( offset ! = TPS65219_GPIO0_IDX ) {
dev_err ( dev , " GPIO%d is output only, cannot change to input \n " , offset ) ;
return - ENOTSUPP ;
}
if ( tps65219_gpio_get_direction ( gc , offset ) = = TPS65219_GPIO_DIR_IN )
return 0 ;
return tps65219_gpio_change_direction ( gc , offset , TPS65219_GPIO_DIR_IN ) ;
}
static int tps65219_gpio_direction_output ( struct gpio_chip * gc , unsigned int offset , int value )
{
tps65219_gpio_set ( gc , offset , value ) ;
if ( offset ! = TPS65219_GPIO0_IDX )
return 0 ;
if ( tps65219_gpio_get_direction ( gc , offset ) = = TPS65219_GPIO_DIR_OUT )
return 0 ;
return tps65219_gpio_change_direction ( gc , offset , TPS65219_GPIO_DIR_OUT ) ;
}
static const struct gpio_chip tps65219_template_chip = {
. label = " tps65219-gpio " ,
. owner = THIS_MODULE ,
. get_direction = tps65219_gpio_get_direction ,
. direction_input = tps65219_gpio_direction_input ,
. direction_output = tps65219_gpio_direction_output ,
. get = tps65219_gpio_get ,
. set = tps65219_gpio_set ,
. base = - 1 ,
. ngpio = 3 ,
. can_sleep = true ,
} ;
static int tps65219_gpio_probe ( struct platform_device * pdev )
{
struct tps65219 * tps = dev_get_drvdata ( pdev - > dev . parent ) ;
struct tps65219_gpio * gpio ;
gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * gpio ) , GFP_KERNEL ) ;
if ( ! gpio )
return - ENOMEM ;
gpio - > tps = tps ;
gpio - > gpio_chip = tps65219_template_chip ;
gpio - > gpio_chip . parent = tps - > dev ;
return devm_gpiochip_add_data ( & pdev - > dev , & gpio - > gpio_chip , gpio ) ;
}
static struct platform_driver tps65219_gpio_driver = {
. driver = {
. name = " tps65219-gpio " ,
} ,
. probe = tps65219_gpio_probe ,
} ;
module_platform_driver ( tps65219_gpio_driver ) ;
MODULE_ALIAS ( " platform:tps65219-gpio " ) ;
MODULE_AUTHOR ( " Jonathan Cormier <jcormier@criticallink.com> " ) ;
MODULE_DESCRIPTION ( " TPS65219 GPIO driver " ) ;
MODULE_LICENSE ( " GPL " ) ;