Commit 425b655c authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'irqchip-core-4.1-2' of git://git.infradead.org/users/jcooper/linux into irq/core

irqchip core changes for v4.1 (round 2) from Jason Cooper

 - gic
    - Tolerate uni-processor systems better in gic_get_cpumask()

 - mvebu
    - Handle per-cpu interrupts properly
    - Enable PMU interrupts
    - Enable wakeup source

 - vybrid
    - Add MSCM interrupt router

 - renesas
    - Add PM and wakeup support
parents fdb7144b 78223354
Freescale Vybrid Miscellaneous System Control - CPU Configuration
The MSCM IP contains multiple sub modules, this binding describes the first
block of registers which contains CPU configuration information.
Required properties:
- compatible: "fsl,vf610-mscm-cpucfg", "syscon"
- reg: the register range of the MSCM CPU configuration registers
Example:
mscm_cpucfg: cpucfg@40001000 {
compatible = "fsl,vf610-mscm-cpucfg", "syscon";
reg = <0x40001000 0x800>;
}
Freescale Vybrid Miscellaneous System Control - Interrupt Router
The MSCM IP contains multiple sub modules, this binding describes the second
block of registers which control the interrupt router. The interrupt router
allows to configure the recipient of each peripheral interrupt. Furthermore
it controls the directed processor interrupts. The module is available in all
Vybrid SoC's but is only really useful in dual core configurations (VF6xx
which comes with a Cortex-A5/Cortex-M4 combination).
Required properties:
- compatible: "fsl,vf610-mscm-ir"
- reg: the register range of the MSCM Interrupt Router
- fsl,cpucfg: The handle to the MSCM CPU configuration node, required
to get the current CPU ID
- interrupt-controller: Identifies the node as an interrupt controller
- #interrupt-cells: Two cells, interrupt number and cells.
The hardware interrupt number according to interrupt
assignment of the interrupt router is required.
Flags get passed only when using GIC as parent. Flags
encoding as documented by the GIC bindings.
- interrupt-parent: Should be the phandle for the interrupt controller of
the CPU the device tree is intended to be used on. This
is either the node of the GIC or NVIC controller.
Example:
mscm_ir: interrupt-controller@40001800 {
compatible = "fsl,vf610-mscm-ir";
reg = <0x40001800 0x400>;
fsl,cpucfg = <&mscm_cpucfg>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&intc>;
}
...@@ -4,7 +4,7 @@ Required properties: ...@@ -4,7 +4,7 @@ Required properties:
- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback. - compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback.
Examples with soctypes are: Examples with soctypes are:
- "renesas,irqc-r8a73a4" (R-Mobile AP6) - "renesas,irqc-r8a73a4" (R-Mobile APE6)
- "renesas,irqc-r8a7790" (R-Car H2) - "renesas,irqc-r8a7790" (R-Car H2)
- "renesas,irqc-r8a7791" (R-Car M2-W) - "renesas,irqc-r8a7791" (R-Car M2-W)
- "renesas,irqc-r8a7792" (R-Car V2H) - "renesas,irqc-r8a7792" (R-Car V2H)
...@@ -12,6 +12,7 @@ Required properties: ...@@ -12,6 +12,7 @@ Required properties:
- "renesas,irqc-r8a7794" (R-Car E2) - "renesas,irqc-r8a7794" (R-Car E2)
- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
interrupts.txt in this directory interrupts.txt in this directory
- clocks: Must contain a reference to the functional clock.
Optional properties: Optional properties:
...@@ -29,4 +30,5 @@ Example: ...@@ -29,4 +30,5 @@ Example:
<0 1 IRQ_TYPE_LEVEL_HIGH>, <0 1 IRQ_TYPE_LEVEL_HIGH>,
<0 2 IRQ_TYPE_LEVEL_HIGH>, <0 2 IRQ_TYPE_LEVEL_HIGH>,
<0 3 IRQ_TYPE_LEVEL_HIGH>; <0 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp4_clks R8A7790_CLK_IRQC>;
}; };
...@@ -631,6 +631,7 @@ config SOC_IMX6SX ...@@ -631,6 +631,7 @@ config SOC_IMX6SX
config SOC_VF610 config SOC_VF610
bool "Vybrid Family VF610 support" bool "Vybrid Family VF610 support"
select IRQ_DOMAIN_HIERARCHY
select ARM_GIC select ARM_GIC
select PINCTRL_VF610 select PINCTRL_VF610
select PL310_ERRATA_769419 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0
......
...@@ -38,6 +38,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o ...@@ -38,6 +38,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o
obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
/* Interrupt Controller Registers Map */ /* Interrupt Controller Registers Map */
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
#define ARMADA_370_XP_INT_CONTROL (0x00) #define ARMADA_370_XP_INT_CONTROL (0x00)
#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
...@@ -56,6 +58,7 @@ ...@@ -56,6 +58,7 @@
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
#define ARMADA_370_XP_FABRIC_IRQ (3)
#define IPI_DOORBELL_START (0) #define IPI_DOORBELL_START (0)
#define IPI_DOORBELL_END (8) #define IPI_DOORBELL_END (8)
...@@ -77,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock); ...@@ -77,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock);
static phys_addr_t msi_doorbell_addr; static phys_addr_t msi_doorbell_addr;
#endif #endif
static inline bool is_percpu_irq(irq_hw_number_t irq)
{
switch (irq) {
case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
case ARMADA_370_XP_FABRIC_IRQ:
return true;
default:
return false;
}
}
/* /*
* In SMP mode: * In SMP mode:
* For shared global interrupts, mask/unmask global enable bit * For shared global interrupts, mask/unmask global enable bit
...@@ -86,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d) ...@@ -86,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d)
{ {
irq_hw_number_t hwirq = irqd_to_hwirq(d); irq_hw_number_t hwirq = irqd_to_hwirq(d);
if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) if (!is_percpu_irq(hwirq))
writel(hwirq, main_int_base + writel(hwirq, main_int_base +
ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
else else
...@@ -98,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d) ...@@ -98,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
{ {
irq_hw_number_t hwirq = irqd_to_hwirq(d); irq_hw_number_t hwirq = irqd_to_hwirq(d);
if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) if (!is_percpu_irq(hwirq))
writel(hwirq, main_int_base + writel(hwirq, main_int_base +
ARMADA_370_XP_INT_SET_ENABLE_OFFS); ARMADA_370_XP_INT_SET_ENABLE_OFFS);
else else
...@@ -281,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = { ...@@ -281,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
.irq_set_affinity = armada_xp_set_affinity, .irq_set_affinity = armada_xp_set_affinity,
#endif #endif
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
}; };
static int armada_370_xp_mpic_irq_map(struct irq_domain *h, static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
unsigned int virq, irq_hw_number_t hw) unsigned int virq, irq_hw_number_t hw)
{ {
armada_370_xp_irq_mask(irq_get_irq_data(virq)); armada_370_xp_irq_mask(irq_get_irq_data(virq));
if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) if (!is_percpu_irq(hw))
writel(hw, per_cpu_int_base + writel(hw, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
else else
writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
irq_set_status_flags(virq, IRQ_LEVEL); irq_set_status_flags(virq, IRQ_LEVEL);
if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { if (is_percpu_irq(hw)) {
irq_set_percpu_devid(virq); irq_set_percpu_devid(virq);
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_percpu_devid_irq); handle_percpu_devid_irq);
...@@ -308,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, ...@@ -308,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
return 0; return 0;
} }
#ifdef CONFIG_SMP
static void armada_mpic_send_doorbell(const struct cpumask *mask,
unsigned int irq)
{
int cpu;
unsigned long map = 0;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= 1 << cpu_logical_map(cpu);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb();
/* submit softirq */
writel((map << 8) | irq, main_int_base +
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
static void armada_xp_mpic_smp_cpu_init(void) static void armada_xp_mpic_smp_cpu_init(void)
{ {
u32 control; u32 control;
...@@ -352,11 +345,44 @@ static void armada_xp_mpic_smp_cpu_init(void) ...@@ -352,11 +345,44 @@ static void armada_xp_mpic_smp_cpu_init(void)
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
} }
static void armada_xp_mpic_perf_init(void)
{
unsigned long cpuid = cpu_logical_map(smp_processor_id());
/* Enable Performance Counter Overflow interrupts */
writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
}
#ifdef CONFIG_SMP
static void armada_mpic_send_doorbell(const struct cpumask *mask,
unsigned int irq)
{
int cpu;
unsigned long map = 0;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= 1 << cpu_logical_map(cpu);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb();
/* submit softirq */
writel((map << 8) | irq, main_int_base +
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, static int armada_xp_mpic_secondary_init(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
armada_xp_mpic_perf_init();
armada_xp_mpic_smp_cpu_init(); armada_xp_mpic_smp_cpu_init();
}
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -369,8 +395,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = { ...@@ -369,8 +395,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = {
static int mpic_cascaded_secondary_init(struct notifier_block *nfb, static int mpic_cascaded_secondary_init(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
armada_xp_mpic_perf_init();
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
}
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -379,7 +407,6 @@ static struct notifier_block mpic_cascaded_cpu_notifier = { ...@@ -379,7 +407,6 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
.notifier_call = mpic_cascaded_secondary_init, .notifier_call = mpic_cascaded_secondary_init,
.priority = 100, .priority = 100,
}; };
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
...@@ -588,9 +615,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, ...@@ -588,9 +615,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
BUG_ON(!armada_370_xp_mpic_domain); BUG_ON(!armada_370_xp_mpic_domain);
#ifdef CONFIG_SMP /* Setup for the boot CPU */
armada_xp_mpic_perf_init();
armada_xp_mpic_smp_cpu_init(); armada_xp_mpic_smp_cpu_init();
#endif
armada_370_xp_msi_init(node, main_int_res.start); armada_370_xp_msi_init(node, main_int_res.start);
......
...@@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs) ...@@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs)
} while (1); } while (1);
} }
static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
unsigned en_reg, unsigned ack_reg) unsigned en_reg, unsigned ack_reg)
{ {
struct irq_chip_generic *gc; struct irq_chip_generic *gc;
......
...@@ -414,7 +414,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) ...@@ -414,7 +414,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
break; break;
} }
if (!mask) if (!mask && num_possible_cpus() > 1)
pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
return mask; return mask;
......
...@@ -166,6 +166,27 @@ cycle_t gic_read_compare(void) ...@@ -166,6 +166,27 @@ cycle_t gic_read_compare(void)
return (((cycle_t) hi) << 32) + lo; return (((cycle_t) hi) << 32) + lo;
} }
void gic_start_count(void)
{
u32 gicconfig;
/* Start the counter */
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF);
gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
}
void gic_stop_count(void)
{
u32 gicconfig;
/* Stop the counter */
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF;
gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
}
#endif #endif
static bool gic_local_irq_is_routable(int intr) static bool gic_local_irq_is_routable(int intr)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <linux/clk.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -29,15 +30,26 @@ ...@@ -29,15 +30,26 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/irq-renesas-irqc.h> #include <linux/platform_data/irq-renesas-irqc.h>
#include <linux/pm_runtime.h>
#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ #define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
#define IRQC_REQ_STS 0x00 #define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */
#define IRQC_EN_STS 0x04 #define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */
#define IRQC_EN_SET 0x08 #define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */
#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
#define DETECT_STATUS 0x100 /* SYS-CPU vs. RT-CPU */
#define DETECT_STATUS 0x100 /* IRQn Detect Status Register */
#define MONITOR 0x104 /* IRQn Signal Level Monitor Register */
#define HLVL_STS 0x108 /* IRQn High Level Detect Status Register */
#define LLVL_STS 0x10c /* IRQn Low Level Detect Status Register */
#define S_R_EDGE_STS 0x110 /* IRQn Sync Rising Edge Detect Status Reg. */
#define S_F_EDGE_STS 0x114 /* IRQn Sync Falling Edge Detect Status Reg. */
#define A_R_EDGE_STS 0x118 /* IRQn Async Rising Edge Detect Status Reg. */
#define A_F_EDGE_STS 0x11c /* IRQn Async Falling Edge Detect Status Reg. */
#define CHTEN_STS 0x120 /* Chattering Reduction Status Register */
#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
/* IRQn Configuration Register */
struct irqc_irq { struct irqc_irq {
int hw_irq; int hw_irq;
...@@ -55,6 +67,7 @@ struct irqc_priv { ...@@ -55,6 +67,7 @@ struct irqc_priv {
struct platform_device *pdev; struct platform_device *pdev;
struct irq_chip irq_chip; struct irq_chip irq_chip;
struct irq_domain *irq_domain; struct irq_domain *irq_domain;
struct clk *clk;
}; };
static void irqc_dbg(struct irqc_irq *i, char *str) static void irqc_dbg(struct irqc_irq *i, char *str)
...@@ -108,6 +121,21 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) ...@@ -108,6 +121,21 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
return 0; return 0;
} }
static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct irqc_priv *p = irq_data_get_irq_chip_data(d);
if (!p->clk)
return 0;
if (on)
clk_enable(p->clk);
else
clk_disable(p->clk);
return 0;
}
static irqreturn_t irqc_irq_handler(int irq, void *dev_id) static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
{ {
struct irqc_irq *i = dev_id; struct irqc_irq *i = dev_id;
...@@ -170,6 +198,15 @@ static int irqc_probe(struct platform_device *pdev) ...@@ -170,6 +198,15 @@ static int irqc_probe(struct platform_device *pdev)
p->pdev = pdev; p->pdev = pdev;
platform_set_drvdata(pdev, p); platform_set_drvdata(pdev, p);
p->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(p->clk)) {
dev_warn(&pdev->dev, "unable to get clock\n");
p->clk = NULL;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
/* get hold of manadatory IOMEM */ /* get hold of manadatory IOMEM */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0); io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!io) { if (!io) {
...@@ -210,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev) ...@@ -210,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev)
irq_chip->irq_mask = irqc_irq_disable; irq_chip->irq_mask = irqc_irq_disable;
irq_chip->irq_unmask = irqc_irq_enable; irq_chip->irq_unmask = irqc_irq_enable;
irq_chip->irq_set_type = irqc_irq_set_type; irq_chip->irq_set_type = irqc_irq_set_type;
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; irq_chip->irq_set_wake = irqc_irq_set_wake;
irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
p->number_of_irqs, p->number_of_irqs,
...@@ -250,6 +288,8 @@ static int irqc_probe(struct platform_device *pdev) ...@@ -250,6 +288,8 @@ static int irqc_probe(struct platform_device *pdev)
err2: err2:
iounmap(p->iomem); iounmap(p->iomem);
err1: err1:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
kfree(p); kfree(p);
err0: err0:
return ret; return ret;
...@@ -265,6 +305,8 @@ static int irqc_remove(struct platform_device *pdev) ...@@ -265,6 +305,8 @@ static int irqc_remove(struct platform_device *pdev)
irq_domain_remove(p->irq_domain); irq_domain_remove(p->irq_domain);
iounmap(p->iomem); iounmap(p->iomem);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
kfree(p); kfree(p);
return 0; return 0;
} }
......
/*
* Copyright (C) 2014-2015 Toradex AG
* Author: Stefan Agner <stefan@agner.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*
* IRQ chip driver for MSCM interrupt router available on Vybrid SoC's.
* The interrupt router is between the CPU's interrupt controller and the
* peripheral. The router allows to route the peripheral interrupts to
* one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or
* Cortex-M4). The router will be configured transparently on a IRQ
* request.
*
* o All peripheral interrupts of the Vybrid SoC can be routed to
* CPU 0, CPU 1 or both. The routing is useful for dual-core
* variants of Vybrid SoC such as VF6xx. This driver routes the
* requested interrupt to the CPU currently running on.
*
* o It is required to setup the interrupt router even on single-core
* variants of Vybrid.
*/
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include "irqchip.h"
#define MSCM_CPxNUM 0x4
#define MSCM_IRSPRC(n) (0x80 + 2 * (n))
#define MSCM_IRSPRC_CPEN_MASK 0x3
#define MSCM_IRSPRC_NUM 112
struct vf610_mscm_ir_chip_data {
void __iomem *mscm_ir_base;
u16 cpu_mask;
u16 saved_irsprc[MSCM_IRSPRC_NUM];
};
static struct vf610_mscm_ir_chip_data *mscm_ir_data;
static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data)
{
int i;
for (i = 0; i < MSCM_IRSPRC_NUM; i++)
data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i));
}
static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data)
{
int i;
for (i = 0; i < MSCM_IRSPRC_NUM; i++)
writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i));
}
static int vf610_mscm_ir_notifier(struct notifier_block *self,
unsigned long cmd, void *v)
{
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
vf610_mscm_ir_save(mscm_ir_data);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
vf610_mscm_ir_restore(mscm_ir_data);
break;
}
return NOTIFY_OK;
}
static struct notifier_block mscm_ir_notifier_block = {
.notifier_call = vf610_mscm_ir_notifier,
};
static void vf610_mscm_ir_enable(struct irq_data *data)
{
irq_hw_number_t hwirq = data->hwirq;
struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
u16 irsprc;
irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
irsprc &= MSCM_IRSPRC_CPEN_MASK;
WARN_ON(irsprc & ~chip_data->cpu_mask);
writew_relaxed(chip_data->cpu_mask,
chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
irq_chip_unmask_parent(data);
}
static void vf610_mscm_ir_disable(struct irq_data *data)
{
irq_hw_number_t hwirq = data->hwirq;
struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
irq_chip_mask_parent(data);
}
static struct irq_chip vf610_mscm_ir_irq_chip = {
.name = "mscm-ir",
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_eoi = irq_chip_eoi_parent,
.irq_enable = vf610_mscm_ir_enable,
.irq_disable = vf610_mscm_ir_disable,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_affinity = irq_chip_set_affinity_parent,
};
static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
int i;
irq_hw_number_t hwirq;
struct of_phandle_args *irq_data = arg;
struct of_phandle_args gic_data;
if (irq_data->args_count != 2)
return -EINVAL;
hwirq = irq_data->args[0];
for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&vf610_mscm_ir_irq_chip,
domain->host_data);
gic_data.np = domain->parent->of_node;
gic_data.args_count = 3;
gic_data.args[0] = GIC_SPI;
gic_data.args[1] = irq_data->args[0];
gic_data.args[2] = irq_data->args[1];
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
}
static const struct irq_domain_ops mscm_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
.alloc = vf610_mscm_ir_domain_alloc,
.free = irq_domain_free_irqs_common,
};
static int __init vf610_mscm_ir_of_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *domain, *domain_parent;
struct regmap *mscm_cp_regmap;
int ret, cpuid;
domain_parent = irq_find_host(parent);
if (!domain_parent) {
pr_err("vf610_mscm_ir: interrupt-parent not found\n");
return -EINVAL;
}
mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL);
if (!mscm_ir_data)
return -ENOMEM;
mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
if (!mscm_ir_data->mscm_ir_base) {
pr_err("vf610_mscm_ir: unable to map mscm register\n");
ret = -ENOMEM;
goto out_free;
}
mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg");
if (IS_ERR(mscm_cp_regmap)) {
ret = PTR_ERR(mscm_cp_regmap);
pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n");
goto out_unmap;
}
regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
mscm_ir_data->cpu_mask = 0x1 << cpuid;
domain = irq_domain_add_hierarchy(domain_parent, 0,
MSCM_IRSPRC_NUM, node,
&mscm_irq_domain_ops, mscm_ir_data);
if (!domain) {
ret = -ENOMEM;
goto out_unmap;
}
cpu_pm_register_notifier(&mscm_ir_notifier_block);
return 0;
out_unmap:
iounmap(mscm_ir_data->mscm_ir_base);
out_free:
kfree(mscm_ir_data);
return ret;
}
IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init);
...@@ -240,6 +240,8 @@ extern unsigned int gic_get_count_width(void); ...@@ -240,6 +240,8 @@ extern unsigned int gic_get_count_width(void);
extern cycle_t gic_read_compare(void); extern cycle_t gic_read_compare(void);
extern void gic_write_compare(cycle_t cnt); extern void gic_write_compare(cycle_t cnt);
extern void gic_write_cpu_compare(cycle_t cnt, int cpu); extern void gic_write_cpu_compare(cycle_t cnt, int cpu);
extern void gic_start_count(void);
extern void gic_stop_count(void);
extern void gic_send_ipi(unsigned int intr); extern void gic_send_ipi(unsigned int intr);
extern unsigned int plat_ipi_call_int_xlate(unsigned int); extern unsigned int plat_ipi_call_int_xlate(unsigned int);
extern unsigned int plat_ipi_resched_int_xlate(unsigned int); extern unsigned int plat_ipi_resched_int_xlate(unsigned int);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment