2022-06-07 17:11:13 +03:00
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2015-2017 Broadcom
2015-05-29 05:14:05 +03:00
# include <linux/bitops.h>
# include <linux/gpio/driver.h>
2023-07-14 20:44:58 +03:00
# include <linux/of.h>
2015-05-29 05:14:05 +03:00
# 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>
2023-07-14 20:44:58 +03:00
# include <linux/platform_device.h>
2015-05-29 05:14:05 +03:00
2017-10-24 22:54:51 +03:00
enum gio_reg_index {
GIO_REG_ODEN = 0 ,
GIO_REG_DATA ,
GIO_REG_IODIR ,
GIO_REG_EC ,
GIO_REG_EI ,
GIO_REG_MASK ,
GIO_REG_LEVEL ,
GIO_REG_STAT ,
NUMBER_OF_GIO_REGISTERS
} ;
# define GIO_BANK_SIZE (NUMBER_OF_GIO_REGISTERS * sizeof(u32))
# define GIO_BANK_OFF(bank, off) (((bank) * GIO_BANK_SIZE) + (off * sizeof(u32)))
# define GIO_ODEN(bank) GIO_BANK_OFF(bank, GIO_REG_ODEN)
# define GIO_DATA(bank) GIO_BANK_OFF(bank, GIO_REG_DATA)
# define GIO_IODIR(bank) GIO_BANK_OFF(bank, GIO_REG_IODIR)
# define GIO_EC(bank) GIO_BANK_OFF(bank, GIO_REG_EC)
# define GIO_EI(bank) GIO_BANK_OFF(bank, GIO_REG_EI)
# define GIO_MASK(bank) GIO_BANK_OFF(bank, GIO_REG_MASK)
# define GIO_LEVEL(bank) GIO_BANK_OFF(bank, GIO_REG_LEVEL)
# define GIO_STAT(bank) GIO_BANK_OFF(bank, GIO_REG_STAT)
2015-05-29 05:14:05 +03:00
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 ;
2017-10-24 22:54:51 +03:00
u32 wake_active ;
u32 saved_regs [ GIO_REG_STAT ] ; /* Don't save and restore GIO_REG_STAT */
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 ;
2017-10-24 22:54:50 +03:00
struct irq_domain * irq_domain ;
struct irq_chip irq_chip ;
2015-08-01 04:17:43 +03:00
int parent_irq ;
2017-10-24 22:54:50 +03:00
int num_gpios ;
2015-08-01 04:17:43 +03:00
int parent_wake_irq ;
2015-05-29 05:14:05 +03:00
} ;
2017-10-24 22:54:50 +03:00
# define MAX_GPIO_PER_BANK 32
2015-05-29 05:14:05 +03:00
# 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 ;
}
2017-10-24 22:54:47 +03:00
static unsigned long
2017-10-24 22:54:51 +03:00
__brcmstb_gpio_get_active_irqs ( struct brcmstb_gpio_bank * bank )
2017-10-24 22:54:47 +03:00
{
void __iomem * reg_base = bank - > parent_priv - > reg_base ;
2017-10-24 22:54:51 +03:00
return bank - > gc . read_reg ( reg_base + GIO_STAT ( bank - > id ) ) &
bank - > gc . read_reg ( reg_base + GIO_MASK ( bank - > id ) ) ;
}
static unsigned long
brcmstb_gpio_get_active_irqs ( struct brcmstb_gpio_bank * bank )
{
2017-10-24 22:54:47 +03:00
unsigned long status ;
unsigned long flags ;
2022-04-19 04:28:10 +03:00
raw_spin_lock_irqsave ( & bank - > gc . bgpio_lock , flags ) ;
2017-10-24 22:54:51 +03:00
status = __brcmstb_gpio_get_active_irqs ( bank ) ;
2022-04-19 04:28:10 +03:00
raw_spin_unlock_irqrestore ( & bank - > gc . bgpio_lock , flags ) ;
2017-10-24 22:54:47 +03:00
return status ;
}
2017-10-24 22:54:50 +03:00
static int brcmstb_gpio_hwirq_to_offset ( irq_hw_number_t hwirq ,
struct brcmstb_gpio_bank * bank )
{
2024-04-23 21:46:05 +03:00
return hwirq - bank - > gc . offset ;
2017-10-24 22:54:50 +03:00
}
2015-08-01 04:17:43 +03:00
static void brcmstb_gpio_set_imask ( struct brcmstb_gpio_bank * bank ,
2017-10-24 22:54:50 +03:00
unsigned int hwirq , bool enable )
2015-08-01 04:17:43 +03:00
{
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 ;
2017-10-24 22:54:50 +03:00
u32 mask = BIT ( brcmstb_gpio_hwirq_to_offset ( hwirq , bank ) ) ;
2015-08-01 04:17:43 +03:00
u32 imask ;
unsigned long flags ;
2022-04-19 04:28:10 +03:00
raw_spin_lock_irqsave ( & gc - > bgpio_lock , flags ) ;
2015-12-04 16:02:58 +03:00
imask = gc - > read_reg ( priv - > reg_base + GIO_MASK ( bank - > id ) ) ;
2015-08-01 04:17:43 +03:00
if ( enable )
2017-10-24 22:54:50 +03:00
imask | = mask ;
2015-08-01 04:17:43 +03:00
else
2017-10-24 22:54:50 +03:00
imask & = ~ mask ;
2015-12-04 16:02:58 +03:00
gc - > write_reg ( priv - > reg_base + GIO_MASK ( bank - > id ) , imask ) ;
2022-04-19 04:28:10 +03:00
raw_spin_unlock_irqrestore ( & gc - > bgpio_lock , flags ) ;
2015-08-01 04:17:43 +03:00
}
2017-10-24 22:54:50 +03:00
static int brcmstb_gpio_to_irq ( struct gpio_chip * gc , unsigned offset )
{
struct brcmstb_gpio_priv * priv = brcmstb_gpio_gc_to_priv ( gc ) ;
/* gc_offset is relative to this gpio_chip; want real offset */
2024-04-23 21:46:05 +03:00
int hwirq = offset + gc - > offset ;
2017-10-24 22:54:50 +03:00
if ( hwirq > = priv - > num_gpios )
return - ENXIO ;
return irq_create_mapping ( priv - > irq_domain , hwirq ) ;
}
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 ) ;
}
2017-10-24 22:54:48 +03:00
static void brcmstb_gpio_irq_ack ( struct irq_data * d )
{
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
2017-10-24 22:54:50 +03:00
u32 mask = BIT ( brcmstb_gpio_hwirq_to_offset ( d - > hwirq , bank ) ) ;
2017-10-24 22:54:48 +03:00
gc - > write_reg ( priv - > reg_base + GIO_STAT ( bank - > id ) , mask ) ;
}
2015-08-01 04:17:43 +03:00
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 ;
2017-10-24 22:54:50 +03:00
u32 mask = BIT ( brcmstb_gpio_hwirq_to_offset ( d - > hwirq , bank ) ) ;
2015-08-01 04:17:43 +03:00
u32 edge_insensitive , iedge_insensitive ;
u32 edge_config , iedge_config ;
u32 level , ilevel ;
unsigned long flags ;
switch ( type ) {
case IRQ_TYPE_LEVEL_LOW :
2017-10-24 22:54:49 +03:00
level = mask ;
2015-08-01 04:17:43 +03:00
edge_config = 0 ;
edge_insensitive = 0 ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
level = mask ;
2017-10-24 22:54:49 +03:00
edge_config = mask ;
2015-08-01 04:17:43 +03:00
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 ;
}
2022-04-19 04:28:10 +03:00
raw_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 ) ;
2022-04-19 04:28:10 +03:00
raw_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 ;
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 ) ;
2017-10-24 22:54:51 +03:00
struct brcmstb_gpio_bank * bank = gpiochip_get_data ( gc ) ;
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
u32 mask = BIT ( brcmstb_gpio_hwirq_to_offset ( d - > hwirq , bank ) ) ;
/*
* Do not do anything specific for now , suspend / resume callbacks will
* configure the interrupt mask appropriately
*/
if ( enable )
bank - > wake_active | = mask ;
else
bank - > wake_active & = ~ mask ;
2015-08-01 04:17:44 +03:00
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 ;
2017-10-24 22:54:51 +03:00
/* Nothing to do */
2015-08-01 04:17:43 +03:00
return IRQ_HANDLED ;
}
static void brcmstb_gpio_irq_bank_handler ( struct brcmstb_gpio_bank * bank )
{
struct brcmstb_gpio_priv * priv = bank - > parent_priv ;
2017-10-24 22:54:50 +03:00
struct irq_domain * domain = priv - > irq_domain ;
2024-04-23 21:46:05 +03:00
int hwbase = bank - > gc . offset ;
2015-08-01 04:17:43 +03:00
unsigned long status ;
2017-10-24 22:54:47 +03:00
while ( ( status = brcmstb_gpio_get_active_irqs ( bank ) ) ) {
2021-05-04 19:42:18 +03:00
unsigned int offset ;
2015-08-01 04:17:43 +03:00
2017-10-24 22:54:50 +03:00
for_each_set_bit ( offset , & status , 32 ) {
if ( offset > = bank - > width )
2015-08-01 04:17:43 +03:00
dev_warn ( & priv - > pdev - > dev ,
" IRQ for invalid GPIO (bank=%d, offset=%d) \n " ,
2017-10-24 22:54:50 +03:00
bank - > id , offset ) ;
2021-05-04 19:42:18 +03:00
generic_handle_domain_irq ( domain , hwbase + offset ) ;
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
{
2017-10-24 22:54:50 +03:00
struct brcmstb_gpio_priv * priv = irq_desc_get_handler_data ( desc ) ;
2015-08-01 04:17:43 +03:00
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 ) ;
}
2017-10-24 22:54:50 +03:00
static struct brcmstb_gpio_bank * brcmstb_gpio_hwirq_to_bank (
struct brcmstb_gpio_priv * priv , irq_hw_number_t hwirq )
{
struct brcmstb_gpio_bank * bank ;
int i = 0 ;
/* banks are in descending order */
list_for_each_entry_reverse ( bank , & priv - > bank_list , node ) {
i + = bank - > gc . ngpio ;
if ( hwirq < i )
return bank ;
}
return NULL ;
}
/*
* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents , so it won ' t report false recursion .
*/
static struct lock_class_key brcmstb_gpio_irq_lock_class ;
2017-12-02 20:11:04 +03:00
static struct lock_class_key brcmstb_gpio_irq_request_class ;
2017-10-24 22:54:50 +03:00
static int brcmstb_gpio_irq_map ( struct irq_domain * d , unsigned int irq ,
irq_hw_number_t hwirq )
{
struct brcmstb_gpio_priv * priv = d - > host_data ;
struct brcmstb_gpio_bank * bank =
brcmstb_gpio_hwirq_to_bank ( priv , hwirq ) ;
struct platform_device * pdev = priv - > pdev ;
int ret ;
if ( ! bank )
return - EINVAL ;
dev_dbg ( & pdev - > dev , " Mapping irq %d for gpio line %d (bank %d) \n " ,
irq , ( int ) hwirq , bank - > id ) ;
ret = irq_set_chip_data ( irq , & bank - > gc ) ;
if ( ret < 0 )
return ret ;
2017-12-02 20:11:04 +03:00
irq_set_lockdep_class ( irq , & brcmstb_gpio_irq_lock_class ,
2017-12-29 18:29:15 +03:00
& brcmstb_gpio_irq_request_class ) ;
2017-10-24 22:54:50 +03:00
irq_set_chip_and_handler ( irq , & priv - > irq_chip , handle_level_irq ) ;
irq_set_noprobe ( irq ) ;
return 0 ;
}
static void brcmstb_gpio_irq_unmap ( struct irq_domain * d , unsigned int irq )
{
irq_set_chip_and_handler ( irq , NULL , NULL ) ;
irq_set_chip_data ( irq , NULL ) ;
}
static const struct irq_domain_ops brcmstb_gpio_irq_domain_ops = {
. map = brcmstb_gpio_irq_map ,
. unmap = brcmstb_gpio_irq_unmap ,
. xlate = irq_domain_xlate_twocell ,
} ;
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 ;
}
}
2023-09-28 10:06:46 +03:00
static void brcmstb_gpio_remove ( struct platform_device * pdev )
2015-05-29 05:14:05 +03:00
{
struct brcmstb_gpio_priv * priv = platform_get_drvdata ( pdev ) ;
struct brcmstb_gpio_bank * bank ;
2022-06-21 09:35:24 +03:00
int offset , virq ;
2015-06-18 04:00:40 +03:00
2017-10-24 22:54:50 +03:00
if ( priv - > parent_irq > 0 )
irq_set_chained_handler_and_data ( priv - > parent_irq , NULL , NULL ) ;
/* Remove all IRQ mappings and delete the domain */
if ( priv - > irq_domain ) {
for ( offset = 0 ; offset < priv - > num_gpios ; offset + + ) {
virq = irq_find_mapping ( priv - > irq_domain , offset ) ;
irq_dispose_mapping ( virq ) ;
}
irq_domain_remove ( priv - > irq_domain ) ;
}
2015-06-18 04:00:40 +03:00
/*
* 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 ) ;
2015-05-29 05:14:05 +03:00
}
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 ;
2024-04-23 21:46:05 +03:00
offset = gpiospec - > args [ 0 ] - bank - > gc . offset ;
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 ;
}
2017-10-24 22:54:50 +03:00
/* priv->parent_irq and priv->num_gpios must be set before calling */
2015-08-01 04:17:43 +03:00
static int brcmstb_gpio_irq_setup ( struct platform_device * pdev ,
2017-10-24 22:54:50 +03:00
struct brcmstb_gpio_priv * priv )
2015-08-01 04:17:43 +03:00
{
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
2017-10-24 22:54:50 +03:00
priv - > irq_domain =
irq_domain_add_linear ( np , priv - > num_gpios ,
& brcmstb_gpio_irq_domain_ops ,
priv ) ;
if ( ! priv - > irq_domain ) {
dev_err ( dev , " Couldn't allocate IRQ domain \n " ) ;
return - ENXIO ;
}
2015-08-01 04:17:43 +03:00
2017-10-24 22:54:50 +03:00
if ( of_property_read_bool ( np , " wakeup-source " ) ) {
2015-08-01 04:17:43 +03:00
priv - > parent_wake_irq = platform_get_irq ( pdev , 1 ) ;
if ( priv - > parent_wake_irq < 0 ) {
2017-10-24 22:54:46 +03:00
priv - > parent_wake_irq = 0 ;
2015-08-01 04:17:43 +03:00
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
/*
2017-10-24 22:54:51 +03:00
* Set wakeup capability so we can process boot - time
* " wakeups " ( e . g . , from S5 cold boot )
2015-08-01 04:17:44 +03:00
*/
device_set_wakeup_capable ( dev , true ) ;
device_wakeup_enable ( dev ) ;
err = devm_request_irq ( dev , priv - > parent_wake_irq ,
2017-10-24 22:54:46 +03:00
brcmstb_gpio_wake_irq_handler ,
IRQF_SHARED ,
" brcmstb-gpio-wake " , priv ) ;
2015-08-01 04:17:43 +03:00
if ( err < 0 ) {
dev_err ( dev , " Couldn't request wake IRQ " ) ;
2017-10-24 22:54:50 +03:00
goto out_free_domain ;
2015-08-01 04:17:43 +03:00
}
}
}
2017-10-24 22:54:50 +03:00
priv - > irq_chip . name = dev_name ( dev ) ;
priv - > irq_chip . irq_disable = brcmstb_gpio_irq_mask ;
priv - > irq_chip . irq_mask = brcmstb_gpio_irq_mask ;
priv - > irq_chip . irq_unmask = brcmstb_gpio_irq_unmask ;
priv - > irq_chip . irq_ack = brcmstb_gpio_irq_ack ;
priv - > irq_chip . irq_set_type = brcmstb_gpio_irq_set_type ;
2017-10-24 22:54:46 +03:00
if ( priv - > parent_wake_irq )
2017-10-24 22:54:50 +03:00
priv - > irq_chip . irq_set_wake = brcmstb_gpio_irq_set_wake ;
2015-08-01 04:17:43 +03:00
2017-10-24 22:54:50 +03:00
irq_set_chained_handler_and_data ( priv - > parent_irq ,
brcmstb_gpio_irq_handler , priv ) ;
2017-10-24 22:54:51 +03:00
irq_set_status_flags ( priv - > parent_irq , IRQ_DISABLE_UNLAZY ) ;
2015-08-01 04:17:43 +03:00
return 0 ;
2017-10-24 22:54:50 +03:00
out_free_domain :
irq_domain_remove ( priv - > irq_domain ) ;
return err ;
2015-08-01 04:17:43 +03:00
}
2017-10-24 22:54:51 +03:00
static void brcmstb_gpio_bank_save ( struct brcmstb_gpio_priv * priv ,
struct brcmstb_gpio_bank * bank )
{
struct gpio_chip * gc = & bank - > gc ;
unsigned int i ;
for ( i = 0 ; i < GIO_REG_STAT ; i + + )
bank - > saved_regs [ i ] = gc - > read_reg ( priv - > reg_base +
GIO_BANK_OFF ( bank - > id , i ) ) ;
}
static void brcmstb_gpio_quiesce ( struct device * dev , bool save )
{
struct brcmstb_gpio_priv * priv = dev_get_drvdata ( dev ) ;
struct brcmstb_gpio_bank * bank ;
struct gpio_chip * gc ;
u32 imask ;
/* disable non-wake interrupt */
if ( priv - > parent_irq > = 0 )
disable_irq ( priv - > parent_irq ) ;
list_for_each_entry ( bank , & priv - > bank_list , node ) {
gc = & bank - > gc ;
if ( save )
brcmstb_gpio_bank_save ( priv , bank ) ;
/* Unmask GPIOs which have been flagged as wake-up sources */
if ( priv - > parent_wake_irq )
imask = bank - > wake_active ;
else
imask = 0 ;
gc - > write_reg ( priv - > reg_base + GIO_MASK ( bank - > id ) ,
imask ) ;
}
}
static void brcmstb_gpio_shutdown ( struct platform_device * pdev )
{
/* Enable GPIO for S5 cold boot */
brcmstb_gpio_quiesce ( & pdev - > dev , false ) ;
}
# ifdef CONFIG_PM_SLEEP
static void brcmstb_gpio_bank_restore ( struct brcmstb_gpio_priv * priv ,
struct brcmstb_gpio_bank * bank )
{
struct gpio_chip * gc = & bank - > gc ;
unsigned int i ;
for ( i = 0 ; i < GIO_REG_STAT ; i + + )
gc - > write_reg ( priv - > reg_base + GIO_BANK_OFF ( bank - > id , i ) ,
bank - > saved_regs [ i ] ) ;
}
static int brcmstb_gpio_suspend ( struct device * dev )
{
brcmstb_gpio_quiesce ( dev , true ) ;
return 0 ;
}
static int brcmstb_gpio_resume ( struct device * dev )
{
struct brcmstb_gpio_priv * priv = dev_get_drvdata ( dev ) ;
struct brcmstb_gpio_bank * bank ;
bool need_wakeup_event = false ;
list_for_each_entry ( bank , & priv - > bank_list , node ) {
need_wakeup_event | = ! ! __brcmstb_gpio_get_active_irqs ( bank ) ;
brcmstb_gpio_bank_restore ( priv , bank ) ;
}
if ( priv - > parent_wake_irq & & need_wakeup_event )
pm_wakeup_event ( dev , 0 ) ;
/* enable non-wake interrupt */
if ( priv - > parent_irq > = 0 )
enable_irq ( priv - > parent_irq ) ;
return 0 ;
}
# else
# define brcmstb_gpio_suspend NULL
# define brcmstb_gpio_resume NULL
# endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
. suspend_noirq = brcmstb_gpio_suspend ,
. resume_noirq = brcmstb_gpio_resume ,
} ;
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 ;
2024-04-23 21:46:05 +03:00
int num_gpios = 0 ;
2015-05-29 05:14:05 +03:00
int err ;
2016-01-06 21:55:22 +03:00
unsigned long flags = 0 ;
2017-10-24 22:54:51 +03:00
bool need_wakeup_event = false ;
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
2023-04-28 09:11:09 +03:00
reg_base = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res ) ;
2015-05-29 05:14:05 +03:00
if ( IS_ERR ( reg_base ) )
return PTR_ERR ( reg_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 ) ;
2019-07-30 21:15:15 +03:00
if ( priv - > parent_irq < = 0 )
2015-08-01 04:17:43 +03:00
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 ;
2018-08-18 02:47:39 +03:00
/*
* If bank_width is 0 , then there is an empty bank in the
* register block . Special handling for this case .
*/
if ( bank_width = = 0 ) {
dev_dbg ( dev , " Width 0 found: Empty bank @ %d \n " ,
num_banks ) ;
num_banks + + ;
2024-04-23 21:46:05 +03:00
num_gpios + = MAX_GPIO_PER_BANK ;
2018-08-18 02:47:39 +03:00
continue ;
}
2015-05-29 05:14:05 +03:00
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 - > owner = THIS_MODULE ;
2021-12-23 16:15:38 +03:00
gc - > label = devm_kasprintf ( dev , GFP_KERNEL , " %pOF " , np ) ;
2017-09-21 08:14:13 +03:00
if ( ! gc - > label ) {
err = - ENOMEM ;
goto fail ;
}
2015-05-29 05:14:05 +03:00
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 ;
2021-07-28 07:12:53 +03:00
gc - > offset = bank - > id * MAX_GPIO_PER_BANK ;
2024-04-24 21:50:39 +03:00
gc - > request = gpiochip_generic_request ;
gc - > free = gpiochip_generic_free ;
2017-10-24 22:54:50 +03:00
if ( priv - > parent_irq > 0 )
gc - > to_irq = brcmstb_gpio_to_irq ;
2015-05-29 05:14:05 +03:00
2015-08-01 04:17:44 +03:00
/*
* Mask all interrupts by default , since wakeup interrupts may
* be retained from S5 cold boot
*/
2017-10-24 22:54:51 +03:00
need_wakeup_event | = ! ! __brcmstb_gpio_get_active_irqs ( bank ) ;
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 ;
}
2024-04-23 21:46:05 +03:00
num_gpios + = gc - > ngpio ;
2015-08-01 04:17:43 +03:00
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
}
2024-04-23 21:46:05 +03:00
priv - > num_gpios = num_gpios ;
2017-10-24 22:54:50 +03:00
if ( priv - > parent_irq > 0 ) {
err = brcmstb_gpio_irq_setup ( pdev , priv ) ;
if ( err )
goto fail ;
}
2017-10-24 22:54:51 +03:00
if ( priv - > parent_wake_irq & & need_wakeup_event )
pm_wakeup_event ( dev , 0 ) ;
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 ,
2017-10-24 22:54:51 +03:00
. pm = & brcmstb_gpio_pm_ops ,
2015-05-29 05:14:05 +03:00
} ,
. probe = brcmstb_gpio_probe ,
2023-09-28 10:06:46 +03:00
. remove_new = brcmstb_gpio_remove ,
2017-10-24 22:54:51 +03:00
. shutdown = brcmstb_gpio_shutdown ,
2015-05-29 05:14:05 +03:00
} ;
module_platform_driver ( brcmstb_gpio_driver ) ;
MODULE_AUTHOR ( " Gregory Fong " ) ;
MODULE_DESCRIPTION ( " Driver for Broadcom BRCMSTB SoC UPG GPIO " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;