diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 17:44:57 (GMT) |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-20 06:09:50 (GMT) |
commit | 14a43e69ed257a1fadadf9fea2c05adb1686419f (patch) | |
tree | 82ac1fccd465e7a58533c17d14792ceba73fa63e /arch/powerpc/platforms | |
parent | 817c21ad9a1f00926f080265493923ada3458c63 (diff) | |
download | linux-fsl-qoriq-14a43e69ed257a1fadadf9fea2c05adb1686419f.tar.xz |
powerpc/powernv: Basic support for OPAL
Add definition of OPAL interfaces along with the wrappers to call
into OPAL runtime and the early device-tree parsing hook to locate
the OPAL runtime firmware.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/powernv/Kconfig | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-wrappers.S | 101 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 154 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/smp.c | 25 |
6 files changed, 293 insertions, 4 deletions
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 24700e8..74fea5c 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -1,11 +1,16 @@ config PPC_POWERNV depends on PPC64 && PPC_BOOK3S bool "IBM PowerNV (Non-Virtualized) platform support" - select PPC_RTAS select PPC_NATIVE select PPC_XICS select PPC_ICP_NATIVE - select PPC_ICS_RTAS select PPC_P7_NAP select PPC_PCI_CHOICE if EMBEDDED default y + +config PPC_POWERNV_RTAS + depends on PPC_POWERNV + bool "Support for RTAS based PowerNV platforms such as BML" + default y + select PPC_ICS_RTAS + select PPC_RTAS diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 4971330..8f69c0d 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,2 @@ -obj-y += setup.o opal-takeover.o +obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S new file mode 100644 index 0000000..4a3f46d --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -0,0 +1,101 @@ +/* + * PowerNV OPAL API wrappers + * + * Copyright 2011 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/ppc_asm.h> +#include <asm/hvcall.h> +#include <asm/asm-offsets.h> +#include <asm/opal.h> + +/* TODO: + * + * - Trace irqs in/off (needs saving/restoring all args, argh...) + * - Get r11 feed up by Dave so I can have better register usage + */ +#define OPAL_CALL(name, token) \ + _GLOBAL(name); \ + mflr r0; \ + mfcr r12; \ + std r0,16(r1); \ + std r12,8(r1); \ + std r1,PACAR1(r13); \ + li r0,0; \ + mfmsr r12; \ + ori r0,r0,MSR_EE; \ + std r12,PACASAVEDMSR(r13); \ + andc r12,r12,r0; \ + mtmsrd r12,1; \ + LOAD_REG_ADDR(r0,.opal_return); \ + mtlr r0; \ + li r0,MSR_DR|MSR_IR; \ + andc r12,r12,r0; \ + li r0,token; \ + mtspr SPRN_HSRR1,r12; \ + LOAD_REG_ADDR(r11,opal); \ + ld r12,8(r11); \ + ld r2,0(r11); \ + mtspr SPRN_HSRR0,r12; \ + hrfid + +_STATIC(opal_return) + ld r2,PACATOC(r13); + ld r4,8(r1); + ld r5,16(r1); + ld r6,PACASAVEDMSR(r13); + mtspr SPRN_SRR0,r5; + mtspr SPRN_SRR1,r6; + mtcr r4; + rfid + +OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE); +OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ); +OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE); +OPAL_CALL(opal_rtc_read, OPAL_RTC_READ); +OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE); +OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN); +OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT); +OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM); +OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM); +OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT); +OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS); +OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY); +OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY); +OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE); +OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD); +OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD); +OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE); +OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD); +OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD); +OPAL_CALL(opal_set_xive, OPAL_SET_XIVE); +OPAL_CALL(opal_get_xive, OPAL_GET_XIVE); +OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER); +OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS); +OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR); +OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC); +OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE); +OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW); +OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW); +OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY); +OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE); +OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV); +OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE); +OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE); +OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE); +OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE); +OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32); +OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64); +OPAL_CALL(opal_start_cpu, OPAL_START_CPU); +OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS); +OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL); +OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW); +OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL); +OPAL_CALL(opal_pci_reset, OPAL_PCI_RESET); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c new file mode 100644 index 0000000..8d55107 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal.c @@ -0,0 +1,154 @@ +/* + * PowerNV OPAL high level interfaces + * + * Copyright 2011 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#undef DEBUG + +#include <linux/types.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <asm/opal.h> +#include <asm/firmware.h> + +#include "powernv.h" + +struct opal { + u64 base; + u64 entry; +} opal; + +static struct device_node *opal_node; +static DEFINE_SPINLOCK(opal_write_lock); + +int __init early_init_dt_scan_opal(unsigned long node, + const char *uname, int depth, void *data) +{ + const void *basep, *entryp; + unsigned long basesz, entrysz; + + if (depth != 1 || strcmp(uname, "ibm,opal") != 0) + return 0; + + basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); + entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); + + if (!basep || !entryp) + return 1; + + opal.base = of_read_number(basep, basesz/4); + opal.entry = of_read_number(entryp, entrysz/4); + + pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", + opal.base, basep, basesz); + pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", + opal.entry, entryp, entrysz); + + powerpc_firmware_features |= FW_FEATURE_OPAL; + if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { + powerpc_firmware_features |= FW_FEATURE_OPALv2; + printk("OPAL V2 detected !\n"); + } else { + printk("OPAL V1 detected !\n"); + } + + return 1; +} + +int opal_get_chars(uint32_t vtermno, char *buf, int count) +{ + s64 len, rc; + u64 evt; + + if (!opal.entry) + return 0; + opal_poll_events(&evt); + if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) + return 0; + len = count; + rc = opal_console_read(vtermno, &len, buf); + if (rc == OPAL_SUCCESS) + return len; + return 0; +} + +int opal_put_chars(uint32_t vtermno, const char *data, int total_len) +{ + int written = 0; + s64 len, rc = OPAL_BUSY; + unsigned long flags; + u64 evt; + + if (!opal.entry) + return 0; + + /* We want put_chars to be atomic to avoid mangling of hvsi + * packets. To do that, we first test for room and return + * -EAGAIN if there isn't enough + */ + spin_lock_irqsave(&opal_write_lock, flags); + rc = opal_console_write_buffer_space(vtermno, &len); + if (rc || len < total_len) { + spin_unlock_irqrestore(&opal_write_lock, flags); + /* Closed -> drop characters */ + if (rc) + return total_len; + opal_poll_events(&evt); + return -EAGAIN; + } + + /* We still try to handle partial completions, though they + * should no longer happen. + */ + while(total_len > 0 && (rc == OPAL_BUSY || + rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { + len = total_len; + rc = opal_console_write(vtermno, &len, data); + if (rc == OPAL_SUCCESS) { + total_len -= len; + data += len; + written += len; + } + /* This is a bit nasty but we need that for the console to + * flush when there aren't any interrupts. We will clean + * things a bit later to limit that to synchronous path + * such as the kernel console and xmon/udbg + */ + do + opal_poll_events(&evt); + while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); + } + spin_unlock_irqrestore(&opal_write_lock, flags); + return written; +} + +static int __init opal_init(void) +{ + struct device_node *np, *consoles; + + opal_node = of_find_node_by_path("/ibm,opal"); + if (!opal_node) { + pr_warn("opal: Node not found\n"); + return -ENODEV; + } + if (firmware_has_feature(FW_FEATURE_OPALv2)) + consoles = of_find_node_by_path("/ibm,opal/consoles"); + else + consoles = of_node_get(opal_node); + + /* Register serial ports */ + for_each_child_of_node(consoles, np) { + if (strcmp(np->name, "serial")) + continue; + of_platform_device_create(np, NULL, NULL); + } + of_node_put(consoles); + return 0; +} +subsys_initcall(opal_init); diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 569f9cc..b6e5ff8 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -74,6 +74,12 @@ static void pnv_show_cpuinfo(struct seq_file *m) if (root) model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: PowerNV %s\n", model); + if (firmware_has_feature(FW_FEATURE_OPALv2)) + seq_printf(m, "firmware\t: OPAL v2\n"); + else if (firmware_has_feature(FW_FEATURE_OPAL)) + seq_printf(m, "firmware\t: OPAL v1\n"); + else + seq_printf(m, "firmware\t: BML\n"); of_node_put(root); } diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 4f4ec37..e877366 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -30,6 +30,7 @@ #include <asm/vdso_datapage.h> #include <asm/cputhreads.h> #include <asm/xics.h> +#include <asm/opal.h> #include "powernv.h" @@ -62,6 +63,28 @@ static int pnv_smp_cpu_bootable(unsigned int nr) return 1; } +int __devinit pnv_smp_kick_cpu(int nr) +{ + unsigned int pcpu = get_hard_smp_processor_id(nr); + unsigned long start_here = __pa(*((unsigned long *) + generic_secondary_smp_init)); + long rc; + + BUG_ON(nr < 0 || nr >= NR_CPUS); + + /* On OPAL v2 the CPU are still spinning inside OPAL itself, + * get them back now + */ + if (firmware_has_feature(FW_FEATURE_OPALv2)) { + pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); + rc = opal_start_cpu(pcpu, start_here); + if (rc != OPAL_SUCCESS) + pr_warn("OPAL Error %ld starting CPU %d\n", + rc, nr); + } + return smp_generic_kick_cpu(nr); +} + #ifdef CONFIG_HOTPLUG_CPU static int pnv_smp_cpu_disable(void) @@ -127,7 +150,7 @@ static struct smp_ops_t pnv_smp_ops = { .message_pass = smp_muxed_ipi_message_pass, .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ .probe = xics_smp_probe, - .kick_cpu = smp_generic_kick_cpu, + .kick_cpu = pnv_smp_kick_cpu, .setup_cpu = pnv_smp_setup_cpu, .cpu_bootable = pnv_smp_cpu_bootable, #ifdef CONFIG_HOTPLUG_CPU |