Skip to content

Commit

Permalink
sh: intc: IRQ auto-distribution support.
Browse files Browse the repository at this point in the history
This implements support for hardware-managed IRQ balancing as implemented
by SH-X3 cores (presently only hooked up for SH7786, but can probably be
carried over to other SH-X3 cores, too).

CPUs need to specify their distribution register along with the mask
definitions, as these follow the same format. Peripheral IRQs that don't
opt out of balancing will be automatically distributed at the whim of the
hardware block, while each CPU needs to verify whether it is handling the
IRQ or not, especially before clearing the mask.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed Apr 15, 2010
1 parent fecf066 commit dc825b1
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 79 deletions.
16 changes: 16 additions & 0 deletions arch/sh/include/asm/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
#define NR_IRQS 256
#define NR_IRQS_LEGACY 8 /* Legacy external IRQ0-7 */

/*
* This is a special IRQ number for indicating that no IRQ has been
* triggered and to simply ignore the IRQ dispatch. This is a special
* case that can happen with IRQ auto-distribution when multiple CPUs
* are woken up and signalled in parallel.
*/
#define NO_IRQ_IGNORE ((unsigned int)-1)

/*
* Convert back and forth between INTEVT and IRQ values.
*/
Expand Down Expand Up @@ -53,6 +61,14 @@ extern void irq_ctx_exit(int cpu);
# define irq_ctx_exit(cpu) do { } while (0)
#endif

#ifdef CONFIG_INTC_BALANCING
extern unsigned int irq_lookup(unsigned int irq);
extern void irq_finish(unsigned int irq);
#else
#define irq_lookup(irq) (irq)
#define irq_finish(irq) do { } while (0)
#endif

#include <asm-generic/irq.h>
#ifdef CONFIG_CPU_SH5
#include <cpu/irq.h>
Expand Down
36 changes: 30 additions & 6 deletions arch/sh/kernel/cpu/sh4a/setup-sh7786.c
Original file line number Diff line number Diff line change
Expand Up @@ -573,14 +573,14 @@ static struct platform_device *sh7786_devices[] __initdata = {
&usb_ohci_device,
};


/*
* Please call this function if your platform board
* use external clock for USB
* */
#define USBCTL0 0xffe70858
#define CLOCK_MODE_MASK 0xffffff7f
#define EXT_CLOCK_MODE 0x00000080

void __init sh7786_usb_use_exclock(void)
{
u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK;
Expand All @@ -598,6 +598,7 @@ void __init sh7786_usb_use_exclock(void)
#define PLL_ENB 0x00000002
#define PHY_RST 0x00000004
#define ACT_PLL_STATUS 0xc0000000

static void __init sh7786_usb_setup(void)
{
int i = 1000000;
Expand Down Expand Up @@ -753,9 +754,19 @@ static struct intc_vect vectors[] __initdata = {
#define INTMSK2 0xfe410068
#define INTMSKCLR2 0xfe41006c

#define INTDISTCR0 0xfe4100b0
#define INTDISTCR1 0xfe4100b4
#define INTACK 0xfe4100b8
#define INTACKCLR 0xfe4100bc
#define INT2DISTCR0 0xfe410900
#define INT2DISTCR1 0xfe410904
#define INT2DISTCR2 0xfe410908
#define INT2DISTCR3 0xfe41090c

static struct intc_mask_reg mask_registers[] __initdata = {
{ CnINTMSK0, CnINTMSKCLR0, 32,
{ IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } },
{ IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 },
INTC_SMP_BALANCING(INTDISTCR0) },
{ INTMSK2, INTMSKCLR2, 32,
{ IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH,
IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH,
Expand All @@ -767,7 +778,8 @@ static struct intc_mask_reg mask_registers[] __initdata = {
IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } },
{ CnINT2MSKR0, CnINT2MSKCR0 , 32,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT } },
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT },
INTC_SMP_BALANCING(INT2DISTCR0) },
{ CnINT2MSKR1, CnINT2MSKCR1, 32,
{ TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0,
DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6,
Expand All @@ -776,14 +788,14 @@ static struct intc_mask_reg mask_registers[] __initdata = {
HPB_0, HPB_1, HPB_2,
SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3,
SCIF1,
TMU2, TMU3, 0, } },
TMU2, TMU3, 0, }, INTC_SMP_BALANCING(INT2DISTCR1) },
{ CnINT2MSKR2, CnINT2MSKCR2, 32,
{ 0, 0, SCIF2, SCIF3, SCIF4, SCIF5,
Eth_0, Eth_1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
PCIeC0_0, PCIeC0_1, PCIeC0_2,
PCIeC1_0, PCIeC1_1, PCIeC1_2,
USB, 0, 0 } },
USB, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR2) },
{ CnINT2MSKR3, CnINT2MSKCR3, 32,
{ 0, 0, 0, 0, 0, 0,
I2C0, I2C1,
Expand All @@ -792,7 +804,7 @@ static struct intc_mask_reg mask_registers[] __initdata = {
HAC0, HAC1,
FLCTL, 0,
HSPI, GPIO0, GPIO1, Thermal,
0, 0, 0, 0, 0, 0, 0, 0 } },
0, 0, 0, 0, 0, 0, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR3) },
};

static struct intc_prio_reg prio_registers[] __initdata = {
Expand Down Expand Up @@ -910,6 +922,18 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
#define INTC_INTMSKCLR2 INTMSKCLR2
#define INTC_USERIMASK 0xfe411000

#ifdef CONFIG_INTC_BALANCING
unsigned int irq_lookup(unsigned int irq)
{
return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE;
}

void irq_finish(unsigned int irq)
{
__raw_writel(irq2evt(irq), INTACKCLR);
}
#endif

void __init plat_irq_setup(void)
{
/* disable IRQ3-0 + IRQ7-4 */
Expand Down
49 changes: 29 additions & 20 deletions arch/sh/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,14 @@ union irq_ctx {

static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly;
static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly;
#endif

asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs)
static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;
static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;

static inline void handle_one_irq(unsigned int irq)
{
struct pt_regs *old_regs = set_irq_regs(regs);
#ifdef CONFIG_IRQSTACKS
union irq_ctx *curctx, *irqctx;
#endif

irq_enter();
irq = irq_demux(irq);

#ifdef CONFIG_IRQSTACKS
curctx = (union irq_ctx *)current_thread_info();
irqctx = hardirq_ctx[smp_processor_id()];

Expand Down Expand Up @@ -164,20 +159,9 @@ asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs)
"r5", "r6", "r7", "r8", "t", "pr"
);
} else
#endif
generic_handle_irq(irq);

irq_exit();

set_irq_regs(old_regs);
return 1;
}

#ifdef CONFIG_IRQSTACKS
static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;

static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss;

/*
* allocate per-cpu stacks for hardirq and for softirq processing
*/
Expand Down Expand Up @@ -257,8 +241,33 @@ asmlinkage void do_softirq(void)

local_irq_restore(flags);
}
#else
static inline void handle_one_irq(unsigned int irq)
{
generic_handle_irq(irq);
}
#endif

asmlinkage __irq_entry int do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);

irq_enter();

irq = irq_demux(irq_lookup(irq));

if (irq != NO_IRQ_IGNORE) {
handle_one_irq(irq);
irq_finish(irq);
}

irq_exit();

set_irq_regs(old_regs);

return IRQ_HANDLED;
}

void __init init_IRQ(void)
{
plat_irq_setup();
Expand Down
11 changes: 11 additions & 0 deletions drivers/sh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ config INTC_USERIMASK
drivers that are using special priority levels.

If in doubt, say N.

config INTC_BALANCING
bool "Hardware IRQ balancing support"
depends on SMP && SUPERH && CPU_SUBTYPE_SH7786
help
This enables support for IRQ auto-distribution mode on SH-X3
SMP parts. All of the balancing and CPU wakeup decisions are
taken care of automatically by hardware for distributed
vectors.

If in doubt, say N.
Loading

0 comments on commit dc825b1

Please sign in to comment.