diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 13:44:57 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-20 02:09:50 -0400 |
commit | 14a43e69ed257a1fadadf9fea2c05adb1686419f (patch) | |
tree | 82ac1fccd465e7a58533c17d14792ceba73fa63e /arch/powerpc/platforms/powernv | |
parent | 817c21ad9a1f00926f080265493923ada3458c63 (diff) |
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/powernv')
-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 24700e8df19c..74fea5c21839 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig | |||
@@ -1,11 +1,16 @@ | |||
1 | config PPC_POWERNV | 1 | config PPC_POWERNV |
2 | depends on PPC64 && PPC_BOOK3S | 2 | depends on PPC64 && PPC_BOOK3S |
3 | bool "IBM PowerNV (Non-Virtualized) platform support" | 3 | bool "IBM PowerNV (Non-Virtualized) platform support" |
4 | select PPC_RTAS | ||
5 | select PPC_NATIVE | 4 | select PPC_NATIVE |
6 | select PPC_XICS | 5 | select PPC_XICS |
7 | select PPC_ICP_NATIVE | 6 | select PPC_ICP_NATIVE |
8 | select PPC_ICS_RTAS | ||
9 | select PPC_P7_NAP | 7 | select PPC_P7_NAP |
10 | select PPC_PCI_CHOICE if EMBEDDED | 8 | select PPC_PCI_CHOICE if EMBEDDED |
11 | default y | 9 | default y |
10 | |||
11 | config PPC_POWERNV_RTAS | ||
12 | depends on PPC_POWERNV | ||
13 | bool "Support for RTAS based PowerNV platforms such as BML" | ||
14 | default y | ||
15 | select PPC_ICS_RTAS | ||
16 | select PPC_RTAS | ||
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 497133027ae5..8f69c0db612c 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | obj-y += setup.o opal-takeover.o | 1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o |
2 | obj-$(CONFIG_SMP) += smp.o | 2 | 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 000000000000..4a3f46d8533e --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * PowerNV OPAL API wrappers | ||
3 | * | ||
4 | * Copyright 2011 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <asm/ppc_asm.h> | ||
13 | #include <asm/hvcall.h> | ||
14 | #include <asm/asm-offsets.h> | ||
15 | #include <asm/opal.h> | ||
16 | |||
17 | /* TODO: | ||
18 | * | ||
19 | * - Trace irqs in/off (needs saving/restoring all args, argh...) | ||
20 | * - Get r11 feed up by Dave so I can have better register usage | ||
21 | */ | ||
22 | #define OPAL_CALL(name, token) \ | ||
23 | _GLOBAL(name); \ | ||
24 | mflr r0; \ | ||
25 | mfcr r12; \ | ||
26 | std r0,16(r1); \ | ||
27 | std r12,8(r1); \ | ||
28 | std r1,PACAR1(r13); \ | ||
29 | li r0,0; \ | ||
30 | mfmsr r12; \ | ||
31 | ori r0,r0,MSR_EE; \ | ||
32 | std r12,PACASAVEDMSR(r13); \ | ||
33 | andc r12,r12,r0; \ | ||
34 | mtmsrd r12,1; \ | ||
35 | LOAD_REG_ADDR(r0,.opal_return); \ | ||
36 | mtlr r0; \ | ||
37 | li r0,MSR_DR|MSR_IR; \ | ||
38 | andc r12,r12,r0; \ | ||
39 | li r0,token; \ | ||
40 | mtspr SPRN_HSRR1,r12; \ | ||
41 | LOAD_REG_ADDR(r11,opal); \ | ||
42 | ld r12,8(r11); \ | ||
43 | ld r2,0(r11); \ | ||
44 | mtspr SPRN_HSRR0,r12; \ | ||
45 | hrfid | ||
46 | |||
47 | _STATIC(opal_return) | ||
48 | ld r2,PACATOC(r13); | ||
49 | ld r4,8(r1); | ||
50 | ld r5,16(r1); | ||
51 | ld r6,PACASAVEDMSR(r13); | ||
52 | mtspr SPRN_SRR0,r5; | ||
53 | mtspr SPRN_SRR1,r6; | ||
54 | mtcr r4; | ||
55 | rfid | ||
56 | |||
57 | OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE); | ||
58 | OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ); | ||
59 | OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE); | ||
60 | OPAL_CALL(opal_rtc_read, OPAL_RTC_READ); | ||
61 | OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE); | ||
62 | OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN); | ||
63 | OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT); | ||
64 | OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM); | ||
65 | OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM); | ||
66 | OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT); | ||
67 | OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS); | ||
68 | OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY); | ||
69 | OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY); | ||
70 | OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE); | ||
71 | OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD); | ||
72 | OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD); | ||
73 | OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE); | ||
74 | OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD); | ||
75 | OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD); | ||
76 | OPAL_CALL(opal_set_xive, OPAL_SET_XIVE); | ||
77 | OPAL_CALL(opal_get_xive, OPAL_GET_XIVE); | ||
78 | OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER); | ||
79 | OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS); | ||
80 | OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR); | ||
81 | OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC); | ||
82 | OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE); | ||
83 | OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW); | ||
84 | OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW); | ||
85 | OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY); | ||
86 | OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE); | ||
87 | OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV); | ||
88 | OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE); | ||
89 | OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE); | ||
90 | OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE); | ||
91 | OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE); | ||
92 | OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE); | ||
93 | OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE); | ||
94 | OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32); | ||
95 | OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64); | ||
96 | OPAL_CALL(opal_start_cpu, OPAL_START_CPU); | ||
97 | OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS); | ||
98 | OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL); | ||
99 | OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW); | ||
100 | OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL); | ||
101 | 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 000000000000..8d5510784cc2 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal.c | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * PowerNV OPAL high level interfaces | ||
3 | * | ||
4 | * Copyright 2011 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #undef DEBUG | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <asm/opal.h> | ||
18 | #include <asm/firmware.h> | ||
19 | |||
20 | #include "powernv.h" | ||
21 | |||
22 | struct opal { | ||
23 | u64 base; | ||
24 | u64 entry; | ||
25 | } opal; | ||
26 | |||
27 | static struct device_node *opal_node; | ||
28 | static DEFINE_SPINLOCK(opal_write_lock); | ||
29 | |||
30 | int __init early_init_dt_scan_opal(unsigned long node, | ||
31 | const char *uname, int depth, void *data) | ||
32 | { | ||
33 | const void *basep, *entryp; | ||
34 | unsigned long basesz, entrysz; | ||
35 | |||
36 | if (depth != 1 || strcmp(uname, "ibm,opal") != 0) | ||
37 | return 0; | ||
38 | |||
39 | basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); | ||
40 | entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); | ||
41 | |||
42 | if (!basep || !entryp) | ||
43 | return 1; | ||
44 | |||
45 | opal.base = of_read_number(basep, basesz/4); | ||
46 | opal.entry = of_read_number(entryp, entrysz/4); | ||
47 | |||
48 | pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", | ||
49 | opal.base, basep, basesz); | ||
50 | pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", | ||
51 | opal.entry, entryp, entrysz); | ||
52 | |||
53 | powerpc_firmware_features |= FW_FEATURE_OPAL; | ||
54 | if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { | ||
55 | powerpc_firmware_features |= FW_FEATURE_OPALv2; | ||
56 | printk("OPAL V2 detected !\n"); | ||
57 | } else { | ||
58 | printk("OPAL V1 detected !\n"); | ||
59 | } | ||
60 | |||
61 | return 1; | ||
62 | } | ||
63 | |||
64 | int opal_get_chars(uint32_t vtermno, char *buf, int count) | ||
65 | { | ||
66 | s64 len, rc; | ||
67 | u64 evt; | ||
68 | |||
69 | if (!opal.entry) | ||
70 | return 0; | ||
71 | opal_poll_events(&evt); | ||
72 | if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) | ||
73 | return 0; | ||
74 | len = count; | ||
75 | rc = opal_console_read(vtermno, &len, buf); | ||
76 | if (rc == OPAL_SUCCESS) | ||
77 | return len; | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | int opal_put_chars(uint32_t vtermno, const char *data, int total_len) | ||
82 | { | ||
83 | int written = 0; | ||
84 | s64 len, rc = OPAL_BUSY; | ||
85 | unsigned long flags; | ||
86 | u64 evt; | ||
87 | |||
88 | if (!opal.entry) | ||
89 | return 0; | ||
90 | |||
91 | /* We want put_chars to be atomic to avoid mangling of hvsi | ||
92 | * packets. To do that, we first test for room and return | ||
93 | * -EAGAIN if there isn't enough | ||
94 | */ | ||
95 | spin_lock_irqsave(&opal_write_lock, flags); | ||
96 | rc = opal_console_write_buffer_space(vtermno, &len); | ||
97 | if (rc || len < total_len) { | ||
98 | spin_unlock_irqrestore(&opal_write_lock, flags); | ||
99 | /* Closed -> drop characters */ | ||
100 | if (rc) | ||
101 | return total_len; | ||
102 | opal_poll_events(&evt); | ||
103 | return -EAGAIN; | ||
104 | } | ||
105 | |||
106 | /* We still try to handle partial completions, though they | ||
107 | * should no longer happen. | ||
108 | */ | ||
109 | while(total_len > 0 && (rc == OPAL_BUSY || | ||
110 | rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { | ||
111 | len = total_len; | ||
112 | rc = opal_console_write(vtermno, &len, data); | ||
113 | if (rc == OPAL_SUCCESS) { | ||
114 | total_len -= len; | ||
115 | data += len; | ||
116 | written += len; | ||
117 | } | ||
118 | /* This is a bit nasty but we need that for the console to | ||
119 | * flush when there aren't any interrupts. We will clean | ||
120 | * things a bit later to limit that to synchronous path | ||
121 | * such as the kernel console and xmon/udbg | ||
122 | */ | ||
123 | do | ||
124 | opal_poll_events(&evt); | ||
125 | while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); | ||
126 | } | ||
127 | spin_unlock_irqrestore(&opal_write_lock, flags); | ||
128 | return written; | ||
129 | } | ||
130 | |||
131 | static int __init opal_init(void) | ||
132 | { | ||
133 | struct device_node *np, *consoles; | ||
134 | |||
135 | opal_node = of_find_node_by_path("/ibm,opal"); | ||
136 | if (!opal_node) { | ||
137 | pr_warn("opal: Node not found\n"); | ||
138 | return -ENODEV; | ||
139 | } | ||
140 | if (firmware_has_feature(FW_FEATURE_OPALv2)) | ||
141 | consoles = of_find_node_by_path("/ibm,opal/consoles"); | ||
142 | else | ||
143 | consoles = of_node_get(opal_node); | ||
144 | |||
145 | /* Register serial ports */ | ||
146 | for_each_child_of_node(consoles, np) { | ||
147 | if (strcmp(np->name, "serial")) | ||
148 | continue; | ||
149 | of_platform_device_create(np, NULL, NULL); | ||
150 | } | ||
151 | of_node_put(consoles); | ||
152 | return 0; | ||
153 | } | ||
154 | subsys_initcall(opal_init); | ||
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 569f9cc4eb04..b6e5ff85cc6f 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) | |||
74 | if (root) | 74 | if (root) |
75 | model = of_get_property(root, "model", NULL); | 75 | model = of_get_property(root, "model", NULL); |
76 | seq_printf(m, "machine\t\t: PowerNV %s\n", model); | 76 | seq_printf(m, "machine\t\t: PowerNV %s\n", model); |
77 | if (firmware_has_feature(FW_FEATURE_OPALv2)) | ||
78 | seq_printf(m, "firmware\t: OPAL v2\n"); | ||
79 | else if (firmware_has_feature(FW_FEATURE_OPAL)) | ||
80 | seq_printf(m, "firmware\t: OPAL v1\n"); | ||
81 | else | ||
82 | seq_printf(m, "firmware\t: BML\n"); | ||
77 | of_node_put(root); | 83 | of_node_put(root); |
78 | } | 84 | } |
79 | 85 | ||
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 4f4ec3797eb6..e87736685243 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <asm/vdso_datapage.h> | 30 | #include <asm/vdso_datapage.h> |
31 | #include <asm/cputhreads.h> | 31 | #include <asm/cputhreads.h> |
32 | #include <asm/xics.h> | 32 | #include <asm/xics.h> |
33 | #include <asm/opal.h> | ||
33 | 34 | ||
34 | #include "powernv.h" | 35 | #include "powernv.h" |
35 | 36 | ||
@@ -62,6 +63,28 @@ static int pnv_smp_cpu_bootable(unsigned int nr) | |||
62 | return 1; | 63 | return 1; |
63 | } | 64 | } |
64 | 65 | ||
66 | int __devinit pnv_smp_kick_cpu(int nr) | ||
67 | { | ||
68 | unsigned int pcpu = get_hard_smp_processor_id(nr); | ||
69 | unsigned long start_here = __pa(*((unsigned long *) | ||
70 | generic_secondary_smp_init)); | ||
71 | long rc; | ||
72 | |||
73 | BUG_ON(nr < 0 || nr >= NR_CPUS); | ||
74 | |||
75 | /* On OPAL v2 the CPU are still spinning inside OPAL itself, | ||
76 | * get them back now | ||
77 | */ | ||
78 | if (firmware_has_feature(FW_FEATURE_OPALv2)) { | ||
79 | pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); | ||
80 | rc = opal_start_cpu(pcpu, start_here); | ||
81 | if (rc != OPAL_SUCCESS) | ||
82 | pr_warn("OPAL Error %ld starting CPU %d\n", | ||
83 | rc, nr); | ||
84 | } | ||
85 | return smp_generic_kick_cpu(nr); | ||
86 | } | ||
87 | |||
65 | #ifdef CONFIG_HOTPLUG_CPU | 88 | #ifdef CONFIG_HOTPLUG_CPU |
66 | 89 | ||
67 | static int pnv_smp_cpu_disable(void) | 90 | static int pnv_smp_cpu_disable(void) |
@@ -127,7 +150,7 @@ static struct smp_ops_t pnv_smp_ops = { | |||
127 | .message_pass = smp_muxed_ipi_message_pass, | 150 | .message_pass = smp_muxed_ipi_message_pass, |
128 | .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ | 151 | .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ |
129 | .probe = xics_smp_probe, | 152 | .probe = xics_smp_probe, |
130 | .kick_cpu = smp_generic_kick_cpu, | 153 | .kick_cpu = pnv_smp_kick_cpu, |
131 | .setup_cpu = pnv_smp_setup_cpu, | 154 | .setup_cpu = pnv_smp_setup_cpu, |
132 | .cpu_bootable = pnv_smp_cpu_bootable, | 155 | .cpu_bootable = pnv_smp_cpu_bootable, |
133 | #ifdef CONFIG_HOTPLUG_CPU | 156 | #ifdef CONFIG_HOTPLUG_CPU |