diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-06 20:12:03 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-06 20:12:03 -0500 |
commit | 1197ab2942f920f261952de0c392ac749a35796b (patch) | |
tree | 4922ccc8a6061e5ece6ac7420001f3bf4524ea92 /arch/powerpc/platforms/powernv | |
parent | ec773e99ab4abce07b1ae23117179c2861831964 (diff) | |
parent | 96cc017c5b7ec095ef047d3c1952b6b6bbf98943 (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (106 commits)
powerpc/p3060qds: Add support for P3060QDS board
powerpc/83xx: Add shutdown request support to MCU handling on MPC8349 MITX
powerpc/85xx: Make kexec to interate over online cpus
powerpc/fsl_booke: Fix comment in head_fsl_booke.S
powerpc/85xx: issue 15 EOI after core reset for FSL CoreNet devices
powerpc/8xxx: Fix interrupt handling in MPC8xxx GPIO driver
powerpc/85xx: Add 'fsl,pq3-gpio' compatiable for GPIO driver
powerpc/86xx: Correct Gianfar support for GE boards
powerpc/cpm: Clear muram before it is in use.
drivers/virt: add ioctl for 32-bit compat on 64-bit to fsl-hv-manager
powerpc/fsl_msi: add support for "msi-address-64" property
powerpc/85xx: Setup secondary cores PIR with hard SMP id
powerpc/fsl-booke: Fix settlbcam for 64-bit
powerpc/85xx: Adding DCSR node to dtsi device trees
powerpc/85xx: clean up FPGA device tree nodes for Freecsale QorIQ boards
powerpc/85xx: fix PHYS_64BIT selection for P1022DS
powerpc/fsl-booke: Fix setup_initial_memory_limit to not blindly map
powerpc: respect mem= setting for early memory limit setup
powerpc: Update corenet64_smp_defconfig
powerpc: Update mpc85xx/corenet 32-bit defconfigs
...
Fix up trivial conflicts in:
- arch/powerpc/configs/40x/hcu4_defconfig
removed stale file, edited elsewhere
- arch/powerpc/include/asm/udbg.h, arch/powerpc/kernel/udbg.c:
added opal and gelic drivers vs added ePAPR driver
- drivers/tty/serial/8250.c
moved UPIO_TSI to powerpc vs removed UPIO_DWAPB support
Diffstat (limited to 'arch/powerpc/platforms/powernv')
-rw-r--r-- | arch/powerpc/platforms/powernv/Kconfig | 16 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-nvram.c | 88 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-rtc.c | 97 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-takeover.S | 140 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-wrappers.S | 101 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 322 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-p5ioc2.c | 234 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 427 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.h | 48 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/powernv.h | 16 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 196 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/smp.c | 182 |
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 000000000000..74fea5c21839 --- /dev/null +++ b/arch/powerpc/platforms/powernv/Kconfig | |||
@@ -0,0 +1,16 @@ | |||
1 | config 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 | |||
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 new file mode 100644 index 000000000000..31853008b418 --- /dev/null +++ b/arch/powerpc/platforms/powernv/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o | ||
2 | obj-y += opal-rtc.o opal-nvram.o | ||
3 | |||
4 | obj-$(CONFIG_SMP) += smp.o | ||
5 | obj-$(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 000000000000..3f83e1ae26ac --- /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 | |||
21 | static unsigned int nvram_size; | ||
22 | |||
23 | static ssize_t opal_nvram_size(void) | ||
24 | { | ||
25 | return nvram_size; | ||
26 | } | ||
27 | |||
28 | static 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 | |||
45 | static 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 | |||
65 | void __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 000000000000..2aa7641aac9b --- /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 | |||
22 | static 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 | |||
35 | unsigned 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 | |||
56 | void 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 | |||
74 | int 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 000000000000..77b48b2b9309 --- /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 | ||
65 | opal_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) | ||
75 | 1: HMT_LOW | ||
76 | ld r4,8(r3) | ||
77 | cmpli cr0,r4,0 | ||
78 | beq 1b | ||
79 | HMT_MEDIUM | ||
80 | 1: 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 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..aaa0dba49471 --- /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 | |||
23 | struct opal { | ||
24 | u64 base; | ||
25 | u64 entry; | ||
26 | } opal; | ||
27 | |||
28 | static struct device_node *opal_node; | ||
29 | static DEFINE_SPINLOCK(opal_write_lock); | ||
30 | extern u64 opal_mc_secondary_handler[]; | ||
31 | |||
32 | int __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 | |||
80 | int 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 | |||
97 | int 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 | |||
154 | int 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 | |||
269 | static 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 | |||
280 | static 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 | } | ||
322 | subsys_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 000000000000..4c80f7c77d56 --- /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 | ||
44 | static 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 | |||
57 | static 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 | ||
85 | static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { } | ||
86 | #endif /* CONFIG_PCI_MSI */ | ||
87 | |||
88 | static 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 | |||
97 | static 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 | |||
175 | void __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 000000000000..85bb66d7f933 --- /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 | ||
45 | static 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 | |||
53 | static 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 | |||
70 | static 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 | |||
83 | static 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 | |||
128 | static 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 | |||
147 | static 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 | |||
179 | static 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 | |||
223 | static 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 | |||
255 | struct pci_ops pnv_pci_ops = { | ||
256 | .read = pnv_pci_read_config, | ||
257 | .write = pnv_pci_write_config, | ||
258 | }; | ||
259 | |||
260 | static 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 | |||
286 | static 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 | |||
294 | void 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 | |||
307 | static struct iommu_table * __devinit | ||
308 | pnv_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 | |||
329 | static 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 | |||
345 | static 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 | |||
359 | static 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 | |||
388 | void __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 000000000000..d4dbc4950936 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci.h | |||
@@ -0,0 +1,48 @@ | |||
1 | #ifndef __POWERNV_PCI_H | ||
2 | #define __POWERNV_PCI_H | ||
3 | |||
4 | struct pci_dn; | ||
5 | |||
6 | enum pnv_phb_type { | ||
7 | PNV_PHB_P5IOC2, | ||
8 | PNV_PHB_IODA1, | ||
9 | PNV_PHB_IODA2, | ||
10 | }; | ||
11 | |||
12 | struct 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 | |||
40 | extern struct pci_ops pnv_pci_ops; | ||
41 | |||
42 | extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, | ||
43 | void *tce_mem, u64 tce_size, | ||
44 | u64 dma_offset); | ||
45 | extern 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 000000000000..8a9df7f9667e --- /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 | ||
5 | extern void pnv_smp_init(void); | ||
6 | #else | ||
7 | static inline void pnv_smp_init(void) { } | ||
8 | #endif | ||
9 | |||
10 | #ifdef CONFIG_PCI | ||
11 | extern void pnv_pci_init(void); | ||
12 | #else | ||
13 | static 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 000000000000..467bd4ac6824 --- /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 | |||
38 | static 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 | |||
56 | static 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 | |||
66 | static void __init pnv_init_IRQ(void) | ||
67 | { | ||
68 | xics_init(); | ||
69 | |||
70 | WARN_ON(!ppc_md.get_irq); | ||
71 | } | ||
72 | |||
73 | static 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 | |||
91 | static 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 | |||
106 | static 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 | |||
121 | static void __noreturn pnv_halt(void) | ||
122 | { | ||
123 | pnv_power_off(); | ||
124 | } | ||
125 | |||
126 | static void pnv_progress(char *s, unsigned short hex) | ||
127 | { | ||
128 | } | ||
129 | |||
130 | #ifdef CONFIG_KEXEC | ||
131 | static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) | ||
132 | { | ||
133 | xics_kexec_teardown_cpu(secondary); | ||
134 | } | ||
135 | #endif /* CONFIG_KEXEC */ | ||
136 | |||
137 | static 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 | ||
149 | static 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 | |||
162 | static 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 | |||
183 | define_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 000000000000..e87736685243 --- /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 | |||
44 | static void __cpuinit pnv_smp_setup_cpu(int cpu) | ||
45 | { | ||
46 | if (cpu != boot_cpuid) | ||
47 | xics_setup_cpu(); | ||
48 | } | ||
49 | |||
50 | static 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 | |||
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 | |||
88 | #ifdef CONFIG_HOTPLUG_CPU | ||
89 | |||
90 | static 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 | |||
106 | static 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 | |||
149 | static 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 */ | ||
163 | void __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 | } | ||