diff options
Diffstat (limited to 'arch/powerpc/kernel/prom.c')
-rw-r--r-- | arch/powerpc/kernel/prom.c | 120 |
1 files changed, 107 insertions, 13 deletions
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index eac97f48b9b8..3bfe7837e820 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c | |||
@@ -31,10 +31,10 @@ | |||
31 | #include <linux/kexec.h> | 31 | #include <linux/kexec.h> |
32 | #include <linux/debugfs.h> | 32 | #include <linux/debugfs.h> |
33 | #include <linux/irq.h> | 33 | #include <linux/irq.h> |
34 | #include <linux/lmb.h> | ||
34 | 35 | ||
35 | #include <asm/prom.h> | 36 | #include <asm/prom.h> |
36 | #include <asm/rtas.h> | 37 | #include <asm/rtas.h> |
37 | #include <asm/lmb.h> | ||
38 | #include <asm/page.h> | 38 | #include <asm/page.h> |
39 | #include <asm/processor.h> | 39 | #include <asm/processor.h> |
40 | #include <asm/irq.h> | 40 | #include <asm/irq.h> |
@@ -51,6 +51,7 @@ | |||
51 | #include <asm/machdep.h> | 51 | #include <asm/machdep.h> |
52 | #include <asm/pSeries_reconfig.h> | 52 | #include <asm/pSeries_reconfig.h> |
53 | #include <asm/pci-bridge.h> | 53 | #include <asm/pci-bridge.h> |
54 | #include <asm/phyp_dump.h> | ||
54 | #include <asm/kexec.h> | 55 | #include <asm/kexec.h> |
55 | 56 | ||
56 | #ifdef DEBUG | 57 | #ifdef DEBUG |
@@ -436,7 +437,7 @@ early_param("mem", early_parse_mem); | |||
436 | * The device tree may be allocated beyond our memory limit, or inside the | 437 | * The device tree may be allocated beyond our memory limit, or inside the |
437 | * crash kernel region for kdump. If so, move it out of the way. | 438 | * crash kernel region for kdump. If so, move it out of the way. |
438 | */ | 439 | */ |
439 | static void move_device_tree(void) | 440 | static void __init move_device_tree(void) |
440 | { | 441 | { |
441 | unsigned long start, size; | 442 | unsigned long start, size; |
442 | void *p; | 443 | void *p; |
@@ -1040,6 +1041,87 @@ static void __init early_reserve_mem(void) | |||
1040 | #endif | 1041 | #endif |
1041 | } | 1042 | } |
1042 | 1043 | ||
1044 | #ifdef CONFIG_PHYP_DUMP | ||
1045 | /** | ||
1046 | * phyp_dump_calculate_reserve_size() - reserve variable boot area 5% or arg | ||
1047 | * | ||
1048 | * Function to find the largest size we need to reserve | ||
1049 | * during early boot process. | ||
1050 | * | ||
1051 | * It either looks for boot param and returns that OR | ||
1052 | * returns larger of 256 or 5% rounded down to multiples of 256MB. | ||
1053 | * | ||
1054 | */ | ||
1055 | static inline unsigned long phyp_dump_calculate_reserve_size(void) | ||
1056 | { | ||
1057 | unsigned long tmp; | ||
1058 | |||
1059 | if (phyp_dump_info->reserve_bootvar) | ||
1060 | return phyp_dump_info->reserve_bootvar; | ||
1061 | |||
1062 | /* divide by 20 to get 5% of value */ | ||
1063 | tmp = lmb_end_of_DRAM(); | ||
1064 | do_div(tmp, 20); | ||
1065 | |||
1066 | /* round it down in multiples of 256 */ | ||
1067 | tmp = tmp & ~0x0FFFFFFFUL; | ||
1068 | |||
1069 | return (tmp > PHYP_DUMP_RMR_END ? tmp : PHYP_DUMP_RMR_END); | ||
1070 | } | ||
1071 | |||
1072 | /** | ||
1073 | * phyp_dump_reserve_mem() - reserve all not-yet-dumped mmemory | ||
1074 | * | ||
1075 | * This routine may reserve memory regions in the kernel only | ||
1076 | * if the system is supported and a dump was taken in last | ||
1077 | * boot instance or if the hardware is supported and the | ||
1078 | * scratch area needs to be setup. In other instances it returns | ||
1079 | * without reserving anything. The memory in case of dump being | ||
1080 | * active is freed when the dump is collected (by userland tools). | ||
1081 | */ | ||
1082 | static void __init phyp_dump_reserve_mem(void) | ||
1083 | { | ||
1084 | unsigned long base, size; | ||
1085 | unsigned long variable_reserve_size; | ||
1086 | |||
1087 | if (!phyp_dump_info->phyp_dump_configured) { | ||
1088 | printk(KERN_ERR "Phyp-dump not supported on this hardware\n"); | ||
1089 | return; | ||
1090 | } | ||
1091 | |||
1092 | if (!phyp_dump_info->phyp_dump_at_boot) { | ||
1093 | printk(KERN_INFO "Phyp-dump disabled at boot time\n"); | ||
1094 | return; | ||
1095 | } | ||
1096 | |||
1097 | variable_reserve_size = phyp_dump_calculate_reserve_size(); | ||
1098 | |||
1099 | if (phyp_dump_info->phyp_dump_is_active) { | ||
1100 | /* Reserve *everything* above RMR.Area freed by userland tools*/ | ||
1101 | base = variable_reserve_size; | ||
1102 | size = lmb_end_of_DRAM() - base; | ||
1103 | |||
1104 | /* XXX crashed_ram_end is wrong, since it may be beyond | ||
1105 | * the memory_limit, it will need to be adjusted. */ | ||
1106 | lmb_reserve(base, size); | ||
1107 | |||
1108 | phyp_dump_info->init_reserve_start = base; | ||
1109 | phyp_dump_info->init_reserve_size = size; | ||
1110 | } else { | ||
1111 | size = phyp_dump_info->cpu_state_size + | ||
1112 | phyp_dump_info->hpte_region_size + | ||
1113 | variable_reserve_size; | ||
1114 | base = lmb_end_of_DRAM() - size; | ||
1115 | lmb_reserve(base, size); | ||
1116 | phyp_dump_info->init_reserve_start = base; | ||
1117 | phyp_dump_info->init_reserve_size = size; | ||
1118 | } | ||
1119 | } | ||
1120 | #else | ||
1121 | static inline void __init phyp_dump_reserve_mem(void) {} | ||
1122 | #endif /* CONFIG_PHYP_DUMP && CONFIG_PPC_RTAS */ | ||
1123 | |||
1124 | |||
1043 | void __init early_init_devtree(void *params) | 1125 | void __init early_init_devtree(void *params) |
1044 | { | 1126 | { |
1045 | DBG(" -> early_init_devtree(%p)\n", params); | 1127 | DBG(" -> early_init_devtree(%p)\n", params); |
@@ -1052,6 +1134,11 @@ void __init early_init_devtree(void *params) | |||
1052 | of_scan_flat_dt(early_init_dt_scan_rtas, NULL); | 1134 | of_scan_flat_dt(early_init_dt_scan_rtas, NULL); |
1053 | #endif | 1135 | #endif |
1054 | 1136 | ||
1137 | #ifdef CONFIG_PHYP_DUMP | ||
1138 | /* scan tree to see if dump occured during last boot */ | ||
1139 | of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); | ||
1140 | #endif | ||
1141 | |||
1055 | /* Retrieve various informations from the /chosen node of the | 1142 | /* Retrieve various informations from the /chosen node of the |
1056 | * device-tree, including the platform type, initrd location and | 1143 | * device-tree, including the platform type, initrd location and |
1057 | * size, TCE reserve, and more ... | 1144 | * size, TCE reserve, and more ... |
@@ -1072,6 +1159,7 @@ void __init early_init_devtree(void *params) | |||
1072 | reserve_kdump_trampoline(); | 1159 | reserve_kdump_trampoline(); |
1073 | reserve_crashkernel(); | 1160 | reserve_crashkernel(); |
1074 | early_reserve_mem(); | 1161 | early_reserve_mem(); |
1162 | phyp_dump_reserve_mem(); | ||
1075 | 1163 | ||
1076 | lmb_enforce_memory_limit(memory_limit); | 1164 | lmb_enforce_memory_limit(memory_limit); |
1077 | lmb_analyze(); | 1165 | lmb_analyze(); |
@@ -1244,12 +1332,14 @@ EXPORT_SYMBOL(of_node_put); | |||
1244 | */ | 1332 | */ |
1245 | void of_attach_node(struct device_node *np) | 1333 | void of_attach_node(struct device_node *np) |
1246 | { | 1334 | { |
1247 | write_lock(&devtree_lock); | 1335 | unsigned long flags; |
1336 | |||
1337 | write_lock_irqsave(&devtree_lock, flags); | ||
1248 | np->sibling = np->parent->child; | 1338 | np->sibling = np->parent->child; |
1249 | np->allnext = allnodes; | 1339 | np->allnext = allnodes; |
1250 | np->parent->child = np; | 1340 | np->parent->child = np; |
1251 | allnodes = np; | 1341 | allnodes = np; |
1252 | write_unlock(&devtree_lock); | 1342 | write_unlock_irqrestore(&devtree_lock, flags); |
1253 | } | 1343 | } |
1254 | 1344 | ||
1255 | /* | 1345 | /* |
@@ -1260,8 +1350,9 @@ void of_attach_node(struct device_node *np) | |||
1260 | void of_detach_node(struct device_node *np) | 1350 | void of_detach_node(struct device_node *np) |
1261 | { | 1351 | { |
1262 | struct device_node *parent; | 1352 | struct device_node *parent; |
1353 | unsigned long flags; | ||
1263 | 1354 | ||
1264 | write_lock(&devtree_lock); | 1355 | write_lock_irqsave(&devtree_lock, flags); |
1265 | 1356 | ||
1266 | parent = np->parent; | 1357 | parent = np->parent; |
1267 | if (!parent) | 1358 | if (!parent) |
@@ -1292,7 +1383,7 @@ void of_detach_node(struct device_node *np) | |||
1292 | of_node_set_flag(np, OF_DETACHED); | 1383 | of_node_set_flag(np, OF_DETACHED); |
1293 | 1384 | ||
1294 | out_unlock: | 1385 | out_unlock: |
1295 | write_unlock(&devtree_lock); | 1386 | write_unlock_irqrestore(&devtree_lock, flags); |
1296 | } | 1387 | } |
1297 | 1388 | ||
1298 | #ifdef CONFIG_PPC_PSERIES | 1389 | #ifdef CONFIG_PPC_PSERIES |
@@ -1373,20 +1464,21 @@ __initcall(prom_reconfig_setup); | |||
1373 | int prom_add_property(struct device_node* np, struct property* prop) | 1464 | int prom_add_property(struct device_node* np, struct property* prop) |
1374 | { | 1465 | { |
1375 | struct property **next; | 1466 | struct property **next; |
1467 | unsigned long flags; | ||
1376 | 1468 | ||
1377 | prop->next = NULL; | 1469 | prop->next = NULL; |
1378 | write_lock(&devtree_lock); | 1470 | write_lock_irqsave(&devtree_lock, flags); |
1379 | next = &np->properties; | 1471 | next = &np->properties; |
1380 | while (*next) { | 1472 | while (*next) { |
1381 | if (strcmp(prop->name, (*next)->name) == 0) { | 1473 | if (strcmp(prop->name, (*next)->name) == 0) { |
1382 | /* duplicate ! don't insert it */ | 1474 | /* duplicate ! don't insert it */ |
1383 | write_unlock(&devtree_lock); | 1475 | write_unlock_irqrestore(&devtree_lock, flags); |
1384 | return -1; | 1476 | return -1; |
1385 | } | 1477 | } |
1386 | next = &(*next)->next; | 1478 | next = &(*next)->next; |
1387 | } | 1479 | } |
1388 | *next = prop; | 1480 | *next = prop; |
1389 | write_unlock(&devtree_lock); | 1481 | write_unlock_irqrestore(&devtree_lock, flags); |
1390 | 1482 | ||
1391 | #ifdef CONFIG_PROC_DEVICETREE | 1483 | #ifdef CONFIG_PROC_DEVICETREE |
1392 | /* try to add to proc as well if it was initialized */ | 1484 | /* try to add to proc as well if it was initialized */ |
@@ -1406,9 +1498,10 @@ int prom_add_property(struct device_node* np, struct property* prop) | |||
1406 | int prom_remove_property(struct device_node *np, struct property *prop) | 1498 | int prom_remove_property(struct device_node *np, struct property *prop) |
1407 | { | 1499 | { |
1408 | struct property **next; | 1500 | struct property **next; |
1501 | unsigned long flags; | ||
1409 | int found = 0; | 1502 | int found = 0; |
1410 | 1503 | ||
1411 | write_lock(&devtree_lock); | 1504 | write_lock_irqsave(&devtree_lock, flags); |
1412 | next = &np->properties; | 1505 | next = &np->properties; |
1413 | while (*next) { | 1506 | while (*next) { |
1414 | if (*next == prop) { | 1507 | if (*next == prop) { |
@@ -1421,7 +1514,7 @@ int prom_remove_property(struct device_node *np, struct property *prop) | |||
1421 | } | 1514 | } |
1422 | next = &(*next)->next; | 1515 | next = &(*next)->next; |
1423 | } | 1516 | } |
1424 | write_unlock(&devtree_lock); | 1517 | write_unlock_irqrestore(&devtree_lock, flags); |
1425 | 1518 | ||
1426 | if (!found) | 1519 | if (!found) |
1427 | return -ENODEV; | 1520 | return -ENODEV; |
@@ -1447,9 +1540,10 @@ int prom_update_property(struct device_node *np, | |||
1447 | struct property *oldprop) | 1540 | struct property *oldprop) |
1448 | { | 1541 | { |
1449 | struct property **next; | 1542 | struct property **next; |
1543 | unsigned long flags; | ||
1450 | int found = 0; | 1544 | int found = 0; |
1451 | 1545 | ||
1452 | write_lock(&devtree_lock); | 1546 | write_lock_irqsave(&devtree_lock, flags); |
1453 | next = &np->properties; | 1547 | next = &np->properties; |
1454 | while (*next) { | 1548 | while (*next) { |
1455 | if (*next == oldprop) { | 1549 | if (*next == oldprop) { |
@@ -1463,7 +1557,7 @@ int prom_update_property(struct device_node *np, | |||
1463 | } | 1557 | } |
1464 | next = &(*next)->next; | 1558 | next = &(*next)->next; |
1465 | } | 1559 | } |
1466 | write_unlock(&devtree_lock); | 1560 | write_unlock_irqrestore(&devtree_lock, flags); |
1467 | 1561 | ||
1468 | if (!found) | 1562 | if (!found) |
1469 | return -ENODEV; | 1563 | return -ENODEV; |