diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/lpar.c | 25 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pci_dlpar.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/phyp_dump.c | 507 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pseries.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 12 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/scanlog.c | 37 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/setup.c | 105 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 87 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.h | 3 |
11 files changed, 628 insertions, 158 deletions
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 992ba6753cf2..bdae04bb7a01 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -18,3 +18,4 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o | |||
18 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o | 18 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o |
19 | obj-$(CONFIG_HVCS) += hvcserver.o | 19 | obj-$(CONFIG_HVCS) += hvcserver.o |
20 | obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o | 20 | obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o |
21 | obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o | ||
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 9eb539ee5f9a..550b2f7d2cc1 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -945,7 +945,6 @@ static void *early_enable_eeh(struct device_node *dn, void *data) | |||
945 | unsigned int rets[3]; | 945 | unsigned int rets[3]; |
946 | struct eeh_early_enable_info *info = data; | 946 | struct eeh_early_enable_info *info = data; |
947 | int ret; | 947 | int ret; |
948 | const char *status = of_get_property(dn, "status", NULL); | ||
949 | const u32 *class_code = of_get_property(dn, "class-code", NULL); | 948 | const u32 *class_code = of_get_property(dn, "class-code", NULL); |
950 | const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); | 949 | const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); |
951 | const u32 *device_id = of_get_property(dn, "device-id", NULL); | 950 | const u32 *device_id = of_get_property(dn, "device-id", NULL); |
@@ -959,8 +958,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data) | |||
959 | pdn->eeh_freeze_count = 0; | 958 | pdn->eeh_freeze_count = 0; |
960 | pdn->eeh_false_positives = 0; | 959 | pdn->eeh_false_positives = 0; |
961 | 960 | ||
962 | if (status && strncmp(status, "ok", 2) != 0) | 961 | if (!of_device_is_available(dn)) |
963 | return NULL; /* ignore devices with bad status */ | 962 | return NULL; |
964 | 963 | ||
965 | /* Ignore bad nodes. */ | 964 | /* Ignore bad nodes. */ |
966 | if (!class_code || !vendor_id || !device_id) | 965 | if (!class_code || !vendor_id || !device_id) |
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 9a455d46379d..9235c469449e 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/smp.h> | 40 | #include <asm/smp.h> |
41 | 41 | ||
42 | #include "plpar_wrappers.h" | 42 | #include "plpar_wrappers.h" |
43 | #include "pseries.h" | ||
43 | 44 | ||
44 | #ifdef DEBUG_LOW | 45 | #ifdef DEBUG_LOW |
45 | #define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0) | 46 | #define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0) |
@@ -203,7 +204,6 @@ void __init find_udbg_vterm(void) | |||
203 | struct device_node *stdout_node; | 204 | struct device_node *stdout_node; |
204 | const u32 *termno; | 205 | const u32 *termno; |
205 | const char *name; | 206 | const char *name; |
206 | int add_console; | ||
207 | 207 | ||
208 | /* find the boot console from /chosen/stdout */ | 208 | /* find the boot console from /chosen/stdout */ |
209 | if (!of_chosen) | 209 | if (!of_chosen) |
@@ -219,8 +219,6 @@ void __init find_udbg_vterm(void) | |||
219 | printk(KERN_WARNING "stdout node missing 'name' property!\n"); | 219 | printk(KERN_WARNING "stdout node missing 'name' property!\n"); |
220 | goto out; | 220 | goto out; |
221 | } | 221 | } |
222 | /* The user has requested a console so this is already set up. */ | ||
223 | add_console = !strstr(cmd_line, "console="); | ||
224 | 222 | ||
225 | /* Check if it's a virtual terminal */ | 223 | /* Check if it's a virtual terminal */ |
226 | if (strncmp(name, "vty", 3) != 0) | 224 | if (strncmp(name, "vty", 3) != 0) |
@@ -234,15 +232,13 @@ void __init find_udbg_vterm(void) | |||
234 | udbg_putc = udbg_putcLP; | 232 | udbg_putc = udbg_putcLP; |
235 | udbg_getc = udbg_getcLP; | 233 | udbg_getc = udbg_getcLP; |
236 | udbg_getc_poll = udbg_getc_pollLP; | 234 | udbg_getc_poll = udbg_getc_pollLP; |
237 | if (add_console) | 235 | add_preferred_console("hvc", termno[0] & 0xff, NULL); |
238 | add_preferred_console("hvc", termno[0] & 0xff, NULL); | ||
239 | } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { | 236 | } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { |
240 | vtermno = termno[0]; | 237 | vtermno = termno[0]; |
241 | udbg_putc = udbg_hvsi_putc; | 238 | udbg_putc = udbg_hvsi_putc; |
242 | udbg_getc = udbg_hvsi_getc; | 239 | udbg_getc = udbg_hvsi_getc; |
243 | udbg_getc_poll = udbg_hvsi_getc_poll; | 240 | udbg_getc_poll = udbg_hvsi_getc_poll; |
244 | if (add_console) | 241 | add_preferred_console("hvsi", termno[0] & 0xff, NULL); |
245 | add_preferred_console("hvsi", termno[0] & 0xff, NULL); | ||
246 | } | 242 | } |
247 | out: | 243 | out: |
248 | of_node_put(stdout_node); | 244 | of_node_put(stdout_node); |
@@ -520,6 +516,20 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, | |||
520 | BUG_ON(lpar_rc != H_SUCCESS); | 516 | BUG_ON(lpar_rc != H_SUCCESS); |
521 | } | 517 | } |
522 | 518 | ||
519 | static void pSeries_lpar_hpte_removebolted(unsigned long ea, | ||
520 | int psize, int ssize) | ||
521 | { | ||
522 | unsigned long slot, vsid, va; | ||
523 | |||
524 | vsid = get_kernel_vsid(ea, ssize); | ||
525 | va = hpt_va(ea, vsid, ssize); | ||
526 | |||
527 | slot = pSeries_lpar_hpte_find(va, psize, ssize); | ||
528 | BUG_ON(slot == -1); | ||
529 | |||
530 | pSeries_lpar_hpte_invalidate(slot, va, psize, ssize, 0); | ||
531 | } | ||
532 | |||
523 | /* Flag bits for H_BULK_REMOVE */ | 533 | /* Flag bits for H_BULK_REMOVE */ |
524 | #define HBR_REQUEST 0x4000000000000000UL | 534 | #define HBR_REQUEST 0x4000000000000000UL |
525 | #define HBR_RESPONSE 0x8000000000000000UL | 535 | #define HBR_RESPONSE 0x8000000000000000UL |
@@ -597,6 +607,7 @@ void __init hpte_init_lpar(void) | |||
597 | ppc_md.hpte_updateboltedpp = pSeries_lpar_hpte_updateboltedpp; | 607 | ppc_md.hpte_updateboltedpp = pSeries_lpar_hpte_updateboltedpp; |
598 | ppc_md.hpte_insert = pSeries_lpar_hpte_insert; | 608 | ppc_md.hpte_insert = pSeries_lpar_hpte_insert; |
599 | ppc_md.hpte_remove = pSeries_lpar_hpte_remove; | 609 | ppc_md.hpte_remove = pSeries_lpar_hpte_remove; |
610 | ppc_md.hpte_removebolted = pSeries_lpar_hpte_removebolted; | ||
600 | ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range; | 611 | ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range; |
601 | ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear; | 612 | ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear; |
602 | } | 613 | } |
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 5a5a19e40bb4..0d7229cde0e9 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c | |||
@@ -123,7 +123,7 @@ pcibios_pci_config_bridge(struct pci_dev *dev) | |||
123 | /* Add to children of PCI bridge dev->bus */ | 123 | /* Add to children of PCI bridge dev->bus */ |
124 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); | 124 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); |
125 | if (!child_bus) { | 125 | if (!child_bus) { |
126 | printk (KERN_ERR "%s: could not add second bus\n", __FUNCTION__); | 126 | printk (KERN_ERR "%s: could not add second bus\n", __func__); |
127 | return -EIO; | 127 | return -EIO; |
128 | } | 128 | } |
129 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); | 129 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); |
diff --git a/arch/powerpc/platforms/pseries/phyp_dump.c b/arch/powerpc/platforms/pseries/phyp_dump.c new file mode 100644 index 000000000000..edbc012c2ebc --- /dev/null +++ b/arch/powerpc/platforms/pseries/phyp_dump.c | |||
@@ -0,0 +1,507 @@ | |||
1 | /* | ||
2 | * Hypervisor-assisted dump | ||
3 | * | ||
4 | * Linas Vepstas, Manish Ahuja 2008 | ||
5 | * Copyright 2008 IBM Corp. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/kobject.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/pfn.h> | ||
19 | #include <linux/swap.h> | ||
20 | #include <linux/sysfs.h> | ||
21 | |||
22 | #include <asm/page.h> | ||
23 | #include <asm/phyp_dump.h> | ||
24 | #include <asm/machdep.h> | ||
25 | #include <asm/prom.h> | ||
26 | #include <asm/rtas.h> | ||
27 | |||
28 | /* Variables, used to communicate data between early boot and late boot */ | ||
29 | static struct phyp_dump phyp_dump_vars; | ||
30 | struct phyp_dump *phyp_dump_info = &phyp_dump_vars; | ||
31 | |||
32 | static int ibm_configure_kernel_dump; | ||
33 | /* ------------------------------------------------- */ | ||
34 | /* RTAS interfaces to declare the dump regions */ | ||
35 | |||
36 | struct dump_section { | ||
37 | u32 dump_flags; | ||
38 | u16 source_type; | ||
39 | u16 error_flags; | ||
40 | u64 source_address; | ||
41 | u64 source_length; | ||
42 | u64 length_copied; | ||
43 | u64 destination_address; | ||
44 | }; | ||
45 | |||
46 | struct phyp_dump_header { | ||
47 | u32 version; | ||
48 | u16 num_of_sections; | ||
49 | u16 status; | ||
50 | |||
51 | u32 first_offset_section; | ||
52 | u32 dump_disk_section; | ||
53 | u64 block_num_dd; | ||
54 | u64 num_of_blocks_dd; | ||
55 | u32 offset_dd; | ||
56 | u32 maxtime_to_auto; | ||
57 | /* No dump disk path string used */ | ||
58 | |||
59 | struct dump_section cpu_data; | ||
60 | struct dump_section hpte_data; | ||
61 | struct dump_section kernel_data; | ||
62 | }; | ||
63 | |||
64 | /* The dump header *must be* in low memory, so .bss it */ | ||
65 | static struct phyp_dump_header phdr; | ||
66 | |||
67 | #define NUM_DUMP_SECTIONS 3 | ||
68 | #define DUMP_HEADER_VERSION 0x1 | ||
69 | #define DUMP_REQUEST_FLAG 0x1 | ||
70 | #define DUMP_SOURCE_CPU 0x0001 | ||
71 | #define DUMP_SOURCE_HPTE 0x0002 | ||
72 | #define DUMP_SOURCE_RMO 0x0011 | ||
73 | #define DUMP_ERROR_FLAG 0x2000 | ||
74 | #define DUMP_TRIGGERED 0x4000 | ||
75 | #define DUMP_PERFORMED 0x8000 | ||
76 | |||
77 | |||
78 | /** | ||
79 | * init_dump_header() - initialize the header declaring a dump | ||
80 | * Returns: length of dump save area. | ||
81 | * | ||
82 | * When the hypervisor saves crashed state, it needs to put | ||
83 | * it somewhere. The dump header tells the hypervisor where | ||
84 | * the data can be saved. | ||
85 | */ | ||
86 | static unsigned long init_dump_header(struct phyp_dump_header *ph) | ||
87 | { | ||
88 | unsigned long addr_offset = 0; | ||
89 | |||
90 | /* Set up the dump header */ | ||
91 | ph->version = DUMP_HEADER_VERSION; | ||
92 | ph->num_of_sections = NUM_DUMP_SECTIONS; | ||
93 | ph->status = 0; | ||
94 | |||
95 | ph->first_offset_section = | ||
96 | (u32)offsetof(struct phyp_dump_header, cpu_data); | ||
97 | ph->dump_disk_section = 0; | ||
98 | ph->block_num_dd = 0; | ||
99 | ph->num_of_blocks_dd = 0; | ||
100 | ph->offset_dd = 0; | ||
101 | |||
102 | ph->maxtime_to_auto = 0; /* disabled */ | ||
103 | |||
104 | /* The first two sections are mandatory */ | ||
105 | ph->cpu_data.dump_flags = DUMP_REQUEST_FLAG; | ||
106 | ph->cpu_data.source_type = DUMP_SOURCE_CPU; | ||
107 | ph->cpu_data.source_address = 0; | ||
108 | ph->cpu_data.source_length = phyp_dump_info->cpu_state_size; | ||
109 | ph->cpu_data.destination_address = addr_offset; | ||
110 | addr_offset += phyp_dump_info->cpu_state_size; | ||
111 | |||
112 | ph->hpte_data.dump_flags = DUMP_REQUEST_FLAG; | ||
113 | ph->hpte_data.source_type = DUMP_SOURCE_HPTE; | ||
114 | ph->hpte_data.source_address = 0; | ||
115 | ph->hpte_data.source_length = phyp_dump_info->hpte_region_size; | ||
116 | ph->hpte_data.destination_address = addr_offset; | ||
117 | addr_offset += phyp_dump_info->hpte_region_size; | ||
118 | |||
119 | /* This section describes the low kernel region */ | ||
120 | ph->kernel_data.dump_flags = DUMP_REQUEST_FLAG; | ||
121 | ph->kernel_data.source_type = DUMP_SOURCE_RMO; | ||
122 | ph->kernel_data.source_address = PHYP_DUMP_RMR_START; | ||
123 | ph->kernel_data.source_length = PHYP_DUMP_RMR_END; | ||
124 | ph->kernel_data.destination_address = addr_offset; | ||
125 | addr_offset += ph->kernel_data.source_length; | ||
126 | |||
127 | return addr_offset; | ||
128 | } | ||
129 | |||
130 | static void print_dump_header(const struct phyp_dump_header *ph) | ||
131 | { | ||
132 | #ifdef DEBUG | ||
133 | printk(KERN_INFO "dump header:\n"); | ||
134 | /* setup some ph->sections required */ | ||
135 | printk(KERN_INFO "version = %d\n", ph->version); | ||
136 | printk(KERN_INFO "Sections = %d\n", ph->num_of_sections); | ||
137 | printk(KERN_INFO "Status = 0x%x\n", ph->status); | ||
138 | |||
139 | /* No ph->disk, so all should be set to 0 */ | ||
140 | printk(KERN_INFO "Offset to first section 0x%x\n", | ||
141 | ph->first_offset_section); | ||
142 | printk(KERN_INFO "dump disk sections should be zero\n"); | ||
143 | printk(KERN_INFO "dump disk section = %d\n", ph->dump_disk_section); | ||
144 | printk(KERN_INFO "block num = %ld\n", ph->block_num_dd); | ||
145 | printk(KERN_INFO "number of blocks = %ld\n", ph->num_of_blocks_dd); | ||
146 | printk(KERN_INFO "dump disk offset = %d\n", ph->offset_dd); | ||
147 | printk(KERN_INFO "Max auto time= %d\n", ph->maxtime_to_auto); | ||
148 | |||
149 | /*set cpu state and hpte states as well scratch pad area */ | ||
150 | printk(KERN_INFO " CPU AREA \n"); | ||
151 | printk(KERN_INFO "cpu dump_flags =%d\n", ph->cpu_data.dump_flags); | ||
152 | printk(KERN_INFO "cpu source_type =%d\n", ph->cpu_data.source_type); | ||
153 | printk(KERN_INFO "cpu error_flags =%d\n", ph->cpu_data.error_flags); | ||
154 | printk(KERN_INFO "cpu source_address =%lx\n", | ||
155 | ph->cpu_data.source_address); | ||
156 | printk(KERN_INFO "cpu source_length =%lx\n", | ||
157 | ph->cpu_data.source_length); | ||
158 | printk(KERN_INFO "cpu length_copied =%lx\n", | ||
159 | ph->cpu_data.length_copied); | ||
160 | |||
161 | printk(KERN_INFO " HPTE AREA \n"); | ||
162 | printk(KERN_INFO "HPTE dump_flags =%d\n", ph->hpte_data.dump_flags); | ||
163 | printk(KERN_INFO "HPTE source_type =%d\n", ph->hpte_data.source_type); | ||
164 | printk(KERN_INFO "HPTE error_flags =%d\n", ph->hpte_data.error_flags); | ||
165 | printk(KERN_INFO "HPTE source_address =%lx\n", | ||
166 | ph->hpte_data.source_address); | ||
167 | printk(KERN_INFO "HPTE source_length =%lx\n", | ||
168 | ph->hpte_data.source_length); | ||
169 | printk(KERN_INFO "HPTE length_copied =%lx\n", | ||
170 | ph->hpte_data.length_copied); | ||
171 | |||
172 | printk(KERN_INFO " SRSD AREA \n"); | ||
173 | printk(KERN_INFO "SRSD dump_flags =%d\n", ph->kernel_data.dump_flags); | ||
174 | printk(KERN_INFO "SRSD source_type =%d\n", ph->kernel_data.source_type); | ||
175 | printk(KERN_INFO "SRSD error_flags =%d\n", ph->kernel_data.error_flags); | ||
176 | printk(KERN_INFO "SRSD source_address =%lx\n", | ||
177 | ph->kernel_data.source_address); | ||
178 | printk(KERN_INFO "SRSD source_length =%lx\n", | ||
179 | ph->kernel_data.source_length); | ||
180 | printk(KERN_INFO "SRSD length_copied =%lx\n", | ||
181 | ph->kernel_data.length_copied); | ||
182 | #endif | ||
183 | } | ||
184 | |||
185 | static ssize_t show_phyp_dump_active(struct kobject *kobj, | ||
186 | struct kobj_attribute *attr, char *buf) | ||
187 | { | ||
188 | |||
189 | /* create filesystem entry so kdump is phyp-dump aware */ | ||
190 | return sprintf(buf, "%lx\n", phyp_dump_info->phyp_dump_at_boot); | ||
191 | } | ||
192 | |||
193 | static struct kobj_attribute pdl = __ATTR(phyp_dump_active, 0600, | ||
194 | show_phyp_dump_active, | ||
195 | NULL); | ||
196 | |||
197 | static void register_dump_area(struct phyp_dump_header *ph, unsigned long addr) | ||
198 | { | ||
199 | int rc; | ||
200 | |||
201 | /* Add addr value if not initialized before */ | ||
202 | if (ph->cpu_data.destination_address == 0) { | ||
203 | ph->cpu_data.destination_address += addr; | ||
204 | ph->hpte_data.destination_address += addr; | ||
205 | ph->kernel_data.destination_address += addr; | ||
206 | } | ||
207 | |||
208 | /* ToDo Invalidate kdump and free memory range. */ | ||
209 | |||
210 | do { | ||
211 | rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, | ||
212 | 1, ph, sizeof(struct phyp_dump_header)); | ||
213 | } while (rtas_busy_delay(rc)); | ||
214 | |||
215 | if (rc) { | ||
216 | printk(KERN_ERR "phyp-dump: unexpected error (%d) on " | ||
217 | "register\n", rc); | ||
218 | print_dump_header(ph); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | rc = sysfs_create_file(kernel_kobj, &pdl.attr); | ||
223 | if (rc) | ||
224 | printk(KERN_ERR "phyp-dump: unable to create sysfs" | ||
225 | " file (%d)\n", rc); | ||
226 | } | ||
227 | |||
228 | static | ||
229 | void invalidate_last_dump(struct phyp_dump_header *ph, unsigned long addr) | ||
230 | { | ||
231 | int rc; | ||
232 | |||
233 | /* Add addr value if not initialized before */ | ||
234 | if (ph->cpu_data.destination_address == 0) { | ||
235 | ph->cpu_data.destination_address += addr; | ||
236 | ph->hpte_data.destination_address += addr; | ||
237 | ph->kernel_data.destination_address += addr; | ||
238 | } | ||
239 | |||
240 | do { | ||
241 | rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, | ||
242 | 2, ph, sizeof(struct phyp_dump_header)); | ||
243 | } while (rtas_busy_delay(rc)); | ||
244 | |||
245 | if (rc) { | ||
246 | printk(KERN_ERR "phyp-dump: unexpected error (%d) " | ||
247 | "on invalidate\n", rc); | ||
248 | print_dump_header(ph); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /* ------------------------------------------------- */ | ||
253 | /** | ||
254 | * release_memory_range -- release memory previously lmb_reserved | ||
255 | * @start_pfn: starting physical frame number | ||
256 | * @nr_pages: number of pages to free. | ||
257 | * | ||
258 | * This routine will release memory that had been previously | ||
259 | * lmb_reserved in early boot. The released memory becomes | ||
260 | * available for genreal use. | ||
261 | */ | ||
262 | static void release_memory_range(unsigned long start_pfn, | ||
263 | unsigned long nr_pages) | ||
264 | { | ||
265 | struct page *rpage; | ||
266 | unsigned long end_pfn; | ||
267 | long i; | ||
268 | |||
269 | end_pfn = start_pfn + nr_pages; | ||
270 | |||
271 | for (i = start_pfn; i <= end_pfn; i++) { | ||
272 | rpage = pfn_to_page(i); | ||
273 | if (PageReserved(rpage)) { | ||
274 | ClearPageReserved(rpage); | ||
275 | init_page_count(rpage); | ||
276 | __free_page(rpage); | ||
277 | totalram_pages++; | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * track_freed_range -- Counts the range being freed. | ||
284 | * Once the counter goes to zero, it re-registers dump for | ||
285 | * future use. | ||
286 | */ | ||
287 | static void | ||
288 | track_freed_range(unsigned long addr, unsigned long length) | ||
289 | { | ||
290 | static unsigned long scratch_area_size, reserved_area_size; | ||
291 | |||
292 | if (addr < phyp_dump_info->init_reserve_start) | ||
293 | return; | ||
294 | |||
295 | if ((addr >= phyp_dump_info->init_reserve_start) && | ||
296 | (addr <= phyp_dump_info->init_reserve_start + | ||
297 | phyp_dump_info->init_reserve_size)) | ||
298 | reserved_area_size += length; | ||
299 | |||
300 | if ((addr >= phyp_dump_info->reserved_scratch_addr) && | ||
301 | (addr <= phyp_dump_info->reserved_scratch_addr + | ||
302 | phyp_dump_info->reserved_scratch_size)) | ||
303 | scratch_area_size += length; | ||
304 | |||
305 | if ((reserved_area_size == phyp_dump_info->init_reserve_size) && | ||
306 | (scratch_area_size == phyp_dump_info->reserved_scratch_size)) { | ||
307 | |||
308 | invalidate_last_dump(&phdr, | ||
309 | phyp_dump_info->reserved_scratch_addr); | ||
310 | register_dump_area(&phdr, | ||
311 | phyp_dump_info->reserved_scratch_addr); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | /* ------------------------------------------------- */ | ||
316 | /** | ||
317 | * sysfs_release_region -- sysfs interface to release memory range. | ||
318 | * | ||
319 | * Usage: | ||
320 | * "echo <start addr> <length> > /sys/kernel/release_region" | ||
321 | * | ||
322 | * Example: | ||
323 | * "echo 0x40000000 0x10000000 > /sys/kernel/release_region" | ||
324 | * | ||
325 | * will release 256MB starting at 1GB. | ||
326 | */ | ||
327 | static ssize_t store_release_region(struct kobject *kobj, | ||
328 | struct kobj_attribute *attr, | ||
329 | const char *buf, size_t count) | ||
330 | { | ||
331 | unsigned long start_addr, length, end_addr; | ||
332 | unsigned long start_pfn, nr_pages; | ||
333 | ssize_t ret; | ||
334 | |||
335 | ret = sscanf(buf, "%lx %lx", &start_addr, &length); | ||
336 | if (ret != 2) | ||
337 | return -EINVAL; | ||
338 | |||
339 | track_freed_range(start_addr, length); | ||
340 | |||
341 | /* Range-check - don't free any reserved memory that | ||
342 | * wasn't reserved for phyp-dump */ | ||
343 | if (start_addr < phyp_dump_info->init_reserve_start) | ||
344 | start_addr = phyp_dump_info->init_reserve_start; | ||
345 | |||
346 | end_addr = phyp_dump_info->init_reserve_start + | ||
347 | phyp_dump_info->init_reserve_size; | ||
348 | if (start_addr+length > end_addr) | ||
349 | length = end_addr - start_addr; | ||
350 | |||
351 | /* Release the region of memory assed in by user */ | ||
352 | start_pfn = PFN_DOWN(start_addr); | ||
353 | nr_pages = PFN_DOWN(length); | ||
354 | release_memory_range(start_pfn, nr_pages); | ||
355 | |||
356 | return count; | ||
357 | } | ||
358 | |||
359 | static ssize_t show_release_region(struct kobject *kobj, | ||
360 | struct kobj_attribute *attr, char *buf) | ||
361 | { | ||
362 | u64 second_addr_range; | ||
363 | |||
364 | /* total reserved size - start of scratch area */ | ||
365 | second_addr_range = phyp_dump_info->init_reserve_size - | ||
366 | phyp_dump_info->reserved_scratch_size; | ||
367 | return sprintf(buf, "CPU:0x%lx-0x%lx: HPTE:0x%lx-0x%lx:" | ||
368 | " DUMP:0x%lx-0x%lx, 0x%lx-0x%lx:\n", | ||
369 | phdr.cpu_data.destination_address, | ||
370 | phdr.cpu_data.length_copied, | ||
371 | phdr.hpte_data.destination_address, | ||
372 | phdr.hpte_data.length_copied, | ||
373 | phdr.kernel_data.destination_address, | ||
374 | phdr.kernel_data.length_copied, | ||
375 | phyp_dump_info->init_reserve_start, | ||
376 | second_addr_range); | ||
377 | } | ||
378 | |||
379 | static struct kobj_attribute rr = __ATTR(release_region, 0600, | ||
380 | show_release_region, | ||
381 | store_release_region); | ||
382 | |||
383 | static int __init phyp_dump_setup(void) | ||
384 | { | ||
385 | struct device_node *rtas; | ||
386 | const struct phyp_dump_header *dump_header = NULL; | ||
387 | unsigned long dump_area_start; | ||
388 | unsigned long dump_area_length; | ||
389 | int header_len = 0; | ||
390 | int rc; | ||
391 | |||
392 | /* If no memory was reserved in early boot, there is nothing to do */ | ||
393 | if (phyp_dump_info->init_reserve_size == 0) | ||
394 | return 0; | ||
395 | |||
396 | /* Return if phyp dump not supported */ | ||
397 | if (!phyp_dump_info->phyp_dump_configured) | ||
398 | return -ENOSYS; | ||
399 | |||
400 | /* Is there dump data waiting for us? If there isn't, | ||
401 | * then register a new dump area, and release all of | ||
402 | * the rest of the reserved ram. | ||
403 | * | ||
404 | * The /rtas/ibm,kernel-dump rtas node is present only | ||
405 | * if there is dump data waiting for us. | ||
406 | */ | ||
407 | rtas = of_find_node_by_path("/rtas"); | ||
408 | if (rtas) { | ||
409 | dump_header = of_get_property(rtas, "ibm,kernel-dump", | ||
410 | &header_len); | ||
411 | of_node_put(rtas); | ||
412 | } | ||
413 | |||
414 | print_dump_header(dump_header); | ||
415 | dump_area_length = init_dump_header(&phdr); | ||
416 | /* align down */ | ||
417 | dump_area_start = phyp_dump_info->init_reserve_start & PAGE_MASK; | ||
418 | |||
419 | if (dump_header == NULL) { | ||
420 | register_dump_area(&phdr, dump_area_start); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /* re-register the dump area, if old dump was invalid */ | ||
425 | if ((dump_header) && (dump_header->status & DUMP_ERROR_FLAG)) { | ||
426 | invalidate_last_dump(&phdr, dump_area_start); | ||
427 | register_dump_area(&phdr, dump_area_start); | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | if (dump_header) { | ||
432 | phyp_dump_info->reserved_scratch_addr = | ||
433 | dump_header->cpu_data.destination_address; | ||
434 | phyp_dump_info->reserved_scratch_size = | ||
435 | dump_header->cpu_data.source_length + | ||
436 | dump_header->hpte_data.source_length + | ||
437 | dump_header->kernel_data.source_length; | ||
438 | } | ||
439 | |||
440 | /* Should we create a dump_subsys, analogous to s390/ipl.c ? */ | ||
441 | rc = sysfs_create_file(kernel_kobj, &rr.attr); | ||
442 | if (rc) | ||
443 | printk(KERN_ERR "phyp-dump: unable to create sysfs file (%d)\n", | ||
444 | rc); | ||
445 | |||
446 | /* ToDo: re-register the dump area, for next time. */ | ||
447 | return 0; | ||
448 | } | ||
449 | machine_subsys_initcall(pseries, phyp_dump_setup); | ||
450 | |||
451 | int __init early_init_dt_scan_phyp_dump(unsigned long node, | ||
452 | const char *uname, int depth, void *data) | ||
453 | { | ||
454 | const unsigned int *sizes; | ||
455 | |||
456 | phyp_dump_info->phyp_dump_configured = 0; | ||
457 | phyp_dump_info->phyp_dump_is_active = 0; | ||
458 | |||
459 | if (depth != 1 || strcmp(uname, "rtas") != 0) | ||
460 | return 0; | ||
461 | |||
462 | if (of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL)) | ||
463 | phyp_dump_info->phyp_dump_configured++; | ||
464 | |||
465 | if (of_get_flat_dt_prop(node, "ibm,dump-kernel", NULL)) | ||
466 | phyp_dump_info->phyp_dump_is_active++; | ||
467 | |||
468 | sizes = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", | ||
469 | NULL); | ||
470 | if (!sizes) | ||
471 | return 0; | ||
472 | |||
473 | if (sizes[0] == 1) | ||
474 | phyp_dump_info->cpu_state_size = *((unsigned long *)&sizes[1]); | ||
475 | |||
476 | if (sizes[3] == 2) | ||
477 | phyp_dump_info->hpte_region_size = | ||
478 | *((unsigned long *)&sizes[4]); | ||
479 | return 1; | ||
480 | } | ||
481 | |||
482 | /* Look for phyp_dump= cmdline option */ | ||
483 | static int __init early_phyp_dump_enabled(char *p) | ||
484 | { | ||
485 | phyp_dump_info->phyp_dump_at_boot = 1; | ||
486 | |||
487 | if (!p) | ||
488 | return 0; | ||
489 | |||
490 | if (strncmp(p, "1", 1) == 0) | ||
491 | phyp_dump_info->phyp_dump_at_boot = 1; | ||
492 | else if (strncmp(p, "0", 1) == 0) | ||
493 | phyp_dump_info->phyp_dump_at_boot = 0; | ||
494 | |||
495 | return 0; | ||
496 | } | ||
497 | early_param("phyp_dump", early_phyp_dump_enabled); | ||
498 | |||
499 | /* Look for phyp_dump_reserve_size= cmdline option */ | ||
500 | static int __init early_phyp_dump_reserve_size(char *p) | ||
501 | { | ||
502 | if (p) | ||
503 | phyp_dump_info->reserve_bootvar = memparse(p, &p); | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | early_param("phyp_dump_reserve_size", early_phyp_dump_reserve_size); | ||
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 61136d019554..9e17c0d2a0c8 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h | |||
@@ -38,4 +38,6 @@ extern void pSeries_final_fixup(void); | |||
38 | /* Poweron flag used for enabling auto ups restart */ | 38 | /* Poweron flag used for enabling auto ups restart */ |
39 | extern unsigned long rtas_poweron_auto; | 39 | extern unsigned long rtas_poweron_auto; |
40 | 40 | ||
41 | extern void find_udbg_vterm(void); | ||
42 | |||
41 | #endif /* _PSERIES_PSERIES_H */ | 43 | #endif /* _PSERIES_PSERIES_H */ |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 2800fced8c7c..ac75c10de278 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
@@ -222,14 +222,14 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length | |||
222 | tmp = strchr(buf, ' '); | 222 | tmp = strchr(buf, ' '); |
223 | if (!tmp) { | 223 | if (!tmp) { |
224 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 224 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
225 | __FUNCTION__, __LINE__); | 225 | __func__, __LINE__); |
226 | return NULL; | 226 | return NULL; |
227 | } | 227 | } |
228 | *tmp = '\0'; | 228 | *tmp = '\0'; |
229 | 229 | ||
230 | if (++tmp >= end) { | 230 | if (++tmp >= end) { |
231 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 231 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
232 | __FUNCTION__, __LINE__); | 232 | __func__, __LINE__); |
233 | return NULL; | 233 | return NULL; |
234 | } | 234 | } |
235 | 235 | ||
@@ -238,12 +238,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length | |||
238 | *length = simple_strtoul(tmp, &tmp, 10); | 238 | *length = simple_strtoul(tmp, &tmp, 10); |
239 | if (*length == -1) { | 239 | if (*length == -1) { |
240 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 240 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
241 | __FUNCTION__, __LINE__); | 241 | __func__, __LINE__); |
242 | return NULL; | 242 | return NULL; |
243 | } | 243 | } |
244 | if (*tmp != ' ' || ++tmp >= end) { | 244 | if (*tmp != ' ' || ++tmp >= end) { |
245 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 245 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
246 | __FUNCTION__, __LINE__); | 246 | __func__, __LINE__); |
247 | return NULL; | 247 | return NULL; |
248 | } | 248 | } |
249 | 249 | ||
@@ -252,12 +252,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length | |||
252 | tmp += *length; | 252 | tmp += *length; |
253 | if (tmp > end) { | 253 | if (tmp > end) { |
254 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 254 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
255 | __FUNCTION__, __LINE__); | 255 | __func__, __LINE__); |
256 | return NULL; | 256 | return NULL; |
257 | } | 257 | } |
258 | else if (tmp < end && *tmp != ' ' && *tmp != '\0') { | 258 | else if (tmp < end && *tmp != ' ' && *tmp != '\0') { |
259 | printk(KERN_ERR "property parse failed in %s at line %d\n", | 259 | printk(KERN_ERR "property parse failed in %s at line %d\n", |
260 | __FUNCTION__, __LINE__); | 260 | __func__, __LINE__); |
261 | return NULL; | 261 | return NULL; |
262 | } | 262 | } |
263 | tmp++; | 263 | tmp++; |
diff --git a/arch/powerpc/platforms/pseries/scanlog.c b/arch/powerpc/platforms/pseries/scanlog.c index 8e1ef168e2dd..e5b0ea870164 100644 --- a/arch/powerpc/platforms/pseries/scanlog.c +++ b/arch/powerpc/platforms/pseries/scanlog.c | |||
@@ -195,31 +195,30 @@ const struct file_operations scanlog_fops = { | |||
195 | static int __init scanlog_init(void) | 195 | static int __init scanlog_init(void) |
196 | { | 196 | { |
197 | struct proc_dir_entry *ent; | 197 | struct proc_dir_entry *ent; |
198 | void *data; | ||
199 | int err = -ENOMEM; | ||
198 | 200 | ||
199 | ibm_scan_log_dump = rtas_token("ibm,scan-log-dump"); | 201 | ibm_scan_log_dump = rtas_token("ibm,scan-log-dump"); |
200 | if (ibm_scan_log_dump == RTAS_UNKNOWN_SERVICE) { | 202 | if (ibm_scan_log_dump == RTAS_UNKNOWN_SERVICE) |
201 | printk(KERN_ERR "scan-log-dump not implemented on this system\n"); | 203 | return -ENODEV; |
202 | return -EIO; | ||
203 | } | ||
204 | 204 | ||
205 | ent = create_proc_entry("ppc64/rtas/scan-log-dump", S_IRUSR, NULL); | 205 | /* Ideally we could allocate a buffer < 4G */ |
206 | if (ent) { | 206 | data = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); |
207 | ent->proc_fops = &scanlog_fops; | 207 | if (!data) |
208 | /* Ideally we could allocate a buffer < 4G */ | 208 | goto err; |
209 | ent->data = kmalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); | 209 | |
210 | if (!ent->data) { | 210 | ent = proc_create("ppc64/rtas/scan-log-dump", S_IRUSR, NULL, |
211 | printk(KERN_ERR "Failed to allocate a buffer\n"); | 211 | &scanlog_fops); |
212 | remove_proc_entry("scan-log-dump", ent->parent); | 212 | if (!ent) |
213 | return -ENOMEM; | 213 | goto err; |
214 | } | 214 | |
215 | ((unsigned int *)ent->data)[0] = 0; | 215 | ent->data = data; |
216 | } else { | ||
217 | printk(KERN_ERR "Failed to create ppc64/scan-log-dump proc entry\n"); | ||
218 | return -EIO; | ||
219 | } | ||
220 | proc_ppc64_scan_log_dump = ent; | 216 | proc_ppc64_scan_log_dump = ent; |
221 | 217 | ||
222 | return 0; | 218 | return 0; |
219 | err: | ||
220 | kfree(data); | ||
221 | return err; | ||
223 | } | 222 | } |
224 | 223 | ||
225 | static void __exit scanlog_cleanup(void) | 224 | static void __exit scanlog_cleanup(void) |
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index fdb9b1c8f977..f66aa9c3b135 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c | |||
@@ -76,9 +76,6 @@ | |||
76 | #define DBG(fmt...) | 76 | #define DBG(fmt...) |
77 | #endif | 77 | #endif |
78 | 78 | ||
79 | /* move those away to a .h */ | ||
80 | extern void find_udbg_vterm(void); | ||
81 | |||
82 | int fwnmi_active; /* TRUE if an FWNMI handler is present */ | 79 | int fwnmi_active; /* TRUE if an FWNMI handler is present */ |
83 | 80 | ||
84 | static void pseries_shared_idle_sleep(void); | 81 | static void pseries_shared_idle_sleep(void); |
@@ -127,14 +124,60 @@ void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc) | |||
127 | desc->chip->eoi(irq); | 124 | desc->chip->eoi(irq); |
128 | } | 125 | } |
129 | 126 | ||
130 | static void __init pseries_mpic_init_IRQ(void) | 127 | static void __init pseries_setup_i8259_cascade(void) |
131 | { | 128 | { |
132 | struct device_node *np, *old, *cascade = NULL; | 129 | struct device_node *np, *old, *found = NULL; |
133 | const unsigned int *addrp; | 130 | unsigned int cascade; |
131 | const u32 *addrp; | ||
134 | unsigned long intack = 0; | 132 | unsigned long intack = 0; |
133 | int naddr; | ||
134 | |||
135 | for_each_node_by_type(np, "interrupt-controller") { | ||
136 | if (of_device_is_compatible(np, "chrp,iic")) { | ||
137 | found = np; | ||
138 | break; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if (found == NULL) { | ||
143 | printk(KERN_DEBUG "pic: no ISA interrupt controller\n"); | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | cascade = irq_of_parse_and_map(found, 0); | ||
148 | if (cascade == NO_IRQ) { | ||
149 | printk(KERN_ERR "pic: failed to map cascade interrupt"); | ||
150 | return; | ||
151 | } | ||
152 | pr_debug("pic: cascade mapped to irq %d\n", cascade); | ||
153 | |||
154 | for (old = of_node_get(found); old != NULL ; old = np) { | ||
155 | np = of_get_parent(old); | ||
156 | of_node_put(old); | ||
157 | if (np == NULL) | ||
158 | break; | ||
159 | if (strcmp(np->name, "pci") != 0) | ||
160 | continue; | ||
161 | addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); | ||
162 | if (addrp == NULL) | ||
163 | continue; | ||
164 | naddr = of_n_addr_cells(np); | ||
165 | intack = addrp[naddr-1]; | ||
166 | if (naddr > 1) | ||
167 | intack |= ((unsigned long)addrp[naddr-2]) << 32; | ||
168 | } | ||
169 | if (intack) | ||
170 | printk(KERN_DEBUG "pic: PCI 8259 intack at 0x%016lx\n", intack); | ||
171 | i8259_init(found, intack); | ||
172 | of_node_put(found); | ||
173 | set_irq_chained_handler(cascade, pseries_8259_cascade); | ||
174 | } | ||
175 | |||
176 | static void __init pseries_mpic_init_IRQ(void) | ||
177 | { | ||
178 | struct device_node *np; | ||
135 | const unsigned int *opprop; | 179 | const unsigned int *opprop; |
136 | unsigned long openpic_addr = 0; | 180 | unsigned long openpic_addr = 0; |
137 | unsigned int cascade_irq; | ||
138 | int naddr, n, i, opplen; | 181 | int naddr, n, i, opplen; |
139 | struct mpic *mpic; | 182 | struct mpic *mpic; |
140 | 183 | ||
@@ -167,43 +210,13 @@ static void __init pseries_mpic_init_IRQ(void) | |||
167 | mpic_init(mpic); | 210 | mpic_init(mpic); |
168 | 211 | ||
169 | /* Look for cascade */ | 212 | /* Look for cascade */ |
170 | for_each_node_by_type(np, "interrupt-controller") | 213 | pseries_setup_i8259_cascade(); |
171 | if (of_device_is_compatible(np, "chrp,iic")) { | 214 | } |
172 | cascade = np; | ||
173 | break; | ||
174 | } | ||
175 | if (cascade == NULL) | ||
176 | return; | ||
177 | |||
178 | cascade_irq = irq_of_parse_and_map(cascade, 0); | ||
179 | if (cascade_irq == NO_IRQ) { | ||
180 | printk(KERN_ERR "mpic: failed to map cascade interrupt"); | ||
181 | return; | ||
182 | } | ||
183 | 215 | ||
184 | /* Check ACK type */ | 216 | static void __init pseries_xics_init_IRQ(void) |
185 | for (old = of_node_get(cascade); old != NULL ; old = np) { | 217 | { |
186 | np = of_get_parent(old); | 218 | xics_init_IRQ(); |
187 | of_node_put(old); | 219 | pseries_setup_i8259_cascade(); |
188 | if (np == NULL) | ||
189 | break; | ||
190 | if (strcmp(np->name, "pci") != 0) | ||
191 | continue; | ||
192 | addrp = of_get_property(np, "8259-interrupt-acknowledge", | ||
193 | NULL); | ||
194 | if (addrp == NULL) | ||
195 | continue; | ||
196 | naddr = of_n_addr_cells(np); | ||
197 | intack = addrp[naddr-1]; | ||
198 | if (naddr > 1) | ||
199 | intack |= ((unsigned long)addrp[naddr-2]) << 32; | ||
200 | } | ||
201 | if (intack) | ||
202 | printk(KERN_DEBUG "mpic: PCI 8259 intack at 0x%016lx\n", | ||
203 | intack); | ||
204 | i8259_init(cascade, intack); | ||
205 | of_node_put(cascade); | ||
206 | set_irq_chained_handler(cascade_irq, pseries_8259_cascade); | ||
207 | } | 220 | } |
208 | 221 | ||
209 | static void pseries_lpar_enable_pmcs(void) | 222 | static void pseries_lpar_enable_pmcs(void) |
@@ -235,7 +248,7 @@ static void __init pseries_discover_pic(void) | |||
235 | smp_init_pseries_mpic(); | 248 | smp_init_pseries_mpic(); |
236 | return; | 249 | return; |
237 | } else if (strstr(typep, "ppc-xicp")) { | 250 | } else if (strstr(typep, "ppc-xicp")) { |
238 | ppc_md.init_IRQ = xics_init_IRQ; | 251 | ppc_md.init_IRQ = pseries_xics_init_IRQ; |
239 | setup_kexec_cpu_down_xics(); | 252 | setup_kexec_cpu_down_xics(); |
240 | smp_init_pseries_xics(); | 253 | smp_init_pseries_xics(); |
241 | return; | 254 | return; |
@@ -393,6 +406,7 @@ static void pseries_dedicated_idle_sleep(void) | |||
393 | { | 406 | { |
394 | unsigned int cpu = smp_processor_id(); | 407 | unsigned int cpu = smp_processor_id(); |
395 | unsigned long start_snooze; | 408 | unsigned long start_snooze; |
409 | unsigned long in_purr, out_purr; | ||
396 | 410 | ||
397 | /* | 411 | /* |
398 | * Indicate to the HV that we are idle. Now would be | 412 | * Indicate to the HV that we are idle. Now would be |
@@ -400,6 +414,7 @@ static void pseries_dedicated_idle_sleep(void) | |||
400 | */ | 414 | */ |
401 | get_lppaca()->idle = 1; | 415 | get_lppaca()->idle = 1; |
402 | get_lppaca()->donate_dedicated_cpu = 1; | 416 | get_lppaca()->donate_dedicated_cpu = 1; |
417 | in_purr = mfspr(SPRN_PURR); | ||
403 | 418 | ||
404 | /* | 419 | /* |
405 | * We come in with interrupts disabled, and need_resched() | 420 | * We come in with interrupts disabled, and need_resched() |
@@ -432,6 +447,8 @@ static void pseries_dedicated_idle_sleep(void) | |||
432 | 447 | ||
433 | out: | 448 | out: |
434 | HMT_medium(); | 449 | HMT_medium(); |
450 | out_purr = mfspr(SPRN_PURR); | ||
451 | get_lppaca()->wait_state_cycles += out_purr - in_purr; | ||
435 | get_lppaca()->donate_dedicated_cpu = 0; | 452 | get_lppaca()->donate_dedicated_cpu = 0; |
436 | get_lppaca()->idle = 0; | 453 | get_lppaca()->idle = 0; |
437 | } | 454 | } |
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index a977f200db89..43df53c30aa0 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c | |||
@@ -516,6 +516,8 @@ static struct irq_chip xics_pic_lpar = { | |||
516 | .set_affinity = xics_set_affinity | 516 | .set_affinity = xics_set_affinity |
517 | }; | 517 | }; |
518 | 518 | ||
519 | /* Points to the irq_chip we're actually using */ | ||
520 | static struct irq_chip *xics_irq_chip; | ||
519 | 521 | ||
520 | static int xics_host_match(struct irq_host *h, struct device_node *node) | 522 | static int xics_host_match(struct irq_host *h, struct device_node *node) |
521 | { | 523 | { |
@@ -526,23 +528,13 @@ static int xics_host_match(struct irq_host *h, struct device_node *node) | |||
526 | return !of_device_is_compatible(node, "chrp,iic"); | 528 | return !of_device_is_compatible(node, "chrp,iic"); |
527 | } | 529 | } |
528 | 530 | ||
529 | static int xics_host_map_direct(struct irq_host *h, unsigned int virq, | 531 | static int xics_host_map(struct irq_host *h, unsigned int virq, |
530 | irq_hw_number_t hw) | 532 | irq_hw_number_t hw) |
531 | { | 533 | { |
532 | pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw); | 534 | pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw); |
533 | 535 | ||
534 | get_irq_desc(virq)->status |= IRQ_LEVEL; | 536 | get_irq_desc(virq)->status |= IRQ_LEVEL; |
535 | set_irq_chip_and_handler(virq, &xics_pic_direct, handle_fasteoi_irq); | 537 | set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); |
536 | return 0; | ||
537 | } | ||
538 | |||
539 | static int xics_host_map_lpar(struct irq_host *h, unsigned int virq, | ||
540 | irq_hw_number_t hw) | ||
541 | { | ||
542 | pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw); | ||
543 | |||
544 | get_irq_desc(virq)->status |= IRQ_LEVEL; | ||
545 | set_irq_chip_and_handler(virq, &xics_pic_lpar, handle_fasteoi_irq); | ||
546 | return 0; | 538 | return 0; |
547 | } | 539 | } |
548 | 540 | ||
@@ -561,27 +553,20 @@ static int xics_host_xlate(struct irq_host *h, struct device_node *ct, | |||
561 | return 0; | 553 | return 0; |
562 | } | 554 | } |
563 | 555 | ||
564 | static struct irq_host_ops xics_host_direct_ops = { | 556 | static struct irq_host_ops xics_host_ops = { |
565 | .match = xics_host_match, | 557 | .match = xics_host_match, |
566 | .map = xics_host_map_direct, | 558 | .map = xics_host_map, |
567 | .xlate = xics_host_xlate, | ||
568 | }; | ||
569 | |||
570 | static struct irq_host_ops xics_host_lpar_ops = { | ||
571 | .match = xics_host_match, | ||
572 | .map = xics_host_map_lpar, | ||
573 | .xlate = xics_host_xlate, | 559 | .xlate = xics_host_xlate, |
574 | }; | 560 | }; |
575 | 561 | ||
576 | static void __init xics_init_host(void) | 562 | static void __init xics_init_host(void) |
577 | { | 563 | { |
578 | struct irq_host_ops *ops; | ||
579 | |||
580 | if (firmware_has_feature(FW_FEATURE_LPAR)) | 564 | if (firmware_has_feature(FW_FEATURE_LPAR)) |
581 | ops = &xics_host_lpar_ops; | 565 | xics_irq_chip = &xics_pic_lpar; |
582 | else | 566 | else |
583 | ops = &xics_host_direct_ops; | 567 | xics_irq_chip = &xics_pic_direct; |
584 | xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, ops, | 568 | |
569 | xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, &xics_host_ops, | ||
585 | XICS_IRQ_SPURIOUS); | 570 | XICS_IRQ_SPURIOUS); |
586 | BUG_ON(xics_host == NULL); | 571 | BUG_ON(xics_host == NULL); |
587 | irq_set_default_host(xics_host); | 572 | irq_set_default_host(xics_host); |
@@ -655,52 +640,6 @@ static void __init xics_init_one_node(struct device_node *np, | |||
655 | } | 640 | } |
656 | } | 641 | } |
657 | 642 | ||
658 | |||
659 | static void __init xics_setup_8259_cascade(void) | ||
660 | { | ||
661 | struct device_node *np, *old, *found = NULL; | ||
662 | int cascade, naddr; | ||
663 | const u32 *addrp; | ||
664 | unsigned long intack = 0; | ||
665 | |||
666 | for_each_node_by_type(np, "interrupt-controller") | ||
667 | if (of_device_is_compatible(np, "chrp,iic")) { | ||
668 | found = np; | ||
669 | break; | ||
670 | } | ||
671 | if (found == NULL) { | ||
672 | printk(KERN_DEBUG "xics: no ISA interrupt controller\n"); | ||
673 | return; | ||
674 | } | ||
675 | cascade = irq_of_parse_and_map(found, 0); | ||
676 | if (cascade == NO_IRQ) { | ||
677 | printk(KERN_ERR "xics: failed to map cascade interrupt"); | ||
678 | return; | ||
679 | } | ||
680 | pr_debug("xics: cascade mapped to irq %d\n", cascade); | ||
681 | |||
682 | for (old = of_node_get(found); old != NULL ; old = np) { | ||
683 | np = of_get_parent(old); | ||
684 | of_node_put(old); | ||
685 | if (np == NULL) | ||
686 | break; | ||
687 | if (strcmp(np->name, "pci") != 0) | ||
688 | continue; | ||
689 | addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL); | ||
690 | if (addrp == NULL) | ||
691 | continue; | ||
692 | naddr = of_n_addr_cells(np); | ||
693 | intack = addrp[naddr-1]; | ||
694 | if (naddr > 1) | ||
695 | intack |= ((unsigned long)addrp[naddr-2]) << 32; | ||
696 | } | ||
697 | if (intack) | ||
698 | printk(KERN_DEBUG "xics: PCI 8259 intack at 0x%016lx\n", intack); | ||
699 | i8259_init(found, intack); | ||
700 | of_node_put(found); | ||
701 | set_irq_chained_handler(cascade, pseries_8259_cascade); | ||
702 | } | ||
703 | |||
704 | void __init xics_init_IRQ(void) | 643 | void __init xics_init_IRQ(void) |
705 | { | 644 | { |
706 | struct device_node *np; | 645 | struct device_node *np; |
@@ -733,8 +672,6 @@ void __init xics_init_IRQ(void) | |||
733 | 672 | ||
734 | xics_setup_cpu(); | 673 | xics_setup_cpu(); |
735 | 674 | ||
736 | xics_setup_8259_cascade(); | ||
737 | |||
738 | ppc64_boot_msg(0x21, "XICS Done"); | 675 | ppc64_boot_msg(0x21, "XICS Done"); |
739 | } | 676 | } |
740 | 677 | ||
diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index c26bcff47b6d..1c5321ae8f2f 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h | |||
@@ -28,7 +28,4 @@ struct xics_ipi_struct { | |||
28 | 28 | ||
29 | extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; | 29 | extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; |
30 | 30 | ||
31 | struct irq_desc; | ||
32 | extern void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc); | ||
33 | |||
34 | #endif /* _POWERPC_KERNEL_XICS_H */ | 31 | #endif /* _POWERPC_KERNEL_XICS_H */ |