2012-11-21 02:24:30 +04:00
/*
* Broadcom specific AMBA
* GPIO driver
*
* Copyright 2011 , Broadcom Corporation
* Copyright 2012 , Hauke Mehrtens < hauke @ hauke - m . de >
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/gpio.h>
2013-12-12 16:46:03 +04:00
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
2012-11-21 02:24:30 +04:00
# include <linux/export.h>
# include <linux/bcma/bcma.h>
# include "bcma_private.h"
static inline struct bcma_drv_cc * bcma_gpio_get_cc ( struct gpio_chip * chip )
{
return container_of ( chip , struct bcma_drv_cc , gpio ) ;
}
static int bcma_gpio_get_value ( struct gpio_chip * chip , unsigned gpio )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
return ! ! bcma_chipco_gpio_in ( cc , 1 < < gpio ) ;
}
static void bcma_gpio_set_value ( struct gpio_chip * chip , unsigned gpio ,
int value )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
bcma_chipco_gpio_out ( cc , 1 < < gpio , value ? 1 < < gpio : 0 ) ;
}
static int bcma_gpio_direction_input ( struct gpio_chip * chip , unsigned gpio )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
bcma_chipco_gpio_outen ( cc , 1 < < gpio , 0 ) ;
return 0 ;
}
static int bcma_gpio_direction_output ( struct gpio_chip * chip , unsigned gpio ,
int value )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
bcma_chipco_gpio_outen ( cc , 1 < < gpio , 1 < < gpio ) ;
bcma_chipco_gpio_out ( cc , 1 < < gpio , value ? 1 < < gpio : 0 ) ;
return 0 ;
}
static int bcma_gpio_request ( struct gpio_chip * chip , unsigned gpio )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
bcma_chipco_gpio_control ( cc , 1 < < gpio , 0 ) ;
/* clear pulldown */
bcma_chipco_gpio_pulldown ( cc , 1 < < gpio , 0 ) ;
/* Set pullup */
bcma_chipco_gpio_pullup ( cc , 1 < < gpio , 1 < < gpio ) ;
return 0 ;
}
static void bcma_gpio_free ( struct gpio_chip * chip , unsigned gpio )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
/* clear pullup */
bcma_chipco_gpio_pullup ( cc , 1 < < gpio , 0 ) ;
}
2013-12-12 16:46:03 +04:00
# if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
2013-01-27 00:39:44 +04:00
static int bcma_gpio_to_irq ( struct gpio_chip * chip , unsigned gpio )
{
struct bcma_drv_cc * cc = bcma_gpio_get_cc ( chip ) ;
if ( cc - > core - > bus - > hosttype = = BCMA_HOSTTYPE_SOC )
2013-12-12 16:46:03 +04:00
return irq_find_mapping ( cc - > irq_domain , gpio ) ;
2013-01-27 00:39:44 +04:00
else
return - EINVAL ;
}
2013-12-12 16:46:03 +04:00
static void bcma_gpio_irq_unmask ( struct irq_data * d )
{
struct bcma_drv_cc * cc = irq_data_get_irq_chip_data ( d ) ;
int gpio = irqd_to_hwirq ( d ) ;
2014-01-02 22:01:08 +04:00
u32 val = bcma_chipco_gpio_in ( cc , BIT ( gpio ) ) ;
2013-12-12 16:46:03 +04:00
2014-01-02 22:01:08 +04:00
bcma_chipco_gpio_polarity ( cc , BIT ( gpio ) , val ) ;
2013-12-12 16:46:03 +04:00
bcma_chipco_gpio_intmask ( cc , BIT ( gpio ) , BIT ( gpio ) ) ;
}
static void bcma_gpio_irq_mask ( struct irq_data * d )
{
struct bcma_drv_cc * cc = irq_data_get_irq_chip_data ( d ) ;
int gpio = irqd_to_hwirq ( d ) ;
bcma_chipco_gpio_intmask ( cc , BIT ( gpio ) , 0 ) ;
}
static struct irq_chip bcma_gpio_irq_chip = {
. name = " BCMA-GPIO " ,
. irq_mask = bcma_gpio_irq_mask ,
. irq_unmask = bcma_gpio_irq_unmask ,
} ;
static irqreturn_t bcma_gpio_irq_handler ( int irq , void * dev_id )
{
struct bcma_drv_cc * cc = dev_id ;
u32 val = bcma_cc_read32 ( cc , BCMA_CC_GPIOIN ) ;
u32 mask = bcma_cc_read32 ( cc , BCMA_CC_GPIOIRQ ) ;
u32 pol = bcma_cc_read32 ( cc , BCMA_CC_GPIOPOL ) ;
2014-01-13 23:05:17 +04:00
unsigned long irqs = ( val ^ pol ) & mask ;
2013-12-12 16:46:03 +04:00
int gpio ;
if ( ! irqs )
return IRQ_NONE ;
2014-01-13 23:05:17 +04:00
for_each_set_bit ( gpio , & irqs , cc - > gpio . ngpio )
2013-12-12 16:46:03 +04:00
generic_handle_irq ( bcma_gpio_to_irq ( & cc - > gpio , gpio ) ) ;
bcma_chipco_gpio_polarity ( cc , irqs , val & irqs ) ;
return IRQ_HANDLED ;
}
static int bcma_gpio_irq_domain_init ( struct bcma_drv_cc * cc )
{
struct gpio_chip * chip = & cc - > gpio ;
int gpio , hwirq , err ;
if ( cc - > core - > bus - > hosttype ! = BCMA_HOSTTYPE_SOC )
return 0 ;
cc - > irq_domain = irq_domain_add_linear ( NULL , chip - > ngpio ,
& irq_domain_simple_ops , cc ) ;
if ( ! cc - > irq_domain ) {
err = - ENODEV ;
goto err_irq_domain ;
}
for ( gpio = 0 ; gpio < chip - > ngpio ; gpio + + ) {
int irq = irq_create_mapping ( cc - > irq_domain , gpio ) ;
irq_set_chip_data ( irq , cc ) ;
irq_set_chip_and_handler ( irq , & bcma_gpio_irq_chip ,
handle_simple_irq ) ;
}
hwirq = bcma_core_irq ( cc - > core ) ;
err = request_irq ( hwirq , bcma_gpio_irq_handler , IRQF_SHARED , " gpio " ,
cc ) ;
if ( err )
goto err_req_irq ;
2014-01-02 22:01:08 +04:00
bcma_chipco_gpio_intmask ( cc , ~ 0 , 0 ) ;
2013-12-12 16:46:03 +04:00
bcma_cc_set32 ( cc , BCMA_CC_IRQMASK , BCMA_CC_IRQ_GPIO ) ;
return 0 ;
err_req_irq :
for ( gpio = 0 ; gpio < chip - > ngpio ; gpio + + ) {
int irq = irq_find_mapping ( cc - > irq_domain , gpio ) ;
irq_dispose_mapping ( irq ) ;
}
irq_domain_remove ( cc - > irq_domain ) ;
err_irq_domain :
return err ;
}
static void bcma_gpio_irq_domain_exit ( struct bcma_drv_cc * cc )
{
struct gpio_chip * chip = & cc - > gpio ;
int gpio ;
if ( cc - > core - > bus - > hosttype ! = BCMA_HOSTTYPE_SOC )
return ;
bcma_cc_mask32 ( cc , BCMA_CC_IRQMASK , ~ BCMA_CC_IRQ_GPIO ) ;
free_irq ( bcma_core_irq ( cc - > core ) , cc ) ;
for ( gpio = 0 ; gpio < chip - > ngpio ; gpio + + ) {
int irq = irq_find_mapping ( cc - > irq_domain , gpio ) ;
irq_dispose_mapping ( irq ) ;
}
irq_domain_remove ( cc - > irq_domain ) ;
}
# else
static int bcma_gpio_irq_domain_init ( struct bcma_drv_cc * cc )
{
return 0 ;
}
static void bcma_gpio_irq_domain_exit ( struct bcma_drv_cc * cc )
{
}
# endif
2012-11-21 02:24:30 +04:00
int bcma_gpio_init ( struct bcma_drv_cc * cc )
{
struct gpio_chip * chip = & cc - > gpio ;
2013-12-12 16:46:03 +04:00
int err ;
2012-11-21 02:24:30 +04:00
chip - > label = " bcma_gpio " ;
chip - > owner = THIS_MODULE ;
chip - > request = bcma_gpio_request ;
chip - > free = bcma_gpio_free ;
chip - > get = bcma_gpio_get_value ;
chip - > set = bcma_gpio_set_value ;
chip - > direction_input = bcma_gpio_direction_input ;
chip - > direction_output = bcma_gpio_direction_output ;
2013-12-12 16:46:03 +04:00
# if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
2013-01-27 00:39:44 +04:00
chip - > to_irq = bcma_gpio_to_irq ;
2013-12-12 16:46:03 +04:00
# endif
2014-03-21 00:09:07 +04:00
switch ( cc - > core - > bus - > chipinfo . id ) {
case BCMA_CHIP_ID_BCM5357 :
2014-06-05 22:20:44 +04:00
case BCMA_CHIP_ID_BCM53572 :
2014-03-21 00:09:07 +04:00
chip - > ngpio = 32 ;
break ;
default :
chip - > ngpio = 16 ;
}
2012-11-21 02:24:30 +04:00
/* There is just one SoC in one device and its GPIO addresses should be
* deterministic to address them more easily . The other buses could get
* a random base number . */
if ( cc - > core - > bus - > hosttype = = BCMA_HOSTTYPE_SOC )
chip - > base = 0 ;
else
chip - > base = - 1 ;
2013-12-12 16:46:03 +04:00
err = bcma_gpio_irq_domain_init ( cc ) ;
if ( err )
return err ;
err = gpiochip_add ( chip ) ;
if ( err ) {
bcma_gpio_irq_domain_exit ( cc ) ;
return err ;
}
return 0 ;
2012-11-21 02:24:30 +04:00
}
2013-02-04 02:25:33 +04:00
int bcma_gpio_unregister ( struct bcma_drv_cc * cc )
{
2013-12-12 16:46:03 +04:00
bcma_gpio_irq_domain_exit ( cc ) ;
2013-02-04 02:25:33 +04:00
return gpiochip_remove ( & cc - > gpio ) ;
}