2009-09-22 16:46:39 -07:00
/*
* GPIO driver for Analog Devices ADP5520 MFD PMICs
*
* Copyright 2009 Analog Devices Inc .
*
* Licensed under the GPL - 2 or later .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/mfd/adp5520.h>
# include <linux/gpio.h>
struct adp5520_gpio {
struct device * master ;
struct gpio_chip gpio_chip ;
unsigned char lut [ ADP5520_MAXGPIOS ] ;
unsigned long output ;
} ;
static int adp5520_gpio_get_value ( struct gpio_chip * chip , unsigned off )
{
struct adp5520_gpio * dev ;
uint8_t reg_val ;
dev = container_of ( chip , struct adp5520_gpio , gpio_chip ) ;
/*
* There are dedicated registers for GPIO IN / OUT .
* Make sure we return the right value , even when configured as output
*/
if ( test_bit ( off , & dev - > output ) )
2009-10-10 13:54:03 -04:00
adp5520_read ( dev - > master , ADP5520_GPIO_OUT , & reg_val ) ;
2009-09-22 16:46:39 -07:00
else
2009-10-10 13:54:03 -04:00
adp5520_read ( dev - > master , ADP5520_GPIO_IN , & reg_val ) ;
2009-09-22 16:46:39 -07:00
return ! ! ( reg_val & dev - > lut [ off ] ) ;
}
static void adp5520_gpio_set_value ( struct gpio_chip * chip ,
unsigned off , int val )
{
struct adp5520_gpio * dev ;
dev = container_of ( chip , struct adp5520_gpio , gpio_chip ) ;
if ( val )
2009-10-10 13:54:03 -04:00
adp5520_set_bits ( dev - > master , ADP5520_GPIO_OUT , dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
else
2009-10-10 13:54:03 -04:00
adp5520_clr_bits ( dev - > master , ADP5520_GPIO_OUT , dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
}
static int adp5520_gpio_direction_input ( struct gpio_chip * chip , unsigned off )
{
struct adp5520_gpio * dev ;
dev = container_of ( chip , struct adp5520_gpio , gpio_chip ) ;
clear_bit ( off , & dev - > output ) ;
2009-10-10 13:54:03 -04:00
return adp5520_clr_bits ( dev - > master , ADP5520_GPIO_CFG_2 ,
dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
}
static int adp5520_gpio_direction_output ( struct gpio_chip * chip ,
unsigned off , int val )
{
struct adp5520_gpio * dev ;
int ret = 0 ;
dev = container_of ( chip , struct adp5520_gpio , gpio_chip ) ;
set_bit ( off , & dev - > output ) ;
if ( val )
2009-10-10 13:54:03 -04:00
ret | = adp5520_set_bits ( dev - > master , ADP5520_GPIO_OUT ,
dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
else
2009-10-10 13:54:03 -04:00
ret | = adp5520_clr_bits ( dev - > master , ADP5520_GPIO_OUT ,
dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
2009-10-10 13:54:03 -04:00
ret | = adp5520_set_bits ( dev - > master , ADP5520_GPIO_CFG_2 ,
dev - > lut [ off ] ) ;
2009-09-22 16:46:39 -07:00
return ret ;
}
static int __devinit adp5520_gpio_probe ( struct platform_device * pdev )
{
2009-10-10 13:54:03 -04:00
struct adp5520_gpio_platform_data * pdata = pdev - > dev . platform_data ;
2009-09-22 16:46:39 -07:00
struct adp5520_gpio * dev ;
struct gpio_chip * gc ;
int ret , i , gpios ;
unsigned char ctl_mask = 0 ;
if ( pdata = = NULL ) {
dev_err ( & pdev - > dev , " missing platform data \n " ) ;
return - ENODEV ;
}
if ( pdev - > id ! = ID_ADP5520 ) {
dev_err ( & pdev - > dev , " only ADP5520 supports GPIO \n " ) ;
return - ENODEV ;
}
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( dev = = NULL ) {
dev_err ( & pdev - > dev , " failed to alloc memory \n " ) ;
return - ENOMEM ;
}
dev - > master = pdev - > dev . parent ;
for ( gpios = 0 , i = 0 ; i < ADP5520_MAXGPIOS ; i + + )
if ( pdata - > gpio_en_mask & ( 1 < < i ) )
dev - > lut [ gpios + + ] = 1 < < i ;
if ( gpios < 1 ) {
ret = - EINVAL ;
goto err ;
}
gc = & dev - > gpio_chip ;
gc - > direction_input = adp5520_gpio_direction_input ;
gc - > direction_output = adp5520_gpio_direction_output ;
gc - > get = adp5520_gpio_get_value ;
gc - > set = adp5520_gpio_set_value ;
gc - > can_sleep = 1 ;
gc - > base = pdata - > gpio_start ;
gc - > ngpio = gpios ;
gc - > label = pdev - > name ;
gc - > owner = THIS_MODULE ;
2009-10-10 13:54:03 -04:00
ret = adp5520_clr_bits ( dev - > master , ADP5520_GPIO_CFG_1 ,
2009-09-22 16:46:39 -07:00
pdata - > gpio_en_mask ) ;
2009-10-10 13:54:03 -04:00
if ( pdata - > gpio_en_mask & ADP5520_GPIO_C3 )
ctl_mask | = ADP5520_C3_MODE ;
2009-09-22 16:46:39 -07:00
2009-10-10 13:54:03 -04:00
if ( pdata - > gpio_en_mask & ADP5520_GPIO_R3 )
ctl_mask | = ADP5520_R3_MODE ;
2009-09-22 16:46:39 -07:00
if ( ctl_mask )
2009-10-10 13:54:03 -04:00
ret = adp5520_set_bits ( dev - > master , ADP5520_LED_CONTROL ,
2009-09-22 16:46:39 -07:00
ctl_mask ) ;
2009-10-10 13:54:03 -04:00
ret | = adp5520_set_bits ( dev - > master , ADP5520_GPIO_PULLUP ,
2009-09-22 16:46:39 -07:00
pdata - > gpio_pullup_mask ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to write \n " ) ;
goto err ;
}
ret = gpiochip_add ( & dev - > gpio_chip ) ;
if ( ret )
goto err ;
platform_set_drvdata ( pdev , dev ) ;
return 0 ;
err :
kfree ( dev ) ;
return ret ;
}
static int __devexit adp5520_gpio_remove ( struct platform_device * pdev )
{
struct adp5520_gpio * dev ;
int ret ;
dev = platform_get_drvdata ( pdev ) ;
ret = gpiochip_remove ( & dev - > gpio_chip ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s failed, %d \n " ,
" gpiochip_remove() " , ret ) ;
return ret ;
}
kfree ( dev ) ;
return 0 ;
}
static struct platform_driver adp5520_gpio_driver = {
. driver = {
. name = " adp5520-gpio " ,
. owner = THIS_MODULE ,
} ,
. probe = adp5520_gpio_probe ,
. remove = __devexit_p ( adp5520_gpio_remove ) ,
} ;
static int __init adp5520_gpio_init ( void )
{
return platform_driver_register ( & adp5520_gpio_driver ) ;
}
module_init ( adp5520_gpio_init ) ;
static void __exit adp5520_gpio_exit ( void )
{
platform_driver_unregister ( & adp5520_gpio_driver ) ;
}
module_exit ( adp5520_gpio_exit ) ;
MODULE_AUTHOR ( " Michael Hennerich <hennerich@blackfin.uclinux.org> " ) ;
MODULE_DESCRIPTION ( " GPIO ADP5520 Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:adp5520-gpio " ) ;