[POWERPC] PS3: Save os-area params to device tree
authorGeoff Levand <geoffrey.levand@am.sony.com>
Sat, 6 Oct 2007 21:35:47 +0000 (07:35 +1000)
committerPaul Mackerras <paulus@samba.org>
Tue, 9 Oct 2007 11:01:59 +0000 (21:01 +1000)
Add the PS3 os-area startup params to the device tree.  This allows
a second stage kernel loaded with kexec to use these values.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/platforms/ps3/os-area.c
arch/powerpc/platforms/ps3/platform.h
arch/powerpc/platforms/ps3/setup.c

index 473aee8580ce35d15961d589a12f9bba9052dd83..e50a276fcf6712c8eeb5a652716732de058a3dfb 100644 (file)
@@ -119,10 +119,65 @@ struct os_area_params {
  */
 
 struct saved_params {
+       unsigned int valid;
        s64 rtc_diff;
        unsigned int av_multi_out;
 } static saved_params;
 
+static struct property property_rtc_diff = {
+       .name = "linux,rtc_diff",
+       .length = sizeof(saved_params.rtc_diff),
+       .value = &saved_params.rtc_diff,
+};
+
+static struct property property_av_multi_out = {
+       .name = "linux,av_multi_out",
+       .length = sizeof(saved_params.av_multi_out),
+       .value = &saved_params.av_multi_out,
+};
+
+/**
+ * os_area_set_property - Add or overwrite a saved_params value to the device tree.
+ *
+ * Overwrites an existing property.
+ */
+
+static void os_area_set_property(struct device_node *node,
+       struct property *prop)
+{
+       int result;
+       struct property *tmp = of_find_property(node, prop->name, NULL);
+
+       if (tmp) {
+               pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name);
+               prom_remove_property(node, tmp);
+       }
+
+       result = prom_add_property(node, prop);
+
+       if (result)
+               pr_debug("%s:%d prom_set_property failed\n", __func__,
+                       __LINE__);
+}
+
+/**
+ * os_area_get_property - Get a saved_params value from the device tree.
+ *
+ */
+
+static void __init os_area_get_property(struct device_node *node,
+       struct property *prop)
+{
+       const struct property *tmp = of_find_property(node, prop->name, NULL);
+
+       if (tmp) {
+               BUG_ON(prop->length != tmp->length);
+               memcpy(prop->value, tmp->value, prop->length);
+       } else
+               pr_debug("%s:%d not found %s\n", __func__, __LINE__,
+                       prop->name);
+}
+
 #define dump_header(_a) _dump_header(_a, __func__, __LINE__)
 static void _dump_header(const struct os_area_header *h, const char *func,
        int line)
@@ -196,8 +251,19 @@ static int __init verify_header(const struct os_area_header *header)
 
 static void os_area_queue_work_handler(struct work_struct *work)
 {
+       struct device_node *node;
+
        pr_debug(" -> %s:%d\n", __func__, __LINE__);
 
+       node = of_find_node_by_path("/");
+
+       if (node) {
+               os_area_set_property(node, &property_rtc_diff);
+               of_node_put(node);
+       } else
+               pr_debug("%s:%d of_find_node_by_path failed\n",
+                       __func__, __LINE__);
+
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
 }
 
@@ -244,6 +310,8 @@ void __init ps3_os_area_save_params(void)
        result = verify_header(header);
 
        if (result) {
+               /* Second stage kernels exit here. */
+
                pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
                dump_header(header);
                return;
@@ -254,20 +322,52 @@ void __init ps3_os_area_save_params(void)
 
        saved_params.rtc_diff = params->rtc_diff;
        saved_params.av_multi_out = params->av_multi_out;
+       saved_params.valid = 1;
 
        memset(header, 0, sizeof(*header));
 
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
 }
 
+/**
+ * ps3_os_area_init - Setup os area device tree properties as needed.
+ */
+
+void __init ps3_os_area_init(void)
+{
+       struct device_node *node;
+
+       pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+       node = of_find_node_by_path("/");
+
+       if (!saved_params.valid && node) {
+               /* Second stage kernels should have a dt entry. */
+               os_area_get_property(node, &property_rtc_diff);
+               os_area_get_property(node, &property_av_multi_out);
+       }
+
+       if(!saved_params.rtc_diff)
+               saved_params.rtc_diff = SECONDS_FROM_1970_TO_2000;
+
+       if (node) {
+               os_area_set_property(node, &property_rtc_diff);
+               os_area_set_property(node, &property_av_multi_out);
+               of_node_put(node);
+       } else
+               pr_debug("%s:%d of_find_node_by_path failed\n",
+                       __func__, __LINE__);
+
+       pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+
 /**
  * ps3_os_area_get_rtc_diff - Returns the rtc diff value.
  */
 
 u64 ps3_os_area_get_rtc_diff(void)
 {
-       return saved_params.rtc_diff ? saved_params.rtc_diff
-               : SECONDS_FROM_1970_TO_2000;
+       return saved_params.rtc_diff;
 }
 
 /**
index 6b4f4dd2d15af5c5aabe9c32c85d74b2603dfe64..01f0c9506e11efec1ea93777abf385b24a8a61c0 100644 (file)
@@ -63,6 +63,7 @@ int ps3_set_rtc_time(struct rtc_time *time);
 /* os area */
 
 void __init ps3_os_area_save_params(void);
+void __init ps3_os_area_init(void);
 u64 ps3_os_area_get_rtc_diff(void);
 void ps3_os_area_set_rtc_diff(u64 rtc_diff);
 
index f0d5ec51ef1763f18dfe2a20e85cdaaba975c5a1..5c2cbb08eb52f82a212503137ebae0cf5d8dca55 100644 (file)
@@ -206,6 +206,7 @@ static void __init ps3_setup_arch(void)
        prealloc_ps3flash_bounce_buffer();
 
        ppc_md.power_save = ps3_power_save;
+       ps3_os_area_init();
 
        DBG(" <- %s:%d\n", __func__, __LINE__);
 }