2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-03-16 05:40:06 +03:00
/*
* arch / arm / mach - tegra / gpio . c
*
* Copyright ( c ) 2010 Google , Inc
2019-02-19 23:32:02 +03:00
* Copyright ( c ) 2011 - 2016 , NVIDIA CORPORATION . All rights reserved .
2010-03-16 05:40:06 +03:00
*
* Author :
* Erik Gilling < konkers @ google . com >
*/
2013-01-21 14:09:01 +04:00
# include <linux/err.h>
2010-03-16 05:40:06 +03:00
# include <linux/init.h>
# include <linux/irq.h>
2010-04-07 23:59:42 +04:00
# include <linux/interrupt.h>
2010-03-16 05:40:06 +03:00
# include <linux/io.h>
2018-08-06 18:38:33 +03:00
# include <linux/gpio/driver.h>
2012-03-17 03:35:08 +04:00
# include <linux/of_device.h>
2011-10-12 02:16:14 +04:00
# include <linux/platform_device.h>
# include <linux/module.h>
2022-10-19 09:12:01 +03:00
# include <linux/seq_file.h>
2012-01-04 12:39:37 +04:00
# include <linux/irqdomain.h>
2013-01-18 19:31:37 +04:00
# include <linux/irqchip/chained_irq.h>
2012-02-18 12:04:55 +04:00
# include <linux/pinctrl/consumer.h>
2012-11-07 19:01:32 +04:00
# include <linux/pm.h>
2010-03-16 05:40:06 +03:00
# define GPIO_BANK(x) ((x) >> 5)
# define GPIO_PORT(x) (((x) >> 3) & 0x3)
# define GPIO_BIT(x) ((x) & 0x7)
2016-04-25 13:38:33 +03:00
# define GPIO_REG(tgi, x) (GPIO_BANK(x) * tgi->soc->bank_stride + \
2012-03-17 03:35:08 +04:00
GPIO_PORT ( x ) * 4 )
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
# define GPIO_CNF(t, x) (GPIO_REG(t, x) + 0x00)
# define GPIO_OE(t, x) (GPIO_REG(t, x) + 0x10)
# define GPIO_OUT(t, x) (GPIO_REG(t, x) + 0X20)
# define GPIO_IN(t, x) (GPIO_REG(t, x) + 0x30)
# define GPIO_INT_STA(t, x) (GPIO_REG(t, x) + 0x40)
# define GPIO_INT_ENB(t, x) (GPIO_REG(t, x) + 0x50)
# define GPIO_INT_LVL(t, x) (GPIO_REG(t, x) + 0x60)
# define GPIO_INT_CLR(t, x) (GPIO_REG(t, x) + 0x70)
2016-04-25 13:38:34 +03:00
# define GPIO_DBC_CNT(t, x) (GPIO_REG(t, x) + 0xF0)
2016-04-25 13:38:33 +03:00
# define GPIO_MSK_CNF(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x00)
# define GPIO_MSK_OE(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x10)
# define GPIO_MSK_OUT(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0X20)
2016-04-25 13:38:34 +03:00
# define GPIO_MSK_DBC_EN(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x30)
2016-04-25 13:38:33 +03:00
# define GPIO_MSK_INT_STA(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x40)
# define GPIO_MSK_INT_ENB(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x50)
# define GPIO_MSK_INT_LVL(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x60)
2010-03-16 05:40:06 +03:00
# define GPIO_INT_LVL_MASK 0x010101
# define GPIO_INT_LVL_EDGE_RISING 0x000101
# define GPIO_INT_LVL_EDGE_FALLING 0x000100
# define GPIO_INT_LVL_EDGE_BOTH 0x010100
# define GPIO_INT_LVL_LEVEL_HIGH 0x000001
# define GPIO_INT_LVL_LEVEL_LOW 0x000000
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info ;
2010-03-16 05:40:06 +03:00
struct tegra_gpio_bank {
2017-07-24 17:55:08 +03:00
unsigned int bank ;
2020-11-04 20:04:23 +03:00
/*
* IRQ - core code uses raw locking , and thus , nested locking also
* should be raw in order not to trip spinlock debug warnings .
*/
raw_spinlock_t lvl_lock [ 4 ] ;
/* Lock for updating debounce count register */
spinlock_t dbc_lock [ 4 ] ;
2012-11-07 19:01:32 +04:00
# ifdef CONFIG_PM_SLEEP
2010-04-07 23:59:42 +04:00
u32 cnf [ 4 ] ;
u32 out [ 4 ] ;
u32 oe [ 4 ] ;
u32 int_enb [ 4 ] ;
u32 int_lvl [ 4 ] ;
2013-04-03 15:31:44 +04:00
u32 wake_enb [ 4 ] ;
2016-04-25 13:38:34 +03:00
u32 dbc_enb [ 4 ] ;
2010-04-07 23:59:42 +04:00
# endif
2016-04-25 13:38:34 +03:00
u32 dbc_cnt [ 4 ] ;
2010-03-16 05:40:06 +03:00
} ;
2016-04-25 13:38:31 +03:00
struct tegra_gpio_soc_config {
2016-04-25 13:38:34 +03:00
bool debounce_supported ;
2016-04-25 13:38:31 +03:00
u32 bank_stride ;
u32 upper_offset ;
} ;
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info {
struct device * dev ;
void __iomem * regs ;
struct tegra_gpio_bank * bank_info ;
const struct tegra_gpio_soc_config * soc ;
struct gpio_chip gc ;
u32 bank_count ;
2020-11-27 17:08:52 +03:00
unsigned int * irqs ;
2016-04-25 13:38:33 +03:00
} ;
2011-10-12 02:16:14 +04:00
2016-04-25 13:38:33 +03:00
static inline void tegra_gpio_writel ( struct tegra_gpio_info * tgi ,
u32 val , u32 reg )
2011-10-12 02:16:14 +04:00
{
2019-12-15 21:30:45 +03:00
writel_relaxed ( val , tgi - > regs + reg ) ;
2011-10-12 02:16:14 +04:00
}
2016-04-25 13:38:33 +03:00
static inline u32 tegra_gpio_readl ( struct tegra_gpio_info * tgi , u32 reg )
2011-10-12 02:16:14 +04:00
{
2019-12-15 21:30:45 +03:00
return readl_relaxed ( tgi - > regs + reg ) ;
2011-10-12 02:16:14 +04:00
}
2010-03-16 05:40:06 +03:00
2017-07-24 17:55:08 +03:00
static unsigned int tegra_gpio_compose ( unsigned int bank , unsigned int port ,
unsigned int bit )
2010-03-16 05:40:06 +03:00
{
return ( bank < < 5 ) | ( ( port & 0x3 ) < < 3 ) | ( bit & 0x7 ) ;
}
2016-04-25 13:38:33 +03:00
static void tegra_gpio_mask_write ( struct tegra_gpio_info * tgi , u32 reg ,
2017-07-24 17:55:08 +03:00
unsigned int gpio , u32 value )
2010-03-16 05:40:06 +03:00
{
u32 val ;
val = 0x100 < < GPIO_BIT ( gpio ) ;
if ( value )
val | = 1 < < GPIO_BIT ( gpio ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , val , reg ) ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:08 +03:00
static void tegra_gpio_enable ( struct tegra_gpio_info * tgi , unsigned int gpio )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_CNF ( tgi , gpio ) , gpio , 1 ) ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:08 +03:00
static void tegra_gpio_disable ( struct tegra_gpio_info * tgi , unsigned int gpio )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_CNF ( tgi , gpio ) , gpio , 0 ) ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:07 +03:00
static int tegra_gpio_request ( struct gpio_chip * chip , unsigned int offset )
2012-02-18 12:04:55 +04:00
{
2019-02-19 23:32:02 +03:00
return pinctrl_gpio_request ( chip - > base + offset ) ;
2012-02-18 12:04:55 +04:00
}
2017-07-24 17:55:07 +03:00
static void tegra_gpio_free ( struct gpio_chip * chip , unsigned int offset )
2012-02-18 12:04:55 +04:00
{
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2019-02-19 23:32:02 +03:00
pinctrl_gpio_free ( chip - > base + offset ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_disable ( tgi , offset ) ;
2012-02-18 12:04:55 +04:00
}
2017-07-24 17:55:07 +03:00
static void tegra_gpio_set ( struct gpio_chip * chip , unsigned int offset ,
int value )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
tegra_gpio_mask_write ( tgi , GPIO_MSK_OUT ( tgi , offset ) , offset , value ) ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:07 +03:00
static int tegra_gpio_get ( struct gpio_chip * chip , unsigned int offset )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2017-07-24 17:55:08 +03:00
unsigned int bval = BIT ( GPIO_BIT ( offset ) ) ;
2016-04-25 13:38:33 +03:00
2012-11-09 10:04:20 +04:00
/* If gpio is in output mode then read from the out value */
2016-04-25 13:38:33 +03:00
if ( tegra_gpio_readl ( tgi , GPIO_OE ( tgi , offset ) ) & bval )
return ! ! ( tegra_gpio_readl ( tgi , GPIO_OUT ( tgi , offset ) ) & bval ) ;
2012-11-09 10:04:20 +04:00
2016-04-25 13:38:33 +03:00
return ! ! ( tegra_gpio_readl ( tgi , GPIO_IN ( tgi , offset ) ) & bval ) ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:07 +03:00
static int tegra_gpio_direction_input ( struct gpio_chip * chip ,
unsigned int offset )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2019-02-19 23:32:02 +03:00
int ret ;
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_OE ( tgi , offset ) , offset , 0 ) ;
tegra_gpio_enable ( tgi , offset ) ;
2019-02-19 23:32:02 +03:00
ret = pinctrl_gpio_direction_input ( chip - > base + offset ) ;
if ( ret < 0 )
dev_err ( tgi - > dev ,
" Failed to set pinctrl input direction of GPIO %d: %d " ,
chip - > base + offset , ret ) ;
return ret ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:07 +03:00
static int tegra_gpio_direction_output ( struct gpio_chip * chip ,
unsigned int offset ,
int value )
2010-03-16 05:40:06 +03:00
{
2016-04-25 13:38:33 +03:00
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2019-02-19 23:32:02 +03:00
int ret ;
2016-04-25 13:38:33 +03:00
2010-03-16 05:40:06 +03:00
tegra_gpio_set ( chip , offset , value ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_OE ( tgi , offset ) , offset , 1 ) ;
tegra_gpio_enable ( tgi , offset ) ;
2019-02-19 23:32:02 +03:00
ret = pinctrl_gpio_direction_output ( chip - > base + offset ) ;
if ( ret < 0 )
dev_err ( tgi - > dev ,
" Failed to set pinctrl output direction of GPIO %d: %d " ,
chip - > base + offset , ret ) ;
return ret ;
2010-03-16 05:40:06 +03:00
}
2017-07-24 17:55:07 +03:00
static int tegra_gpio_get_direction ( struct gpio_chip * chip ,
unsigned int offset )
2016-04-29 19:25:23 +03:00
{
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
u32 pin_mask = BIT ( GPIO_BIT ( offset ) ) ;
u32 cnf , oe ;
cnf = tegra_gpio_readl ( tgi , GPIO_CNF ( tgi , offset ) ) ;
if ( ! ( cnf & pin_mask ) )
return - EINVAL ;
oe = tegra_gpio_readl ( tgi , GPIO_OE ( tgi , offset ) ) ;
2019-11-06 11:54:12 +03:00
if ( oe & pin_mask )
return GPIO_LINE_DIRECTION_OUT ;
return GPIO_LINE_DIRECTION_IN ;
2016-04-29 19:25:23 +03:00
}
2016-04-25 13:38:34 +03:00
static int tegra_gpio_set_debounce ( struct gpio_chip * chip , unsigned int offset ,
unsigned int debounce )
{
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
struct tegra_gpio_bank * bank = & tgi - > bank_info [ GPIO_BANK ( offset ) ] ;
unsigned int debounce_ms = DIV_ROUND_UP ( debounce , 1000 ) ;
unsigned long flags ;
2017-07-24 17:55:08 +03:00
unsigned int port ;
2016-04-25 13:38:34 +03:00
if ( ! debounce_ms ) {
tegra_gpio_mask_write ( tgi , GPIO_MSK_DBC_EN ( tgi , offset ) ,
offset , 0 ) ;
return 0 ;
}
debounce_ms = min ( debounce_ms , 255U ) ;
port = GPIO_PORT ( offset ) ;
/* There is only one debounce count register per port and hence
* set the maximum of current and requested debounce time .
*/
spin_lock_irqsave ( & bank - > dbc_lock [ port ] , flags ) ;
if ( bank - > dbc_cnt [ port ] < debounce_ms ) {
tegra_gpio_writel ( tgi , debounce_ms , GPIO_DBC_CNT ( tgi , offset ) ) ;
bank - > dbc_cnt [ port ] = debounce_ms ;
}
spin_unlock_irqrestore ( & bank - > dbc_lock [ port ] , flags ) ;
tegra_gpio_mask_write ( tgi , GPIO_MSK_DBC_EN ( tgi , offset ) , offset , 1 ) ;
return 0 ;
}
2017-01-23 15:34:34 +03:00
static int tegra_gpio_set_config ( struct gpio_chip * chip , unsigned int offset ,
unsigned long config )
{
u32 debounce ;
if ( pinconf_to_config_param ( config ) ! = PIN_CONFIG_INPUT_DEBOUNCE )
return - ENOTSUPP ;
debounce = pinconf_to_config_argument ( config ) ;
return tegra_gpio_set_debounce ( chip , offset , debounce ) ;
}
2010-11-29 13:14:46 +03:00
static void tegra_gpio_irq_ack ( struct irq_data * d )
2010-03-16 05:40:06 +03:00
{
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , 1 < < GPIO_BIT ( gpio ) , GPIO_INT_CLR ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
}
2010-11-29 13:14:46 +03:00
static void tegra_gpio_irq_mask ( struct irq_data * d )
2010-03-16 05:40:06 +03:00
{
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_INT_ENB ( tgi , gpio ) , gpio , 0 ) ;
2022-10-19 09:12:01 +03:00
gpiochip_disable_irq ( chip , gpio ) ;
2010-03-16 05:40:06 +03:00
}
2010-11-29 13:14:46 +03:00
static void tegra_gpio_irq_unmask ( struct irq_data * d )
2010-03-16 05:40:06 +03:00
{
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq ;
2010-03-16 05:40:06 +03:00
2022-10-19 09:12:01 +03:00
gpiochip_enable_irq ( chip , gpio ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_INT_ENB ( tgi , gpio ) , gpio , 1 ) ;
2010-03-16 05:40:06 +03:00
}
2010-11-29 13:14:46 +03:00
static int tegra_gpio_irq_set_type ( struct irq_data * d , unsigned int type )
2010-03-16 05:40:06 +03:00
{
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq , port = GPIO_PORT ( gpio ) , lvl_type ;
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
struct tegra_gpio_bank * bank ;
2010-03-16 05:40:06 +03:00
unsigned long flags ;
2013-10-16 23:25:33 +04:00
int ret ;
2020-11-27 17:08:52 +03:00
u32 val ;
bank = & tgi - > bank_info [ GPIO_BANK ( d - > hwirq ) ] ;
2010-03-16 05:40:06 +03:00
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
lvl_type = GPIO_INT_LVL_EDGE_RISING ;
break ;
case IRQ_TYPE_EDGE_FALLING :
lvl_type = GPIO_INT_LVL_EDGE_FALLING ;
break ;
case IRQ_TYPE_EDGE_BOTH :
lvl_type = GPIO_INT_LVL_EDGE_BOTH ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
lvl_type = GPIO_INT_LVL_LEVEL_HIGH ;
break ;
case IRQ_TYPE_LEVEL_LOW :
lvl_type = GPIO_INT_LVL_LEVEL_LOW ;
break ;
default :
return - EINVAL ;
}
2020-11-04 20:04:23 +03:00
raw_spin_lock_irqsave ( & bank - > lvl_lock [ port ] , flags ) ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
val = tegra_gpio_readl ( tgi , GPIO_INT_LVL ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
val & = ~ ( GPIO_INT_LVL_MASK < < GPIO_BIT ( gpio ) ) ;
val | = lvl_type < < GPIO_BIT ( gpio ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , val , GPIO_INT_LVL ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
2020-11-04 20:04:23 +03:00
raw_spin_unlock_irqrestore ( & bank - > lvl_lock [ port ] , flags ) ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
tegra_gpio_mask_write ( tgi , GPIO_MSK_OE ( tgi , gpio ) , gpio , 0 ) ;
tegra_gpio_enable ( tgi , gpio ) ;
2012-03-19 20:31:58 +04:00
2018-07-17 19:10:38 +03:00
ret = gpiochip_lock_as_irq ( & tgi - > gc , gpio ) ;
if ( ret ) {
dev_err ( tgi - > dev ,
" unable to lock Tegra GPIO %u as IRQ \n " , gpio ) ;
tegra_gpio_disable ( tgi , gpio ) ;
return ret ;
}
2010-03-16 05:40:06 +03:00
if ( type & ( IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH ) )
2015-06-23 16:52:40 +03:00
irq_set_handler_locked ( d , handle_level_irq ) ;
2010-03-16 05:40:06 +03:00
else if ( type & ( IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING ) )
2015-06-23 16:52:40 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2010-03-16 05:40:06 +03:00
2020-11-27 17:08:52 +03:00
if ( d - > parent_data )
ret = irq_chip_set_type_parent ( d , type ) ;
return ret ;
2010-03-16 05:40:06 +03:00
}
2013-10-16 23:25:33 +04:00
static void tegra_gpio_irq_shutdown ( struct irq_data * d )
{
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq ;
2013-10-16 23:25:33 +04:00
2020-04-28 02:26:05 +03:00
tegra_gpio_irq_mask ( d ) ;
2016-04-25 13:38:33 +03:00
gpiochip_unlock_as_irq ( & tgi - > gc , gpio ) ;
2013-10-16 23:25:33 +04:00
}
2015-09-14 11:42:37 +03:00
static void tegra_gpio_irq_handler ( struct irq_desc * desc )
2010-03-16 05:40:06 +03:00
{
2020-11-27 17:08:52 +03:00
struct tegra_gpio_info * tgi = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
struct irq_domain * domain = tgi - > gc . irq . domain ;
unsigned int irq = irq_desc_get_irq ( desc ) ;
struct tegra_gpio_bank * bank = NULL ;
unsigned int port , pin , gpio , i ;
2017-07-18 15:35:45 +03:00
bool unmasked = false ;
2016-04-25 13:38:33 +03:00
unsigned long sta ;
2020-11-27 17:08:52 +03:00
u32 lvl ;
for ( i = 0 ; i < tgi - > bank_count ; i + + ) {
if ( tgi - > irqs [ i ] = = irq ) {
bank = & tgi - > bank_info [ i ] ;
break ;
}
}
if ( WARN_ON ( bank = = NULL ) )
return ;
2010-03-16 05:40:06 +03:00
2011-02-21 16:58:10 +03:00
chained_irq_enter ( chip , desc ) ;
2010-03-16 05:40:06 +03:00
for ( port = 0 ; port < 4 ; port + + ) {
2016-04-25 13:38:33 +03:00
gpio = tegra_gpio_compose ( bank - > bank , port , 0 ) ;
sta = tegra_gpio_readl ( tgi , GPIO_INT_STA ( tgi , gpio ) ) &
tegra_gpio_readl ( tgi , GPIO_INT_ENB ( tgi , gpio ) ) ;
lvl = tegra_gpio_readl ( tgi , GPIO_INT_LVL ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
for_each_set_bit ( pin , & sta , 8 ) {
2021-05-04 19:42:18 +03:00
int ret ;
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , 1 < < pin ,
GPIO_INT_CLR ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
/* if gpio is edge triggered, clear condition
2015-05-18 21:41:43 +03:00
* before executing the handler so that we don ' t
2010-03-16 05:40:06 +03:00
* miss edges
*/
2017-07-18 15:35:45 +03:00
if ( ! unmasked & & lvl & ( 0x100 < < pin ) ) {
unmasked = true ;
2011-02-21 16:58:10 +03:00
chained_irq_exit ( chip , desc ) ;
2010-03-16 05:40:06 +03:00
}
2021-05-04 19:42:18 +03:00
ret = generic_handle_domain_irq ( domain , gpio + pin ) ;
WARN_RATELIMIT ( ret , " hwirq = %d " , gpio + pin ) ;
2010-03-16 05:40:06 +03:00
}
}
if ( ! unmasked )
2011-02-21 16:58:10 +03:00
chained_irq_exit ( chip , desc ) ;
2020-11-27 17:08:52 +03:00
}
2021-01-22 22:59:59 +03:00
static int tegra_gpio_child_to_parent_hwirq ( struct gpio_chip * chip ,
unsigned int hwirq ,
unsigned int type ,
unsigned int * parent_hwirq ,
2020-11-27 17:08:52 +03:00
unsigned int * parent_type )
{
* parent_hwirq = chip - > irq . child_offset_to_irq ( chip , hwirq ) ;
* parent_type = type ;
2010-03-16 05:40:06 +03:00
2020-11-27 17:08:52 +03:00
return 0 ;
}
2022-07-07 21:23:09 +03:00
static int tegra_gpio_populate_parent_fwspec ( struct gpio_chip * chip ,
union gpio_irq_fwspec * gfwspec ,
unsigned int parent_hwirq ,
unsigned int parent_type )
2020-11-27 17:08:52 +03:00
{
2022-07-07 21:23:09 +03:00
struct irq_fwspec * fwspec = & gfwspec - > fwspec ;
2020-11-27 17:08:52 +03:00
fwspec - > fwnode = chip - > irq . parent_domain - > fwnode ;
fwspec - > param_count = 3 ;
fwspec - > param [ 0 ] = 0 ;
fwspec - > param [ 1 ] = parent_hwirq ;
fwspec - > param [ 2 ] = parent_type ;
2022-07-07 21:23:09 +03:00
return 0 ;
2010-03-16 05:40:06 +03:00
}
2012-11-07 19:01:32 +04:00
# ifdef CONFIG_PM_SLEEP
static int tegra_gpio_resume ( struct device * dev )
2010-04-07 23:59:42 +04:00
{
2018-10-21 23:00:00 +03:00
struct tegra_gpio_info * tgi = dev_get_drvdata ( dev ) ;
2017-07-24 17:55:08 +03:00
unsigned int b , p ;
2010-04-07 23:59:42 +04:00
2016-04-25 13:38:33 +03:00
for ( b = 0 ; b < tgi - > bank_count ; b + + ) {
struct tegra_gpio_bank * bank = & tgi - > bank_info [ b ] ;
2010-04-07 23:59:42 +04:00
for ( p = 0 ; p < ARRAY_SIZE ( bank - > oe ) ; p + + ) {
2017-07-24 17:55:07 +03:00
unsigned int gpio = ( b < < 5 ) | ( p < < 3 ) ;
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , bank - > cnf [ p ] ,
GPIO_CNF ( tgi , gpio ) ) ;
2016-04-25 13:38:34 +03:00
if ( tgi - > soc - > debounce_supported ) {
tegra_gpio_writel ( tgi , bank - > dbc_cnt [ p ] ,
GPIO_DBC_CNT ( tgi , gpio ) ) ;
tegra_gpio_writel ( tgi , bank - > dbc_enb [ p ] ,
GPIO_MSK_DBC_EN ( tgi , gpio ) ) ;
}
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , bank - > out [ p ] ,
GPIO_OUT ( tgi , gpio ) ) ;
tegra_gpio_writel ( tgi , bank - > oe [ p ] ,
GPIO_OE ( tgi , gpio ) ) ;
tegra_gpio_writel ( tgi , bank - > int_lvl [ p ] ,
GPIO_INT_LVL ( tgi , gpio ) ) ;
tegra_gpio_writel ( tgi , bank - > int_enb [ p ] ,
GPIO_INT_ENB ( tgi , gpio ) ) ;
2010-04-07 23:59:42 +04:00
}
}
2012-11-07 19:01:32 +04:00
return 0 ;
2010-04-07 23:59:42 +04:00
}
2012-11-07 19:01:32 +04:00
static int tegra_gpio_suspend ( struct device * dev )
2010-04-07 23:59:42 +04:00
{
2018-10-21 23:00:00 +03:00
struct tegra_gpio_info * tgi = dev_get_drvdata ( dev ) ;
2017-07-24 17:55:08 +03:00
unsigned int b , p ;
2010-04-07 23:59:42 +04:00
2016-04-25 13:38:33 +03:00
for ( b = 0 ; b < tgi - > bank_count ; b + + ) {
struct tegra_gpio_bank * bank = & tgi - > bank_info [ b ] ;
2010-04-07 23:59:42 +04:00
for ( p = 0 ; p < ARRAY_SIZE ( bank - > oe ) ; p + + ) {
2017-07-24 17:55:07 +03:00
unsigned int gpio = ( b < < 5 ) | ( p < < 3 ) ;
2016-04-25 13:38:33 +03:00
bank - > cnf [ p ] = tegra_gpio_readl ( tgi ,
GPIO_CNF ( tgi , gpio ) ) ;
bank - > out [ p ] = tegra_gpio_readl ( tgi ,
GPIO_OUT ( tgi , gpio ) ) ;
bank - > oe [ p ] = tegra_gpio_readl ( tgi ,
GPIO_OE ( tgi , gpio ) ) ;
2016-04-25 13:38:34 +03:00
if ( tgi - > soc - > debounce_supported ) {
bank - > dbc_enb [ p ] = tegra_gpio_readl ( tgi ,
GPIO_MSK_DBC_EN ( tgi , gpio ) ) ;
bank - > dbc_enb [ p ] = ( bank - > dbc_enb [ p ] < < 8 ) |
bank - > dbc_enb [ p ] ;
}
2016-04-25 13:38:33 +03:00
bank - > int_enb [ p ] = tegra_gpio_readl ( tgi ,
GPIO_INT_ENB ( tgi , gpio ) ) ;
bank - > int_lvl [ p ] = tegra_gpio_readl ( tgi ,
GPIO_INT_LVL ( tgi , gpio ) ) ;
2013-04-03 15:31:44 +04:00
/* Enable gpio irq for wake up source */
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , bank - > wake_enb [ p ] ,
GPIO_INT_ENB ( tgi , gpio ) ) ;
2010-04-07 23:59:42 +04:00
}
}
2019-12-15 21:30:47 +03:00
2012-11-07 19:01:32 +04:00
return 0 ;
2010-04-07 23:59:42 +04:00
}
2013-04-03 15:31:44 +04:00
static int tegra_gpio_irq_set_wake ( struct irq_data * d , unsigned int enable )
2010-04-07 23:59:42 +04:00
{
2020-11-27 17:08:52 +03:00
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
struct tegra_gpio_bank * bank ;
2017-07-24 17:55:08 +03:00
unsigned int gpio = d - > hwirq ;
2013-04-03 15:31:44 +04:00
u32 port , bit , mask ;
2021-01-12 16:30:09 +03:00
int err ;
2019-12-15 21:30:46 +03:00
2020-11-27 17:08:52 +03:00
bank = & tgi - > bank_info [ GPIO_BANK ( d - > hwirq ) ] ;
2013-04-03 15:31:44 +04:00
port = GPIO_PORT ( gpio ) ;
bit = GPIO_BIT ( gpio ) ;
mask = BIT ( bit ) ;
2021-01-12 16:30:09 +03:00
err = irq_set_irq_wake ( tgi - > irqs [ bank - > bank ] , enable ) ;
if ( err )
return err ;
if ( d - > parent_data ) {
err = irq_chip_set_wake_parent ( d , enable ) ;
if ( err ) {
irq_set_irq_wake ( tgi - > irqs [ bank - > bank ] , ! enable ) ;
return err ;
}
}
2013-04-03 15:31:44 +04:00
if ( enable )
bank - > wake_enb [ port ] | = mask ;
else
bank - > wake_enb [ port ] & = ~ mask ;
2019-12-15 21:30:46 +03:00
return 0 ;
2010-04-07 23:59:42 +04:00
}
# endif
2010-03-16 05:40:06 +03:00
2021-01-22 22:59:59 +03:00
static int tegra_gpio_irq_set_affinity ( struct irq_data * data ,
const struct cpumask * dest ,
2020-11-27 17:08:52 +03:00
bool force )
{
if ( data - > parent_data )
return irq_chip_set_affinity_parent ( data , dest , force ) ;
return - EINVAL ;
}
static int tegra_gpio_irq_request_resources ( struct irq_data * d )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
tegra_gpio_enable ( tgi , d - > hwirq ) ;
return gpiochip_reqres_irq ( chip , d - > hwirq ) ;
}
static void tegra_gpio_irq_release_resources ( struct irq_data * d )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
struct tegra_gpio_info * tgi = gpiochip_get_data ( chip ) ;
gpiochip_relres_irq ( chip , d - > hwirq ) ;
tegra_gpio_enable ( tgi , d - > hwirq ) ;
}
2022-10-19 09:12:01 +03:00
static void tegra_gpio_irq_print_chip ( struct irq_data * d , struct seq_file * s )
{
struct gpio_chip * chip = irq_data_get_irq_chip_data ( d ) ;
seq_printf ( s , dev_name ( chip - > parent ) ) ;
}
static const struct irq_chip tegra_gpio_irq_chip = {
. irq_shutdown = tegra_gpio_irq_shutdown ,
. irq_ack = tegra_gpio_irq_ack ,
. irq_mask = tegra_gpio_irq_mask ,
. irq_unmask = tegra_gpio_irq_unmask ,
. irq_set_type = tegra_gpio_irq_set_type ,
# ifdef CONFIG_PM_SLEEP
. irq_set_wake = tegra_gpio_irq_set_wake ,
# endif
. irq_print_chip = tegra_gpio_irq_print_chip ,
. irq_request_resources = tegra_gpio_irq_request_resources ,
. irq_release_resources = tegra_gpio_irq_release_resources ,
. flags = IRQCHIP_IMMUTABLE ,
} ;
static const struct irq_chip tegra210_gpio_irq_chip = {
. irq_shutdown = tegra_gpio_irq_shutdown ,
. irq_ack = tegra_gpio_irq_ack ,
. irq_mask = tegra_gpio_irq_mask ,
. irq_unmask = tegra_gpio_irq_unmask ,
. irq_set_affinity = tegra_gpio_irq_set_affinity ,
. irq_set_type = tegra_gpio_irq_set_type ,
# ifdef CONFIG_PM_SLEEP
. irq_set_wake = tegra_gpio_irq_set_wake ,
# endif
. irq_print_chip = tegra_gpio_irq_print_chip ,
. irq_request_resources = tegra_gpio_irq_request_resources ,
. irq_release_resources = tegra_gpio_irq_release_resources ,
. flags = IRQCHIP_IMMUTABLE ,
} ;
2015-11-16 19:07:10 +03:00
# ifdef CONFIG_DEBUG_FS
# include <linux/debugfs.h>
2018-02-12 17:01:57 +03:00
static int tegra_dbg_gpio_show ( struct seq_file * s , void * unused )
2015-11-16 19:07:10 +03:00
{
2021-01-22 21:55:41 +03:00
struct tegra_gpio_info * tgi = dev_get_drvdata ( s - > private ) ;
2017-07-24 17:55:08 +03:00
unsigned int i , j ;
2015-11-16 19:07:10 +03:00
2016-04-25 13:38:33 +03:00
for ( i = 0 ; i < tgi - > bank_count ; i + + ) {
2015-11-16 19:07:10 +03:00
for ( j = 0 ; j < 4 ; j + + ) {
2017-07-24 17:55:08 +03:00
unsigned int gpio = tegra_gpio_compose ( i , j , 0 ) ;
2017-07-24 17:55:07 +03:00
2015-11-16 19:07:10 +03:00
seq_printf ( s ,
2017-07-24 17:55:08 +03:00
" %u:%u %02x %02x %02x %02x %02x %02x %06x \n " ,
2015-11-16 19:07:10 +03:00
i , j ,
2016-04-25 13:38:33 +03:00
tegra_gpio_readl ( tgi , GPIO_CNF ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_OE ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_OUT ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_IN ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_INT_STA ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_INT_ENB ( tgi , gpio ) ) ,
tegra_gpio_readl ( tgi , GPIO_INT_LVL ( tgi , gpio ) ) ) ;
2015-11-16 19:07:10 +03:00
}
}
return 0 ;
}
2016-04-25 13:38:33 +03:00
static void tegra_gpio_debuginit ( struct tegra_gpio_info * tgi )
2015-11-16 19:07:10 +03:00
{
2021-01-22 21:55:41 +03:00
debugfs_create_devm_seqfile ( tgi - > dev , " tegra_gpio " , NULL ,
tegra_dbg_gpio_show ) ;
2015-11-16 19:07:10 +03:00
}
# else
2016-04-25 13:38:33 +03:00
static inline void tegra_gpio_debuginit ( struct tegra_gpio_info * tgi )
2015-11-16 19:07:10 +03:00
{
}
# endif
2012-11-07 19:01:32 +04:00
static const struct dev_pm_ops tegra_gpio_pm_ops = {
2019-12-15 21:30:47 +03:00
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS ( tegra_gpio_suspend , tegra_gpio_resume )
2012-11-07 19:01:32 +04:00
} ;
2020-11-27 17:08:52 +03:00
static const struct of_device_id tegra_pmc_of_match [ ] = {
{ . compatible = " nvidia,tegra210-pmc " , } ,
{ /* sentinel */ } ,
} ;
2020-11-04 20:04:22 +03:00
2012-11-19 22:22:34 +04:00
static int tegra_gpio_probe ( struct platform_device * pdev )
2010-03-16 05:40:06 +03:00
{
struct tegra_gpio_bank * bank ;
2020-11-27 17:08:52 +03:00
struct tegra_gpio_info * tgi ;
struct gpio_irq_chip * irq ;
struct device_node * np ;
unsigned int i , j ;
2013-12-07 00:36:11 +04:00
int ret ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
tgi = devm_kzalloc ( & pdev - > dev , sizeof ( * tgi ) , GFP_KERNEL ) ;
if ( ! tgi )
return - ENODEV ;
2017-07-24 17:55:05 +03:00
tgi - > soc = of_device_get_match_data ( & pdev - > dev ) ;
2016-04-25 13:38:33 +03:00
tgi - > dev = & pdev - > dev ;
2012-03-17 03:35:08 +04:00
2017-07-20 19:00:56 +03:00
ret = platform_irq_count ( pdev ) ;
if ( ret < 0 )
return ret ;
tgi - > bank_count = ret ;
2016-04-25 13:38:33 +03:00
if ( ! tgi - > bank_count ) {
2012-01-19 12:16:35 +04:00
dev_err ( & pdev - > dev , " Missing IRQ resource \n " ) ;
return - ENODEV ;
}
2016-04-25 13:38:33 +03:00
tgi - > gc . label = " tegra-gpio " ;
tgi - > gc . request = tegra_gpio_request ;
tgi - > gc . free = tegra_gpio_free ;
tgi - > gc . direction_input = tegra_gpio_direction_input ;
tgi - > gc . get = tegra_gpio_get ;
tgi - > gc . direction_output = tegra_gpio_direction_output ;
tgi - > gc . set = tegra_gpio_set ;
2016-04-29 19:25:23 +03:00
tgi - > gc . get_direction = tegra_gpio_get_direction ;
2016-04-25 13:38:33 +03:00
tgi - > gc . base = 0 ;
tgi - > gc . ngpio = tgi - > bank_count * 32 ;
tgi - > gc . parent = & pdev - > dev ;
platform_set_drvdata ( pdev , tgi ) ;
2012-01-19 12:16:35 +04:00
2017-07-24 17:55:05 +03:00
if ( tgi - > soc - > debounce_supported )
2017-01-23 15:34:34 +03:00
tgi - > gc . set_config = tegra_gpio_set_config ;
2016-04-25 13:38:34 +03:00
2017-07-24 17:55:06 +03:00
tgi - > bank_info = devm_kcalloc ( & pdev - > dev , tgi - > bank_count ,
2016-04-25 13:38:33 +03:00
sizeof ( * tgi - > bank_info ) , GFP_KERNEL ) ;
if ( ! tgi - > bank_info )
2017-07-24 17:55:06 +03:00
return - ENOMEM ;
2012-01-19 12:16:35 +04:00
2021-01-22 22:59:59 +03:00
tgi - > irqs = devm_kcalloc ( & pdev - > dev , tgi - > bank_count ,
sizeof ( * tgi - > irqs ) , GFP_KERNEL ) ;
2020-11-27 17:08:52 +03:00
if ( ! tgi - > irqs )
return - ENOMEM ;
2012-01-04 12:39:37 +04:00
2016-04-25 13:38:33 +03:00
for ( i = 0 ; i < tgi - > bank_count ; i + + ) {
2017-07-20 19:00:57 +03:00
ret = platform_get_irq ( pdev , i ) ;
2019-07-30 21:15:15 +03:00
if ( ret < 0 )
2017-07-20 19:00:57 +03:00
return ret ;
2011-10-12 02:16:14 +04:00
2016-04-25 13:38:33 +03:00
bank = & tgi - > bank_info [ i ] ;
2011-10-12 02:16:14 +04:00
bank - > bank = i ;
2020-11-27 17:08:52 +03:00
tgi - > irqs [ i ] = ret ;
for ( j = 0 ; j < 4 ; j + + ) {
raw_spin_lock_init ( & bank - > lvl_lock [ j ] ) ;
spin_lock_init ( & bank - > dbc_lock [ j ] ) ;
}
}
irq = & tgi - > gc . irq ;
irq - > fwnode = of_node_to_fwnode ( pdev - > dev . of_node ) ;
irq - > child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq ;
irq - > populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec ;
irq - > handler = handle_simple_irq ;
irq - > default_type = IRQ_TYPE_NONE ;
irq - > parent_handler = tegra_gpio_irq_handler ;
irq - > parent_handler_data = tgi ;
irq - > num_parents = tgi - > bank_count ;
irq - > parents = tgi - > irqs ;
np = of_find_matching_node ( NULL , tegra_pmc_of_match ) ;
if ( np ) {
irq - > parent_domain = irq_find_host ( np ) ;
of_node_put ( np ) ;
if ( ! irq - > parent_domain )
return - EPROBE_DEFER ;
2021-01-20 03:45:48 +03:00
2022-10-19 09:12:01 +03:00
gpio_irq_chip_set_chip ( irq , & tegra210_gpio_irq_chip ) ;
} else {
gpio_irq_chip_set_chip ( irq , & tegra_gpio_irq_chip ) ;
2011-10-12 02:16:14 +04:00
}
2019-03-11 21:55:12 +03:00
tgi - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-04-25 13:38:33 +03:00
if ( IS_ERR ( tgi - > regs ) )
return PTR_ERR ( tgi - > regs ) ;
2011-10-12 02:16:14 +04:00
2016-04-25 13:38:33 +03:00
for ( i = 0 ; i < tgi - > bank_count ; i + + ) {
2010-03-16 05:40:06 +03:00
for ( j = 0 ; j < 4 ; j + + ) {
int gpio = tegra_gpio_compose ( i , j , 0 ) ;
2017-07-24 17:55:07 +03:00
2016-04-25 13:38:33 +03:00
tegra_gpio_writel ( tgi , 0x00 , GPIO_INT_ENB ( tgi , gpio ) ) ;
2010-03-16 05:40:06 +03:00
}
}
2016-04-25 13:38:33 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & tgi - > gc , tgi ) ;
2020-11-27 17:08:52 +03:00
if ( ret < 0 )
2013-12-07 00:36:11 +04:00
return ret ;
2010-03-16 05:40:06 +03:00
2016-04-25 13:38:33 +03:00
tegra_gpio_debuginit ( tgi ) ;
2015-11-16 19:07:10 +03:00
2010-03-16 05:40:06 +03:00
return 0 ;
}
2016-04-25 13:38:32 +03:00
static const struct tegra_gpio_soc_config tegra20_gpio_config = {
2016-04-25 13:38:31 +03:00
. bank_stride = 0x80 ,
. upper_offset = 0x800 ,
} ;
2016-04-25 13:38:32 +03:00
static const struct tegra_gpio_soc_config tegra30_gpio_config = {
2016-04-25 13:38:31 +03:00
. bank_stride = 0x100 ,
. upper_offset = 0x80 ,
} ;
2016-04-25 13:38:34 +03:00
static const struct tegra_gpio_soc_config tegra210_gpio_config = {
. debounce_supported = true ,
. bank_stride = 0x100 ,
. upper_offset = 0x80 ,
} ;
2016-04-25 13:38:31 +03:00
static const struct of_device_id tegra_gpio_of_match [ ] = {
2016-04-25 13:38:34 +03:00
{ . compatible = " nvidia,tegra210-gpio " , . data = & tegra210_gpio_config } ,
2016-04-25 13:38:31 +03:00
{ . compatible = " nvidia,tegra30-gpio " , . data = & tegra30_gpio_config } ,
{ . compatible = " nvidia,tegra20-gpio " , . data = & tegra20_gpio_config } ,
{ } ,
} ;
2021-01-22 21:55:43 +03:00
MODULE_DEVICE_TABLE ( of , tegra_gpio_of_match ) ;
2016-04-25 13:38:31 +03:00
2011-10-12 02:16:14 +04:00
static struct platform_driver tegra_gpio_driver = {
2021-01-22 21:55:42 +03:00
. driver = {
. name = " tegra-gpio " ,
. pm = & tegra_gpio_pm_ops ,
2011-10-12 02:16:14 +04:00
. of_match_table = tegra_gpio_of_match ,
} ,
2021-01-22 21:55:42 +03:00
. probe = tegra_gpio_probe ,
2011-10-12 02:16:14 +04:00
} ;
2021-01-22 21:55:43 +03:00
module_platform_driver ( tegra_gpio_driver ) ;
MODULE_DESCRIPTION ( " NVIDIA Tegra GPIO controller driver " ) ;
MODULE_AUTHOR ( " Laxman Dewangan <ldewangan@nvidia.com> " ) ;
MODULE_AUTHOR ( " Stephen Warren <swarren@nvidia.com> " ) ;
MODULE_AUTHOR ( " Thierry Reding <treding@nvidia.com> " ) ;
MODULE_AUTHOR ( " Erik Gilling <konkers@google.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;