diff options
Diffstat (limited to 'arch/powerpc/platforms/ps3')
-rw-r--r-- | arch/powerpc/platforms/ps3/Kconfig | 22 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/mm.c | 77 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/platform.h | 16 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/repository.c | 198 |
4 files changed, 270 insertions, 43 deletions
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 476d9d9b2405..46b7f0232523 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -7,7 +7,6 @@ config PPC_PS3 select USB_OHCI_BIG_ENDIAN_MMIO select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO - select MEMORY_HOTPLUG select PPC_PCI_CHOICE help This option enables support for the Sony PS3 game console @@ -74,7 +73,7 @@ config PS3_PS3AV help Include support for the PS3 AV Settings driver. - This support is required for graphics and sound. In + This support is required for PS3 graphics and sound. In general, all users will say Y or M. config PS3_SYS_MANAGER @@ -85,9 +84,22 @@ config PS3_SYS_MANAGER help Include support for the PS3 System Manager. - This support is required for system control. In + This support is required for PS3 system control. In general, all users will say Y or M. +config PS3_REPOSITORY_WRITE + bool "PS3 Repository write support" if PS3_ADVANCED + depends on PPC_PS3 + default n + help + Enables support for writing to the PS3 System Repository. + + This support is intended for bootloaders that need to store data + in the repository for later boot stages. + + If in doubt, say N here and reduce the size of the kernel by a + small amount. + config PS3_STORAGE depends on PPC_PS3 tristate @@ -122,7 +134,7 @@ config PS3_FLASH This support is required to access the PS3 FLASH ROM, which contains the boot loader and some boot options. - In general, all users will say Y or M. + In general, PS3 OtherOS users will say Y or M. As this driver needs a fixed buffer of 256 KiB of memory, it can be disabled on the kernel command line using "ps3flash=off", to @@ -156,7 +168,7 @@ config PS3GELIC_UDBG via the Ethernet port (UDP port number 18194). This driver uses a trivial implementation and is independent - from the main network driver. + from the main PS3 gelic network driver. If in doubt, say N here. diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index de2aea421707..0c9f643d9e2a 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/export.h> -#include <linux/memory_hotplug.h> #include <linux/memblock.h> #include <linux/slab.h> @@ -79,12 +78,14 @@ enum { * @base: base address * @size: size in bytes * @offset: difference between base and rm.size + * @destroy: flag if region should be destroyed upon shutdown */ struct mem_region { u64 base; u64 size; unsigned long offset; + int destroy; }; /** @@ -96,7 +97,7 @@ struct mem_region { * The HV virtual address space (vas) allows for hotplug memory regions. * Memory regions can be created and destroyed in the vas at runtime. * @rm: real mode (bootmem) region - * @r1: hotplug memory region(s) + * @r1: highmem region(s) * * ps3 addresses * virt_addr: a cpu 'translated' effective address @@ -222,10 +223,6 @@ void ps3_mm_vas_destroy(void) } } -/*============================================================================*/ -/* memory hotplug routines */ -/*============================================================================*/ - /** * ps3_mm_region_create - create a memory region in the vas * @r: pointer to a struct mem_region to accept initialized values @@ -262,6 +259,7 @@ static int ps3_mm_region_create(struct mem_region *r, unsigned long size) goto zero_region; } + r->destroy = 1; r->offset = r->base - map.rm.size; return result; @@ -279,7 +277,14 @@ static void ps3_mm_region_destroy(struct mem_region *r) { int result; + if (!r->destroy) { + pr_info("%s:%d: Not destroying high region: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); + return; + } + DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base); + if (r->base) { result = lv1_release_memory(r->base); BUG_ON(result); @@ -288,50 +293,36 @@ static void ps3_mm_region_destroy(struct mem_region *r) } } -/** - * ps3_mm_add_memory - hot add memory - */ - -static int __init ps3_mm_add_memory(void) +static int ps3_mm_get_repository_highmem(struct mem_region *r) { int result; - unsigned long start_addr; - unsigned long start_pfn; - unsigned long nr_pages; - - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ENODEV; - BUG_ON(!mem_init_done); + /* Assume a single highmem region. */ - start_addr = map.rm.size; - start_pfn = start_addr >> PAGE_SHIFT; - nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; + result = ps3_repository_read_highmem_info(0, &r->base, &r->size); - DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n", - __func__, __LINE__, start_addr, start_pfn, nr_pages); - - result = add_memory(0, start_addr, map.r1.size); + if (result) + goto zero_region; - if (result) { - pr_err("%s:%d: add_memory failed: (%d)\n", - __func__, __LINE__, result); - return result; + if (!r->base || !r->size) { + result = -1; + goto zero_region; } - memblock_add(start_addr, map.r1.size); + r->offset = r->base - map.rm.size; - result = online_pages(start_pfn, nr_pages); + DBG("%s:%d: Found high region in repository: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); - if (result) - pr_err("%s:%d: online_pages failed: (%d)\n", - __func__, __LINE__, result); + return 0; +zero_region: + DBG("%s:%d: No high region in repository.\n", __func__, __LINE__); + + r->size = r->base = r->offset = 0; return result; } -device_initcall(ps3_mm_add_memory); - /*============================================================================*/ /* dma routines */ /*============================================================================*/ @@ -1217,13 +1208,23 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); + /* Check if we got the highmem region from an earlier boot step */ - /* arrange to do this in ps3_mm_add_memory */ - ps3_mm_region_create(&map.r1, map.total - map.rm.size); + if (ps3_mm_get_repository_highmem(&map.r1)) + ps3_mm_region_create(&map.r1, map.total - map.rm.size); /* correct map.total for the real total amount of memory we use */ map.total = map.rm.size + map.r1.size; + if (!map.r1.size) { + DBG("%s:%d: No highmem region found\n", __func__, __LINE__); + } else { + DBG("%s:%d: Adding highmem region: %llxh %llxh\n", + __func__, __LINE__, map.rm.size, + map.total - map.rm.size); + memblock_add(map.rm.size, map.total - map.rm.size); + } + DBG(" <- %s:%d\n", __func__, __LINE__); } diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 1a633ed0fe98..d71329a8e325 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -188,6 +188,22 @@ int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size); int ps3_repository_read_region_total(u64 *region_total); int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total); +int ps3_repository_read_highmem_region_count(unsigned int *region_count); +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base); +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size); +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size); + +int ps3_repository_write_highmem_region_count(unsigned int region_count); +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base); +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size); +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size); +int ps3_repository_delete_highmem_info(unsigned int region_index); /* repository pme info */ diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 7bdfea336f5e..9b47ba7a5de7 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -779,6 +779,72 @@ int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total) } /** + * ps3_repository_read_highmem_region_count - Read the number of highmem regions + * + * Bootloaders must arrange the repository nodes such that regions are indexed + * with a region_index from 0 to region_count-1. + */ + +int ps3_repository_read_highmem_region_count(unsigned int *region_count) +{ + int result; + u64 v1 = 0; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + &v1, NULL); + *region_count = v1; + return result; +} + + +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, NULL); +} + +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, NULL); +} + +/** + * ps3_repository_read_highmem_info - Read high memory region info + * @region_index: Region index, {0,..,region_count-1}. + * @highmem_base: High memory base address. + * @highmem_size: High memory size. + * + * Bootloaders that preallocate highmem regions must place the + * region info into the repository at these well known nodes. + */ + +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size) +{ + int result; + + *highmem_base = 0; + result = ps3_repository_read_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_read_highmem_size(region_index, highmem_size); +} + +/** * ps3_repository_read_num_spu_reserved - Number of physical spus reserved. * @num_spu: Number of physical spus. */ @@ -1002,6 +1068,138 @@ int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, lpar, rights); } +#if defined(CONFIG_PS3_REPOSITORY_WRITE) + +static int create_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + dump_node(0, n1, n2, n3, n4, v1, v2); + + result = lv1_create_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_create_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int delete_node(u64 n1, u64 n2, u64 n3, u64 n4) +{ + int result; + + dump_node(0, n1, n2, n3, n4, 0, 0); + + result = lv1_delete_repository_node(n1, n2, n3, n4); + + if (result) { + pr_devel("%s:%d: lv1_delete_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int write_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + result = create_node(n1, n2, n3, n4, v1, v2); + + if (!result) + return 0; + + result = lv1_write_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_write_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +int ps3_repository_write_highmem_region_count(unsigned int region_count) +{ + int result; + u64 v1 = (u64)region_count; + + result = write_node( + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + v1, 0); + return result; +} + +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, 0); +} + +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, 0); +} + +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size) +{ + int result; + + result = ps3_repository_write_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_write_highmem_size(region_index, highmem_size); +} + +static int ps3_repository_delete_highmem_base(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0); +} + +static int ps3_repository_delete_highmem_size(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0); +} + +int ps3_repository_delete_highmem_info(unsigned int region_index) +{ + int result; + + result = ps3_repository_delete_highmem_base(region_index); + result += ps3_repository_delete_highmem_size(region_index); + + return result ? -1 : 0; +} + +#endif /* defined(CONFIG_PS3_WRITE_REPOSITORY) */ + #if defined(DEBUG) int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) |