2018-07-05 16:43:10 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2009 - 2011 Gabor Juhos < juhosg @ openwrt . org >
* Copyright ( C ) 2013 John Crispin < blogic @ openwrt . org >
*/
# include <linux/err.h>
# include <linux/gpio/driver.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_irq.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# define MTK_BANK_CNT 3
# define MTK_BANK_WIDTH 32
# define GPIO_BANK_STRIDE 0x04
# define GPIO_REG_CTRL 0x00
# define GPIO_REG_POL 0x10
# define GPIO_REG_DATA 0x20
# define GPIO_REG_DSET 0x30
# define GPIO_REG_DCLR 0x40
# define GPIO_REG_REDGE 0x50
# define GPIO_REG_FEDGE 0x60
# define GPIO_REG_HLVL 0x70
# define GPIO_REG_LLVL 0x80
# define GPIO_REG_STAT 0x90
# define GPIO_REG_EDGE 0xA0
struct mtk_gc {
struct gpio_chip chip ;
spinlock_t lock ;
int bank ;
u32 rising ;
u32 falling ;
u32 hlevel ;
u32 llevel ;
} ;
/**
2018-07-09 14:51:57 +03:00
* struct mtk - state container for
2018-07-05 16:43:10 +03:00
* data of the platform driver . It is 3
* separate gpio - chip each one with its
* own irq_chip .
* @ dev : device instance
2018-07-09 14:51:57 +03:00
* @ base : memory base address
2018-07-05 16:43:10 +03:00
* @ gpio_irq : irq number from the device tree
* @ gc_map : array of the gpio chips
*/
2018-07-09 14:51:57 +03:00
struct mtk {
2018-07-05 16:43:10 +03:00
struct device * dev ;
2018-07-09 14:51:57 +03:00
void __iomem * base ;
2018-07-05 16:43:10 +03:00
int gpio_irq ;
struct mtk_gc gc_map [ MTK_BANK_CNT ] ;
} ;
static inline struct mtk_gc *
to_mediatek_gpio ( struct gpio_chip * chip )
{
return container_of ( chip , struct mtk_gc , chip ) ;
}
static inline void
mtk_gpio_w32 ( struct mtk_gc * rg , u32 offset , u32 val )
{
struct gpio_chip * gc = & rg - > chip ;
2018-07-09 14:51:57 +03:00
struct mtk * mtk = gpiochip_get_data ( gc ) ;
2018-07-05 16:43:10 +03:00
offset = ( rg - > bank * GPIO_BANK_STRIDE ) + offset ;
2018-07-09 14:51:57 +03:00
gc - > write_reg ( mtk - > base + offset , val ) ;
2018-07-05 16:43:10 +03:00
}
static inline u32
mtk_gpio_r32 ( struct mtk_gc * rg , u32 offset )
{
struct gpio_chip * gc = & rg - > chip ;
2018-07-09 14:51:57 +03:00
struct mtk * mtk = gpiochip_get_data ( gc ) ;
2018-07-05 16:43:10 +03:00
offset = ( rg - > bank * GPIO_BANK_STRIDE ) + offset ;
2018-07-09 14:51:57 +03:00
return gc - > read_reg ( mtk - > base + offset ) ;
2018-07-05 16:43:10 +03:00
}
static irqreturn_t
mediatek_gpio_irq_handler ( int irq , void * data )
{
struct gpio_chip * gc = data ;
struct mtk_gc * rg = to_mediatek_gpio ( gc ) ;
irqreturn_t ret = IRQ_NONE ;
unsigned long pending ;
int bit ;
pending = mtk_gpio_r32 ( rg , GPIO_REG_STAT ) ;
for_each_set_bit ( bit , & pending , MTK_BANK_WIDTH ) {
u32 map = irq_find_mapping ( gc - > irq . domain , bit ) ;
generic_handle_irq ( map ) ;
mtk_gpio_w32 ( rg , GPIO_REG_STAT , BIT ( bit ) ) ;
ret | = IRQ_HANDLED ;
}
return ret ;
}
static void
mediatek_gpio_irq_unmask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct mtk_gc * rg = to_mediatek_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 rise , fall , high , low ;
spin_lock_irqsave ( & rg - > lock , flags ) ;
rise = mtk_gpio_r32 ( rg , GPIO_REG_REDGE ) ;
fall = mtk_gpio_r32 ( rg , GPIO_REG_FEDGE ) ;
high = mtk_gpio_r32 ( rg , GPIO_REG_HLVL ) ;
low = mtk_gpio_r32 ( rg , GPIO_REG_LLVL ) ;
mtk_gpio_w32 ( rg , GPIO_REG_REDGE , rise | ( BIT ( pin ) & rg - > rising ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_FEDGE , fall | ( BIT ( pin ) & rg - > falling ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_HLVL , high | ( BIT ( pin ) & rg - > hlevel ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_LLVL , low | ( BIT ( pin ) & rg - > llevel ) ) ;
spin_unlock_irqrestore ( & rg - > lock , flags ) ;
}
static void
mediatek_gpio_irq_mask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct mtk_gc * rg = to_mediatek_gpio ( gc ) ;
int pin = d - > hwirq ;
unsigned long flags ;
u32 rise , fall , high , low ;
spin_lock_irqsave ( & rg - > lock , flags ) ;
rise = mtk_gpio_r32 ( rg , GPIO_REG_REDGE ) ;
fall = mtk_gpio_r32 ( rg , GPIO_REG_FEDGE ) ;
high = mtk_gpio_r32 ( rg , GPIO_REG_HLVL ) ;
low = mtk_gpio_r32 ( rg , GPIO_REG_LLVL ) ;
mtk_gpio_w32 ( rg , GPIO_REG_FEDGE , fall & ~ BIT ( pin ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_REDGE , rise & ~ BIT ( pin ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_HLVL , high & ~ BIT ( pin ) ) ;
mtk_gpio_w32 ( rg , GPIO_REG_LLVL , low & ~ BIT ( pin ) ) ;
spin_unlock_irqrestore ( & rg - > lock , flags ) ;
}
static int
mediatek_gpio_irq_type ( struct irq_data * d , unsigned int type )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct mtk_gc * rg = to_mediatek_gpio ( gc ) ;
int pin = d - > hwirq ;
u32 mask = BIT ( pin ) ;
if ( type = = IRQ_TYPE_PROBE ) {
if ( ( rg - > rising | rg - > falling |
rg - > hlevel | rg - > llevel ) & mask )
return 0 ;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING ;
}
rg - > rising & = ~ mask ;
rg - > falling & = ~ mask ;
rg - > hlevel & = ~ mask ;
rg - > llevel & = ~ mask ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_BOTH :
rg - > rising | = mask ;
rg - > falling | = mask ;
break ;
case IRQ_TYPE_EDGE_RISING :
rg - > rising | = mask ;
break ;
case IRQ_TYPE_EDGE_FALLING :
rg - > falling | = mask ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
rg - > hlevel | = mask ;
break ;
case IRQ_TYPE_LEVEL_LOW :
rg - > llevel | = mask ;
break ;
}
return 0 ;
}
static struct irq_chip mediatek_gpio_irq_chip = {
. irq_unmask = mediatek_gpio_irq_unmask ,
. irq_mask = mediatek_gpio_irq_mask ,
. irq_mask_ack = mediatek_gpio_irq_mask ,
. irq_set_type = mediatek_gpio_irq_type ,
} ;
static int
mediatek_gpio_xlate ( struct gpio_chip * chip ,
const struct of_phandle_args * spec , u32 * flags )
{
int gpio = spec - > args [ 0 ] ;
struct mtk_gc * rg = to_mediatek_gpio ( chip ) ;
if ( rg - > bank ! = gpio / MTK_BANK_WIDTH )
return - EINVAL ;
if ( flags )
* flags = spec - > args [ 1 ] ;
return gpio % MTK_BANK_WIDTH ;
}
static int
2018-07-09 14:51:57 +03:00
mediatek_gpio_bank_probe ( struct device * dev ,
2018-07-05 16:43:10 +03:00
struct device_node * node , int bank )
{
2018-07-09 14:51:57 +03:00
struct mtk * mtk = dev_get_drvdata ( dev ) ;
2018-07-05 16:43:10 +03:00
struct mtk_gc * rg ;
void __iomem * dat , * set , * ctrl , * diro ;
int ret ;
2018-07-09 14:51:57 +03:00
rg = & mtk - > gc_map [ bank ] ;
2018-07-05 16:43:10 +03:00
memset ( rg , 0 , sizeof ( * rg ) ) ;
spin_lock_init ( & rg - > lock ) ;
rg - > chip . of_node = node ;
rg - > bank = bank ;
2018-07-09 14:51:57 +03:00
dat = mtk - > base + GPIO_REG_DATA + ( rg - > bank * GPIO_BANK_STRIDE ) ;
set = mtk - > base + GPIO_REG_DSET + ( rg - > bank * GPIO_BANK_STRIDE ) ;
ctrl = mtk - > base + GPIO_REG_DCLR + ( rg - > bank * GPIO_BANK_STRIDE ) ;
diro = mtk - > base + GPIO_REG_CTRL + ( rg - > bank * GPIO_BANK_STRIDE ) ;
2018-07-05 16:43:10 +03:00
2018-07-09 14:51:57 +03:00
ret = bgpio_init ( & rg - > chip , dev , 4 ,
2018-07-05 16:43:10 +03:00
dat , set , ctrl , diro , NULL , 0 ) ;
if ( ret ) {
2018-07-09 14:51:57 +03:00
dev_err ( dev , " bgpio_init() failed \n " ) ;
2018-07-05 16:43:10 +03:00
return ret ;
}
rg - > chip . of_gpio_n_cells = 2 ;
rg - > chip . of_xlate = mediatek_gpio_xlate ;
2018-07-09 14:51:57 +03:00
rg - > chip . label = devm_kasprintf ( dev , GFP_KERNEL , " %s-bank%d " ,
dev_name ( dev ) , bank ) ;
2018-11-21 21:06:12 +03:00
if ( ! rg - > chip . label )
return - ENOMEM ;
2018-07-05 16:43:10 +03:00
2018-07-09 14:51:57 +03:00
ret = devm_gpiochip_add_data ( dev , & rg - > chip , mtk ) ;
2018-07-05 16:43:10 +03:00
if ( ret < 0 ) {
2018-07-09 14:51:57 +03:00
dev_err ( dev , " Could not register gpio %d, ret=%d \n " ,
2018-07-05 16:43:10 +03:00
rg - > chip . ngpio , ret ) ;
return ret ;
}
2018-07-09 14:51:57 +03:00
if ( mtk - > gpio_irq ) {
2018-07-05 16:43:10 +03:00
/*
* Manually request the irq here instead of passing
* a flow - handler to gpiochip_set_chained_irqchip ,
* because the irq is shared .
*/
2018-07-09 14:51:57 +03:00
ret = devm_request_irq ( dev , mtk - > gpio_irq ,
2018-07-05 16:43:10 +03:00
mediatek_gpio_irq_handler , IRQF_SHARED ,
rg - > chip . label , & rg - > chip ) ;
if ( ret ) {
2018-07-09 14:51:57 +03:00
dev_err ( dev , " Error requesting IRQ %d: %d \n " ,
mtk - > gpio_irq , ret ) ;
2018-07-05 16:43:10 +03:00
return ret ;
}
ret = gpiochip_irqchip_add ( & rg - > chip , & mediatek_gpio_irq_chip ,
0 , handle_simple_irq , IRQ_TYPE_NONE ) ;
if ( ret ) {
2018-07-09 14:51:57 +03:00
dev_err ( dev , " failed to add gpiochip_irqchip \n " ) ;
2018-07-05 16:43:10 +03:00
return ret ;
}
gpiochip_set_chained_irqchip ( & rg - > chip , & mediatek_gpio_irq_chip ,
2018-07-09 14:51:57 +03:00
mtk - > gpio_irq , NULL ) ;
2018-07-05 16:43:10 +03:00
}
/* set polarity to low for all gpios */
mtk_gpio_w32 ( rg , GPIO_REG_POL , 0 ) ;
2018-07-09 14:51:57 +03:00
dev_info ( dev , " registering %d gpios \n " , rg - > chip . ngpio ) ;
2018-07-05 16:43:10 +03:00
return 0 ;
}
static int
mediatek_gpio_probe ( struct platform_device * pdev )
{
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2018-07-09 14:51:57 +03:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct mtk * mtk ;
2018-07-05 16:43:10 +03:00
int i ;
2018-11-27 20:00:18 +03:00
int ret ;
2018-07-05 16:43:10 +03:00
2018-07-09 14:51:57 +03:00
mtk = devm_kzalloc ( dev , sizeof ( * mtk ) , GFP_KERNEL ) ;
if ( ! mtk )
2018-07-05 16:43:10 +03:00
return - ENOMEM ;
2018-07-09 14:51:57 +03:00
mtk - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( mtk - > base ) )
return PTR_ERR ( mtk - > base ) ;
2018-07-05 16:43:10 +03:00
2018-07-09 14:51:57 +03:00
mtk - > gpio_irq = irq_of_parse_and_map ( np , 0 ) ;
mtk - > dev = dev ;
platform_set_drvdata ( pdev , mtk ) ;
mediatek_gpio_irq_chip . name = dev_name ( dev ) ;
2018-07-05 16:43:10 +03:00
2018-11-27 20:00:18 +03:00
for ( i = 0 ; i < MTK_BANK_CNT ; i + + ) {
ret = mediatek_gpio_bank_probe ( dev , np , i ) ;
if ( ret )
return ret ;
}
2018-07-05 16:43:10 +03:00
return 0 ;
}
static const struct of_device_id mediatek_gpio_match [ ] = {
{ . compatible = " mediatek,mt7621-gpio " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mediatek_gpio_match ) ;
static struct platform_driver mediatek_gpio_driver = {
. probe = mediatek_gpio_probe ,
. driver = {
. name = " mt7621_gpio " ,
. of_match_table = mediatek_gpio_match ,
} ,
} ;
builtin_platform_driver ( mediatek_gpio_driver ) ;