2012-06-23 16:29:25 +04: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 18:50:46 +03:00
# include <linux/pm_runtime.h>
2012-06-23 16:29:25 +04: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 17:31:31 +03:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 16:29:25 +04:00
struct arizona * arizona = arizona_gpio - > arizona ;
return regmap_update_bits ( arizona - > regmap , ARIZONA_GPIO1_CTRL + offset ,
ARIZONA_GPN_DIR , ARIZONA_GPN_DIR ) ;
}
static int arizona_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2015-12-04 17:31:31 +03:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 16:29:25 +04:00
struct arizona * arizona = arizona_gpio - > arizona ;
2017-04-05 18:50:46 +03:00
unsigned int reg , val ;
2012-06-23 16:29:25 +04:00
int ret ;
2017-04-05 18:50:46 +03:00
reg = ARIZONA_GPIO1_CTRL + offset ;
ret = regmap_read ( arizona - > regmap , reg , & val ) ;
2012-06-23 16:29:25 +04:00
if ( ret < 0 )
return ret ;
2017-04-05 18:50:46 +03:00
/* Resume to read actual registers for input pins */
2017-04-19 12:30:44 +03:00
if ( val & ARIZONA_GPN_DIR ) {
2017-04-05 18:50:46 +03: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 16:29:25 +04: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 17:31:31 +03:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 16:29:25 +04:00
struct arizona * arizona = arizona_gpio - > arizona ;
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 17:31:31 +03:00
struct arizona_gpio * arizona_gpio = gpiochip_get_data ( chip ) ;
2012-06-23 16:29:25 +04: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 15:14:37 +03:00
static const struct gpio_chip template_chip = {
2012-06-23 16:29:25 +04: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 17:42:46 +04:00
. can_sleep = true ,
2012-06-23 16:29:25 +04:00
} ;
2012-11-19 22:22:34 +04:00
static int arizona_gpio_probe ( struct platform_device * pdev )
2012-06-23 16:29:25 +04:00
{
struct arizona * arizona = dev_get_drvdata ( pdev - > dev . parent ) ;
2013-07-30 12:08:05 +04:00
struct arizona_pdata * pdata = dev_get_platdata ( arizona - > dev ) ;
2012-06-23 16:29:25 +04:00
struct arizona_gpio * arizona_gpio ;
int ret ;
arizona_gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * arizona_gpio ) ,
GFP_KERNEL ) ;
2015-03-31 07:19:11 +03:00
if ( ! arizona_gpio )
2012-06-23 16:29:25 +04:00
return - ENOMEM ;
arizona_gpio - > arizona = arizona ;
arizona_gpio - > gpio_chip = template_chip ;
2015-11-04 11:56:26 +03:00
arizona_gpio - > gpio_chip . parent = & pdev - > dev ;
2013-09-30 00:00:37 +04:00
# ifdef CONFIG_OF_GPIO
arizona_gpio - > gpio_chip . of_node = arizona - > dev - > of_node ;
# endif
2012-06-23 16:29:25 +04:00
switch ( arizona - > type ) {
case WM5102 :
case WM5110 :
2015-01-17 18:21:25 +03:00
case WM8280 :
2013-09-11 17:03:27 +04:00
case WM8997 :
2015-09-10 18:59:01 +03:00
case WM8998 :
case WM1814 :
2012-06-23 16:29:25 +04:00
arizona_gpio - > gpio_chip . ngpio = 5 ;
break ;
2015-11-03 18:08:34 +03:00
case WM1831 :
case CS47L24 :
arizona_gpio - > gpio_chip . ngpio = 2 ;
break ;
2012-06-23 16:29:25 +04: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 ;
2016-02-22 15:13:28 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & arizona_gpio - > gpio_chip ,
arizona_gpio ) ;
2012-06-23 16:29:25 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Could not register gpiochip, %d \n " ,
ret ) ;
2016-11-25 16:48:30 +03:00
return ret ;
2012-06-23 16:29:25 +04:00
}
2016-11-25 16:48:30 +03:00
return 0 ;
2012-06-23 16:29:25 +04: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 " ) ;