2019-10-24 23:27:03 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2017 Broadcom
*/
# include <linux/gpio/driver.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# define IPROC_CCA_INT_F_GPIOINT BIT(0)
# define IPROC_CCA_INT_STS 0x20
# define IPROC_CCA_INT_MASK 0x24
# define IPROC_GPIO_CCA_DIN 0x0
# define IPROC_GPIO_CCA_DOUT 0x4
# define IPROC_GPIO_CCA_OUT_EN 0x8
# define IPROC_GPIO_CCA_INT_LEVEL 0x10
# define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14
# define IPROC_GPIO_CCA_INT_EVENT 0x18
# define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C
# define IPROC_GPIO_CCA_INT_EDGE 0x24
struct iproc_gpio_chip {
struct irq_chip irqchip ;
struct gpio_chip gc ;
spinlock_t lock ;
struct device * dev ;
void __iomem * base ;
void __iomem * intr ;
} ;
static inline struct iproc_gpio_chip *
to_iproc_gpio ( struct gpio_chip * gc )
{
return container_of ( gc , struct iproc_gpio_chip , gc ) ;
}
static void iproc_gpio_irq_ack ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct iproc_gpio_chip * chip = to_iproc_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 irq = d - > irq ;
u32 irq_type , event_status = 0 ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
irq_type = irq_get_trigger_type ( irq ) ;
if ( irq_type & IRQ_TYPE_EDGE_BOTH ) {
event_status | = BIT ( pin ) ;
writel_relaxed ( event_status ,
chip - > base + IPROC_GPIO_CCA_INT_EVENT ) ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static void iproc_gpio_irq_unmask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct iproc_gpio_chip * chip = to_iproc_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 irq = d - > irq ;
u32 int_mask , irq_type , event_mask ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
irq_type = irq_get_trigger_type ( irq ) ;
event_mask = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EVENT_MASK ) ;
int_mask = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL_MASK ) ;
if ( irq_type & IRQ_TYPE_EDGE_BOTH ) {
event_mask | = 1 < < pin ;
writel_relaxed ( event_mask ,
chip - > base + IPROC_GPIO_CCA_INT_EVENT_MASK ) ;
} else {
int_mask | = 1 < < pin ;
writel_relaxed ( int_mask ,
chip - > base + IPROC_GPIO_CCA_INT_LEVEL_MASK ) ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static void iproc_gpio_irq_mask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct iproc_gpio_chip * chip = to_iproc_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 irq = d - > irq ;
u32 irq_type , int_mask , event_mask ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
irq_type = irq_get_trigger_type ( irq ) ;
event_mask = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EVENT_MASK ) ;
int_mask = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL_MASK ) ;
if ( irq_type & IRQ_TYPE_EDGE_BOTH ) {
event_mask & = ~ BIT ( pin ) ;
writel_relaxed ( event_mask ,
chip - > base + IPROC_GPIO_CCA_INT_EVENT_MASK ) ;
} else {
int_mask & = ~ BIT ( pin ) ;
writel_relaxed ( int_mask ,
chip - > base + IPROC_GPIO_CCA_INT_LEVEL_MASK ) ;
}
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
}
static int iproc_gpio_irq_set_type ( struct irq_data * d , u32 type )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct iproc_gpio_chip * chip = to_iproc_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 irq = d - > irq ;
u32 event_pol , int_pol ;
int ret = 0 ;
spin_lock_irqsave ( & chip - > lock , flags ) ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
event_pol = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EDGE ) ;
event_pol & = ~ BIT ( pin ) ;
writel_relaxed ( event_pol , chip - > base + IPROC_GPIO_CCA_INT_EDGE ) ;
break ;
case IRQ_TYPE_EDGE_FALLING :
event_pol = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EDGE ) ;
event_pol | = BIT ( pin ) ;
writel_relaxed ( event_pol , chip - > base + IPROC_GPIO_CCA_INT_EDGE ) ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
int_pol = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL ) ;
int_pol & = ~ BIT ( pin ) ;
writel_relaxed ( int_pol , chip - > base + IPROC_GPIO_CCA_INT_LEVEL ) ;
break ;
case IRQ_TYPE_LEVEL_LOW :
int_pol = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL ) ;
int_pol | = BIT ( pin ) ;
writel_relaxed ( int_pol , chip - > base + IPROC_GPIO_CCA_INT_LEVEL ) ;
break ;
default :
/* should not come here */
ret = - EINVAL ;
goto out_unlock ;
}
if ( type & IRQ_TYPE_LEVEL_MASK )
irq_set_handler_locked ( irq_get_irq_data ( irq ) , handle_level_irq ) ;
else if ( type & IRQ_TYPE_EDGE_BOTH )
irq_set_handler_locked ( irq_get_irq_data ( irq ) , handle_edge_irq ) ;
out_unlock :
spin_unlock_irqrestore ( & chip - > lock , flags ) ;
return ret ;
}
static irqreturn_t iproc_gpio_irq_handler ( int irq , void * data )
{
struct gpio_chip * gc = ( struct gpio_chip * ) data ;
struct iproc_gpio_chip * chip = to_iproc_gpio ( gc ) ;
int bit ;
unsigned long int_bits = 0 ;
u32 int_status ;
/* go through the entire GPIOs and handle all interrupts */
int_status = readl_relaxed ( chip - > intr + IPROC_CCA_INT_STS ) ;
if ( int_status & IPROC_CCA_INT_F_GPIOINT ) {
u32 event , level ;
/* Get level and edge interrupts */
event =
readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EVENT_MASK ) ;
event & = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_EVENT ) ;
level = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_DIN ) ;
level ^ = readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL ) ;
level & =
readl_relaxed ( chip - > base + IPROC_GPIO_CCA_INT_LEVEL_MASK ) ;
int_bits = level | event ;
for_each_set_bit ( bit , & int_bits , gc - > ngpio )
generic_handle_irq ( irq_linear_revmap ( gc - > irq . domain , bit ) ) ;
}
return int_bits ? IRQ_HANDLED : IRQ_NONE ;
}
static int iproc_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * dn = pdev - > dev . of_node ;
struct iproc_gpio_chip * chip ;
u32 num_gpios ;
int irq , ret ;
chip = devm_kzalloc ( dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > dev = dev ;
platform_set_drvdata ( pdev , chip ) ;
spin_lock_init ( & chip - > lock ) ;
chip - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( chip - > base ) )
return PTR_ERR ( chip - > base ) ;
ret = bgpio_init ( & chip - > gc , dev , 4 ,
chip - > base + IPROC_GPIO_CCA_DIN ,
chip - > base + IPROC_GPIO_CCA_DOUT ,
NULL ,
chip - > base + IPROC_GPIO_CCA_OUT_EN ,
NULL ,
0 ) ;
if ( ret ) {
dev_err ( dev , " unable to init GPIO chip \n " ) ;
return ret ;
}
chip - > gc . label = dev_name ( dev ) ;
if ( of_property_read_u32 ( dn , " ngpios " , & num_gpios ) )
chip - > gc . ngpio = num_gpios ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq > 0 ) {
struct gpio_irq_chip * girq ;
struct irq_chip * irqc ;
u32 val ;
irqc = & chip - > irqchip ;
irqc - > name = dev_name ( dev ) ;
irqc - > irq_ack = iproc_gpio_irq_ack ;
irqc - > irq_mask = iproc_gpio_irq_mask ;
irqc - > irq_unmask = iproc_gpio_irq_unmask ;
irqc - > irq_set_type = iproc_gpio_irq_set_type ;
chip - > intr = devm_platform_ioremap_resource ( pdev , 1 ) ;
if ( IS_ERR ( chip - > intr ) )
return PTR_ERR ( chip - > intr ) ;
/* Enable GPIO interrupts for CCA GPIO */
val = readl_relaxed ( chip - > intr + IPROC_CCA_INT_MASK ) ;
val | = IPROC_CCA_INT_F_GPIOINT ;
writel_relaxed ( val , chip - > intr + IPROC_CCA_INT_MASK ) ;
/*
* Directly request the irq here instead of passing
2020-01-14 01:08:00 +03:00
* a flow - handler because the irq is shared .
2019-10-24 23:27:03 +03:00
*/
ret = devm_request_irq ( dev , irq , iproc_gpio_irq_handler ,
IRQF_SHARED , chip - > gc . label , & chip - > gc ) ;
if ( ret ) {
dev_err ( dev , " Fail to request IRQ%d: %d \n " , irq , ret ) ;
return ret ;
}
girq = & chip - > gc . irq ;
girq - > chip = irqc ;
/* This will let us handle the parent IRQ in the driver */
girq - > parent_handler = NULL ;
girq - > num_parents = 0 ;
girq - > parents = NULL ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_simple_irq ;
}
ret = devm_gpiochip_add_data ( dev , & chip - > gc , chip ) ;
if ( ret ) {
dev_err ( dev , " unable to add GPIO chip \n " ) ;
return ret ;
}
return 0 ;
}
2019-12-10 22:54:01 +03:00
static int iproc_gpio_remove ( struct platform_device * pdev )
2019-10-24 23:27:03 +03:00
{
struct iproc_gpio_chip * chip ;
chip = platform_get_drvdata ( pdev ) ;
if ( ! chip )
return - ENODEV ;
if ( chip - > intr ) {
u32 val ;
val = readl_relaxed ( chip - > intr + IPROC_CCA_INT_MASK ) ;
val & = ~ IPROC_CCA_INT_F_GPIOINT ;
writel_relaxed ( val , chip - > intr + IPROC_CCA_INT_MASK ) ;
}
return 0 ;
}
2019-11-05 15:49:15 +03:00
static const struct of_device_id bcm_iproc_gpio_of_match [ ] = {
2019-10-24 23:27:03 +03:00
{ . compatible = " brcm,iproc-gpio-cca " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , bcm_iproc_gpio_of_match ) ;
static struct platform_driver bcm_iproc_gpio_driver = {
. driver = {
. name = " iproc-xgs-gpio " ,
. of_match_table = bcm_iproc_gpio_of_match ,
} ,
. probe = iproc_gpio_probe ,
. remove = iproc_gpio_remove ,
} ;
module_platform_driver ( bcm_iproc_gpio_driver ) ;
MODULE_DESCRIPTION ( " XGS IPROC GPIO driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;