2016-01-30 20:06:44 +03:00
/*
* Copyright 2015 Verifone Int .
*
* Author : Nicolas Saenz Julienne < nicolassaenzj @ gmail . com >
*
* This program is free software ; you can redistribute it and / or modify i t
* under the terms of the GNU General Public License as published by th e
* Free Software Foundation ; either version 2 of the License , or ( at you r
* option ) any later version .
*
* This driver is based on the gpio - tps65912 implementation .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/gpio/driver.h>
# include <linux/platform_device.h>
2016-09-19 10:39:02 +03:00
# include <linux/regmap.h>
2016-01-30 20:06:44 +03:00
# include <linux/mfd/tps65218.h>
struct tps65218_gpio {
struct tps65218 * tps65218 ;
struct gpio_chip gpio_chip ;
} ;
static int tps65218_gpio_get ( struct gpio_chip * gc , unsigned offset )
{
struct tps65218_gpio * tps65218_gpio = gpiochip_get_data ( gc ) ;
struct tps65218 * tps65218 = tps65218_gpio - > tps65218 ;
unsigned int val ;
int ret ;
2016-09-19 10:39:02 +03:00
ret = regmap_read ( tps65218 - > regmap , TPS65218_REG_ENABLE2 , & val ) ;
2016-01-30 20:06:44 +03:00
if ( ret )
return ret ;
return ! ! ( val & ( TPS65218_ENABLE2_GPIO1 < < offset ) ) ;
}
static void tps65218_gpio_set ( struct gpio_chip * gc , unsigned offset ,
int value )
{
struct tps65218_gpio * tps65218_gpio = gpiochip_get_data ( gc ) ;
struct tps65218 * tps65218 = tps65218_gpio - > tps65218 ;
if ( value )
tps65218_set_bits ( tps65218 , TPS65218_REG_ENABLE2 ,
TPS65218_ENABLE2_GPIO1 < < offset ,
TPS65218_ENABLE2_GPIO1 < < offset ,
TPS65218_PROTECT_L1 ) ;
else
tps65218_clear_bits ( tps65218 , TPS65218_REG_ENABLE2 ,
TPS65218_ENABLE2_GPIO1 < < offset ,
TPS65218_PROTECT_L1 ) ;
}
static int tps65218_gpio_output ( struct gpio_chip * gc , unsigned offset ,
int value )
{
/* Only drives GPOs */
2016-02-15 15:10:40 +03:00
tps65218_gpio_set ( gc , offset , value ) ;
2016-01-30 20:06:44 +03:00
return 0 ;
}
static int tps65218_gpio_input ( struct gpio_chip * gc , unsigned offset )
{
return - EPERM ;
}
static int tps65218_gpio_request ( struct gpio_chip * gc , unsigned offset )
{
struct tps65218_gpio * tps65218_gpio = gpiochip_get_data ( gc ) ;
struct tps65218 * tps65218 = tps65218_gpio - > tps65218 ;
int ret ;
2016-02-16 17:41:42 +03:00
if ( gpiochip_line_is_open_source ( gc , offset ) ) {
2016-01-30 20:06:44 +03:00
dev_err ( gc - > parent , " can't work as open source \n " ) ;
return - EINVAL ;
}
switch ( offset ) {
case 0 :
2016-02-16 17:41:42 +03:00
if ( ! gpiochip_line_is_open_drain ( gc , offset ) ) {
2016-01-30 20:06:44 +03:00
dev_err ( gc - > parent , " GPO1 works only as open drain \n " ) ;
return - EINVAL ;
}
/* Disable sequencer for GPO1 */
ret = tps65218_clear_bits ( tps65218 , TPS65218_REG_SEQ7 ,
TPS65218_SEQ7_GPO1_SEQ_MASK ,
TPS65218_PROTECT_L1 ) ;
if ( ret )
return ret ;
/* Setup GPO1 */
ret = tps65218_clear_bits ( tps65218 , TPS65218_REG_CONFIG1 ,
TPS65218_CONFIG1_IO1_SEL ,
TPS65218_PROTECT_L1 ) ;
if ( ret )
return ret ;
break ;
case 1 :
/* Setup GPO2 */
ret = tps65218_clear_bits ( tps65218 , TPS65218_REG_CONFIG1 ,
TPS65218_CONFIG1_IO1_SEL ,
TPS65218_PROTECT_L1 ) ;
if ( ret )
return ret ;
break ;
case 2 :
2016-02-16 17:41:42 +03:00
if ( ! gpiochip_line_is_open_drain ( gc , offset ) ) {
2016-01-30 20:06:44 +03:00
dev_err ( gc - > parent , " GPO3 works only as open drain \n " ) ;
return - EINVAL ;
}
/* Disable sequencer for GPO3 */
ret = tps65218_clear_bits ( tps65218 , TPS65218_REG_SEQ7 ,
TPS65218_SEQ7_GPO3_SEQ_MASK ,
TPS65218_PROTECT_L1 ) ;
if ( ret )
return ret ;
/* Setup GPO3 */
ret = tps65218_clear_bits ( tps65218 , TPS65218_REG_CONFIG2 ,
TPS65218_CONFIG2_DC12_RST ,
TPS65218_PROTECT_L1 ) ;
if ( ret )
return ret ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2017-01-23 15:34:34 +03:00
static int tps65218_gpio_set_config ( struct gpio_chip * gc , unsigned offset ,
unsigned long config )
2016-04-08 15:13:53 +03:00
{
struct tps65218_gpio * tps65218_gpio = gpiochip_get_data ( gc ) ;
struct tps65218 * tps65218 = tps65218_gpio - > tps65218 ;
2017-01-23 15:34:34 +03:00
enum pin_config_param param = pinconf_to_config_param ( config ) ;
2016-04-08 15:13:53 +03:00
switch ( offset ) {
case 0 :
case 2 :
/* GPO1 is hardwired to be open drain */
2017-01-23 15:34:34 +03:00
if ( param = = PIN_CONFIG_DRIVE_OPEN_DRAIN )
2016-04-08 15:13:53 +03:00
return 0 ;
return - ENOTSUPP ;
case 1 :
/* GPO2 is push-pull by default, can be set as open drain. */
2017-01-23 15:34:34 +03:00
if ( param = = PIN_CONFIG_DRIVE_OPEN_DRAIN )
2016-04-08 15:13:53 +03:00
return tps65218_clear_bits ( tps65218 ,
TPS65218_REG_CONFIG1 ,
TPS65218_CONFIG1_GPO2_BUF ,
TPS65218_PROTECT_L1 ) ;
2017-01-23 15:34:34 +03:00
if ( param = = PIN_CONFIG_DRIVE_PUSH_PULL )
2016-04-08 15:13:53 +03:00
return tps65218_set_bits ( tps65218 ,
TPS65218_REG_CONFIG1 ,
TPS65218_CONFIG1_GPO2_BUF ,
TPS65218_CONFIG1_GPO2_BUF ,
TPS65218_PROTECT_L1 ) ;
return - ENOTSUPP ;
default :
break ;
}
return - ENOTSUPP ;
}
2016-09-11 15:14:37 +03:00
static const struct gpio_chip template_chip = {
2016-01-30 20:06:44 +03:00
. label = " gpio-tps65218 " ,
. owner = THIS_MODULE ,
. request = tps65218_gpio_request ,
. direction_output = tps65218_gpio_output ,
. direction_input = tps65218_gpio_input ,
. get = tps65218_gpio_get ,
. set = tps65218_gpio_set ,
2017-01-23 15:34:34 +03:00
. set_config = tps65218_gpio_set_config ,
2016-01-30 20:06:44 +03:00
. can_sleep = true ,
. ngpio = 3 ,
. base = - 1 ,
} ;
static int tps65218_gpio_probe ( struct platform_device * pdev )
{
struct tps65218 * tps65218 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct tps65218_gpio * tps65218_gpio ;
int ret ;
tps65218_gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * tps65218_gpio ) ,
GFP_KERNEL ) ;
if ( ! tps65218_gpio )
return - ENOMEM ;
tps65218_gpio - > tps65218 = tps65218 ;
tps65218_gpio - > gpio_chip = template_chip ;
tps65218_gpio - > gpio_chip . parent = & pdev - > dev ;
# ifdef CONFIG_OF_GPIO
tps65218_gpio - > gpio_chip . of_node = pdev - > dev . of_node ;
# endif
2016-09-16 04:51:10 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & tps65218_gpio - > gpio_chip ,
tps65218_gpio ) ;
2016-01-30 20:06:44 +03:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Failed to register gpiochip, %d \n " , ret ) ;
return ret ;
}
platform_set_drvdata ( pdev , tps65218_gpio ) ;
return ret ;
}
static const struct of_device_id tps65218_dt_match [ ] = {
{ . compatible = " ti,tps65218-gpio " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , tps65218_dt_match ) ;
2016-06-28 15:30:52 +03:00
static const struct platform_device_id tps65218_gpio_id_table [ ] = {
{ " tps65218-gpio " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , tps65218_gpio_id_table ) ;
2016-01-30 20:06:44 +03:00
static struct platform_driver tps65218_gpio_driver = {
. driver = {
. name = " tps65218-gpio " ,
. of_match_table = of_match_ptr ( tps65218_dt_match )
} ,
. probe = tps65218_gpio_probe ,
2016-06-28 15:30:52 +03:00
. id_table = tps65218_gpio_id_table ,
2016-01-30 20:06:44 +03:00
} ;
module_platform_driver ( tps65218_gpio_driver ) ;
MODULE_AUTHOR ( " Nicolas Saenz Julienne <nicolassaenzj@gmail.com> " ) ;
MODULE_DESCRIPTION ( " GPO interface for TPS65218 PMICs " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:tps65218-gpio " ) ;