diff mbox

[U-Boot,1/4] x86: Refactor PIRQ routing support

Message ID BLU436-SMTP170ACEE180D2866BC921266BFC20@phx.gbl
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Bin Meng May 20, 2015, 7:55 a.m. UTC
PIRQ routing is pretty much common in Intel chipset. It has several
PIRQ links (normally 8) and corresponding registers (either in PCI
configuration space or memory-mapped IBASE) to configure the legacy
8259 IRQ vector mapping. Refactor current Queensbay PIRQ routing
support using device tree and move it to a common place, so that we
can easily add PIRQ routing support on a new platform.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
---

 arch/x86/cpu/Makefile                            |   2 +-
 arch/x86/cpu/irq.c                               | 221 +++++++++++++++++++++
 arch/x86/cpu/queensbay/Makefile                  |   2 +-
 arch/x86/cpu/queensbay/irq.c                     | 242 -----------------------
 arch/x86/cpu/queensbay/tnc.c                     |  39 +++-
 arch/x86/dts/crownbay.dts                        |  54 +++++
 arch/x86/include/asm/arch-queensbay/irq.h        |  55 ------
 arch/x86/include/asm/irq.h                       | 105 ++++++++++
 include/dt-bindings/interrupt-router/intel-irq.h |  29 +++
 include/fdtdec.h                                 |   1 +
 lib/fdtdec.c                                     |   1 +
 11 files changed, 451 insertions(+), 300 deletions(-)
 create mode 100644 arch/x86/cpu/irq.c
 delete mode 100644 arch/x86/cpu/queensbay/irq.c
 delete mode 100644 arch/x86/include/asm/arch-queensbay/irq.h
 create mode 100644 arch/x86/include/asm/irq.h
 create mode 100644 include/dt-bindings/interrupt-router/intel-irq.h

Comments

Simon Glass May 20, 2015, 5:45 p.m. UTC | #1
Hi Bin,

On 20 May 2015 at 01:55, Bin Meng <bmeng.cn@gmail.com> wrote:
> PIRQ routing is pretty much common in Intel chipset. It has several
> PIRQ links (normally 8) and corresponding registers (either in PCI
> configuration space or memory-mapped IBASE) to configure the legacy
> 8259 IRQ vector mapping. Refactor current Queensbay PIRQ routing
> support using device tree and move it to a common place, so that we
> can easily add PIRQ routing support on a new platform.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/cpu/Makefile                            |   2 +-
>  arch/x86/cpu/irq.c                               | 221 +++++++++++++++++++++
>  arch/x86/cpu/queensbay/Makefile                  |   2 +-
>  arch/x86/cpu/queensbay/irq.c                     | 242 -----------------------
>  arch/x86/cpu/queensbay/tnc.c                     |  39 +++-
>  arch/x86/dts/crownbay.dts                        |  54 +++++
>  arch/x86/include/asm/arch-queensbay/irq.h        |  55 ------
>  arch/x86/include/asm/irq.h                       | 105 ++++++++++
>  include/dt-bindings/interrupt-router/intel-irq.h |  29 +++
>  include/fdtdec.h                                 |   1 +
>  lib/fdtdec.c                                     |   1 +
>  11 files changed, 451 insertions(+), 300 deletions(-)
>  create mode 100644 arch/x86/cpu/irq.c
>  delete mode 100644 arch/x86/cpu/queensbay/irq.c
>  delete mode 100644 arch/x86/include/asm/arch-queensbay/irq.h
>  create mode 100644 arch/x86/include/asm/irq.h
>  create mode 100644 include/dt-bindings/interrupt-router/intel-irq.h

Great to see this! Nit below.

[snip]

> diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
> new file mode 100644
> index 0000000..a7ad462
> --- /dev/null
> +++ b/arch/x86/include/asm/irq.h
> @@ -0,0 +1,105 @@
> +/*
> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef _ARCH_IRQ_H_
> +#define _ARCH_IRQ_H_
> +
> +enum pci_int_pin {
> +       INTX,
> +       INTA,
> +       INTB,
> +       INTC,
> +       INTD
> +};
> +
> +enum pirq_pin {
> +       PIRQA,
> +       PIRQB,
> +       PIRQC,
> +       PIRQD,
> +       PIRQE,
> +       PIRQF,
> +       PIRQG,
> +       PIRQH
> +};
> +
> +/**
> + * Intel interrupt router configuration mechanism
> + *
> + * There are two known ways of Intel interrupt router configuration mechanism
> + * so far. On most cases, the IRQ routing configuraiton is controlled by PCI
> + * configuraiton registers on the legacy bridge, normally PCI BDF(0, 31, 0).
> + * On some newer platforms like BayTrail and Braswell, the IRQ routing is now
> + * in the IBASE register block where IBASE is memory-mapped.
> + */
> +enum pirq_config {
> +       PIRQ_VIA_PCI,
> +       PIRQ_VIA_IBASE
> +};
> +
> +/**
> + * Intel interrupt router control block
> + *
> + * Its members' value will be filled in based on device tree's input.
> + *
> + * @config:    PIRQ_VIA_PCI or PIRQ_VIA_IBASE
> + * @link_base: link value base number
> + * @irq_mask:  IRQ mask reprenting the 16 IRQs in 8259, bit N is 1 means
> + *             IRQ N is available to be routed
> + * @lb_bdf:    irq router's PCI bus/device/function number encoding
> + * @ibase:     IBASE register block base address
> + */
> +struct irq_router {
> +       int config;
> +       u32 link_base;
> +       u16 irq_mask;
> +       u32 bdf;
> +       u32 ibase;
> +};
> +
> +struct pirq_routing {
> +       int bdf;
> +       int pin;
> +       int pirq;
> +};
> +
> +/* PIRQ link number and value conversion */
> +#define LINK_V2N(link, base)   (link - base)
> +#define LINK_N2V(link, base)   (link + base)
> +
> +#define PIRQ_BITMAP            0xdef8
> +
> +struct irq_info;
> +
> +/**
> + * board_fill_irq_info() - Board-specific irq_info fill routine
> + *
> + * This fills the irq_info table for any board-specific add-in cards.
> + *
> + * @slot:      pointer to the struct irq_info that is to be filled in
> + * @return:    number of entries were written to the struct irq_info
> + */
> +int board_fill_irq_info(struct irq_info *slot);
> +
> +/**
> + * cpu_irq_init() - Initialize CPU IRQ routing
> + *
> + * This initializes some platform-specific registers related to IRQ routing,
> + * like configuring internal PCI devices to use which PCI interrupt pin,
> + * and which PCI interrupt pin is mapped to which PIRQ line. Note on some
> + * platforms, such IRQ routing might be hard-coded thus cannot configure.
> + */
> +void cpu_irq_init(void);
> +
> +/**
> + * pirq_init() - Initialize platform PIRQ routing
> + *
> + * This initializes the PIRQ routing on the platform and configures all PCI
> + * devices' interrupt line register to a working IRQ number on the 8259 PIC.
> + */
> +void pirq_init(void);
> +
> +#endif /* _ARCH_IRQ_H_ */
> diff --git a/include/dt-bindings/interrupt-router/intel-irq.h b/include/dt-bindings/interrupt-router/intel-irq.h
> new file mode 100644
> index 0000000..41077ef
> --- /dev/null
> +++ b/include/dt-bindings/interrupt-router/intel-irq.h
> @@ -0,0 +1,29 @@
> +/*
> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef _DT_BINDINGS_INTEL_IRQ_H_
> +#define _DT_BINDINGS_INTEL_IRQ_H_
> +
> +/* PCI interrupt pin */
> +#define INTA                   1
> +#define INTB                   2
> +#define INTC                   3
> +#define INTD                   4
> +
> +/* PIRQs */
> +#define PIRQA                  0
> +#define PIRQB                  1
> +#define PIRQC                  2
> +#define PIRQD                  3
> +#define PIRQE                  4
> +#define PIRQF                  5
> +#define PIRQG                  6
> +#define PIRQH                  7

Can these go in a common file or can this file be included by the
other file? Really want to avoid duplicate code.

> +
> +/* PCI bdf encoding */
> +#define PCI_BDF(b, d, f)       ((b) << 16 | (d) << 11 | (f) << 8)
> +
> +#endif /* _DT_BINDINGS_INTEL_IRQ_H_ */
> diff --git a/include/fdtdec.h b/include/fdtdec.h
> index 6590470..dc1c85b 100644
> --- a/include/fdtdec.h
> +++ b/include/fdtdec.h
> @@ -177,6 +177,7 @@ enum fdt_compat_id {
>         COMPAT_INTEL_QRK_MRC,           /* Intel Quark MRC */
>         COMPAT_SOCIONEXT_XHCI,          /* Socionext UniPhier xHCI */
>         COMPAT_INTEL_PCH,               /* Intel PCH */
> +       COMPAT_INTEL_IRQ_ROUTER,        /* Intel Interrupt Router */
>
>         COMPAT_COUNT,
>  };
> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
> index 80b897a..22d1e39 100644
> --- a/lib/fdtdec.c
> +++ b/lib/fdtdec.c
> @@ -76,6 +76,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
>         COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
>         COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"),
>         COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"),
> +       COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"),
>  };
>
>  const char *fdtdec_get_compatible(enum fdt_compat_id id)
> --
> 1.8.2.1
>

Do you think (later) this might be a target to move to driver model?

Regards,
Simon
Bin Meng May 23, 2015, 4:22 p.m. UTC | #2
Hi Simon,

On Thu, May 21, 2015 at 1:45 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 20 May 2015 at 01:55, Bin Meng <bmeng.cn@gmail.com> wrote:
>> PIRQ routing is pretty much common in Intel chipset. It has several
>> PIRQ links (normally 8) and corresponding registers (either in PCI
>> configuration space or memory-mapped IBASE) to configure the legacy
>> 8259 IRQ vector mapping. Refactor current Queensbay PIRQ routing
>> support using device tree and move it to a common place, so that we
>> can easily add PIRQ routing support on a new platform.
>>
>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>> ---
>>
>>  arch/x86/cpu/Makefile                            |   2 +-
>>  arch/x86/cpu/irq.c                               | 221 +++++++++++++++++++++
>>  arch/x86/cpu/queensbay/Makefile                  |   2 +-
>>  arch/x86/cpu/queensbay/irq.c                     | 242 -----------------------
>>  arch/x86/cpu/queensbay/tnc.c                     |  39 +++-
>>  arch/x86/dts/crownbay.dts                        |  54 +++++
>>  arch/x86/include/asm/arch-queensbay/irq.h        |  55 ------
>>  arch/x86/include/asm/irq.h                       | 105 ++++++++++
>>  include/dt-bindings/interrupt-router/intel-irq.h |  29 +++
>>  include/fdtdec.h                                 |   1 +
>>  lib/fdtdec.c                                     |   1 +
>>  11 files changed, 451 insertions(+), 300 deletions(-)
>>  create mode 100644 arch/x86/cpu/irq.c
>>  delete mode 100644 arch/x86/cpu/queensbay/irq.c
>>  delete mode 100644 arch/x86/include/asm/arch-queensbay/irq.h
>>  create mode 100644 arch/x86/include/asm/irq.h
>>  create mode 100644 include/dt-bindings/interrupt-router/intel-irq.h
>
> Great to see this! Nit below.
>
> [snip]
>
>> diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
>> new file mode 100644
>> index 0000000..a7ad462
>> --- /dev/null
>> +++ b/arch/x86/include/asm/irq.h
>> @@ -0,0 +1,105 @@
>> +/*
>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef _ARCH_IRQ_H_
>> +#define _ARCH_IRQ_H_
>> +
>> +enum pci_int_pin {
>> +       INTX,
>> +       INTA,
>> +       INTB,
>> +       INTC,
>> +       INTD
>> +};
>> +
>> +enum pirq_pin {
>> +       PIRQA,
>> +       PIRQB,
>> +       PIRQC,
>> +       PIRQD,
>> +       PIRQE,
>> +       PIRQF,
>> +       PIRQG,
>> +       PIRQH
>> +};
>> +
>> +/**
>> + * Intel interrupt router configuration mechanism
>> + *
>> + * There are two known ways of Intel interrupt router configuration mechanism
>> + * so far. On most cases, the IRQ routing configuraiton is controlled by PCI
>> + * configuraiton registers on the legacy bridge, normally PCI BDF(0, 31, 0).
>> + * On some newer platforms like BayTrail and Braswell, the IRQ routing is now
>> + * in the IBASE register block where IBASE is memory-mapped.
>> + */
>> +enum pirq_config {
>> +       PIRQ_VIA_PCI,
>> +       PIRQ_VIA_IBASE
>> +};
>> +
>> +/**
>> + * Intel interrupt router control block
>> + *
>> + * Its members' value will be filled in based on device tree's input.
>> + *
>> + * @config:    PIRQ_VIA_PCI or PIRQ_VIA_IBASE
>> + * @link_base: link value base number
>> + * @irq_mask:  IRQ mask reprenting the 16 IRQs in 8259, bit N is 1 means
>> + *             IRQ N is available to be routed
>> + * @lb_bdf:    irq router's PCI bus/device/function number encoding
>> + * @ibase:     IBASE register block base address
>> + */
>> +struct irq_router {
>> +       int config;
>> +       u32 link_base;
>> +       u16 irq_mask;
>> +       u32 bdf;
>> +       u32 ibase;
>> +};
>> +
>> +struct pirq_routing {
>> +       int bdf;
>> +       int pin;
>> +       int pirq;
>> +};
>> +
>> +/* PIRQ link number and value conversion */
>> +#define LINK_V2N(link, base)   (link - base)
>> +#define LINK_N2V(link, base)   (link + base)
>> +
>> +#define PIRQ_BITMAP            0xdef8
>> +
>> +struct irq_info;
>> +
>> +/**
>> + * board_fill_irq_info() - Board-specific irq_info fill routine
>> + *
>> + * This fills the irq_info table for any board-specific add-in cards.
>> + *
>> + * @slot:      pointer to the struct irq_info that is to be filled in
>> + * @return:    number of entries were written to the struct irq_info
>> + */
>> +int board_fill_irq_info(struct irq_info *slot);
>> +
>> +/**
>> + * cpu_irq_init() - Initialize CPU IRQ routing
>> + *
>> + * This initializes some platform-specific registers related to IRQ routing,
>> + * like configuring internal PCI devices to use which PCI interrupt pin,
>> + * and which PCI interrupt pin is mapped to which PIRQ line. Note on some
>> + * platforms, such IRQ routing might be hard-coded thus cannot configure.
>> + */
>> +void cpu_irq_init(void);
>> +
>> +/**
>> + * pirq_init() - Initialize platform PIRQ routing
>> + *
>> + * This initializes the PIRQ routing on the platform and configures all PCI
>> + * devices' interrupt line register to a working IRQ number on the 8259 PIC.
>> + */
>> +void pirq_init(void);
>> +
>> +#endif /* _ARCH_IRQ_H_ */
>> diff --git a/include/dt-bindings/interrupt-router/intel-irq.h b/include/dt-bindings/interrupt-router/intel-irq.h
>> new file mode 100644
>> index 0000000..41077ef
>> --- /dev/null
>> +++ b/include/dt-bindings/interrupt-router/intel-irq.h
>> @@ -0,0 +1,29 @@
>> +/*
>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef _DT_BINDINGS_INTEL_IRQ_H_
>> +#define _DT_BINDINGS_INTEL_IRQ_H_
>> +
>> +/* PCI interrupt pin */
>> +#define INTA                   1
>> +#define INTB                   2
>> +#define INTC                   3
>> +#define INTD                   4
>> +
>> +/* PIRQs */
>> +#define PIRQA                  0
>> +#define PIRQB                  1
>> +#define PIRQC                  2
>> +#define PIRQD                  3
>> +#define PIRQE                  4
>> +#define PIRQF                  5
>> +#define PIRQG                  6
>> +#define PIRQH                  7
>
> Can these go in a common file or can this file be included by the
> other file? Really want to avoid duplicate code.

I think we can have the defines here in
include/dt-bindings/interrupt-router/intel-irq.h and have
arch/x86/include/asm/irq.h to include
include/dt-bindings/interrupt-router/intel-irq.h. Would that be OK?

>> +
>> +/* PCI bdf encoding */
>> +#define PCI_BDF(b, d, f)       ((b) << 16 | (d) << 11 | (f) << 8)
>> +
>> +#endif /* _DT_BINDINGS_INTEL_IRQ_H_ */
>> diff --git a/include/fdtdec.h b/include/fdtdec.h
>> index 6590470..dc1c85b 100644
>> --- a/include/fdtdec.h
>> +++ b/include/fdtdec.h
>> @@ -177,6 +177,7 @@ enum fdt_compat_id {
>>         COMPAT_INTEL_QRK_MRC,           /* Intel Quark MRC */
>>         COMPAT_SOCIONEXT_XHCI,          /* Socionext UniPhier xHCI */
>>         COMPAT_INTEL_PCH,               /* Intel PCH */
>> +       COMPAT_INTEL_IRQ_ROUTER,        /* Intel Interrupt Router */
>>
>>         COMPAT_COUNT,
>>  };
>> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
>> index 80b897a..22d1e39 100644
>> --- a/lib/fdtdec.c
>> +++ b/lib/fdtdec.c
>> @@ -76,6 +76,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
>>         COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
>>         COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"),
>>         COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"),
>> +       COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"),
>>  };
>>
>>  const char *fdtdec_get_compatible(enum fdt_compat_id id)
>> --
>> 1.8.2.1
>>
>
> Do you think (later) this might be a target to move to driver model?
>

Maybe, if we want to support more chipset's IRQ router. But I guess
that might not be the case since we may only support Intel processors
in U-Boot?

Regards,
Bin
Simon Glass May 23, 2015, 4:36 p.m. UTC | #3
Hi Bin,

On 23 May 2015 at 10:22, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Thu, May 21, 2015 at 1:45 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Bin,
>>
>> On 20 May 2015 at 01:55, Bin Meng <bmeng.cn@gmail.com> wrote:
>>> PIRQ routing is pretty much common in Intel chipset. It has several
>>> PIRQ links (normally 8) and corresponding registers (either in PCI
>>> configuration space or memory-mapped IBASE) to configure the legacy
>>> 8259 IRQ vector mapping. Refactor current Queensbay PIRQ routing
>>> support using device tree and move it to a common place, so that we
>>> can easily add PIRQ routing support on a new platform.
>>>
>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>>
>>>  arch/x86/cpu/Makefile                            |   2 +-
>>>  arch/x86/cpu/irq.c                               | 221 +++++++++++++++++++++
>>>  arch/x86/cpu/queensbay/Makefile                  |   2 +-
>>>  arch/x86/cpu/queensbay/irq.c                     | 242 -----------------------
>>>  arch/x86/cpu/queensbay/tnc.c                     |  39 +++-
>>>  arch/x86/dts/crownbay.dts                        |  54 +++++
>>>  arch/x86/include/asm/arch-queensbay/irq.h        |  55 ------
>>>  arch/x86/include/asm/irq.h                       | 105 ++++++++++
>>>  include/dt-bindings/interrupt-router/intel-irq.h |  29 +++
>>>  include/fdtdec.h                                 |   1 +
>>>  lib/fdtdec.c                                     |   1 +
>>>  11 files changed, 451 insertions(+), 300 deletions(-)
>>>  create mode 100644 arch/x86/cpu/irq.c
>>>  delete mode 100644 arch/x86/cpu/queensbay/irq.c
>>>  delete mode 100644 arch/x86/include/asm/arch-queensbay/irq.h
>>>  create mode 100644 arch/x86/include/asm/irq.h
>>>  create mode 100644 include/dt-bindings/interrupt-router/intel-irq.h
>>
>> Great to see this! Nit below.
>>
>> [snip]
>>
>>> diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
>>> new file mode 100644
>>> index 0000000..a7ad462
>>> --- /dev/null
>>> +++ b/arch/x86/include/asm/irq.h
>>> @@ -0,0 +1,105 @@
>>> +/*
>>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +
>>> +#ifndef _ARCH_IRQ_H_
>>> +#define _ARCH_IRQ_H_
>>> +
>>> +enum pci_int_pin {
>>> +       INTX,
>>> +       INTA,
>>> +       INTB,
>>> +       INTC,
>>> +       INTD
>>> +};
>>> +
>>> +enum pirq_pin {
>>> +       PIRQA,
>>> +       PIRQB,
>>> +       PIRQC,
>>> +       PIRQD,
>>> +       PIRQE,
>>> +       PIRQF,
>>> +       PIRQG,
>>> +       PIRQH
>>> +};
>>> +
>>> +/**
>>> + * Intel interrupt router configuration mechanism
>>> + *
>>> + * There are two known ways of Intel interrupt router configuration mechanism
>>> + * so far. On most cases, the IRQ routing configuraiton is controlled by PCI
>>> + * configuraiton registers on the legacy bridge, normally PCI BDF(0, 31, 0).
>>> + * On some newer platforms like BayTrail and Braswell, the IRQ routing is now
>>> + * in the IBASE register block where IBASE is memory-mapped.
>>> + */
>>> +enum pirq_config {
>>> +       PIRQ_VIA_PCI,
>>> +       PIRQ_VIA_IBASE
>>> +};
>>> +
>>> +/**
>>> + * Intel interrupt router control block
>>> + *
>>> + * Its members' value will be filled in based on device tree's input.
>>> + *
>>> + * @config:    PIRQ_VIA_PCI or PIRQ_VIA_IBASE
>>> + * @link_base: link value base number
>>> + * @irq_mask:  IRQ mask reprenting the 16 IRQs in 8259, bit N is 1 means
>>> + *             IRQ N is available to be routed
>>> + * @lb_bdf:    irq router's PCI bus/device/function number encoding
>>> + * @ibase:     IBASE register block base address
>>> + */
>>> +struct irq_router {
>>> +       int config;
>>> +       u32 link_base;
>>> +       u16 irq_mask;
>>> +       u32 bdf;
>>> +       u32 ibase;
>>> +};
>>> +
>>> +struct pirq_routing {
>>> +       int bdf;
>>> +       int pin;
>>> +       int pirq;
>>> +};
>>> +
>>> +/* PIRQ link number and value conversion */
>>> +#define LINK_V2N(link, base)   (link - base)
>>> +#define LINK_N2V(link, base)   (link + base)
>>> +
>>> +#define PIRQ_BITMAP            0xdef8
>>> +
>>> +struct irq_info;
>>> +
>>> +/**
>>> + * board_fill_irq_info() - Board-specific irq_info fill routine
>>> + *
>>> + * This fills the irq_info table for any board-specific add-in cards.
>>> + *
>>> + * @slot:      pointer to the struct irq_info that is to be filled in
>>> + * @return:    number of entries were written to the struct irq_info
>>> + */
>>> +int board_fill_irq_info(struct irq_info *slot);
>>> +
>>> +/**
>>> + * cpu_irq_init() - Initialize CPU IRQ routing
>>> + *
>>> + * This initializes some platform-specific registers related to IRQ routing,
>>> + * like configuring internal PCI devices to use which PCI interrupt pin,
>>> + * and which PCI interrupt pin is mapped to which PIRQ line. Note on some
>>> + * platforms, such IRQ routing might be hard-coded thus cannot configure.
>>> + */
>>> +void cpu_irq_init(void);
>>> +
>>> +/**
>>> + * pirq_init() - Initialize platform PIRQ routing
>>> + *
>>> + * This initializes the PIRQ routing on the platform and configures all PCI
>>> + * devices' interrupt line register to a working IRQ number on the 8259 PIC.
>>> + */
>>> +void pirq_init(void);
>>> +
>>> +#endif /* _ARCH_IRQ_H_ */
>>> diff --git a/include/dt-bindings/interrupt-router/intel-irq.h b/include/dt-bindings/interrupt-router/intel-irq.h
>>> new file mode 100644
>>> index 0000000..41077ef
>>> --- /dev/null
>>> +++ b/include/dt-bindings/interrupt-router/intel-irq.h
>>> @@ -0,0 +1,29 @@
>>> +/*
>>> + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +
>>> +#ifndef _DT_BINDINGS_INTEL_IRQ_H_
>>> +#define _DT_BINDINGS_INTEL_IRQ_H_
>>> +
>>> +/* PCI interrupt pin */
>>> +#define INTA                   1
>>> +#define INTB                   2
>>> +#define INTC                   3
>>> +#define INTD                   4
>>> +
>>> +/* PIRQs */
>>> +#define PIRQA                  0
>>> +#define PIRQB                  1
>>> +#define PIRQC                  2
>>> +#define PIRQD                  3
>>> +#define PIRQE                  4
>>> +#define PIRQF                  5
>>> +#define PIRQG                  6
>>> +#define PIRQH                  7
>>
>> Can these go in a common file or can this file be included by the
>> other file? Really want to avoid duplicate code.
>
> I think we can have the defines here in
> include/dt-bindings/interrupt-router/intel-irq.h and have
> arch/x86/include/asm/irq.h to include
> include/dt-bindings/interrupt-router/intel-irq.h. Would that be OK?

Yes that sounds reasonable.

>
>>> +
>>> +/* PCI bdf encoding */
>>> +#define PCI_BDF(b, d, f)       ((b) << 16 | (d) << 11 | (f) << 8)
>>> +
>>> +#endif /* _DT_BINDINGS_INTEL_IRQ_H_ */
>>> diff --git a/include/fdtdec.h b/include/fdtdec.h
>>> index 6590470..dc1c85b 100644
>>> --- a/include/fdtdec.h
>>> +++ b/include/fdtdec.h
>>> @@ -177,6 +177,7 @@ enum fdt_compat_id {
>>>         COMPAT_INTEL_QRK_MRC,           /* Intel Quark MRC */
>>>         COMPAT_SOCIONEXT_XHCI,          /* Socionext UniPhier xHCI */
>>>         COMPAT_INTEL_PCH,               /* Intel PCH */
>>> +       COMPAT_INTEL_IRQ_ROUTER,        /* Intel Interrupt Router */
>>>
>>>         COMPAT_COUNT,
>>>  };
>>> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
>>> index 80b897a..22d1e39 100644
>>> --- a/lib/fdtdec.c
>>> +++ b/lib/fdtdec.c
>>> @@ -76,6 +76,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
>>>         COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
>>>         COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"),
>>>         COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"),
>>> +       COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"),
>>>  };
>>>
>>>  const char *fdtdec_get_compatible(enum fdt_compat_id id)
>>> --
>>> 1.8.2.1
>>>
>>
>> Do you think (later) this might be a target to move to driver model?
>>
>
> Maybe, if we want to support more chipset's IRQ router. But I guess
> that might not be the case since we may only support Intel processors
> in U-Boot?

Not sure. I don't have any x86 AMD devices so it won't come from
me...but in generate we are trying to deprecate this compat table as
things move to driver model. It services mainly as a list of things to
fix.

Regards,
Simon
diff mbox

Patch

diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 307545a..7ff05e6 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -19,7 +19,7 @@  obj-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += ivybridge/
 obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
 obj-$(CONFIG_INTEL_QUARK) += quark/
 obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
-obj-y += lapic.o
+obj-y += irq.o lapic.o
 obj-$(CONFIG_SMP) += mp_init.o
 obj-y += mtrr.o
 obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/x86/cpu/irq.c b/arch/x86/cpu/irq.c
new file mode 100644
index 0000000..32dc6f2
--- /dev/null
+++ b/arch/x86/cpu/irq.c
@@ -0,0 +1,221 @@ 
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pci.h>
+#include <asm/pirq_routing.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct irq_router irq_router;
+static struct irq_routing_table *pirq_routing_table;
+
+bool pirq_check_irq_routed(int link, u8 irq)
+{
+	u8 pirq;
+	int base = irq_router.link_base;
+
+	if (irq_router.config == PIRQ_VIA_PCI)
+		pirq = x86_pci_read_config8(irq_router.bdf,
+					    LINK_N2V(link, base));
+	else
+		pirq = readb(irq_router.ibase + LINK_N2V(link, base));
+
+	pirq &= 0xf;
+
+	/* IRQ# 0/1/2/8/13 are reserved */
+	if (pirq < 3 || pirq == 8 || pirq == 13)
+		return false;
+
+	return pirq == irq ? true : false;
+}
+
+int pirq_translate_link(int link)
+{
+	return LINK_V2N(link, irq_router.link_base);
+}
+
+void pirq_assign_irq(int link, u8 irq)
+{
+	int base = irq_router.link_base;
+
+	/* IRQ# 0/1/2/8/13 are reserved */
+	if (irq < 3 || irq == 8 || irq == 13)
+		return;
+
+	if (irq_router.config == PIRQ_VIA_PCI)
+		x86_pci_write_config8(irq_router.bdf,
+				      LINK_N2V(link, base), irq);
+	else
+		writeb(irq, irq_router.ibase + LINK_N2V(link, base));
+}
+
+static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus,
+				 u8 device, u8 func, u8 pin, u8 pirq)
+{
+	struct irq_info *slot = *slotp;
+
+	slot->bus = bus;
+	slot->devfn = (device << 3) | func;
+	slot->irq[pin - 1].link = LINK_N2V(pirq, irq_router.link_base);
+	slot->irq[pin - 1].bitmap = irq_router.irq_mask;
+	(*entries)++;
+	(*slotp)++;
+}
+
+__weak int board_fill_irq_info(struct irq_info *slot)
+{
+	return 0;
+}
+
+__weak void cpu_irq_init(void)
+{
+	return;
+}
+
+static int create_pirq_routing_table(void)
+{
+	const void *blob = gd->fdt_blob;
+	struct fdt_pci_addr addr;
+	int node;
+	int len, count;
+	const u32 *cell;
+	struct irq_routing_table *rt;
+	struct irq_info *slot;
+	int irq_entries = 0;
+	int i;
+	int ret;
+
+	node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
+	if (node < 0) {
+		debug("%s: Cannot find irq router node\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = fdtdec_get_pci_addr(blob, node, FDT_PCI_SPACE_CONFIG,
+				  "reg", &addr);
+	if (ret)
+		return ret;
+
+	/* extract the bdf from fdt_pci_addr */
+	irq_router.bdf = addr.phys_hi & 0xffff00;
+
+	ret = fdt_find_string(blob, node, "intel,pirq-config", "pci");
+	if (!ret) {
+		irq_router.config = PIRQ_VIA_PCI;
+	} else {
+		ret = fdt_find_string(blob, node, "intel,pirq-config", "ibase");
+		if (!ret)
+			irq_router.config = PIRQ_VIA_IBASE;
+		else
+			return -EINVAL;
+	}
+
+	ret = fdtdec_get_int_array(blob, node, "intel,pirq-link",
+				   &irq_router.link_base, 1);
+	if (ret)
+		return ret;
+
+	irq_router.irq_mask = fdtdec_get_int(blob, node,
+					     "intel,pirq-mask", PIRQ_BITMAP);
+
+	if (irq_router.config == PIRQ_VIA_IBASE) {
+		int ibase_off;
+
+		ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0);
+		if (!ibase_off)
+			return -EINVAL;
+
+		/*
+		 * Here we assume that the IBASE register has already been
+		 * properly configured by U-Boot before.
+		 *
+		 * By 'valid' we mean:
+		 *   1) a valid memory space carved within system memory space
+		 *      assigned to IBASE register block.
+		 *   2) memory range decoding is enabled.
+		 * Hence we don't do any santify test here.
+		 */
+		irq_router.ibase = x86_pci_read_config32(irq_router.bdf,
+							 ibase_off);
+		irq_router.ibase &= ~0xf;
+	}
+
+	cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
+	if (!cell)
+		return -EINVAL;
+
+	if ((len % sizeof(struct pirq_routing)) == 0)
+		count = len / sizeof(struct pirq_routing);
+	else
+		return -EINVAL;
+
+	rt = malloc(sizeof(struct irq_routing_table));
+	if (!rt)
+		return -ENOMEM;
+	memset((char *)rt, 0, sizeof(struct irq_routing_table));
+
+	/* Populate the PIRQ table fields */
+	rt->signature = PIRQ_SIGNATURE;
+	rt->version = PIRQ_VERSION;
+	rt->rtr_bus = 0;
+	rt->rtr_devfn = (PCI_DEV(irq_router.bdf) << 3) |
+			PCI_FUNC(irq_router.bdf);
+	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
+	rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
+
+	slot = rt->slots;
+
+	/* Now fill in the irq_info entries in the PIRQ table */
+	for (i = 0; i < count; i++) {
+		struct pirq_routing pr;
+
+		pr.bdf = fdt_addr_to_cpu(cell[0]);
+		pr.pin = fdt_addr_to_cpu(cell[1]);
+		pr.pirq = fdt_addr_to_cpu(cell[2]);
+
+		debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n",
+		      i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
+		      PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
+		      'A' + pr.pirq);
+		fill_irq_info(&slot, &irq_entries, PCI_BUS(pr.bdf),
+			      PCI_DEV(pr.bdf), PCI_FUNC(pr.bdf),
+			      pr.pin, pr.pirq);
+		cell += sizeof(struct pirq_routing) / sizeof(u32);
+	}
+
+	/* Call board-specific routine to fill in add-in card's irq info */
+	irq_entries += board_fill_irq_info(slot);
+
+	rt->size = irq_entries * sizeof(struct irq_info) + 32;
+
+	pirq_routing_table = rt;
+
+	return 0;
+}
+
+void pirq_init(void)
+{
+	cpu_irq_init();
+
+	if (create_pirq_routing_table()) {
+		debug("Failed to create pirq routing table\n");
+	} else {
+		/* Route PIRQ */
+		pirq_route_irqs(pirq_routing_table->slots,
+				get_irq_slot_count(pirq_routing_table));
+	}
+}
+
+u32 write_pirq_routing_table(u32 addr)
+{
+	return copy_pirq_routing_table(addr, pirq_routing_table);
+}
diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile
index 4599a48..d8761fd 100644
--- a/arch/x86/cpu/queensbay/Makefile
+++ b/arch/x86/cpu/queensbay/Makefile
@@ -5,5 +5,5 @@ 
 #
 
 obj-y += fsp_configs.o
-obj-y += irq.o tnc.o topcliff.o
+obj-y += tnc.o topcliff.o
 obj-$(CONFIG_PCI) += tnc_pci.o
diff --git a/arch/x86/cpu/queensbay/irq.c b/arch/x86/cpu/queensbay/irq.c
deleted file mode 100644
index faf9515..0000000
--- a/arch/x86/cpu/queensbay/irq.c
+++ /dev/null
@@ -1,242 +0,0 @@ 
-/*
- * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
- *
- * SPDX-License-Identifier:	GPL-2.0+
- */
-
-#include <common.h>
-#include <errno.h>
-#include <malloc.h>
-#include <asm/io.h>
-#include <asm/pci.h>
-#include <asm/post.h>
-#include <asm/processor.h>
-#include <asm/pirq_routing.h>
-#include <asm/arch/device.h>
-#include <asm/arch/tnc.h>
-#include <asm/arch/irq.h>
-
-static struct irq_routing_table *pirq_routing_table;
-
-bool pirq_check_irq_routed(int link, u8 irq)
-{
-	u8 pirq;
-
-	pirq = x86_pci_read_config8(TNC_LPC, LINK_N2V(link));
-	pirq &= 0xf;
-
-	/* IRQ# 0/1/2/8/13 are reserved */
-	if (pirq < 3 || pirq == 8 || pirq == 13)
-		return false;
-
-	return pirq == irq ? true : false;
-}
-
-int pirq_translate_link(int link)
-{
-	return LINK_V2N(link);
-}
-
-void pirq_assign_irq(int link, u8 irq)
-{
-	/* IRQ# 0/1/2/8/13 are reserved */
-	if (irq < 3 || irq == 8 || irq == 13)
-		return;
-
-	x86_pci_write_config8(TNC_LPC, LINK_N2V(link), irq);
-}
-
-static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus,
-				 u8 device, u8 func, u8 pin, u8 pirq)
-{
-	struct irq_info *slot = *slotp;
-
-	slot->bus = bus;
-	slot->devfn = (device << 3) | func;
-	slot->irq[pin - 1].link = LINK_N2V(pirq);
-	slot->irq[pin - 1].bitmap = PIRQ_BITMAP;
-	(*entries)++;
-	(*slotp)++;
-}
-
-/* PCIe port downstream INTx swizzle */
-static inline u8 pin_swizzle(u8 pin, int port)
-{
-	return (pin + port) % 4;
-}
-
-__weak int board_fill_irq_info(struct irq_info *slot)
-{
-	return 0;
-}
-
-static int create_pirq_routing_table(void)
-{
-	struct irq_routing_table *rt;
-	struct irq_info *slot;
-	int irq_entries = 0;
-	pci_dev_t tcf_bdf;
-	u8 tcf_bus, bus;
-	int i;
-
-	rt = malloc(sizeof(struct irq_routing_table));
-	if (!rt)
-		return -ENOMEM;
-	memset((char *)rt, 0, sizeof(struct irq_routing_table));
-
-	/* Populate the PIRQ table fields */
-	rt->signature = PIRQ_SIGNATURE;
-	rt->version = PIRQ_VERSION;
-	rt->rtr_bus = 0;
-	rt->rtr_devfn = (TNC_LPC_DEV << 3) | TNC_LPC_FUNC;
-	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
-	rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
-
-	slot = rt->slots;
-
-	/*
-	 * Now fill in the irq_info entries in the PIRQ table
-	 *
-	 * We start from internal TunnelCreek PCI devices first, then
-	 * followed by all the 4 PCIe ports downstream devices, including
-	 * the Queensbay platform Topcliff chipset devices.
-	 */
-	fill_irq_info(&slot, &irq_entries, 0, TNC_IGD_DEV,
-		      TNC_IGD_FUNC, INTA, PIRQE);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_SDVO_DEV,
-		      TNC_SDVO_FUNC, INTA, PIRQF);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_HDA_DEV,
-		      TNC_HDA_FUNC, INTA, PIRQG);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE0_DEV,
-		      TNC_PCIE0_FUNC, INTA, PIRQE);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE1_DEV,
-		      TNC_PCIE1_FUNC, INTA, PIRQF);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE2_DEV,
-		      TNC_PCIE2_FUNC, INTA, PIRQG);
-	fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE3_DEV,
-		      TNC_PCIE3_FUNC, INTA, PIRQH);
-
-	/* Check which PCIe port the Topcliff chipset is connected to */
-	tcf_bdf = pci_find_device(PCI_VENDOR_ID_INTEL, 0x8800, 0);
-	tcf_bus = PCI_BUS(tcf_bdf);
-	for (i = 0; i < 4; i++) {
-		bus = x86_pci_read_config8(PCI_BDF(0, TNC_PCIE0_DEV + i, 0),
-					   PCI_SECONDARY_BUS);
-		if (bus == tcf_bus)
-			break;
-	}
-
-	/* Fill in the Topcliff chipset devices' irq info */
-	if (i < 4) {
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_PCIE_PORT_DEV,
-			      TCF_PCIE_PORT_FUNC, INTA, pin_swizzle(PIRQA, i));
-
-		tcf_bus++;
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0,
-			      TCF_GBE_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0,
-			      TCF_GPIO_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
-			      TCF_USB1_OHCI0_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
-			      TCF_USB1_OHCI1_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
-			      TCF_USB1_OHCI2_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
-			      TCF_USB1_EHCI_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
-			      TCF_USB_DEVICE_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4,
-			      TCF_SDIO0_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4,
-			      TCF_SDIO1_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_6,
-			      TCF_SATA_FUNC, INTD, pin_swizzle(PIRQD, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
-			      TCF_USB2_OHCI0_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
-			      TCF_USB2_OHCI1_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
-			      TCF_USB2_OHCI2_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
-			      TCF_USB2_EHCI_FUNC, INTA, pin_swizzle(PIRQA, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
-			      TCF_DMA1_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
-			      TCF_UART0_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
-			      TCF_UART1_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
-			      TCF_UART2_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
-			      TCF_UART3_FUNC, INTB, pin_swizzle(PIRQB, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
-			      TCF_DMA2_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
-			      TCF_SPI_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
-			      TCF_I2C_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
-			      TCF_CAN_FUNC, INTC, pin_swizzle(PIRQC, i));
-		fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
-			      TCF_1588_FUNC, INTC, pin_swizzle(PIRQC, i));
-	}
-
-	/* Call board-specific routine to fill in add-in card's irq info */
-	irq_entries += board_fill_irq_info(slot);
-
-	rt->size = irq_entries * sizeof(struct irq_info) + 32;
-
-	pirq_routing_table = rt;
-
-	return 0;
-}
-
-void pirq_init(void)
-{
-	struct tnc_rcba *rcba;
-	u32 base;
-
-	base = x86_pci_read_config32(TNC_LPC, LPC_RCBA);
-	base &= ~MEM_BAR_EN;
-	rcba = (struct tnc_rcba *)base;
-
-	/* Make sure all internal PCI devices are using INTA */
-	writel(INTA, &rcba->d02ip);
-	writel(INTA, &rcba->d03ip);
-	writel(INTA, &rcba->d27ip);
-	writel(INTA, &rcba->d31ip);
-	writel(INTA, &rcba->d23ip);
-	writel(INTA, &rcba->d24ip);
-	writel(INTA, &rcba->d25ip);
-	writel(INTA, &rcba->d26ip);
-
-	/*
-	 * Route TunnelCreek PCI device interrupt pin to PIRQ
-	 *
-	 * Since PCIe downstream ports received INTx are routed to PIRQ
-	 * A/B/C/D directly and not configurable, we route internal PCI
-	 * device's INTx to PIRQ E/F/G/H.
-	 */
-	writew(PIRQE, &rcba->d02ir);
-	writew(PIRQF, &rcba->d03ir);
-	writew(PIRQG, &rcba->d27ir);
-	writew(PIRQH, &rcba->d31ir);
-	writew(PIRQE, &rcba->d23ir);
-	writew(PIRQF, &rcba->d24ir);
-	writew(PIRQG, &rcba->d25ir);
-	writew(PIRQH, &rcba->d26ir);
-
-	if (create_pirq_routing_table()) {
-		debug("Failed to create pirq routing table\n");
-	} else {
-		/* Route PIRQ */
-		pirq_route_irqs(pirq_routing_table->slots,
-				get_irq_slot_count(pirq_routing_table));
-	}
-}
-
-u32 write_pirq_routing_table(u32 addr)
-{
-	return copy_pirq_routing_table(addr, pirq_routing_table);
-}
diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c
index b46a7e9..873de7b 100644
--- a/arch/x86/cpu/queensbay/tnc.c
+++ b/arch/x86/cpu/queensbay/tnc.c
@@ -6,10 +6,11 @@ 
 
 #include <common.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/pci.h>
 #include <asm/post.h>
 #include <asm/arch/device.h>
-#include <asm/arch/irq.h>
+#include <asm/arch/tnc.h>
 #include <asm/fsp/fsp_support.h>
 #include <asm/processor.h>
 
@@ -45,6 +46,42 @@  int arch_cpu_init(void)
 	return 0;
 }
 
+void cpu_irq_init(void)
+{
+	struct tnc_rcba *rcba;
+	u32 base;
+
+	base = x86_pci_read_config32(TNC_LPC, LPC_RCBA);
+	base &= ~MEM_BAR_EN;
+	rcba = (struct tnc_rcba *)base;
+
+	/* Make sure all internal PCI devices are using INTA */
+	writel(INTA, &rcba->d02ip);
+	writel(INTA, &rcba->d03ip);
+	writel(INTA, &rcba->d27ip);
+	writel(INTA, &rcba->d31ip);
+	writel(INTA, &rcba->d23ip);
+	writel(INTA, &rcba->d24ip);
+	writel(INTA, &rcba->d25ip);
+	writel(INTA, &rcba->d26ip);
+
+	/*
+	 * Route TunnelCreek PCI device interrupt pin to PIRQ
+	 *
+	 * Since PCIe downstream ports received INTx are routed to PIRQ
+	 * A/B/C/D directly and not configurable, we route internal PCI
+	 * device's INTx to PIRQ E/F/G/H.
+	 */
+	writew(PIRQE, &rcba->d02ir);
+	writew(PIRQF, &rcba->d03ir);
+	writew(PIRQG, &rcba->d27ir);
+	writew(PIRQH, &rcba->d31ir);
+	writew(PIRQE, &rcba->d23ir);
+	writew(PIRQF, &rcba->d24ir);
+	writew(PIRQG, &rcba->d25ir);
+	writew(PIRQH, &rcba->d26ir);
+}
+
 int arch_misc_init(void)
 {
 	pirq_init();
diff --git a/arch/x86/dts/crownbay.dts b/arch/x86/dts/crownbay.dts
index fbdeade..d68efda 100644
--- a/arch/x86/dts/crownbay.dts
+++ b/arch/x86/dts/crownbay.dts
@@ -6,6 +6,8 @@ 
 
 /dts-v1/;
 
+#include <dt-bindings/interrupt-router/intel-irq.h>
+
 /include/ "skeleton.dtsi"
 /include/ "serial.dtsi"
 
@@ -135,6 +137,58 @@ 
 				};
 			};
 		};
+
+		irq-router@1f,0 {
+			reg = <0x0000f800 0 0 0 0>;
+			compatible = "intel,irq-router";
+			intel,pirq-config = "pci";
+			intel,pirq-link = <0x60 8>;
+			intel,pirq-mask = <0xdee0>;
+			intel,pirq-routing = <
+				/* TunnelCreek PCI devices */
+				PCI_BDF(0, 2, 0) INTA PIRQE
+				PCI_BDF(0, 3, 0) INTA PIRQF
+				PCI_BDF(0, 23, 0) INTA PIRQE
+				PCI_BDF(0, 24, 0) INTA PIRQF
+				PCI_BDF(0, 25, 0) INTA PIRQG
+				PCI_BDF(0, 26, 0) INTA PIRQH
+				PCI_BDF(0, 27, 0) INTA PIRQG
+				/*
+				 * Topcliff PCI devices
+				 *
+				 * Note on the Crown Bay board, Topcliff chipset
+				 * is connected to TunnelCreek PCIe port 0, so
+				 * its bus number is 1 for its PCIe port and 2
+				 * for its PCI devices per U-Boot currnet PCI
+				 * bus enumeration algorithm.
+				 */
+				PCI_BDF(1, 0, 0) INTA PIRQA
+				PCI_BDF(2, 0, 1) INTA PIRQA
+				PCI_BDF(2, 0, 2) INTA PIRQA
+				PCI_BDF(2, 2, 0) INTB PIRQB
+				PCI_BDF(2, 2, 1) INTB PIRQB
+				PCI_BDF(2, 2, 2) INTB PIRQB
+				PCI_BDF(2, 2, 3) INTB PIRQB
+				PCI_BDF(2, 2, 4) INTB PIRQB
+				PCI_BDF(2, 4, 0) INTC PIRQC
+				PCI_BDF(2, 4, 1) INTC PIRQC
+				PCI_BDF(2, 6, 0) INTD PIRQD
+				PCI_BDF(2, 8, 0) INTA PIRQA
+				PCI_BDF(2, 8, 1) INTA PIRQA
+				PCI_BDF(2, 8, 2) INTA PIRQA
+				PCI_BDF(2, 8, 3) INTA PIRQA
+				PCI_BDF(2, 10, 0) INTB PIRQB
+				PCI_BDF(2, 10, 1) INTB PIRQB
+				PCI_BDF(2, 10, 2) INTB PIRQB
+				PCI_BDF(2, 10, 3) INTB PIRQB
+				PCI_BDF(2, 10, 4) INTB PIRQB
+				PCI_BDF(2, 12, 0) INTC PIRQC
+				PCI_BDF(2, 12, 1) INTC PIRQC
+				PCI_BDF(2, 12, 2) INTC PIRQC
+				PCI_BDF(2, 12, 3) INTC PIRQC
+				PCI_BDF(2, 12, 4) INTC PIRQC
+			>;
+		};
 	};
 
 };
diff --git a/arch/x86/include/asm/arch-queensbay/irq.h b/arch/x86/include/asm/arch-queensbay/irq.h
deleted file mode 100644
index e7f8616..0000000
--- a/arch/x86/include/asm/arch-queensbay/irq.h
+++ /dev/null
@@ -1,55 +0,0 @@ 
-/*
- * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
- *
- * SPDX-License-Identifier:	GPL-2.0+
- */
-
-#ifndef _ARCH_IRQ_H_
-#define _ARCH_IRQ_H_
-
-enum pci_int_pin {
-	INTX,
-	INTA,
-	INTB,
-	INTC,
-	INTD
-};
-
-enum pirq_pin {
-	PIRQA,
-	PIRQB,
-	PIRQC,
-	PIRQD,
-	PIRQE,
-	PIRQF,
-	PIRQG,
-	PIRQH
-};
-
-/* PIRQ link number and value conversion */
-#define LINK_V2N(link)	(link - 0x60)
-#define LINK_N2V(link)	(link + 0x60)
-
-#define PIRQ_BITMAP	0xdee0
-
-struct irq_info;
-
-/**
- * board_fill_irq_info() - Board-specific irq_info fill routine
- *
- * This fills the irq_info table for any board-specific add-in cards.
- *
- * @slot:	pointer to the struct irq_info that is to be filled in
- * @return:	number of entries were written to the struct irq_info
- */
-int board_fill_irq_info(struct irq_info *slot);
-
-/**
- * pirq_init() - Initialize platform PIRQ routing
- *
- * This initializes the PIRQ routing on the platform and configures all PCI
- * devices' interrupt line register to a working IRQ number on the 8259 PIC.
- */
-void pirq_init(void);
-
-#endif /* _ARCH_IRQ_H_ */
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
new file mode 100644
index 0000000..a7ad462
--- /dev/null
+++ b/arch/x86/include/asm/irq.h
@@ -0,0 +1,105 @@ 
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _ARCH_IRQ_H_
+#define _ARCH_IRQ_H_
+
+enum pci_int_pin {
+	INTX,
+	INTA,
+	INTB,
+	INTC,
+	INTD
+};
+
+enum pirq_pin {
+	PIRQA,
+	PIRQB,
+	PIRQC,
+	PIRQD,
+	PIRQE,
+	PIRQF,
+	PIRQG,
+	PIRQH
+};
+
+/**
+ * Intel interrupt router configuration mechanism
+ *
+ * There are two known ways of Intel interrupt router configuration mechanism
+ * so far. On most cases, the IRQ routing configuraiton is controlled by PCI
+ * configuraiton registers on the legacy bridge, normally PCI BDF(0, 31, 0).
+ * On some newer platforms like BayTrail and Braswell, the IRQ routing is now
+ * in the IBASE register block where IBASE is memory-mapped.
+ */
+enum pirq_config {
+	PIRQ_VIA_PCI,
+	PIRQ_VIA_IBASE
+};
+
+/**
+ * Intel interrupt router control block
+ *
+ * Its members' value will be filled in based on device tree's input.
+ *
+ * @config:	PIRQ_VIA_PCI or PIRQ_VIA_IBASE
+ * @link_base:	link value base number
+ * @irq_mask:	IRQ mask reprenting the 16 IRQs in 8259, bit N is 1 means
+ *		IRQ N is available to be routed
+ * @lb_bdf:	irq router's PCI bus/device/function number encoding
+ * @ibase:	IBASE register block base address
+ */
+struct irq_router {
+	int config;
+	u32 link_base;
+	u16 irq_mask;
+	u32 bdf;
+	u32 ibase;
+};
+
+struct pirq_routing {
+	int bdf;
+	int pin;
+	int pirq;
+};
+
+/* PIRQ link number and value conversion */
+#define LINK_V2N(link, base)	(link - base)
+#define LINK_N2V(link, base)	(link + base)
+
+#define PIRQ_BITMAP		0xdef8
+
+struct irq_info;
+
+/**
+ * board_fill_irq_info() - Board-specific irq_info fill routine
+ *
+ * This fills the irq_info table for any board-specific add-in cards.
+ *
+ * @slot:	pointer to the struct irq_info that is to be filled in
+ * @return:	number of entries were written to the struct irq_info
+ */
+int board_fill_irq_info(struct irq_info *slot);
+
+/**
+ * cpu_irq_init() - Initialize CPU IRQ routing
+ *
+ * This initializes some platform-specific registers related to IRQ routing,
+ * like configuring internal PCI devices to use which PCI interrupt pin,
+ * and which PCI interrupt pin is mapped to which PIRQ line. Note on some
+ * platforms, such IRQ routing might be hard-coded thus cannot configure.
+ */
+void cpu_irq_init(void);
+
+/**
+ * pirq_init() - Initialize platform PIRQ routing
+ *
+ * This initializes the PIRQ routing on the platform and configures all PCI
+ * devices' interrupt line register to a working IRQ number on the 8259 PIC.
+ */
+void pirq_init(void);
+
+#endif /* _ARCH_IRQ_H_ */
diff --git a/include/dt-bindings/interrupt-router/intel-irq.h b/include/dt-bindings/interrupt-router/intel-irq.h
new file mode 100644
index 0000000..41077ef
--- /dev/null
+++ b/include/dt-bindings/interrupt-router/intel-irq.h
@@ -0,0 +1,29 @@ 
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DT_BINDINGS_INTEL_IRQ_H_
+#define _DT_BINDINGS_INTEL_IRQ_H_
+
+/* PCI interrupt pin */
+#define INTA			1
+#define INTB			2
+#define INTC			3
+#define INTD			4
+
+/* PIRQs */
+#define PIRQA			0
+#define PIRQB			1
+#define PIRQC			2
+#define PIRQD			3
+#define PIRQE			4
+#define PIRQF			5
+#define PIRQG			6
+#define PIRQH			7
+
+/* PCI bdf encoding */
+#define PCI_BDF(b, d, f)	((b) << 16 | (d) << 11 | (f) << 8)
+
+#endif /* _DT_BINDINGS_INTEL_IRQ_H_ */
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 6590470..dc1c85b 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -177,6 +177,7 @@  enum fdt_compat_id {
 	COMPAT_INTEL_QRK_MRC,		/* Intel Quark MRC */
 	COMPAT_SOCIONEXT_XHCI,		/* Socionext UniPhier xHCI */
 	COMPAT_INTEL_PCH,		/* Intel PCH */
+	COMPAT_INTEL_IRQ_ROUTER,	/* Intel Interrupt Router */
 
 	COMPAT_COUNT,
 };
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 80b897a..22d1e39 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -76,6 +76,7 @@  static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
 	COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"),
 	COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"),
+	COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"),
 };
 
 const char *fdtdec_get_compatible(enum fdt_compat_id id)