Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
37b25fff
Commit
37b25fff
authored
Apr 10, 2015
by
Jason Cooper
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'irqchip/stacked-tegra' into irqchip/core
parents
78223354
1eec5821
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
488 additions
and
227 deletions
+488
-227
Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra-ictlr.txt
...tree/bindings/interrupt-controller/nvidia,tegra-ictlr.txt
+43
-0
arch/arm/boot/dts/tegra114.dtsi
arch/arm/boot/dts/tegra114.dtsi
+15
-1
arch/arm/boot/dts/tegra124.dtsi
arch/arm/boot/dts/tegra124.dtsi
+15
-1
arch/arm/boot/dts/tegra20.dtsi
arch/arm/boot/dts/tegra20.dtsi
+14
-1
arch/arm/boot/dts/tegra30.dtsi
arch/arm/boot/dts/tegra30.dtsi
+15
-1
arch/arm/mach-tegra/iomap.h
arch/arm/mach-tegra/iomap.h
+0
-15
arch/arm/mach-tegra/irq.c
arch/arm/mach-tegra/irq.c
+8
-201
arch/arm/mach-tegra/irq.h
arch/arm/mach-tegra/irq.h
+0
-6
arch/arm/mach-tegra/tegra.c
arch/arm/mach-tegra/tegra.c
+0
-1
drivers/irqchip/Makefile
drivers/irqchip/Makefile
+1
-0
drivers/irqchip/irq-tegra.c
drivers/irqchip/irq-tegra.c
+377
-0
No files found.
Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra-ictlr.txt
0 → 100644
View file @
37b25fff
NVIDIA Legacy Interrupt Controller
All Tegra SoCs contain a legacy interrupt controller that routes
interrupts to the GIC, and also serves as a wakeup source. It is also
referred to as "ictlr", hence the name of the binding.
The HW block exposes a number of interrupt controllers, each
implementing a set of 32 interrupts.
Required properties:
- compatible : should be: "nvidia,tegra<chip>-ictlr". The LIC on
subsequent SoCs remained backwards-compatible with Tegra30, so on
Tegra generations later than Tegra30 the compatible value should
include "nvidia,tegra30-ictlr".
- reg : Specifies base physical address and size of the registers.
Each controller must be described separately (Tegra20 has 4 of them,
whereas Tegra30 and later have 5"
- interrupt-controller : Identifies the node as an interrupt controller.
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value must be 3.
- interrupt-parent : a phandle to the GIC these interrupts are routed
to.
Notes:
- Because this HW ultimately routes interrupts to the GIC, the
interrupt specifier must be that of the GIC.
- Only SPIs can use the ictlr as an interrupt parent. SGIs and PPIs
are explicitly forbidden.
Example:
ictlr: interrupt-controller@60004000 {
compatible = "nvidia,tegra20-ictlr", "nvidia,tegra-ictlr";
reg = <0x60004000 64>,
<0x60004100 64>,
<0x60004200 64>,
<0x60004300 64>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&intc>;
};
arch/arm/boot/dts/tegra114.dtsi
View file @
37b25fff
...
...
@@ -8,7 +8,7 @@
/ {
compatible = "nvidia,tegra114";
interrupt-parent = <&
g
ic>;
interrupt-parent = <&
l
ic>;
host1x@50000000 {
compatible = "nvidia,tegra114-host1x", "simple-bus";
...
...
@@ -134,6 +134,19 @@ gic: interrupt-controller@50041000 {
<0x50046000 0x2000>;
interrupts = <GIC_PPI 9
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
interrupt-parent = <&gic>;
};
lic: interrupt-controller@60004000 {
compatible = "nvidia,tegra114-ictlr", "nvidia,tegra30-ictlr";
reg = <0x60004000 0x100>,
<0x60004100 0x50>,
<0x60004200 0x50>,
<0x60004300 0x50>,
<0x60004400 0x50>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};
timer@60005000 {
...
...
@@ -766,5 +779,6 @@ timer {
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
interrupt-parent = <&gic>;
};
};
arch/arm/boot/dts/tegra124.dtsi
View file @
37b25fff
...
...
@@ -10,7 +10,7 @@
/ {
compatible = "nvidia,tegra124";
interrupt-parent = <&
g
ic>;
interrupt-parent = <&
l
ic>;
#address-cells = <2>;
#size-cells = <2>;
...
...
@@ -173,6 +173,7 @@ gic: interrupt-controller@0,50041000 {
<0x0 0x50046000 0x0 0x2000>;
interrupts = <GIC_PPI 9
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
interrupt-parent = <&gic>;
};
gpu@0,57000000 {
...
...
@@ -190,6 +191,18 @@ gpu@0,57000000 {
status = "disabled";
};
lic: interrupt-controller@60004000 {
compatible = "nvidia,tegra124-ictlr", "nvidia,tegra30-ictlr";
reg = <0x0 0x60004000 0x0 0x100>,
<0x0 0x60004100 0x0 0x100>,
<0x0 0x60004200 0x0 0x100>,
<0x0 0x60004300 0x0 0x100>,
<0x0 0x60004400 0x0 0x100>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};
timer@0,60005000 {
compatible = "nvidia,tegra124-timer", "nvidia,tegra20-timer";
reg = <0x0 0x60005000 0x0 0x400>;
...
...
@@ -955,5 +968,6 @@ timer {
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
interrupt-parent = <&gic>;
};
};
arch/arm/boot/dts/tegra20.dtsi
View file @
37b25fff
...
...
@@ -7,7 +7,7 @@
/ {
compatible = "nvidia,tegra20";
interrupt-parent = <&
int
c>;
interrupt-parent = <&
li
c>;
host1x@50000000 {
compatible = "nvidia,tegra20-host1x", "simple-bus";
...
...
@@ -142,6 +142,7 @@ dsi@54300000 {
timer@50040600 {
compatible = "arm,cortex-a9-twd-timer";
interrupt-parent = <&intc>;
reg = <0x50040600 0x20>;
interrupts = <GIC_PPI 13
(GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
...
...
@@ -154,6 +155,7 @@ intc: interrupt-controller@50041000 {
0x50040100 0x0100>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&intc>;
};
cache-controller@50043000 {
...
...
@@ -165,6 +167,17 @@ cache-controller@50043000 {
cache-level = <2>;
};
lic: interrupt-controller@60004000 {
compatible = "nvidia,tegra20-ictlr";
reg = <0x60004000 0x100>,
<0x60004100 0x50>,
<0x60004200 0x50>,
<0x60004300 0x50>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&intc>;
};
timer@60005000 {
compatible = "nvidia,tegra20-timer";
reg = <0x60005000 0x60>;
...
...
arch/arm/boot/dts/tegra30.dtsi
View file @
37b25fff
...
...
@@ -8,7 +8,7 @@
/ {
compatible = "nvidia,tegra30";
interrupt-parent = <&
int
c>;
interrupt-parent = <&
li
c>;
pcie-controller@00003000 {
compatible = "nvidia,tegra30-pcie";
...
...
@@ -228,6 +228,7 @@ dsi@54300000 {
timer@50040600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x50040600 0x20>;
interrupt-parent = <&intc>;
interrupts = <GIC_PPI 13
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
clocks = <&tegra_car TEGRA30_CLK_TWD>;
...
...
@@ -239,6 +240,7 @@ intc: interrupt-controller@50041000 {
0x50040100 0x0100>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&intc>;
};
cache-controller@50043000 {
...
...
@@ -250,6 +252,18 @@ cache-controller@50043000 {
cache-level = <2>;
};
lic: interrupt-controller@60004000 {
compatible = "nvidia,tegra30-ictlr";
reg = <0x60004000 0x100>,
<0x60004100 0x50>,
<0x60004200 0x50>,
<0x60004300 0x50>,
<0x60004400 0x50>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&intc>;
};
timer@60005000 {
compatible = "nvidia,tegra30-timer", "nvidia,tegra20-timer";
reg = <0x60005000 0x400>;
...
...
arch/arm/mach-tegra/iomap.h
View file @
37b25fff
...
...
@@ -31,21 +31,6 @@
#define TEGRA_ARM_INT_DIST_BASE 0x50041000
#define TEGRA_ARM_INT_DIST_SIZE SZ_4K
#define TEGRA_PRIMARY_ICTLR_BASE 0x60004000
#define TEGRA_PRIMARY_ICTLR_SIZE SZ_64
#define TEGRA_SECONDARY_ICTLR_BASE 0x60004100
#define TEGRA_SECONDARY_ICTLR_SIZE SZ_64
#define TEGRA_TERTIARY_ICTLR_BASE 0x60004200
#define TEGRA_TERTIARY_ICTLR_SIZE SZ_64
#define TEGRA_QUATERNARY_ICTLR_BASE 0x60004300
#define TEGRA_QUATERNARY_ICTLR_SIZE SZ_64
#define TEGRA_QUINARY_ICTLR_BASE 0x60004400
#define TEGRA_QUINARY_ICTLR_SIZE SZ_64
#define TEGRA_TMR1_BASE 0x60005000
#define TEGRA_TMR1_SIZE SZ_8
...
...
arch/arm/mach-tegra/irq.c
View file @
37b25fff
...
...
@@ -30,43 +30,9 @@
#include "board.h"
#include "iomap.h"
#define ICTLR_CPU_IEP_VFIQ 0x08
#define ICTLR_CPU_IEP_FIR 0x14
#define ICTLR_CPU_IEP_FIR_SET 0x18
#define ICTLR_CPU_IEP_FIR_CLR 0x1c
#define ICTLR_CPU_IER 0x20
#define ICTLR_CPU_IER_SET 0x24
#define ICTLR_CPU_IER_CLR 0x28
#define ICTLR_CPU_IEP_CLASS 0x2C
#define ICTLR_COP_IER 0x30
#define ICTLR_COP_IER_SET 0x34
#define ICTLR_COP_IER_CLR 0x38
#define ICTLR_COP_IEP_CLASS 0x3c
#define FIRST_LEGACY_IRQ 32
#define TEGRA_MAX_NUM_ICTLRS 5
#define SGI_MASK 0xFFFF
static
int
num_ictlrs
;
static
void
__iomem
*
ictlr_reg_base
[]
=
{
IO_ADDRESS
(
TEGRA_PRIMARY_ICTLR_BASE
),
IO_ADDRESS
(
TEGRA_SECONDARY_ICTLR_BASE
),
IO_ADDRESS
(
TEGRA_TERTIARY_ICTLR_BASE
),
IO_ADDRESS
(
TEGRA_QUATERNARY_ICTLR_BASE
),
IO_ADDRESS
(
TEGRA_QUINARY_ICTLR_BASE
),
};
#ifdef CONFIG_PM_SLEEP
static
u32
cop_ier
[
TEGRA_MAX_NUM_ICTLRS
];
static
u32
cop_iep
[
TEGRA_MAX_NUM_ICTLRS
];
static
u32
cpu_ier
[
TEGRA_MAX_NUM_ICTLRS
];
static
u32
cpu_iep
[
TEGRA_MAX_NUM_ICTLRS
];
static
u32
ictlr_wake_mask
[
TEGRA_MAX_NUM_ICTLRS
];
static
void
__iomem
*
tegra_gic_cpu_base
;
#endif
...
...
@@ -83,140 +49,7 @@ bool tegra_pending_sgi(void)
return
false
;
}
static
inline
void
tegra_irq_write_mask
(
unsigned
int
irq
,
unsigned
long
reg
)
{
void
__iomem
*
base
;
u32
mask
;
BUG_ON
(
irq
<
FIRST_LEGACY_IRQ
||
irq
>=
FIRST_LEGACY_IRQ
+
num_ictlrs
*
32
);
base
=
ictlr_reg_base
[(
irq
-
FIRST_LEGACY_IRQ
)
/
32
];
mask
=
BIT
((
irq
-
FIRST_LEGACY_IRQ
)
%
32
);
__raw_writel
(
mask
,
base
+
reg
);
}
static
void
tegra_mask
(
struct
irq_data
*
d
)
{
if
(
d
->
hwirq
<
FIRST_LEGACY_IRQ
)
return
;
tegra_irq_write_mask
(
d
->
hwirq
,
ICTLR_CPU_IER_CLR
);
}
static
void
tegra_unmask
(
struct
irq_data
*
d
)
{
if
(
d
->
hwirq
<
FIRST_LEGACY_IRQ
)
return
;
tegra_irq_write_mask
(
d
->
hwirq
,
ICTLR_CPU_IER_SET
);
}
static
void
tegra_ack
(
struct
irq_data
*
d
)
{
if
(
d
->
hwirq
<
FIRST_LEGACY_IRQ
)
return
;
tegra_irq_write_mask
(
d
->
hwirq
,
ICTLR_CPU_IEP_FIR_CLR
);
}
static
void
tegra_eoi
(
struct
irq_data
*
d
)
{
if
(
d
->
hwirq
<
FIRST_LEGACY_IRQ
)
return
;
tegra_irq_write_mask
(
d
->
hwirq
,
ICTLR_CPU_IEP_FIR_CLR
);
}
static
int
tegra_retrigger
(
struct
irq_data
*
d
)
{
if
(
d
->
hwirq
<
FIRST_LEGACY_IRQ
)
return
0
;
tegra_irq_write_mask
(
d
->
hwirq
,
ICTLR_CPU_IEP_FIR_SET
);
return
1
;
}
#ifdef CONFIG_PM_SLEEP
static
int
tegra_set_wake
(
struct
irq_data
*
d
,
unsigned
int
enable
)
{
u32
irq
=
d
->
hwirq
;
u32
index
,
mask
;
if
(
irq
<
FIRST_LEGACY_IRQ
||
irq
>=
FIRST_LEGACY_IRQ
+
num_ictlrs
*
32
)
return
-
EINVAL
;
index
=
((
irq
-
FIRST_LEGACY_IRQ
)
/
32
);
mask
=
BIT
((
irq
-
FIRST_LEGACY_IRQ
)
%
32
);
if
(
enable
)
ictlr_wake_mask
[
index
]
|=
mask
;
else
ictlr_wake_mask
[
index
]
&=
~
mask
;
return
0
;
}
static
int
tegra_legacy_irq_suspend
(
void
)
{
unsigned
long
flags
;
int
i
;
local_irq_save
(
flags
);
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
{
void
__iomem
*
ictlr
=
ictlr_reg_base
[
i
];
/* Save interrupt state */
cpu_ier
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_CPU_IER
);
cpu_iep
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_CPU_IEP_CLASS
);
cop_ier
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_COP_IER
);
cop_iep
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_COP_IEP_CLASS
);
/* Disable COP interrupts */
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_COP_IER_CLR
);
/* Disable CPU interrupts */
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_CPU_IER_CLR
);
/* Enable the wakeup sources of ictlr */
writel_relaxed
(
ictlr_wake_mask
[
i
],
ictlr
+
ICTLR_CPU_IER_SET
);
}
local_irq_restore
(
flags
);
return
0
;
}
static
void
tegra_legacy_irq_resume
(
void
)
{
unsigned
long
flags
;
int
i
;
local_irq_save
(
flags
);
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
{
void
__iomem
*
ictlr
=
ictlr_reg_base
[
i
];
writel_relaxed
(
cpu_iep
[
i
],
ictlr
+
ICTLR_CPU_IEP_CLASS
);
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_CPU_IER_CLR
);
writel_relaxed
(
cpu_ier
[
i
],
ictlr
+
ICTLR_CPU_IER_SET
);
writel_relaxed
(
cop_iep
[
i
],
ictlr
+
ICTLR_COP_IEP_CLASS
);
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_COP_IER_CLR
);
writel_relaxed
(
cop_ier
[
i
],
ictlr
+
ICTLR_COP_IER_SET
);
}
local_irq_restore
(
flags
);
}
static
struct
syscore_ops
tegra_legacy_irq_syscore_ops
=
{
.
suspend
=
tegra_legacy_irq_suspend
,
.
resume
=
tegra_legacy_irq_resume
,
};
int
tegra_legacy_irq_syscore_init
(
void
)
{
register_syscore_ops
(
&
tegra_legacy_irq_syscore_ops
);
return
0
;
}
static
int
tegra_gic_notifier
(
struct
notifier_block
*
self
,
unsigned
long
cmd
,
void
*
v
)
{
...
...
@@ -251,45 +84,19 @@ static void tegra114_gic_cpu_pm_registration(void)
cpu_pm_register_notifier
(
&
tegra_gic_notifier_block
);
}
#else
#define tegra_set_wake NULL
static
void
tegra114_gic_cpu_pm_registration
(
void
)
{
}
#endif
static
const
struct
of_device_id
tegra_ictlr_match
[]
__initconst
=
{
{
.
compatible
=
"nvidia,tegra20-ictlr"
},
{
.
compatible
=
"nvidia,tegra30-ictlr"
},
{
}
};
void
__init
tegra_init_irq
(
void
)
{
int
i
;
void
__iomem
*
distbase
;
distbase
=
IO_ADDRESS
(
TEGRA_ARM_INT_DIST_BASE
);
num_ictlrs
=
readl_relaxed
(
distbase
+
GIC_DIST_CTR
)
&
0x1f
;
if
(
num_ictlrs
>
ARRAY_SIZE
(
ictlr_reg_base
))
{
WARN
(
1
,
"Too many (%d) interrupt controllers found. Maximum is %d."
,
num_ictlrs
,
ARRAY_SIZE
(
ictlr_reg_base
));
num_ictlrs
=
ARRAY_SIZE
(
ictlr_reg_base
);
}
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
{
void
__iomem
*
ictlr
=
ictlr_reg_base
[
i
];
writel
(
~
0
,
ictlr
+
ICTLR_CPU_IER_CLR
);
writel
(
0
,
ictlr
+
ICTLR_CPU_IEP_CLASS
);
}
gic_arch_extn
.
irq_ack
=
tegra_ack
;
gic_arch_extn
.
irq_eoi
=
tegra_eoi
;
gic_arch_extn
.
irq_mask
=
tegra_mask
;
gic_arch_extn
.
irq_unmask
=
tegra_unmask
;
gic_arch_extn
.
irq_retrigger
=
tegra_retrigger
;
gic_arch_extn
.
irq_set_wake
=
tegra_set_wake
;
gic_arch_extn
.
flags
=
IRQCHIP_MASK_ON_SUSPEND
;
/*
* Check if there is a devicetree present, since the GIC will be
* initialized elsewhere under DT.
*/
if
(
!
of_have_populated_dt
())
gic_init
(
0
,
29
,
distbase
,
IO_ADDRESS
(
TEGRA_ARM_PERIF_BASE
+
0x100
));
if
(
WARN_ON
(
!
of_find_matching_node
(
NULL
,
tegra_ictlr_match
)))
pr_warn
(
"Outdated DT detected, suspend/resume will NOT work
\n
"
);
tegra114_gic_cpu_pm_registration
();
}
arch/arm/mach-tegra/irq.h
View file @
37b25fff
...
...
@@ -19,10 +19,4 @@
bool
tegra_pending_sgi
(
void
);
#ifdef CONFIG_PM_SLEEP
int
tegra_legacy_irq_syscore_init
(
void
);
#else
static
inline
int
tegra_legacy_irq_syscore_init
(
void
)
{
return
0
;
}
#endif
#endif
arch/arm/mach-tegra/tegra.c
View file @
37b25fff
...
...
@@ -82,7 +82,6 @@ static void __init tegra_dt_init_irq(void)
{
tegra_init_irq
();
irqchip_init
();
tegra_legacy_irq_syscore_init
();
}
static
void
__init
tegra_dt_init
(
void
)
...
...
drivers/irqchip/Makefile
View file @
37b25fff
...
...
@@ -6,6 +6,7 @@ obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_MMP)
+=
irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU)
+=
irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS)
+=
irq-mxs.o
obj-$(CONFIG_ARCH_TEGRA)
+=
irq-tegra.o
obj-$(CONFIG_ARCH_S3C24XX)
+=
irq-s3c24xx.o
obj-$(CONFIG_DW_APB_ICTL)
+=
irq-dw-apb-ictl.o
obj-$(CONFIG_METAG)
+=
irq-metag-ext.o
...
...
drivers/irqchip/irq-tegra.c
0 → 100644
View file @
37b25fff
/*
* Driver code for Tegra's Legacy Interrupt Controller
*
* Author: Marc Zyngier <marc.zyngier@arm.com>
*
* Heavily based on the original arch/arm/mach-tegra/irq.c code:
* Copyright (C) 2011 Google, Inc.
*
* Author:
* Colin Cross <ccross@android.com>
*
* Copyright (C) 2010,2013, NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/syscore_ops.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "irqchip.h"
#define ICTLR_CPU_IEP_VFIQ 0x08
#define ICTLR_CPU_IEP_FIR 0x14
#define ICTLR_CPU_IEP_FIR_SET 0x18
#define ICTLR_CPU_IEP_FIR_CLR 0x1c
#define ICTLR_CPU_IER 0x20
#define ICTLR_CPU_IER_SET 0x24
#define ICTLR_CPU_IER_CLR 0x28
#define ICTLR_CPU_IEP_CLASS 0x2C
#define ICTLR_COP_IER 0x30
#define ICTLR_COP_IER_SET 0x34
#define ICTLR_COP_IER_CLR 0x38
#define ICTLR_COP_IEP_CLASS 0x3c
#define TEGRA_MAX_NUM_ICTLRS 6
static
unsigned
int
num_ictlrs
;
struct
tegra_ictlr_soc
{
unsigned
int
num_ictlrs
;
};
static
const
struct
tegra_ictlr_soc
tegra20_ictlr_soc
=
{
.
num_ictlrs
=
4
,
};
static
const
struct
tegra_ictlr_soc
tegra30_ictlr_soc
=
{
.
num_ictlrs
=
5
,
};
static
const
struct
tegra_ictlr_soc
tegra210_ictlr_soc
=
{
.
num_ictlrs
=
6
,
};
static
const
struct
of_device_id
ictlr_matches
[]
=
{
{
.
compatible
=
"nvidia,tegra210-ictlr"
,
.
data
=
&
tegra210_ictlr_soc
},
{
.
compatible
=
"nvidia,tegra30-ictlr"
,
.
data
=
&
tegra30_ictlr_soc
},
{
.
compatible
=
"nvidia,tegra20-ictlr"
,
.
data
=
&
tegra20_ictlr_soc
},
{
}
};
struct
tegra_ictlr_info
{
void
__iomem
*
base
[
TEGRA_MAX_NUM_ICTLRS
];
#ifdef CONFIG_PM_SLEEP
u32
cop_ier
[
TEGRA_MAX_NUM_ICTLRS
];
u32
cop_iep
[
TEGRA_MAX_NUM_ICTLRS
];
u32
cpu_ier
[
TEGRA_MAX_NUM_ICTLRS
];
u32
cpu_iep
[
TEGRA_MAX_NUM_ICTLRS
];
u32
ictlr_wake_mask
[
TEGRA_MAX_NUM_ICTLRS
];
#endif
};
static
struct
tegra_ictlr_info
*
lic
;
static
inline
void
tegra_ictlr_write_mask
(
struct
irq_data
*
d
,
unsigned
long
reg
)
{
void
__iomem
*
base
=
d
->
chip_data
;
u32
mask
;
mask
=
BIT
(
d
->
hwirq
%
32
);
writel_relaxed
(
mask
,
base
+
reg
);
}
static
void
tegra_mask
(
struct
irq_data
*
d
)
{
tegra_ictlr_write_mask
(
d
,
ICTLR_CPU_IER_CLR
);
irq_chip_mask_parent
(
d
);
}
static
void
tegra_unmask
(
struct
irq_data
*
d
)
{
tegra_ictlr_write_mask
(
d
,
ICTLR_CPU_IER_SET
);
irq_chip_unmask_parent
(
d
);
}
static
void
tegra_eoi
(
struct
irq_data
*
d
)
{
tegra_ictlr_write_mask
(
d
,
ICTLR_CPU_IEP_FIR_CLR
);
irq_chip_eoi_parent
(
d
);
}
static
int
tegra_retrigger
(
struct
irq_data
*
d
)
{
tegra_ictlr_write_mask
(
d
,
ICTLR_CPU_IEP_FIR_SET
);
return
irq_chip_retrigger_hierarchy
(
d
);
}
#ifdef CONFIG_PM_SLEEP
static
int
tegra_set_wake
(
struct
irq_data
*
d
,
unsigned
int
enable
)
{
u32
irq
=
d
->
hwirq
;
u32
index
,
mask
;
index
=
(
irq
/
32
);
mask
=
BIT
(
irq
%
32
);
if
(
enable
)
lic
->
ictlr_wake_mask
[
index
]
|=
mask
;
else
lic
->
ictlr_wake_mask
[
index
]
&=
~
mask
;
/*
* Do *not* call into the parent, as the GIC doesn't have any
* wake-up facility...
*/
return
0
;
}
static
int
tegra_ictlr_suspend
(
void
)
{
unsigned
long
flags
;
unsigned
int
i
;
local_irq_save
(
flags
);
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
{
void
__iomem
*
ictlr
=
lic
->
base
[
i
];
/* Save interrupt state */
lic
->
cpu_ier
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_CPU_IER
);
lic
->
cpu_iep
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_CPU_IEP_CLASS
);
lic
->
cop_ier
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_COP_IER
);
lic
->
cop_iep
[
i
]
=
readl_relaxed
(
ictlr
+
ICTLR_COP_IEP_CLASS
);
/* Disable COP interrupts */
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_COP_IER_CLR
);
/* Disable CPU interrupts */
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_CPU_IER_CLR
);
/* Enable the wakeup sources of ictlr */
writel_relaxed
(
lic
->
ictlr_wake_mask
[
i
],
ictlr
+
ICTLR_CPU_IER_SET
);
}
local_irq_restore
(
flags
);
return
0
;
}
static
void
tegra_ictlr_resume
(
void
)
{
unsigned
long
flags
;
unsigned
int
i
;
local_irq_save
(
flags
);
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
{
void
__iomem
*
ictlr
=
lic
->
base
[
i
];
writel_relaxed
(
lic
->
cpu_iep
[
i
],
ictlr
+
ICTLR_CPU_IEP_CLASS
);
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_CPU_IER_CLR
);
writel_relaxed
(
lic
->
cpu_ier
[
i
],
ictlr
+
ICTLR_CPU_IER_SET
);
writel_relaxed
(
lic
->
cop_iep
[
i
],
ictlr
+
ICTLR_COP_IEP_CLASS
);
writel_relaxed
(
~
0ul
,
ictlr
+
ICTLR_COP_IER_CLR
);
writel_relaxed
(
lic
->
cop_ier
[
i
],
ictlr
+
ICTLR_COP_IER_SET
);
}
local_irq_restore
(
flags
);
}
static
struct
syscore_ops
tegra_ictlr_syscore_ops
=
{
.
suspend
=
tegra_ictlr_suspend
,
.
resume
=
tegra_ictlr_resume
,
};
static
void
tegra_ictlr_syscore_init
(
void
)
{
register_syscore_ops
(
&
tegra_ictlr_syscore_ops
);
}
#else
#define tegra_set_wake NULL
static
inline
void
tegra_ictlr_syscore_init
(
void
)
{}
#endif
static
struct
irq_chip
tegra_ictlr_chip
=
{
.
name
=
"LIC"
,
.
irq_eoi
=
tegra_eoi
,
.
irq_mask
=
tegra_mask
,
.
irq_unmask
=
tegra_unmask
,
.
irq_retrigger
=
tegra_retrigger
,
.
irq_set_wake
=
tegra_set_wake
,
.
flags
=
IRQCHIP_MASK_ON_SUSPEND
,
#ifdef CONFIG_SMP
.
irq_set_affinity
=
irq_chip_set_affinity_parent
,
#endif
};
static
int
tegra_ictlr_domain_xlate
(
struct
irq_domain
*
domain
,
struct
device_node
*
controller
,
const
u32
*
intspec
,
unsigned
int
intsize
,
unsigned
long
*
out_hwirq
,
unsigned
int
*
out_type
)
{
if
(
domain
->
of_node
!=
controller
)
return
-
EINVAL
;
/* Shouldn't happen, really... */
if
(
intsize
!=
3
)
return
-
EINVAL
;
/* Not GIC compliant */
if
(
intspec
[
0
]
!=
GIC_SPI
)
return
-
EINVAL
;
/* No PPI should point to this domain */
*
out_hwirq
=
intspec
[
1
];
*
out_type
=
intspec
[
2
];
return
0
;
}
static
int
tegra_ictlr_domain_alloc
(
struct
irq_domain
*
domain
,
unsigned
int
virq
,
unsigned
int
nr_irqs
,
void
*
data
)
{
struct
of_phandle_args
*
args
=
data
;
struct
of_phandle_args
parent_args
;
struct
tegra_ictlr_info
*
info
=
domain
->
host_data
;
irq_hw_number_t
hwirq
;
unsigned
int
i
;
if
(
args
->
args_count
!=
3
)
return
-
EINVAL
;
/* Not GIC compliant */
if
(
args
->
args
[
0
]
!=
GIC_SPI
)
return
-
EINVAL
;
/* No PPI should point to this domain */
hwirq
=
args
->
args
[
1
];
if
(
hwirq
>=
(
num_ictlrs
*
32
))
return
-
EINVAL
;
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
{
int
ictlr
=
(
hwirq
+
i
)
/
32
;
irq_domain_set_hwirq_and_chip
(
domain
,
virq
+
i
,
hwirq
+
i
,
&
tegra_ictlr_chip
,
&
info
->
base
[
ictlr
]);
}
parent_args
=
*
args
;
parent_args
.
np
=
domain
->
parent
->
of_node
;
return
irq_domain_alloc_irqs_parent
(
domain
,
virq
,
nr_irqs
,
&
parent_args
);
}
static
void
tegra_ictlr_domain_free
(
struct
irq_domain
*
domain
,
unsigned
int
virq
,
unsigned
int
nr_irqs
)
{
unsigned
int
i
;
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
{
struct
irq_data
*
d
=
irq_domain_get_irq_data
(
domain
,
virq
+
i
);
irq_domain_reset_irq_data
(
d
);
}
}
static
const
struct
irq_domain_ops
tegra_ictlr_domain_ops
=
{
.
xlate
=
tegra_ictlr_domain_xlate
,
.
alloc
=
tegra_ictlr_domain_alloc
,
.
free
=
tegra_ictlr_domain_free
,
};
static
int
__init
tegra_ictlr_init
(
struct
device_node
*
node
,
struct
device_node
*
parent
)
{
struct
irq_domain
*
parent_domain
,
*
domain
;
const
struct
of_device_id
*
match
;
const
struct
tegra_ictlr_soc
*
soc
;
unsigned
int
i
;
int
err
;
if
(
!
parent
)
{
pr_err
(
"%s: no parent, giving up
\n
"
,
node
->
full_name
);
return
-
ENODEV
;
}
parent_domain
=
irq_find_host
(
parent
);
if
(
!
parent_domain
)
{
pr_err
(
"%s: unable to obtain parent domain
\n
"
,
node
->
full_name
);
return
-
ENXIO
;
}
match
=
of_match_node
(
ictlr_matches
,
node
);
if
(
!
match
)
/* Should never happen... */
return
-
ENODEV
;
soc
=
match
->
data
;
lic
=
kzalloc
(
sizeof
(
*
lic
),
GFP_KERNEL
);
if
(
!
lic
)
return
-
ENOMEM
;
for
(
i
=
0
;
i
<
TEGRA_MAX_NUM_ICTLRS
;
i
++
)
{
void
__iomem
*
base
;
base
=
of_iomap
(
node
,
i
);
if
(
!
base
)
break
;
lic
->
base
[
i
]
=
base
;
/* Disable all interrupts */
writel_relaxed
(
~
0UL
,
base
+
ICTLR_CPU_IER_CLR
);
/* All interrupts target IRQ */
writel_relaxed
(
0
,
base
+
ICTLR_CPU_IEP_CLASS
);
num_ictlrs
++
;
}
if
(
!
num_ictlrs
)
{
pr_err
(
"%s: no valid regions, giving up
\n
"
,
node
->
full_name
);
err
=
-
ENOMEM
;
goto
out_free
;
}
WARN
(
num_ictlrs
!=
soc
->
num_ictlrs
,
"%s: Found %u interrupt controllers in DT; expected %u.
\n
"
,
node
->
full_name
,
num_ictlrs
,
soc
->
num_ictlrs
);
domain
=
irq_domain_add_hierarchy
(
parent_domain
,
0
,
num_ictlrs
*
32
,
node
,
&
tegra_ictlr_domain_ops
,
lic
);
if
(
!
domain
)
{
pr_err
(
"%s: failed to allocated domain
\n
"
,
node
->
full_name
);
err
=
-
ENOMEM
;
goto
out_unmap
;
}
tegra_ictlr_syscore_init
();
pr_info
(
"%s: %d interrupts forwarded to %s
\n
"
,
node
->
full_name
,
num_ictlrs
*
32
,
parent
->
full_name
);
return
0
;
out_unmap:
for
(
i
=
0
;
i
<
num_ictlrs
;
i
++
)
iounmap
(
lic
->
base
[
i
]);
out_free:
kfree
(
lic
);
return
err
;
}
IRQCHIP_DECLARE
(
tegra20_ictlr
,
"nvidia,tegra20-ictlr"
,
tegra_ictlr_init
);
IRQCHIP_DECLARE
(
tegra30_ictlr
,
"nvidia,tegra30-ictlr"
,
tegra_ictlr_init
);
IRQCHIP_DECLARE
(
tegra210_ictlr
,
"nvidia,tegra210-ictlr"
,
tegra_ictlr_init
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment