2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-04-29 13:58:54 +04:00
/*
2015-04-01 05:20:09 +03:00
* Loongson - 2F / 3 A / 3 B GPIO Support
2010-04-29 13:58:54 +04:00
*
2013-01-22 15:59:30 +04:00
* Copyright ( c ) 2008 Richard Liu , STMicroelectronics < richard . liu @ st . com >
2010-04-29 13:58:54 +04:00
* Copyright ( c ) 2008 - 2010 Arnaud Patard < apatard @ mandriva . com >
2015-04-01 05:20:09 +03:00
* Copyright ( c ) 2013 Hongbing Hu < huhb @ lemote . com >
* Copyright ( c ) 2014 Huacai Chen < chenhc @ lemote . com >
2010-04-29 13:58:54 +04:00
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/err.h>
2018-04-13 11:28:21 +03:00
# include <linux/gpio/driver.h>
2018-04-13 13:05:36 +03:00
# include <linux/platform_device.h>
2018-04-13 13:20:24 +03:00
# include <linux/bitops.h>
2010-04-29 13:58:54 +04:00
# include <asm/types.h>
# include <loongson.h>
# define STLS2F_N_GPIO 4
2015-04-01 05:20:09 +03:00
# define STLS3A_N_GPIO 16
# ifdef CONFIG_CPU_LOONGSON3
# define LOONGSON_N_GPIO STLS3A_N_GPIO
# else
# define LOONGSON_N_GPIO STLS2F_N_GPIO
# endif
2018-04-13 13:20:24 +03:00
/*
* Offset into the register where we read lines , we write them from offset 0.
* This offset is the only thing that stand between us and using
* GPIO_GENERIC .
*/
2015-04-01 05:20:09 +03:00
# define LOONGSON_GPIO_IN_OFFSET 16
2010-04-29 13:58:54 +04:00
static DEFINE_SPINLOCK ( gpio_lock ) ;
2015-04-01 05:20:09 +03:00
static int loongson_gpio_get_value ( struct gpio_chip * chip , unsigned gpio )
2010-04-29 13:58:54 +04:00
{
2015-04-01 05:20:07 +03:00
u32 val ;
spin_lock ( & gpio_lock ) ;
val = LOONGSON_GPIODATA ;
spin_unlock ( & gpio_lock ) ;
2018-04-13 13:20:24 +03:00
return ! ! ( val & BIT ( gpio + LOONGSON_GPIO_IN_OFFSET ) ) ;
2010-04-29 13:58:54 +04:00
}
2015-04-01 05:20:09 +03:00
static void loongson_gpio_set_value ( struct gpio_chip * chip ,
2010-04-29 13:58:54 +04:00
unsigned gpio , int value )
{
2015-04-01 05:20:07 +03:00
u32 val ;
spin_lock ( & gpio_lock ) ;
val = LOONGSON_GPIODATA ;
if ( value )
2018-04-13 13:20:24 +03:00
val | = BIT ( gpio ) ;
2015-04-01 05:20:07 +03:00
else
2018-04-13 13:20:24 +03:00
val & = ~ BIT ( gpio ) ;
2015-04-01 05:20:07 +03:00
LOONGSON_GPIODATA = val ;
spin_unlock ( & gpio_lock ) ;
2010-04-29 13:58:54 +04:00
}
2018-04-13 11:28:21 +03:00
static int loongson_gpio_direction_input ( struct gpio_chip * chip , unsigned gpio )
{
u32 temp ;
spin_lock ( & gpio_lock ) ;
temp = LOONGSON_GPIOIE ;
2018-04-13 13:20:24 +03:00
temp | = BIT ( gpio ) ;
2018-04-13 11:28:21 +03:00
LOONGSON_GPIOIE = temp ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
static int loongson_gpio_direction_output ( struct gpio_chip * chip ,
unsigned gpio , int level )
{
u32 temp ;
loongson_gpio_set_value ( chip , gpio , level ) ;
spin_lock ( & gpio_lock ) ;
temp = LOONGSON_GPIOIE ;
2018-04-13 13:20:24 +03:00
temp & = ~ BIT ( gpio ) ;
2018-04-13 11:28:21 +03:00
LOONGSON_GPIOIE = temp ;
spin_unlock ( & gpio_lock ) ;
return 0 ;
}
2018-04-13 13:05:36 +03:00
static int loongson_gpio_probe ( struct platform_device * pdev )
{
struct gpio_chip * gc ;
struct device * dev = & pdev - > dev ;
gc = devm_kzalloc ( dev , sizeof ( * gc ) , GFP_KERNEL ) ;
if ( ! gc )
return - ENOMEM ;
gc - > label = " loongson-gpio-chip " ;
gc - > base = 0 ;
gc - > ngpio = LOONGSON_N_GPIO ;
gc - > get = loongson_gpio_get_value ;
gc - > set = loongson_gpio_set_value ;
gc - > direction_input = loongson_gpio_direction_input ;
gc - > direction_output = loongson_gpio_direction_output ;
return gpiochip_add_data ( gc , NULL ) ;
}
static struct platform_driver loongson_gpio_driver = {
. driver = {
. name = " loongson-gpio " ,
} ,
. probe = loongson_gpio_probe ,
2010-04-29 13:58:54 +04:00
} ;
2015-04-01 05:20:09 +03:00
static int __init loongson_gpio_setup ( void )
2010-04-29 13:58:54 +04:00
{
2018-04-13 13:05:36 +03:00
struct platform_device * pdev ;
int ret ;
ret = platform_driver_register ( & loongson_gpio_driver ) ;
if ( ret ) {
pr_err ( " error registering loongson GPIO driver \n " ) ;
return ret ;
}
pdev = platform_device_register_simple ( " loongson-gpio " , - 1 , NULL , 0 ) ;
return PTR_ERR_OR_ZERO ( pdev ) ;
2010-04-29 13:58:54 +04:00
}
2015-04-01 05:20:09 +03:00
postcore_initcall ( loongson_gpio_setup ) ;