diff mbox

[U-Boot,09/10] x86: Generate a valid MultiProcessor (MP) table

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

Commit Message

Bin Meng June 15, 2015, 8 a.m. UTC
Implement a weak write_mp_table() to create a minimal working MP
table. This includes an MP floating table, a configuration table
header and all of the 5 base configuration table entries. The I/O
interrupt assignment table entry is created based on the same
information used in the creation of PIRQ routing table from device
tree. A check duplicated entry logic is applied to prevent writing
multiple I/O interrupt entries with the same information.

Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
need actually write the MP table at the F seg, just like we did for
PIRQ routing and SFI tables. With MP table existence, linux kernel
will switch to I/O APIC and local APIC to process all the peripheral
interrupts instead of 8259 PICs. This takes full advantage of the
multicore hardware and the SMP kernel.

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

 arch/x86/Kconfig              |   8 +++
 arch/x86/include/asm/mpspec.h |  10 +++
 arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
 arch/x86/lib/tables.c         |   5 ++
 4 files changed, 170 insertions(+)

Comments

Simon Glass June 16, 2015, 2:46 a.m. UTC | #1
Hi Bin,

On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
> Implement a weak write_mp_table() to create a minimal working MP
> table. This includes an MP floating table, a configuration table
> header and all of the 5 base configuration table entries. The I/O
> interrupt assignment table entry is created based on the same
> information used in the creation of PIRQ routing table from device
> tree. A check duplicated entry logic is applied to prevent writing
> multiple I/O interrupt entries with the same information.
>
> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
> need actually write the MP table at the F seg, just like we did for
> PIRQ routing and SFI tables. With MP table existence, linux kernel
> will switch to I/O APIC and local APIC to process all the peripheral
> interrupts instead of 8259 PICs. This takes full advantage of the
> multicore hardware and the SMP kernel.
>
> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
> ---
>
>  arch/x86/Kconfig              |   8 +++
>  arch/x86/include/asm/mpspec.h |  10 +++
>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>  arch/x86/lib/tables.c         |   5 ++
>  4 files changed, 170 insertions(+)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 1aeae9d..d4b2772 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>
>           For more information, see http://simplefirmware.org
>
> +config GENERATE_MP_TABLE
> +       bool "Generate an MP (Multi-Processor) table"
> +       help
> +         Generate an MP (Multi-Processor) table for this board. The MP table
> +         provides a way for the operating system to support for symmetric
> +         multiprocessing as well as symmetric I/O interrupt handling with
> +         the local APIC and I/O APIC.
> +
>  endmenu
>
>  config MAX_PIRQ_LINKS
> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
> index c489a58..e9e1a2a 100644
> --- a/arch/x86/include/asm/mpspec.h
> +++ b/arch/x86/include/asm/mpspec.h
> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>   */
>  u32 mptable_finalize(struct mp_config_table *mc);
>
> +/**
> + * write_mp_table() - Write MP table
> + *
> + * This writes MP table at a given address.
> + *
> + * @addr:      start address to write MP table
> + * @return:    end address of MP table
> + */
> +u32 write_mp_table(u32 addr);
> +
>  #endif /* __ASM_MPSPEC_H */
> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
> index 657df22..1e3523f 100644
> --- a/arch/x86/lib/mpspec.c
> +++ b/arch/x86/lib/mpspec.c
> @@ -9,13 +9,17 @@
>  #include <common.h>
>  #include <cpu.h>
>  #include <dm.h>
> +#include <fdtdec.h>
>  #include <asm/cpu.h>
> +#include <asm/irq.h>
>  #include <asm/ioapic.h>
>  #include <asm/lapic.h>
>  #include <asm/mpspec.h>
>  #include <asm/tables.h>
>  #include <dm/uclass-internal.h>
>
> +DECLARE_GLOBAL_DATA_PTR;
> +
>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>  {
>         u32 mc;
> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>
>         return end;
>  }
> +
> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
> +                                      u32 apicid, int external_int2)
> +{
> +       int i;
> +
> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 0, apicid, 0);
> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 1, apicid, 1);
> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                       bus_isa, 0, apicid, 2);
> +
> +       for (i = 3; i < 16; i++)
> +               mp_write_intsrc(mc, MP_INT,
> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                               bus_isa, i, apicid, i);
> +}
> +
> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
> +                           int entry_num, u8 bus, u8 device, u8 pin)

Again can we avoid u8 on function parameters?

Also this one coudl use a comment.

> +{
> +       struct mpc_config_intsrc *intsrc = intsrc_base;
> +       int i;
> +
> +       for (i = 0; i < entry_num; i++) {
> +               if (intsrc->mpc_srcbus == bus &&
> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
> +                       break;
> +               intsrc++;
> +       }
> +
> +       return (i == entry_num) ? false : true;
> +}
> +
> +static void mptable_add_intsrc(struct mp_config_table *mc,
> +                              int bus_isa, u32 apicid)
> +{
> +       struct mpc_config_intsrc *intsrc_base;
> +       int intsrc_entries = 0;
> +       const void *blob = gd->fdt_blob;
> +       int node;
> +       int len, count;
> +       const u32 *cell;
> +       int i;
> +
> +       /* Legacy Interrupts */
> +       debug("Writing ISA IRQs\n");
> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
> +
> +       /* Get I/O interrupt information from device tree */
> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
> +       if (node < 0) {
> +               debug("%s: Cannot find irq router node\n", __func__);
> +               return;

return -ENOENT

(and it's caller should check for error)

> +       }
> +
> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
> +       if (!cell)
> +               return;
> +
> +       if ((len % sizeof(struct pirq_routing)) == 0)
> +               count = len / sizeof(struct pirq_routing);
> +       else
> +               return;
> +
> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
> +
> +       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]);
> +
> +               if (check_dup_entry(intsrc_base, intsrc_entries,
> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
> +                             'A' + pr.pin - 1);
> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
> +                       continue;
> +               }
> +
> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
> +                                   pr.pirq + 16);
> +               intsrc_entries++;
> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
> +       }
> +}
> +
> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
> +{
> +       mp_write_lintsrc(mc, MP_EXTINT,
> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                        bus_isa, 0, MP_APIC_ALL, 0);
> +       mp_write_lintsrc(mc, MP_NMI,
> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
> +                        bus_isa, 0, MP_APIC_ALL, 1);
> +}
> +
> +__weak u32 write_mp_table(u32 addr)

Why does this need to be weak?

> +{
> +       struct mp_config_table *mc;
> +       int ioapic_id, ioapic_ver;
> +       int bus_isa = 0xff;
> +       u32 end;
> +
> +       /* 16 byte align the table address */
> +       addr = ALIGN(addr, 16);
> +
> +       /* Write floating table */
> +       mc = mp_write_floating_table((struct mp_floating_table *)addr);
> +
> +       /* Write configuration table header */
> +       mp_config_table_init(mc);
> +
> +       /* Write processor entry */
> +       mp_write_processor(mc);
> +
> +       /* Write bus entry */
> +       mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
> +
> +       /* Write I/O APIC entry */
> +       ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
> +       ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
> +       mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
> +
> +       /* Write I/O interrupt assignment entry */
> +       mptable_add_intsrc(mc, bus_isa, ioapic_id);
> +
> +       /* Write local interrupt assignment entry */
> +       mptable_add_lintsrc(mc, bus_isa);
> +
> +       /* Finalize the MP table */
> +       end = mptable_finalize(mc);
> +
> +       return end;
> +}
> diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
> index 8031201..d3bef6f 100644
> --- a/arch/x86/lib/tables.c
> +++ b/arch/x86/lib/tables.c
> @@ -6,6 +6,7 @@
>
>  #include <common.h>
>  #include <asm/sfi.h>
> +#include <asm/mpspec.h>
>  #include <asm/tables.h>
>
>  u8 table_compute_checksum(void *v, int len)
> @@ -32,4 +33,8 @@ void write_tables(void)
>         rom_table_end = write_sfi_table(rom_table_end);
>         rom_table_end = ALIGN(rom_table_end, 1024);
>  #endif
> +#ifdef CONFIG_GENERATE_MP_TABLE
> +       rom_table_end = write_mp_table(rom_table_end);
> +       rom_table_end = ALIGN(rom_table_end, 1024);
> +#endif
>  }
> --
> 1.8.2.1
>

Regards,
Simon
Bin Meng June 17, 2015, 7:49 a.m. UTC | #2
Hi Simon,

On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Bin,
>
> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>> Implement a weak write_mp_table() to create a minimal working MP
>> table. This includes an MP floating table, a configuration table
>> header and all of the 5 base configuration table entries. The I/O
>> interrupt assignment table entry is created based on the same
>> information used in the creation of PIRQ routing table from device
>> tree. A check duplicated entry logic is applied to prevent writing
>> multiple I/O interrupt entries with the same information.
>>
>> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
>> need actually write the MP table at the F seg, just like we did for
>> PIRQ routing and SFI tables. With MP table existence, linux kernel
>> will switch to I/O APIC and local APIC to process all the peripheral
>> interrupts instead of 8259 PICs. This takes full advantage of the
>> multicore hardware and the SMP kernel.
>>
>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>> ---
>>
>>  arch/x86/Kconfig              |   8 +++
>>  arch/x86/include/asm/mpspec.h |  10 +++
>>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>>  arch/x86/lib/tables.c         |   5 ++
>>  4 files changed, 170 insertions(+)
>>
>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>> index 1aeae9d..d4b2772 100644
>> --- a/arch/x86/Kconfig
>> +++ b/arch/x86/Kconfig
>> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>>
>>           For more information, see http://simplefirmware.org
>>
>> +config GENERATE_MP_TABLE
>> +       bool "Generate an MP (Multi-Processor) table"
>> +       help
>> +         Generate an MP (Multi-Processor) table for this board. The MP table
>> +         provides a way for the operating system to support for symmetric
>> +         multiprocessing as well as symmetric I/O interrupt handling with
>> +         the local APIC and I/O APIC.
>> +
>>  endmenu
>>
>>  config MAX_PIRQ_LINKS
>> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
>> index c489a58..e9e1a2a 100644
>> --- a/arch/x86/include/asm/mpspec.h
>> +++ b/arch/x86/include/asm/mpspec.h
>> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>>   */
>>  u32 mptable_finalize(struct mp_config_table *mc);
>>
>> +/**
>> + * write_mp_table() - Write MP table
>> + *
>> + * This writes MP table at a given address.
>> + *
>> + * @addr:      start address to write MP table
>> + * @return:    end address of MP table
>> + */
>> +u32 write_mp_table(u32 addr);
>> +
>>  #endif /* __ASM_MPSPEC_H */
>> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
>> index 657df22..1e3523f 100644
>> --- a/arch/x86/lib/mpspec.c
>> +++ b/arch/x86/lib/mpspec.c
>> @@ -9,13 +9,17 @@
>>  #include <common.h>
>>  #include <cpu.h>
>>  #include <dm.h>
>> +#include <fdtdec.h>
>>  #include <asm/cpu.h>
>> +#include <asm/irq.h>
>>  #include <asm/ioapic.h>
>>  #include <asm/lapic.h>
>>  #include <asm/mpspec.h>
>>  #include <asm/tables.h>
>>  #include <dm/uclass-internal.h>
>>
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>>  {
>>         u32 mc;
>> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>>
>>         return end;
>>  }
>> +
>> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
>> +                                      u32 apicid, int external_int2)
>> +{
>> +       int i;
>> +
>> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 0, apicid, 0);
>> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 1, apicid, 1);
>> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                       bus_isa, 0, apicid, 2);
>> +
>> +       for (i = 3; i < 16; i++)
>> +               mp_write_intsrc(mc, MP_INT,
>> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                               bus_isa, i, apicid, i);
>> +}
>> +
>> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
>> +                           int entry_num, u8 bus, u8 device, u8 pin)
>
> Again can we avoid u8 on function parameters?

Yes, in v2.

> Also this one coudl use a comment.
>

OK.

>> +{
>> +       struct mpc_config_intsrc *intsrc = intsrc_base;
>> +       int i;
>> +
>> +       for (i = 0; i < entry_num; i++) {
>> +               if (intsrc->mpc_srcbus == bus &&
>> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
>> +                       break;
>> +               intsrc++;
>> +       }
>> +
>> +       return (i == entry_num) ? false : true;
>> +}
>> +
>> +static void mptable_add_intsrc(struct mp_config_table *mc,
>> +                              int bus_isa, u32 apicid)
>> +{
>> +       struct mpc_config_intsrc *intsrc_base;
>> +       int intsrc_entries = 0;
>> +       const void *blob = gd->fdt_blob;
>> +       int node;
>> +       int len, count;
>> +       const u32 *cell;
>> +       int i;
>> +
>> +       /* Legacy Interrupts */
>> +       debug("Writing ISA IRQs\n");
>> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
>> +
>> +       /* Get I/O interrupt information from device tree */
>> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
>> +       if (node < 0) {
>> +               debug("%s: Cannot find irq router node\n", __func__);
>> +               return;
>
> return -ENOENT
>
> (and it's caller should check for error)
>

OK

>> +       }
>> +
>> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
>> +       if (!cell)
>> +               return;
>> +
>> +       if ((len % sizeof(struct pirq_routing)) == 0)
>> +               count = len / sizeof(struct pirq_routing);
>> +       else
>> +               return;
>> +
>> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
>> +
>> +       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]);
>> +
>> +               if (check_dup_entry(intsrc_base, intsrc_entries,
>> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
>> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
>> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
>> +                             'A' + pr.pin - 1);
>> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
>> +                       continue;
>> +               }
>> +
>> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
>> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
>> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
>> +                                   pr.pirq + 16);
>> +               intsrc_entries++;
>> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
>> +       }
>> +}
>> +
>> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
>> +{
>> +       mp_write_lintsrc(mc, MP_EXTINT,
>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                        bus_isa, 0, MP_APIC_ALL, 0);
>> +       mp_write_lintsrc(mc, MP_NMI,
>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>> +                        bus_isa, 0, MP_APIC_ALL, 1);
>> +}
>> +
>> +__weak u32 write_mp_table(u32 addr)
>
> Why does this need to be weak?
>

I wanted to leave an option to have board-specific codes to implement
the writing of mp table by using the APIs provided in this file,
although I think this weak version could probably fit for 90% boards.
We can remove the weak for now, and see how things go?

[snip]

Regards,
Bin
Simon Glass June 17, 2015, 1:15 p.m. UTC | #3
Hi Bin,

On 17 June 2015 at 01:49, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Tue, Jun 16, 2015 at 10:46 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Bin,
>>
>> On 15 June 2015 at 02:00, Bin Meng <bmeng.cn@gmail.com> wrote:
>>> Implement a weak write_mp_table() to create a minimal working MP
>>> table. This includes an MP floating table, a configuration table
>>> header and all of the 5 base configuration table entries. The I/O
>>> interrupt assignment table entry is created based on the same
>>> information used in the creation of PIRQ routing table from device
>>> tree. A check duplicated entry logic is applied to prevent writing
>>> multiple I/O interrupt entries with the same information.
>>>
>>> Use a Kconfig option GENERATE_MP_TABLE to tell U-Boot whether we
>>> need actually write the MP table at the F seg, just like we did for
>>> PIRQ routing and SFI tables. With MP table existence, linux kernel
>>> will switch to I/O APIC and local APIC to process all the peripheral
>>> interrupts instead of 8259 PICs. This takes full advantage of the
>>> multicore hardware and the SMP kernel.
>>>
>>> Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
>>> ---
>>>
>>>  arch/x86/Kconfig              |   8 +++
>>>  arch/x86/include/asm/mpspec.h |  10 +++
>>>  arch/x86/lib/mpspec.c         | 147 ++++++++++++++++++++++++++++++++++++++++++
>>>  arch/x86/lib/tables.c         |   5 ++
>>>  4 files changed, 170 insertions(+)
>>>
>>> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>>> index 1aeae9d..d4b2772 100644
>>> --- a/arch/x86/Kconfig
>>> +++ b/arch/x86/Kconfig
>>> @@ -319,6 +319,14 @@ config GENERATE_SFI_TABLE
>>>
>>>           For more information, see http://simplefirmware.org
>>>
>>> +config GENERATE_MP_TABLE
>>> +       bool "Generate an MP (Multi-Processor) table"
>>> +       help
>>> +         Generate an MP (Multi-Processor) table for this board. The MP table
>>> +         provides a way for the operating system to support for symmetric
>>> +         multiprocessing as well as symmetric I/O interrupt handling with
>>> +         the local APIC and I/O APIC.
>>> +
>>>  endmenu
>>>
>>>  config MAX_PIRQ_LINKS
>>> diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
>>> index c489a58..e9e1a2a 100644
>>> --- a/arch/x86/include/asm/mpspec.h
>>> +++ b/arch/x86/include/asm/mpspec.h
>>> @@ -431,4 +431,14 @@ void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
>>>   */
>>>  u32 mptable_finalize(struct mp_config_table *mc);
>>>
>>> +/**
>>> + * write_mp_table() - Write MP table
>>> + *
>>> + * This writes MP table at a given address.
>>> + *
>>> + * @addr:      start address to write MP table
>>> + * @return:    end address of MP table
>>> + */
>>> +u32 write_mp_table(u32 addr);
>>> +
>>>  #endif /* __ASM_MPSPEC_H */
>>> diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
>>> index 657df22..1e3523f 100644
>>> --- a/arch/x86/lib/mpspec.c
>>> +++ b/arch/x86/lib/mpspec.c
>>> @@ -9,13 +9,17 @@
>>>  #include <common.h>
>>>  #include <cpu.h>
>>>  #include <dm.h>
>>> +#include <fdtdec.h>
>>>  #include <asm/cpu.h>
>>> +#include <asm/irq.h>
>>>  #include <asm/ioapic.h>
>>>  #include <asm/lapic.h>
>>>  #include <asm/mpspec.h>
>>>  #include <asm/tables.h>
>>>  #include <dm/uclass-internal.h>
>>>
>>> +DECLARE_GLOBAL_DATA_PTR;
>>> +
>>>  struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
>>>  {
>>>         u32 mc;
>>> @@ -236,3 +240,146 @@ u32 mptable_finalize(struct mp_config_table *mc)
>>>
>>>         return end;
>>>  }
>>> +
>>> +static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
>>> +                                      u32 apicid, int external_int2)
>>> +{
>>> +       int i;
>>> +
>>> +       mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
>>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 0, apicid, 0);
>>> +       mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 1, apicid, 1);
>>> +       mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
>>> +                       MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                       bus_isa, 0, apicid, 2);
>>> +
>>> +       for (i = 3; i < 16; i++)
>>> +               mp_write_intsrc(mc, MP_INT,
>>> +                               MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                               bus_isa, i, apicid, i);
>>> +}
>>> +
>>> +static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
>>> +                           int entry_num, u8 bus, u8 device, u8 pin)
>>
>> Again can we avoid u8 on function parameters?
>
> Yes, in v2.
>
>> Also this one coudl use a comment.
>>
>
> OK.
>
>>> +{
>>> +       struct mpc_config_intsrc *intsrc = intsrc_base;
>>> +       int i;
>>> +
>>> +       for (i = 0; i < entry_num; i++) {
>>> +               if (intsrc->mpc_srcbus == bus &&
>>> +                   intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
>>> +                       break;
>>> +               intsrc++;
>>> +       }
>>> +
>>> +       return (i == entry_num) ? false : true;
>>> +}
>>> +
>>> +static void mptable_add_intsrc(struct mp_config_table *mc,
>>> +                              int bus_isa, u32 apicid)
>>> +{
>>> +       struct mpc_config_intsrc *intsrc_base;
>>> +       int intsrc_entries = 0;
>>> +       const void *blob = gd->fdt_blob;
>>> +       int node;
>>> +       int len, count;
>>> +       const u32 *cell;
>>> +       int i;
>>> +
>>> +       /* Legacy Interrupts */
>>> +       debug("Writing ISA IRQs\n");
>>> +       mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
>>> +
>>> +       /* Get I/O interrupt information from device tree */
>>> +       node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
>>> +       if (node < 0) {
>>> +               debug("%s: Cannot find irq router node\n", __func__);
>>> +               return;
>>
>> return -ENOENT
>>
>> (and it's caller should check for error)
>>
>
> OK
>
>>> +       }
>>> +
>>> +       cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
>>> +       if (!cell)
>>> +               return;
>>> +
>>> +       if ((len % sizeof(struct pirq_routing)) == 0)
>>> +               count = len / sizeof(struct pirq_routing);
>>> +       else
>>> +               return;
>>> +
>>> +       intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
>>> +
>>> +       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]);
>>> +
>>> +               if (check_dup_entry(intsrc_base, intsrc_entries,
>>> +                                   PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
>>> +                       debug("found entry for bus %d device %d INT%c, skipping\n",
>>> +                             PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
>>> +                             'A' + pr.pin - 1);
>>> +                       cell += sizeof(struct pirq_routing) / sizeof(u32);
>>> +                       continue;
>>> +               }
>>> +
>>> +               /* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
>>> +               mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
>>> +                                   PCI_DEV(pr.bdf), pr.pin, apicid,
>>> +                                   pr.pirq + 16);
>>> +               intsrc_entries++;
>>> +               cell += sizeof(struct pirq_routing) / sizeof(u32);
>>> +       }
>>> +}
>>> +
>>> +static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
>>> +{
>>> +       mp_write_lintsrc(mc, MP_EXTINT,
>>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                        bus_isa, 0, MP_APIC_ALL, 0);
>>> +       mp_write_lintsrc(mc, MP_NMI,
>>> +                        MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
>>> +                        bus_isa, 0, MP_APIC_ALL, 1);
>>> +}
>>> +
>>> +__weak u32 write_mp_table(u32 addr)
>>
>> Why does this need to be weak?
>>
>
> I wanted to leave an option to have board-specific codes to implement
> the writing of mp table by using the APIs provided in this file,
> although I think this weak version could probably fit for 90% boards.
> We can remove the weak for now, and see how things go?

Sounds good - 90% is a pretty good hit rate. We can always expand this
later when needed.

Regards,
Simon
diff mbox

Patch

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 1aeae9d..d4b2772 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -319,6 +319,14 @@  config GENERATE_SFI_TABLE
 
 	  For more information, see http://simplefirmware.org
 
+config GENERATE_MP_TABLE
+	bool "Generate an MP (Multi-Processor) table"
+	help
+	  Generate an MP (Multi-Processor) table for this board. The MP table
+	  provides a way for the operating system to support for symmetric
+	  multiprocessing as well as symmetric I/O interrupt handling with
+	  the local APIC and I/O APIC.
+
 endmenu
 
 config MAX_PIRQ_LINKS
diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
index c489a58..e9e1a2a 100644
--- a/arch/x86/include/asm/mpspec.h
+++ b/arch/x86/include/asm/mpspec.h
@@ -431,4 +431,14 @@  void mp_write_compat_address_space(struct mp_config_table *mc, u8 busid,
  */
 u32 mptable_finalize(struct mp_config_table *mc);
 
+/**
+ * write_mp_table() - Write MP table
+ *
+ * This writes MP table at a given address.
+ *
+ * @addr:	start address to write MP table
+ * @return:	end address of MP table
+ */
+u32 write_mp_table(u32 addr);
+
 #endif /* __ASM_MPSPEC_H */
diff --git a/arch/x86/lib/mpspec.c b/arch/x86/lib/mpspec.c
index 657df22..1e3523f 100644
--- a/arch/x86/lib/mpspec.c
+++ b/arch/x86/lib/mpspec.c
@@ -9,13 +9,17 @@ 
 #include <common.h>
 #include <cpu.h>
 #include <dm.h>
+#include <fdtdec.h>
 #include <asm/cpu.h>
+#include <asm/irq.h>
 #include <asm/ioapic.h>
 #include <asm/lapic.h>
 #include <asm/mpspec.h>
 #include <asm/tables.h>
 #include <dm/uclass-internal.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
 {
 	u32 mc;
@@ -236,3 +240,146 @@  u32 mptable_finalize(struct mp_config_table *mc)
 
 	return end;
 }
+
+static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
+				       u32 apicid, int external_int2)
+{
+	int i;
+
+	mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
+			MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 0, apicid, 0);
+	mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 1, apicid, 1);
+	mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
+			MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			bus_isa, 0, apicid, 2);
+
+	for (i = 3; i < 16; i++)
+		mp_write_intsrc(mc, MP_INT,
+				MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+				bus_isa, i, apicid, i);
+}
+
+static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
+			    int entry_num, u8 bus, u8 device, u8 pin)
+{
+	struct mpc_config_intsrc *intsrc = intsrc_base;
+	int i;
+
+	for (i = 0; i < entry_num; i++) {
+		if (intsrc->mpc_srcbus == bus &&
+		    intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
+			break;
+		intsrc++;
+	}
+
+	return (i == entry_num) ? false : true;
+}
+
+static void mptable_add_intsrc(struct mp_config_table *mc,
+			       int bus_isa, u32 apicid)
+{
+	struct mpc_config_intsrc *intsrc_base;
+	int intsrc_entries = 0;
+	const void *blob = gd->fdt_blob;
+	int node;
+	int len, count;
+	const u32 *cell;
+	int i;
+
+	/* Legacy Interrupts */
+	debug("Writing ISA IRQs\n");
+	mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
+
+	/* Get I/O interrupt information from device tree */
+	node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IRQ_ROUTER);
+	if (node < 0) {
+		debug("%s: Cannot find irq router node\n", __func__);
+		return;
+	}
+
+	cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
+	if (!cell)
+		return;
+
+	if ((len % sizeof(struct pirq_routing)) == 0)
+		count = len / sizeof(struct pirq_routing);
+	else
+		return;
+
+	intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
+
+	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]);
+
+		if (check_dup_entry(intsrc_base, intsrc_entries,
+				    PCI_BUS(pr.bdf), PCI_DEV(pr.bdf), pr.pin)) {
+			debug("found entry for bus %d device %d INT%c, skipping\n",
+			      PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
+			      'A' + pr.pin - 1);
+			cell += sizeof(struct pirq_routing) / sizeof(u32);
+			continue;
+		}
+
+		/* PIRQ[A-H] are always connected to I/O APIC INTPIN#16-23 */
+		mp_write_pci_intsrc(mc, MP_INT, PCI_BUS(pr.bdf),
+				    PCI_DEV(pr.bdf), pr.pin, apicid,
+				    pr.pirq + 16);
+		intsrc_entries++;
+		cell += sizeof(struct pirq_routing) / sizeof(u32);
+	}
+}
+
+static void mptable_add_lintsrc(struct mp_config_table *mc, u32 bus_isa)
+{
+	mp_write_lintsrc(mc, MP_EXTINT,
+			 MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			 bus_isa, 0, MP_APIC_ALL, 0);
+	mp_write_lintsrc(mc, MP_NMI,
+			 MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
+			 bus_isa, 0, MP_APIC_ALL, 1);
+}
+
+__weak u32 write_mp_table(u32 addr)
+{
+	struct mp_config_table *mc;
+	int ioapic_id, ioapic_ver;
+	int bus_isa = 0xff;
+	u32 end;
+
+	/* 16 byte align the table address */
+	addr = ALIGN(addr, 16);
+
+	/* Write floating table */
+	mc = mp_write_floating_table((struct mp_floating_table *)addr);
+
+	/* Write configuration table header */
+	mp_config_table_init(mc);
+
+	/* Write processor entry */
+	mp_write_processor(mc);
+
+	/* Write bus entry */
+	mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
+
+	/* Write I/O APIC entry */
+	ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
+	ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
+	mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
+
+	/* Write I/O interrupt assignment entry */
+	mptable_add_intsrc(mc, bus_isa, ioapic_id);
+
+	/* Write local interrupt assignment entry */
+	mptable_add_lintsrc(mc, bus_isa);
+
+	/* Finalize the MP table */
+	end = mptable_finalize(mc);
+
+	return end;
+}
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index 8031201..d3bef6f 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -6,6 +6,7 @@ 
 
 #include <common.h>
 #include <asm/sfi.h>
+#include <asm/mpspec.h>
 #include <asm/tables.h>
 
 u8 table_compute_checksum(void *v, int len)
@@ -32,4 +33,8 @@  void write_tables(void)
 	rom_table_end = write_sfi_table(rom_table_end);
 	rom_table_end = ALIGN(rom_table_end, 1024);
 #endif
+#ifdef CONFIG_GENERATE_MP_TABLE
+	rom_table_end = write_mp_table(rom_table_end);
+	rom_table_end = ALIGN(rom_table_end, 1024);
+#endif
 }