Message ID | BLU436-SMTP19282A90307320CAD64B74BFB80@phx.gbl |
---|---|
State | Superseded |
Delegated to: | Simon Glass |
Headers | show |
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
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
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 --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 }
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(+)