2018-12-05 11:25:00 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-02-18 18:28:34 +04:00
/*
* Renesas INTC External IRQ Pin Driver
*
* Copyright ( C ) 2013 Magnus Damm
*/
# include <linux/init.h>
2013-06-13 13:23:38 +04:00
# include <linux/of.h>
2013-02-18 18:28:34 +04:00
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/irqdomain.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/module.h>
2014-09-12 17:15:18 +04:00
# include <linux/pm_runtime.h>
2013-02-18 18:28:34 +04:00
# define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */
# define INTC_IRQPIN_REG_SENSE 0 /* ICRn */
# define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */
# define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */
# define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */
# define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */
2014-12-03 15:18:03 +03:00
# define INTC_IRQPIN_REG_NR_MANDATORY 5
# define INTC_IRQPIN_REG_IRLM 5 /* ICR0 with IRLM bit (optional) */
# define INTC_IRQPIN_REG_NR 6
2013-02-18 18:28:34 +04:00
/* INTC external IRQ PIN hardware register access:
*
* SENSE is read - write 32 - bit with 2 - bits or 4 - bits per IRQ ( * )
* PRIO is read - write 32 - bit with 4 - bits per IRQ ( * * )
* SOURCE is read - only 32 - bit or 8 - bit with 1 - bit per IRQ ( * * * )
* MASK is write - only 32 - bit or 8 - bit with 1 - bit per IRQ ( * * * )
* CLEAR is write - only 32 - bit or 8 - bit with 1 - bit per IRQ ( * * * )
*
* ( * ) May be accessed by more than one driver instance - lock needed
* ( * * ) Read - modify - write access by one driver instance - lock needed
* ( * * * ) Accessed by one driver instance only - no locking needed
*/
struct intc_irqpin_iomem {
void __iomem * iomem ;
unsigned long ( * read ) ( void __iomem * iomem ) ;
void ( * write ) ( void __iomem * iomem , unsigned long data ) ;
int width ;
2013-02-26 15:58:44 +04:00
} ;
2013-02-18 18:28:34 +04:00
struct intc_irqpin_irq {
int hw_irq ;
2013-02-26 15:58:54 +04:00
int requested_irq ;
int domain_irq ;
2013-02-18 18:28:34 +04:00
struct intc_irqpin_priv * p ;
2013-02-26 15:58:44 +04:00
} ;
2013-02-18 18:28:34 +04:00
struct intc_irqpin_priv {
struct intc_irqpin_iomem iomem [ INTC_IRQPIN_REG_NR ] ;
struct intc_irqpin_irq irq [ INTC_IRQPIN_MAX ] ;
2015-11-24 17:49:40 +03:00
unsigned int sense_bitfield_width ;
2013-02-18 18:28:34 +04:00
struct platform_device * pdev ;
struct irq_chip irq_chip ;
struct irq_domain * irq_domain ;
2018-02-12 16:55:11 +03:00
atomic_t wakeup_path ;
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
unsigned shared_irqs : 1 ;
2013-03-27 17:54:03 +04:00
u8 shared_irq_mask ;
2013-02-18 18:28:34 +04:00
} ;
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
struct intc_irqpin_config {
2020-10-28 18:39:55 +03:00
int irlm_bit ; /* -1 if non-existent */
2014-12-03 15:18:03 +03:00
} ;
2013-02-18 18:28:34 +04:00
static unsigned long intc_irqpin_read32 ( void __iomem * iomem )
{
return ioread32 ( iomem ) ;
}
static unsigned long intc_irqpin_read8 ( void __iomem * iomem )
{
return ioread8 ( iomem ) ;
}
static void intc_irqpin_write32 ( void __iomem * iomem , unsigned long data )
{
iowrite32 ( data , iomem ) ;
}
static void intc_irqpin_write8 ( void __iomem * iomem , unsigned long data )
{
iowrite8 ( data , iomem ) ;
}
static inline unsigned long intc_irqpin_read ( struct intc_irqpin_priv * p ,
int reg )
{
struct intc_irqpin_iomem * i = & p - > iomem [ reg ] ;
2013-02-26 15:58:44 +04:00
2013-02-18 18:28:34 +04:00
return i - > read ( i - > iomem ) ;
}
static inline void intc_irqpin_write ( struct intc_irqpin_priv * p ,
int reg , unsigned long data )
{
struct intc_irqpin_iomem * i = & p - > iomem [ reg ] ;
2013-02-26 15:58:44 +04:00
2013-02-18 18:28:34 +04:00
i - > write ( i - > iomem , data ) ;
}
static inline unsigned long intc_irqpin_hwirq_mask ( struct intc_irqpin_priv * p ,
int reg , int hw_irq )
{
return BIT ( ( p - > iomem [ reg ] . width - 1 ) - hw_irq ) ;
}
static inline void intc_irqpin_irq_write_hwirq ( struct intc_irqpin_priv * p ,
int reg , int hw_irq )
{
intc_irqpin_write ( p , reg , intc_irqpin_hwirq_mask ( p , reg , hw_irq ) ) ;
}
static DEFINE_RAW_SPINLOCK ( intc_irqpin_lock ) ; /* only used by slow path */
static void intc_irqpin_read_modify_write ( struct intc_irqpin_priv * p ,
int reg , int shift ,
int width , int value )
{
unsigned long flags ;
unsigned long tmp ;
raw_spin_lock_irqsave ( & intc_irqpin_lock , flags ) ;
tmp = intc_irqpin_read ( p , reg ) ;
tmp & = ~ ( ( ( 1 < < width ) - 1 ) < < shift ) ;
tmp | = value < < shift ;
intc_irqpin_write ( p , reg , tmp ) ;
raw_spin_unlock_irqrestore ( & intc_irqpin_lock , flags ) ;
}
static void intc_irqpin_mask_unmask_prio ( struct intc_irqpin_priv * p ,
int irq , int do_mask )
{
2013-11-09 16:18:01 +04:00
/* The PRIO register is assumed to be 32-bit with fixed 4-bit fields. */
int bitfield_width = 4 ;
int shift = 32 - ( irq + 1 ) * bitfield_width ;
2013-02-18 18:28:34 +04:00
intc_irqpin_read_modify_write ( p , INTC_IRQPIN_REG_PRIO ,
shift , bitfield_width ,
do_mask ? 0 : ( 1 < < bitfield_width ) - 1 ) ;
}
static int intc_irqpin_set_sense ( struct intc_irqpin_priv * p , int irq , int value )
{
2013-11-09 16:18:01 +04:00
/* The SENSE register is assumed to be 32-bit. */
2015-11-24 17:49:40 +03:00
int bitfield_width = p - > sense_bitfield_width ;
2013-11-09 16:18:01 +04:00
int shift = 32 - ( irq + 1 ) * bitfield_width ;
2013-02-18 18:28:34 +04:00
dev_dbg ( & p - > pdev - > dev , " sense irq = %d, mode = %d \n " , irq , value ) ;
if ( value > = ( 1 < < bitfield_width ) )
return - EINVAL ;
intc_irqpin_read_modify_write ( p , INTC_IRQPIN_REG_SENSE , shift ,
bitfield_width , value ) ;
return 0 ;
}
static void intc_irqpin_dbg ( struct intc_irqpin_irq * i , char * str )
{
dev_dbg ( & i - > p - > pdev - > dev , " %s (%d:%d:%d) \n " ,
2013-02-26 15:58:54 +04:00
str , i - > requested_irq , i - > hw_irq , i - > domain_irq ) ;
2013-02-18 18:28:34 +04:00
}
static void intc_irqpin_irq_enable ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
int hw_irq = irqd_to_hwirq ( d ) ;
intc_irqpin_dbg ( & p - > irq [ hw_irq ] , " enable " ) ;
intc_irqpin_irq_write_hwirq ( p , INTC_IRQPIN_REG_CLEAR , hw_irq ) ;
}
static void intc_irqpin_irq_disable ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
int hw_irq = irqd_to_hwirq ( d ) ;
intc_irqpin_dbg ( & p - > irq [ hw_irq ] , " disable " ) ;
intc_irqpin_irq_write_hwirq ( p , INTC_IRQPIN_REG_MASK , hw_irq ) ;
}
2013-03-27 17:54:03 +04:00
static void intc_irqpin_shared_irq_enable ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
int hw_irq = irqd_to_hwirq ( d ) ;
intc_irqpin_dbg ( & p - > irq [ hw_irq ] , " shared enable " ) ;
intc_irqpin_irq_write_hwirq ( p , INTC_IRQPIN_REG_CLEAR , hw_irq ) ;
p - > shared_irq_mask & = ~ BIT ( hw_irq ) ;
}
static void intc_irqpin_shared_irq_disable ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
int hw_irq = irqd_to_hwirq ( d ) ;
intc_irqpin_dbg ( & p - > irq [ hw_irq ] , " shared disable " ) ;
intc_irqpin_irq_write_hwirq ( p , INTC_IRQPIN_REG_MASK , hw_irq ) ;
p - > shared_irq_mask | = BIT ( hw_irq ) ;
}
2013-02-18 18:28:34 +04:00
static void intc_irqpin_irq_enable_force ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
2013-02-26 15:58:54 +04:00
int irq = p - > irq [ irqd_to_hwirq ( d ) ] . requested_irq ;
2013-02-18 18:28:34 +04:00
intc_irqpin_irq_enable ( d ) ;
2013-02-26 15:59:04 +04:00
/* enable interrupt through parent interrupt controller,
* assumes non - shared interrupt with 1 : 1 mapping
* needed for busted IRQs on some SoCs like sh73a0
*/
2013-02-18 18:28:34 +04:00
irq_get_chip ( irq ) - > irq_unmask ( irq_get_irq_data ( irq ) ) ;
}
static void intc_irqpin_irq_disable_force ( struct irq_data * d )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
2013-02-26 15:58:54 +04:00
int irq = p - > irq [ irqd_to_hwirq ( d ) ] . requested_irq ;
2013-02-18 18:28:34 +04:00
2013-02-26 15:59:04 +04:00
/* disable interrupt through parent interrupt controller,
* assumes non - shared interrupt with 1 : 1 mapping
* needed for busted IRQs on some SoCs like sh73a0
*/
2013-02-18 18:28:34 +04:00
irq_get_chip ( irq ) - > irq_mask ( irq_get_irq_data ( irq ) ) ;
intc_irqpin_irq_disable ( d ) ;
}
# define INTC_IRQ_SENSE_VALID 0x10
# define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
static unsigned char intc_irqpin_sense [ IRQ_TYPE_SENSE_MASK + 1 ] = {
[ IRQ_TYPE_EDGE_FALLING ] = INTC_IRQ_SENSE ( 0x00 ) ,
[ IRQ_TYPE_EDGE_RISING ] = INTC_IRQ_SENSE ( 0x01 ) ,
[ IRQ_TYPE_LEVEL_LOW ] = INTC_IRQ_SENSE ( 0x02 ) ,
[ IRQ_TYPE_LEVEL_HIGH ] = INTC_IRQ_SENSE ( 0x03 ) ,
[ IRQ_TYPE_EDGE_BOTH ] = INTC_IRQ_SENSE ( 0x04 ) ,
} ;
static int intc_irqpin_irq_set_type ( struct irq_data * d , unsigned int type )
{
unsigned char value = intc_irqpin_sense [ type & IRQ_TYPE_SENSE_MASK ] ;
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
if ( ! ( value & INTC_IRQ_SENSE_VALID ) )
return - EINVAL ;
return intc_irqpin_set_sense ( p , irqd_to_hwirq ( d ) ,
value ^ INTC_IRQ_SENSE_VALID ) ;
}
2014-09-12 17:15:18 +04:00
static int intc_irqpin_irq_set_wake ( struct irq_data * d , unsigned int on )
{
struct intc_irqpin_priv * p = irq_data_get_irq_chip_data ( d ) ;
2015-09-08 20:00:35 +03:00
int hw_irq = irqd_to_hwirq ( d ) ;
irq_set_irq_wake ( p - > irq [ hw_irq ] . requested_irq , on ) ;
2014-09-12 17:15:18 +04:00
if ( on )
2018-02-12 16:55:11 +03:00
atomic_inc ( & p - > wakeup_path ) ;
2014-09-12 17:15:18 +04:00
else
2018-02-12 16:55:11 +03:00
atomic_dec ( & p - > wakeup_path ) ;
2014-09-12 17:15:18 +04:00
return 0 ;
}
2013-02-18 18:28:34 +04:00
static irqreturn_t intc_irqpin_irq_handler ( int irq , void * dev_id )
{
struct intc_irqpin_irq * i = dev_id ;
struct intc_irqpin_priv * p = i - > p ;
unsigned long bit ;
intc_irqpin_dbg ( i , " demux1 " ) ;
bit = intc_irqpin_hwirq_mask ( p , INTC_IRQPIN_REG_SOURCE , i - > hw_irq ) ;
if ( intc_irqpin_read ( p , INTC_IRQPIN_REG_SOURCE ) & bit ) {
intc_irqpin_write ( p , INTC_IRQPIN_REG_SOURCE , ~ bit ) ;
intc_irqpin_dbg ( i , " demux2 " ) ;
2013-02-26 15:58:54 +04:00
generic_handle_irq ( i - > domain_irq ) ;
2013-02-18 18:28:34 +04:00
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
2013-03-27 17:54:03 +04:00
static irqreturn_t intc_irqpin_shared_irq_handler ( int irq , void * dev_id )
{
struct intc_irqpin_priv * p = dev_id ;
unsigned int reg_source = intc_irqpin_read ( p , INTC_IRQPIN_REG_SOURCE ) ;
irqreturn_t status = IRQ_NONE ;
int k ;
for ( k = 0 ; k < 8 ; k + + ) {
if ( reg_source & BIT ( 7 - k ) ) {
if ( BIT ( k ) & p - > shared_irq_mask )
continue ;
status | = intc_irqpin_irq_handler ( irq , & p - > irq [ k ] ) ;
}
}
return status ;
}
irqchip/renesas-intc-irqpin: Use a separate lockdep class
The renesas-intc-irqpin interrupt controller is cascaded to the GIC.
Hence when propagating wake-up settings to its parent interrupt
controller, the following lockdep warning is printed:
=============================================
[ INFO: possible recursive locking detected ]
4.2.0-armadillo-10725-g50fcd7643c034198 #781 Not tainted
---------------------------------------------
s2ram/1179 is trying to acquire lock:
(&irq_desc_lock_class){-.-...}, at: [<c005bb54>] __irq_get_desc_lock+0x78/0x94
but task is already holding lock:
(&irq_desc_lock_class){-.-...}, at: [<c005bb54>] __irq_get_desc_lock+0x78/0x94
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock(&irq_desc_lock_class);
lock(&irq_desc_lock_class);
*** DEADLOCK ***
May be due to missing lock nesting notation
7 locks held by s2ram/1179:
#0: (sb_writers#7){.+.+.+}, at: [<c00c9708>] __sb_start_write+0x64/0xb8
#1: (&of->mutex){+.+.+.}, at: [<c0125a00>] kernfs_fop_write+0x78/0x1a0
#2: (s_active#23){.+.+.+}, at: [<c0125a08>] kernfs_fop_write+0x80/0x1a0
#3: (autosleep_lock){+.+.+.}, at: [<c0058244>] pm_autosleep_lock+0x18/0x20
#4: (pm_mutex){+.+.+.}, at: [<c0057e50>] pm_suspend+0x54/0x248
#5: (&dev->mutex){......}, at: [<c0243a20>] __device_suspend+0xdc/0x240
#6: (&irq_desc_lock_class){-.-...}, at: [<c005bb54>] __irq_get_desc_lock+0x78/0x94
stack backtrace:
CPU: 0 PID: 1179 Comm: s2ram Not tainted 4.2.0-armadillo-10725-g50fcd7643c034198
Hardware name: Generic R8A7740 (Flattened Device Tree)
[<c00129f4>] (dump_backtrace) from [<c0012bec>] (show_stack+0x18/0x1c)
[<c0012bd4>] (show_stack) from [<c03f5d94>] (dump_stack+0x20/0x28)
[<c03f5d74>] (dump_stack) from [<c00514d4>] (__lock_acquire+0x67c/0x1b88)
[<c0050e58>] (__lock_acquire) from [<c0052df8>] (lock_acquire+0x9c/0xbc)
[<c0052d5c>] (lock_acquire) from [<c03fb068>] (_raw_spin_lock_irqsave+0x44/0x58)
[<c03fb024>] (_raw_spin_lock_irqsave) from [<c005bb54>] (__irq_get_desc_lock+0x78/0x94
[<c005badc>] (__irq_get_desc_lock) from [<c005c3d8>] (irq_set_irq_wake+0x28/0x100)
[<c005c3b0>] (irq_set_irq_wake) from [<c01e50d0>] (intc_irqpin_irq_set_wake+0x24/0x4c)
[<c01e50ac>] (intc_irqpin_irq_set_wake) from [<c005c17c>] (set_irq_wake_real+0x3c/0x50
[<c005c140>] (set_irq_wake_real) from [<c005c414>] (irq_set_irq_wake+0x64/0x100)
[<c005c3b0>] (irq_set_irq_wake) from [<c02a19b4>] (gpio_keys_suspend+0x60/0xa0)
[<c02a1954>] (gpio_keys_suspend) from [<c023b750>] (platform_pm_suspend+0x3c/0x5c)
Avoid this false positive by using a separate lockdep class for INTC
External IRQ Pin interrupts.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Grygorii Strashko <grygorii.strashko@ti.com>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Link: http://lkml.kernel.org/r/1441798974-25716-3-git-send-email-geert%2Brenesas@glider.be
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2015-09-09 14:42:54 +03:00
/*
* This lock class tells lockdep that INTC External IRQ Pin irqs are in a
* different category than their parents , so it won ' t report false recursion .
*/
static struct lock_class_key intc_irqpin_irq_lock_class ;
2017-12-02 20:11:04 +03:00
/* And this is for the request mutex */
static struct lock_class_key intc_irqpin_irq_request_class ;
2013-02-18 18:28:34 +04:00
static int intc_irqpin_irq_domain_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
struct intc_irqpin_priv * p = h - > host_data ;
2013-02-26 15:58:54 +04:00
p - > irq [ hw ] . domain_irq = virq ;
p - > irq [ hw ] . hw_irq = hw ;
2013-02-18 18:28:34 +04:00
intc_irqpin_dbg ( & p - > irq [ hw ] , " map " ) ;
irq_set_chip_data ( virq , h - > host_data ) ;
2017-12-02 20:11:04 +03:00
irq_set_lockdep_class ( virq , & intc_irqpin_irq_lock_class ,
& intc_irqpin_irq_request_class ) ;
2013-02-18 18:28:34 +04:00
irq_set_chip_and_handler ( virq , & p - > irq_chip , handle_level_irq ) ;
return 0 ;
}
2015-04-27 15:54:24 +03:00
static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
2013-02-18 18:28:34 +04:00
. map = intc_irqpin_irq_domain_map ,
2013-03-06 10:16:08 +04:00
. xlate = irq_domain_xlate_twocell ,
2013-02-18 18:28:34 +04:00
} ;
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
static const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = {
2014-12-03 15:18:03 +03:00
. irlm_bit = 23 , /* ICR0.IRLM0 */
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
} ;
static const struct intc_irqpin_config intc_irqpin_rmobile = {
2020-10-28 18:39:55 +03:00
. irlm_bit = - 1 ,
2014-12-03 15:18:03 +03:00
} ;
static const struct of_device_id intc_irqpin_dt_ids [ ] = {
{ . compatible = " renesas,intc-irqpin " , } ,
2015-09-30 13:03:07 +03:00
{ . compatible = " renesas,intc-irqpin-r8a7778 " ,
. data = & intc_irqpin_irlm_r8a777x } ,
2014-12-03 15:18:03 +03:00
{ . compatible = " renesas,intc-irqpin-r8a7779 " ,
2015-09-30 13:03:07 +03:00
. data = & intc_irqpin_irlm_r8a777x } ,
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
{ . compatible = " renesas,intc-irqpin-r8a7740 " ,
. data = & intc_irqpin_rmobile } ,
{ . compatible = " renesas,intc-irqpin-sh73a0 " ,
. data = & intc_irqpin_rmobile } ,
2014-12-03 15:18:03 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , intc_irqpin_dt_ids ) ;
2013-02-18 18:28:34 +04:00
static int intc_irqpin_probe ( struct platform_device * pdev )
{
2017-10-04 15:17:58 +03:00
const struct intc_irqpin_config * config ;
2014-09-12 17:15:17 +04:00
struct device * dev = & pdev - > dev ;
2013-02-18 18:28:34 +04:00
struct intc_irqpin_priv * p ;
struct intc_irqpin_iomem * i ;
struct resource * io [ INTC_IRQPIN_REG_NR ] ;
struct irq_chip * irq_chip ;
void ( * enable_fn ) ( struct irq_data * d ) ;
void ( * disable_fn ) ( struct irq_data * d ) ;
2014-09-12 17:15:17 +04:00
const char * name = dev_name ( dev ) ;
2015-11-24 17:49:40 +03:00
bool control_parent ;
2015-11-24 17:49:41 +03:00
unsigned int nirqs ;
2013-03-27 17:54:03 +04:00
int ref_irq ;
2013-02-18 18:28:34 +04:00
int ret ;
int k ;
2014-09-12 17:15:17 +04:00
p = devm_kzalloc ( dev , sizeof ( * p ) , GFP_KERNEL ) ;
2019-04-29 18:15:14 +03:00
if ( ! p )
2014-09-12 17:15:18 +04:00
return - ENOMEM ;
2013-02-18 18:28:34 +04:00
/* deal with driver instance configuration */
2015-11-24 17:49:40 +03:00
of_property_read_u32 ( dev - > of_node , " sense-bitfield-width " ,
& p - > sense_bitfield_width ) ;
control_parent = of_property_read_bool ( dev - > of_node , " control-parent " ) ;
if ( ! p - > sense_bitfield_width )
p - > sense_bitfield_width = 4 ; /* default to 4 bits */
2013-02-18 18:28:34 +04:00
p - > pdev = pdev ;
platform_set_drvdata ( pdev , p ) ;
2017-10-04 15:17:58 +03:00
config = of_device_get_match_data ( dev ) ;
2014-09-12 17:15:18 +04:00
pm_runtime_enable ( dev ) ;
pm_runtime_get_sync ( dev ) ;
2014-12-03 15:18:03 +03:00
/* get hold of register banks */
memset ( io , 0 , sizeof ( io ) ) ;
2013-02-18 18:28:34 +04:00
for ( k = 0 ; k < INTC_IRQPIN_REG_NR ; k + + ) {
io [ k ] = platform_get_resource ( pdev , IORESOURCE_MEM , k ) ;
2014-12-03 15:18:03 +03:00
if ( ! io [ k ] & & k < INTC_IRQPIN_REG_NR_MANDATORY ) {
2014-09-12 17:15:17 +04:00
dev_err ( dev , " not enough IOMEM resources \n " ) ;
2013-02-18 18:28:34 +04:00
ret = - EINVAL ;
2013-02-26 15:59:13 +04:00
goto err0 ;
2013-02-18 18:28:34 +04:00
}
}
/* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */
for ( k = 0 ; k < INTC_IRQPIN_MAX ; k + + ) {
2021-12-16 21:21:20 +03:00
ret = platform_get_irq_optional ( pdev , k ) ;
if ( ret = = - ENXIO )
2013-02-18 18:28:34 +04:00
break ;
2021-12-16 21:21:20 +03:00
if ( ret < 0 )
goto err0 ;
2013-02-18 18:28:34 +04:00
p - > irq [ k ] . p = p ;
2021-12-16 21:21:20 +03:00
p - > irq [ k ] . requested_irq = ret ;
2013-02-18 18:28:34 +04:00
}
2015-11-24 17:49:41 +03:00
nirqs = k ;
if ( nirqs < 1 ) {
2014-09-12 17:15:17 +04:00
dev_err ( dev , " not enough IRQ resources \n " ) ;
2013-02-18 18:28:34 +04:00
ret = - EINVAL ;
2013-02-26 15:59:13 +04:00
goto err0 ;
2013-02-18 18:28:34 +04:00
}
/* ioremap IOMEM and setup read/write callbacks */
for ( k = 0 ; k < INTC_IRQPIN_REG_NR ; k + + ) {
i = & p - > iomem [ k ] ;
2014-12-03 15:18:03 +03:00
/* handle optional registers */
if ( ! io [ k ] )
continue ;
2013-02-18 18:28:34 +04:00
switch ( resource_size ( io [ k ] ) ) {
case 1 :
i - > width = 8 ;
i - > read = intc_irqpin_read8 ;
i - > write = intc_irqpin_write8 ;
break ;
case 4 :
i - > width = 32 ;
i - > read = intc_irqpin_read32 ;
i - > write = intc_irqpin_write32 ;
break ;
default :
2014-09-12 17:15:17 +04:00
dev_err ( dev , " IOMEM size mismatch \n " ) ;
2013-02-18 18:28:34 +04:00
ret = - EINVAL ;
2013-02-26 15:59:13 +04:00
goto err0 ;
2013-02-18 18:28:34 +04:00
}
2020-01-06 11:43:50 +03:00
i - > iomem = devm_ioremap ( dev , io [ k ] - > start ,
2020-02-12 11:47:44 +03:00
resource_size ( io [ k ] ) ) ;
2013-02-18 18:28:34 +04:00
if ( ! i - > iomem ) {
2014-09-12 17:15:17 +04:00
dev_err ( dev , " failed to remap IOMEM \n " ) ;
2013-02-18 18:28:34 +04:00
ret = - ENXIO ;
2013-02-26 15:59:13 +04:00
goto err0 ;
2013-02-18 18:28:34 +04:00
}
}
2014-12-03 15:18:03 +03:00
/* configure "individual IRQ mode" where needed */
2020-10-28 18:39:55 +03:00
if ( config & & config - > irlm_bit > = 0 ) {
2014-12-03 15:18:03 +03:00
if ( io [ INTC_IRQPIN_REG_IRLM ] )
intc_irqpin_read_modify_write ( p , INTC_IRQPIN_REG_IRLM ,
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
config - > irlm_bit , 1 , 1 ) ;
2014-12-03 15:18:03 +03:00
else
dev_warn ( dev , " unable to select IRLM mode \n " ) ;
}
2013-02-18 18:28:34 +04:00
/* mask all interrupts using priority */
2015-11-24 17:49:41 +03:00
for ( k = 0 ; k < nirqs ; k + + )
2013-02-18 18:28:34 +04:00
intc_irqpin_mask_unmask_prio ( p , k , 1 ) ;
2013-03-27 17:54:03 +04:00
/* clear all pending interrupts */
intc_irqpin_write ( p , INTC_IRQPIN_REG_SOURCE , 0x0 ) ;
/* scan for shared interrupt lines */
ref_irq = p - > irq [ 0 ] . requested_irq ;
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
p - > shared_irqs = 1 ;
2015-11-24 17:49:41 +03:00
for ( k = 1 ; k < nirqs ; k + + ) {
2013-03-27 17:54:03 +04:00
if ( ref_irq ! = p - > irq [ k ] . requested_irq ) {
irqchip/renesas-intc-irqpin: Improve clock error handling and reporting
If the Renesas External IRQ Pin driver cannot find a functional clock,
it prints a warning, .e.g.
renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock
and continues, as the clock is optional, depending on the SoC type.
This warning may confuse users.
To fix this, add a flag to indicate that the clock is mandatory or
optional, and add a few more compatible entries:
- If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a
missing clock is now treated as a fatal error,
- If the clock is optional (on R-Car Gen1, or using the generic
"renesas,intc-irqpin" compatible value), the warning is no longer
printed.
This requires making struct intc_irqpin_irlm_config more generic by
renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM
is needed.
The new clock flag is merged with the existing shared_irqs boolean into
a bitfield to save space.
Suggested-by: Magnus Damm <magnus.damm@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2015-11-24 18:08:13 +03:00
p - > shared_irqs = 0 ;
2013-03-27 17:54:03 +04:00
break ;
}
}
2013-02-18 18:28:34 +04:00
/* use more severe masking method if requested */
2015-11-24 17:49:40 +03:00
if ( control_parent ) {
2013-02-18 18:28:34 +04:00
enable_fn = intc_irqpin_irq_enable_force ;
disable_fn = intc_irqpin_irq_disable_force ;
2013-03-27 17:54:03 +04:00
} else if ( ! p - > shared_irqs ) {
2013-02-18 18:28:34 +04:00
enable_fn = intc_irqpin_irq_enable ;
disable_fn = intc_irqpin_irq_disable ;
2013-03-27 17:54:03 +04:00
} else {
enable_fn = intc_irqpin_shared_irq_enable ;
disable_fn = intc_irqpin_shared_irq_disable ;
2013-02-18 18:28:34 +04:00
}
irq_chip = & p - > irq_chip ;
2019-06-07 12:58:55 +03:00
irq_chip - > name = " intc-irqpin " ;
2013-02-18 18:28:34 +04:00
irq_chip - > irq_mask = disable_fn ;
irq_chip - > irq_unmask = enable_fn ;
irq_chip - > irq_set_type = intc_irqpin_irq_set_type ;
2014-09-12 17:15:18 +04:00
irq_chip - > irq_set_wake = intc_irqpin_irq_set_wake ;
irq_chip - > flags = IRQCHIP_MASK_ON_SUSPEND ;
2013-02-18 18:28:34 +04:00
2015-11-24 17:49:41 +03:00
p - > irq_domain = irq_domain_add_simple ( dev - > of_node , nirqs , 0 ,
& intc_irqpin_irq_domain_ops , p ) ;
2013-02-18 18:28:34 +04:00
if ( ! p - > irq_domain ) {
ret = - ENXIO ;
2014-09-12 17:15:17 +04:00
dev_err ( dev , " cannot initialize irq domain \n " ) ;
2013-02-26 15:59:13 +04:00
goto err0 ;
2013-02-18 18:28:34 +04:00
}
2022-02-01 15:03:01 +03:00
irq_domain_set_pm_device ( p - > irq_domain , dev ) ;
2013-03-27 17:54:03 +04:00
if ( p - > shared_irqs ) {
/* request one shared interrupt */
2014-09-12 17:15:17 +04:00
if ( devm_request_irq ( dev , p - > irq [ 0 ] . requested_irq ,
2013-03-27 17:54:03 +04:00
intc_irqpin_shared_irq_handler ,
IRQF_SHARED , name , p ) ) {
2014-09-12 17:15:17 +04:00
dev_err ( dev , " failed to request low IRQ \n " ) ;
2013-02-18 18:28:34 +04:00
ret = - ENOENT ;
2013-02-26 15:59:13 +04:00
goto err1 ;
2013-02-18 18:28:34 +04:00
}
2013-03-27 17:54:03 +04:00
} else {
/* request interrupts one by one */
2015-11-24 17:49:41 +03:00
for ( k = 0 ; k < nirqs ; k + + ) {
2014-09-12 17:15:17 +04:00
if ( devm_request_irq ( dev , p - > irq [ k ] . requested_irq ,
intc_irqpin_irq_handler , 0 , name ,
& p - > irq [ k ] ) ) {
dev_err ( dev , " failed to request low IRQ \n " ) ;
2013-03-27 17:54:03 +04:00
ret = - ENOENT ;
goto err1 ;
}
}
2013-02-18 18:28:34 +04:00
}
2013-03-27 17:54:03 +04:00
/* unmask all interrupts on prio level */
2015-11-24 17:49:41 +03:00
for ( k = 0 ; k < nirqs ; k + + )
2013-03-27 17:54:03 +04:00
intc_irqpin_mask_unmask_prio ( p , k , 0 ) ;
2015-11-24 17:49:41 +03:00
dev_info ( dev , " driving %d irqs \n " , nirqs ) ;
2013-02-18 18:28:34 +04:00
return 0 ;
err1 :
2013-02-26 15:59:13 +04:00
irq_domain_remove ( p - > irq_domain ) ;
2013-02-18 18:28:34 +04:00
err0 :
2014-09-12 17:15:18 +04:00
pm_runtime_put ( dev ) ;
pm_runtime_disable ( dev ) ;
2013-02-18 18:28:34 +04:00
return ret ;
}
static int intc_irqpin_remove ( struct platform_device * pdev )
{
struct intc_irqpin_priv * p = platform_get_drvdata ( pdev ) ;
irq_domain_remove ( p - > irq_domain ) ;
2014-09-12 17:15:18 +04:00
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2013-02-18 18:28:34 +04:00
return 0 ;
}
2018-02-12 16:55:11 +03:00
static int __maybe_unused intc_irqpin_suspend ( struct device * dev )
{
struct intc_irqpin_priv * p = dev_get_drvdata ( dev ) ;
if ( atomic_read ( & p - > wakeup_path ) )
device_set_wakeup_path ( dev ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( intc_irqpin_pm_ops , intc_irqpin_suspend , NULL ) ;
2013-02-18 18:28:34 +04:00
static struct platform_driver intc_irqpin_device_driver = {
. probe = intc_irqpin_probe ,
. remove = intc_irqpin_remove ,
. driver = {
. name = " renesas_intc_irqpin " ,
2013-03-06 10:16:08 +04:00
. of_match_table = intc_irqpin_dt_ids ,
2018-02-12 16:55:11 +03:00
. pm = & intc_irqpin_pm_ops ,
2013-02-18 18:28:34 +04:00
}
} ;
static int __init intc_irqpin_init ( void )
{
return platform_driver_register ( & intc_irqpin_device_driver ) ;
}
postcore_initcall ( intc_irqpin_init ) ;
static void __exit intc_irqpin_exit ( void )
{
platform_driver_unregister ( & intc_irqpin_device_driver ) ;
}
module_exit ( intc_irqpin_exit ) ;
MODULE_AUTHOR ( " Magnus Damm " ) ;
MODULE_DESCRIPTION ( " Renesas INTC External IRQ Pin Driver " ) ;