2010-03-21 03:06:09 +03:00
/*
* RDC321x GPIO driver
*
* Copyright ( C ) 2008 , Volker Weiss < dev @ tintuc . de >
* Copyright ( C ) 2007 - 2010 Florian Fainelli < florian @ openwrt . org >
*
* 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 .
*
* This program is distributed in the hope that 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 , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/platform_device.h>
# include <linux/pci.h>
# include <linux/gpio.h>
# include <linux/mfd/rdc321x.h>
2010-04-02 20:24:20 +04:00
# include <linux/slab.h>
2010-03-21 03:06:09 +03:00
struct rdc321x_gpio {
spinlock_t lock ;
struct pci_dev * sb_pdev ;
u32 data_reg [ 2 ] ;
int reg1_ctrl_base ;
int reg1_data_base ;
int reg2_ctrl_base ;
int reg2_data_base ;
struct gpio_chip chip ;
} ;
/* read GPIO pin */
static int rdc_gpio_get_value ( struct gpio_chip * chip , unsigned gpio )
{
struct rdc321x_gpio * gpch ;
u32 value = 0 ;
int reg ;
gpch = container_of ( chip , struct rdc321x_gpio , chip ) ;
reg = gpio < 32 ? gpch - > reg1_data_base : gpch - > reg2_data_base ;
spin_lock ( & gpch - > lock ) ;
pci_write_config_dword ( gpch - > sb_pdev , reg ,
gpch - > data_reg [ gpio < 32 ? 0 : 1 ] ) ;
pci_read_config_dword ( gpch - > sb_pdev , reg , & value ) ;
spin_unlock ( & gpch - > lock ) ;
return ( 1 < < ( gpio & 0x1f ) ) & value ? 1 : 0 ;
}
static void rdc_gpio_set_value_impl ( struct gpio_chip * chip ,
unsigned gpio , int value )
{
struct rdc321x_gpio * gpch ;
int reg = ( gpio < 32 ) ? 0 : 1 ;
gpch = container_of ( chip , struct rdc321x_gpio , chip ) ;
if ( value )
gpch - > data_reg [ reg ] | = 1 < < ( gpio & 0x1f ) ;
else
gpch - > data_reg [ reg ] & = ~ ( 1 < < ( gpio & 0x1f ) ) ;
pci_write_config_dword ( gpch - > sb_pdev ,
2010-05-16 14:02:18 +04:00
reg ? gpch - > reg2_data_base : gpch - > reg1_data_base ,
2010-03-21 03:06:09 +03:00
gpch - > data_reg [ reg ] ) ;
}
/* set GPIO pin to value */
static void rdc_gpio_set_value ( struct gpio_chip * chip ,
unsigned gpio , int value )
{
struct rdc321x_gpio * gpch ;
gpch = container_of ( chip , struct rdc321x_gpio , chip ) ;
spin_lock ( & gpch - > lock ) ;
rdc_gpio_set_value_impl ( chip , gpio , value ) ;
spin_unlock ( & gpch - > lock ) ;
}
static int rdc_gpio_config ( struct gpio_chip * chip ,
unsigned gpio , int value )
{
struct rdc321x_gpio * gpch ;
int err ;
u32 reg ;
gpch = container_of ( chip , struct rdc321x_gpio , chip ) ;
spin_lock ( & gpch - > lock ) ;
err = pci_read_config_dword ( gpch - > sb_pdev , gpio < 32 ?
gpch - > reg1_ctrl_base : gpch - > reg2_ctrl_base , & reg ) ;
if ( err )
goto unlock ;
reg | = 1 < < ( gpio & 0x1f ) ;
err = pci_write_config_dword ( gpch - > sb_pdev , gpio < 32 ?
gpch - > reg1_ctrl_base : gpch - > reg2_ctrl_base , reg ) ;
if ( err )
goto unlock ;
rdc_gpio_set_value_impl ( chip , gpio , value ) ;
unlock :
spin_unlock ( & gpch - > lock ) ;
return err ;
}
/* configure GPIO pin as input */
static int rdc_gpio_direction_input ( struct gpio_chip * chip , unsigned gpio )
{
return rdc_gpio_config ( chip , gpio , 1 ) ;
}
/*
* Cache the initial value of both GPIO data registers
*/
2012-11-19 22:22:34 +04:00
static int rdc321x_gpio_probe ( struct platform_device * pdev )
2010-03-21 03:06:09 +03:00
{
int err ;
struct resource * r ;
struct rdc321x_gpio * rdc321x_gpio_dev ;
struct rdc321x_gpio_pdata * pdata ;
2013-07-30 12:08:05 +04:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
2010-03-21 03:06:09 +03:00
if ( ! pdata ) {
dev_err ( & pdev - > dev , " no platform data supplied \n " ) ;
return - ENODEV ;
}
2014-04-11 10:16:01 +04:00
rdc321x_gpio_dev = devm_kzalloc ( & pdev - > dev , sizeof ( struct rdc321x_gpio ) ,
GFP_KERNEL ) ;
2010-03-21 03:06:09 +03:00
if ( ! rdc321x_gpio_dev ) {
dev_err ( & pdev - > dev , " failed to allocate private data \n " ) ;
return - ENOMEM ;
}
2010-05-16 00:58:27 +04:00
r = platform_get_resource_byname ( pdev , IORESOURCE_IO , " gpio-reg1 " ) ;
2010-03-21 03:06:09 +03:00
if ( ! r ) {
dev_err ( & pdev - > dev , " failed to get gpio-reg1 resource \n " ) ;
2014-04-11 10:16:01 +04:00
return - ENODEV ;
2010-03-21 03:06:09 +03:00
}
spin_lock_init ( & rdc321x_gpio_dev - > lock ) ;
rdc321x_gpio_dev - > sb_pdev = pdata - > sb_pdev ;
rdc321x_gpio_dev - > reg1_ctrl_base = r - > start ;
rdc321x_gpio_dev - > reg1_data_base = r - > start + 0x4 ;
2010-05-16 00:58:27 +04:00
r = platform_get_resource_byname ( pdev , IORESOURCE_IO , " gpio-reg2 " ) ;
2010-03-21 03:06:09 +03:00
if ( ! r ) {
dev_err ( & pdev - > dev , " failed to get gpio-reg2 resource \n " ) ;
2014-04-11 10:16:01 +04:00
return - ENODEV ;
2010-03-21 03:06:09 +03:00
}
rdc321x_gpio_dev - > reg2_ctrl_base = r - > start ;
rdc321x_gpio_dev - > reg2_data_base = r - > start + 0x4 ;
rdc321x_gpio_dev - > chip . label = " rdc321x-gpio " ;
2012-09-01 10:11:22 +04:00
rdc321x_gpio_dev - > chip . owner = THIS_MODULE ;
2010-03-21 03:06:09 +03:00
rdc321x_gpio_dev - > chip . direction_input = rdc_gpio_direction_input ;
rdc321x_gpio_dev - > chip . direction_output = rdc_gpio_config ;
rdc321x_gpio_dev - > chip . get = rdc_gpio_get_value ;
rdc321x_gpio_dev - > chip . set = rdc_gpio_set_value ;
rdc321x_gpio_dev - > chip . base = 0 ;
rdc321x_gpio_dev - > chip . ngpio = pdata - > max_gpios ;
platform_set_drvdata ( pdev , rdc321x_gpio_dev ) ;
/* This might not be, what others (BIOS, bootloader, etc.)
wrote to these registers before , but it ' s a good guess . Still
better than just using 0xffffffff . */
err = pci_read_config_dword ( rdc321x_gpio_dev - > sb_pdev ,
rdc321x_gpio_dev - > reg1_data_base ,
& rdc321x_gpio_dev - > data_reg [ 0 ] ) ;
if ( err )
2014-04-11 10:16:01 +04:00
return err ;
2010-03-21 03:06:09 +03:00
err = pci_read_config_dword ( rdc321x_gpio_dev - > sb_pdev ,
rdc321x_gpio_dev - > reg2_data_base ,
& rdc321x_gpio_dev - > data_reg [ 1 ] ) ;
if ( err )
2014-04-11 10:16:01 +04:00
return err ;
2010-03-21 03:06:09 +03:00
dev_info ( & pdev - > dev , " registering %d GPIOs \n " ,
rdc321x_gpio_dev - > chip . ngpio ) ;
return gpiochip_add ( & rdc321x_gpio_dev - > chip ) ;
}
2012-11-19 22:25:50 +04:00
static int rdc321x_gpio_remove ( struct platform_device * pdev )
2010-03-21 03:06:09 +03:00
{
int ret ;
struct rdc321x_gpio * rdc321x_gpio_dev = platform_get_drvdata ( pdev ) ;
ret = gpiochip_remove ( & rdc321x_gpio_dev - > chip ) ;
if ( ret )
dev_err ( & pdev - > dev , " failed to unregister chip \n " ) ;
return ret ;
}
static struct platform_driver rdc321x_gpio_driver = {
. driver . name = " rdc321x-gpio " ,
. driver . owner = THIS_MODULE ,
. probe = rdc321x_gpio_probe ,
2012-11-19 22:20:08 +04:00
. remove = rdc321x_gpio_remove ,
2010-03-21 03:06:09 +03:00
} ;
2011-12-07 20:24:00 +04:00
module_platform_driver ( rdc321x_gpio_driver ) ;
2010-03-21 03:06:09 +03:00
MODULE_AUTHOR ( " Florian Fainelli <florian@openwrt.org> " ) ;
MODULE_DESCRIPTION ( " RDC321x GPIO driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:rdc321x-gpio " ) ;