2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-02-24 12:53:03 +03:00
/*
* Copyright ( C ) 2013 Altera Corporation
* Based on gpio - mpc8xxx . c
*/
# include <linux/io.h>
2016-09-13 01:16:27 +03:00
# include <linux/module.h>
2018-01-14 00:18:34 +03:00
# include <linux/gpio/driver.h>
# include <linux/of_gpio.h> /* For of_mm_gpio_chip */
2015-02-24 12:53:03 +03:00
# include <linux/platform_device.h>
# define ALTERA_GPIO_MAX_NGPIO 32
# define ALTERA_GPIO_DATA 0x0
# define ALTERA_GPIO_DIR 0x4
# define ALTERA_GPIO_IRQ_MASK 0x8
# define ALTERA_GPIO_EDGE_CAP 0xc
/**
* struct altera_gpio_chip
* @ mmchip : memory mapped chip structure .
* @ gpio_lock : synchronization lock so that new irq / set / get requests
2019-01-24 12:24:53 +03:00
* will be blocked until the current one completes .
2015-02-24 12:53:03 +03:00
* @ interrupt_trigger : specifies the hardware configured IRQ trigger type
2019-01-24 12:24:53 +03:00
* ( rising , falling , both , high )
2015-02-24 12:53:03 +03:00
* @ mapped_irq : kernel mapped irq number .
2020-06-30 16:33:37 +03:00
* @ irq_chip : IRQ chip configuration
2015-02-24 12:53:03 +03:00
*/
struct altera_gpio_chip {
struct of_mm_gpio_chip mmchip ;
2017-03-09 19:21:49 +03:00
raw_spinlock_t gpio_lock ;
2015-02-24 12:53:03 +03:00
int interrupt_trigger ;
int mapped_irq ;
2019-06-10 12:50:11 +03:00
struct irq_chip irq_chip ;
2015-02-24 12:53:03 +03:00
} ;
static void altera_gpio_irq_unmask ( struct irq_data * d )
{
struct altera_gpio_chip * altera_gc ;
struct of_mm_gpio_chip * mm_gc ;
unsigned long flags ;
u32 intmask ;
2015-12-04 17:16:43 +03:00
altera_gc = gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-02-24 12:53:03 +03:00
mm_gc = & altera_gc - > mmchip ;
2017-03-09 19:21:49 +03:00
raw_spin_lock_irqsave ( & altera_gc - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
intmask = readl ( mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ;
/* Set ALTERA_GPIO_IRQ_MASK bit to unmask */
intmask | = BIT ( irqd_to_hwirq ( d ) ) ;
writel ( intmask , mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ;
2017-03-09 19:21:49 +03:00
raw_spin_unlock_irqrestore ( & altera_gc - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
}
static void altera_gpio_irq_mask ( struct irq_data * d )
{
struct altera_gpio_chip * altera_gc ;
struct of_mm_gpio_chip * mm_gc ;
unsigned long flags ;
u32 intmask ;
2015-12-04 17:16:43 +03:00
altera_gc = gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-02-24 12:53:03 +03:00
mm_gc = & altera_gc - > mmchip ;
2017-03-09 19:21:49 +03:00
raw_spin_lock_irqsave ( & altera_gc - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
intmask = readl ( mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ;
/* Clear ALTERA_GPIO_IRQ_MASK bit to mask */
intmask & = ~ BIT ( irqd_to_hwirq ( d ) ) ;
writel ( intmask , mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ;
2017-03-09 19:21:49 +03:00
raw_spin_unlock_irqrestore ( & altera_gc - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
}
2020-06-30 16:33:37 +03:00
/*
2015-02-24 12:53:03 +03:00
* This controller ' s IRQ type is synthesized in hardware , so this function
* just checks if the requested set_type matches the synthesized IRQ type
*/
static int altera_gpio_irq_set_type ( struct irq_data * d ,
unsigned int type )
{
struct altera_gpio_chip * altera_gc ;
2015-12-04 17:16:43 +03:00
altera_gc = gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-02-24 12:53:03 +03:00
2017-02-20 04:41:45 +03:00
if ( type = = IRQ_TYPE_NONE ) {
irq_set_handler_locked ( d , handle_bad_irq ) ;
2015-02-24 12:53:03 +03:00
return 0 ;
2017-02-20 04:41:45 +03:00
}
if ( type = = altera_gc - > interrupt_trigger ) {
if ( type = = IRQ_TYPE_LEVEL_HIGH )
irq_set_handler_locked ( d , handle_level_irq ) ;
else
irq_set_handler_locked ( d , handle_simple_irq ) ;
2015-02-24 12:53:03 +03:00
return 0 ;
2017-02-20 04:41:45 +03:00
}
irq_set_handler_locked ( d , handle_bad_irq ) ;
2015-02-24 12:53:03 +03:00
return - EINVAL ;
}
2015-06-10 16:26:27 +03:00
static unsigned int altera_gpio_irq_startup ( struct irq_data * d )
{
2015-02-24 12:53:03 +03:00
altera_gpio_irq_unmask ( d ) ;
return 0 ;
}
static int altera_gpio_get ( struct gpio_chip * gc , unsigned offset )
{
struct of_mm_gpio_chip * mm_gc ;
mm_gc = to_of_mm_gpio_chip ( gc ) ;
return ! ! ( readl ( mm_gc - > regs + ALTERA_GPIO_DATA ) & BIT ( offset ) ) ;
}
static void altera_gpio_set ( struct gpio_chip * gc , unsigned offset , int value )
{
struct of_mm_gpio_chip * mm_gc ;
struct altera_gpio_chip * chip ;
unsigned long flags ;
unsigned int data_reg ;
mm_gc = to_of_mm_gpio_chip ( gc ) ;
2015-12-04 17:16:43 +03:00
chip = gpiochip_get_data ( gc ) ;
2015-02-24 12:53:03 +03:00
2017-03-09 19:21:49 +03:00
raw_spin_lock_irqsave ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
data_reg = readl ( mm_gc - > regs + ALTERA_GPIO_DATA ) ;
if ( value )
data_reg | = BIT ( offset ) ;
else
data_reg & = ~ BIT ( offset ) ;
writel ( data_reg , mm_gc - > regs + ALTERA_GPIO_DATA ) ;
2017-03-09 19:21:49 +03:00
raw_spin_unlock_irqrestore ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
}
static int altera_gpio_direction_input ( struct gpio_chip * gc , unsigned offset )
{
struct of_mm_gpio_chip * mm_gc ;
struct altera_gpio_chip * chip ;
unsigned long flags ;
unsigned int gpio_ddr ;
mm_gc = to_of_mm_gpio_chip ( gc ) ;
2015-12-04 17:16:43 +03:00
chip = gpiochip_get_data ( gc ) ;
2015-02-24 12:53:03 +03:00
2017-03-09 19:21:49 +03:00
raw_spin_lock_irqsave ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
/* Set pin as input, assumes software controlled IP */
gpio_ddr = readl ( mm_gc - > regs + ALTERA_GPIO_DIR ) ;
gpio_ddr & = ~ BIT ( offset ) ;
writel ( gpio_ddr , mm_gc - > regs + ALTERA_GPIO_DIR ) ;
2017-03-09 19:21:49 +03:00
raw_spin_unlock_irqrestore ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
return 0 ;
}
static int altera_gpio_direction_output ( struct gpio_chip * gc ,
unsigned offset , int value )
{
struct of_mm_gpio_chip * mm_gc ;
struct altera_gpio_chip * chip ;
unsigned long flags ;
unsigned int data_reg , gpio_ddr ;
mm_gc = to_of_mm_gpio_chip ( gc ) ;
2015-12-04 17:16:43 +03:00
chip = gpiochip_get_data ( gc ) ;
2015-02-24 12:53:03 +03:00
2017-03-09 19:21:49 +03:00
raw_spin_lock_irqsave ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
/* Sets the GPIO value */
data_reg = readl ( mm_gc - > regs + ALTERA_GPIO_DATA ) ;
if ( value )
data_reg | = BIT ( offset ) ;
else
data_reg & = ~ BIT ( offset ) ;
writel ( data_reg , mm_gc - > regs + ALTERA_GPIO_DATA ) ;
/* Set pin as output, assumes software controlled IP */
gpio_ddr = readl ( mm_gc - > regs + ALTERA_GPIO_DIR ) ;
gpio_ddr | = BIT ( offset ) ;
writel ( gpio_ddr , mm_gc - > regs + ALTERA_GPIO_DIR ) ;
2017-03-09 19:21:49 +03:00
raw_spin_unlock_irqrestore ( & chip - > gpio_lock , flags ) ;
2015-02-24 12:53:03 +03:00
return 0 ;
}
2015-09-14 11:42:37 +03:00
static void altera_gpio_irq_edge_handler ( struct irq_desc * desc )
2015-02-24 12:53:03 +03:00
{
struct altera_gpio_chip * altera_gc ;
struct irq_chip * chip ;
struct of_mm_gpio_chip * mm_gc ;
struct irq_domain * irqdomain ;
unsigned long status ;
int i ;
2015-12-04 17:16:43 +03:00
altera_gc = gpiochip_get_data ( irq_desc_get_handler_data ( desc ) ) ;
2015-02-24 12:53:03 +03:00
chip = irq_desc_get_chip ( desc ) ;
mm_gc = & altera_gc - > mmchip ;
2017-11-07 21:15:47 +03:00
irqdomain = altera_gc - > mmchip . gc . irq . domain ;
2015-02-24 12:53:03 +03:00
chained_irq_enter ( chip , desc ) ;
while ( ( status =
( readl ( mm_gc - > regs + ALTERA_GPIO_EDGE_CAP ) &
readl ( mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ) ) ) {
writel ( status , mm_gc - > regs + ALTERA_GPIO_EDGE_CAP ) ;
for_each_set_bit ( i , & status , mm_gc - > gc . ngpio ) {
generic_handle_irq ( irq_find_mapping ( irqdomain , i ) ) ;
}
}
chained_irq_exit ( chip , desc ) ;
}
2015-09-14 11:42:37 +03:00
static void altera_gpio_irq_leveL_high_handler ( struct irq_desc * desc )
2015-02-24 12:53:03 +03:00
{
struct altera_gpio_chip * altera_gc ;
struct irq_chip * chip ;
struct of_mm_gpio_chip * mm_gc ;
struct irq_domain * irqdomain ;
unsigned long status ;
int i ;
2015-12-04 17:16:43 +03:00
altera_gc = gpiochip_get_data ( irq_desc_get_handler_data ( desc ) ) ;
2015-02-24 12:53:03 +03:00
chip = irq_desc_get_chip ( desc ) ;
mm_gc = & altera_gc - > mmchip ;
2017-11-07 21:15:47 +03:00
irqdomain = altera_gc - > mmchip . gc . irq . domain ;
2015-02-24 12:53:03 +03:00
chained_irq_enter ( chip , desc ) ;
status = readl ( mm_gc - > regs + ALTERA_GPIO_DATA ) ;
status & = readl ( mm_gc - > regs + ALTERA_GPIO_IRQ_MASK ) ;
for_each_set_bit ( i , & status , mm_gc - > gc . ngpio ) {
generic_handle_irq ( irq_find_mapping ( irqdomain , i ) ) ;
}
chained_irq_exit ( chip , desc ) ;
}
2015-03-19 12:40:02 +03:00
static int altera_gpio_probe ( struct platform_device * pdev )
2015-02-24 12:53:03 +03:00
{
struct device_node * node = pdev - > dev . of_node ;
int reg , ret ;
struct altera_gpio_chip * altera_gc ;
2019-06-25 14:35:32 +03:00
struct gpio_irq_chip * girq ;
2015-02-24 12:53:03 +03:00
altera_gc = devm_kzalloc ( & pdev - > dev , sizeof ( * altera_gc ) , GFP_KERNEL ) ;
if ( ! altera_gc )
return - ENOMEM ;
2017-03-09 19:21:49 +03:00
raw_spin_lock_init ( & altera_gc - > gpio_lock ) ;
2015-02-24 12:53:03 +03:00
if ( of_property_read_u32 ( node , " altr,ngpio " , & reg ) )
/* By default assume maximum ngpio */
altera_gc - > mmchip . gc . ngpio = ALTERA_GPIO_MAX_NGPIO ;
else
altera_gc - > mmchip . gc . ngpio = reg ;
if ( altera_gc - > mmchip . gc . ngpio > ALTERA_GPIO_MAX_NGPIO ) {
dev_warn ( & pdev - > dev ,
" ngpio is greater than %d, defaulting to %d \n " ,
ALTERA_GPIO_MAX_NGPIO , ALTERA_GPIO_MAX_NGPIO ) ;
altera_gc - > mmchip . gc . ngpio = ALTERA_GPIO_MAX_NGPIO ;
}
altera_gc - > mmchip . gc . direction_input = altera_gpio_direction_input ;
altera_gc - > mmchip . gc . direction_output = altera_gpio_direction_output ;
altera_gc - > mmchip . gc . get = altera_gpio_get ;
altera_gc - > mmchip . gc . set = altera_gpio_set ;
altera_gc - > mmchip . gc . owner = THIS_MODULE ;
2015-11-04 11:56:26 +03:00
altera_gc - > mmchip . gc . parent = & pdev - > dev ;
2015-02-24 12:53:03 +03:00
2020-01-08 19:16:20 +03:00
altera_gc - > mapped_irq = platform_get_irq_optional ( pdev , 0 ) ;
2015-02-24 12:53:03 +03:00
if ( altera_gc - > mapped_irq < 0 )
goto skip_irq ;
if ( of_property_read_u32 ( node , " altr,interrupt-type " , & reg ) ) {
dev_err ( & pdev - > dev ,
" altr,interrupt-type value not set in device tree \n " ) ;
2019-06-25 14:35:32 +03:00
return - EINVAL ;
2015-02-24 12:53:03 +03:00
}
altera_gc - > interrupt_trigger = reg ;
2019-06-10 12:50:11 +03:00
altera_gc - > irq_chip . name = " altera-gpio " ;
altera_gc - > irq_chip . irq_mask = altera_gpio_irq_mask ;
altera_gc - > irq_chip . irq_unmask = altera_gpio_irq_unmask ;
altera_gc - > irq_chip . irq_set_type = altera_gpio_irq_set_type ;
altera_gc - > irq_chip . irq_startup = altera_gpio_irq_startup ;
altera_gc - > irq_chip . irq_shutdown = altera_gpio_irq_mask ;
2019-06-25 14:35:32 +03:00
girq = & altera_gc - > mmchip . gc . irq ;
girq - > chip = & altera_gc - > irq_chip ;
if ( altera_gc - > interrupt_trigger = = IRQ_TYPE_LEVEL_HIGH )
girq - > parent_handler = altera_gpio_irq_leveL_high_handler ;
else
girq - > parent_handler = altera_gpio_irq_edge_handler ;
girq - > num_parents = 1 ;
girq - > parents = devm_kcalloc ( & pdev - > dev , 1 , sizeof ( * girq - > parents ) ,
GFP_KERNEL ) ;
if ( ! girq - > parents )
return - ENOMEM ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_bad_irq ;
girq - > parents [ 0 ] = altera_gc - > mapped_irq ;
2015-02-24 12:53:03 +03:00
2019-06-25 14:35:32 +03:00
skip_irq :
ret = of_mm_gpiochip_add_data ( node , & altera_gc - > mmchip , altera_gc ) ;
2015-02-24 12:53:03 +03:00
if ( ret ) {
2019-06-25 14:35:32 +03:00
dev_err ( & pdev - > dev , " Failed adding memory mapped gpiochip \n " ) ;
return ret ;
2015-02-24 12:53:03 +03:00
}
2019-06-25 14:35:32 +03:00
platform_set_drvdata ( pdev , altera_gc ) ;
2015-02-24 12:53:03 +03:00
return 0 ;
}
static int altera_gpio_remove ( struct platform_device * pdev )
{
struct altera_gpio_chip * altera_gc = platform_get_drvdata ( pdev ) ;
2015-06-17 14:59:42 +03:00
of_mm_gpiochip_remove ( & altera_gc - > mmchip ) ;
2015-02-24 12:53:03 +03:00
2015-06-17 14:59:43 +03:00
return 0 ;
2015-02-24 12:53:03 +03:00
}
static const struct of_device_id altera_gpio_of_match [ ] = {
{ . compatible = " altr,pio-1.0 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , altera_gpio_of_match ) ;
static struct platform_driver altera_gpio_driver = {
. driver = {
. name = " altera_gpio " ,
. of_match_table = of_match_ptr ( altera_gpio_of_match ) ,
} ,
. probe = altera_gpio_probe ,
. remove = altera_gpio_remove ,
} ;
static int __init altera_gpio_init ( void )
{
return platform_driver_register ( & altera_gpio_driver ) ;
}
subsys_initcall ( altera_gpio_init ) ;
static void __exit altera_gpio_exit ( void )
{
platform_driver_unregister ( & altera_gpio_driver ) ;
}
module_exit ( altera_gpio_exit ) ;
MODULE_AUTHOR ( " Tien Hock Loh <thloh@altera.com> " ) ;
MODULE_DESCRIPTION ( " Altera GPIO driver " ) ;
MODULE_LICENSE ( " GPL " ) ;