2011-01-04 23:28:15 +03:00
/*
* Atheros AR71XX / AR724X / AR913X GPIO API support
*
2016-01-28 22:44:33 +03:00
* Copyright ( C ) 2015 Alban Bedel < albeu @ free . fr >
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 >
*
* 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 .
*/
2015-09-01 12:38:02 +03:00
# include <linux/gpio/driver.h>
2015-05-31 03:18:24 +03:00
# include <linux/platform_data/gpio-ath79.h>
# include <linux/of_device.h>
2016-01-28 22:44:32 +03:00
# include <linux/interrupt.h>
2016-09-13 01:16:28 +03:00
# include <linux/module.h>
2016-01-28 22:44:32 +03:00
# include <linux/irq.h>
2011-01-04 23:28:15 +03:00
2016-01-28 22:44:30 +03:00
# define AR71XX_GPIO_REG_OE 0x00
# define AR71XX_GPIO_REG_IN 0x04
# define AR71XX_GPIO_REG_SET 0x0c
# define AR71XX_GPIO_REG_CLEAR 0x10
2011-01-04 23:28:15 +03:00
2016-01-28 22:44:32 +03:00
# define AR71XX_GPIO_REG_INT_ENABLE 0x14
# define AR71XX_GPIO_REG_INT_TYPE 0x18
# define AR71XX_GPIO_REG_INT_POLARITY 0x1c
# define AR71XX_GPIO_REG_INT_PENDING 0x20
# define AR71XX_GPIO_REG_INT_MASK 0x24
2015-09-01 12:38:02 +03:00
struct ath79_gpio_ctrl {
2016-01-28 22:44:29 +03:00
struct gpio_chip gc ;
2015-09-01 12:38:02 +03:00
void __iomem * base ;
2017-03-09 19:21:53 +03:00
raw_spinlock_t lock ;
2016-01-28 22:44:32 +03:00
unsigned long both_edges ;
} ;
static struct ath79_gpio_ctrl * irq_data_to_ath79_gpio ( struct irq_data * data )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( data ) ;
return container_of ( gc , struct ath79_gpio_ctrl , gc ) ;
}
static u32 ath79_gpio_read ( struct ath79_gpio_ctrl * ctrl , unsigned reg )
{
return readl ( ctrl - > base + reg ) ;
}
static void ath79_gpio_write ( struct ath79_gpio_ctrl * ctrl ,
unsigned reg , u32 val )
{
2018-07-24 14:57:43 +03:00
writel ( val , ctrl - > base + reg ) ;
2016-01-28 22:44:32 +03:00
}
static bool ath79_gpio_update_bits (
struct ath79_gpio_ctrl * ctrl , unsigned reg , u32 mask , u32 bits )
{
u32 old_val , new_val ;
old_val = ath79_gpio_read ( ctrl , reg ) ;
new_val = ( old_val & ~ mask ) | ( bits & mask ) ;
if ( new_val ! = old_val )
ath79_gpio_write ( ctrl , reg , new_val ) ;
return new_val ! = old_val ;
}
static void ath79_gpio_irq_unmask ( struct irq_data * data )
{
struct ath79_gpio_ctrl * ctrl = irq_data_to_ath79_gpio ( data ) ;
u32 mask = BIT ( irqd_to_hwirq ( data ) ) ;
unsigned long flags ;
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_MASK , mask , mask ) ;
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
}
static void ath79_gpio_irq_mask ( struct irq_data * data )
{
struct ath79_gpio_ctrl * ctrl = irq_data_to_ath79_gpio ( data ) ;
u32 mask = BIT ( irqd_to_hwirq ( data ) ) ;
unsigned long flags ;
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_MASK , mask , 0 ) ;
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
}
static void ath79_gpio_irq_enable ( struct irq_data * data )
{
struct ath79_gpio_ctrl * ctrl = irq_data_to_ath79_gpio ( data ) ;
u32 mask = BIT ( irqd_to_hwirq ( data ) ) ;
unsigned long flags ;
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_ENABLE , mask , mask ) ;
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_MASK , mask , mask ) ;
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
}
static void ath79_gpio_irq_disable ( struct irq_data * data )
{
struct ath79_gpio_ctrl * ctrl = irq_data_to_ath79_gpio ( data ) ;
u32 mask = BIT ( irqd_to_hwirq ( data ) ) ;
unsigned long flags ;
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_MASK , mask , 0 ) ;
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_ENABLE , mask , 0 ) ;
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
}
static int ath79_gpio_irq_set_type ( struct irq_data * data ,
unsigned int flow_type )
{
struct ath79_gpio_ctrl * ctrl = irq_data_to_ath79_gpio ( data ) ;
u32 mask = BIT ( irqd_to_hwirq ( data ) ) ;
u32 type = 0 , polarity = 0 ;
unsigned long flags ;
bool disabled ;
switch ( flow_type ) {
case IRQ_TYPE_EDGE_RISING :
polarity | = mask ;
case IRQ_TYPE_EDGE_FALLING :
case IRQ_TYPE_EDGE_BOTH :
break ;
case IRQ_TYPE_LEVEL_HIGH :
polarity | = mask ;
2017-10-13 23:43:53 +03:00
/* fall through */
2016-01-28 22:44:32 +03:00
case IRQ_TYPE_LEVEL_LOW :
type | = mask ;
break ;
default :
return - EINVAL ;
}
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
if ( flow_type = = IRQ_TYPE_EDGE_BOTH ) {
ctrl - > both_edges | = mask ;
polarity = ~ ath79_gpio_read ( ctrl , AR71XX_GPIO_REG_IN ) ;
} else {
ctrl - > both_edges & = ~ mask ;
}
/* As the IRQ configuration can't be loaded atomically we
* have to disable the interrupt while the configuration state
* is invalid .
*/
disabled = ath79_gpio_update_bits (
ctrl , AR71XX_GPIO_REG_INT_ENABLE , mask , 0 ) ;
ath79_gpio_update_bits (
ctrl , AR71XX_GPIO_REG_INT_TYPE , mask , type ) ;
ath79_gpio_update_bits (
ctrl , AR71XX_GPIO_REG_INT_POLARITY , mask , polarity ) ;
if ( disabled )
ath79_gpio_update_bits (
ctrl , AR71XX_GPIO_REG_INT_ENABLE , mask , mask ) ;
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
return 0 ;
}
static struct irq_chip ath79_gpio_irqchip = {
. name = " gpio-ath79 " ,
. irq_enable = ath79_gpio_irq_enable ,
. irq_disable = ath79_gpio_irq_disable ,
. irq_mask = ath79_gpio_irq_mask ,
. irq_unmask = ath79_gpio_irq_unmask ,
. irq_set_type = ath79_gpio_irq_set_type ,
2015-09-01 12:38:02 +03:00
} ;
2016-01-28 22:44:32 +03:00
static void ath79_gpio_irq_handler ( struct irq_desc * desc )
{
struct gpio_chip * gc = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * irqchip = irq_desc_get_chip ( desc ) ;
struct ath79_gpio_ctrl * ctrl =
container_of ( gc , struct ath79_gpio_ctrl , gc ) ;
unsigned long flags , pending ;
u32 both_edges , state ;
int irq ;
chained_irq_enter ( irqchip , desc ) ;
2017-03-09 19:21:53 +03:00
raw_spin_lock_irqsave ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
pending = ath79_gpio_read ( ctrl , AR71XX_GPIO_REG_INT_PENDING ) ;
/* Update the polarity of the both edges irqs */
both_edges = ctrl - > both_edges & pending ;
if ( both_edges ) {
state = ath79_gpio_read ( ctrl , AR71XX_GPIO_REG_IN ) ;
ath79_gpio_update_bits ( ctrl , AR71XX_GPIO_REG_INT_POLARITY ,
both_edges , ~ state ) ;
}
2017-03-09 19:21:53 +03:00
raw_spin_unlock_irqrestore ( & ctrl - > lock , flags ) ;
2016-01-28 22:44:32 +03:00
if ( pending ) {
for_each_set_bit ( irq , & pending , gc - > ngpio )
generic_handle_irq (
2017-11-07 21:15:47 +03:00
irq_linear_revmap ( gc - > irq . domain , irq ) ) ;
2016-01-28 22:44:32 +03:00
}
chained_irq_exit ( irqchip , desc ) ;
}
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 " } ,
{ } ,
} ;
2016-10-18 23:44:01 +03:00
MODULE_DEVICE_TABLE ( of , ath79_gpio_of_match ) ;
2015-05-31 03:18:24 +03:00
static int ath79_gpio_probe ( struct platform_device * pdev )
2011-01-04 23:28:15 +03:00
{
2015-11-23 18:23:18 +03:00
struct ath79_gpio_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2015-05-31 03:18:24 +03:00
struct device_node * np = pdev - > dev . of_node ;
2015-09-01 12:38:02 +03:00
struct ath79_gpio_ctrl * ctrl ;
2015-05-31 03:18:24 +03:00
struct resource * res ;
2015-09-01 12:38:02 +03:00
u32 ath79_gpio_count ;
2015-05-31 03:18:24 +03:00
bool oe_inverted ;
2011-01-04 23:28:15 +03:00
int err ;
2015-09-01 12:38:02 +03:00
ctrl = devm_kzalloc ( & pdev - > dev , sizeof ( * ctrl ) , GFP_KERNEL ) ;
if ( ! ctrl )
return - ENOMEM ;
2016-01-28 22:44:31 +03:00
platform_set_drvdata ( pdev , ctrl ) ;
2015-09-01 12:38:02 +03:00
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 ;
}
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 ;
}
2016-02-20 04:48:07 +03:00
if ( ath79_gpio_count > = 32 ) {
dev_err ( & pdev - > dev , " ngpios must be less than 32 \n " ) ;
return - EINVAL ;
}
2015-05-31 03:18:24 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
gpio: ath79: Fix potential NULL dereference in ath79_gpio_probe()
platform_get_resource() may return NULL, add proper
check to avoid potential NULL dereferencing.
This is detected by Coccinelle semantic patch.
@@
expression pdev, res, n, t, e, e1, e2;
@@
res = platform_get_resource(pdev, t, n);
+ if (!res)
+ return -EINVAL;
... when != res == NULL
e = devm_ioremap(e1, res->start, e2);
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
[albeu@free.fr: Fixed patch to apply on current tree]
Signed-off-by: Alban Bedel <albeu@free.fr>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2018-03-25 02:40:48 +03:00
if ( ! res )
return - EINVAL ;
2015-09-01 12:38:02 +03:00
ctrl - > base = devm_ioremap_nocache (
2015-05-31 03:18:24 +03:00
& pdev - > dev , res - > start , resource_size ( res ) ) ;
2015-09-01 12:38:02 +03:00
if ( ! ctrl - > base )
2015-05-31 03:18:24 +03:00
return - ENOMEM ;
2011-01-04 23:28:15 +03:00
2017-03-09 19:21:53 +03:00
raw_spin_lock_init ( & ctrl - > lock ) ;
2016-01-28 22:44:29 +03:00
err = bgpio_init ( & ctrl - > gc , & pdev - > dev , 4 ,
ctrl - > base + AR71XX_GPIO_REG_IN ,
ctrl - > base + AR71XX_GPIO_REG_SET ,
ctrl - > base + AR71XX_GPIO_REG_CLEAR ,
oe_inverted ? NULL : ctrl - > base + AR71XX_GPIO_REG_OE ,
oe_inverted ? ctrl - > base + AR71XX_GPIO_REG_OE : NULL ,
0 ) ;
if ( err ) {
dev_err ( & pdev - > dev , " bgpio_init failed \n " ) ;
return err ;
2012-03-14 13:45:23 +04:00
}
2016-01-28 22:44:29 +03:00
/* Use base 0 to stay compatible with legacy platforms */
ctrl - > gc . base = 0 ;
2011-01-04 23:28:15 +03:00
2016-01-28 22:44:29 +03:00
err = gpiochip_add_data ( & ctrl - > gc , ctrl ) ;
2015-05-31 03:18:24 +03:00
if ( err ) {
dev_err ( & pdev - > dev ,
" cannot add AR71xx GPIO chip, error=%d " , err ) ;
return err ;
}
2016-01-28 22:44:32 +03:00
if ( np & & ! of_property_read_bool ( np , " interrupt-controller " ) )
return 0 ;
err = gpiochip_irqchip_add ( & ctrl - > gc , & ath79_gpio_irqchip , 0 ,
handle_simple_irq , IRQ_TYPE_NONE ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to add gpiochip_irqchip \n " ) ;
goto gpiochip_remove ;
}
gpiochip_set_chained_irqchip ( & ctrl - > gc , & ath79_gpio_irqchip ,
platform_get_irq ( pdev , 0 ) ,
ath79_gpio_irq_handler ) ;
2015-05-31 03:18:24 +03:00
return 0 ;
2016-01-28 22:44:32 +03:00
gpiochip_remove :
gpiochip_remove ( & ctrl - > gc ) ;
return err ;
2011-01-04 23:28:15 +03:00
}
2016-01-28 22:44:31 +03:00
static int ath79_gpio_remove ( struct platform_device * pdev )
{
struct ath79_gpio_ctrl * ctrl = platform_get_drvdata ( pdev ) ;
gpiochip_remove ( & ctrl - > gc ) ;
return 0 ;
}
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 ,
2016-01-28 22:44:31 +03:00
. remove = ath79_gpio_remove ,
2015-05-31 03:18:24 +03:00
} ;
module_platform_driver ( ath79_gpio_driver ) ;
2017-11-20 23:54:26 +03:00
MODULE_DESCRIPTION ( " Atheros AR71XX/AR724X/AR913X GPIO API support " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;