2015-05-29 05:14:05 +03:00
/*
* Copyright ( C ) 2015 Broadcom Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/bitops.h>
# include <linux/gpio/driver.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/module.h>
2015-08-01 04:17:43 +03:00
# include <linux/irqdomain.h>
# include <linux/irqchip/chained_irq.h>
# include <linux/interrupt.h>
2015-08-01 04:17:44 +03:00
# include <linux/reboot.h>
2015-05-29 05:14:05 +03:00
# define GIO_BANK_SIZE 0x20
# define GIO_ODEN(bank) (((bank) * GIO_BANK_SIZE) + 0x00)
# define GIO_DATA(bank) (((bank) * GIO_BANK_SIZE) + 0x04)
# define GIO_IODIR(bank) (((bank) * GIO_BANK_SIZE) + 0x08)
# define GIO_EC(bank) (((bank) * GIO_BANK_SIZE) + 0x0c)
# define GIO_EI(bank) (((bank) * GIO_BANK_SIZE) + 0x10)
# define GIO_MASK(bank) (((bank) * GIO_BANK_SIZE) + 0x14)
# define GIO_LEVEL(bank) (((bank) * GIO_BANK_SIZE) + 0x18)
# define GIO_STAT(bank) (((bank) * GIO_BANK_SIZE) + 0x1c)
struct brcmstb_gpio_bank {
struct list_head node ;
int id ;
2015-12-04 16:02:58 +03:00
struct gpio_chip gc ;
2015-05-29 05:14:05 +03:00
struct brcmstb_gpio_priv * parent_priv ;
u32 width ;
2015-08-01 04:17:43 +03:00
struct irq_chip irq_chip ;
2015-05-29 05:14:05 +03:00
} ;
struct brcmstb_gpio_priv {
struct list_head bank_list ;
void __iomem * reg_base ;
struct platform_device * pdev ;
2015-08-01 04:17:43 +03:00
int parent_irq ;
2015-05-29 05:14:05 +03:00
int gpio_base ;
2015-08-01 04:17:43 +03:00
bool can_wake ;
int parent_wake_irq ;
2015-08-01 04:17:44 +03:00
struct notifier_block reboot_notifier ;
2015-05-29 05:14:05 +03:00
} ;
# define MAX_GPIO_PER_BANK 32
# define GPIO_BANK(gpio) ((gpio) >> 5)
/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */
# define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1))
static inline struct brcmstb_gpio_priv *
brcmstb_gpio_gc_to_priv ( struct gpio_chip * gc )
{
2015-12-04 16:02:58 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
2015-05-29 05:14:05 +03:00
return bank - > parent_priv ;
}
2015-08-01 04:17:43 +03:00
static void brcmstb_gpio_set_imask ( struct brcmstb_gpio_bank * bank ,
unsigned int offset , bool enable )
{
2015-12-04 16:02:58 +03:00
struct gpio_chip * gc = & bank - > gc ;
2015-08-01 04:17:43 +03:00
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
2015-12-04 16:02:58 +03:00
u32 mask = gc - > pin2mask ( gc , offset ) ;
2015-08-01 04:17:43 +03:00
u32 imask ;
unsigned long flags ;
2015-12-04 16:02:58 +03:00
spin_lock_irqsave ( & gc - > bgpio_lock , flags ) ;
imask = gc - > read_reg ( priv - > reg_base + GIO_MASK ( bank - > id ) ) ;
2015-08-01 04:17:43 +03:00
if ( enable )
imask | = mask ;
else
imask & = ~ mask ;
2015-12-04 16:02:58 +03:00
gc - > write_reg ( priv - > reg_base + GIO_MASK ( bank - > id ) , imask ) ;
spin_unlock_irqrestore ( & gc - > bgpio_lock , flags ) ;
2015-08-01 04:17:43 +03:00
}
/* -------------------- IRQ chip functions -------------------- */
static void brcmstb_gpio_irq_mask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-04 16:02:58 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
2015-08-01 04:17:43 +03:00
brcmstb_gpio_set_imask ( bank , d - > hwirq , false ) ;
}
static void brcmstb_gpio_irq_unmask ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-04 16:02:58 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
2015-08-01 04:17:43 +03:00
brcmstb_gpio_set_imask ( bank , d - > hwirq , true ) ;
}
static int brcmstb_gpio_irq_set_type ( struct irq_data * d , unsigned int type )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
2015-12-04 16:02:58 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
2015-08-01 04:17:43 +03:00
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
u32 mask = BIT ( d - > hwirq ) ;
u32 edge_insensitive , iedge_insensitive ;
u32 edge_config , iedge_config ;
u32 level , ilevel ;
unsigned long flags ;
switch ( type ) {
case IRQ_TYPE_LEVEL_LOW :
level = 0 ;
edge_config = 0 ;
edge_insensitive = 0 ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
level = mask ;
edge_config = 0 ;
edge_insensitive = 0 ;
break ;
case IRQ_TYPE_EDGE_FALLING :
level = 0 ;
edge_config = 0 ;
edge_insensitive = 0 ;
break ;
case IRQ_TYPE_EDGE_RISING :
level = 0 ;
edge_config = mask ;
edge_insensitive = 0 ;
break ;
case IRQ_TYPE_EDGE_BOTH :
level = 0 ;
edge_config = 0 ; /* don't care, but want known value */
edge_insensitive = mask ;
break ;
default :
return - EINVAL ;
}
2015-12-04 16:02:58 +03:00
spin_lock_irqsave ( & bank - > gc . bgpio_lock , flags ) ;
2015-08-01 04:17:43 +03:00
2015-12-04 16:02:58 +03:00
iedge_config = bank - > gc . read_reg ( priv - > reg_base +
2015-08-01 04:17:43 +03:00
GIO_EC ( bank - > id ) ) & ~ mask ;
2015-12-04 16:02:58 +03:00
iedge_insensitive = bank - > gc . read_reg ( priv - > reg_base +
2015-08-01 04:17:43 +03:00
GIO_EI ( bank - > id ) ) & ~ mask ;
2015-12-04 16:02:58 +03:00
ilevel = bank - > gc . read_reg ( priv - > reg_base +
2015-08-01 04:17:43 +03:00
GIO_LEVEL ( bank - > id ) ) & ~ mask ;
2015-12-04 16:02:58 +03:00
bank - > gc . write_reg ( priv - > reg_base + GIO_EC ( bank - > id ) ,
2015-08-01 04:17:43 +03:00
iedge_config | edge_config ) ;
2015-12-04 16:02:58 +03:00
bank - > gc . write_reg ( priv - > reg_base + GIO_EI ( bank - > id ) ,
2015-08-01 04:17:43 +03:00
iedge_insensitive | edge_insensitive ) ;
2015-12-04 16:02:58 +03:00
bank - > gc . write_reg ( priv - > reg_base + GIO_LEVEL ( bank - > id ) ,
2015-08-01 04:17:43 +03:00
ilevel | level ) ;
2015-12-04 16:02:58 +03:00
spin_unlock_irqrestore ( & bank - > gc . bgpio_lock , flags ) ;
2015-08-01 04:17:43 +03:00
return 0 ;
}
2015-08-01 04:17:44 +03:00
static int brcmstb_gpio_priv_set_wake ( struct brcmstb_gpio_priv * priv ,
unsigned int enable )
2015-08-01 04:17:43 +03:00
{
int ret = 0 ;
/*
* Only enable wake IRQ once for however many hwirqs can wake
* since they all use the same wake IRQ . Mask will be set
* up appropriately thanks to IRQCHIP_MASK_ON_SUSPEND flag .
*/
if ( enable )
ret = enable_irq_wake ( priv - > parent_wake_irq ) ;
else
ret = disable_irq_wake ( priv - > parent_wake_irq ) ;
if ( ret )
dev_err ( & priv - > pdev - > dev , " failed to %s wake-up interrupt \n " ,
enable ? " enable " : " disable " ) ;
return ret ;
}
2015-08-01 04:17:44 +03:00
static int brcmstb_gpio_irq_set_wake ( struct irq_data * d , unsigned int enable )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct brcmstb_gpio_priv * priv = brcmstb_gpio_gc_to_priv ( gc ) ;
return brcmstb_gpio_priv_set_wake ( priv , enable ) ;
}
2015-08-01 04:17:43 +03:00
static irqreturn_t brcmstb_gpio_wake_irq_handler ( int irq , void * data )
{
struct brcmstb_gpio_priv * priv = data ;
if ( ! priv | | irq ! = priv - > parent_wake_irq )
return IRQ_NONE ;
pm_wakeup_event ( & priv - > pdev - > dev , 0 ) ;
return IRQ_HANDLED ;
}
static void brcmstb_gpio_irq_bank_handler ( struct brcmstb_gpio_bank * bank )
{
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
2015-12-04 16:02:58 +03:00
struct irq_domain * irq_domain = bank - > gc . irqdomain ;
2015-08-01 04:17:43 +03:00
void __iomem * reg_base = priv - > reg_base ;
unsigned long status ;
unsigned long flags ;
2015-12-04 16:02:58 +03:00
spin_lock_irqsave ( & bank - > gc . bgpio_lock , flags ) ;
while ( ( status = bank - > gc . read_reg ( reg_base + GIO_STAT ( bank - > id ) ) &
bank - > gc . read_reg ( reg_base + GIO_MASK ( bank - > id ) ) ) ) {
2015-08-01 04:17:43 +03:00
int bit ;
for_each_set_bit ( bit , & status , 32 ) {
2015-12-04 16:02:58 +03:00
u32 stat = bank - > gc . read_reg ( reg_base +
2015-08-01 04:17:43 +03:00
GIO_STAT ( bank - > id ) ) ;
if ( bit > = bank - > width )
dev_warn ( & priv - > pdev - > dev ,
" IRQ for invalid GPIO (bank=%d, offset=%d) \n " ,
bank - > id , bit ) ;
2015-12-04 16:02:58 +03:00
bank - > gc . write_reg ( reg_base + GIO_STAT ( bank - > id ) ,
2015-08-01 04:17:43 +03:00
stat | BIT ( bit ) ) ;
generic_handle_irq ( irq_find_mapping ( irq_domain , bit ) ) ;
}
}
2015-12-04 16:02:58 +03:00
spin_unlock_irqrestore ( & bank - > gc . bgpio_lock , flags ) ;
2015-08-01 04:17:43 +03:00
}
/* Each UPG GIO block has one IRQ for all banks */
2015-09-14 11:42:37 +03:00
static void brcmstb_gpio_irq_handler ( struct irq_desc * desc )
2015-08-01 04:17:43 +03:00
{
struct gpio_chip * gc = irq_desc_get_handler_data ( desc ) ;
struct brcmstb_gpio_priv * priv = brcmstb_gpio_gc_to_priv ( gc ) ;
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2016-02-20 04:50:37 +03:00
struct brcmstb_gpio_bank * bank ;
2015-08-01 04:17:43 +03:00
/* Interrupts weren't properly cleared during probe */
BUG_ON ( ! priv | | ! chip ) ;
chained_irq_enter ( chip , desc ) ;
2016-02-20 04:50:37 +03:00
list_for_each_entry ( bank , & priv - > bank_list , node )
2015-08-01 04:17:43 +03:00
brcmstb_gpio_irq_bank_handler ( bank ) ;
chained_irq_exit ( chip , desc ) ;
}
2015-08-01 04:17:44 +03:00
static int brcmstb_gpio_reboot ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct brcmstb_gpio_priv * priv =
container_of ( nb , struct brcmstb_gpio_priv , reboot_notifier ) ;
/* Enable GPIO for S5 cold boot */
if ( action = = SYS_POWER_OFF )
brcmstb_gpio_priv_set_wake ( priv , 1 ) ;
return NOTIFY_DONE ;
}
2015-05-29 05:14:05 +03:00
/* Make sure that the number of banks matches up between properties */
static int brcmstb_gpio_sanity_check_banks ( struct device * dev ,
struct device_node * np , struct resource * res )
{
int res_num_banks = resource_size ( res ) / GIO_BANK_SIZE ;
int num_banks =
of_property_count_u32_elems ( np , " brcm,gpio-bank-widths " ) ;
if ( res_num_banks ! = num_banks ) {
dev_err ( dev , " Mismatch in banks: res had %d, bank-widths had %d \n " ,
res_num_banks , num_banks ) ;
return - EINVAL ;
} else {
return 0 ;
}
}
static int brcmstb_gpio_remove ( struct platform_device * pdev )
{
struct brcmstb_gpio_priv * priv = platform_get_drvdata ( pdev ) ;
struct brcmstb_gpio_bank * bank ;
int ret = 0 ;
2015-06-18 04:00:40 +03:00
if ( ! priv ) {
dev_err ( & pdev - > dev , " called %s without drvdata! \n " , __func__ ) ;
return - EFAULT ;
}
/*
* You can lose return values below , but we report all errors , and it ' s
* more important to actually perform all of the steps .
*/
2016-02-20 04:50:37 +03:00
list_for_each_entry ( bank , & priv - > bank_list , node )
2015-12-04 16:02:58 +03:00
gpiochip_remove ( & bank - > gc ) ;
2016-02-20 04:50:37 +03:00
2015-08-01 04:17:44 +03:00
if ( priv - > reboot_notifier . notifier_call ) {
ret = unregister_reboot_notifier ( & priv - > reboot_notifier ) ;
if ( ret )
dev_err ( & pdev - > dev ,
" failed to unregister reboot notifier \n " ) ;
}
2015-05-29 05:14:05 +03:00
return ret ;
}
static int brcmstb_gpio_of_xlate ( struct gpio_chip * gc ,
const struct of_phandle_args * gpiospec , u32 * flags )
{
struct brcmstb_gpio_priv * priv = brcmstb_gpio_gc_to_priv ( gc ) ;
2015-12-04 16:02:58 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
2015-05-29 05:14:05 +03:00
int offset ;
if ( gc - > of_gpio_n_cells ! = 2 ) {
WARN_ON ( 1 ) ;
return - EINVAL ;
}
if ( WARN_ON ( gpiospec - > args_count < gc - > of_gpio_n_cells ) )
return - EINVAL ;
offset = gpiospec - > args [ 0 ] - ( gc - > base - priv - > gpio_base ) ;
2015-08-01 04:17:43 +03:00
if ( offset > = gc - > ngpio | | offset < 0 )
2015-05-29 05:14:05 +03:00
return - EINVAL ;
if ( unlikely ( offset > = bank - > width ) ) {
dev_warn_ratelimited ( & priv - > pdev - > dev ,
" Received request for invalid GPIO offset %d \n " ,
gpiospec - > args [ 0 ] ) ;
}
if ( flags )
* flags = gpiospec - > args [ 1 ] ;
return offset ;
}
2015-08-01 04:17:43 +03:00
/* Before calling, must have bank->parent_irq set and gpiochip registered */
static int brcmstb_gpio_irq_setup ( struct platform_device * pdev ,
struct brcmstb_gpio_bank * bank )
{
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2017-08-10 01:51:27 +03:00
int err ;
2015-08-01 04:17:43 +03:00
bank - > irq_chip . name = dev_name ( dev ) ;
bank - > irq_chip . irq_mask = brcmstb_gpio_irq_mask ;
bank - > irq_chip . irq_unmask = brcmstb_gpio_irq_unmask ;
bank - > irq_chip . irq_set_type = brcmstb_gpio_irq_set_type ;
/* Ensures that all non-wakeup IRQs are disabled at suspend */
bank - > irq_chip . flags = IRQCHIP_MASK_ON_SUSPEND ;
if ( IS_ENABLED ( CONFIG_PM_SLEEP ) & & ! priv - > can_wake & &
of_property_read_bool ( np , " wakeup-source " ) ) {
priv - > parent_wake_irq = platform_get_irq ( pdev , 1 ) ;
if ( priv - > parent_wake_irq < 0 ) {
dev_warn ( dev ,
" Couldn't get wake IRQ - GPIOs will not be able to wake from sleep " ) ;
} else {
2015-08-01 04:17:44 +03:00
/*
* Set wakeup capability before requesting wakeup
* interrupt , so we can process boot - time " wakeups "
* ( e . g . , from S5 cold boot )
*/
device_set_wakeup_capable ( dev , true ) ;
device_wakeup_enable ( dev ) ;
err = devm_request_irq ( dev , priv - > parent_wake_irq ,
2015-08-01 04:17:43 +03:00
brcmstb_gpio_wake_irq_handler , 0 ,
" brcmstb-gpio-wake " , priv ) ;
if ( err < 0 ) {
dev_err ( dev , " Couldn't request wake IRQ " ) ;
return err ;
}
2015-08-01 04:17:44 +03:00
priv - > reboot_notifier . notifier_call =
brcmstb_gpio_reboot ;
register_reboot_notifier ( & priv - > reboot_notifier ) ;
2015-08-01 04:17:43 +03:00
priv - > can_wake = true ;
}
}
if ( priv - > can_wake )
bank - > irq_chip . irq_set_wake = brcmstb_gpio_irq_set_wake ;
2017-08-10 01:51:27 +03:00
err = gpiochip_irqchip_add ( & bank - > gc , & bank - > irq_chip , 0 ,
handle_simple_irq , IRQ_TYPE_NONE ) ;
if ( err )
return err ;
2015-12-04 16:02:58 +03:00
gpiochip_set_chained_irqchip ( & bank - > gc , & bank - > irq_chip ,
2015-08-01 04:17:43 +03:00
priv - > parent_irq , brcmstb_gpio_irq_handler ) ;
return 0 ;
}
2015-05-29 05:14:05 +03:00
static int brcmstb_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
void __iomem * reg_base ;
struct brcmstb_gpio_priv * priv ;
struct resource * res ;
struct property * prop ;
const __be32 * p ;
u32 bank_width ;
2015-08-01 04:17:43 +03:00
int num_banks = 0 ;
2015-05-29 05:14:05 +03:00
int err ;
static int gpio_base ;
2016-01-06 21:55:22 +03:00
unsigned long flags = 0 ;
2015-05-29 05:14:05 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2015-06-18 04:00:40 +03:00
platform_set_drvdata ( pdev , priv ) ;
INIT_LIST_HEAD ( & priv - > bank_list ) ;
2015-05-29 05:14:05 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
reg_base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( reg_base ) )
return PTR_ERR ( reg_base ) ;
priv - > gpio_base = gpio_base ;
priv - > reg_base = reg_base ;
priv - > pdev = pdev ;
2015-08-01 04:17:43 +03:00
if ( of_property_read_bool ( np , " interrupt-controller " ) ) {
priv - > parent_irq = platform_get_irq ( pdev , 0 ) ;
if ( priv - > parent_irq < = 0 ) {
dev_err ( dev , " Couldn't get IRQ " ) ;
return - ENOENT ;
}
} else {
priv - > parent_irq = - ENOENT ;
}
2015-05-29 05:14:05 +03:00
if ( brcmstb_gpio_sanity_check_banks ( dev , np , res ) )
return - EINVAL ;
2016-01-06 21:55:22 +03:00
/*
* MIPS endianness is configured by boot strap , which also reverses all
* bus endianness ( i . e . , big - endian CPU + big endian bus = = > native
* endian I / O ) .
*
* Other architectures ( e . g . , ARM ) either do not support big endian , or
* else leave I / O in little endian mode .
*/
# if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER ;
# endif
2015-05-29 05:14:05 +03:00
of_property_for_each_u32 ( np , " brcm,gpio-bank-widths " , prop , p ,
bank_width ) {
struct brcmstb_gpio_bank * bank ;
struct gpio_chip * gc ;
bank = devm_kzalloc ( dev , sizeof ( * bank ) , GFP_KERNEL ) ;
if ( ! bank ) {
err = - ENOMEM ;
goto fail ;
}
bank - > parent_priv = priv ;
2015-08-01 04:17:43 +03:00
bank - > id = num_banks ;
2015-05-29 05:14:05 +03:00
if ( bank_width < = 0 | | bank_width > MAX_GPIO_PER_BANK ) {
dev_err ( dev , " Invalid bank width %d \n " , bank_width ) ;
2016-04-10 13:15:15 +03:00
err = - EINVAL ;
2015-05-29 05:14:05 +03:00
goto fail ;
} else {
bank - > width = bank_width ;
}
/*
* Regs are 4 bytes wide , have data reg , no set / clear regs ,
* and direction bits have 0 = output and 1 = input
*/
2015-12-04 16:02:58 +03:00
gc = & bank - > gc ;
err = bgpio_init ( gc , dev , 4 ,
2015-05-29 05:14:05 +03:00
reg_base + GIO_DATA ( bank - > id ) ,
NULL , NULL , NULL ,
2016-01-06 21:55:22 +03:00
reg_base + GIO_IODIR ( bank - > id ) , flags ) ;
2015-05-29 05:14:05 +03:00
if ( err ) {
dev_err ( dev , " bgpio_init() failed \n " ) ;
goto fail ;
}
gc - > of_node = np ;
gc - > owner = THIS_MODULE ;
2017-07-19 00:43:03 +03:00
gc - > label = devm_kasprintf ( dev , GFP_KERNEL , " %pOF " , dev - > of_node ) ;
2015-05-29 05:14:05 +03:00
gc - > base = gpio_base ;
gc - > of_gpio_n_cells = 2 ;
gc - > of_xlate = brcmstb_gpio_of_xlate ;
/* not all ngpio lines are valid, will use bank width later */
gc - > ngpio = MAX_GPIO_PER_BANK ;
2015-08-01 04:17:44 +03:00
/*
* Mask all interrupts by default , since wakeup interrupts may
* be retained from S5 cold boot
*/
2015-12-04 16:02:58 +03:00
gc - > write_reg ( reg_base + GIO_MASK ( bank - > id ) , 0 ) ;
2015-08-01 04:17:44 +03:00
2015-12-04 16:02:58 +03:00
err = gpiochip_add_data ( gc , bank ) ;
2015-05-29 05:14:05 +03:00
if ( err ) {
dev_err ( dev , " Could not add gpiochip for bank %d \n " ,
bank - > id ) ;
goto fail ;
}
gpio_base + = gc - > ngpio ;
2015-08-01 04:17:43 +03:00
if ( priv - > parent_irq > 0 ) {
err = brcmstb_gpio_irq_setup ( pdev , bank ) ;
if ( err )
goto fail ;
}
2015-05-29 05:14:05 +03:00
dev_dbg ( dev , " bank=%d, base=%d, ngpio=%d, width=%d \n " , bank - > id ,
gc - > base , gc - > ngpio , bank - > width ) ;
/* Everything looks good, so add bank to list */
list_add ( & bank - > node , & priv - > bank_list ) ;
2015-08-01 04:17:43 +03:00
num_banks + + ;
2015-05-29 05:14:05 +03:00
}
dev_info ( dev , " Registered %d banks (GPIO(s): %d-%d) \n " ,
2015-08-01 04:17:43 +03:00
num_banks , priv - > gpio_base , gpio_base - 1 ) ;
2015-05-29 05:14:05 +03:00
return 0 ;
fail :
( void ) brcmstb_gpio_remove ( pdev ) ;
return err ;
}
static const struct of_device_id brcmstb_gpio_of_match [ ] = {
{ . compatible = " brcm,brcmstb-gpio " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , brcmstb_gpio_of_match ) ;
static struct platform_driver brcmstb_gpio_driver = {
. driver = {
. name = " brcmstb-gpio " ,
. of_match_table = brcmstb_gpio_of_match ,
} ,
. probe = brcmstb_gpio_probe ,
. remove = brcmstb_gpio_remove ,
} ;
module_platform_driver ( brcmstb_gpio_driver ) ;
MODULE_AUTHOR ( " Gregory Fong " ) ;
MODULE_DESCRIPTION ( " Driver for Broadcom BRCMSTB SoC UPG GPIO " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;