mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 19:20:50 +07:00
irqchip core changes for v4.1 (round 2)
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJVKFIpAAoJEP45WPkGe8ZnICwP/iYj5L6Xe1JvTCKrkIMgUOi1 RbnMe9igjHrew2cQR07eSXT7ukw/uFj8NSZ5N+ggY8e2lnzMds5iU34RLzWuAENH vR6UPV7q28x6AyKeUDczOgnlh4DWNnU44OX/3e8aFBBGYrZrhFLoq/yuyFAtjAyJ EQUEvJPQsawaVW5XM+DKwQ9SA8pQiKdhS15oJAVxEpZ3NPplGDzmKuV7fq+PW19j UhrgLn9m71nU62LiBJ+Nc8YLjknqenhuleNWRCQttH1igdeGQAJWWhiOLIR8LnFo ZIwaXcOHTzHdJepvYCCUcISKg/o1YcnveGUi6pV+m2qAFwCbaXQKO/O2hLkqFd1C 1jNFgKuaiwxVnlE5iuufHMvC4ep+kvedSduIW6POL9PtUjIKinxwEN4NessAncc+ oqc5Xu0wL/PwopoqkT211l/yeYYZyog1Sov2Z0ORaxwroG3Ax2hrRbdk1hpXxqe/ OFeiONSakPSlVJUnH2O8GSXJooR/WjeELTqO2gk87IuhPRdsDbEyN+nqYCc7aK/Q Zc1uUEyWI0yPXcGYhaT8piG7zogxuGLW1WHyU4sZMiOiFiBpgCm7ANaapJw2LIup AP6u21+6qGXF1MB3sDeRlG701+h7eC+JlmK9HzIaea980kat1q6I6FMX0r4Apasg +/GNGy2NyaZS00mwNFsK =TUXq -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
425b655ce4
@ -0,0 +1,14 @@
|
||||
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>;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
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:
|
||||
|
||||
- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback.
|
||||
Examples with soctypes are:
|
||||
- "renesas,irqc-r8a73a4" (R-Mobile AP6)
|
||||
- "renesas,irqc-r8a73a4" (R-Mobile APE6)
|
||||
- "renesas,irqc-r8a7790" (R-Car H2)
|
||||
- "renesas,irqc-r8a7791" (R-Car M2-W)
|
||||
- "renesas,irqc-r8a7792" (R-Car V2H)
|
||||
@ -12,6 +12,7 @@ Required properties:
|
||||
- "renesas,irqc-r8a7794" (R-Car E2)
|
||||
- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
|
||||
interrupts.txt in this directory
|
||||
- clocks: Must contain a reference to the functional clock.
|
||||
|
||||
Optional properties:
|
||||
|
||||
@ -29,4 +30,5 @@ Example:
|
||||
<0 1 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 2 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp4_clks R8A7790_CLK_IRQC>;
|
||||
};
|
||||
|
@ -631,6 +631,7 @@ config SOC_IMX6SX
|
||||
|
||||
config SOC_VF610
|
||||
bool "Vybrid Family VF610 support"
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select ARM_GIC
|
||||
select PINCTRL_VF610
|
||||
select PL310_ERRATA_769419 if CACHE_L2X0
|
||||
|
@ -38,6 +38,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
|
||||
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
|
||||
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.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_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
|
||||
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
|
||||
|
@ -38,6 +38,8 @@
|
||||
/* Interrupt Controller Registers Map */
|
||||
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
|
||||
#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_SET_ENABLE_OFFS (0x30)
|
||||
@ -56,6 +58,7 @@
|
||||
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
|
||||
|
||||
#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
|
||||
#define ARMADA_370_XP_FABRIC_IRQ (3)
|
||||
|
||||
#define IPI_DOORBELL_START (0)
|
||||
#define IPI_DOORBELL_END (8)
|
||||
@ -77,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock);
|
||||
static phys_addr_t msi_doorbell_addr;
|
||||
#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:
|
||||
* For shared global interrupts, mask/unmask global enable bit
|
||||
@ -86,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *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 +
|
||||
ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
|
||||
else
|
||||
@ -98,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *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 +
|
||||
ARMADA_370_XP_INT_SET_ENABLE_OFFS);
|
||||
else
|
||||
@ -281,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = {
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = armada_xp_set_affinity,
|
||||
#endif
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
|
||||
};
|
||||
|
||||
static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
|
||||
unsigned int virq, irq_hw_number_t hw)
|
||||
{
|
||||
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 +
|
||||
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
else
|
||||
writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
|
||||
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_chip_and_handler(virq, &armada_370_xp_irq_chip,
|
||||
handle_percpu_devid_irq);
|
||||
@ -308,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
|
||||
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)
|
||||
{
|
||||
u32 control;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
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();
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
@ -369,8 +395,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = {
|
||||
static int mpic_cascaded_secondary_init(struct notifier_block *nfb,
|
||||
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);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
@ -379,7 +407,6 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
|
||||
.notifier_call = mpic_cascaded_secondary_init,
|
||||
.priority = 100,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
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,
|
||||
|
||||
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();
|
||||
#endif
|
||||
|
||||
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)
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
|
||||
unsigned en_reg, unsigned ack_reg)
|
||||
static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
|
||||
unsigned en_reg, unsigned ack_reg)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
|
||||
|
@ -414,7 +414,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mask)
|
||||
if (!mask && num_possible_cpus() > 1)
|
||||
pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
|
||||
|
||||
return mask;
|
||||
|
@ -166,6 +166,27 @@ cycle_t gic_read_compare(void)
|
||||
|
||||
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
|
||||
|
||||
static bool gic_local_irq_is_routable(int intr)
|
||||
|
@ -17,6 +17,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
@ -29,15 +30,26 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.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_EN_STS 0x04
|
||||
#define IRQC_EN_SET 0x08
|
||||
#define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */
|
||||
#define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */
|
||||
#define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */
|
||||
#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))
|
||||
/* IRQn Configuration Register */
|
||||
|
||||
struct irqc_irq {
|
||||
int hw_irq;
|
||||
@ -55,6 +67,7 @@ struct irqc_priv {
|
||||
struct platform_device *pdev;
|
||||
struct irq_chip irq_chip;
|
||||
struct irq_domain *irq_domain;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
struct irqc_irq *i = dev_id;
|
||||
@ -170,6 +198,15 @@ static int irqc_probe(struct platform_device *pdev)
|
||||
p->pdev = pdev;
|
||||
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 */
|
||||
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!io) {
|
||||
@ -210,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev)
|
||||
irq_chip->irq_mask = irqc_irq_disable;
|
||||
irq_chip->irq_unmask = irqc_irq_enable;
|
||||
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->number_of_irqs,
|
||||
@ -250,6 +288,8 @@ static int irqc_probe(struct platform_device *pdev)
|
||||
err2:
|
||||
iounmap(p->iomem);
|
||||
err1:
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
kfree(p);
|
||||
err0:
|
||||
return ret;
|
||||
@ -265,6 +305,8 @@ static int irqc_remove(struct platform_device *pdev)
|
||||
|
||||
irq_domain_remove(p->irq_domain);
|
||||
iounmap(p->iomem);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
kfree(p);
|
||||
return 0;
|
||||
}
|
||||
|
212
drivers/irqchip/irq-vf610-mscm-ir.c
Normal file
212
drivers/irqchip/irq-vf610-mscm-ir.c
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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);
|
||||
extern cycle_t gic_read_compare(void);
|
||||
extern void gic_write_compare(cycle_t cnt);
|
||||
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 unsigned int plat_ipi_call_int_xlate(unsigned int);
|
||||
extern unsigned int plat_ipi_resched_int_xlate(unsigned int);
|
||||
|
Loading…
Reference in New Issue
Block a user