2012-06-23 13:29:25 +01:00
/*
* gpiolib support for Wolfson Arizona class devices
*
* Copyright 2012 Wolfson Microelectronics PLC .
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/gpio.h>
# include <linux/platform_device.h>
2017-04-05 16:50:46 +01:00
# include <linux/pm_runtime.h>
2012-06-23 13:29:25 +01:00
# include <linux/seq_file.h>
# include <linux/mfd/arizona/core.h>
# include <linux/mfd/arizona/pdata.h>
# include <linux/mfd/arizona/registers.h>
struct arizona_gpio {
struct arizona * arizona ;
struct gpio_chip gpio_chip ;
} ;
static int arizona_gpio_direction_in ( struct gpio_chip * chip , unsigned offset )
{
2015-12-04 15:31:31 +01:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 13:29:25 +01:00
struct arizona * arizona = arizona_gpio - > arizona ;
2017-05-23 15:47:30 +01:00
bool persistent = gpiochip_line_is_persistent ( chip , offset ) ;
bool change ;
int ret ;
2012-06-23 13:29:25 +01:00
2017-05-23 15:47:30 +01:00
ret = regmap_update_bits_check ( arizona - > regmap ,
ARIZONA_GPIO1_CTRL + offset ,
ARIZONA_GPN_DIR , ARIZONA_GPN_DIR ,
& change ) ;
if ( ret < 0 )
return ret ;
if ( change & & persistent ) {
pm_runtime_mark_last_busy ( chip - > parent ) ;
pm_runtime_put_autosuspend ( chip - > parent ) ;
}
return 0 ;
2012-06-23 13:29:25 +01:00
}
static int arizona_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2015-12-04 15:31:31 +01:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 13:29:25 +01:00
struct arizona * arizona = arizona_gpio - > arizona ;
2017-04-05 16:50:46 +01:00
unsigned int reg , val ;
2012-06-23 13:29:25 +01:00
int ret ;
2017-04-05 16:50:46 +01:00
reg = ARIZONA_GPIO1_CTRL + offset ;
ret = regmap_read ( arizona - > regmap , reg , & val ) ;
2012-06-23 13:29:25 +01:00
if ( ret < 0 )
return ret ;
2017-04-05 16:50:46 +01:00
/* Resume to read actual registers for input pins */
2017-04-19 10:30:44 +01:00
if ( val & ARIZONA_GPN_DIR ) {
2017-04-05 16:50:46 +01:00
ret = pm_runtime_get_sync ( chip - > parent ) ;
if ( ret < 0 ) {
dev_err ( chip - > parent , " Failed to resume: %d \n " , ret ) ;
return ret ;
}
/* Register is cached, drop it to ensure a physical read */
ret = regcache_drop_region ( arizona - > regmap , reg , reg ) ;
if ( ret < 0 ) {
dev_err ( chip - > parent , " Failed to drop cache: %d \n " ,
ret ) ;
return ret ;
}
ret = regmap_read ( arizona - > regmap , reg , & val ) ;
if ( ret < 0 )
return ret ;
pm_runtime_mark_last_busy ( chip - > parent ) ;
pm_runtime_put_autosuspend ( chip - > parent ) ;
}
2012-06-23 13:29:25 +01:00
if ( val & ARIZONA_GPN_LVL )
return 1 ;
else
return 0 ;
}
static int arizona_gpio_direction_out ( struct gpio_chip * chip ,
unsigned offset , int value )
{
2015-12-04 15:31:31 +01:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 13:29:25 +01:00
struct arizona * arizona = arizona_gpio - > arizona ;
2017-05-23 15:47:30 +01:00
bool persistent = gpiochip_line_is_persistent ( chip , offset ) ;
unsigned int val ;
int ret ;
ret = regmap_read ( arizona - > regmap , ARIZONA_GPIO1_CTRL + offset , & val ) ;
if ( ret < 0 )
return ret ;
if ( ( val & ARIZONA_GPN_DIR ) & & persistent ) {
ret = pm_runtime_get_sync ( chip - > parent ) ;
if ( ret < 0 ) {
dev_err ( chip - > parent , " Failed to resume: %d \n " , ret ) ;
return ret ;
}
}
2012-06-23 13:29:25 +01:00
if ( value )
value = ARIZONA_GPN_LVL ;
return regmap_update_bits ( arizona - > regmap , ARIZONA_GPIO1_CTRL + offset ,
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL , value ) ;
}
static void arizona_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
2015-12-04 15:31:31 +01:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 13:29:25 +01:00
struct arizona * arizona = arizona_gpio - > arizona ;
if ( value )
value = ARIZONA_GPN_LVL ;
regmap_update_bits ( arizona - > regmap , ARIZONA_GPIO1_CTRL + offset ,
ARIZONA_GPN_LVL , value ) ;
}
2016-09-11 14:14:37 +02:00
static const struct gpio_chip template_chip = {
2012-06-23 13:29:25 +01:00
. label = " arizona " ,
. owner = THIS_MODULE ,
. direction_input = arizona_gpio_direction_in ,
. get = arizona_gpio_get ,
. direction_output = arizona_gpio_direction_out ,
. set = arizona_gpio_set ,
2013-12-04 14:42:46 +01:00
. can_sleep = true ,
2012-06-23 13:29:25 +01:00
} ;
2012-11-19 13:22:34 -05:00
static int arizona_gpio_probe ( struct platform_device * pdev )
2012-06-23 13:29:25 +01:00
{
struct arizona * arizona = dev_get_drvdata ( pdev - > dev . parent ) ;
2013-07-30 17:08:05 +09:00
struct arizona_pdata * pdata = dev_get_platdata ( arizona - > dev ) ;
2012-06-23 13:29:25 +01:00
struct arizona_gpio * arizona_gpio ;
int ret ;
arizona_gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * arizona_gpio ) ,
GFP_KERNEL ) ;
2015-03-31 09:49:11 +05:30
if ( ! arizona_gpio )
2012-06-23 13:29:25 +01:00
return - ENOMEM ;
arizona_gpio - > arizona = arizona ;
arizona_gpio - > gpio_chip = template_chip ;
2015-11-04 09:56:26 +01:00
arizona_gpio - > gpio_chip . parent = & pdev - > dev ;
2013-09-29 21:00:37 +01:00
# ifdef CONFIG_OF_GPIO
arizona_gpio - > gpio_chip . of_node = arizona - > dev - > of_node ;
# endif
2012-06-23 13:29:25 +01:00
switch ( arizona - > type ) {
case WM5102 :
case WM5110 :
2015-01-17 15:21:25 +00:00
case WM8280 :
2013-09-11 14:03:27 +01:00
case WM8997 :
2015-09-10 16:59:01 +01:00
case WM8998 :
case WM1814 :
2012-06-23 13:29:25 +01:00
arizona_gpio - > gpio_chip . ngpio = 5 ;
break ;
2015-11-03 15:08:34 +00:00
case WM1831 :
case CS47L24 :
arizona_gpio - > gpio_chip . ngpio = 2 ;
break ;
2012-06-23 13:29:25 +01:00
default :
dev_err ( & pdev - > dev , " Unknown chip variant %d \n " ,
arizona - > type ) ;
return - EINVAL ;
}
if ( pdata & & pdata - > gpio_base )
arizona_gpio - > gpio_chip . base = pdata - > gpio_base ;
else
arizona_gpio - > gpio_chip . base = - 1 ;
2017-05-23 15:47:30 +01:00
pm_runtime_enable ( & pdev - > dev ) ;
2016-02-22 17:43:28 +05:30
ret = devm_gpiochip_add_data ( & pdev - > dev , & arizona_gpio - > gpio_chip ,
arizona_gpio ) ;
2012-06-23 13:29:25 +01:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Could not register gpiochip, %d \n " ,
ret ) ;
2016-11-25 13:48:30 +00:00
return ret ;
2012-06-23 13:29:25 +01:00
}
2016-11-25 13:48:30 +00:00
return 0 ;
2012-06-23 13:29:25 +01:00
}
static struct platform_driver arizona_gpio_driver = {
. driver . name = " arizona-gpio " ,
. probe = arizona_gpio_probe ,
} ;
module_platform_driver ( arizona_gpio_driver ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_DESCRIPTION ( " GPIO interface for Arizona devices " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:arizona-gpio " ) ;