Merge branch 'irqchip/stacked-exynos' into irqchip/core
This commit is contained in:
commit
fb414e908b
|
@ -29,10 +29,27 @@ Properties:
|
||||||
- clocks : list of phandles and specifiers to all input clocks listed in
|
- clocks : list of phandles and specifiers to all input clocks listed in
|
||||||
clock-names property.
|
clock-names property.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
Some PMUs are capable of behaving as an interrupt controller (mostly
|
||||||
|
to wake up a suspended PMU). In which case, they can have the
|
||||||
|
following properties:
|
||||||
|
|
||||||
|
- interrupt-controller: indicate that said PMU is an interrupt controller
|
||||||
|
|
||||||
|
- #interrupt-cells: must be identical to the that of the parent interrupt
|
||||||
|
controller.
|
||||||
|
|
||||||
|
- interrupt-parent: a phandle indicating which interrupt controller
|
||||||
|
this PMU signals interrupts to.
|
||||||
|
|
||||||
Example :
|
Example :
|
||||||
pmu_system_controller: system-controller@10040000 {
|
pmu_system_controller: system-controller@10040000 {
|
||||||
compatible = "samsung,exynos5250-pmu", "syscon";
|
compatible = "samsung,exynos5250-pmu", "syscon";
|
||||||
reg = <0x10040000 0x5000>;
|
reg = <0x10040000 0x5000>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
#clock-cells = <1>;
|
#clock-cells = <1>;
|
||||||
clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
|
clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
|
||||||
"clkout4", "clkout8", "clkout9";
|
"clkout4", "clkout8", "clkout9";
|
||||||
|
|
|
@ -130,6 +130,9 @@
|
||||||
pmu_system_controller: system-controller@10020000 {
|
pmu_system_controller: system-controller@10020000 {
|
||||||
compatible = "samsung,exynos3250-pmu", "syscon";
|
compatible = "samsung,exynos3250-pmu", "syscon";
|
||||||
reg = <0x10020000 0x4000>;
|
reg = <0x10020000 0x4000>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
};
|
};
|
||||||
|
|
||||||
mipi_phy: video-phy@10020710 {
|
mipi_phy: video-phy@10020710 {
|
||||||
|
@ -184,6 +187,7 @@
|
||||||
compatible = "samsung,exynos3250-rtc";
|
compatible = "samsung,exynos3250-rtc";
|
||||||
reg = <0x10070000 0x100>;
|
reg = <0x10070000 0x100>;
|
||||||
interrupts = <0 73 0>, <0 74 0>;
|
interrupts = <0 73 0>, <0 74 0>;
|
||||||
|
interrupt-parent = <&pmu_system_controller>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,9 @@
|
||||||
pmu_system_controller: system-controller@10020000 {
|
pmu_system_controller: system-controller@10020000 {
|
||||||
compatible = "samsung,exynos4210-pmu", "syscon";
|
compatible = "samsung,exynos4210-pmu", "syscon";
|
||||||
reg = <0x10020000 0x4000>;
|
reg = <0x10020000 0x4000>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
};
|
};
|
||||||
|
|
||||||
dsi_0: dsi@11C80000 {
|
dsi_0: dsi@11C80000 {
|
||||||
|
@ -264,6 +267,7 @@
|
||||||
rtc@10070000 {
|
rtc@10070000 {
|
||||||
compatible = "samsung,s3c6410-rtc";
|
compatible = "samsung,s3c6410-rtc";
|
||||||
reg = <0x10070000 0x100>;
|
reg = <0x10070000 0x100>;
|
||||||
|
interrupt-parent = <&pmu_system_controller>;
|
||||||
interrupts = <0 44 0>, <0 45 0>;
|
interrupts = <0 44 0>, <0 45 0>;
|
||||||
clocks = <&clock CLK_RTC>;
|
clocks = <&clock CLK_RTC>;
|
||||||
clock-names = "rtc";
|
clock-names = "rtc";
|
||||||
|
|
|
@ -196,6 +196,9 @@
|
||||||
clock-names = "clkout16";
|
clock-names = "clkout16";
|
||||||
clocks = <&clock CLK_FIN_PLL>;
|
clocks = <&clock CLK_FIN_PLL>;
|
||||||
#clock-cells = <1>;
|
#clock-cells = <1>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
};
|
};
|
||||||
|
|
||||||
sysreg_system_controller: syscon@10050000 {
|
sysreg_system_controller: syscon@10050000 {
|
||||||
|
@ -232,6 +235,7 @@
|
||||||
rtc: rtc@101E0000 {
|
rtc: rtc@101E0000 {
|
||||||
clocks = <&clock CLK_RTC>;
|
clocks = <&clock CLK_RTC>;
|
||||||
clock-names = "rtc";
|
clock-names = "rtc";
|
||||||
|
interrupt-parent = <&pmu_system_controller>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -327,6 +327,7 @@
|
||||||
rtc: rtc@101E0000 {
|
rtc: rtc@101E0000 {
|
||||||
clocks = <&clock CLK_RTC>;
|
clocks = <&clock CLK_RTC>;
|
||||||
clock-names = "rtc";
|
clock-names = "rtc";
|
||||||
|
interrupt-parent = <&pmu_system_controller>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -769,6 +770,9 @@
|
||||||
clock-names = "clkout16";
|
clock-names = "clkout16";
|
||||||
clocks = <&clock CLK_FIN_PLL>;
|
clocks = <&clock CLK_FIN_PLL>;
|
||||||
#clock-cells = <1>;
|
#clock-cells = <1>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
};
|
};
|
||||||
|
|
||||||
sysreg_system_controller: syscon@10050000 {
|
sysreg_system_controller: syscon@10050000 {
|
||||||
|
|
|
@ -166,16 +166,14 @@ static void __init exynos_init_io(void)
|
||||||
exynos_map_io();
|
exynos_map_io();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apparently, these SoCs are not able to wake-up from suspend using
|
||||||
|
* the PMU. Too bad. Should they suddenly become capable of such a
|
||||||
|
* feat, the matches below should be moved to suspend.c.
|
||||||
|
*/
|
||||||
static const struct of_device_id exynos_dt_pmu_match[] = {
|
static const struct of_device_id exynos_dt_pmu_match[] = {
|
||||||
{ .compatible = "samsung,exynos3250-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos4210-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos4212-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos4412-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos4415-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos5250-pmu" },
|
|
||||||
{ .compatible = "samsung,exynos5260-pmu" },
|
{ .compatible = "samsung,exynos5260-pmu" },
|
||||||
{ .compatible = "samsung,exynos5410-pmu" },
|
{ .compatible = "samsung,exynos5410-pmu" },
|
||||||
{ .compatible = "samsung,exynos5420-pmu" },
|
|
||||||
{ /*sentinel*/ },
|
{ /*sentinel*/ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -186,9 +184,6 @@ static void exynos_map_pmu(void)
|
||||||
np = of_find_matching_node(NULL, exynos_dt_pmu_match);
|
np = of_find_matching_node(NULL, exynos_dt_pmu_match);
|
||||||
if (np)
|
if (np)
|
||||||
pmu_base_addr = of_iomap(np, 0);
|
pmu_base_addr = of_iomap(np, 0);
|
||||||
|
|
||||||
if (!pmu_base_addr)
|
|
||||||
panic("failed to find exynos pmu register\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init exynos_init_irq(void)
|
static void __init exynos_init_irq(void)
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
#include <linux/syscore_ops.h>
|
#include <linux/syscore_ops.h>
|
||||||
#include <linux/cpu_pm.h>
|
#include <linux/cpu_pm.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irqchip/arm-gic.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
|
|
||||||
|
@ -43,8 +45,8 @@
|
||||||
#define EXYNOS5420_CPU_STATE 0x28
|
#define EXYNOS5420_CPU_STATE 0x28
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
|
* struct exynos_wkup_irq - PMU IRQ to mask mapping
|
||||||
* @hwirq: Hardware IRQ signal of the GIC
|
* @hwirq: Hardware IRQ signal of the PMU
|
||||||
* @mask: Mask in PMU wake-up mask register
|
* @mask: Mask in PMU wake-up mask register
|
||||||
*/
|
*/
|
||||||
struct exynos_wkup_irq {
|
struct exynos_wkup_irq {
|
||||||
|
@ -93,14 +95,14 @@ static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
|
static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
|
||||||
{ 76, BIT(1) }, /* RTC alarm */
|
{ 44, BIT(1) }, /* RTC alarm */
|
||||||
{ 77, BIT(2) }, /* RTC tick */
|
{ 45, BIT(2) }, /* RTC tick */
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
|
static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
|
||||||
{ 75, BIT(1) }, /* RTC alarm */
|
{ 43, BIT(1) }, /* RTC alarm */
|
||||||
{ 76, BIT(2) }, /* RTC tick */
|
{ 44, BIT(2) }, /* RTC tick */
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,6 +169,113 @@ static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct irq_chip exynos_pmu_chip = {
|
||||||
|
.name = "PMU",
|
||||||
|
.irq_eoi = irq_chip_eoi_parent,
|
||||||
|
.irq_mask = irq_chip_mask_parent,
|
||||||
|
.irq_unmask = irq_chip_unmask_parent,
|
||||||
|
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||||
|
.irq_set_wake = exynos_irq_set_wake,
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static int exynos_pmu_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] != 0)
|
||||||
|
return -EINVAL; /* No PPI should point to this domain */
|
||||||
|
|
||||||
|
*out_hwirq = intspec[1];
|
||||||
|
*out_type = intspec[2];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_pmu_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;
|
||||||
|
irq_hw_number_t hwirq;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (args->args_count != 3)
|
||||||
|
return -EINVAL; /* Not GIC compliant */
|
||||||
|
if (args->args[0] != 0)
|
||||||
|
return -EINVAL; /* No PPI should point to this domain */
|
||||||
|
|
||||||
|
hwirq = args->args[1];
|
||||||
|
|
||||||
|
for (i = 0; i < nr_irqs; i++)
|
||||||
|
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
|
||||||
|
&exynos_pmu_chip, NULL);
|
||||||
|
|
||||||
|
parent_args = *args;
|
||||||
|
parent_args.np = domain->parent->of_node;
|
||||||
|
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_domain_ops exynos_pmu_domain_ops = {
|
||||||
|
.xlate = exynos_pmu_domain_xlate,
|
||||||
|
.alloc = exynos_pmu_domain_alloc,
|
||||||
|
.free = irq_domain_free_irqs_common,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init exynos_pmu_irq_init(struct device_node *node,
|
||||||
|
struct device_node *parent)
|
||||||
|
{
|
||||||
|
struct irq_domain *parent_domain, *domain;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmu_base_addr = of_iomap(node, 0);
|
||||||
|
|
||||||
|
if (!pmu_base_addr) {
|
||||||
|
pr_err("%s: failed to find exynos pmu register\n",
|
||||||
|
node->full_name);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = irq_domain_add_hierarchy(parent_domain, 0, 0,
|
||||||
|
node, &exynos_pmu_domain_ops,
|
||||||
|
NULL);
|
||||||
|
if (!domain) {
|
||||||
|
iounmap(pmu_base_addr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXYNOS_PMU_IRQ(symbol, name) OF_DECLARE_2(irqchip, symbol, name, exynos_pmu_irq_init)
|
||||||
|
|
||||||
|
EXYNOS_PMU_IRQ(exynos3250_pmu_irq, "samsung,exynos3250-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos4210_pmu_irq, "samsung,exynos4210-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos4212_pmu_irq, "samsung,exynos4212-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos4412_pmu_irq, "samsung,exynos4412-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos4415_pmu_irq, "samsung,exynos4415-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos5250_pmu_irq, "samsung,exynos5250-pmu");
|
||||||
|
EXYNOS_PMU_IRQ(exynos5420_pmu_irq, "samsung,exynos5420-pmu");
|
||||||
|
|
||||||
static int exynos_cpu_do_idle(void)
|
static int exynos_cpu_do_idle(void)
|
||||||
{
|
{
|
||||||
/* issue the standby signal into the pm unit. */
|
/* issue the standby signal into the pm unit. */
|
||||||
|
@ -615,17 +724,19 @@ static struct syscore_ops exynos_pm_syscore_ops;
|
||||||
void __init exynos_pm_init(void)
|
void __init exynos_pm_init(void)
|
||||||
{
|
{
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
struct device_node *np;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
|
np = of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
|
||||||
if (!match) {
|
if (!np) {
|
||||||
pr_err("Failed to find PMU node\n");
|
pr_err("Failed to find PMU node\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pm_data = (struct exynos_pm_data *) match->data;
|
|
||||||
|
|
||||||
/* Platform-specific GIC callback */
|
if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL)))
|
||||||
gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
|
pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
|
||||||
|
|
||||||
|
pm_data = (struct exynos_pm_data *) match->data;
|
||||||
|
|
||||||
/* All wakeup disable */
|
/* All wakeup disable */
|
||||||
tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
|
tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
|
||||||
|
|
Loading…
Reference in a new issue