2018-09-12 15:13:17 +03:00
// SPDX-License-Identifier: GPL-2.0+
2010-10-01 00:55:48 +04:00
/*
* Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
*
* Copyright ( C ) 2009 VIA Technologies , Inc .
* Copyright ( C ) 2010 One Laptop per Child
* Author : Harald Welte < HaraldWelte @ viatech . com >
* All rights reserved .
*/
# include <linux/kernel.h>
# include <linux/module.h>
2018-09-12 15:11:59 +03:00
# include <linux/gpio/driver.h>
2011-05-25 04:13:43 +04:00
# include <linux/slab.h>
2010-10-01 00:55:48 +04:00
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/pci.h>
# include <linux/io.h>
# define MODULE_NAME "vx855_gpio"
/* The VX855 south bridge has the following GPIO pins:
* GPI 0. . .13 General Purpose Input
* GPO 0. . .12 General Purpose Output
* GPIO 0. . .14 General Purpose I / O ( Open - Drain )
*/
# define NR_VX855_GPI 14
# define NR_VX855_GPO 13
# define NR_VX855_GPIO 15
# define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO)
# define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO)
struct vx855_gpio {
struct gpio_chip gpio ;
spinlock_t lock ;
u32 io_gpi ;
u32 io_gpo ;
} ;
/* resolve a GPIx into the corresponding bit position */
static inline u_int32_t gpi_i_bit ( int i )
{
if ( i < 10 )
return 1 < < i ;
else
return 1 < < ( i + 14 ) ;
}
static inline u_int32_t gpo_o_bit ( int i )
{
if ( i < 11 )
return 1 < < i ;
else
return 1 < < ( i + 14 ) ;
}
static inline u_int32_t gpio_i_bit ( int i )
{
if ( i < 14 )
return 1 < < ( i + 10 ) ;
else
return 1 < < ( i + 14 ) ;
}
static inline u_int32_t gpio_o_bit ( int i )
{
if ( i < 14 )
return 1 < < ( i + 11 ) ;
else
return 1 < < ( i + 13 ) ;
}
2020-01-12 17:33:12 +03:00
/* Mapping between numeric GPIO ID and the actual GPIO hardware numbering:
2010-10-01 00:55:48 +04:00
* 0. .13 GPI 0. .13
* 14. .26 GPO 0. .12
* 27. .41 GPIO 0. .14
*/
static int vx855gpio_direction_input ( struct gpio_chip * gpio ,
unsigned int nr )
{
2015-12-07 17:09:57 +03:00
struct vx855_gpio * vg = gpiochip_get_data ( gpio ) ;
2010-10-01 00:55:48 +04:00
unsigned long flags ;
u_int32_t reg_out ;
/* Real GPI bits are always in input direction */
if ( nr < NR_VX855_GPI )
return 0 ;
/* Real GPO bits cannot be put in output direction */
if ( nr < NR_VX855_GPInO )
return - EINVAL ;
/* Open Drain GPIO have to be set to one */
spin_lock_irqsave ( & vg - > lock , flags ) ;
reg_out = inl ( vg - > io_gpo ) ;
reg_out | = gpio_o_bit ( nr - NR_VX855_GPInO ) ;
outl ( reg_out , vg - > io_gpo ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
return 0 ;
}
static int vx855gpio_get ( struct gpio_chip * gpio , unsigned int nr )
{
2015-12-07 17:09:57 +03:00
struct vx855_gpio * vg = gpiochip_get_data ( gpio ) ;
2010-10-01 00:55:48 +04:00
u_int32_t reg_in ;
int ret = 0 ;
if ( nr < NR_VX855_GPI ) {
reg_in = inl ( vg - > io_gpi ) ;
if ( reg_in & gpi_i_bit ( nr ) )
ret = 1 ;
} else if ( nr < NR_VX855_GPInO ) {
/* GPO don't have an input bit, we need to read it
* back from the output register */
reg_in = inl ( vg - > io_gpo ) ;
if ( reg_in & gpo_o_bit ( nr - NR_VX855_GPI ) )
ret = 1 ;
} else {
reg_in = inl ( vg - > io_gpi ) ;
if ( reg_in & gpio_i_bit ( nr - NR_VX855_GPInO ) )
ret = 1 ;
}
return ret ;
}
static void vx855gpio_set ( struct gpio_chip * gpio , unsigned int nr ,
int val )
{
2015-12-07 17:09:57 +03:00
struct vx855_gpio * vg = gpiochip_get_data ( gpio ) ;
2010-10-01 00:55:48 +04:00
unsigned long flags ;
u_int32_t reg_out ;
/* True GPI cannot be switched to output mode */
if ( nr < NR_VX855_GPI )
return ;
spin_lock_irqsave ( & vg - > lock , flags ) ;
reg_out = inl ( vg - > io_gpo ) ;
if ( nr < NR_VX855_GPInO ) {
if ( val )
reg_out | = gpo_o_bit ( nr - NR_VX855_GPI ) ;
else
reg_out & = ~ gpo_o_bit ( nr - NR_VX855_GPI ) ;
} else {
if ( val )
reg_out | = gpio_o_bit ( nr - NR_VX855_GPInO ) ;
else
reg_out & = ~ gpio_o_bit ( nr - NR_VX855_GPInO ) ;
}
outl ( reg_out , vg - > io_gpo ) ;
spin_unlock_irqrestore ( & vg - > lock , flags ) ;
}
static int vx855gpio_direction_output ( struct gpio_chip * gpio ,
unsigned int nr , int val )
{
/* True GPI cannot be switched to output mode */
if ( nr < NR_VX855_GPI )
return - EINVAL ;
/* True GPO don't need to be switched to output mode,
* and GPIO are open - drain , i . e . also need no switching ,
* so all we do is set the level */
vx855gpio_set ( gpio , nr , val ) ;
return 0 ;
}
2017-01-23 15:34:34 +03:00
static int vx855gpio_set_config ( struct gpio_chip * gpio , unsigned int nr ,
unsigned long config )
2016-04-10 13:26:08 +03:00
{
2017-01-23 15:34:34 +03:00
enum pin_config_param param = pinconf_to_config_param ( config ) ;
2016-04-10 13:26:08 +03:00
/* The GPI cannot be single-ended */
if ( nr < NR_VX855_GPI )
return - EINVAL ;
/* The GPO's are push-pull */
if ( nr < NR_VX855_GPInO ) {
2017-01-23 15:34:34 +03:00
if ( param ! = PIN_CONFIG_DRIVE_PUSH_PULL )
2016-04-10 13:26:08 +03:00
return - ENOTSUPP ;
return 0 ;
}
/* The GPIO's are open drain */
2017-01-23 15:34:34 +03:00
if ( param ! = PIN_CONFIG_DRIVE_OPEN_DRAIN )
2016-04-10 13:26:08 +03:00
return - ENOTSUPP ;
return 0 ;
}
2010-10-01 00:55:48 +04:00
static const char * vx855gpio_names [ NR_VX855_GP ] = {
" VX855_GPI0 " , " VX855_GPI1 " , " VX855_GPI2 " , " VX855_GPI3 " , " VX855_GPI4 " ,
" VX855_GPI5 " , " VX855_GPI6 " , " VX855_GPI7 " , " VX855_GPI8 " , " VX855_GPI9 " ,
" VX855_GPI10 " , " VX855_GPI11 " , " VX855_GPI12 " , " VX855_GPI13 " ,
" VX855_GPO0 " , " VX855_GPO1 " , " VX855_GPO2 " , " VX855_GPO3 " , " VX855_GPO4 " ,
" VX855_GPO5 " , " VX855_GPO6 " , " VX855_GPO7 " , " VX855_GPO8 " , " VX855_GPO9 " ,
" VX855_GPO10 " , " VX855_GPO11 " , " VX855_GPO12 " ,
" VX855_GPIO0 " , " VX855_GPIO1 " , " VX855_GPIO2 " , " VX855_GPIO3 " ,
" VX855_GPIO4 " , " VX855_GPIO5 " , " VX855_GPIO6 " , " VX855_GPIO7 " ,
" VX855_GPIO8 " , " VX855_GPIO9 " , " VX855_GPIO10 " , " VX855_GPIO11 " ,
" VX855_GPIO12 " , " VX855_GPIO13 " , " VX855_GPIO14 "
} ;
static void vx855gpio_gpio_setup ( struct vx855_gpio * vg )
{
struct gpio_chip * c = & vg - > gpio ;
c - > label = " VX855 South Bridge " ;
c - > owner = THIS_MODULE ;
c - > direction_input = vx855gpio_direction_input ;
c - > direction_output = vx855gpio_direction_output ;
c - > get = vx855gpio_get ;
c - > set = vx855gpio_set ;
2017-01-23 15:34:34 +03:00
c - > set_config = vx855gpio_set_config ,
2010-10-01 00:55:48 +04:00
c - > dbg_show = NULL ;
c - > base = 0 ;
c - > ngpio = NR_VX855_GP ;
2013-12-04 17:42:46 +04:00
c - > can_sleep = false ;
2010-10-01 00:55:48 +04:00
c - > names = vx855gpio_names ;
}
/* This platform device is ordinarily registered by the vx855 mfd driver */
2012-11-19 22:22:34 +04:00
static int vx855gpio_probe ( struct platform_device * pdev )
2010-10-01 00:55:48 +04:00
{
struct resource * res_gpi ;
struct resource * res_gpo ;
struct vx855_gpio * vg ;
res_gpi = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
res_gpo = platform_get_resource ( pdev , IORESOURCE_IO , 1 ) ;
if ( ! res_gpi | | ! res_gpo )
return - EBUSY ;
2014-12-12 07:17:41 +03:00
vg = devm_kzalloc ( & pdev - > dev , sizeof ( * vg ) , GFP_KERNEL ) ;
2010-10-01 00:55:48 +04:00
if ( ! vg )
return - ENOMEM ;
platform_set_drvdata ( pdev , vg ) ;
dev_info ( & pdev - > dev , " found VX855 GPIO controller \n " ) ;
vg - > io_gpi = res_gpi - > start ;
vg - > io_gpo = res_gpo - > start ;
spin_lock_init ( & vg - > lock ) ;
/*
* A single byte is used to control various GPIO ports on the VX855 ,
* and in the case of the OLPC XO - 1.5 , some of those ports are used
* for switches that are interpreted and exposed through ACPI . ACPI
* will have reserved the region , so our own reservation will not
* succeed . Ignore and continue .
*/
2014-12-12 07:17:41 +03:00
if ( ! devm_request_region ( & pdev - > dev , res_gpi - > start ,
resource_size ( res_gpi ) , MODULE_NAME " _gpi " ) )
2010-10-01 00:55:48 +04:00
dev_warn ( & pdev - > dev ,
" GPI I/O resource busy, probably claimed by ACPI \n " ) ;
2014-12-12 07:17:41 +03:00
if ( ! devm_request_region ( & pdev - > dev , res_gpo - > start ,
resource_size ( res_gpo ) , MODULE_NAME " _gpo " ) )
2010-10-01 00:55:48 +04:00
dev_warn ( & pdev - > dev ,
" GPO I/O resource busy, probably claimed by ACPI \n " ) ;
vx855gpio_gpio_setup ( vg ) ;
2016-02-22 15:13:28 +03:00
return devm_gpiochip_add_data ( & pdev - > dev , & vg - > gpio , vg ) ;
2010-10-01 00:55:48 +04:00
}
static struct platform_driver vx855gpio_driver = {
. driver = {
. name = MODULE_NAME ,
} ,
. probe = vx855gpio_probe ,
} ;
2011-12-07 20:24:00 +04:00
module_platform_driver ( vx855gpio_driver ) ;
2010-10-01 00:55:48 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Harald Welte <HaraldWelte@viatech.com> " ) ;
MODULE_DESCRIPTION ( " GPIO driver for the VIA VX855 chipset " ) ;
MODULE_ALIAS ( " platform:vx855_gpio " ) ;