2014-07-31 23:03:25 +04:00
/*
* AppliedMicro X - Gene SoC GPIO Driver
*
* Copyright ( c ) 2014 , Applied Micro Circuits Corporation
* Author : Feng Kan < fkan @ apm . com > .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*/
2016-04-30 23:49:27 +03:00
# include <linux/acpi.h>
2014-07-31 23:03:25 +04:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/spinlock.h>
# include <linux/platform_device.h>
# include <linux/gpio/driver.h>
# include <linux/types.h>
# include <linux/bitops.h>
# define GPIO_SET_DR_OFFSET 0x0C
# define GPIO_DATA_OFFSET 0x14
# define GPIO_BANK_STRIDE 0x0C
# define XGENE_GPIOS_PER_BANK 16
# define XGENE_MAX_GPIO_BANKS 3
# define XGENE_MAX_GPIOS (XGENE_GPIOS_PER_BANK * XGENE_MAX_GPIO_BANKS)
# define GPIO_BIT_OFFSET(x) (x % XGENE_GPIOS_PER_BANK)
# define GPIO_BANK_OFFSET(x) ((x / XGENE_GPIOS_PER_BANK) * GPIO_BANK_STRIDE)
struct xgene_gpio {
struct gpio_chip chip ;
void __iomem * base ;
spinlock_t lock ;
u32 set_dr_val [ XGENE_MAX_GPIO_BANKS ] ;
} ;
static int xgene_gpio_get ( struct gpio_chip * gc , unsigned int offset )
{
2015-12-07 17:16:50 +03:00
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
2014-07-31 23:03:25 +04:00
unsigned long bank_offset ;
u32 bit_offset ;
bank_offset = GPIO_DATA_OFFSET + GPIO_BANK_OFFSET ( offset ) ;
bit_offset = GPIO_BIT_OFFSET ( offset ) ;
return ! ! ( ioread32 ( chip - > base + bank_offset ) & BIT ( bit_offset ) ) ;
}
static void __xgene_gpio_set ( struct gpio_chip * gc , unsigned int offset , int val )
{
2015-12-07 17:16:50 +03:00
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
2014-07-31 23:03:25 +04:00
unsigned long bank_offset ;
u32 setval , bit_offset ;
bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET ( offset ) ;
bit_offset = GPIO_BIT_OFFSET ( offset ) + XGENE_GPIOS_PER_BANK ;
setval = ioread32 ( chip - > base + bank_offset ) ;
if ( val )
setval | = BIT ( bit_offset ) ;
else
setval & = ~ BIT ( bit_offset ) ;
iowrite32 ( setval , chip - > base + bank_offset ) ;
}
static void xgene_gpio_set ( struct gpio_chip * gc , unsigned int offset , int val )
{
2015-12-07 17:16:50 +03:00
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
2014-07-31 23:03:25 +04:00
unsigned long flags ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
__xgene_gpio_set ( gc , offset , val ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
2016-05-01 11:29:10 +03:00
static int xgene_gpio_get_direction ( struct gpio_chip * gc , unsigned int offset )
{
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
unsigned long bank_offset , bit_offset ;
bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET ( offset ) ;
bit_offset = GPIO_BIT_OFFSET ( offset ) ;
return ! ! ( ioread32 ( chip - > base + bank_offset ) & BIT ( bit_offset ) ) ;
}
2014-07-31 23:03:25 +04:00
static int xgene_gpio_dir_in ( struct gpio_chip * gc , unsigned int offset )
{
2015-12-07 17:16:50 +03:00
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
2014-07-31 23:03:25 +04:00
unsigned long flags , bank_offset ;
u32 dirval , bit_offset ;
bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET ( offset ) ;
bit_offset = GPIO_BIT_OFFSET ( offset ) ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
dirval = ioread32 ( chip - > base + bank_offset ) ;
dirval | = BIT ( bit_offset ) ;
iowrite32 ( dirval , chip - > base + bank_offset ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
static int xgene_gpio_dir_out ( struct gpio_chip * gc ,
unsigned int offset , int val )
{
2015-12-07 17:16:50 +03:00
struct xgene_gpio * chip = gpiochip_get_data ( gc ) ;
2014-07-31 23:03:25 +04:00
unsigned long flags , bank_offset ;
u32 dirval , bit_offset ;
bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET ( offset ) ;
bit_offset = GPIO_BIT_OFFSET ( offset ) ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
dirval = ioread32 ( chip - > base + bank_offset ) ;
dirval & = ~ BIT ( bit_offset ) ;
iowrite32 ( dirval , chip - > base + bank_offset ) ;
__xgene_gpio_set ( gc , offset , val ) ;
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return 0 ;
}
2017-02-17 18:13:44 +03:00
static __maybe_unused int xgene_gpio_suspend ( struct device * dev )
2014-07-31 23:03:25 +04:00
{
struct xgene_gpio * gpio = dev_get_drvdata ( dev ) ;
unsigned long bank_offset ;
unsigned int bank ;
for ( bank = 0 ; bank < XGENE_MAX_GPIO_BANKS ; bank + + ) {
bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE ;
gpio - > set_dr_val [ bank ] = ioread32 ( gpio - > base + bank_offset ) ;
}
return 0 ;
}
2017-02-17 18:13:44 +03:00
static __maybe_unused int xgene_gpio_resume ( struct device * dev )
2014-07-31 23:03:25 +04:00
{
struct xgene_gpio * gpio = dev_get_drvdata ( dev ) ;
unsigned long bank_offset ;
unsigned int bank ;
for ( bank = 0 ; bank < XGENE_MAX_GPIO_BANKS ; bank + + ) {
bank_offset = GPIO_SET_DR_OFFSET + bank * GPIO_BANK_STRIDE ;
iowrite32 ( gpio - > set_dr_val [ bank ] , gpio - > base + bank_offset ) ;
}
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( xgene_gpio_pm , xgene_gpio_suspend , xgene_gpio_resume ) ;
static int xgene_gpio_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct xgene_gpio * gpio ;
int err = 0 ;
gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * gpio ) , GFP_KERNEL ) ;
if ( ! gpio ) {
err = - ENOMEM ;
goto err ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2016-03-21 15:03:50 +03:00
if ( ! res ) {
err = - EINVAL ;
goto err ;
}
2014-07-31 23:03:25 +04:00
gpio - > base = devm_ioremap_nocache ( & pdev - > dev , res - > start ,
resource_size ( res ) ) ;
2014-09-12 03:12:14 +04:00
if ( ! gpio - > base ) {
err = - ENOMEM ;
2014-07-31 23:03:25 +04:00
goto err ;
}
gpio - > chip . ngpio = XGENE_MAX_GPIOS ;
2014-09-21 08:31:29 +04:00
spin_lock_init ( & gpio - > lock ) ;
2015-11-04 11:56:26 +03:00
gpio - > chip . parent = & pdev - > dev ;
2016-05-01 11:29:10 +03:00
gpio - > chip . get_direction = xgene_gpio_get_direction ;
2014-07-31 23:03:25 +04:00
gpio - > chip . direction_input = xgene_gpio_dir_in ;
gpio - > chip . direction_output = xgene_gpio_dir_out ;
gpio - > chip . get = xgene_gpio_get ;
gpio - > chip . set = xgene_gpio_set ;
gpio - > chip . label = dev_name ( & pdev - > dev ) ;
gpio - > chip . base = - 1 ;
platform_set_drvdata ( pdev , gpio ) ;
2016-02-22 15:13:28 +03:00
err = devm_gpiochip_add_data ( & pdev - > dev , & gpio - > chip , gpio ) ;
2014-07-31 23:03:25 +04:00
if ( err ) {
dev_err ( & pdev - > dev ,
" failed to register gpiochip. \n " ) ;
goto err ;
}
dev_info ( & pdev - > dev , " X-Gene GPIO driver registered. \n " ) ;
return 0 ;
err :
dev_err ( & pdev - > dev , " X-Gene GPIO driver registration failed. \n " ) ;
return err ;
}
static const struct of_device_id xgene_gpio_of_match [ ] = {
{ . compatible = " apm,xgene-gpio " , } ,
{ } ,
} ;
2016-04-30 23:49:27 +03:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_gpio_acpi_match [ ] = {
{ " APMC0D14 " , 0 } ,
{ } ,
} ;
# endif
2014-07-31 23:03:25 +04:00
static struct platform_driver xgene_gpio_driver = {
. driver = {
. name = " xgene-gpio " ,
. of_match_table = xgene_gpio_of_match ,
2016-04-30 23:49:27 +03:00
. acpi_match_table = ACPI_PTR ( xgene_gpio_acpi_match ) ,
2017-02-17 18:13:44 +03:00
. pm = & xgene_gpio_pm ,
2014-07-31 23:03:25 +04:00
} ,
. probe = xgene_gpio_probe ,
} ;
2016-03-27 18:44:48 +03:00
builtin_platform_driver ( xgene_gpio_driver ) ;