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>
2018-11-10 17:21:18 +03:00
# include <linux/clk.h>
2014-10-16 23:47:58 +04:00
# 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>
2022-07-05 13:21:14 +03:00
# include <linux/pinctrl/consumer.h>
2014-10-16 23:47:58 +04:00
# 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 ;
2019-03-11 09:27:31 +03:00
struct irq_chip ic ;
2014-10-16 23:47:58 +04:00
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 ] ;
2018-11-10 17:21:18 +03:00
struct clk * clk_port ;
struct clk * clk_gpio ;
2014-10-16 23:47:58 +04:00
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
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 ) ;
2019-03-11 09:27:33 +03:00
unsigned long offset = GPIO_PDIR ;
2017-08-01 17:04:17 +03:00
if ( port - > sdata & & port - > sdata - > have_paddr ) {
mask & = vf610_gpio_readl ( port - > gpio_base + GPIO_PDDR ) ;
2019-03-11 09:27:33 +03:00
if ( mask )
offset = GPIO_PDOR ;
2017-08-01 17:04:17 +03:00
}
2019-03-11 09:27:33 +03:00
return ! ! ( vf610_gpio_readl ( port - > gpio_base + offset ) & 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 ) ;
2019-03-11 09:27:32 +03:00
unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR ;
2014-10-16 23:47:58 +04:00
2019-03-11 09:27:32 +03:00
vf610_gpio_writel ( mask , port - > gpio_base + offset ) ;
2014-10-16 23:47:58 +04:00
}
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 ) ;
2022-05-11 05:15:04 +03:00
u32 val ;
2017-08-01 17:04:17 +03:00
2022-05-11 05:15:04 +03:00
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 ) ;
}
2017-08-01 17:04:17 +03:00
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 ) ;
2021-05-04 19:42:18 +03:00
generic_handle_domain_irq ( 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 ;
}
2019-03-11 09:27:34 +03:00
static void vf610_gpio_disable_clk ( void * data )
{
clk_disable_unprepare ( data ) ;
}
2014-10-16 23:47:58 +04:00
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 gpio_chip * gc ;
2019-08-09 17:19:16 +03:00
struct gpio_irq_chip * girq ;
2019-03-11 09:27:31 +03:00
struct irq_chip * ic ;
2019-01-28 00:58:00 +03:00
int i ;
2014-10-16 23:47:58 +04:00
int ret ;
2019-03-11 09:27:37 +03:00
port = devm_kzalloc ( dev , sizeof ( * port ) , GFP_KERNEL ) ;
2014-10-16 23:47:58 +04:00
if ( ! port )
return - ENOMEM ;
2018-04-30 10:38:18 +03:00
port - > sdata = of_device_get_match_data ( dev ) ;
2019-03-11 21:55:16 +03:00
port - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-10-16 23:47:58 +04:00
if ( IS_ERR ( port - > base ) )
return PTR_ERR ( port - > base ) ;
2019-03-11 21:55:16 +03:00
port - > gpio_base = devm_platform_ioremap_resource ( pdev , 1 ) ;
2014-10-16 23:47:58 +04:00
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 ;
2019-03-11 09:27:37 +03:00
port - > clk_port = devm_clk_get ( dev , " port " ) ;
2019-04-29 08:49:48 +03:00
ret = PTR_ERR_OR_ZERO ( port - > clk_port ) ;
if ( ! ret ) {
2018-11-10 17:21:18 +03:00
ret = clk_prepare_enable ( port - > clk_port ) ;
if ( ret )
return ret ;
2019-03-11 09:27:34 +03:00
ret = devm_add_action_or_reset ( dev , vf610_gpio_disable_clk ,
port - > clk_port ) ;
if ( ret )
return ret ;
2019-04-29 08:49:48 +03:00
} else if ( ret = = - EPROBE_DEFER ) {
2018-11-10 17:21:18 +03:00
/*
* Percolate deferrals , for anything else ,
* just live without the clocking .
*/
2019-04-29 08:49:48 +03:00
return ret ;
2018-11-10 17:21:18 +03:00
}
2019-03-11 09:27:37 +03:00
port - > clk_gpio = devm_clk_get ( dev , " gpio " ) ;
2019-04-29 08:49:48 +03:00
ret = PTR_ERR_OR_ZERO ( port - > clk_gpio ) ;
if ( ! ret ) {
2018-11-10 17:21:18 +03:00
ret = clk_prepare_enable ( port - > clk_gpio ) ;
2019-03-11 09:27:34 +03:00
if ( ret )
2018-11-10 17:21:18 +03:00
return ret ;
2019-03-11 09:27:35 +03:00
ret = devm_add_action_or_reset ( dev , vf610_gpio_disable_clk ,
port - > clk_gpio ) ;
if ( ret )
2018-11-10 17:21:18 +03:00
return ret ;
2019-04-29 08:49:48 +03:00
} else if ( ret = = - EPROBE_DEFER ) {
return ret ;
2018-11-10 17:21:18 +03:00
}
2014-10-16 23:47:58 +04:00
gc = & port - > gc ;
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
2019-03-11 09:27:31 +03:00
ic = & port - > ic ;
ic - > name = " gpio-vf610 " ;
ic - > irq_ack = vf610_gpio_irq_ack ;
ic - > irq_mask = vf610_gpio_irq_mask ;
ic - > irq_unmask = vf610_gpio_irq_unmask ;
ic - > irq_set_type = vf610_gpio_irq_set_type ;
ic - > irq_set_wake = vf610_gpio_irq_set_wake ;
2019-01-28 00:58:00 +03:00
/* Mask all GPIO interrupts */
for ( i = 0 ; i < gc - > ngpio ; i + + )
vf610_gpio_writel ( 0 , port - > base + PORT_PCR ( i ) ) ;
2014-10-16 23:47:58 +04:00
/* Clear the interrupt status register for all GPIO's */
vf610_gpio_writel ( ~ 0 , port - > base + PORT_ISFR ) ;
2019-08-09 17:19:16 +03:00
girq = & gc - > irq ;
girq - > chip = ic ;
girq - > parent_handler = vf610_gpio_irq_handler ;
girq - > num_parents = 1 ;
girq - > parents = devm_kcalloc ( & pdev - > dev , 1 ,
sizeof ( * girq - > parents ) ,
GFP_KERNEL ) ;
if ( ! girq - > parents )
return - ENOMEM ;
girq - > parents [ 0 ] = port - > irq ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_edge_irq ;
2014-10-16 23:47:58 +04:00
2019-08-09 17:19:16 +03:00
return devm_gpiochip_add_data ( dev , gc , port ) ;
2014-10-16 23:47:58 +04:00
}
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 ) ;