2011-01-04 23:28:15 +03:00
/*
* Atheros AR71XX / AR724X / AR913X GPIO API support
*
2012-03-14 13:45:23 +04:00
* Copyright ( C ) 2010 - 2011 Jaiganesh Narayanan < jnarayanan @ atheros . com >
* Copyright ( C ) 2008 - 2011 Gabor Juhos < juhosg @ openwrt . org >
2011-01-04 23:28:15 +03:00
* Copyright ( C ) 2008 Imre Kaloz < kaloz @ openwrt . org >
*
2012-03-14 13:45:23 +04:00
* Parts of this file are based on Atheros ' 2.6 .15 / 2.6 .31 BSP
*
2011-01-04 23:28:15 +03:00
* 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 .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/spinlock.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/gpio.h>
2015-05-31 03:18:24 +03:00
# include <linux/platform_data/gpio-ath79.h>
# include <linux/of_device.h>
2011-01-04 23:28:15 +03:00
# include <asm/mach-ath79/ar71xx_regs.h>
static void __iomem * ath79_gpio_base ;
2015-05-31 03:18:24 +03:00
static u32 ath79_gpio_count ;
2011-01-04 23:28:15 +03:00
static DEFINE_SPINLOCK ( ath79_gpio_lock ) ;
static void __ath79_gpio_set_value ( unsigned gpio , int value )
{
void __iomem * base = ath79_gpio_base ;
if ( value )
__raw_writel ( 1 < < gpio , base + AR71XX_GPIO_REG_SET ) ;
else
__raw_writel ( 1 < < gpio , base + AR71XX_GPIO_REG_CLEAR ) ;
}
static int __ath79_gpio_get_value ( unsigned gpio )
{
return ( __raw_readl ( ath79_gpio_base + AR71XX_GPIO_REG_IN ) > > gpio ) & 1 ;
}
static int ath79_gpio_get_value ( struct gpio_chip * chip , unsigned offset )
{
return __ath79_gpio_get_value ( offset ) ;
}
static void ath79_gpio_set_value ( struct gpio_chip * chip ,
unsigned offset , int value )
{
__ath79_gpio_set_value ( offset , value ) ;
}
static int ath79_gpio_direction_input ( struct gpio_chip * chip ,
unsigned offset )
{
void __iomem * base = ath79_gpio_base ;
unsigned long flags ;
spin_lock_irqsave ( & ath79_gpio_lock , flags ) ;
__raw_writel ( __raw_readl ( base + AR71XX_GPIO_REG_OE ) & ~ ( 1 < < offset ) ,
base + AR71XX_GPIO_REG_OE ) ;
spin_unlock_irqrestore ( & ath79_gpio_lock , flags ) ;
return 0 ;
}
static int ath79_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
void __iomem * base = ath79_gpio_base ;
unsigned long flags ;
spin_lock_irqsave ( & ath79_gpio_lock , flags ) ;
if ( value )
__raw_writel ( 1 < < offset , base + AR71XX_GPIO_REG_SET ) ;
else
__raw_writel ( 1 < < offset , base + AR71XX_GPIO_REG_CLEAR ) ;
__raw_writel ( __raw_readl ( base + AR71XX_GPIO_REG_OE ) | ( 1 < < offset ) ,
base + AR71XX_GPIO_REG_OE ) ;
spin_unlock_irqrestore ( & ath79_gpio_lock , flags ) ;
return 0 ;
}
2012-03-14 13:45:23 +04:00
static int ar934x_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
void __iomem * base = ath79_gpio_base ;
unsigned long flags ;
spin_lock_irqsave ( & ath79_gpio_lock , flags ) ;
__raw_writel ( __raw_readl ( base + AR71XX_GPIO_REG_OE ) | ( 1 < < offset ) ,
base + AR71XX_GPIO_REG_OE ) ;
spin_unlock_irqrestore ( & ath79_gpio_lock , flags ) ;
return 0 ;
}
static int ar934x_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
{
void __iomem * base = ath79_gpio_base ;
unsigned long flags ;
spin_lock_irqsave ( & ath79_gpio_lock , flags ) ;
if ( value )
__raw_writel ( 1 < < offset , base + AR71XX_GPIO_REG_SET ) ;
else
__raw_writel ( 1 < < offset , base + AR71XX_GPIO_REG_CLEAR ) ;
__raw_writel ( __raw_readl ( base + AR71XX_GPIO_REG_OE ) & ~ ( 1 < < offset ) ,
base + AR71XX_GPIO_REG_OE ) ;
spin_unlock_irqrestore ( & ath79_gpio_lock , flags ) ;
return 0 ;
}
2011-01-04 23:28:15 +03:00
static struct gpio_chip ath79_gpio_chip = {
. label = " ath79 " ,
. get = ath79_gpio_get_value ,
. set = ath79_gpio_set_value ,
. direction_input = ath79_gpio_direction_input ,
. direction_output = ath79_gpio_direction_output ,
. base = 0 ,
} ;
2015-05-31 03:18:24 +03:00
static const struct of_device_id ath79_gpio_of_match [ ] = {
{ . compatible = " qca,ar7100-gpio " } ,
{ . compatible = " qca,ar9340-gpio " } ,
{ } ,
} ;
static int ath79_gpio_probe ( struct platform_device * pdev )
2011-01-04 23:28:15 +03:00
{
2015-05-31 03:18:24 +03:00
struct ath79_gpio_platform_data * pdata = pdev - > dev . platform_data ;
struct device_node * np = pdev - > dev . of_node ;
struct resource * res ;
bool oe_inverted ;
2011-01-04 23:28:15 +03:00
int err ;
2015-05-31 03:18:24 +03:00
if ( np ) {
err = of_property_read_u32 ( np , " ngpios " , & ath79_gpio_count ) ;
if ( err ) {
dev_err ( & pdev - > dev , " ngpios property is not valid \n " ) ;
return err ;
}
if ( ath79_gpio_count > = 32 ) {
dev_err ( & pdev - > dev , " ngpios must be less than 32 \n " ) ;
return - EINVAL ;
}
oe_inverted = of_device_is_compatible ( np , " qca,ar9340-gpio " ) ;
} else if ( pdata ) {
ath79_gpio_count = pdata - > ngpios ;
oe_inverted = pdata - > oe_inverted ;
} else {
dev_err ( & pdev - > dev , " No DT node or platform data found \n " ) ;
return - EINVAL ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
ath79_gpio_base = devm_ioremap_nocache (
& pdev - > dev , res - > start , resource_size ( res ) ) ;
if ( ! ath79_gpio_base )
return - ENOMEM ;
2011-01-04 23:28:15 +03:00
2015-05-31 03:18:24 +03:00
ath79_gpio_chip . dev = & pdev - > dev ;
2011-01-04 23:28:15 +03:00
ath79_gpio_chip . ngpio = ath79_gpio_count ;
2015-05-31 03:18:24 +03:00
if ( oe_inverted ) {
2012-03-14 13:45:23 +04:00
ath79_gpio_chip . direction_input = ar934x_gpio_direction_input ;
ath79_gpio_chip . direction_output = ar934x_gpio_direction_output ;
}
2011-01-04 23:28:15 +03:00
err = gpiochip_add ( & ath79_gpio_chip ) ;
2015-05-31 03:18:24 +03:00
if ( err ) {
dev_err ( & pdev - > dev ,
" cannot add AR71xx GPIO chip, error=%d " , err ) ;
return err ;
}
return 0 ;
2011-01-04 23:28:15 +03:00
}
2015-05-31 03:18:24 +03:00
static struct platform_driver ath79_gpio_driver = {
. driver = {
. name = " ath79-gpio " ,
. of_match_table = ath79_gpio_of_match ,
} ,
. probe = ath79_gpio_probe ,
} ;
module_platform_driver ( ath79_gpio_driver ) ;
2011-01-04 23:28:15 +03:00
int gpio_get_value ( unsigned gpio )
{
if ( gpio < ath79_gpio_count )
return __ath79_gpio_get_value ( gpio ) ;
return __gpio_get_value ( gpio ) ;
}
EXPORT_SYMBOL ( gpio_get_value ) ;
void gpio_set_value ( unsigned gpio , int value )
{
if ( gpio < ath79_gpio_count )
__ath79_gpio_set_value ( gpio , value ) ;
else
__gpio_set_value ( gpio , value ) ;
}
EXPORT_SYMBOL ( gpio_set_value ) ;
int gpio_to_irq ( unsigned gpio )
{
/* FIXME */
return - EINVAL ;
}
EXPORT_SYMBOL ( gpio_to_irq ) ;
int irq_to_gpio ( unsigned irq )
{
/* FIXME */
return - EINVAL ;
}
EXPORT_SYMBOL ( irq_to_gpio ) ;