2018-09-03 09:20:59 +03:00
// SPDX-License-Identifier: GPL-2.0+
2008-10-21 01:51:46 +04:00
/*
2011-06-05 04:38:28 +04:00
* Access to GPIOs on TWL4030 / TPS659x0 chips
2008-10-21 01:51:46 +04:00
*
* Copyright ( C ) 2006 - 2007 Texas Instruments , Inc .
* Copyright ( C ) 2006 MontaVista Software , Inc .
*
* Code re - arranged and cleaned up by :
* Syed Mohammed Khasim < x0khasim @ ti . com >
*
* Initial Code :
* Andy Lowe / Nishanth Menon
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kthread.h>
# include <linux/irq.h>
2023-05-01 12:05:21 +03:00
# include <linux/gpio/machine.h>
2018-09-03 09:15:50 +03:00
# include <linux/gpio/driver.h>
2023-05-01 12:05:21 +03:00
# include <linux/gpio/consumer.h>
2008-10-21 01:51:46 +04:00
# include <linux/platform_device.h>
2012-03-01 01:51:32 +04:00
# include <linux/of.h>
# include <linux/irqdomain.h>
2008-10-21 01:51:46 +04:00
2017-08-14 19:34:24 +03:00
# include <linux/mfd/twl.h>
2008-10-21 01:51:46 +04:00
/*
* The GPIO " subchip " supports 18 GPIOs which can be configured as
* inputs or outputs , with pullups or pulldowns on each pin . Each
* GPIO can trigger interrupts on either or both edges .
*
* GPIO interrupts can be fed to either of two IRQ lines ; this is
* intended to support multiple hosts .
*
* There are also two LED pins used sometimes as output - only GPIOs .
*/
/* genirq interfaces are not available to modules */
# ifdef MODULE
# define is_module() true
# else
# define is_module() false
# endif
/* GPIO_CTRL Fields */
# define MASK_GPIO_CTRL_GPIO0CD1 BIT(0)
# define MASK_GPIO_CTRL_GPIO1CD2 BIT(1)
# define MASK_GPIO_CTRL_GPIO_ON BIT(2)
/* Mask for GPIO registers when aggregated into a 32-bit integer */
# define GPIO_32_MASK 0x0003ffff
2012-12-06 14:52:05 +04:00
struct gpio_twl4030_priv {
struct gpio_chip gpio_chip ;
2012-12-20 13:44:11 +04:00
struct mutex mutex ;
2012-12-06 14:52:05 +04:00
int irq_base ;
2012-12-20 13:44:11 +04:00
/* Bitfields for state caching */
2012-12-06 14:52:05 +04:00
unsigned int usage_count ;
2012-12-20 13:44:11 +04:00
unsigned int direction ;
unsigned int out_state ;
2012-12-06 14:52:05 +04:00
} ;
2008-10-21 01:51:46 +04:00
/*----------------------------------------------------------------------*/
/*
* To configure TWL4030 GPIO module registers
*/
static inline int gpio_twl4030_write ( u8 address , u8 data )
{
2009-12-13 23:23:33 +03:00
return twl_i2c_write_u8 ( TWL4030_MODULE_GPIO , data , address ) ;
2008-10-21 01:51:46 +04:00
}
/*----------------------------------------------------------------------*/
/*
2012-11-13 13:35:13 +04:00
* LED register offsets from TWL_MODULE_LED base
2008-10-21 01:51:46 +04:00
* PWMs A and B are dedicated to LEDs A and B , respectively .
*/
2012-11-13 13:35:13 +04:00
# define TWL4030_LED_LEDEN_REG 0x00
# define TWL4030_PWMAON_REG 0x01
# define TWL4030_PWMAOFF_REG 0x02
# define TWL4030_PWMBON_REG 0x03
# define TWL4030_PWMBOFF_REG 0x04
2008-10-21 01:51:46 +04:00
/* LEDEN bits */
# define LEDEN_LEDAON BIT(0)
# define LEDEN_LEDBON BIT(1)
# define LEDEN_LEDAEXT BIT(2)
# define LEDEN_LEDBEXT BIT(3)
# define LEDEN_LEDAPWM BIT(4)
# define LEDEN_LEDBPWM BIT(5)
# define LEDEN_PWM_LENGTHA BIT(6)
# define LEDEN_PWM_LENGTHB BIT(7)
# define PWMxON_LENGTH BIT(7)
/*----------------------------------------------------------------------*/
/*
* To read a TWL4030 GPIO module register
*/
static inline int gpio_twl4030_read ( u8 address )
{
u8 data ;
int ret = 0 ;
2009-12-13 23:23:33 +03:00
ret = twl_i2c_read_u8 ( TWL4030_MODULE_GPIO , & data , address ) ;
2008-10-21 01:51:46 +04:00
return ( ret < 0 ) ? ret : data ;
}
/*----------------------------------------------------------------------*/
2012-12-20 13:44:11 +04:00
static u8 cached_leden ;
2008-10-21 01:51:46 +04:00
/* The LED lines are open drain outputs ... a FET pulls to GND, so an
* external pullup is needed . We could also expose the integrated PWM
* as a LED brightness control ; we initialize it as " always on " .
*/
static void twl4030_led_set_value ( int led , int value )
{
u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM ;
if ( led )
mask < < = 1 ;
if ( value )
cached_leden & = ~ mask ;
else
cached_leden | = mask ;
2014-03-22 12:32:15 +04:00
WARN_ON_ONCE ( twl_i2c_write_u8 ( TWL4030_MODULE_LED , cached_leden ,
TWL4030_LED_LEDEN_REG ) ) ;
2008-10-21 01:51:46 +04:00
}
static int twl4030_set_gpio_direction ( int gpio , int is_input )
{
u8 d_bnk = gpio > > 3 ;
u8 d_msk = BIT ( gpio & 0x7 ) ;
u8 reg = 0 ;
u8 base = REG_GPIODATADIR1 + d_bnk ;
int ret = 0 ;
ret = gpio_twl4030_read ( base ) ;
if ( ret > = 0 ) {
if ( is_input )
reg = ret & ~ d_msk ;
else
reg = ret | d_msk ;
ret = gpio_twl4030_write ( base , reg ) ;
}
return ret ;
}
2018-09-03 10:52:10 +03:00
static int twl4030_get_gpio_direction ( int gpio )
{
u8 d_bnk = gpio > > 3 ;
u8 d_msk = BIT ( gpio & 0x7 ) ;
u8 base = REG_GPIODATADIR1 + d_bnk ;
int ret = 0 ;
ret = gpio_twl4030_read ( base ) ;
if ( ret < 0 )
return ret ;
2019-11-06 11:54:12 +03:00
if ( ret & d_msk )
return GPIO_LINE_DIRECTION_OUT ;
2018-09-03 10:52:10 +03:00
2019-11-06 11:54:12 +03:00
return GPIO_LINE_DIRECTION_IN ;
2018-09-03 10:52:10 +03:00
}
2008-10-21 01:51:46 +04:00
static int twl4030_set_gpio_dataout ( int gpio , int enable )
{
u8 d_bnk = gpio > > 3 ;
u8 d_msk = BIT ( gpio & 0x7 ) ;
u8 base = 0 ;
if ( enable )
base = REG_SETGPIODATAOUT1 + d_bnk ;
else
base = REG_CLEARGPIODATAOUT1 + d_bnk ;
return gpio_twl4030_write ( base , d_msk ) ;
}
static int twl4030_get_gpio_datain ( int gpio )
{
u8 d_bnk = gpio > > 3 ;
u8 d_off = gpio & 0x7 ;
u8 base = 0 ;
int ret = 0 ;
base = REG_GPIODATAIN1 + d_bnk ;
ret = gpio_twl4030_read ( base ) ;
if ( ret > 0 )
ret = ( ret > > d_off ) & 0x1 ;
return ret ;
}
/*----------------------------------------------------------------------*/
static int twl_request ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2008-10-21 01:51:46 +04:00
int status = 0 ;
2012-12-20 13:44:11 +04:00
mutex_lock ( & priv - > mutex ) ;
2008-10-21 01:51:46 +04:00
/* Support the two LED outputs as output-only GPIOs. */
if ( offset > = TWL4030_GPIO_MAX ) {
u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
| LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA ;
2012-11-13 13:35:13 +04:00
u8 reg = TWL4030_PWMAON_REG ;
2008-10-21 01:51:46 +04:00
offset - = TWL4030_GPIO_MAX ;
if ( offset ) {
ledclr_mask < < = 1 ;
2012-11-13 13:35:13 +04:00
reg = TWL4030_PWMBON_REG ;
2008-10-21 01:51:46 +04:00
}
/* initialize PWM to always-drive */
2012-11-13 13:35:13 +04:00
/* Configure PWM OFF register first */
status = twl_i2c_write_u8 ( TWL4030_MODULE_LED , 0x7f , reg + 1 ) ;
2008-10-21 01:51:46 +04:00
if ( status < 0 )
goto done ;
2012-11-13 13:35:13 +04:00
/* Followed by PWM ON register */
status = twl_i2c_write_u8 ( TWL4030_MODULE_LED , 0x7f , reg ) ;
2008-10-21 01:51:46 +04:00
if ( status < 0 )
goto done ;
/* init LED to not-driven (high) */
2012-11-13 13:35:13 +04:00
status = twl_i2c_read_u8 ( TWL4030_MODULE_LED , & cached_leden ,
TWL4030_LED_LEDEN_REG ) ;
2008-10-21 01:51:46 +04:00
if ( status < 0 )
goto done ;
cached_leden & = ~ ledclr_mask ;
2012-11-13 13:35:13 +04:00
status = twl_i2c_write_u8 ( TWL4030_MODULE_LED , cached_leden ,
TWL4030_LED_LEDEN_REG ) ;
2008-10-21 01:51:46 +04:00
if ( status < 0 )
goto done ;
status = 0 ;
goto done ;
}
/* on first use, turn GPIO module "on" */
2012-12-06 14:52:05 +04:00
if ( ! priv - > usage_count ) {
2008-10-21 01:51:46 +04:00
struct twl4030_gpio_platform_data * pdata ;
u8 value = MASK_GPIO_CTRL_GPIO_ON ;
/* optionally have the first two GPIOs switch vMMC1
* and vMMC2 power supplies based on card presence .
*/
2015-11-04 11:56:26 +03:00
pdata = dev_get_platdata ( chip - > parent ) ;
2012-03-01 01:51:32 +04:00
if ( pdata )
value | = pdata - > mmc_cd & 0x03 ;
2008-10-21 01:51:46 +04:00
status = gpio_twl4030_write ( REG_GPIO_CTRL , value ) ;
}
2012-12-06 14:52:05 +04:00
done :
2008-10-21 01:51:46 +04:00
if ( ! status )
2012-12-06 14:52:05 +04:00
priv - > usage_count | = BIT ( offset ) ;
2008-10-21 01:51:46 +04:00
2012-12-20 13:44:11 +04:00
mutex_unlock ( & priv - > mutex ) ;
2008-10-21 01:51:46 +04:00
return status ;
}
static void twl_free ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2012-12-06 14:52:05 +04:00
2012-12-20 13:44:11 +04:00
mutex_lock ( & priv - > mutex ) ;
2008-10-21 01:51:46 +04:00
if ( offset > = TWL4030_GPIO_MAX ) {
twl4030_led_set_value ( offset - TWL4030_GPIO_MAX , 1 ) ;
2012-12-20 13:44:11 +04:00
goto out ;
2008-10-21 01:51:46 +04:00
}
2012-12-06 14:52:05 +04:00
priv - > usage_count & = ~ BIT ( offset ) ;
2008-10-21 01:51:46 +04:00
/* on last use, switch off GPIO module */
2012-12-06 14:52:05 +04:00
if ( ! priv - > usage_count )
2008-10-21 01:51:46 +04:00
gpio_twl4030_write ( REG_GPIO_CTRL , 0x0 ) ;
2012-12-20 13:44:11 +04:00
out :
mutex_unlock ( & priv - > mutex ) ;
2008-10-21 01:51:46 +04:00
}
static int twl_direction_in ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2012-12-20 13:44:11 +04:00
int ret ;
mutex_lock ( & priv - > mutex ) ;
if ( offset < TWL4030_GPIO_MAX )
ret = twl4030_set_gpio_direction ( offset , 1 ) ;
else
2013-12-05 13:23:35 +04:00
ret = - EINVAL ; /* LED outputs can't be set as input */
2012-12-20 13:44:11 +04:00
if ( ! ret )
priv - > direction & = ~ BIT ( offset ) ;
mutex_unlock ( & priv - > mutex ) ;
return ret ;
2008-10-21 01:51:46 +04:00
}
static int twl_get ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2012-12-20 13:44:11 +04:00
int ret ;
2008-10-21 01:51:46 +04:00
int status = 0 ;
2012-12-20 13:44:11 +04:00
mutex_lock ( & priv - > mutex ) ;
if ( ! ( priv - > usage_count & BIT ( offset ) ) ) {
ret = - EPERM ;
goto out ;
}
2012-12-06 14:52:05 +04:00
2012-12-20 13:44:11 +04:00
if ( priv - > direction & BIT ( offset ) )
status = priv - > out_state & BIT ( offset ) ;
2008-10-21 01:51:46 +04:00
else
2012-12-20 13:44:11 +04:00
status = twl4030_get_gpio_datain ( offset ) ;
2012-12-06 14:52:05 +04:00
2015-12-21 13:47:16 +03:00
ret = ( status < 0 ) ? status : ! ! status ;
2012-12-20 13:44:11 +04:00
out :
mutex_unlock ( & priv - > mutex ) ;
return ret ;
2008-10-21 01:51:46 +04:00
}
2012-12-20 13:44:11 +04:00
static void twl_set ( struct gpio_chip * chip , unsigned offset , int value )
2008-10-21 01:51:46 +04:00
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2012-12-20 13:44:11 +04:00
mutex_lock ( & priv - > mutex ) ;
if ( offset < TWL4030_GPIO_MAX )
2008-10-21 01:51:46 +04:00
twl4030_set_gpio_dataout ( offset , value ) ;
2012-12-20 13:44:11 +04:00
else
2008-10-21 01:51:46 +04:00
twl4030_led_set_value ( offset - TWL4030_GPIO_MAX , value ) ;
2012-12-20 13:44:11 +04:00
if ( value )
priv - > out_state | = BIT ( offset ) ;
else
priv - > out_state & = ~ BIT ( offset ) ;
mutex_unlock ( & priv - > mutex ) ;
2008-10-21 01:51:46 +04:00
}
2012-12-20 13:44:11 +04:00
static int twl_direction_out ( struct gpio_chip * chip , unsigned offset , int value )
2008-10-21 01:51:46 +04:00
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2013-12-05 13:23:35 +04:00
int ret = 0 ;
2012-12-20 13:44:11 +04:00
mutex_lock ( & priv - > mutex ) ;
2013-12-05 13:23:35 +04:00
if ( offset < TWL4030_GPIO_MAX ) {
2013-11-19 03:22:49 +04:00
ret = twl4030_set_gpio_direction ( offset , 0 ) ;
2013-12-05 13:23:35 +04:00
if ( ret ) {
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
}
/*
* LED gpios i . e . offset > = TWL4030_GPIO_MAX are always output
*/
2012-12-20 13:44:11 +04:00
priv - > direction | = BIT ( offset ) ;
mutex_unlock ( & priv - > mutex ) ;
twl_set ( chip , offset , value ) ;
2013-11-19 03:22:49 +04:00
return ret ;
2008-10-21 01:51:46 +04:00
}
2018-09-03 10:52:10 +03:00
static int twl_get_direction ( struct gpio_chip * chip , unsigned offset )
{
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
/*
2019-11-06 11:54:12 +03:00
* Default GPIO_LINE_DIRECTION_OUT
2018-09-03 10:52:10 +03:00
* LED GPIOs > = TWL4030_GPIO_MAX are always output
*/
2019-11-06 11:54:12 +03:00
int ret = GPIO_LINE_DIRECTION_OUT ;
2018-09-03 10:52:10 +03:00
mutex_lock ( & priv - > mutex ) ;
if ( offset < TWL4030_GPIO_MAX ) {
ret = twl4030_get_gpio_direction ( offset ) ;
if ( ret ) {
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
}
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
2008-10-21 01:51:46 +04:00
static int twl_to_irq ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 16:55:07 +03:00
struct gpio_twl4030_priv * priv = gpiochip_get_data ( chip ) ;
2012-12-06 14:52:05 +04:00
return ( priv - > irq_base & & ( offset < TWL4030_GPIO_MAX ) )
? ( priv - > irq_base + offset )
2008-10-21 01:51:46 +04:00
: - EINVAL ;
}
2016-09-11 15:14:37 +03:00
static const struct gpio_chip template_chip = {
2008-10-21 01:51:46 +04:00
. label = " twl4030 " ,
. owner = THIS_MODULE ,
. request = twl_request ,
. free = twl_free ,
. direction_input = twl_direction_in ,
. direction_output = twl_direction_out ,
2018-09-03 10:52:10 +03:00
. get_direction = twl_get_direction ,
. get = twl_get ,
2008-10-21 01:51:46 +04:00
. set = twl_set ,
. to_irq = twl_to_irq ,
2013-12-04 17:42:46 +04:00
. can_sleep = true ,
2008-10-21 01:51:46 +04:00
} ;
/*----------------------------------------------------------------------*/
2012-11-19 22:22:34 +04:00
static int gpio_twl4030_pulls ( u32 ups , u32 downs )
2008-10-21 01:51:46 +04:00
{
2012-11-13 12:28:45 +04:00
u8 message [ 5 ] ;
2008-10-21 01:51:46 +04:00
unsigned i , gpio_bit ;
/* For most pins, a pulldown was enabled by default.
* We should have data that ' s specific to this board .
*/
2012-11-13 12:28:45 +04:00
for ( gpio_bit = 1 , i = 0 ; i < 5 ; i + + ) {
2008-10-21 01:51:46 +04:00
u8 bit_mask ;
unsigned j ;
for ( bit_mask = 0 , j = 0 ; j < 8 ; j + = 2 , gpio_bit < < = 1 ) {
if ( ups & gpio_bit )
bit_mask | = 1 < < ( j + 1 ) ;
else if ( downs & gpio_bit )
bit_mask | = 1 < < ( j + 0 ) ;
}
message [ i ] = bit_mask ;
}
2009-12-13 23:23:33 +03:00
return twl_i2c_write ( TWL4030_MODULE_GPIO , message ,
2008-10-21 01:51:46 +04:00
REG_GPIOPUPDCTR1 , 5 ) ;
}
2012-11-19 22:22:34 +04:00
static int gpio_twl4030_debounce ( u32 debounce , u8 mmc_cd )
2009-01-07 01:42:26 +03:00
{
2012-11-13 12:28:45 +04:00
u8 message [ 3 ] ;
2009-01-07 01:42:26 +03:00
/* 30 msec of debouncing is always used for MMC card detect,
* and is optional for everything else .
*/
2012-11-13 12:28:45 +04:00
message [ 0 ] = ( debounce & 0xff ) | ( mmc_cd & 0x03 ) ;
2009-01-07 01:42:26 +03:00
debounce > > = 8 ;
2012-11-13 12:28:45 +04:00
message [ 1 ] = ( debounce & 0xff ) ;
2009-01-07 01:42:26 +03:00
debounce > > = 8 ;
2012-11-13 12:28:45 +04:00
message [ 2 ] = ( debounce & 0x03 ) ;
2009-01-07 01:42:26 +03:00
2009-12-13 23:23:33 +03:00
return twl_i2c_write ( TWL4030_MODULE_GPIO , message ,
2009-01-07 01:42:26 +03:00
REG_GPIO_DEBEN1 , 3 ) ;
}
2023-05-01 12:05:21 +03:00
static struct twl4030_gpio_platform_data * of_gpio_twl4030 ( struct device * dev )
2012-09-05 11:46:25 +04:00
{
struct twl4030_gpio_platform_data * omap_twl_info ;
omap_twl_info = devm_kzalloc ( dev , sizeof ( * omap_twl_info ) , GFP_KERNEL ) ;
if ( ! omap_twl_info )
return NULL ;
omap_twl_info - > use_leds = of_property_read_bool ( dev - > of_node ,
" ti,use-leds " ) ;
of_property_read_u32 ( dev - > of_node , " ti,debounce " ,
& omap_twl_info - > debounce ) ;
of_property_read_u32 ( dev - > of_node , " ti,mmc-cd " ,
( u32 * ) & omap_twl_info - > mmc_cd ) ;
of_property_read_u32 ( dev - > of_node , " ti,pullups " ,
& omap_twl_info - > pullups ) ;
of_property_read_u32 ( dev - > of_node , " ti,pulldowns " ,
& omap_twl_info - > pulldowns ) ;
return omap_twl_info ;
}
2023-05-01 12:05:21 +03:00
/* Called from the registered devm action */
static void gpio_twl4030_power_off_action ( void * data )
{
struct gpio_desc * d = data ;
gpiod_unexport ( d ) ;
gpiochip_free_own_desc ( d ) ;
}
2012-11-19 22:22:34 +04:00
static int gpio_twl4030_probe ( struct platform_device * pdev )
2008-10-21 01:51:46 +04:00
{
2023-05-01 12:05:21 +03:00
struct twl4030_gpio_platform_data * pdata ;
2012-03-01 01:51:32 +04:00
struct device_node * node = pdev - > dev . of_node ;
2012-12-06 14:52:05 +04:00
struct gpio_twl4030_priv * priv ;
2012-03-01 01:48:32 +04:00
int ret , irq_base ;
2008-10-21 01:51:46 +04:00
2012-12-06 14:52:05 +04:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct gpio_twl4030_priv ) ,
GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2008-10-21 01:51:46 +04:00
/* maybe setup IRQs */
2012-03-01 01:48:32 +04:00
if ( is_module ( ) ) {
dev_err ( & pdev - > dev , " can't dispatch IRQs from modules \n " ) ;
goto no_irqs ;
}
2017-03-04 19:23:30 +03:00
irq_base = devm_irq_alloc_descs ( & pdev - > dev , - 1 ,
0 , TWL4030_GPIO_MAX , 0 ) ;
2012-03-01 01:48:32 +04:00
if ( irq_base < 0 ) {
dev_err ( & pdev - > dev , " Failed to alloc irq_descs \n " ) ;
return irq_base ;
2008-10-21 01:51:46 +04:00
}
2012-03-01 01:51:32 +04:00
irq_domain_add_legacy ( node , TWL4030_GPIO_MAX , irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
2012-03-01 01:48:32 +04:00
ret = twl4030_sih_setup ( & pdev - > dev , TWL4030_MODULE_GPIO , irq_base ) ;
if ( ret < 0 )
return ret ;
2012-12-06 14:52:05 +04:00
priv - > irq_base = irq_base ;
2012-03-01 01:48:32 +04:00
2008-10-21 01:51:46 +04:00
no_irqs :
2012-12-06 14:52:05 +04:00
priv - > gpio_chip = template_chip ;
priv - > gpio_chip . base = - 1 ;
priv - > gpio_chip . ngpio = TWL4030_GPIO_MAX ;
2015-11-04 11:56:26 +03:00
priv - > gpio_chip . parent = & pdev - > dev ;
2008-10-21 01:51:46 +04:00
2012-12-20 13:44:11 +04:00
mutex_init ( & priv - > mutex ) ;
2023-05-01 12:05:21 +03:00
pdata = of_gpio_twl4030 ( & pdev - > dev ) ;
2012-09-05 11:46:25 +04:00
if ( pdata = = NULL ) {
dev_err ( & pdev - > dev , " Platform data is missing \n " ) ;
return - ENXIO ;
2012-03-01 01:51:32 +04:00
}
2008-10-21 01:51:46 +04:00
2012-09-05 11:46:25 +04:00
/*
* NOTE : boards may waste power if they don ' t set pullups
* and pulldowns correctly . . . default for non - ULPI pins is
* pulldown , and some other pins may have external pullups
* or pulldowns . Careful !
*/
ret = gpio_twl4030_pulls ( pdata - > pullups , pdata - > pulldowns ) ;
if ( ret )
dev_dbg ( & pdev - > dev , " pullups %.05x %.05x --> %d \n " ,
pdata - > pullups , pdata - > pulldowns , ret ) ;
ret = gpio_twl4030_debounce ( pdata - > debounce , pdata - > mmc_cd ) ;
if ( ret )
dev_dbg ( & pdev - > dev , " debounce %.03x %.01x --> %d \n " ,
pdata - > debounce , pdata - > mmc_cd , ret ) ;
/*
* NOTE : we assume VIBRA_CTL . VIBRA_EN , in MODULE_AUDIO_VOICE ,
* is ( still ) clear if use_leds is set .
*/
if ( pdata - > use_leds )
2012-12-06 14:52:05 +04:00
priv - > gpio_chip . ngpio + = 2 ;
2012-09-05 11:46:25 +04:00
2023-05-15 20:40:26 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & priv - > gpio_chip , priv ) ;
2008-10-21 01:51:46 +04:00
if ( ret < 0 ) {
2012-03-01 01:48:32 +04:00
dev_err ( & pdev - > dev , " could not register gpiochip, %d \n " , ret ) ;
2012-12-06 14:52:05 +04:00
priv - > gpio_chip . ngpio = 0 ;
2023-05-15 20:40:26 +03:00
return ret ;
2012-09-05 04:43:30 +04:00
}
2023-05-01 12:05:21 +03:00
/*
* Special quirk for the OMAP3 to hog and export a WLAN power
* GPIO .
*/
if ( IS_ENABLED ( CONFIG_ARCH_OMAP3 ) & &
of_machine_is_compatible ( " compulab,omap3-sbc-t3730 " ) ) {
struct gpio_desc * d ;
2012-09-05 04:43:30 +04:00
2023-05-01 12:05:21 +03:00
d = gpiochip_request_own_desc ( & priv - > gpio_chip ,
2 , " wlan pwr " ,
GPIO_ACTIVE_HIGH ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( d ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( d ) ,
" unable to hog wlan pwr GPIO \n " ) ;
gpiod_export ( d , 0 ) ;
ret = devm_add_action_or_reset ( & pdev - > dev , gpio_twl4030_power_off_action , d ) ;
if ( ret )
return dev_err_probe ( & pdev - > dev , ret ,
" failed to install power off handler \n " ) ;
2008-10-21 01:51:46 +04:00
}
2023-05-15 20:40:26 +03:00
return 0 ;
2008-10-21 01:51:46 +04:00
}
2012-03-01 01:51:32 +04:00
static const struct of_device_id twl_gpio_match [ ] = {
{ . compatible = " ti,twl4030-gpio " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , twl_gpio_match ) ;
2008-10-21 01:51:46 +04:00
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS ( " platform:twl4030_gpio " ) ;
static struct platform_driver gpio_twl4030_driver = {
2012-03-01 01:51:32 +04:00
. driver = {
. name = " twl4030_gpio " ,
2013-09-28 16:04:09 +04:00
. of_match_table = twl_gpio_match ,
2012-03-01 01:51:32 +04:00
} ,
2008-10-21 01:51:46 +04:00
. probe = gpio_twl4030_probe ,
} ;
static int __init gpio_twl4030_init ( void )
{
return platform_driver_register ( & gpio_twl4030_driver ) ;
}
subsys_initcall ( gpio_twl4030_init ) ;
static void __exit gpio_twl4030_exit ( void )
{
platform_driver_unregister ( & gpio_twl4030_driver ) ;
}
module_exit ( gpio_twl4030_exit ) ;
MODULE_AUTHOR ( " Texas Instruments, Inc. " ) ;
MODULE_DESCRIPTION ( " GPIO interface for TWL4030 " ) ;
MODULE_LICENSE ( " GPL " ) ;