2018-09-07 23:04:50 +03:00
// SPDX-License-Identifier: GPL-2.0+
2014-10-16 23:47:58 +04:00
/*
2016-08-22 19:48:33 +03:00
* Freescale vf610 GPIO support through PORT and GPIO
2014-10-16 23:47:58 +04:00
*
* Copyright ( c ) 2014 Toradex AG .
*
* Author : Stefan Agner < stefan @ agner . ch > .
*/
# include <linux/bitops.h>
# include <linux/err.h>
2018-09-07 23:03:07 +03:00
# include <linux/gpio/driver.h>
2014-10-16 23:47:58 +04:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/irq.h>
# include <linux/platform_device.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# define VF610_GPIO_PER_PORT 32
2017-08-01 17:04:17 +03:00
struct fsl_gpio_soc_data {
/* SoCs has a Port Data Direction Register (PDDR) */
bool have_paddr ;
} ;
2014-10-16 23:47:58 +04:00
struct vf610_gpio_port {
struct gpio_chip gc ;
void __iomem * base ;
void __iomem * gpio_base ;
2017-08-01 17:04:17 +03:00
const struct fsl_gpio_soc_data * sdata ;
2014-10-16 23:47:58 +04:00
u8 irqc [ VF610_GPIO_PER_PORT ] ;
int irq ;
} ;
# define GPIO_PDOR 0x00
# define GPIO_PSOR 0x04
# define GPIO_PCOR 0x08
# define GPIO_PTOR 0x0c
# define GPIO_PDIR 0x10
2017-08-01 17:04:17 +03:00
# define GPIO_PDDR 0x14
2014-10-16 23:47:58 +04:00
# define PORT_PCR(n) ((n) * 0x4)
# define PORT_PCR_IRQC_OFFSET 16
# define PORT_ISFR 0xa0
# define PORT_DFER 0xc0
# define PORT_DFCR 0xc4
# define PORT_DFWR 0xc8
# define PORT_INT_OFF 0x0
# define PORT_INT_LOGIC_ZERO 0x8
# define PORT_INT_RISING_EDGE 0x9
# define PORT_INT_FALLING_EDGE 0xa
# define PORT_INT_EITHER_EDGE 0xb
# define PORT_INT_LOGIC_ONE 0xc
2015-08-22 01:56:42 +03:00
static struct irq_chip vf610_gpio_irq_chip ;
2017-08-01 17:04:17 +03:00
static const struct fsl_gpio_soc_data imx_data = {
. have_paddr = true ,
} ;
2014-10-16 23:47:58 +04:00
static const struct of_device_id vf610_gpio_dt_ids [ ] = {
2017-08-01 17:04:17 +03:00
{ . compatible = " fsl,vf610-gpio " , . data = NULL , } ,
{ . compatible = " fsl,imx7ulp-gpio " , . data = & imx_data , } ,
2014-10-16 23:47:58 +04:00
{ /* sentinel */ }
} ;
static inline void vf610_gpio_writel ( u32 val , void __iomem * reg )
{
writel_relaxed ( val , reg ) ;
}
static inline u32 vf610_gpio_readl ( void __iomem * reg )
{
return readl_relaxed ( reg ) ;
}
static int vf610_gpio_get ( struct gpio_chip * gc , unsigned int gpio )
{
2015-12-07 17:03:30 +03:00
struct vf610_gpio_port * port = gpiochip_get_data ( gc ) ;
2017-08-01 17:04:17 +03:00
unsigned long mask = BIT ( gpio ) ;
void __iomem * addr ;
if ( port - > sdata & & port - > sdata - > have_paddr ) {
mask & = vf610_gpio_readl ( port - > gpio_base + GPIO_PDDR ) ;
addr = mask ? port - > gpio_base + GPIO_PDOR :
port - > gpio_base + GPIO_PDIR ;
return ! ! ( vf610_gpio_readl ( addr ) & BIT ( gpio ) ) ;
} else {
return ! ! ( vf610_gpio_readl ( port - > gpio_base + GPIO_PDIR )
& BIT ( gpio ) ) ;
}
2014-10-16 23:47:58 +04:00
}
static void vf610_gpio_set ( struct gpio_chip * gc , unsigned int gpio , int val )
{
2015-12-07 17:03:30 +03:00
struct vf610_gpio_port * port = gpiochip_get_data ( gc ) ;
2014-10-16 23:47:58 +04:00
unsigned long mask = BIT ( gpio ) ;
if ( val )
vf610_gpio_writel ( mask , port - > gpio_base + GPIO_PSOR ) ;
else
vf610_gpio_writel ( mask , port - > gpio_base + GPIO_PCOR ) ;
}
static int vf610_gpio_direction_input ( struct gpio_chip * chip , unsigned gpio )
{
2017-08-01 17:04:17 +03:00
struct vf610_gpio_port * port = gpiochip_get_data ( chip ) ;
unsigned long mask = BIT ( gpio ) ;
u32 val ;
if ( port - > sdata & & port - > sdata - > have_paddr ) {
val = vf610_gpio_readl ( port - > gpio_base + GPIO_PDDR ) ;
val & = ~ mask ;
vf610_gpio_writel ( val , port - > gpio_base + GPIO_PDDR ) ;
}
2014-10-16 23:47:58 +04:00
return pinctrl_gpio_direction_input ( chip - > base + gpio ) ;
}
static int vf610_gpio_direction_output ( struct gpio_chip * chip , unsigned gpio ,
int value )
{
2017-08-01 17:04:17 +03:00
struct vf610_gpio_port * port = gpiochip_get_data ( chip ) ;
unsigned long mask = BIT ( gpio ) ;
if ( port - > sdata & & port - > sdata - > have_paddr )
vf610_gpio_writel ( mask , port - > gpio_base + GPIO_PDDR ) ;
2014-10-16 23:47:58 +04:00
vf610_gpio_set ( chip , gpio , value ) ;
return pinctrl_gpio_direction_output ( chip - > base + gpio ) ;
}
2015-09-14 11:42:37 +03:00
static void vf610_gpio_irq_handler ( struct irq_desc * desc )
2014-10-16 23:47:58 +04:00
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_desc_get_handler_data ( desc ) ) ;
2014-10-16 23:47:58 +04:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
int pin ;
unsigned long irq_isfr ;
chained_irq_enter ( chip , desc ) ;
irq_isfr = vf610_gpio_readl ( port - > base + PORT_ISFR ) ;
for_each_set_bit ( pin , & irq_isfr , VF610_GPIO_PER_PORT ) {
vf610_gpio_writel ( BIT ( pin ) , port - > base + PORT_ISFR ) ;
2017-11-07 21:15:47 +03:00
generic_handle_irq ( irq_find_mapping ( port - > gc . irq . domain , pin ) ) ;
2014-10-16 23:47:58 +04:00
}
chained_irq_exit ( chip , desc ) ;
}
static void vf610_gpio_irq_ack ( struct irq_data * d )
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2014-10-16 23:47:58 +04:00
int gpio = d - > hwirq ;
vf610_gpio_writel ( BIT ( gpio ) , port - > base + PORT_ISFR ) ;
}
static int vf610_gpio_irq_set_type ( struct irq_data * d , u32 type )
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2014-10-16 23:47:58 +04:00
u8 irqc ;
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
irqc = PORT_INT_RISING_EDGE ;
break ;
case IRQ_TYPE_EDGE_FALLING :
irqc = PORT_INT_FALLING_EDGE ;
break ;
case IRQ_TYPE_EDGE_BOTH :
irqc = PORT_INT_EITHER_EDGE ;
break ;
case IRQ_TYPE_LEVEL_LOW :
irqc = PORT_INT_LOGIC_ZERO ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
irqc = PORT_INT_LOGIC_ONE ;
break ;
default :
return - EINVAL ;
}
port - > irqc [ d - > hwirq ] = irqc ;
2015-08-22 01:56:42 +03:00
if ( type & IRQ_TYPE_LEVEL_MASK )
2015-09-16 13:51:00 +03:00
irq_set_handler_locked ( d , handle_level_irq ) ;
2015-08-22 01:56:42 +03:00
else
2015-09-16 13:51:00 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2015-08-22 01:56:42 +03:00
2014-10-16 23:47:58 +04:00
return 0 ;
}
static void vf610_gpio_irq_mask ( struct irq_data * d )
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2014-10-16 23:47:58 +04:00
void __iomem * pcr_base = port - > base + PORT_PCR ( d - > hwirq ) ;
vf610_gpio_writel ( 0 , pcr_base ) ;
}
static void vf610_gpio_irq_unmask ( struct irq_data * d )
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2014-10-16 23:47:58 +04:00
void __iomem * pcr_base = port - > base + PORT_PCR ( d - > hwirq ) ;
vf610_gpio_writel ( port - > irqc [ d - > hwirq ] < < PORT_PCR_IRQC_OFFSET ,
pcr_base ) ;
}
static int vf610_gpio_irq_set_wake ( struct irq_data * d , u32 enable )
{
2015-08-27 15:13:46 +03:00
struct vf610_gpio_port * port =
2015-12-07 17:03:30 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2014-10-16 23:47:58 +04:00
if ( enable )
enable_irq_wake ( port - > irq ) ;
else
disable_irq_wake ( port - > irq ) ;
return 0 ;
}
static struct irq_chip vf610_gpio_irq_chip = {
. name = " gpio-vf610 " ,
. irq_ack = vf610_gpio_irq_ack ,
. irq_mask = vf610_gpio_irq_mask ,
. irq_unmask = vf610_gpio_irq_unmask ,
. irq_set_type = vf610_gpio_irq_set_type ,
. irq_set_wake = vf610_gpio_irq_set_wake ,
} ;
static int vf610_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct vf610_gpio_port * port ;
struct resource * iores ;
struct gpio_chip * gc ;
int ret ;
port = devm_kzalloc ( & pdev - > dev , sizeof ( * port ) , GFP_KERNEL ) ;
if ( ! port )
return - ENOMEM ;
2018-04-30 10:38:18 +03:00
port - > sdata = of_device_get_match_data ( dev ) ;
2014-10-16 23:47:58 +04:00
iores = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
port - > base = devm_ioremap_resource ( dev , iores ) ;
if ( IS_ERR ( port - > base ) )
return PTR_ERR ( port - > base ) ;
iores = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
port - > gpio_base = devm_ioremap_resource ( dev , iores ) ;
if ( IS_ERR ( port - > gpio_base ) )
return PTR_ERR ( port - > gpio_base ) ;
port - > irq = platform_get_irq ( pdev , 0 ) ;
if ( port - > irq < 0 )
return port - > irq ;
gc = & port - > gc ;
gc - > of_node = np ;
2015-11-04 11:56:26 +03:00
gc - > parent = dev ;
2015-02-13 16:04:42 +03:00
gc - > label = " vf610-gpio " ;
gc - > ngpio = VF610_GPIO_PER_PORT ;
2014-10-16 23:47:58 +04:00
gc - > base = of_alias_get_id ( np , " gpio " ) * VF610_GPIO_PER_PORT ;
2015-10-11 18:34:16 +03:00
gc - > request = gpiochip_generic_request ;
gc - > free = gpiochip_generic_free ;
2015-02-13 16:04:42 +03:00
gc - > direction_input = vf610_gpio_direction_input ;
gc - > get = vf610_gpio_get ;
gc - > direction_output = vf610_gpio_direction_output ;
gc - > set = vf610_gpio_set ;
2014-10-16 23:47:58 +04:00
2015-12-07 17:03:30 +03:00
ret = gpiochip_add_data ( gc , port ) ;
2014-10-16 23:47:58 +04:00
if ( ret < 0 )
return ret ;
/* Clear the interrupt status register for all GPIO's */
vf610_gpio_writel ( ~ 0 , port - > base + PORT_ISFR ) ;
ret = gpiochip_irqchip_add ( gc , & vf610_gpio_irq_chip , 0 ,
2015-08-22 01:56:42 +03:00
handle_edge_irq , IRQ_TYPE_NONE ) ;
2014-10-16 23:47:58 +04:00
if ( ret ) {
dev_err ( dev , " failed to add irqchip \n " ) ;
gpiochip_remove ( gc ) ;
return ret ;
}
gpiochip_set_chained_irqchip ( gc , & vf610_gpio_irq_chip , port - > irq ,
vf610_gpio_irq_handler ) ;
return 0 ;
}
static struct platform_driver vf610_gpio_driver = {
. driver = {
. name = " gpio-vf610 " ,
. of_match_table = vf610_gpio_dt_ids ,
} ,
. probe = vf610_gpio_probe ,
} ;
2016-11-23 17:47:48 +03:00
builtin_platform_driver ( vf610_gpio_driver ) ;