aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/powernv
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/powernv')
-rw-r--r--arch/powerpc/platforms/powernv/Kconfig16
-rw-r--r--arch/powerpc/platforms/powernv/Makefile5
-rw-r--r--arch/powerpc/platforms/powernv/opal-nvram.c88
-rw-r--r--arch/powerpc/platforms/powernv/opal-rtc.c97
-rw-r--r--arch/powerpc/platforms/powernv/opal-takeover.S140
-rw-r--r--arch/powerpc/platforms/powernv/opal-wrappers.S101
-rw-r--r--arch/powerpc/platforms/powernv/opal.c322
-rw-r--r--arch/powerpc/platforms/powernv/pci-p5ioc2.c234
-rw-r--r--arch/powerpc/platforms/powernv/pci.c427
-rw-r--r--arch/powerpc/platforms/powernv/pci.h48
-rw-r--r--arch/powerpc/platforms/powernv/powernv.h16
-rw-r--r--arch/powerpc/platforms/powernv/setup.c196
-rw-r--r--arch/powerpc/platforms/powernv/smp.c182
13 files changed, 1872 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
new file mode 100644
index 00000000000..74fea5c2183
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -0,0 +1,16 @@
1config PPC_POWERNV
2 depends on PPC64 && PPC_BOOK3S
3 bool "IBM PowerNV (Non-Virtualized) platform support"
4 select PPC_NATIVE
5 select PPC_XICS
6 select PPC_ICP_NATIVE
7 select PPC_P7_NAP
8 select PPC_PCI_CHOICE if EMBEDDED
9 default y
10
11config 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
new file mode 100644
index 00000000000..31853008b41
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -0,0 +1,5 @@
1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
2obj-y += opal-rtc.o opal-nvram.o
3
4obj-$(CONFIG_SMP) += smp.o
5obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o
diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
new file mode 100644
index 00000000000..3f83e1ae26a
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
@@ -0,0 +1,88 @@
1/*
2 * PowerNV nvram code.
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#define DEBUG
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/of.h>
17
18#include <asm/opal.h>
19#include <asm/machdep.h>
20
21static unsigned int nvram_size;
22
23static ssize_t opal_nvram_size(void)
24{
25 return nvram_size;
26}
27
28static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
29{
30 s64 rc;
31 int off;
32
33 if (*index >= nvram_size)
34 return 0;
35 off = *index;
36 if ((off + count) > nvram_size)
37 count = nvram_size - off;
38 rc = opal_read_nvram(__pa(buf), count, off);
39 if (rc != OPAL_SUCCESS)
40 return -EIO;
41 *index += count;
42 return count;
43}
44
45static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
46{
47 s64 rc = OPAL_BUSY;
48 int off;
49
50 if (*index >= nvram_size)
51 return 0;
52 off = *index;
53 if ((off + count) > nvram_size)
54 count = nvram_size - off;
55
56 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
57 rc = opal_write_nvram(__pa(buf), count, off);
58 if (rc == OPAL_BUSY_EVENT)
59 opal_poll_events(NULL);
60 }
61 *index += count;
62 return count;
63}
64
65void __init opal_nvram_init(void)
66{
67 struct device_node *np;
68 const u32 *nbytes_p;
69
70 np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram");
71 if (np == NULL)
72 return;
73
74 nbytes_p = of_get_property(np, "#bytes", NULL);
75 if (!nbytes_p) {
76 of_node_put(np);
77 return;
78 }
79 nvram_size = *nbytes_p;
80
81 printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size);
82 of_node_put(np);
83
84 ppc_md.nvram_read = opal_nvram_read;
85 ppc_md.nvram_write = opal_nvram_write;
86 ppc_md.nvram_size = opal_nvram_size;
87}
88
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
new file mode 100644
index 00000000000..2aa7641aac9
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -0,0 +1,97 @@
1/*
2 * PowerNV Real Time Clock.
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
13#include <linux/kernel.h>
14#include <linux/time.h>
15#include <linux/bcd.h>
16#include <linux/rtc.h>
17#include <linux/delay.h>
18
19#include <asm/opal.h>
20#include <asm/firmware.h>
21
22static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
23{
24 tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
25 bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
26 tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1;
27 tm->tm_mday = bcd2bin(y_m_d & 0xff);
28 tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
29 tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff);
30 tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff);
31
32 GregorianDay(tm);
33}
34
35unsigned long __init opal_get_boot_time(void)
36{
37 struct rtc_time tm;
38 u32 y_m_d;
39 u64 h_m_s_ms;
40 long rc = OPAL_BUSY;
41
42 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
43 rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
44 if (rc == OPAL_BUSY_EVENT)
45 opal_poll_events(NULL);
46 else
47 mdelay(10);
48 }
49 if (rc != OPAL_SUCCESS)
50 return 0;
51 opal_to_tm(y_m_d, h_m_s_ms, &tm);
52 return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
53 tm.tm_hour, tm.tm_min, tm.tm_sec);
54}
55
56void opal_get_rtc_time(struct rtc_time *tm)
57{
58 long rc = OPAL_BUSY;
59 u32 y_m_d;
60 u64 h_m_s_ms;
61
62 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
63 rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
64 if (rc == OPAL_BUSY_EVENT)
65 opal_poll_events(NULL);
66 else
67 mdelay(10);
68 }
69 if (rc != OPAL_SUCCESS)
70 return;
71 opal_to_tm(y_m_d, h_m_s_ms, tm);
72}
73
74int opal_set_rtc_time(struct rtc_time *tm)
75{
76 long rc = OPAL_BUSY;
77 u32 y_m_d = 0;
78 u64 h_m_s_ms = 0;
79
80 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
81 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
82 y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
83 y_m_d |= ((u32)bin2bcd(tm->tm_mday));
84
85 h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
86 h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
87 h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
88
89 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
90 rc = opal_rtc_write(y_m_d, h_m_s_ms);
91 if (rc == OPAL_BUSY_EVENT)
92 opal_poll_events(NULL);
93 else
94 mdelay(10);
95 }
96 return rc == OPAL_SUCCESS ? 0 : -EIO;
97}
diff --git a/arch/powerpc/platforms/powernv/opal-takeover.S b/arch/powerpc/platforms/powernv/opal-takeover.S
new file mode 100644
index 00000000000..77b48b2b930
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-takeover.S
@@ -0,0 +1,140 @@
1/*
2 * PowerNV OPAL takeover assembly code, for use by prom_init.c
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#define STK_PARAM(i) (48 + ((i)-3)*8)
18
19#define H_HAL_TAKEOVER 0x5124
20#define H_HAL_TAKEOVER_QUERY_MAGIC -1
21
22 .text
23_GLOBAL(opal_query_takeover)
24 mfcr r0
25 stw r0,8(r1)
26 std r3,STK_PARAM(r3)(r1)
27 std r4,STK_PARAM(r4)(r1)
28 li r3,H_HAL_TAKEOVER
29 li r4,H_HAL_TAKEOVER_QUERY_MAGIC
30 HVSC
31 ld r10,STK_PARAM(r3)(r1)
32 std r4,0(r10)
33 ld r10,STK_PARAM(r4)(r1)
34 std r5,0(r10)
35 lwz r0,8(r1)
36 mtcrf 0xff,r0
37 blr
38
39_GLOBAL(opal_do_takeover)
40 mfcr r0
41 stw r0,8(r1)
42 mflr r0
43 std r0,16(r1)
44 bl __opal_do_takeover
45 ld r0,16(r1)
46 mtlr r0
47 lwz r0,8(r1)
48 mtcrf 0xff,r0
49 blr
50
51__opal_do_takeover:
52 ld r4,0(r3)
53 ld r5,0x8(r3)
54 ld r6,0x10(r3)
55 ld r7,0x18(r3)
56 ld r8,0x20(r3)
57 ld r9,0x28(r3)
58 ld r10,0x30(r3)
59 ld r11,0x38(r3)
60 li r3,H_HAL_TAKEOVER
61 HVSC
62 blr
63
64 .globl opal_secondary_entry
65opal_secondary_entry:
66 mr r31,r3
67 mfmsr r11
68 li r12,(MSR_SF | MSR_ISF)@highest
69 sldi r12,r12,48
70 or r11,r11,r12
71 mtmsrd r11
72 isync
73 mfspr r4,SPRN_PIR
74 std r4,0(r3)
751: HMT_LOW
76 ld r4,8(r3)
77 cmpli cr0,r4,0
78 beq 1b
79 HMT_MEDIUM
801: addi r3,r31,16
81 bl __opal_do_takeover
82 b 1b
83
84_GLOBAL(opal_enter_rtas)
85 mflr r0
86 std r0,16(r1)
87 stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */
88
89 /* Because PROM is running in 32b mode, it clobbers the high order half
90 * of all registers that it saves. We therefore save those registers
91 * PROM might touch to the stack. (r0, r3-r13 are caller saved)
92 */
93 SAVE_GPR(2, r1)
94 SAVE_GPR(13, r1)
95 SAVE_8GPRS(14, r1)
96 SAVE_10GPRS(22, r1)
97 mfcr r10
98 mfmsr r11
99 std r10,_CCR(r1)
100 std r11,_MSR(r1)
101
102 /* Get the PROM entrypoint */
103 mtlr r5
104
105 /* Switch MSR to 32 bits mode
106 */
107 li r12,1
108 rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG)
109 andc r11,r11,r12
110 li r12,1
111 rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG)
112 andc r11,r11,r12
113 mtmsrd r11
114 isync
115
116 /* Enter RTAS here... */
117 blrl
118
119 /* Just make sure that r1 top 32 bits didn't get
120 * corrupt by OF
121 */
122 rldicl r1,r1,0,32
123
124 /* Restore the MSR (back to 64 bits) */
125 ld r0,_MSR(r1)
126 MTMSRD(r0)
127 isync
128
129 /* Restore other registers */
130 REST_GPR(2, r1)
131 REST_GPR(13, r1)
132 REST_8GPRS(14, r1)
133 REST_10GPRS(22, r1)
134 ld r4,_CCR(r1)
135 mtcr r4
136
137 addi r1,r1,PROM_FRAME_SIZE
138 ld r0,16(r1)
139 mtlr r0
140 blr
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
new file mode 100644
index 00000000000..4a3f46d8533
--- /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
57OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE);
58OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ);
59OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE);
60OPAL_CALL(opal_rtc_read, OPAL_RTC_READ);
61OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE);
62OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN);
63OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT);
64OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM);
65OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM);
66OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT);
67OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS);
68OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY);
69OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY);
70OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE);
71OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD);
72OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD);
73OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE);
74OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD);
75OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD);
76OPAL_CALL(opal_set_xive, OPAL_SET_XIVE);
77OPAL_CALL(opal_get_xive, OPAL_GET_XIVE);
78OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER);
79OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS);
80OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR);
81OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC);
82OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE);
83OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW);
84OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW);
85OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY);
86OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE);
87OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV);
88OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE);
89OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE);
90OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE);
91OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE);
92OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE);
93OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE);
94OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32);
95OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64);
96OPAL_CALL(opal_start_cpu, OPAL_START_CPU);
97OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS);
98OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL);
99OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW);
100OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL);
101OPAL_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 00000000000..aaa0dba4947
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -0,0 +1,322 @@
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 <linux/interrupt.h>
18#include <asm/opal.h>
19#include <asm/firmware.h>
20
21#include "powernv.h"
22
23struct opal {
24 u64 base;
25 u64 entry;
26} opal;
27
28static struct device_node *opal_node;
29static DEFINE_SPINLOCK(opal_write_lock);
30extern u64 opal_mc_secondary_handler[];
31
32int __init early_init_dt_scan_opal(unsigned long node,
33 const char *uname, int depth, void *data)
34{
35 const void *basep, *entryp;
36 unsigned long basesz, entrysz;
37 u64 glue;
38
39 if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
40 return 0;
41
42 basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
43 entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
44
45 if (!basep || !entryp)
46 return 1;
47
48 opal.base = of_read_number(basep, basesz/4);
49 opal.entry = of_read_number(entryp, entrysz/4);
50
51 pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n",
52 opal.base, basep, basesz);
53 pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
54 opal.entry, entryp, entrysz);
55
56 powerpc_firmware_features |= FW_FEATURE_OPAL;
57 if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
58 powerpc_firmware_features |= FW_FEATURE_OPALv2;
59 printk("OPAL V2 detected !\n");
60 } else {
61 printk("OPAL V1 detected !\n");
62 }
63
64 /* Hookup some exception handlers. We use the fwnmi area at 0x7000
65 * to provide the glue space to OPAL
66 */
67 glue = 0x7000;
68 opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
69 __pa(opal_mc_secondary_handler[0]),
70 glue);
71 glue += 128;
72 opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
73 0, glue);
74 glue += 128;
75 opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
76
77 return 1;
78}
79
80int opal_get_chars(uint32_t vtermno, char *buf, int count)
81{
82 s64 len, rc;
83 u64 evt;
84
85 if (!opal.entry)
86 return -ENODEV;
87 opal_poll_events(&evt);
88 if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
89 return 0;
90 len = count;
91 rc = opal_console_read(vtermno, &len, buf);
92 if (rc == OPAL_SUCCESS)
93 return len;
94 return 0;
95}
96
97int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
98{
99 int written = 0;
100 s64 len, rc;
101 unsigned long flags;
102 u64 evt;
103
104 if (!opal.entry)
105 return -ENODEV;
106
107 /* We want put_chars to be atomic to avoid mangling of hvsi
108 * packets. To do that, we first test for room and return
109 * -EAGAIN if there isn't enough.
110 *
111 * Unfortunately, opal_console_write_buffer_space() doesn't
112 * appear to work on opal v1, so we just assume there is
113 * enough room and be done with it
114 */
115 spin_lock_irqsave(&opal_write_lock, flags);
116 if (firmware_has_feature(FW_FEATURE_OPALv2)) {
117 rc = opal_console_write_buffer_space(vtermno, &len);
118 if (rc || len < total_len) {
119 spin_unlock_irqrestore(&opal_write_lock, flags);
120 /* Closed -> drop characters */
121 if (rc)
122 return total_len;
123 opal_poll_events(&evt);
124 return -EAGAIN;
125 }
126 }
127
128 /* We still try to handle partial completions, though they
129 * should no longer happen.
130 */
131 rc = OPAL_BUSY;
132 while(total_len > 0 && (rc == OPAL_BUSY ||
133 rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
134 len = total_len;
135 rc = opal_console_write(vtermno, &len, data);
136 if (rc == OPAL_SUCCESS) {
137 total_len -= len;
138 data += len;
139 written += len;
140 }
141 /* This is a bit nasty but we need that for the console to
142 * flush when there aren't any interrupts. We will clean
143 * things a bit later to limit that to synchronous path
144 * such as the kernel console and xmon/udbg
145 */
146 do
147 opal_poll_events(&evt);
148 while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT));
149 }
150 spin_unlock_irqrestore(&opal_write_lock, flags);
151 return written;
152}
153
154int opal_machine_check(struct pt_regs *regs)
155{
156 struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
157 struct opal_machine_check_event evt;
158 const char *level, *sevstr, *subtype;
159 static const char *opal_mc_ue_types[] = {
160 "Indeterminate",
161 "Instruction fetch",
162 "Page table walk ifetch",
163 "Load/Store",
164 "Page table walk Load/Store",
165 };
166 static const char *opal_mc_slb_types[] = {
167 "Indeterminate",
168 "Parity",
169 "Multihit",
170 };
171 static const char *opal_mc_erat_types[] = {
172 "Indeterminate",
173 "Parity",
174 "Multihit",
175 };
176 static const char *opal_mc_tlb_types[] = {
177 "Indeterminate",
178 "Parity",
179 "Multihit",
180 };
181
182 /* Copy the event structure and release the original */
183 evt = *opal_evt;
184 opal_evt->in_use = 0;
185
186 /* Print things out */
187 if (evt.version != OpalMCE_V1) {
188 pr_err("Machine Check Exception, Unknown event version %d !\n",
189 evt.version);
190 return 0;
191 }
192 switch(evt.severity) {
193 case OpalMCE_SEV_NO_ERROR:
194 level = KERN_INFO;
195 sevstr = "Harmless";
196 break;
197 case OpalMCE_SEV_WARNING:
198 level = KERN_WARNING;
199 sevstr = "";
200 break;
201 case OpalMCE_SEV_ERROR_SYNC:
202 level = KERN_ERR;
203 sevstr = "Severe";
204 break;
205 case OpalMCE_SEV_FATAL:
206 default:
207 level = KERN_ERR;
208 sevstr = "Fatal";
209 break;
210 }
211
212 printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
213 evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
214 "Recovered" : "[Not recovered");
215 printk("%s Initiator: %s\n", level,
216 evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
217 switch(evt.error_type) {
218 case OpalMCE_ERROR_TYPE_UE:
219 subtype = evt.u.ue_error.ue_error_type <
220 ARRAY_SIZE(opal_mc_ue_types) ?
221 opal_mc_ue_types[evt.u.ue_error.ue_error_type]
222 : "Unknown";
223 printk("%s Error type: UE [%s]\n", level, subtype);
224 if (evt.u.ue_error.effective_address_provided)
225 printk("%s Effective address: %016llx\n",
226 level, evt.u.ue_error.effective_address);
227 if (evt.u.ue_error.physical_address_provided)
228 printk("%s Physial address: %016llx\n",
229 level, evt.u.ue_error.physical_address);
230 break;
231 case OpalMCE_ERROR_TYPE_SLB:
232 subtype = evt.u.slb_error.slb_error_type <
233 ARRAY_SIZE(opal_mc_slb_types) ?
234 opal_mc_slb_types[evt.u.slb_error.slb_error_type]
235 : "Unknown";
236 printk("%s Error type: SLB [%s]\n", level, subtype);
237 if (evt.u.slb_error.effective_address_provided)
238 printk("%s Effective address: %016llx\n",
239 level, evt.u.slb_error.effective_address);
240 break;
241 case OpalMCE_ERROR_TYPE_ERAT:
242 subtype = evt.u.erat_error.erat_error_type <
243 ARRAY_SIZE(opal_mc_erat_types) ?
244 opal_mc_erat_types[evt.u.erat_error.erat_error_type]
245 : "Unknown";
246 printk("%s Error type: ERAT [%s]\n", level, subtype);
247 if (evt.u.erat_error.effective_address_provided)
248 printk("%s Effective address: %016llx\n",
249 level, evt.u.erat_error.effective_address);
250 break;
251 case OpalMCE_ERROR_TYPE_TLB:
252 subtype = evt.u.tlb_error.tlb_error_type <
253 ARRAY_SIZE(opal_mc_tlb_types) ?
254 opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
255 : "Unknown";
256 printk("%s Error type: TLB [%s]\n", level, subtype);
257 if (evt.u.tlb_error.effective_address_provided)
258 printk("%s Effective address: %016llx\n",
259 level, evt.u.tlb_error.effective_address);
260 break;
261 default:
262 case OpalMCE_ERROR_TYPE_UNKNOWN:
263 printk("%s Error type: Unknown\n", level);
264 break;
265 }
266 return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
267}
268
269static irqreturn_t opal_interrupt(int irq, void *data)
270{
271 uint64_t events;
272
273 opal_handle_interrupt(virq_to_hw(irq), &events);
274
275 /* XXX TODO: Do something with the events */
276
277 return IRQ_HANDLED;
278}
279
280static int __init opal_init(void)
281{
282 struct device_node *np, *consoles;
283 const u32 *irqs;
284 int rc, i, irqlen;
285
286 opal_node = of_find_node_by_path("/ibm,opal");
287 if (!opal_node) {
288 pr_warn("opal: Node not found\n");
289 return -ENODEV;
290 }
291 if (firmware_has_feature(FW_FEATURE_OPALv2))
292 consoles = of_find_node_by_path("/ibm,opal/consoles");
293 else
294 consoles = of_node_get(opal_node);
295
296 /* Register serial ports */
297 for_each_child_of_node(consoles, np) {
298 if (strcmp(np->name, "serial"))
299 continue;
300 of_platform_device_create(np, NULL, NULL);
301 }
302 of_node_put(consoles);
303
304 /* Find all OPAL interrupts and request them */
305 irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
306 pr_debug("opal: Found %d interrupts reserved for OPAL\n",
307 irqs ? (irqlen / 4) : 0);
308 for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
309 unsigned int hwirq = be32_to_cpup(irqs);
310 unsigned int irq = irq_create_mapping(NULL, hwirq);
311 if (irq == NO_IRQ) {
312 pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
313 continue;
314 }
315 rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
316 if (rc)
317 pr_warning("opal: Error %d requesting irq %d"
318 " (0x%x)\n", rc, irq, hwirq);
319 }
320 return 0;
321}
322subsys_initcall(opal_init);
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
new file mode 100644
index 00000000000..4c80f7c77d5
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
@@ -0,0 +1,234 @@
1/*
2 * Support PCI/PCIe on PowerNV platforms
3 *
4 * Currently supports only P5IOC2
5 *
6 * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14#include <linux/kernel.h>
15#include <linux/pci.h>
16#include <linux/delay.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/bootmem.h>
20#include <linux/irq.h>
21#include <linux/io.h>
22#include <linux/msi.h>
23
24#include <asm/sections.h>
25#include <asm/io.h>
26#include <asm/prom.h>
27#include <asm/pci-bridge.h>
28#include <asm/machdep.h>
29#include <asm/ppc-pci.h>
30#include <asm/opal.h>
31#include <asm/iommu.h>
32#include <asm/tce.h>
33#include <asm/abs_addr.h>
34
35#include "powernv.h"
36#include "pci.h"
37
38/* For now, use a fixed amount of TCE memory for each p5ioc2
39 * hub, 16M will do
40 */
41#define P5IOC2_TCE_MEMORY 0x01000000
42
43#ifdef CONFIG_PCI_MSI
44static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
45 unsigned int hwirq, unsigned int is_64,
46 struct msi_msg *msg)
47{
48 if (WARN_ON(!is_64))
49 return -ENXIO;
50 msg->data = hwirq - phb->msi_base;
51 msg->address_hi = 0x10000000;
52 msg->address_lo = 0;
53
54 return 0;
55}
56
57static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb)
58{
59 unsigned int bmap_size;
60 const __be32 *prop = of_get_property(phb->hose->dn,
61 "ibm,opal-msi-ranges", NULL);
62 if (!prop)
63 return;
64
65 /* Don't do MSI's on p5ioc2 PCI-X are they are not properly
66 * verified in HW
67 */
68 if (of_device_is_compatible(phb->hose->dn, "ibm,p5ioc2-pcix"))
69 return;
70 phb->msi_base = be32_to_cpup(prop);
71 phb->msi_count = be32_to_cpup(prop + 1);
72 bmap_size = BITS_TO_LONGS(phb->msi_count) * sizeof(unsigned long);
73 phb->msi_map = zalloc_maybe_bootmem(bmap_size, GFP_KERNEL);
74 if (!phb->msi_map) {
75 pr_err("PCI %d: Failed to allocate MSI bitmap !\n",
76 phb->hose->global_number);
77 return;
78 }
79 phb->msi_setup = pnv_pci_p5ioc2_msi_setup;
80 phb->msi32_support = 0;
81 pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n",
82 phb->msi_count, phb->msi_base);
83}
84#else
85static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { }
86#endif /* CONFIG_PCI_MSI */
87
88static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
89 struct pci_dev *pdev)
90{
91 if (phb->p5ioc2.iommu_table.it_map == NULL)
92 iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node);
93
94 set_iommu_table_base(&pdev->dev, &phb->p5ioc2.iommu_table);
95}
96
97static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np,
98 void *tce_mem, u64 tce_size)
99{
100 struct pnv_phb *phb;
101 const u64 *prop64;
102 u64 phb_id;
103 int64_t rc;
104 static int primary = 1;
105
106 pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name);
107
108 prop64 = of_get_property(np, "ibm,opal-phbid", NULL);
109 if (!prop64) {
110 pr_err(" Missing \"ibm,opal-phbid\" property !\n");
111 return;
112 }
113 phb_id = be64_to_cpup(prop64);
114 pr_devel(" PHB-ID : 0x%016llx\n", phb_id);
115 pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem));
116 pr_devel(" TCE SZ : 0x%016llx\n", tce_size);
117
118 rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size);
119 if (rc != OPAL_SUCCESS) {
120 pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc);
121 return;
122 }
123
124 phb = alloc_bootmem(sizeof(struct pnv_phb));
125 if (phb) {
126 memset(phb, 0, sizeof(struct pnv_phb));
127 phb->hose = pcibios_alloc_controller(np);
128 }
129 if (!phb || !phb->hose) {
130 pr_err(" Failed to allocate PCI controller\n");
131 return;
132 }
133
134 spin_lock_init(&phb->lock);
135 phb->hose->first_busno = 0;
136 phb->hose->last_busno = 0xff;
137 phb->hose->private_data = phb;
138 phb->opal_id = phb_id;
139 phb->type = PNV_PHB_P5IOC2;
140
141 phb->regs = of_iomap(np, 0);
142
143 if (phb->regs == NULL)
144 pr_err(" Failed to map registers !\n");
145 else {
146 pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100));
147 pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0));
148 pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0));
149 pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0));
150 pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190));
151 pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0));
152 pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0));
153 pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0));
154 pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0));
155 pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0));
156 pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0));
157 }
158
159 /* Interpret the "ranges" property */
160 /* This also maps the I/O region and sets isa_io/mem_base */
161 pci_process_bridge_OF_ranges(phb->hose, np, primary);
162 primary = 0;
163
164 phb->hose->ops = &pnv_pci_ops;
165
166 /* Setup MSI support */
167 pnv_pci_init_p5ioc2_msis(phb);
168
169 /* Setup TCEs */
170 phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup;
171 pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table,
172 tce_mem, tce_size, 0);
173}
174
175void __init pnv_pci_init_p5ioc2_hub(struct device_node *np)
176{
177 struct device_node *phbn;
178 const u64 *prop64;
179 u64 hub_id;
180 void *tce_mem;
181 uint64_t tce_per_phb;
182 int64_t rc;
183 int phb_count = 0;
184
185 pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name);
186
187 prop64 = of_get_property(np, "ibm,opal-hubid", NULL);
188 if (!prop64) {
189 pr_err(" Missing \"ibm,opal-hubid\" property !\n");
190 return;
191 }
192 hub_id = be64_to_cpup(prop64);
193 pr_info(" HUB-ID : 0x%016llx\n", hub_id);
194
195 /* Currently allocate 16M of TCE memory for every Hub
196 *
197 * XXX TODO: Make it chip local if possible
198 */
199 tce_mem = __alloc_bootmem(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY,
200 __pa(MAX_DMA_ADDRESS));
201 if (!tce_mem) {
202 pr_err(" Failed to allocate TCE Memory !\n");
203 return;
204 }
205 pr_debug(" TCE : 0x%016lx..0x%016lx\n",
206 __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1);
207 rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem),
208 P5IOC2_TCE_MEMORY);
209 if (rc != OPAL_SUCCESS) {
210 pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc);
211 return;
212 }
213
214 /* Count child PHBs */
215 for_each_child_of_node(np, phbn) {
216 if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
217 of_device_is_compatible(phbn, "ibm,p5ioc2-pciex"))
218 phb_count++;
219 }
220
221 /* Calculate how much TCE space we can give per PHB */
222 tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count);
223 pr_info(" Allocating %lld MB of TCE memory per PHB\n",
224 tce_per_phb >> 20);
225
226 /* Initialize PHBs */
227 for_each_child_of_node(np, phbn) {
228 if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
229 of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) {
230 pnv_pci_init_p5ioc2_phb(phbn, tce_mem, tce_per_phb);
231 tce_mem += tce_per_phb;
232 }
233 }
234}
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
new file mode 100644
index 00000000000..85bb66d7f93
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -0,0 +1,427 @@
1/*
2 * Support PCI/PCIe on PowerNV platforms
3 *
4 * Currently supports only P5IOC2
5 *
6 * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14#include <linux/kernel.h>
15#include <linux/pci.h>
16#include <linux/delay.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/bootmem.h>
20#include <linux/irq.h>
21#include <linux/io.h>
22#include <linux/msi.h>
23
24#include <asm/sections.h>
25#include <asm/io.h>
26#include <asm/prom.h>
27#include <asm/pci-bridge.h>
28#include <asm/machdep.h>
29#include <asm/ppc-pci.h>
30#include <asm/opal.h>
31#include <asm/iommu.h>
32#include <asm/tce.h>
33#include <asm/abs_addr.h>
34
35#include "powernv.h"
36#include "pci.h"
37
38/* Delay in usec */
39#define PCI_RESET_DELAY_US 3000000
40
41#define cfg_dbg(fmt...) do { } while(0)
42//#define cfg_dbg(fmt...) printk(fmt)
43
44#ifdef CONFIG_PCI_MSI
45static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
46{
47 struct pci_controller *hose = pci_bus_to_host(pdev->bus);
48 struct pnv_phb *phb = hose->private_data;
49
50 return (phb && phb->msi_map) ? 0 : -ENODEV;
51}
52
53static unsigned int pnv_get_one_msi(struct pnv_phb *phb)
54{
55 unsigned int id;
56
57 spin_lock(&phb->lock);
58 id = find_next_zero_bit(phb->msi_map, phb->msi_count, phb->msi_next);
59 if (id >= phb->msi_count && phb->msi_next)
60 id = find_next_zero_bit(phb->msi_map, phb->msi_count, 0);
61 if (id >= phb->msi_count) {
62 spin_unlock(&phb->lock);
63 return 0;
64 }
65 __set_bit(id, phb->msi_map);
66 spin_unlock(&phb->lock);
67 return id + phb->msi_base;
68}
69
70static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq)
71{
72 unsigned int id;
73
74 if (WARN_ON(hwirq < phb->msi_base ||
75 hwirq >= (phb->msi_base + phb->msi_count)))
76 return;
77 id = hwirq - phb->msi_base;
78 spin_lock(&phb->lock);
79 __clear_bit(id, phb->msi_map);
80 spin_unlock(&phb->lock);
81}
82
83static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
84{
85 struct pci_controller *hose = pci_bus_to_host(pdev->bus);
86 struct pnv_phb *phb = hose->private_data;
87 struct msi_desc *entry;
88 struct msi_msg msg;
89 unsigned int hwirq, virq;
90 int rc;
91
92 if (WARN_ON(!phb))
93 return -ENODEV;
94
95 list_for_each_entry(entry, &pdev->msi_list, list) {
96 if (!entry->msi_attrib.is_64 && !phb->msi32_support) {
97 pr_warn("%s: Supports only 64-bit MSIs\n",
98 pci_name(pdev));
99 return -ENXIO;
100 }
101 hwirq = pnv_get_one_msi(phb);
102 if (!hwirq) {
103 pr_warn("%s: Failed to find a free MSI\n",
104 pci_name(pdev));
105 return -ENOSPC;
106 }
107 virq = irq_create_mapping(NULL, hwirq);
108 if (virq == NO_IRQ) {
109 pr_warn("%s: Failed to map MSI to linux irq\n",
110 pci_name(pdev));
111 pnv_put_msi(phb, hwirq);
112 return -ENOMEM;
113 }
114 rc = phb->msi_setup(phb, pdev, hwirq, entry->msi_attrib.is_64,
115 &msg);
116 if (rc) {
117 pr_warn("%s: Failed to setup MSI\n", pci_name(pdev));
118 irq_dispose_mapping(virq);
119 pnv_put_msi(phb, hwirq);
120 return rc;
121 }
122 irq_set_msi_desc(virq, entry);
123 write_msi_msg(virq, &msg);
124 }
125 return 0;
126}
127
128static void pnv_teardown_msi_irqs(struct pci_dev *pdev)
129{
130 struct pci_controller *hose = pci_bus_to_host(pdev->bus);
131 struct pnv_phb *phb = hose->private_data;
132 struct msi_desc *entry;
133
134 if (WARN_ON(!phb))
135 return;
136
137 list_for_each_entry(entry, &pdev->msi_list, list) {
138 if (entry->irq == NO_IRQ)
139 continue;
140 irq_set_msi_desc(entry->irq, NULL);
141 pnv_put_msi(phb, virq_to_hw(entry->irq));
142 irq_dispose_mapping(entry->irq);
143 }
144}
145#endif /* CONFIG_PCI_MSI */
146
147static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus,
148 u32 bdfn)
149{
150 s64 rc;
151 u8 fstate;
152 u16 pcierr;
153 u32 pe_no;
154
155 /* Get PE# if we support IODA */
156 pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0;
157
158 /* Read freeze status */
159 rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr,
160 NULL);
161 if (rc) {
162 pr_warning("PCI %d: Failed to read EEH status for PE#%d,"
163 " err %lld\n", phb->hose->global_number, pe_no, rc);
164 return;
165 }
166 cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n",
167 bdfn, pe_no, fstate);
168 if (fstate != 0) {
169 rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no,
170 OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
171 if (rc) {
172 pr_warning("PCI %d: Failed to clear EEH freeze state"
173 " for PE#%d, err %lld\n",
174 phb->hose->global_number, pe_no, rc);
175 }
176 }
177}
178
179static int pnv_pci_read_config(struct pci_bus *bus,
180 unsigned int devfn,
181 int where, int size, u32 *val)
182{
183 struct pci_controller *hose = pci_bus_to_host(bus);
184 struct pnv_phb *phb = hose->private_data;
185 u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
186 s64 rc;
187
188 if (hose == NULL)
189 return PCIBIOS_DEVICE_NOT_FOUND;
190
191 switch (size) {
192 case 1: {
193 u8 v8;
194 rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8);
195 *val = (rc == OPAL_SUCCESS) ? v8 : 0xff;
196 break;
197 }
198 case 2: {
199 u16 v16;
200 rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where,
201 &v16);
202 *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff;
203 break;
204 }
205 case 4: {
206 u32 v32;
207 rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32);
208 *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff;
209 break;
210 }
211 default:
212 return PCIBIOS_FUNC_NOT_SUPPORTED;
213 }
214 cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n",
215 bus->number, devfn, where, size, *val);
216
217 /* Check if the PHB got frozen due to an error (no response) */
218 pnv_pci_config_check_eeh(phb, bus, bdfn);
219
220 return PCIBIOS_SUCCESSFUL;
221}
222
223static int pnv_pci_write_config(struct pci_bus *bus,
224 unsigned int devfn,
225 int where, int size, u32 val)
226{
227 struct pci_controller *hose = pci_bus_to_host(bus);
228 struct pnv_phb *phb = hose->private_data;
229 u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
230
231 if (hose == NULL)
232 return PCIBIOS_DEVICE_NOT_FOUND;
233
234 cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n",
235 bus->number, devfn, where, size, val);
236 switch (size) {
237 case 1:
238 opal_pci_config_write_byte(phb->opal_id, bdfn, where, val);
239 break;
240 case 2:
241 opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val);
242 break;
243 case 4:
244 opal_pci_config_write_word(phb->opal_id, bdfn, where, val);
245 break;
246 default:
247 return PCIBIOS_FUNC_NOT_SUPPORTED;
248 }
249 /* Check if the PHB got frozen due to an error (no response) */
250 pnv_pci_config_check_eeh(phb, bus, bdfn);
251
252 return PCIBIOS_SUCCESSFUL;
253}
254
255struct pci_ops pnv_pci_ops = {
256 .read = pnv_pci_read_config,
257 .write = pnv_pci_write_config,
258};
259
260static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
261 unsigned long uaddr, enum dma_data_direction direction,
262 struct dma_attrs *attrs)
263{
264 u64 proto_tce;
265 u64 *tcep;
266 u64 rpn;
267
268 proto_tce = TCE_PCI_READ; // Read allowed
269
270 if (direction != DMA_TO_DEVICE)
271 proto_tce |= TCE_PCI_WRITE;
272
273 tcep = ((u64 *)tbl->it_base) + index;
274
275 while (npages--) {
276 /* can't move this out since we might cross LMB boundary */
277 rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
278 *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
279
280 uaddr += TCE_PAGE_SIZE;
281 tcep++;
282 }
283 return 0;
284}
285
286static void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
287{
288 u64 *tcep = ((u64 *)tbl->it_base) + index;
289
290 while (npages--)
291 *(tcep++) = 0;
292}
293
294void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
295 void *tce_mem, u64 tce_size,
296 u64 dma_offset)
297{
298 tbl->it_blocksize = 16;
299 tbl->it_base = (unsigned long)tce_mem;
300 tbl->it_offset = dma_offset >> IOMMU_PAGE_SHIFT;
301 tbl->it_index = 0;
302 tbl->it_size = tce_size >> 3;
303 tbl->it_busno = 0;
304 tbl->it_type = TCE_PCI;
305}
306
307static struct iommu_table * __devinit
308pnv_pci_setup_bml_iommu(struct pci_controller *hose)
309{
310 struct iommu_table *tbl;
311 const __be64 *basep;
312 const __be32 *sizep;
313
314 basep = of_get_property(hose->dn, "linux,tce-base", NULL);
315 sizep = of_get_property(hose->dn, "linux,tce-size", NULL);
316 if (basep == NULL || sizep == NULL) {
317 pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name);
318 return NULL;
319 }
320 tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node);
321 if (WARN_ON(!tbl))
322 return NULL;
323 pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)),
324 be32_to_cpup(sizep), 0);
325 iommu_init_table(tbl, hose->node);
326 return tbl;
327}
328
329static void __devinit pnv_pci_dma_fallback_setup(struct pci_controller *hose,
330 struct pci_dev *pdev)
331{
332 struct device_node *np = pci_bus_to_OF_node(hose->bus);
333 struct pci_dn *pdn;
334
335 if (np == NULL)
336 return;
337 pdn = PCI_DN(np);
338 if (!pdn->iommu_table)
339 pdn->iommu_table = pnv_pci_setup_bml_iommu(hose);
340 if (!pdn->iommu_table)
341 return;
342 set_iommu_table_base(&pdev->dev, pdn->iommu_table);
343}
344
345static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev)
346{
347 struct pci_controller *hose = pci_bus_to_host(pdev->bus);
348 struct pnv_phb *phb = hose->private_data;
349
350 /* If we have no phb structure, try to setup a fallback based on
351 * the device-tree (RTAS PCI for example)
352 */
353 if (phb && phb->dma_dev_setup)
354 phb->dma_dev_setup(phb, pdev);
355 else
356 pnv_pci_dma_fallback_setup(hose, pdev);
357}
358
359static int pnv_pci_probe_mode(struct pci_bus *bus)
360{
361 struct pci_controller *hose = pci_bus_to_host(bus);
362 const __be64 *tstamp;
363 u64 now, target;
364
365
366 /* We hijack this as a way to ensure we have waited long
367 * enough since the reset was lifted on the PCI bus
368 */
369 if (bus != hose->bus)
370 return PCI_PROBE_NORMAL;
371 tstamp = of_get_property(hose->dn, "reset-clear-timestamp", NULL);
372 if (!tstamp || !*tstamp)
373 return PCI_PROBE_NORMAL;
374
375 now = mftb() / tb_ticks_per_usec;
376 target = (be64_to_cpup(tstamp) / tb_ticks_per_usec)
377 + PCI_RESET_DELAY_US;
378
379 pr_devel("pci %04d: Reset target: 0x%llx now: 0x%llx\n",
380 hose->global_number, target, now);
381
382 if (now < target)
383 msleep((target - now + 999) / 1000);
384
385 return PCI_PROBE_NORMAL;
386}
387
388void __init pnv_pci_init(void)
389{
390 struct device_node *np;
391
392 pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN);
393
394 /* We do not want to just probe */
395 pci_probe_only = 0;
396
397 /* OPAL absent, try POPAL first then RTAS detection of PHBs */
398 if (!firmware_has_feature(FW_FEATURE_OPAL)) {
399#ifdef CONFIG_PPC_POWERNV_RTAS
400 init_pci_config_tokens();
401 find_and_init_phbs();
402#endif /* CONFIG_PPC_POWERNV_RTAS */
403 } else {
404 /* OPAL is here, do our normal stuff */
405
406 /* Look for p5ioc2 IO-Hubs */
407 for_each_compatible_node(np, NULL, "ibm,p5ioc2")
408 pnv_pci_init_p5ioc2_hub(np);
409 }
410
411 /* Setup the linkage between OF nodes and PHBs */
412 pci_devs_phb_init();
413
414 /* Configure IOMMU DMA hooks */
415 ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
416 ppc_md.tce_build = pnv_tce_build;
417 ppc_md.tce_free = pnv_tce_free;
418 ppc_md.pci_probe_mode = pnv_pci_probe_mode;
419 set_pci_dma_ops(&dma_iommu_ops);
420
421 /* Configure MSIs */
422#ifdef CONFIG_PCI_MSI
423 ppc_md.msi_check_device = pnv_msi_check_device;
424 ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
425 ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
426#endif
427}
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
new file mode 100644
index 00000000000..d4dbc495093
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -0,0 +1,48 @@
1#ifndef __POWERNV_PCI_H
2#define __POWERNV_PCI_H
3
4struct pci_dn;
5
6enum pnv_phb_type {
7 PNV_PHB_P5IOC2,
8 PNV_PHB_IODA1,
9 PNV_PHB_IODA2,
10};
11
12struct pnv_phb {
13 struct pci_controller *hose;
14 enum pnv_phb_type type;
15 u64 opal_id;
16 void __iomem *regs;
17 spinlock_t lock;
18
19#ifdef CONFIG_PCI_MSI
20 unsigned long *msi_map;
21 unsigned int msi_base;
22 unsigned int msi_count;
23 unsigned int msi_next;
24 unsigned int msi32_support;
25#endif
26 int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev,
27 unsigned int hwirq, unsigned int is_64,
28 struct msi_msg *msg);
29 void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
30 void (*fixup_phb)(struct pci_controller *hose);
31 u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
32
33 union {
34 struct {
35 struct iommu_table iommu_table;
36 } p5ioc2;
37 };
38};
39
40extern struct pci_ops pnv_pci_ops;
41
42extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
43 void *tce_mem, u64 tce_size,
44 u64 dma_offset);
45extern void pnv_pci_init_p5ioc2_hub(struct device_node *np);
46
47
48#endif /* __POWERNV_PCI_H */
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
new file mode 100644
index 00000000000..8a9df7f9667
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -0,0 +1,16 @@
1#ifndef _POWERNV_H
2#define _POWERNV_H
3
4#ifdef CONFIG_SMP
5extern void pnv_smp_init(void);
6#else
7static inline void pnv_smp_init(void) { }
8#endif
9
10#ifdef CONFIG_PCI
11extern void pnv_pci_init(void);
12#else
13static inline void pnv_pci_init(void) { }
14#endif
15
16#endif /* _POWERNV_H */
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
new file mode 100644
index 00000000000..467bd4ac682
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -0,0 +1,196 @@
1/*
2 * PowerNV setup code.
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/cpu.h>
15#include <linux/errno.h>
16#include <linux/sched.h>
17#include <linux/kernel.h>
18#include <linux/tty.h>
19#include <linux/reboot.h>
20#include <linux/init.h>
21#include <linux/console.h>
22#include <linux/delay.h>
23#include <linux/irq.h>
24#include <linux/seq_file.h>
25#include <linux/of.h>
26#include <linux/interrupt.h>
27#include <linux/bug.h>
28
29#include <asm/machdep.h>
30#include <asm/firmware.h>
31#include <asm/xics.h>
32#include <asm/rtas.h>
33#include <asm/opal.h>
34#include <asm/xics.h>
35
36#include "powernv.h"
37
38static void __init pnv_setup_arch(void)
39{
40 /* Initialize SMP */
41 pnv_smp_init();
42
43 /* Setup PCI */
44 pnv_pci_init();
45
46 /* Setup RTC and NVRAM callbacks */
47 if (firmware_has_feature(FW_FEATURE_OPAL))
48 opal_nvram_init();
49
50 /* Enable NAP mode */
51 powersave_nap = 1;
52
53 /* XXX PMCS */
54}
55
56static void __init pnv_init_early(void)
57{
58#ifdef CONFIG_HVC_OPAL
59 if (firmware_has_feature(FW_FEATURE_OPAL))
60 hvc_opal_init_early();
61 else
62#endif
63 add_preferred_console("hvc", 0, NULL);
64}
65
66static void __init pnv_init_IRQ(void)
67{
68 xics_init();
69
70 WARN_ON(!ppc_md.get_irq);
71}
72
73static void pnv_show_cpuinfo(struct seq_file *m)
74{
75 struct device_node *root;
76 const char *model = "";
77
78 root = of_find_node_by_path("/");
79 if (root)
80 model = of_get_property(root, "model", NULL);
81 seq_printf(m, "machine\t\t: PowerNV %s\n", model);
82 if (firmware_has_feature(FW_FEATURE_OPALv2))
83 seq_printf(m, "firmware\t: OPAL v2\n");
84 else if (firmware_has_feature(FW_FEATURE_OPAL))
85 seq_printf(m, "firmware\t: OPAL v1\n");
86 else
87 seq_printf(m, "firmware\t: BML\n");
88 of_node_put(root);
89}
90
91static void __noreturn pnv_restart(char *cmd)
92{
93 long rc = OPAL_BUSY;
94
95 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
96 rc = opal_cec_reboot();
97 if (rc == OPAL_BUSY_EVENT)
98 opal_poll_events(NULL);
99 else
100 mdelay(10);
101 }
102 for (;;)
103 opal_poll_events(NULL);
104}
105
106static void __noreturn pnv_power_off(void)
107{
108 long rc = OPAL_BUSY;
109
110 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
111 rc = opal_cec_power_down(0);
112 if (rc == OPAL_BUSY_EVENT)
113 opal_poll_events(NULL);
114 else
115 mdelay(10);
116 }
117 for (;;)
118 opal_poll_events(NULL);
119}
120
121static void __noreturn pnv_halt(void)
122{
123 pnv_power_off();
124}
125
126static void pnv_progress(char *s, unsigned short hex)
127{
128}
129
130#ifdef CONFIG_KEXEC
131static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
132{
133 xics_kexec_teardown_cpu(secondary);
134}
135#endif /* CONFIG_KEXEC */
136
137static void __init pnv_setup_machdep_opal(void)
138{
139 ppc_md.get_boot_time = opal_get_boot_time;
140 ppc_md.get_rtc_time = opal_get_rtc_time;
141 ppc_md.set_rtc_time = opal_set_rtc_time;
142 ppc_md.restart = pnv_restart;
143 ppc_md.power_off = pnv_power_off;
144 ppc_md.halt = pnv_halt;
145 ppc_md.machine_check_exception = opal_machine_check;
146}
147
148#ifdef CONFIG_PPC_POWERNV_RTAS
149static void __init pnv_setup_machdep_rtas(void)
150{
151 if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) {
152 ppc_md.get_boot_time = rtas_get_boot_time;
153 ppc_md.get_rtc_time = rtas_get_rtc_time;
154 ppc_md.set_rtc_time = rtas_set_rtc_time;
155 }
156 ppc_md.restart = rtas_restart;
157 ppc_md.power_off = rtas_power_off;
158 ppc_md.halt = rtas_halt;
159}
160#endif /* CONFIG_PPC_POWERNV_RTAS */
161
162static int __init pnv_probe(void)
163{
164 unsigned long root = of_get_flat_dt_root();
165
166 if (!of_flat_dt_is_compatible(root, "ibm,powernv"))
167 return 0;
168
169 hpte_init_native();
170
171 if (firmware_has_feature(FW_FEATURE_OPAL))
172 pnv_setup_machdep_opal();
173#ifdef CONFIG_PPC_POWERNV_RTAS
174 else if (rtas.base)
175 pnv_setup_machdep_rtas();
176#endif /* CONFIG_PPC_POWERNV_RTAS */
177
178 pr_debug("PowerNV detected !\n");
179
180 return 1;
181}
182
183define_machine(powernv) {
184 .name = "PowerNV",
185 .probe = pnv_probe,
186 .init_early = pnv_init_early,
187 .setup_arch = pnv_setup_arch,
188 .init_IRQ = pnv_init_IRQ,
189 .show_cpuinfo = pnv_show_cpuinfo,
190 .progress = pnv_progress,
191 .power_save = power7_idle,
192 .calibrate_decr = generic_calibrate_decr,
193#ifdef CONFIG_KEXEC
194 .kexec_cpu_down = pnv_kexec_cpu_down,
195#endif
196};
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
new file mode 100644
index 00000000000..e8773668524
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -0,0 +1,182 @@
1/*
2 * SMP support for PowerNV machines.
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 <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/sched.h>
15#include <linux/smp.h>
16#include <linux/interrupt.h>
17#include <linux/delay.h>
18#include <linux/init.h>
19#include <linux/spinlock.h>
20#include <linux/cpu.h>
21
22#include <asm/irq.h>
23#include <asm/smp.h>
24#include <asm/paca.h>
25#include <asm/machdep.h>
26#include <asm/cputable.h>
27#include <asm/firmware.h>
28#include <asm/system.h>
29#include <asm/rtas.h>
30#include <asm/vdso_datapage.h>
31#include <asm/cputhreads.h>
32#include <asm/xics.h>
33#include <asm/opal.h>
34
35#include "powernv.h"
36
37#ifdef DEBUG
38#include <asm/udbg.h>
39#define DBG(fmt...) udbg_printf(fmt)
40#else
41#define DBG(fmt...)
42#endif
43
44static void __cpuinit pnv_smp_setup_cpu(int cpu)
45{
46 if (cpu != boot_cpuid)
47 xics_setup_cpu();
48}
49
50static int pnv_smp_cpu_bootable(unsigned int nr)
51{
52 /* Special case - we inhibit secondary thread startup
53 * during boot if the user requests it.
54 */
55 if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) {
56 if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0)
57 return 0;
58 if (smt_enabled_at_boot
59 && cpu_thread_in_core(nr) >= smt_enabled_at_boot)
60 return 0;
61 }
62
63 return 1;
64}
65
66int __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
88#ifdef CONFIG_HOTPLUG_CPU
89
90static int pnv_smp_cpu_disable(void)
91{
92 int cpu = smp_processor_id();
93
94 /* This is identical to pSeries... might consolidate by
95 * moving migrate_irqs_away to a ppc_md with default to
96 * the generic fixup_irqs. --BenH.
97 */
98 set_cpu_online(cpu, false);
99 vdso_data->processorCount--;
100 if (cpu == boot_cpuid)
101 boot_cpuid = cpumask_any(cpu_online_mask);
102 xics_migrate_irqs_away();
103 return 0;
104}
105
106static void pnv_smp_cpu_kill_self(void)
107{
108 unsigned int cpu;
109
110 /* If powersave_nap is enabled, use NAP mode, else just
111 * spin aimlessly
112 */
113 if (!powersave_nap) {
114 generic_mach_cpu_die();
115 return;
116 }
117
118 /* Standard hot unplug procedure */
119 local_irq_disable();
120 idle_task_exit();
121 current->active_mm = NULL; /* for sanity */
122 cpu = smp_processor_id();
123 DBG("CPU%d offline\n", cpu);
124 generic_set_cpu_dead(cpu);
125 smp_wmb();
126
127 /* We don't want to take decrementer interrupts while we are offline,
128 * so clear LPCR:PECE1. We keep PECE2 enabled.
129 */
130 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
131 while (!generic_check_cpu_restart(cpu)) {
132 power7_idle();
133 if (!generic_check_cpu_restart(cpu)) {
134 DBG("CPU%d Unexpected exit while offline !\n", cpu);
135 /* We may be getting an IPI, so we re-enable
136 * interrupts to process it, it will be ignored
137 * since we aren't online (hopefully)
138 */
139 local_irq_enable();
140 local_irq_disable();
141 }
142 }
143 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
144 DBG("CPU%d coming online...\n", cpu);
145}
146
147#endif /* CONFIG_HOTPLUG_CPU */
148
149static struct smp_ops_t pnv_smp_ops = {
150 .message_pass = smp_muxed_ipi_message_pass,
151 .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
152 .probe = xics_smp_probe,
153 .kick_cpu = pnv_smp_kick_cpu,
154 .setup_cpu = pnv_smp_setup_cpu,
155 .cpu_bootable = pnv_smp_cpu_bootable,
156#ifdef CONFIG_HOTPLUG_CPU
157 .cpu_disable = pnv_smp_cpu_disable,
158 .cpu_die = generic_cpu_die,
159#endif /* CONFIG_HOTPLUG_CPU */
160};
161
162/* This is called very early during platform setup_arch */
163void __init pnv_smp_init(void)
164{
165 smp_ops = &pnv_smp_ops;
166
167 /* XXX We don't yet have a proper entry point from HAL, for
168 * now we rely on kexec-style entry from BML
169 */
170
171#ifdef CONFIG_PPC_RTAS
172 /* Non-lpar has additional take/give timebase */
173 if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
174 smp_ops->give_timebase = rtas_give_timebase;
175 smp_ops->take_timebase = rtas_take_timebase;
176 }
177#endif /* CONFIG_PPC_RTAS */
178
179#ifdef CONFIG_HOTPLUG_CPU
180 ppc_md.cpu_die = pnv_smp_cpu_kill_self;
181#endif
182}