mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
synced 2025-11-07 11:45:29 +10:00
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq core updates from Thomas Gleixner:
"This is the first (boring) part of irq updates:
- support for big endian I/O accessors in the generic irq chip
- cleanup of brcmstb/bcm7120 drivers so they can be reused for non
ARM SoCs
- the usual pile of fixes and updates for the various ARM irq chips"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (25 commits)
irqchip: dw-apb-ictl: Add PM support
irqchip: dw-apb-ictl: Enable IRQ_GC_MASK_CACHE_PER_TYPE
irqchip: dw-apb-ictl: Always use use {readl|writel}_relaxed
ARM: orion: convert the irq_reg_{readl,writel} calls to the new API
irqchip: atmel-aic: Add missing entry for rm9200 irq fixups
irqchip: atmel-aic: Rename at91sam9_aic_irq_fixup for naming consistency
irqchip: atmel-aic: Add specific irq fixup function for sam9g45 and sam9rl
irqchip: atmel-aic: Add irq fixups for at91sam926x SoCs
irqchip: atmel-aic: Add irq fixup for RTT block
irqchip: brcmstb-l2: Convert driver to use irq_reg_{readl,writel}
irqchip: bcm7120-l2: Convert driver to use irq_reg_{readl,writel}
irqchip: bcm7120-l2: Decouple driver from brcmstb-l2
irqchip: bcm7120-l2: Extend driver to support 64+ bit controllers
irqchip: bcm7120-l2: Use gc->mask_cache to simplify suspend/resume functions
irqchip: bcm7120-l2: Fix missing nibble in gc->unused mask
irqchip: bcm7120-l2: Make sure all register accesses use base+offset
irqchip: bcm7120-l2, brcmstb-l2: Remove ARM Kconfig dependency
irqchip: bcm7120-l2: Eliminate bad IRQ check
irqchip: brcmstb-l2: Eliminate dependency on ARM code
genirq: Generic chip: Add big endian I/O accessors
...
This commit is contained in:
commit
ecb50f0afd
@ -13,7 +13,12 @@ Such an interrupt controller has the following hardware design:
|
|||||||
or if they will output an interrupt signal at this 2nd level interrupt
|
or if they will output an interrupt signal at this 2nd level interrupt
|
||||||
controller, in particular for UARTs
|
controller, in particular for UARTs
|
||||||
|
|
||||||
- not all 32-bits within the interrupt controller actually map to an interrupt
|
- typically has one 32-bit enable word and one 32-bit status word, but on
|
||||||
|
some hardware may have more than one enable/status pair
|
||||||
|
|
||||||
|
- no atomic set/clear operations
|
||||||
|
|
||||||
|
- not all bits within the interrupt controller actually map to an interrupt
|
||||||
|
|
||||||
The typical hardware layout for this controller is represented below:
|
The typical hardware layout for this controller is represented below:
|
||||||
|
|
||||||
@ -48,7 +53,9 @@ The typical hardware layout for this controller is represented below:
|
|||||||
Required properties:
|
Required properties:
|
||||||
|
|
||||||
- compatible: should be "brcm,bcm7120-l2-intc"
|
- compatible: should be "brcm,bcm7120-l2-intc"
|
||||||
- reg: specifies the base physical address and size of the registers
|
- reg: specifies the base physical address and size of the registers;
|
||||||
|
multiple pairs may be specified, with the first pair handling IRQ offsets
|
||||||
|
0..31 and the second pair handling 32..63
|
||||||
- interrupt-controller: identifies the node as an interrupt controller
|
- interrupt-controller: identifies the node as an interrupt controller
|
||||||
- #interrupt-cells: specifies the number of cells needed to encode an interrupt
|
- #interrupt-cells: specifies the number of cells needed to encode an interrupt
|
||||||
source, should be 1.
|
source, should be 1.
|
||||||
@ -59,18 +66,21 @@ Required properties:
|
|||||||
- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts
|
- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts
|
||||||
are wired to this 2nd level interrupt controller, and how they match their
|
are wired to this 2nd level interrupt controller, and how they match their
|
||||||
respective interrupt parents. Should match exactly the number of interrupts
|
respective interrupt parents. Should match exactly the number of interrupts
|
||||||
specified in the 'interrupts' property.
|
specified in the 'interrupts' property, multiplied by the number of
|
||||||
|
enable/status register pairs implemented by this controller. For
|
||||||
|
multiple parent IRQs with multiple enable/status words, this looks like:
|
||||||
|
<irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
||||||
- brcm,irq-can-wake: if present, this means the L2 controller can be used as a
|
- brcm,irq-can-wake: if present, this means the L2 controller can be used as a
|
||||||
wakeup source for system suspend/resume.
|
wakeup source for system suspend/resume.
|
||||||
|
|
||||||
- brcm,int-fwd-mask: if present, a 32-bits bit mask to configure for the
|
- brcm,int-fwd-mask: if present, a bit mask to configure the interrupts which
|
||||||
interrupts which have a mux gate, typically UARTs. Setting these bits will
|
have a mux gate, typically UARTs. Setting these bits will make their
|
||||||
make their respective interrupts outputs bypass this 2nd level interrupt
|
respective interrupt outputs bypass this 2nd level interrupt controller
|
||||||
controller completely, it completely transparent for the interrupt controller
|
completely; it is completely transparent for the interrupt controller
|
||||||
parent
|
parent. This should have one 32-bit word per enable/status pair.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|||||||
@ -143,6 +143,7 @@ config ARCH_BRCMSTB
|
|||||||
select HAVE_ARM_ARCH_TIMER
|
select HAVE_ARM_ARCH_TIMER
|
||||||
select BRCMSTB_GISB_ARB
|
select BRCMSTB_GISB_ARB
|
||||||
select BRCMSTB_L2_IRQ
|
select BRCMSTB_L2_IRQ
|
||||||
|
select BCM7120_L2_IRQ
|
||||||
help
|
help
|
||||||
Say Y if you intend to run the kernel on a Broadcom ARM-based STB
|
Say Y if you intend to run the kernel on a Broadcom ARM-based STB
|
||||||
chipset.
|
chipset.
|
||||||
|
|||||||
@ -505,9 +505,9 @@ static void orion_gpio_unmask_irq(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
|
reg_val = irq_reg_readl(gc, ct->regs.mask);
|
||||||
reg_val |= mask;
|
reg_val |= mask;
|
||||||
irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
|
irq_reg_writel(gc, reg_val, ct->regs.mask);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,9 +519,9 @@ static void orion_gpio_mask_irq(struct irq_data *d)
|
|||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
|
reg_val = irq_reg_readl(gc, ct->regs.mask);
|
||||||
reg_val &= ~mask;
|
reg_val &= ~mask;
|
||||||
irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
|
irq_reg_writel(gc, reg_val, ct->regs.mask);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,14 +48,19 @@ config ATMEL_AIC5_IRQ
|
|||||||
select MULTI_IRQ_HANDLER
|
select MULTI_IRQ_HANDLER
|
||||||
select SPARSE_IRQ
|
select SPARSE_IRQ
|
||||||
|
|
||||||
|
config BCM7120_L2_IRQ
|
||||||
|
bool
|
||||||
|
select GENERIC_IRQ_CHIP
|
||||||
|
select IRQ_DOMAIN
|
||||||
|
|
||||||
config BRCMSTB_L2_IRQ
|
config BRCMSTB_L2_IRQ
|
||||||
bool
|
bool
|
||||||
depends on ARM
|
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
|
|
||||||
config DW_APB_ICTL
|
config DW_APB_ICTL
|
||||||
bool
|
bool
|
||||||
|
select GENERIC_IRQ_CHIP
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
|
|
||||||
config IMGPDC_IRQ
|
config IMGPDC_IRQ
|
||||||
|
|||||||
@ -35,6 +35,6 @@ 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_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \
|
obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
|
||||||
irq-bcm7120-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
|
||||||
|
|||||||
@ -268,7 +268,7 @@ static int armada_xp_set_affinity(struct irq_data *d,
|
|||||||
writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
|
writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
|
||||||
raw_spin_unlock(&irq_controller_lock);
|
raw_spin_unlock(&irq_controller_lock);
|
||||||
|
|
||||||
return 0;
|
return IRQ_SET_MASK_OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -167,6 +167,32 @@ void __init aic_common_rtc_irq_fixup(struct device_node *root)
|
|||||||
iounmap(regs);
|
iounmap(regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
|
||||||
|
#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */
|
||||||
|
#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */
|
||||||
|
|
||||||
|
void __init aic_common_rtt_irq_fixup(struct device_node *root)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
void __iomem *regs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The at91sam9263 SoC has 2 instances of the RTT block, hence we
|
||||||
|
* iterate over the DT to find each occurrence.
|
||||||
|
*/
|
||||||
|
for_each_compatible_node(np, NULL, "atmel,at91sam9260-rtt") {
|
||||||
|
regs = of_iomap(np, 0);
|
||||||
|
if (!regs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
writel(readl(regs + AT91_RTT_MR) &
|
||||||
|
~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN),
|
||||||
|
regs + AT91_RTT_MR);
|
||||||
|
|
||||||
|
iounmap(regs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void __init aic_common_irq_fixup(const struct of_device_id *matches)
|
void __init aic_common_irq_fixup(const struct of_device_id *matches)
|
||||||
{
|
{
|
||||||
struct device_node *root = of_find_node_by_path("/");
|
struct device_node *root = of_find_node_by_path("/");
|
||||||
|
|||||||
@ -34,6 +34,8 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node,
|
|||||||
|
|
||||||
void __init aic_common_rtc_irq_fixup(struct device_node *root);
|
void __init aic_common_rtc_irq_fixup(struct device_node *root);
|
||||||
|
|
||||||
|
void __init aic_common_rtt_irq_fixup(struct device_node *root);
|
||||||
|
|
||||||
void __init aic_common_irq_fixup(const struct of_device_id *matches);
|
void __init aic_common_irq_fixup(const struct of_device_id *matches);
|
||||||
|
|
||||||
#endif /* __IRQ_ATMEL_AIC_COMMON_H */
|
#endif /* __IRQ_ATMEL_AIC_COMMON_H */
|
||||||
|
|||||||
@ -65,11 +65,11 @@ aic_handle(struct pt_regs *regs)
|
|||||||
u32 irqnr;
|
u32 irqnr;
|
||||||
u32 irqstat;
|
u32 irqstat;
|
||||||
|
|
||||||
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
|
irqnr = irq_reg_readl(gc, AT91_AIC_IVR);
|
||||||
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
|
irqstat = irq_reg_readl(gc, AT91_AIC_ISR);
|
||||||
|
|
||||||
if (!irqstat)
|
if (!irqstat)
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
|
irq_reg_writel(gc, 0, AT91_AIC_EOICR);
|
||||||
else
|
else
|
||||||
handle_domain_irq(aic_domain, irqnr, regs);
|
handle_domain_irq(aic_domain, irqnr, regs);
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ static int aic_retrigger(struct irq_data *d)
|
|||||||
|
|
||||||
/* Enable interrupt on AIC5 */
|
/* Enable interrupt on AIC5 */
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR);
|
irq_reg_writel(gc, d->mask, AT91_AIC_ISCR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -92,12 +92,12 @@ static int aic_set_type(struct irq_data *d, unsigned type)
|
|||||||
unsigned int smr;
|
unsigned int smr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq));
|
smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq));
|
||||||
ret = aic_common_set_type(d, type, &smr);
|
ret = aic_common_set_type(d, type, &smr);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq));
|
irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -108,8 +108,8 @@ static void aic_suspend(struct irq_data *d)
|
|||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR);
|
irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR);
|
||||||
irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR);
|
irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ static void aic_resume(struct irq_data *d)
|
|||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR);
|
irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR);
|
||||||
irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR);
|
irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +128,8 @@ static void aic_pm_shutdown(struct irq_data *d)
|
|||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -148,24 +148,24 @@ static void __init aic_hw_init(struct irq_domain *domain)
|
|||||||
* will not Lock out nIRQ
|
* will not Lock out nIRQ
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
|
irq_reg_writel(gc, 0, AT91_AIC_EOICR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spurious Interrupt ID in Spurious Vector Register.
|
* Spurious Interrupt ID in Spurious Vector Register.
|
||||||
* When there is no current interrupt, the IRQ Vector Register
|
* When there is no current interrupt, the IRQ Vector Register
|
||||||
* reads the value stored in AIC_SPU
|
* reads the value stored in AIC_SPU
|
||||||
*/
|
*/
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU);
|
||||||
|
|
||||||
/* No debugging in AIC: Debug (Protect) Control Register */
|
/* No debugging in AIC: Debug (Protect) Control Register */
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR);
|
irq_reg_writel(gc, 0, AT91_AIC_DCR);
|
||||||
|
|
||||||
/* Disable and clear all interrupts initially */
|
/* Disable and clear all interrupts initially */
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
|
||||||
|
|
||||||
for (i = 0; i < 32; i++)
|
for (i = 0; i < 32; i++)
|
||||||
irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i));
|
irq_reg_writel(gc, i, AT91_AIC_SVR(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aic_irq_domain_xlate(struct irq_domain *d,
|
static int aic_irq_domain_xlate(struct irq_domain *d,
|
||||||
@ -195,10 +195,10 @@ static int aic_irq_domain_xlate(struct irq_domain *d,
|
|||||||
gc = dgc->gc[idx];
|
gc = dgc->gc[idx];
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq));
|
smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq));
|
||||||
ret = aic_common_set_priority(intspec[2], &smr);
|
ret = aic_common_set_priority(intspec[2], &smr);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq));
|
irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq));
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -209,16 +209,32 @@ static const struct irq_domain_ops aic_irq_ops = {
|
|||||||
.xlate = aic_irq_domain_xlate,
|
.xlate = aic_irq_domain_xlate,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __init at91sam9_aic_irq_fixup(struct device_node *root)
|
static void __init at91rm9200_aic_irq_fixup(struct device_node *root)
|
||||||
{
|
{
|
||||||
aic_common_rtc_irq_fixup(root);
|
aic_common_rtc_irq_fixup(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __init at91sam9260_aic_irq_fixup(struct device_node *root)
|
||||||
|
{
|
||||||
|
aic_common_rtt_irq_fixup(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __init at91sam9g45_aic_irq_fixup(struct device_node *root)
|
||||||
|
{
|
||||||
|
aic_common_rtc_irq_fixup(root);
|
||||||
|
aic_common_rtt_irq_fixup(root);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct of_device_id __initdata aic_irq_fixups[] = {
|
static const struct of_device_id __initdata aic_irq_fixups[] = {
|
||||||
{ .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup },
|
{ .compatible = "atmel,at91rm9200", .data = at91rm9200_aic_irq_fixup },
|
||||||
{ .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup },
|
{ .compatible = "atmel,at91sam9g45", .data = at91sam9g45_aic_irq_fixup },
|
||||||
{ .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup },
|
{ .compatible = "atmel,at91sam9n12", .data = at91rm9200_aic_irq_fixup },
|
||||||
{ .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup },
|
{ .compatible = "atmel,at91sam9rl", .data = at91sam9g45_aic_irq_fixup },
|
||||||
|
{ .compatible = "atmel,at91sam9x5", .data = at91rm9200_aic_irq_fixup },
|
||||||
|
{ .compatible = "atmel,at91sam9260", .data = at91sam9260_aic_irq_fixup },
|
||||||
|
{ .compatible = "atmel,at91sam9261", .data = at91sam9260_aic_irq_fixup },
|
||||||
|
{ .compatible = "atmel,at91sam9263", .data = at91sam9260_aic_irq_fixup },
|
||||||
|
{ .compatible = "atmel,at91sam9g20", .data = at91sam9260_aic_irq_fixup },
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -75,11 +75,11 @@ aic5_handle(struct pt_regs *regs)
|
|||||||
u32 irqnr;
|
u32 irqnr;
|
||||||
u32 irqstat;
|
u32 irqstat;
|
||||||
|
|
||||||
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
|
irqnr = irq_reg_readl(gc, AT91_AIC5_IVR);
|
||||||
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
|
irqstat = irq_reg_readl(gc, AT91_AIC5_ISR);
|
||||||
|
|
||||||
if (!irqstat)
|
if (!irqstat)
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
|
irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
|
||||||
else
|
else
|
||||||
handle_domain_irq(aic5_domain, irqnr, regs);
|
handle_domain_irq(aic5_domain, irqnr, regs);
|
||||||
}
|
}
|
||||||
@ -92,8 +92,8 @@ static void aic5_mask(struct irq_data *d)
|
|||||||
|
|
||||||
/* Disable interrupt on AIC5 */
|
/* Disable interrupt on AIC5 */
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
|
||||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
|
irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
|
||||||
gc->mask_cache &= ~d->mask;
|
gc->mask_cache &= ~d->mask;
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
@ -106,8 +106,8 @@ static void aic5_unmask(struct irq_data *d)
|
|||||||
|
|
||||||
/* Enable interrupt on AIC5 */
|
/* Enable interrupt on AIC5 */
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
|
||||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR);
|
irq_reg_writel(gc, 1, AT91_AIC5_IECR);
|
||||||
gc->mask_cache |= d->mask;
|
gc->mask_cache |= d->mask;
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
@ -120,8 +120,8 @@ static int aic5_retrigger(struct irq_data *d)
|
|||||||
|
|
||||||
/* Enable interrupt on AIC5 */
|
/* Enable interrupt on AIC5 */
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
|
||||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR);
|
irq_reg_writel(gc, 1, AT91_AIC5_ISCR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -136,11 +136,11 @@ static int aic5_set_type(struct irq_data *d, unsigned type)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
|
||||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
|
smr = irq_reg_readl(gc, AT91_AIC5_SMR);
|
||||||
ret = aic_common_set_type(d, type, &smr);
|
ret = aic_common_set_type(d, type, &smr);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR);
|
irq_reg_writel(gc, smr, AT91_AIC5_SMR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -162,12 +162,11 @@ static void aic5_suspend(struct irq_data *d)
|
|||||||
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
irq_reg_writel(i + gc->irq_base,
|
irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
|
||||||
bgc->reg_base + AT91_AIC5_SSR);
|
|
||||||
if (mask & gc->wake_active)
|
if (mask & gc->wake_active)
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
|
||||||
else
|
else
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
|
||||||
}
|
}
|
||||||
irq_gc_unlock(bgc);
|
irq_gc_unlock(bgc);
|
||||||
}
|
}
|
||||||
@ -187,12 +186,11 @@ static void aic5_resume(struct irq_data *d)
|
|||||||
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
irq_reg_writel(i + gc->irq_base,
|
irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
|
||||||
bgc->reg_base + AT91_AIC5_SSR);
|
|
||||||
if (mask & gc->mask_cache)
|
if (mask & gc->mask_cache)
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
|
||||||
else
|
else
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
|
||||||
}
|
}
|
||||||
irq_gc_unlock(bgc);
|
irq_gc_unlock(bgc);
|
||||||
}
|
}
|
||||||
@ -207,10 +205,9 @@ static void aic5_pm_shutdown(struct irq_data *d)
|
|||||||
|
|
||||||
irq_gc_lock(bgc);
|
irq_gc_lock(bgc);
|
||||||
for (i = 0; i < dgc->irqs_per_chip; i++) {
|
for (i = 0; i < dgc->irqs_per_chip; i++) {
|
||||||
irq_reg_writel(i + gc->irq_base,
|
irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
|
||||||
bgc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
irq_reg_writel(bgc, 1, AT91_AIC5_ICCR);
|
||||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR);
|
|
||||||
}
|
}
|
||||||
irq_gc_unlock(bgc);
|
irq_gc_unlock(bgc);
|
||||||
}
|
}
|
||||||
@ -230,24 +227,24 @@ static void __init aic5_hw_init(struct irq_domain *domain)
|
|||||||
* will not Lock out nIRQ
|
* will not Lock out nIRQ
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
|
irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spurious Interrupt ID in Spurious Vector Register.
|
* Spurious Interrupt ID in Spurious Vector Register.
|
||||||
* When there is no current interrupt, the IRQ Vector Register
|
* When there is no current interrupt, the IRQ Vector Register
|
||||||
* reads the value stored in AIC_SPU
|
* reads the value stored in AIC_SPU
|
||||||
*/
|
*/
|
||||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU);
|
irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU);
|
||||||
|
|
||||||
/* No debugging in AIC: Debug (Protect) Control Register */
|
/* No debugging in AIC: Debug (Protect) Control Register */
|
||||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR);
|
irq_reg_writel(gc, 0, AT91_AIC5_DCR);
|
||||||
|
|
||||||
/* Disable and clear all interrupts initially */
|
/* Disable and clear all interrupts initially */
|
||||||
for (i = 0; i < domain->revmap_size; i++) {
|
for (i = 0; i < domain->revmap_size; i++) {
|
||||||
irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, i, AT91_AIC5_SSR);
|
||||||
irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR);
|
irq_reg_writel(gc, i, AT91_AIC5_SVR);
|
||||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
|
irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
|
||||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR);
|
irq_reg_writel(gc, 1, AT91_AIC5_ICCR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,11 +270,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
|
|||||||
gc = dgc->gc[0];
|
gc = dgc->gc[0];
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR);
|
irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR);
|
||||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
|
smr = irq_reg_readl(gc, AT91_AIC5_SMR);
|
||||||
ret = aic_common_set_priority(intspec[2], &smr);
|
ret = aic_common_set_priority(intspec[2], &smr);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR);
|
irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
@ -23,47 +24,52 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
|
|
||||||
#include "irqchip.h"
|
#include "irqchip.h"
|
||||||
|
|
||||||
#include <asm/mach/irq.h>
|
|
||||||
|
|
||||||
/* Register offset in the L2 interrupt controller */
|
/* Register offset in the L2 interrupt controller */
|
||||||
#define IRQEN 0x00
|
#define IRQEN 0x00
|
||||||
#define IRQSTAT 0x04
|
#define IRQSTAT 0x04
|
||||||
|
|
||||||
|
#define MAX_WORDS 4
|
||||||
|
#define IRQS_PER_WORD 32
|
||||||
|
|
||||||
struct bcm7120_l2_intc_data {
|
struct bcm7120_l2_intc_data {
|
||||||
void __iomem *base;
|
unsigned int n_words;
|
||||||
|
void __iomem *base[MAX_WORDS];
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
bool can_wake;
|
bool can_wake;
|
||||||
u32 irq_fwd_mask;
|
u32 irq_fwd_mask[MAX_WORDS];
|
||||||
u32 irq_map_mask;
|
u32 irq_map_mask[MAX_WORDS];
|
||||||
u32 saved_mask;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
|
static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
|
||||||
{
|
{
|
||||||
struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
|
struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
u32 status;
|
unsigned int idx;
|
||||||
|
|
||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
status = __raw_readl(b->base + IRQSTAT);
|
for (idx = 0; idx < b->n_words; idx++) {
|
||||||
|
int base = idx * IRQS_PER_WORD;
|
||||||
|
struct irq_chip_generic *gc =
|
||||||
|
irq_get_domain_generic_chip(b->domain, base);
|
||||||
|
unsigned long pending;
|
||||||
|
int hwirq;
|
||||||
|
|
||||||
if (status == 0) {
|
irq_gc_lock(gc);
|
||||||
do_bad_IRQ(irq, desc);
|
pending = irq_reg_readl(gc, IRQSTAT) & gc->mask_cache;
|
||||||
goto out;
|
irq_gc_unlock(gc);
|
||||||
|
|
||||||
|
for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
|
||||||
|
generic_handle_irq(irq_find_mapping(b->domain,
|
||||||
|
base + hwirq));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
|
||||||
irq = ffs(status) - 1;
|
|
||||||
status &= ~(1 << irq);
|
|
||||||
generic_handle_irq(irq_find_mapping(b->domain, irq));
|
|
||||||
} while (status);
|
|
||||||
|
|
||||||
out:
|
|
||||||
chained_irq_exit(chip, desc);
|
chained_irq_exit(chip, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,26 +77,20 @@ static void bcm7120_l2_intc_suspend(struct irq_data *d)
|
|||||||
{
|
{
|
||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct bcm7120_l2_intc_data *b = gc->private;
|
struct bcm7120_l2_intc_data *b = gc->private;
|
||||||
u32 reg;
|
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
/* Save the current mask and the interrupt forward mask */
|
if (b->can_wake)
|
||||||
b->saved_mask = __raw_readl(b->base) | b->irq_fwd_mask;
|
irq_reg_writel(gc, gc->mask_cache | gc->wake_active, IRQEN);
|
||||||
if (b->can_wake) {
|
|
||||||
reg = b->saved_mask | gc->wake_active;
|
|
||||||
__raw_writel(reg, b->base);
|
|
||||||
}
|
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm7120_l2_intc_resume(struct irq_data *d)
|
static void bcm7120_l2_intc_resume(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct bcm7120_l2_intc_data *b = gc->private;
|
|
||||||
|
|
||||||
/* Restore the saved mask */
|
/* Restore the saved mask */
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
__raw_writel(b->saved_mask, b->base);
|
irq_reg_writel(gc, gc->mask_cache, IRQEN);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +99,7 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
|
|||||||
int irq, const __be32 *map_mask)
|
int irq, const __be32 *map_mask)
|
||||||
{
|
{
|
||||||
int parent_irq;
|
int parent_irq;
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
parent_irq = irq_of_parse_and_map(dn, irq);
|
parent_irq = irq_of_parse_and_map(dn, irq);
|
||||||
if (!parent_irq) {
|
if (!parent_irq) {
|
||||||
@ -106,7 +107,12 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->irq_map_mask |= be32_to_cpup(map_mask + irq);
|
/* For multiple parent IRQs with multiple words, this looks like:
|
||||||
|
* <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
|
||||||
|
*/
|
||||||
|
for (idx = 0; idx < data->n_words; idx++)
|
||||||
|
data->irq_map_mask[idx] |=
|
||||||
|
be32_to_cpup(map_mask + irq * data->n_words + idx);
|
||||||
|
|
||||||
irq_set_handler_data(parent_irq, data);
|
irq_set_handler_data(parent_irq, data);
|
||||||
irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
|
irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
|
||||||
@ -123,26 +129,41 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
|
|||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
const __be32 *map_mask;
|
const __be32 *map_mask;
|
||||||
int num_parent_irqs;
|
int num_parent_irqs;
|
||||||
int ret = 0, len, irq;
|
int ret = 0, len;
|
||||||
|
unsigned int idx, irq, flags;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data->base = of_iomap(dn, 0);
|
for (idx = 0; idx < MAX_WORDS; idx++) {
|
||||||
if (!data->base) {
|
data->base[idx] = of_iomap(dn, idx);
|
||||||
|
if (!data->base[idx])
|
||||||
|
break;
|
||||||
|
data->n_words = idx + 1;
|
||||||
|
}
|
||||||
|
if (!data->n_words) {
|
||||||
pr_err("failed to remap intc L2 registers\n");
|
pr_err("failed to remap intc L2 registers\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_free;
|
goto out_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask))
|
/* Enable all interrupts specified in the interrupt forward mask;
|
||||||
data->irq_fwd_mask = 0;
|
* disable all others. If the property doesn't exist (-EINVAL),
|
||||||
|
* assume all zeroes.
|
||||||
/* Enable all interrupt specified in the interrupt forward mask and have
|
|
||||||
* the other disabled
|
|
||||||
*/
|
*/
|
||||||
__raw_writel(data->irq_fwd_mask, data->base + IRQEN);
|
ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask",
|
||||||
|
data->irq_fwd_mask, data->n_words);
|
||||||
|
if (ret == 0 || ret == -EINVAL) {
|
||||||
|
for (idx = 0; idx < data->n_words; idx++)
|
||||||
|
__raw_writel(data->irq_fwd_mask[idx],
|
||||||
|
data->base[idx] + IRQEN);
|
||||||
|
} else {
|
||||||
|
/* property exists but has the wrong number of words */
|
||||||
|
pr_err("invalid int-fwd-mask property\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
num_parent_irqs = of_irq_count(dn);
|
num_parent_irqs = of_irq_count(dn);
|
||||||
if (num_parent_irqs <= 0) {
|
if (num_parent_irqs <= 0) {
|
||||||
@ -152,7 +173,8 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
|
map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
|
||||||
if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) {
|
if (!map_mask ||
|
||||||
|
(len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) {
|
||||||
pr_err("invalid brcm,int-map-mask property\n");
|
pr_err("invalid brcm,int-map-mask property\n");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
@ -164,56 +186,70 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
|
|||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->domain = irq_domain_add_linear(dn, 32,
|
data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words,
|
||||||
&irq_generic_chip_ops, NULL);
|
&irq_generic_chip_ops, NULL);
|
||||||
if (!data->domain) {
|
if (!data->domain) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
|
/* MIPS chips strapped for BE will automagically configure the
|
||||||
dn->full_name, handle_level_irq, clr, 0,
|
* peripheral registers for CPU-native byte order.
|
||||||
IRQ_GC_INIT_MASK_CACHE);
|
*/
|
||||||
|
flags = IRQ_GC_INIT_MASK_CACHE;
|
||||||
|
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
|
||||||
|
flags |= IRQ_GC_BE_IO;
|
||||||
|
|
||||||
|
ret = irq_alloc_domain_generic_chips(data->domain, IRQS_PER_WORD, 1,
|
||||||
|
dn->full_name, handle_level_irq, clr, 0, flags);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("failed to allocate generic irq chip\n");
|
pr_err("failed to allocate generic irq chip\n");
|
||||||
goto out_free_domain;
|
goto out_free_domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
gc = irq_get_domain_generic_chip(data->domain, 0);
|
if (of_property_read_bool(dn, "brcm,irq-can-wake"))
|
||||||
gc->unused = 0xfffffff & ~data->irq_map_mask;
|
|
||||||
gc->reg_base = data->base;
|
|
||||||
gc->private = data;
|
|
||||||
ct = gc->chip_types;
|
|
||||||
|
|
||||||
ct->regs.mask = IRQEN;
|
|
||||||
ct->chip.irq_mask = irq_gc_mask_clr_bit;
|
|
||||||
ct->chip.irq_unmask = irq_gc_mask_set_bit;
|
|
||||||
ct->chip.irq_ack = irq_gc_noop;
|
|
||||||
ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
|
|
||||||
ct->chip.irq_resume = bcm7120_l2_intc_resume;
|
|
||||||
|
|
||||||
if (of_property_read_bool(dn, "brcm,irq-can-wake")) {
|
|
||||||
data->can_wake = true;
|
data->can_wake = true;
|
||||||
/* This IRQ chip can wake the system, set all relevant child
|
|
||||||
* interupts in wake_enabled mask
|
for (idx = 0; idx < data->n_words; idx++) {
|
||||||
*/
|
irq = idx * IRQS_PER_WORD;
|
||||||
gc->wake_enabled = 0xffffffff;
|
gc = irq_get_domain_generic_chip(data->domain, irq);
|
||||||
gc->wake_enabled &= ~gc->unused;
|
|
||||||
ct->chip.irq_set_wake = irq_gc_set_wake;
|
gc->unused = 0xffffffff & ~data->irq_map_mask[idx];
|
||||||
|
gc->reg_base = data->base[idx];
|
||||||
|
gc->private = data;
|
||||||
|
ct = gc->chip_types;
|
||||||
|
|
||||||
|
ct->regs.mask = IRQEN;
|
||||||
|
ct->chip.irq_mask = irq_gc_mask_clr_bit;
|
||||||
|
ct->chip.irq_unmask = irq_gc_mask_set_bit;
|
||||||
|
ct->chip.irq_ack = irq_gc_noop;
|
||||||
|
ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
|
||||||
|
ct->chip.irq_resume = bcm7120_l2_intc_resume;
|
||||||
|
|
||||||
|
if (data->can_wake) {
|
||||||
|
/* This IRQ chip can wake the system, set all
|
||||||
|
* relevant child interupts in wake_enabled mask
|
||||||
|
*/
|
||||||
|
gc->wake_enabled = 0xffffffff;
|
||||||
|
gc->wake_enabled &= ~gc->unused;
|
||||||
|
ct->chip.irq_set_wake = irq_gc_set_wake;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
|
pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
|
||||||
data->base, num_parent_irqs);
|
data->base[0], num_parent_irqs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_domain:
|
out_free_domain:
|
||||||
irq_domain_remove(data->domain);
|
irq_domain_remove(data->domain);
|
||||||
out_unmap:
|
out_unmap:
|
||||||
iounmap(data->base);
|
for (idx = 0; idx < MAX_WORDS; idx++) {
|
||||||
out_free:
|
if (data->base[idx])
|
||||||
|
iounmap(data->base[idx]);
|
||||||
|
}
|
||||||
kfree(data);
|
kfree(data);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc",
|
IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc",
|
||||||
bcm7120_l2_intc_of_init);
|
bcm7120_l2_intc_of_init);
|
||||||
|
|||||||
@ -18,7 +18,9 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
@ -30,8 +32,6 @@
|
|||||||
#include <linux/irqchip.h>
|
#include <linux/irqchip.h>
|
||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
|
|
||||||
#include <asm/mach/irq.h>
|
|
||||||
|
|
||||||
#include "irqchip.h"
|
#include "irqchip.h"
|
||||||
|
|
||||||
/* Register offsets in the L2 interrupt controller */
|
/* Register offsets in the L2 interrupt controller */
|
||||||
@ -54,23 +54,26 @@ struct brcmstb_l2_intc_data {
|
|||||||
static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
|
static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
|
||||||
{
|
{
|
||||||
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
|
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
|
||||||
|
struct irq_chip_generic *gc = irq_get_domain_generic_chip(b->domain, 0);
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
status = __raw_readl(b->base + CPU_STATUS) &
|
status = irq_reg_readl(gc, CPU_STATUS) &
|
||||||
~(__raw_readl(b->base + CPU_MASK_STATUS));
|
~(irq_reg_readl(gc, CPU_MASK_STATUS));
|
||||||
|
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
do_bad_IRQ(irq, desc);
|
raw_spin_lock(&desc->lock);
|
||||||
|
handle_bad_irq(irq, desc);
|
||||||
|
raw_spin_unlock(&desc->lock);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
irq = ffs(status) - 1;
|
irq = ffs(status) - 1;
|
||||||
/* ack at our level */
|
/* ack at our level */
|
||||||
__raw_writel(1 << irq, b->base + CPU_CLEAR);
|
irq_reg_writel(gc, 1 << irq, CPU_CLEAR);
|
||||||
status &= ~(1 << irq);
|
status &= ~(1 << irq);
|
||||||
generic_handle_irq(irq_find_mapping(b->domain, irq));
|
generic_handle_irq(irq_find_mapping(b->domain, irq));
|
||||||
} while (status);
|
} while (status);
|
||||||
@ -85,12 +88,12 @@ static void brcmstb_l2_intc_suspend(struct irq_data *d)
|
|||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
/* Save the current mask */
|
/* Save the current mask */
|
||||||
b->saved_mask = __raw_readl(b->base + CPU_MASK_STATUS);
|
b->saved_mask = irq_reg_readl(gc, CPU_MASK_STATUS);
|
||||||
|
|
||||||
if (b->can_wake) {
|
if (b->can_wake) {
|
||||||
/* Program the wakeup mask */
|
/* Program the wakeup mask */
|
||||||
__raw_writel(~gc->wake_active, b->base + CPU_MASK_SET);
|
irq_reg_writel(gc, ~gc->wake_active, CPU_MASK_SET);
|
||||||
__raw_writel(gc->wake_active, b->base + CPU_MASK_CLEAR);
|
irq_reg_writel(gc, gc->wake_active, CPU_MASK_CLEAR);
|
||||||
}
|
}
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
@ -102,11 +105,11 @@ static void brcmstb_l2_intc_resume(struct irq_data *d)
|
|||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
/* Clear unmasked non-wakeup interrupts */
|
/* Clear unmasked non-wakeup interrupts */
|
||||||
__raw_writel(~b->saved_mask & ~gc->wake_active, b->base + CPU_CLEAR);
|
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, CPU_CLEAR);
|
||||||
|
|
||||||
/* Restore the saved mask */
|
/* Restore the saved mask */
|
||||||
__raw_writel(b->saved_mask, b->base + CPU_MASK_SET);
|
irq_reg_writel(gc, b->saved_mask, CPU_MASK_SET);
|
||||||
__raw_writel(~b->saved_mask, b->base + CPU_MASK_CLEAR);
|
irq_reg_writel(gc, ~b->saved_mask, CPU_MASK_CLEAR);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +121,7 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
|
|||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -131,8 +135,8 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Disable all interrupts by default */
|
/* Disable all interrupts by default */
|
||||||
__raw_writel(0xffffffff, data->base + CPU_MASK_SET);
|
writel(0xffffffff, data->base + CPU_MASK_SET);
|
||||||
__raw_writel(0xffffffff, data->base + CPU_CLEAR);
|
writel(0xffffffff, data->base + CPU_CLEAR);
|
||||||
|
|
||||||
data->parent_irq = irq_of_parse_and_map(np, 0);
|
data->parent_irq = irq_of_parse_and_map(np, 0);
|
||||||
if (!data->parent_irq) {
|
if (!data->parent_irq) {
|
||||||
@ -148,9 +152,16 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
|
|||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MIPS chips strapped for BE will automagically configure the
|
||||||
|
* peripheral registers for CPU-native byte order.
|
||||||
|
*/
|
||||||
|
flags = 0;
|
||||||
|
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
|
||||||
|
flags |= IRQ_GC_BE_IO;
|
||||||
|
|
||||||
/* Allocate a single Generic IRQ chip for this node */
|
/* Allocate a single Generic IRQ chip for this node */
|
||||||
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
|
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
|
||||||
np->full_name, handle_edge_irq, clr, 0, 0);
|
np->full_name, handle_edge_irq, clr, 0, flags);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("failed to allocate generic irq chip\n");
|
pr_err("failed to allocate generic irq chip\n");
|
||||||
goto out_free_domain;
|
goto out_free_domain;
|
||||||
|
|||||||
@ -50,6 +50,21 @@ static void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc)
|
|||||||
chained_irq_exit(chip, desc);
|
chained_irq_exit(chip, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static void dw_apb_ictl_resume(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||||
|
|
||||||
|
irq_gc_lock(gc);
|
||||||
|
writel_relaxed(~0, gc->reg_base + ct->regs.enable);
|
||||||
|
writel_relaxed(*ct->mask_cache, gc->reg_base + ct->regs.mask);
|
||||||
|
irq_gc_unlock(gc);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define dw_apb_ictl_resume NULL
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
static int __init dw_apb_ictl_init(struct device_node *np,
|
static int __init dw_apb_ictl_init(struct device_node *np,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
@ -94,16 +109,16 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* mask and enable all interrupts */
|
/* mask and enable all interrupts */
|
||||||
writel(~0, iobase + APB_INT_MASK_L);
|
writel_relaxed(~0, iobase + APB_INT_MASK_L);
|
||||||
writel(~0, iobase + APB_INT_MASK_H);
|
writel_relaxed(~0, iobase + APB_INT_MASK_H);
|
||||||
writel(~0, iobase + APB_INT_ENABLE_L);
|
writel_relaxed(~0, iobase + APB_INT_ENABLE_L);
|
||||||
writel(~0, iobase + APB_INT_ENABLE_H);
|
writel_relaxed(~0, iobase + APB_INT_ENABLE_H);
|
||||||
|
|
||||||
reg = readl(iobase + APB_INT_ENABLE_H);
|
reg = readl_relaxed(iobase + APB_INT_ENABLE_H);
|
||||||
if (reg)
|
if (reg)
|
||||||
nrirqs = 32 + fls(reg);
|
nrirqs = 32 + fls(reg);
|
||||||
else
|
else
|
||||||
nrirqs = fls(readl(iobase + APB_INT_ENABLE_L));
|
nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L));
|
||||||
|
|
||||||
domain = irq_domain_add_linear(np, nrirqs,
|
domain = irq_domain_add_linear(np, nrirqs,
|
||||||
&irq_generic_chip_ops, NULL);
|
&irq_generic_chip_ops, NULL);
|
||||||
@ -115,6 +130,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||||||
|
|
||||||
ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1,
|
ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1,
|
||||||
np->name, handle_level_irq, clr, 0,
|
np->name, handle_level_irq, clr, 0,
|
||||||
|
IRQ_GC_MASK_CACHE_PER_TYPE |
|
||||||
IRQ_GC_INIT_MASK_CACHE);
|
IRQ_GC_INIT_MASK_CACHE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("%s: unable to alloc irq domain gc\n", np->full_name);
|
pr_err("%s: unable to alloc irq domain gc\n", np->full_name);
|
||||||
@ -126,13 +142,17 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||||||
gc->reg_base = iobase;
|
gc->reg_base = iobase;
|
||||||
|
|
||||||
gc->chip_types[0].regs.mask = APB_INT_MASK_L;
|
gc->chip_types[0].regs.mask = APB_INT_MASK_L;
|
||||||
|
gc->chip_types[0].regs.enable = APB_INT_ENABLE_L;
|
||||||
gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
|
gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
|
||||||
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
|
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
|
||||||
|
gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
|
||||||
|
|
||||||
if (nrirqs > 32) {
|
if (nrirqs > 32) {
|
||||||
gc->chip_types[1].regs.mask = APB_INT_MASK_H;
|
gc->chip_types[1].regs.mask = APB_INT_MASK_H;
|
||||||
|
gc->chip_types[1].regs.enable = APB_INT_ENABLE_H;
|
||||||
gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit;
|
gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit;
|
||||||
gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit;
|
gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit;
|
||||||
|
gc->chip_types[1].chip.irq_resume = dw_apb_ictl_resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_set_handler_data(irq, gc);
|
irq_set_handler_data(irq, gc);
|
||||||
|
|||||||
@ -176,8 +176,7 @@ static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
|
|||||||
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
|
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
|
||||||
|
|
||||||
if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
|
if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
|
||||||
irqnr = irq_find_mapping(hip04_data.domain, irqnr);
|
handle_domain_irq(hip04_data.domain, irqnr, regs);
|
||||||
handle_IRQ(irqnr, regs);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (irqnr < 16) {
|
if (irqnr < 16) {
|
||||||
|
|||||||
@ -50,12 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = {
|
|||||||
static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
|
static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
|
||||||
u32 val)
|
u32 val)
|
||||||
{
|
{
|
||||||
irq_reg_writel(val, gc->reg_base + off);
|
irq_reg_writel(gc, val, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
|
static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
|
||||||
{
|
{
|
||||||
return irq_reg_readl(gc->reg_base + off);
|
return irq_reg_readl(gc, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc)
|
static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc)
|
||||||
|
|||||||
@ -43,12 +43,12 @@
|
|||||||
static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg,
|
static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg,
|
||||||
u32 val)
|
u32 val)
|
||||||
{
|
{
|
||||||
irq_reg_writel(val, gc->reg_base + reg);
|
irq_reg_writel(gc, val, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg)
|
static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg)
|
||||||
{
|
{
|
||||||
return irq_reg_readl(gc->reg_base + reg);
|
return irq_reg_readl(gc, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/topology.h>
|
#include <linux/topology.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
@ -639,13 +640,6 @@ void arch_teardown_hwirq(unsigned int irq);
|
|||||||
void irq_init_desc(unsigned int irq);
|
void irq_init_desc(unsigned int irq);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef irq_reg_writel
|
|
||||||
# define irq_reg_writel(val, addr) writel(val, addr)
|
|
||||||
#endif
|
|
||||||
#ifndef irq_reg_readl
|
|
||||||
# define irq_reg_readl(addr) readl(addr)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct irq_chip_regs - register offsets for struct irq_gci
|
* struct irq_chip_regs - register offsets for struct irq_gci
|
||||||
* @enable: Enable register offset to reg_base
|
* @enable: Enable register offset to reg_base
|
||||||
@ -692,6 +686,8 @@ struct irq_chip_type {
|
|||||||
* struct irq_chip_generic - Generic irq chip data structure
|
* struct irq_chip_generic - Generic irq chip data structure
|
||||||
* @lock: Lock to protect register and cache data access
|
* @lock: Lock to protect register and cache data access
|
||||||
* @reg_base: Register base address (virtual)
|
* @reg_base: Register base address (virtual)
|
||||||
|
* @reg_readl: Alternate I/O accessor (defaults to readl if NULL)
|
||||||
|
* @reg_writel: Alternate I/O accessor (defaults to writel if NULL)
|
||||||
* @irq_base: Interrupt base nr for this chip
|
* @irq_base: Interrupt base nr for this chip
|
||||||
* @irq_cnt: Number of interrupts handled by this chip
|
* @irq_cnt: Number of interrupts handled by this chip
|
||||||
* @mask_cache: Cached mask register shared between all chip types
|
* @mask_cache: Cached mask register shared between all chip types
|
||||||
@ -716,6 +712,8 @@ struct irq_chip_type {
|
|||||||
struct irq_chip_generic {
|
struct irq_chip_generic {
|
||||||
raw_spinlock_t lock;
|
raw_spinlock_t lock;
|
||||||
void __iomem *reg_base;
|
void __iomem *reg_base;
|
||||||
|
u32 (*reg_readl)(void __iomem *addr);
|
||||||
|
void (*reg_writel)(u32 val, void __iomem *addr);
|
||||||
unsigned int irq_base;
|
unsigned int irq_base;
|
||||||
unsigned int irq_cnt;
|
unsigned int irq_cnt;
|
||||||
u32 mask_cache;
|
u32 mask_cache;
|
||||||
@ -740,12 +738,14 @@ struct irq_chip_generic {
|
|||||||
* the parent irq. Usually GPIO implementations
|
* the parent irq. Usually GPIO implementations
|
||||||
* @IRQ_GC_MASK_CACHE_PER_TYPE: Mask cache is chip type private
|
* @IRQ_GC_MASK_CACHE_PER_TYPE: Mask cache is chip type private
|
||||||
* @IRQ_GC_NO_MASK: Do not calculate irq_data->mask
|
* @IRQ_GC_NO_MASK: Do not calculate irq_data->mask
|
||||||
|
* @IRQ_GC_BE_IO: Use big-endian register accesses (default: LE)
|
||||||
*/
|
*/
|
||||||
enum irq_gc_flags {
|
enum irq_gc_flags {
|
||||||
IRQ_GC_INIT_MASK_CACHE = 1 << 0,
|
IRQ_GC_INIT_MASK_CACHE = 1 << 0,
|
||||||
IRQ_GC_INIT_NESTED_LOCK = 1 << 1,
|
IRQ_GC_INIT_NESTED_LOCK = 1 << 1,
|
||||||
IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2,
|
IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2,
|
||||||
IRQ_GC_NO_MASK = 1 << 3,
|
IRQ_GC_NO_MASK = 1 << 3,
|
||||||
|
IRQ_GC_BE_IO = 1 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -821,4 +821,22 @@ static inline void irq_gc_lock(struct irq_chip_generic *gc) { }
|
|||||||
static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
|
static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline void irq_reg_writel(struct irq_chip_generic *gc,
|
||||||
|
u32 val, int reg_offset)
|
||||||
|
{
|
||||||
|
if (gc->reg_writel)
|
||||||
|
gc->reg_writel(val, gc->reg_base + reg_offset);
|
||||||
|
else
|
||||||
|
writel(val, gc->reg_base + reg_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 irq_reg_readl(struct irq_chip_generic *gc,
|
||||||
|
int reg_offset)
|
||||||
|
{
|
||||||
|
if (gc->reg_readl)
|
||||||
|
return gc->reg_readl(gc->reg_base + reg_offset);
|
||||||
|
else
|
||||||
|
return readl(gc->reg_base + reg_offset);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _LINUX_IRQ_H */
|
#endif /* _LINUX_IRQ_H */
|
||||||
|
|||||||
@ -39,7 +39,7 @@ void irq_gc_mask_disable_reg(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.disable);
|
irq_reg_writel(gc, mask, ct->regs.disable);
|
||||||
*ct->mask_cache &= ~mask;
|
*ct->mask_cache &= ~mask;
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ void irq_gc_mask_set_bit(struct irq_data *d)
|
|||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
*ct->mask_cache |= mask;
|
*ct->mask_cache |= mask;
|
||||||
irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask);
|
irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit);
|
EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit);
|
||||||
@ -79,7 +79,7 @@ void irq_gc_mask_clr_bit(struct irq_data *d)
|
|||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
*ct->mask_cache &= ~mask;
|
*ct->mask_cache &= ~mask;
|
||||||
irq_reg_writel(*ct->mask_cache, gc->reg_base + ct->regs.mask);
|
irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit);
|
EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit);
|
||||||
@ -98,7 +98,7 @@ void irq_gc_unmask_enable_reg(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.enable);
|
irq_reg_writel(gc, mask, ct->regs.enable);
|
||||||
*ct->mask_cache |= mask;
|
*ct->mask_cache |= mask;
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ void irq_gc_ack_set_bit(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.ack);
|
irq_reg_writel(gc, mask, ct->regs.ack);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit);
|
EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit);
|
||||||
@ -130,7 +130,7 @@ void irq_gc_ack_clr_bit(struct irq_data *d)
|
|||||||
u32 mask = ~d->mask;
|
u32 mask = ~d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.ack);
|
irq_reg_writel(gc, mask, ct->regs.ack);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +145,8 @@ void irq_gc_mask_disable_reg_and_ack(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.mask);
|
irq_reg_writel(gc, mask, ct->regs.mask);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.ack);
|
irq_reg_writel(gc, mask, ct->regs.ack);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ void irq_gc_eoi(struct irq_data *d)
|
|||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
irq_reg_writel(mask, gc->reg_base + ct->regs.eoi);
|
irq_reg_writel(gc, mask, ct->regs.eoi);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +191,16 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 irq_readl_be(void __iomem *addr)
|
||||||
|
{
|
||||||
|
return ioread32be(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void irq_writel_be(u32 val, void __iomem *addr)
|
||||||
|
{
|
||||||
|
iowrite32be(val, addr);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
|
irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
|
||||||
int num_ct, unsigned int irq_base,
|
int num_ct, unsigned int irq_base,
|
||||||
@ -245,7 +255,7 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
|
|||||||
}
|
}
|
||||||
ct[i].mask_cache = mskptr;
|
ct[i].mask_cache = mskptr;
|
||||||
if (flags & IRQ_GC_INIT_MASK_CACHE)
|
if (flags & IRQ_GC_INIT_MASK_CACHE)
|
||||||
*mskptr = irq_reg_readl(gc->reg_base + mskreg);
|
*mskptr = irq_reg_readl(gc, mskreg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +310,13 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
|
|||||||
dgc->gc[i] = gc = tmp;
|
dgc->gc[i] = gc = tmp;
|
||||||
irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
|
irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
|
||||||
NULL, handler);
|
NULL, handler);
|
||||||
|
|
||||||
gc->domain = d;
|
gc->domain = d;
|
||||||
|
if (gcflags & IRQ_GC_BE_IO) {
|
||||||
|
gc->reg_readl = &irq_readl_be;
|
||||||
|
gc->reg_writel = &irq_writel_be;
|
||||||
|
}
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&gc_lock, flags);
|
raw_spin_lock_irqsave(&gc_lock, flags);
|
||||||
list_add_tail(&gc->list, &gc_list);
|
list_add_tail(&gc->list, &gc_list);
|
||||||
raw_spin_unlock_irqrestore(&gc_lock, flags);
|
raw_spin_unlock_irqrestore(&gc_lock, flags);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user