2017-12-26 19:09:42 +01:00
// SPDX-License-Identifier: GPL-2.0+
//
// Exynos specific support for Samsung pinctrl/gpiolib driver with eint support.
//
// Copyright (c) 2012 Samsung Electronics Co., Ltd.
// http://www.samsung.com
// Copyright (c) 2012 Linaro Ltd
// http://www.linaro.org
//
// Author: Thomas Abraham <thomas.ab@samsung.com>
//
// This file contains the Samsung Exynos specific information required by the
// the Samsung pinctrl/gpiolib driver. It also includes the implementation of
// external gpio and wakeup interrupt support.
2012-09-07 06:07:19 +09:00
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
# include <linux/irq.h>
2013-01-18 15:31:37 +00:00
# include <linux/irqchip/chained_irq.h>
2017-05-16 20:56:27 +02:00
# include <linux/of.h>
2012-09-07 06:07:19 +09:00
# include <linux/of_irq.h>
# include <linux/slab.h>
2013-03-18 22:31:50 +01:00
# include <linux/spinlock.h>
2017-01-26 10:29:25 +01:00
# include <linux/regmap.h>
2012-09-07 06:07:19 +09:00
# include <linux/err.h>
2017-01-26 10:29:25 +01:00
# include <linux/soc/samsung/exynos-pmu.h>
2018-07-23 19:52:58 +02:00
# include <linux/soc/samsung/exynos-regs-pmu.h>
2012-09-07 06:07:19 +09:00
2017-06-15 17:06:28 +02:00
# include <dt-bindings/pinctrl/samsung.h>
2012-09-07 06:07:19 +09:00
# include "pinctrl-samsung.h"
# include "pinctrl-exynos.h"
2014-07-02 17:41:01 +02:00
struct exynos_irq_chip {
struct irq_chip chip ;
u32 eint_con ;
u32 eint_mask ;
u32 eint_pend ;
2018-07-23 19:52:58 +02:00
u32 eint_wake_mask_value ;
u32 eint_wake_mask_reg ;
2020-04-04 10:08:49 -07:00
void ( * set_eint_wakeup_mask ) ( struct samsung_pinctrl_drv_data * drvdata ,
struct exynos_irq_chip * irq_chip ) ;
2014-07-02 17:41:01 +02:00
} ;
static inline struct exynos_irq_chip * to_exynos_irq_chip ( struct irq_chip * chip )
{
return container_of ( chip , struct exynos_irq_chip , chip ) ;
}
2013-03-18 22:31:52 +01:00
2014-07-02 17:41:01 +02:00
static void exynos_irq_mask ( struct irq_data * irqd )
2012-09-07 06:07:19 +09:00
{
2014-07-02 17:41:01 +02:00
struct irq_chip * chip = irq_data_get_irq_chip ( irqd ) ;
struct exynos_irq_chip * our_chip = to_exynos_irq_chip ( chip ) ;
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-07-02 17:41:01 +02:00
unsigned long reg_mask = our_chip - > eint_mask + bank - > eint_offset ;
2012-09-07 06:07:19 +09:00
unsigned long mask ;
2013-06-12 10:33:17 -07:00
unsigned long flags ;
spin_lock_irqsave ( & bank - > slock , flags ) ;
2012-09-07 06:07:19 +09:00
2016-11-09 17:40:10 +09:00
mask = readl ( bank - > eint_base + reg_mask ) ;
2013-06-12 10:33:18 -07:00
mask | = 1 < < irqd - > hwirq ;
2016-11-09 17:40:10 +09:00
writel ( mask , bank - > eint_base + reg_mask ) ;
2013-06-12 10:33:17 -07:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 06:07:19 +09:00
}
2014-07-02 17:41:01 +02:00
static void exynos_irq_ack ( struct irq_data * irqd )
2012-09-07 06:07:19 +09:00
{
2014-07-02 17:41:01 +02:00
struct irq_chip * chip = irq_data_get_irq_chip ( irqd ) ;
struct exynos_irq_chip * our_chip = to_exynos_irq_chip ( chip ) ;
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-07-02 17:41:01 +02:00
unsigned long reg_pend = our_chip - > eint_pend + bank - > eint_offset ;
2012-09-07 06:07:19 +09:00
2016-11-09 17:40:10 +09:00
writel ( 1 < < irqd - > hwirq , bank - > eint_base + reg_pend ) ;
2012-09-07 06:07:19 +09:00
}
2014-07-02 17:41:01 +02:00
static void exynos_irq_unmask ( struct irq_data * irqd )
2012-09-07 06:07:19 +09:00
{
2014-07-02 17:41:01 +02:00
struct irq_chip * chip = irq_data_get_irq_chip ( irqd ) ;
struct exynos_irq_chip * our_chip = to_exynos_irq_chip ( chip ) ;
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-07-02 17:41:01 +02:00
unsigned long reg_mask = our_chip - > eint_mask + bank - > eint_offset ;
2012-09-07 06:07:19 +09:00
unsigned long mask ;
2013-06-12 10:33:17 -07:00
unsigned long flags ;
2012-09-07 06:07:19 +09:00
2013-06-17 09:50:43 -07:00
/*
* Ack level interrupts right before unmask
*
* If we don ' t do this we ' ll get a double - interrupt . Level triggered
* interrupts must not fire an interrupt if the level is not
* _currently_ active , even if it was active while the interrupt was
* masked .
*/
if ( irqd_get_trigger_type ( irqd ) & IRQ_TYPE_LEVEL_MASK )
2014-07-02 17:41:01 +02:00
exynos_irq_ack ( irqd ) ;
2013-06-17 09:50:43 -07:00
2013-06-12 10:33:17 -07:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2012-09-07 06:07:19 +09:00
2016-11-09 17:40:10 +09:00
mask = readl ( bank - > eint_base + reg_mask ) ;
2013-06-12 10:33:18 -07:00
mask & = ~ ( 1 < < irqd - > hwirq ) ;
2016-11-09 17:40:10 +09:00
writel ( mask , bank - > eint_base + reg_mask ) ;
2013-06-12 10:33:17 -07:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 06:07:19 +09:00
}
2014-07-02 17:41:01 +02:00
static int exynos_irq_set_type ( struct irq_data * irqd , unsigned int type )
2012-09-07 06:07:19 +09:00
{
2014-07-02 17:41:01 +02:00
struct irq_chip * chip = irq_data_get_irq_chip ( irqd ) ;
struct exynos_irq_chip * our_chip = to_exynos_irq_chip ( chip ) ;
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-08-09 01:48:05 +02:00
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd - > hwirq ;
2012-09-07 06:07:19 +09:00
unsigned int con , trig_type ;
2014-07-02 17:41:01 +02:00
unsigned long reg_con = our_chip - > eint_con + bank - > eint_offset ;
2012-09-07 06:07:19 +09:00
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
trig_type = EXYNOS_EINT_EDGE_RISING ;
break ;
case IRQ_TYPE_EDGE_FALLING :
trig_type = EXYNOS_EINT_EDGE_FALLING ;
break ;
case IRQ_TYPE_EDGE_BOTH :
trig_type = EXYNOS_EINT_EDGE_BOTH ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
trig_type = EXYNOS_EINT_LEVEL_HIGH ;
break ;
case IRQ_TYPE_LEVEL_LOW :
trig_type = EXYNOS_EINT_LEVEL_LOW ;
break ;
default :
pr_err ( " unsupported external interrupt type \n " ) ;
return - EINVAL ;
}
if ( type & IRQ_TYPE_EDGE_BOTH )
2015-06-23 15:52:57 +02:00
irq_set_handler_locked ( irqd , handle_edge_irq ) ;
2012-09-07 06:07:19 +09:00
else
2015-06-23 15:52:57 +02:00
irq_set_handler_locked ( irqd , handle_level_irq ) ;
2012-09-07 06:07:19 +09:00
2016-11-09 17:40:10 +09:00
con = readl ( bank - > eint_base + reg_con ) ;
2012-09-07 06:07:19 +09:00
con & = ~ ( EXYNOS_EINT_CON_MASK < < shift ) ;
con | = trig_type < < shift ;
2016-11-09 17:40:10 +09:00
writel ( con , bank - > eint_base + reg_con ) ;
2012-09-21 07:33:48 +09:00
2014-08-09 01:48:05 +02:00
return 0 ;
}
static int exynos_irq_request_resources ( struct irq_data * irqd )
{
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-09-23 21:05:39 +02:00
const struct samsung_pin_bank_type * bank_type = bank - > type ;
2017-07-18 19:43:28 +02:00
unsigned long reg_con , flags ;
unsigned int shift , mask , con ;
2014-08-09 01:48:05 +02:00
int ret ;
2014-10-23 17:27:07 +09:00
ret = gpiochip_lock_as_irq ( & bank - > gpio_chip , irqd - > hwirq ) ;
2014-08-09 01:48:05 +02:00
if ( ret ) {
2015-11-04 09:56:26 +01:00
dev_err ( bank - > gpio_chip . parent ,
" unable to lock pin %s-%lu IRQ \n " ,
2014-08-09 01:48:05 +02:00
bank - > name , irqd - > hwirq ) ;
return ret ;
}
2013-03-18 22:31:53 +01:00
reg_con = bank - > pctl_offset + bank_type - > reg_offset [ PINCFG_TYPE_FUNC ] ;
2014-08-09 01:48:05 +02:00
shift = irqd - > hwirq * bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ;
2013-03-18 22:31:52 +01:00
mask = ( 1 < < bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ) - 1 ;
2012-09-21 07:33:48 +09:00
2013-03-18 22:31:50 +01:00
spin_lock_irqsave ( & bank - > slock , flags ) ;
2017-06-14 15:18:28 +02:00
con = readl ( bank - > pctl_base + reg_con ) ;
2012-09-21 07:33:48 +09:00
con & = ~ ( mask < < shift ) ;
2017-06-15 17:06:28 +02:00
con | = EXYNOS_PIN_FUNC_EINT < < shift ;
2017-06-14 15:18:28 +02:00
writel ( con , bank - > pctl_base + reg_con ) ;
2012-09-21 07:33:48 +09:00
2013-03-18 22:31:50 +01:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2012-09-07 06:07:19 +09:00
return 0 ;
}
2014-08-09 01:48:05 +02:00
static void exynos_irq_release_resources ( struct irq_data * irqd )
{
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
2014-09-23 21:05:39 +02:00
const struct samsung_pin_bank_type * bank_type = bank - > type ;
2017-07-18 19:43:28 +02:00
unsigned long reg_con , flags ;
unsigned int shift , mask , con ;
2014-08-09 01:48:05 +02:00
reg_con = bank - > pctl_offset + bank_type - > reg_offset [ PINCFG_TYPE_FUNC ] ;
shift = irqd - > hwirq * bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ;
mask = ( 1 < < bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ) - 1 ;
spin_lock_irqsave ( & bank - > slock , flags ) ;
2017-06-14 15:18:28 +02:00
con = readl ( bank - > pctl_base + reg_con ) ;
2014-08-09 01:48:05 +02:00
con & = ~ ( mask < < shift ) ;
2017-06-15 17:06:28 +02:00
con | = EXYNOS_PIN_FUNC_INPUT < < shift ;
2017-06-14 15:18:28 +02:00
writel ( con , bank - > pctl_base + reg_con ) ;
2014-08-09 01:48:05 +02:00
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
2014-10-23 17:27:07 +09:00
gpiochip_unlock_as_irq ( & bank - > gpio_chip , irqd - > hwirq ) ;
2014-08-09 01:48:05 +02:00
}
2012-09-07 06:07:19 +09:00
/*
* irq_chip for gpio interrupts .
*/
2014-07-02 17:41:01 +02:00
static struct exynos_irq_chip exynos_gpio_irq_chip = {
. chip = {
. name = " exynos_gpio_irq_chip " ,
. irq_unmask = exynos_irq_unmask ,
. irq_mask = exynos_irq_mask ,
. irq_ack = exynos_irq_ack ,
. irq_set_type = exynos_irq_set_type ,
2014-08-09 01:48:05 +02:00
. irq_request_resources = exynos_irq_request_resources ,
. irq_release_resources = exynos_irq_release_resources ,
2014-07-02 17:41:01 +02:00
} ,
. eint_con = EXYNOS_GPIO_ECON_OFFSET ,
. eint_mask = EXYNOS_GPIO_EMASK_OFFSET ,
. eint_pend = EXYNOS_GPIO_EPEND_OFFSET ,
2018-07-23 19:52:58 +02:00
/* eint_wake_mask_value not used */
2012-09-07 06:07:19 +09:00
} ;
2014-10-09 19:24:30 +05:30
static int exynos_eint_irq_map ( struct irq_domain * h , unsigned int virq ,
2012-09-07 06:07:19 +09:00
irq_hw_number_t hw )
{
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * b = h - > host_data ;
2012-09-07 06:07:19 +09:00
2012-10-11 10:11:16 +02:00
irq_set_chip_data ( virq , b ) ;
2014-10-09 19:24:29 +05:30
irq_set_chip_and_handler ( virq , & b - > irq_chip - > chip ,
2012-09-07 06:07:19 +09:00
handle_level_irq ) ;
return 0 ;
}
/*
2014-10-09 19:24:30 +05:30
* irq domain callbacks for external gpio and wakeup interrupt controllers .
2012-09-07 06:07:19 +09:00
*/
2014-10-09 19:24:30 +05:30
static const struct irq_domain_ops exynos_eint_irqd_ops = {
. map = exynos_eint_irq_map ,
2012-09-07 06:07:19 +09:00
. xlate = irq_domain_xlate_twocell ,
} ;
static irqreturn_t exynos_eint_gpio_irq ( int irq , void * data )
{
struct samsung_pinctrl_drv_data * d = data ;
2014-09-23 21:05:40 +02:00
struct samsung_pin_bank * bank = d - > pin_banks ;
2012-09-07 06:07:19 +09:00
unsigned int svc , group , pin , virq ;
2016-11-09 17:40:10 +09:00
svc = readl ( bank - > eint_base + EXYNOS_SVC_OFFSET ) ;
2012-09-07 06:07:19 +09:00
group = EXYNOS_SVC_GROUP ( svc ) ;
pin = svc & EXYNOS_SVC_NUM_MASK ;
if ( ! group )
return IRQ_HANDLED ;
bank + = ( group - 1 ) ;
2012-10-11 10:11:16 +02:00
virq = irq_linear_revmap ( bank - > irq_domain , pin ) ;
2012-09-07 06:07:19 +09:00
if ( ! virq )
return IRQ_NONE ;
generic_handle_irq ( virq ) ;
return IRQ_HANDLED ;
}
2013-05-22 16:03:17 +02:00
struct exynos_eint_gpio_save {
u32 eint_con ;
u32 eint_fltcon0 ;
u32 eint_fltcon1 ;
2020-04-25 16:10:46 -07:00
u32 eint_mask ;
2013-05-22 16:03:17 +02:00
} ;
2012-09-07 06:07:19 +09:00
/*
* exynos_eint_gpio_init ( ) - setup handling of external gpio interrupts .
* @ d : driver data of samsung pinctrl driver .
*/
2017-05-16 20:56:27 +02:00
int exynos_eint_gpio_init ( struct samsung_pinctrl_drv_data * d )
2012-09-07 06:07:19 +09:00
{
2012-10-11 10:11:16 +02:00
struct samsung_pin_bank * bank ;
2012-09-07 06:07:19 +09:00
struct device * dev = d - > dev ;
2013-05-22 16:03:17 +02:00
int ret ;
int i ;
2012-09-07 06:07:19 +09:00
if ( ! d - > irq ) {
dev_err ( dev , " irq number not available \n " ) ;
return - EINVAL ;
}
ret = devm_request_irq ( dev , d - > irq , exynos_eint_gpio_irq ,
0 , dev_name ( dev ) , d ) ;
if ( ret ) {
dev_err ( dev , " irq request failed \n " ) ;
return - ENXIO ;
}
2014-09-23 21:05:40 +02:00
bank = d - > pin_banks ;
for ( i = 0 ; i < d - > nr_banks ; + + i , + + bank ) {
2012-10-11 10:11:16 +02:00
if ( bank - > eint_type ! = EINT_TYPE_GPIO )
continue ;
bank - > irq_domain = irq_domain_add_linear ( bank - > of_node ,
2014-10-09 19:24:30 +05:30
bank - > nr_pins , & exynos_eint_irqd_ops , bank ) ;
2012-10-11 10:11:16 +02:00
if ( ! bank - > irq_domain ) {
dev_err ( dev , " gpio irq domain add failed \n " ) ;
2013-05-22 16:03:17 +02:00
ret = - ENXIO ;
goto err_domains ;
}
bank - > soc_priv = devm_kzalloc ( d - > dev ,
sizeof ( struct exynos_eint_gpio_save ) , GFP_KERNEL ) ;
if ( ! bank - > soc_priv ) {
irq_domain_remove ( bank - > irq_domain ) ;
ret = - ENOMEM ;
goto err_domains ;
2012-10-11 10:11:16 +02:00
}
2014-10-09 19:24:29 +05:30
bank - > irq_chip = & exynos_gpio_irq_chip ;
2012-09-07 06:07:19 +09:00
}
return 0 ;
2013-05-22 16:03:17 +02:00
err_domains :
for ( - - i , - - bank ; i > = 0 ; - - i , - - bank ) {
if ( bank - > eint_type ! = EINT_TYPE_GPIO )
continue ;
irq_domain_remove ( bank - > irq_domain ) ;
}
return ret ;
2012-09-07 06:07:19 +09:00
}
2013-05-17 18:24:27 +02:00
static int exynos_wkup_irq_set_wake ( struct irq_data * irqd , unsigned int on )
{
2018-07-23 19:52:58 +02:00
struct irq_chip * chip = irq_data_get_irq_chip ( irqd ) ;
struct exynos_irq_chip * our_chip = to_exynos_irq_chip ( chip ) ;
2013-05-17 18:24:27 +02:00
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
unsigned long bit = 1UL < < ( 2 * bank - > eint_offset + irqd - > hwirq ) ;
pr_info ( " wake %s for irq %d \n " , on ? " enabled " : " disabled " , irqd - > irq ) ;
if ( ! on )
2019-02-05 20:59:09 +01:00
our_chip - > eint_wake_mask_value | = bit ;
2013-05-17 18:24:27 +02:00
else
2019-02-05 20:59:09 +01:00
our_chip - > eint_wake_mask_value & = ~ bit ;
2013-05-17 18:24:27 +02:00
return 0 ;
}
2020-04-04 10:08:49 -07:00
static void
exynos_pinctrl_set_eint_wakeup_mask ( struct samsung_pinctrl_drv_data * drvdata ,
struct exynos_irq_chip * irq_chip )
{
struct regmap * pmu_regs ;
if ( ! drvdata - > retention_ctrl | | ! drvdata - > retention_ctrl - > priv ) {
dev_warn ( drvdata - > dev ,
" No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set. \n " ) ;
return ;
}
pmu_regs = drvdata - > retention_ctrl - > priv ;
dev_info ( drvdata - > dev ,
" Setting external wakeup interrupt mask: 0x%x \n " ,
irq_chip - > eint_wake_mask_value ) ;
regmap_write ( pmu_regs , irq_chip - > eint_wake_mask_reg ,
irq_chip - > eint_wake_mask_value ) ;
}
static void
s5pv210_pinctrl_set_eint_wakeup_mask ( struct samsung_pinctrl_drv_data * drvdata ,
struct exynos_irq_chip * irq_chip )
{
void __iomem * clk_base ;
if ( ! drvdata - > retention_ctrl | | ! drvdata - > retention_ctrl - > priv ) {
dev_warn ( drvdata - > dev ,
" No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set. \n " ) ;
return ;
}
clk_base = ( void __iomem * ) drvdata - > retention_ctrl - > priv ;
__raw_writel ( irq_chip - > eint_wake_mask_value ,
clk_base + irq_chip - > eint_wake_mask_reg ) ;
}
2012-09-07 06:07:19 +09:00
/*
* irq_chip for wakeup interrupts
*/
2018-07-23 19:52:56 +02:00
static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
. chip = {
. name = " s5pv210_wkup_irq_chip " ,
. irq_unmask = exynos_irq_unmask ,
. irq_mask = exynos_irq_mask ,
. irq_ack = exynos_irq_ack ,
. irq_set_type = exynos_irq_set_type ,
. irq_set_wake = exynos_wkup_irq_set_wake ,
. irq_request_resources = exynos_irq_request_resources ,
. irq_release_resources = exynos_irq_release_resources ,
} ,
. eint_con = EXYNOS_WKUP_ECON_OFFSET ,
. eint_mask = EXYNOS_WKUP_EMASK_OFFSET ,
. eint_pend = EXYNOS_WKUP_EPEND_OFFSET ,
2018-07-23 19:52:58 +02:00
. eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED ,
2020-04-04 10:08:49 -07:00
/* Only differences with exynos4210_wkup_irq_chip: */
2018-07-23 19:52:58 +02:00
. eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK ,
2020-04-04 10:08:49 -07:00
. set_eint_wakeup_mask = s5pv210_pinctrl_set_eint_wakeup_mask ,
2018-07-23 19:52:56 +02:00
} ;
2017-05-23 20:41:39 +02:00
static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
2014-07-02 17:41:01 +02:00
. chip = {
2014-10-09 19:24:31 +05:30
. name = " exynos4210_wkup_irq_chip " ,
2014-07-02 17:41:01 +02:00
. irq_unmask = exynos_irq_unmask ,
. irq_mask = exynos_irq_mask ,
. irq_ack = exynos_irq_ack ,
. irq_set_type = exynos_irq_set_type ,
. irq_set_wake = exynos_wkup_irq_set_wake ,
2014-08-09 01:48:05 +02:00
. irq_request_resources = exynos_irq_request_resources ,
. irq_release_resources = exynos_irq_release_resources ,
2014-07-02 17:41:01 +02:00
} ,
. eint_con = EXYNOS_WKUP_ECON_OFFSET ,
. eint_mask = EXYNOS_WKUP_EMASK_OFFSET ,
. eint_pend = EXYNOS_WKUP_EPEND_OFFSET ,
2018-07-23 19:52:58 +02:00
. eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED ,
. eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK ,
2020-04-04 10:08:49 -07:00
. set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask ,
2012-09-07 06:07:19 +09:00
} ;
2017-05-23 20:41:39 +02:00
static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
2014-10-09 19:24:31 +05:30
. chip = {
. name = " exynos7_wkup_irq_chip " ,
. irq_unmask = exynos_irq_unmask ,
. irq_mask = exynos_irq_mask ,
. irq_ack = exynos_irq_ack ,
. irq_set_type = exynos_irq_set_type ,
. irq_set_wake = exynos_wkup_irq_set_wake ,
. irq_request_resources = exynos_irq_request_resources ,
. irq_release_resources = exynos_irq_release_resources ,
} ,
. eint_con = EXYNOS7_WKUP_ECON_OFFSET ,
. eint_mask = EXYNOS7_WKUP_EMASK_OFFSET ,
. eint_pend = EXYNOS7_WKUP_EPEND_OFFSET ,
2018-07-23 19:52:58 +02:00
. eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED ,
. eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK ,
2020-04-04 10:08:49 -07:00
. set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask ,
2014-10-09 19:24:31 +05:30
} ;
/* list of external wakeup controllers supported */
static const struct of_device_id exynos_wkup_irq_ids [ ] = {
2018-07-23 19:52:56 +02:00
{ . compatible = " samsung,s5pv210-wakeup-eint " ,
. data = & s5pv210_wkup_irq_chip } ,
2014-10-09 19:24:31 +05:30
{ . compatible = " samsung,exynos4210-wakeup-eint " ,
. data = & exynos4210_wkup_irq_chip } ,
{ . compatible = " samsung,exynos7-wakeup-eint " ,
. data = & exynos7_wkup_irq_chip } ,
{ }
} ;
2012-09-07 06:07:19 +09:00
/* interrupt handler for wakeup interrupts 0..15 */
2015-09-14 10:42:37 +02:00
static void exynos_irq_eint0_15 ( struct irq_desc * desc )
2012-09-07 06:07:19 +09:00
{
2015-06-04 12:13:16 +08:00
struct exynos_weint_data * eintd = irq_desc_get_handler_data ( desc ) ;
2012-10-11 10:11:18 +02:00
struct samsung_pin_bank * bank = eintd - > bank ;
2015-06-04 12:13:16 +08:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2012-09-07 06:07:19 +09:00
int eint_irq ;
chained_irq_enter ( chip , desc ) ;
2012-10-11 10:11:18 +02:00
eint_irq = irq_linear_revmap ( bank - > irq_domain , eintd - > irq ) ;
2012-09-07 06:07:19 +09:00
generic_handle_irq ( eint_irq ) ;
2016-08-16 18:45:29 +08:00
2012-09-07 06:07:19 +09:00
chained_irq_exit ( chip , desc ) ;
}
2012-10-11 10:11:18 +02:00
static inline void exynos_irq_demux_eint ( unsigned long pend ,
struct irq_domain * domain )
2012-09-07 06:07:19 +09:00
{
unsigned int irq ;
while ( pend ) {
irq = fls ( pend ) - 1 ;
2012-10-11 10:11:18 +02:00
generic_handle_irq ( irq_find_mapping ( domain , irq ) ) ;
2012-09-07 06:07:19 +09:00
pend & = ~ ( 1 < < irq ) ;
}
}
/* interrupt handler for wakeup interrupt 16 */
2015-09-14 10:42:37 +02:00
static void exynos_irq_demux_eint16_31 ( struct irq_desc * desc )
2012-09-07 06:07:19 +09:00
{
2015-06-04 12:13:16 +08:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
struct exynos_muxed_weint_data * eintd = irq_desc_get_handler_data ( desc ) ;
2012-09-07 06:07:19 +09:00
unsigned long pend ;
2012-09-21 07:33:55 +09:00
unsigned long mask ;
2012-10-11 10:11:18 +02:00
int i ;
2012-09-07 06:07:19 +09:00
chained_irq_enter ( chip , desc ) ;
2012-10-11 10:11:18 +02:00
for ( i = 0 ; i < eintd - > nr_banks ; + + i ) {
struct samsung_pin_bank * b = eintd - > banks [ i ] ;
2016-11-09 17:40:10 +09:00
pend = readl ( b - > eint_base + b - > irq_chip - > eint_pend
2014-07-02 17:41:01 +02:00
+ b - > eint_offset ) ;
2016-11-09 17:40:10 +09:00
mask = readl ( b - > eint_base + b - > irq_chip - > eint_mask
2014-07-02 17:41:01 +02:00
+ b - > eint_offset ) ;
2012-10-11 10:11:18 +02:00
exynos_irq_demux_eint ( pend & ~ mask , b - > irq_domain ) ;
}
2012-09-07 06:07:19 +09:00
chained_irq_exit ( chip , desc ) ;
}
/*
* exynos_eint_wkup_init ( ) - setup handling of external wakeup interrupts .
* @ d : driver data of samsung pinctrl driver .
*/
2017-05-16 20:56:27 +02:00
int exynos_eint_wkup_init ( struct samsung_pinctrl_drv_data * d )
2012-09-07 06:07:19 +09:00
{
struct device * dev = d - > dev ;
2012-09-21 07:34:01 +09:00
struct device_node * wkup_np = NULL ;
struct device_node * np ;
2012-10-11 10:11:18 +02:00
struct samsung_pin_bank * bank ;
2012-09-07 06:07:19 +09:00
struct exynos_weint_data * weint_data ;
2012-10-11 10:11:18 +02:00
struct exynos_muxed_weint_data * muxed_data ;
2014-10-09 19:24:31 +05:30
struct exynos_irq_chip * irq_chip ;
2012-10-11 10:11:18 +02:00
unsigned int muxed_banks = 0 ;
unsigned int i ;
2012-09-07 06:07:19 +09:00
int idx , irq ;
2012-09-21 07:34:01 +09:00
for_each_child_of_node ( dev - > of_node , np ) {
2014-10-09 19:24:31 +05:30
const struct of_device_id * match ;
match = of_match_node ( exynos_wkup_irq_ids , np ) ;
if ( match ) {
irq_chip = kmemdup ( match - > data ,
sizeof ( * irq_chip ) , GFP_KERNEL ) ;
2019-08-04 21:32:00 +05:30
if ( ! irq_chip ) {
of_node_put ( np ) ;
2017-05-23 20:41:40 +02:00
return - ENOMEM ;
2019-08-04 21:32:00 +05:30
}
2012-09-21 07:34:01 +09:00
wkup_np = np ;
break ;
}
2012-09-07 06:07:19 +09:00
}
2012-09-21 07:34:01 +09:00
if ( ! wkup_np )
return - ENODEV ;
2012-09-07 06:07:19 +09:00
2014-09-23 21:05:40 +02:00
bank = d - > pin_banks ;
for ( i = 0 ; i < d - > nr_banks ; + + i , + + bank ) {
2012-10-11 10:11:18 +02:00
if ( bank - > eint_type ! = EINT_TYPE_WKUP )
continue ;
2012-09-07 06:07:19 +09:00
2012-10-11 10:11:18 +02:00
bank - > irq_domain = irq_domain_add_linear ( bank - > of_node ,
2014-10-09 19:24:30 +05:30
bank - > nr_pins , & exynos_eint_irqd_ops , bank ) ;
2012-10-11 10:11:18 +02:00
if ( ! bank - > irq_domain ) {
dev_err ( dev , " wkup irq domain add failed \n " ) ;
2019-08-05 18:27:07 +02:00
of_node_put ( wkup_np ) ;
2012-10-11 10:11:18 +02:00
return - ENXIO ;
}
2012-09-07 06:07:19 +09:00
2014-10-09 19:24:31 +05:30
bank - > irq_chip = irq_chip ;
2014-10-09 19:24:29 +05:30
2012-10-11 10:11:18 +02:00
if ( ! of_find_property ( bank - > of_node , " interrupts " , NULL ) ) {
bank - > eint_type = EINT_TYPE_WKUP_MUX ;
+ + muxed_banks ;
continue ;
}
2012-09-07 06:07:19 +09:00
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 14:07:58 -07:00
weint_data = devm_kcalloc ( dev ,
bank - > nr_pins , sizeof ( * weint_data ) ,
GFP_KERNEL ) ;
2019-08-05 18:27:07 +02:00
if ( ! weint_data ) {
of_node_put ( wkup_np ) ;
2012-10-11 10:11:18 +02:00
return - ENOMEM ;
2019-08-05 18:27:07 +02:00
}
2012-09-07 06:07:19 +09:00
2012-10-11 10:11:18 +02:00
for ( idx = 0 ; idx < bank - > nr_pins ; + + idx ) {
irq = irq_of_parse_and_map ( bank - > of_node , idx ) ;
if ( ! irq ) {
dev_err ( dev , " irq number for eint-%s-%d not found \n " ,
bank - > name , idx ) ;
continue ;
}
weint_data [ idx ] . irq = idx ;
weint_data [ idx ] . bank = bank ;
2015-06-21 21:11:07 +02:00
irq_set_chained_handler_and_data ( irq ,
exynos_irq_eint0_15 ,
& weint_data [ idx ] ) ;
2012-09-07 06:07:19 +09:00
}
}
2012-10-11 10:11:18 +02:00
2019-08-05 18:27:07 +02:00
if ( ! muxed_banks ) {
of_node_put ( wkup_np ) ;
2012-10-11 10:11:18 +02:00
return 0 ;
2019-08-05 18:27:07 +02:00
}
2012-10-11 10:11:18 +02:00
irq = irq_of_parse_and_map ( wkup_np , 0 ) ;
2019-08-05 18:27:07 +02:00
of_node_put ( wkup_np ) ;
2012-10-11 10:11:18 +02:00
if ( ! irq ) {
dev_err ( dev , " irq number for muxed EINTs not found \n " ) ;
return 0 ;
}
muxed_data = devm_kzalloc ( dev , sizeof ( * muxed_data )
+ muxed_banks * sizeof ( struct samsung_pin_bank * ) , GFP_KERNEL ) ;
2017-01-19 14:48:45 +01:00
if ( ! muxed_data )
2012-10-11 10:11:18 +02:00
return - ENOMEM ;
pinctrl/exynos: Fix race in installing chained IRQ handler
Fix a race where a pending interrupt could be received and the handler
called before the handler's data has been setup, by converting to
irq_set_chained_handler_and_data().
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
|
-irq_set_chained_handler(E1, E3);
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
...
|
-irq_set_chained_handler(E1, E3);
...
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Tomasz Figa <tomasz.figa@gmail.com>
Cc: Thomas Abraham <thomas.abraham@linaro.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
Cc: linux-gpio@vger.kernel.org
2015-06-21 20:16:16 +02:00
irq_set_chained_handler_and_data ( irq , exynos_irq_demux_eint16_31 ,
muxed_data ) ;
2012-10-11 10:11:18 +02:00
2014-09-23 21:05:40 +02:00
bank = d - > pin_banks ;
2012-10-11 10:11:18 +02:00
idx = 0 ;
2014-09-23 21:05:40 +02:00
for ( i = 0 ; i < d - > nr_banks ; + + i , + + bank ) {
2012-10-11 10:11:18 +02:00
if ( bank - > eint_type ! = EINT_TYPE_WKUP_MUX )
continue ;
muxed_data - > banks [ idx + + ] = bank ;
}
muxed_data - > nr_banks = muxed_banks ;
2012-09-07 06:07:19 +09:00
return 0 ;
}
2013-05-22 16:03:17 +02:00
static void exynos_pinctrl_suspend_bank (
struct samsung_pinctrl_drv_data * drvdata ,
struct samsung_pin_bank * bank )
{
struct exynos_eint_gpio_save * save = bank - > soc_priv ;
2016-11-09 17:40:10 +09:00
void __iomem * regs = bank - > eint_base ;
2013-05-22 16:03:17 +02:00
save - > eint_con = readl ( regs + EXYNOS_GPIO_ECON_OFFSET
+ bank - > eint_offset ) ;
save - > eint_fltcon0 = readl ( regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset ) ;
save - > eint_fltcon1 = readl ( regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset + 4 ) ;
2020-04-25 16:10:46 -07:00
save - > eint_mask = readl ( regs + bank - > irq_chip - > eint_mask
+ bank - > eint_offset ) ;
2013-05-22 16:03:17 +02:00
pr_debug ( " %s: save con %#010x \n " , bank - > name , save - > eint_con ) ;
pr_debug ( " %s: save fltcon0 %#010x \n " , bank - > name , save - > eint_fltcon0 ) ;
pr_debug ( " %s: save fltcon1 %#010x \n " , bank - > name , save - > eint_fltcon1 ) ;
2020-04-25 16:10:46 -07:00
pr_debug ( " %s: save mask %#010x \n " , bank - > name , save - > eint_mask ) ;
2013-05-22 16:03:17 +02:00
}
2017-05-16 20:56:27 +02:00
void exynos_pinctrl_suspend ( struct samsung_pinctrl_drv_data * drvdata )
2013-05-22 16:03:17 +02:00
{
2014-09-23 21:05:40 +02:00
struct samsung_pin_bank * bank = drvdata - > pin_banks ;
2018-07-23 19:52:58 +02:00
struct exynos_irq_chip * irq_chip = NULL ;
2013-05-22 16:03:17 +02:00
int i ;
2018-07-23 19:52:58 +02:00
for ( i = 0 ; i < drvdata - > nr_banks ; + + i , + + bank ) {
2013-05-22 16:03:17 +02:00
if ( bank - > eint_type = = EINT_TYPE_GPIO )
exynos_pinctrl_suspend_bank ( drvdata , bank ) ;
2018-07-23 19:52:58 +02:00
else if ( bank - > eint_type = = EINT_TYPE_WKUP ) {
if ( ! irq_chip ) {
irq_chip = bank - > irq_chip ;
2020-04-04 10:08:49 -07:00
irq_chip - > set_eint_wakeup_mask ( drvdata ,
irq_chip ) ;
2018-07-23 19:52:58 +02:00
} else if ( bank - > irq_chip ! = irq_chip ) {
dev_warn ( drvdata - > dev ,
" More than one external wakeup interrupt chip configured (bank: %s). This is not supported by hardware nor by driver. \n " ,
bank - > name ) ;
}
}
}
2013-05-22 16:03:17 +02:00
}
static void exynos_pinctrl_resume_bank (
struct samsung_pinctrl_drv_data * drvdata ,
struct samsung_pin_bank * bank )
{
struct exynos_eint_gpio_save * save = bank - > soc_priv ;
2016-11-09 17:40:10 +09:00
void __iomem * regs = bank - > eint_base ;
2013-05-22 16:03:17 +02:00
pr_debug ( " %s: con %#010x => %#010x \n " , bank - > name ,
readl ( regs + EXYNOS_GPIO_ECON_OFFSET
+ bank - > eint_offset ) , save - > eint_con ) ;
pr_debug ( " %s: fltcon0 %#010x => %#010x \n " , bank - > name ,
readl ( regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset ) , save - > eint_fltcon0 ) ;
pr_debug ( " %s: fltcon1 %#010x => %#010x \n " , bank - > name ,
readl ( regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset + 4 ) , save - > eint_fltcon1 ) ;
2020-04-25 16:10:46 -07:00
pr_debug ( " %s: mask %#010x => %#010x \n " , bank - > name ,
readl ( regs + bank - > irq_chip - > eint_mask
+ bank - > eint_offset ) , save - > eint_mask ) ;
2013-05-22 16:03:17 +02:00
writel ( save - > eint_con , regs + EXYNOS_GPIO_ECON_OFFSET
+ bank - > eint_offset ) ;
writel ( save - > eint_fltcon0 , regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset ) ;
writel ( save - > eint_fltcon1 , regs + EXYNOS_GPIO_EFLTCON_OFFSET
+ 2 * bank - > eint_offset + 4 ) ;
2020-04-25 16:10:46 -07:00
writel ( save - > eint_mask , regs + bank - > irq_chip - > eint_mask
+ bank - > eint_offset ) ;
2013-05-22 16:03:17 +02:00
}
2017-05-16 20:56:27 +02:00
void exynos_pinctrl_resume ( struct samsung_pinctrl_drv_data * drvdata )
2013-05-22 16:03:17 +02:00
{
2014-09-23 21:05:40 +02:00
struct samsung_pin_bank * bank = drvdata - > pin_banks ;
2013-05-22 16:03:17 +02:00
int i ;
2014-09-23 21:05:40 +02:00
for ( i = 0 ; i < drvdata - > nr_banks ; + + i , + + bank )
2013-05-22 16:03:17 +02:00
if ( bank - > eint_type = = EINT_TYPE_GPIO )
exynos_pinctrl_resume_bank ( drvdata , bank ) ;
}
2017-01-26 10:29:25 +01:00
static void exynos_retention_enable ( struct samsung_pinctrl_drv_data * drvdata )
{
if ( drvdata - > retention_ctrl - > refcnt )
atomic_inc ( drvdata - > retention_ctrl - > refcnt ) ;
}
static void exynos_retention_disable ( struct samsung_pinctrl_drv_data * drvdata )
{
struct samsung_retention_ctrl * ctrl = drvdata - > retention_ctrl ;
struct regmap * pmu_regs = ctrl - > priv ;
int i ;
if ( ctrl - > refcnt & & ! atomic_dec_and_test ( ctrl - > refcnt ) )
return ;
for ( i = 0 ; i < ctrl - > nr_regs ; i + + )
regmap_write ( pmu_regs , ctrl - > regs [ i ] , ctrl - > value ) ;
}
2017-05-16 20:56:27 +02:00
struct samsung_retention_ctrl *
2017-01-26 10:29:25 +01:00
exynos_retention_init ( struct samsung_pinctrl_drv_data * drvdata ,
const struct samsung_retention_data * data )
{
struct samsung_retention_ctrl * ctrl ;
struct regmap * pmu_regs ;
2017-03-23 09:03:22 +01:00
int i ;
2017-01-26 10:29:25 +01:00
ctrl = devm_kzalloc ( drvdata - > dev , sizeof ( * ctrl ) , GFP_KERNEL ) ;
if ( ! ctrl )
return ERR_PTR ( - ENOMEM ) ;
pmu_regs = exynos_get_pmu_regmap ( ) ;
if ( IS_ERR ( pmu_regs ) )
return ERR_CAST ( pmu_regs ) ;
ctrl - > priv = pmu_regs ;
ctrl - > regs = data - > regs ;
ctrl - > nr_regs = data - > nr_regs ;
ctrl - > value = data - > value ;
ctrl - > refcnt = data - > refcnt ;
ctrl - > enable = exynos_retention_enable ;
ctrl - > disable = exynos_retention_disable ;
2017-03-23 09:03:22 +01:00
/* Ensure that retention is disabled on driver init */
for ( i = 0 ; i < ctrl - > nr_regs ; i + + )
regmap_write ( pmu_regs , ctrl - > regs [ i ] , ctrl - > value ) ;
2017-01-26 10:29:25 +01:00
return ctrl ;
}