2013-01-03 14:46:59 +04:00
/*
* TI Palma series PMIC ' s GPIO driver .
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*
* Author : Laxman Dewangan < ldewangan @ nvidia . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/gpio.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mfd/palmas.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
struct palmas_gpio {
struct gpio_chip gpio_chip ;
struct palmas * palmas ;
} ;
2013-09-03 18:28:03 +04:00
struct palmas_device_data {
int ngpio ;
} ;
2013-01-03 14:46:59 +04:00
static inline struct palmas_gpio * to_palmas_gpio ( struct gpio_chip * chip )
{
return container_of ( chip , struct palmas_gpio , gpio_chip ) ;
}
static int palmas_gpio_get ( struct gpio_chip * gc , unsigned offset )
{
struct palmas_gpio * pg = to_palmas_gpio ( gc ) ;
struct palmas * palmas = pg - > palmas ;
unsigned int val ;
int ret ;
2013-09-03 18:28:03 +04:00
unsigned int reg ;
int gpio16 = ( offset / 8 ) ;
offset % = 8 ;
reg = ( gpio16 ) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR ;
2013-01-03 14:46:59 +04:00
2013-09-03 18:28:03 +04:00
ret = palmas_read ( palmas , PALMAS_GPIO_BASE , reg , & val ) ;
2013-01-03 14:46:59 +04:00
if ( ret < 0 ) {
2013-09-03 18:28:03 +04:00
dev_err ( gc - > dev , " Reg 0x%02x read failed, %d \n " , reg , ret ) ;
2013-07-27 00:39:53 +04:00
return ret ;
}
2013-09-03 18:28:03 +04:00
if ( val & BIT ( offset ) )
reg = ( gpio16 ) ? PALMAS_GPIO_DATA_OUT2 : PALMAS_GPIO_DATA_OUT ;
else
reg = ( gpio16 ) ? PALMAS_GPIO_DATA_IN2 : PALMAS_GPIO_DATA_IN ;
ret = palmas_read ( palmas , PALMAS_GPIO_BASE , reg , & val ) ;
2013-07-27 00:39:53 +04:00
if ( ret < 0 ) {
2013-09-03 18:28:03 +04:00
dev_err ( gc - > dev , " Reg 0x%02x read failed, %d \n " , reg , ret ) ;
2013-01-03 14:46:59 +04:00
return ret ;
}
return ! ! ( val & BIT ( offset ) ) ;
}
static void palmas_gpio_set ( struct gpio_chip * gc , unsigned offset ,
int value )
{
struct palmas_gpio * pg = to_palmas_gpio ( gc ) ;
struct palmas * palmas = pg - > palmas ;
int ret ;
2013-09-03 18:28:03 +04:00
unsigned int reg ;
int gpio16 = ( offset / 8 ) ;
2013-01-03 14:46:59 +04:00
2013-09-03 18:28:03 +04:00
offset % = 8 ;
if ( gpio16 )
reg = ( value ) ?
PALMAS_GPIO_SET_DATA_OUT2 : PALMAS_GPIO_CLEAR_DATA_OUT2 ;
2013-01-03 14:46:59 +04:00
else
2013-09-03 18:28:03 +04:00
reg = ( value ) ?
PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT ;
ret = palmas_write ( palmas , PALMAS_GPIO_BASE , reg , BIT ( offset ) ) ;
2013-01-03 14:46:59 +04:00
if ( ret < 0 )
2013-09-03 18:28:03 +04:00
dev_err ( gc - > dev , " Reg 0x%02x write failed, %d \n " , reg , ret ) ;
2013-01-03 14:46:59 +04:00
}
static int palmas_gpio_output ( struct gpio_chip * gc , unsigned offset ,
int value )
{
struct palmas_gpio * pg = to_palmas_gpio ( gc ) ;
struct palmas * palmas = pg - > palmas ;
int ret ;
2013-09-03 18:28:03 +04:00
unsigned int reg ;
int gpio16 = ( offset / 8 ) ;
offset % = 8 ;
reg = ( gpio16 ) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR ;
2013-01-03 14:46:59 +04:00
/* Set the initial value */
palmas_gpio_set ( gc , offset , value ) ;
2013-09-03 18:28:03 +04:00
ret = palmas_update_bits ( palmas , PALMAS_GPIO_BASE , reg ,
BIT ( offset ) , BIT ( offset ) ) ;
2013-01-03 14:46:59 +04:00
if ( ret < 0 )
2013-09-03 18:28:03 +04:00
dev_err ( gc - > dev , " Reg 0x%02x update failed, %d \n " , reg , ret ) ;
2013-01-03 14:46:59 +04:00
return ret ;
}
static int palmas_gpio_input ( struct gpio_chip * gc , unsigned offset )
{
struct palmas_gpio * pg = to_palmas_gpio ( gc ) ;
struct palmas * palmas = pg - > palmas ;
int ret ;
2013-09-03 18:28:03 +04:00
unsigned int reg ;
int gpio16 = ( offset / 8 ) ;
offset % = 8 ;
reg = ( gpio16 ) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR ;
2013-01-03 14:46:59 +04:00
2013-09-03 18:28:03 +04:00
ret = palmas_update_bits ( palmas , PALMAS_GPIO_BASE , reg , BIT ( offset ) , 0 ) ;
2013-01-03 14:46:59 +04:00
if ( ret < 0 )
2013-09-03 18:28:03 +04:00
dev_err ( gc - > dev , " Reg 0x%02x update failed, %d \n " , reg , ret ) ;
2013-01-03 14:46:59 +04:00
return ret ;
}
static int palmas_gpio_to_irq ( struct gpio_chip * gc , unsigned offset )
{
struct palmas_gpio * pg = to_palmas_gpio ( gc ) ;
struct palmas * palmas = pg - > palmas ;
return palmas_irq_get_virq ( palmas , PALMAS_GPIO_0_IRQ + offset ) ;
}
2013-09-03 18:28:03 +04:00
static const struct palmas_device_data palmas_dev_data = {
. ngpio = 8 ,
} ;
static const struct palmas_device_data tps80036_dev_data = {
. ngpio = 16 ,
} ;
static struct of_device_id of_palmas_gpio_match [ ] = {
{ . compatible = " ti,palmas-gpio " , . data = & palmas_dev_data , } ,
{ . compatible = " ti,tps65913-gpio " , . data = & palmas_dev_data , } ,
{ . compatible = " ti,tps65914-gpio " , . data = & palmas_dev_data , } ,
{ . compatible = " ti,tps80036-gpio " , . data = & tps80036_dev_data , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_palmas_gpio_match ) ;
2013-01-03 14:46:59 +04:00
static int palmas_gpio_probe ( struct platform_device * pdev )
{
struct palmas * palmas = dev_get_drvdata ( pdev - > dev . parent ) ;
struct palmas_platform_data * palmas_pdata ;
struct palmas_gpio * palmas_gpio ;
int ret ;
2013-09-03 18:28:03 +04:00
const struct of_device_id * match ;
const struct palmas_device_data * dev_data ;
match = of_match_device ( of_palmas_gpio_match , & pdev - > dev ) ;
dev_data = match - > data ;
if ( ! dev_data )
dev_data = & palmas_dev_data ;
2013-01-03 14:46:59 +04:00
palmas_gpio = devm_kzalloc ( & pdev - > dev ,
sizeof ( * palmas_gpio ) , GFP_KERNEL ) ;
if ( ! palmas_gpio ) {
dev_err ( & pdev - > dev , " Could not allocate palmas_gpio \n " ) ;
return - ENOMEM ;
}
palmas_gpio - > palmas = palmas ;
palmas_gpio - > gpio_chip . owner = THIS_MODULE ;
palmas_gpio - > gpio_chip . label = dev_name ( & pdev - > dev ) ;
2013-09-03 18:28:03 +04:00
palmas_gpio - > gpio_chip . ngpio = dev_data - > ngpio ;
2013-12-04 17:42:46 +04:00
palmas_gpio - > gpio_chip . can_sleep = true ;
2013-01-03 14:46:59 +04:00
palmas_gpio - > gpio_chip . direction_input = palmas_gpio_input ;
palmas_gpio - > gpio_chip . direction_output = palmas_gpio_output ;
palmas_gpio - > gpio_chip . to_irq = palmas_gpio_to_irq ;
palmas_gpio - > gpio_chip . set = palmas_gpio_set ;
palmas_gpio - > gpio_chip . get = palmas_gpio_get ;
palmas_gpio - > gpio_chip . dev = & pdev - > dev ;
# ifdef CONFIG_OF_GPIO
2013-07-23 11:36:04 +04:00
palmas_gpio - > gpio_chip . of_node = pdev - > dev . of_node ;
2013-01-03 14:46:59 +04:00
# endif
palmas_pdata = dev_get_platdata ( palmas - > dev ) ;
if ( palmas_pdata & & palmas_pdata - > gpio_base )
palmas_gpio - > gpio_chip . base = palmas_pdata - > gpio_base ;
else
palmas_gpio - > gpio_chip . base = - 1 ;
ret = gpiochip_add ( & palmas_gpio - > gpio_chip ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Could not register gpiochip, %d \n " , ret ) ;
return ret ;
}
platform_set_drvdata ( pdev , palmas_gpio ) ;
return ret ;
}
static int palmas_gpio_remove ( struct platform_device * pdev )
{
struct palmas_gpio * palmas_gpio = platform_get_drvdata ( pdev ) ;
return gpiochip_remove ( & palmas_gpio - > gpio_chip ) ;
}
static struct platform_driver palmas_gpio_driver = {
. driver . name = " palmas-gpio " ,
. driver . owner = THIS_MODULE ,
2013-07-23 11:36:04 +04:00
. driver . of_match_table = of_palmas_gpio_match ,
2013-01-03 14:46:59 +04:00
. probe = palmas_gpio_probe ,
. remove = palmas_gpio_remove ,
} ;
static int __init palmas_gpio_init ( void )
{
return platform_driver_register ( & palmas_gpio_driver ) ;
}
subsys_initcall ( palmas_gpio_init ) ;
static void __exit palmas_gpio_exit ( void )
{
platform_driver_unregister ( & palmas_gpio_driver ) ;
}
module_exit ( palmas_gpio_exit ) ;
MODULE_ALIAS ( " platform:palmas-gpio " ) ;
MODULE_AUTHOR ( " Laxman Dewangan <ldewangan@nvidia.com> " ) ;
MODULE_DESCRIPTION ( " GPIO driver for TI Palmas series PMICs " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;