diff mbox series

[v3,3/4] Advertise the self-save and self-restore attributes in the device tree

Message ID 7eff63284f877abacf0251dcbc355e929bd03e18.1581048597.git.psampat@linux.ibm.com
State Changes Requested
Headers show
Series Support for Self Save API in OPAL | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (179d53dfcca30436777b0c748d530a979bbc8a45)
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present

Commit Message

Pratik R. Sampat Feb. 7, 2020, 4:20 a.m. UTC
Support for self save and self restore interface is advertised in the
device tree, along with the list of SPRs it supports for each.

The Special Purpose Register identification is encoded in a 2048 bitmask
structure, where each bit signifies the identification key of that SPR
which is consistent with that of the POWER architecture set for that
register.

Signed-off-by: Pratik Rajesh Sampat <psampat@linux.ibm.com>
---
 hw/slw.c                        | 83 +++++++++++++++++++++++++++++++++
 include/skiboot.h               |  1 +
 libpore/p8_pore_table_gen_api.H |  1 +
 3 files changed, 85 insertions(+)

Comments

Oliver O'Halloran Feb. 10, 2020, 2:13 a.m. UTC | #1
On Fri, Feb 7, 2020 at 3:21 PM Pratik Rajesh Sampat
<psampat@linux.ibm.com> wrote:
>
> Support for self save and self restore interface is advertised in the
> device tree, along with the list of SPRs it supports for each.
>
> The Special Purpose Register identification is encoded in a 2048 bitmask
> structure, where each bit signifies the identification key of that SPR
> which is consistent with that of the POWER architecture set for that
> register.

Please answer the questions I asked the last time I reviewed this:
https://patchwork.ozlabs.org/patch/1174835/#2278701

Since then I've also forgotten what the difference between
self-restore and the existing API is. So can you add something to
doc/power-management.rst to describe the relationship between the
current API, self-restore, and self-save? The new device-tree bindings
need to be documented in doc/device-tree/ibm,opal/power-mgt/ too. The
cover letter of the series covers some of that, but actual
documentation is sorely needed.

> Signed-off-by: Pratik Rajesh Sampat <psampat@linux.ibm.com>
> ---
>  hw/slw.c                        | 83 +++++++++++++++++++++++++++++++++
>  include/skiboot.h               |  1 +
>  libpore/p8_pore_table_gen_api.H |  1 +
>  3 files changed, 85 insertions(+)
>
> diff --git a/hw/slw.c b/hw/slw.c
> index 5efa2168..087a8619 100644
> --- a/hw/slw.c
> +++ b/hw/slw.c
> @@ -23,12 +23,15 @@
>  #include <nvram.h>
>  #include <sbe-p8.h>
>  #include <xive.h>
> +#include <bitmap.h>
>
>  #include <p9_stop_api.H>
>  #include <p8_pore_table_gen_api.H>
>  #include <sbe_xip_image.h>
>
>  static uint32_t slw_saved_reset[0x100];

> +#define MAX_RESET_PATCH_SIZE   64
unused?

> +#define SPR_BITMAP_LENGTH      2048
>
>  static bool slw_current_le = false;
>
> @@ -750,6 +753,84 @@ static void slw_late_init_p9(struct proc_chip *chip)
>         }
>  }
>
> +/* Add device tree properties to determine self-save | restore */
> +void add_cpu_self_save_properties()
should be: void add_cpu_self_save_properties(void)

> +{
> +       int i;
> +       struct dt_node *self_restore, *self_save, *power_mgt;
> +       bitmap_t *self_restore_map, *self_save_map;
declarations should be sorted reverse christmas tree order. i.e.
longest at the top, shortest at the bottom.

> +
> +       const uint64_t self_restore_regs[] = {
> +               P8_SPR_HRMOR,
> +               P8_SPR_HMEER,
> +               P8_SPR_PMICR,
> +               P8_SPR_PMCR,
> +               P8_SPR_HID0,
> +               P8_SPR_HID1,
> +               P8_SPR_HID4,
> +               P8_SPR_HID5,
> +               P8_SPR_HSPRG0,
> +               P8_SPR_LPCR,
> +               P8_SPR_PSSCR,
> +               P8_MSR_MSR
> +       };
> +
> +       const uint64_t self_save_regs[] = {
> +               P9_STOP_SPR_DAWR,
> +               P9_STOP_SPR_HSPRG0,
> +               P9_STOP_SPR_LDBAR,
> +               P9_STOP_SPR_LPCR,
> +               P9_STOP_SPR_PSSCR,
> +               P9_STOP_SPR_MSR,
> +               P9_STOP_SPR_HRMOR,
> +               P9_STOP_SPR_HMEER,
> +               P9_STOP_SPR_PMCR,
> +               P9_STOP_SPR_PTCR
> +       };

Again, where did these sets of registers come from?

> +       self_save_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
> +       self_restore_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));

In general I prefer people avoid doing allocations until they're
actually needed. If any of the checks below fail then we've done a
bunch of work for no reason so... do the checks first?

> +       for (i = 0; i < ARRAY_SIZE(self_save_regs); i++)
> +               bitmap_set_bit(*self_save_map, self_save_regs[i]);
> +
> +       for (i = 0; i < ARRAY_SIZE(self_restore_regs); i++)
> +               bitmap_set_bit(*self_restore_map, self_restore_regs[i]);
> +
> +       power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt");
> +       if (!power_mgt) {
> +               prerror("OCC: dt node /ibm,opal/power-mgt not found\n");
> +               goto bail;
> +       }
> +
> +       self_restore = dt_new(power_mgt, "self-restore");
> +       if (!self_restore) {
> +               prerror("OCC: Failed to create self restore node");
> +               goto bail;
> +       }
> +       dt_add_property_string(self_restore, "status", "enabled");
> +
> +       dt_add_property(self_restore, "sprn-bitmask", *self_restore_map,
> +                       SPR_BITMAP_LENGTH / 8);
> +
> +       self_save = dt_new(power_mgt, "self-save");
> +       if (!self_save) {
> +               prerror("OCC: Failed to create self save node");
> +               goto bail;
> +       }
> +       if (proc_gen == proc_gen_p9) {
> +               dt_add_property_string(self_save, "status", "enabled");
> +
> +               dt_add_property(self_save, "sprn-bitmask", *self_save_map,
> +                               SPR_BITMAP_LENGTH / 8);

If skiboot is compiled as little endian this is going to break. It
works today because we always build skiboot BE, but that may not
always be true.

> +       } else {
> +               dt_add_property_string(self_save, "status", "disabled");

I still don't understand why we're adding anything to the DT if the
feature isn't supported.

> +       }
> +bail:
> +       free(self_save_map);
> +       free(self_restore_map);
> +}
> +
>  /* Add device tree properties to describe idle states */
>  void add_cpu_idle_state_properties(void)
>  {
> @@ -1563,4 +1644,6 @@ void slw_init(void)
>                 }
>         }
>         add_cpu_idle_state_properties();
> +       if (has_deep_states)
> +               add_cpu_self_save_properties();
>  }
> diff --git a/include/skiboot.h b/include/skiboot.h
> index 072ce589..676211ad 100644
> --- a/include/skiboot.h
> +++ b/include/skiboot.h
> @@ -209,6 +209,7 @@ extern void early_uart_init(void);
>  extern void homer_init(void);
>  extern void slw_init(void);
>  extern void add_cpu_idle_state_properties(void);
> +extern void add_cpu_self_save_properties(void);
>  extern void lpc_rtc_init(void);
>
>  /* flash support */
> diff --git a/libpore/p8_pore_table_gen_api.H b/libpore/p8_pore_table_gen_api.H
> index 63081ca5..d72fee90 100644
> --- a/libpore/p8_pore_table_gen_api.H
> +++ b/libpore/p8_pore_table_gen_api.H
> @@ -233,6 +233,7 @@ enum  {
>    P8_SPR_HRMOR  =  313,
>    P8_SPR_HMEER  =  337,
>    P8_SPR_PMICR  =  852,
> +  P8_SPR_PSSCR  =  855,
>    P8_SPR_PMCR   =  884,
>    P8_SPR_HID0   = 1008,
>    P8_SPR_HID1   = 1009,
> --
> 2.24.1
>
> _______________________________________________
> Skiboot mailing list
> Skiboot@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot
Pratik R. Sampat Feb. 10, 2020, 5:41 a.m. UTC | #2
Hello Oliver, Thanks for the review.


On 10/02/20 7:43 am, Oliver O'Halloran wrote:
> On Fri, Feb 7, 2020 at 3:21 PM Pratik Rajesh Sampat
> <psampat@linux.ibm.com> wrote:
>> Support for self save and self restore interface is advertised in the
>> device tree, along with the list of SPRs it supports for each.
>>
>> The Special Purpose Register identification is encoded in a 2048 bitmask
>> structure, where each bit signifies the identification key of that SPR
>> which is consistent with that of the POWER architecture set for that
>> register.
> Please answer the questions I asked the last time I reviewed this:
> https://patchwork.ozlabs.org/patch/1174835/#2278701

Sure, I'll address them as they come further in the patch.

> Since then I've also forgotten what the difference between
> self-restore and the existing API is. So can you add something to
> doc/power-management.rst to describe the relationship between the
> current API, self-restore, and self-save? The new device-tree bindings
> need to be documented in doc/device-tree/ibm,opal/power-mgt/ too. The
> cover letter of the series covers some of that, but actual
> documentation is sorely needed.

Right, I'll add more description in the documentation and make sure it's
complete

>> +#define MAX_RESET_PATCH_SIZE   64
> unused?

Yeah, I got rid of it. Sorry about that

>
>> +
>> +       const uint64_t self_restore_regs[] = {
>> +               P8_SPR_HRMOR,
>> +               P8_SPR_HMEER,
>> +               P8_SPR_PMICR,
>> +               P8_SPR_PMCR,
>> +               P8_SPR_HID0,
>> +               P8_SPR_HID1,
>> +               P8_SPR_HID4,
>> +               P8_SPR_HID5,
>> +               P8_SPR_HSPRG0,
>> +               P8_SPR_LPCR,
>> +               P8_SPR_PSSCR,
>> +               P8_MSR_MSR
>> +       };
>> +
>> +       const uint64_t self_save_regs[] = {
>> +               P9_STOP_SPR_DAWR,
>> +               P9_STOP_SPR_HSPRG0,
>> +               P9_STOP_SPR_LDBAR,
>> +               P9_STOP_SPR_LPCR,
>> +               P9_STOP_SPR_PSSCR,
>> +               P9_STOP_SPR_MSR,
>> +               P9_STOP_SPR_HRMOR,
>> +               P9_STOP_SPR_HMEER,
>> +               P9_STOP_SPR_PMCR,
>> +               P9_STOP_SPR_PTCR
>> +       };
> Again, where did these sets of registers come from?
>
These are the set of registers that are supported by the pore engine. Although
our kernel only needs a subset of them, the whole list is sent out to indicate
what is supported.

>> +       self_save_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
>> +       self_restore_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
> In general I prefer people avoid doing allocations until they're
> actually needed. If any of the checks below fail then we've done a
> bunch of work for no reason so...	 do the checks first?

Yeah makes sense, I can move the allocations down later when I really need them.
It will also get rid of all the goto bail and I can just use a return instead.

>
>> +       dt_add_property_string(self_restore, "status", "enabled");
>> +
>> +       dt_add_property(self_restore, "sprn-bitmask", *self_restore_map,
>> +                       SPR_BITMAP_LENGTH / 8);
>> +
>> +       self_save = dt_new(power_mgt, "self-save");
>> +       if (!self_save) {
>> +               prerror("OCC: Failed to create self save node");
>> +               goto bail;
>> +       }
>> +       if (proc_gen == proc_gen_p9) {
>> +               dt_add_property_string(self_save, "status", "enabled");
>> +
>> +               dt_add_property(self_save, "sprn-bitmask", *self_save_map,
>> +                               SPR_BITMAP_LENGTH / 8);
> If skiboot is compiled as little endian this is going to break. It
> works today because we always build skiboot BE, but that may not
> always be true.
>
Okay, I think I understand what you mean. I should run my bitmask through a
loop 32 bits at a time with a be32_to_cpu while populating it?

>> +       } else {
>> +               dt_add_property_string(self_save, "status", "disabled");
> I still don't understand why we're adding anything to the DT if the
> feature isn't supported.

Today in the kernel, if the node doesn't exist we assume that we are running an
older firmware on a newer kernel and make the self-restore call as it was
always supported in legacy.

Now let's take an example of a case where self-restore is actually unsupported,
in that case if the node does not exist the kernel will assume it does and make
the call anyways to see it fail later.

Not that it is a bad thing for it to fail later and then cut support for stop
states, however, I'm trying to avoid this try-catch scenario. If you feel that
I should let it fail and handle it later, I'll do that instead.

If you consider opposite scenario instead, that we allow self-restore only if
the node is present, then using legacy skiboot deep stop states will be
disabled which is again an undesired behavior.

--

Pratik
Oliver O'Halloran Feb. 10, 2020, 7:50 a.m. UTC | #3
On Mon, Feb 10, 2020 at 4:41 PM Pratik Sampat <psampat@linux.ibm.com> wrote:
>
> Hello Oliver, Thanks for the review.
>
>
> On 10/02/20 7:43 am, Oliver O'Halloran wrote:
> > On Fri, Feb 7, 2020 at 3:21 PM Pratik Rajesh Sampat
> > <psampat@linux.ibm.com> wrote:
> >> Support for self save and self restore interface is advertised in the
> >> device tree, along with the list of SPRs it supports for each.
> >>
> >> The Special Purpose Register identification is encoded in a 2048 bitmask
> >> structure, where each bit signifies the identification key of that SPR
> >> which is consistent with that of the POWER architecture set for that
> >> register.
> > Please answer the questions I asked the last time I reviewed this:
> > https://patchwork.ozlabs.org/patch/1174835/#2278701
>
> Sure, I'll address them as they come further in the patch.
>
> > Since then I've also forgotten what the difference between
> > self-restore and the existing API is. So can you add something to
> > doc/power-management.rst to describe the relationship between the
> > current API, self-restore, and self-save? The new device-tree bindings
> > need to be documented in doc/device-tree/ibm,opal/power-mgt/ too. The
> > cover letter of the series covers some of that, but actual
> > documentation is sorely needed.
>
> Right, I'll add more description in the documentation and make sure it's
> complete
>
> >> +#define MAX_RESET_PATCH_SIZE   64
> > unused?
>
> Yeah, I got rid of it. Sorry about that
>
> >
> >> +
> >> +       const uint64_t self_restore_regs[] = {
> >> +               P8_SPR_HRMOR,
> >> +               P8_SPR_HMEER,
> >> +               P8_SPR_PMICR,
> >> +               P8_SPR_PMCR,
> >> +               P8_SPR_HID0,
> >> +               P8_SPR_HID1,
> >> +               P8_SPR_HID4,
> >> +               P8_SPR_HID5,
> >> +               P8_SPR_HSPRG0,
> >> +               P8_SPR_LPCR,
> >> +               P8_SPR_PSSCR,
> >> +               P8_MSR_MSR
> >> +       };
> >> +
> >> +       const uint64_t self_save_regs[] = {
> >> +               P9_STOP_SPR_DAWR,
> >> +               P9_STOP_SPR_HSPRG0,
> >> +               P9_STOP_SPR_LDBAR,
> >> +               P9_STOP_SPR_LPCR,
> >> +               P9_STOP_SPR_PSSCR,
> >> +               P9_STOP_SPR_MSR,
> >> +               P9_STOP_SPR_HRMOR,
> >> +               P9_STOP_SPR_HMEER,
> >> +               P9_STOP_SPR_PMCR,
> >> +               P9_STOP_SPR_PTCR
> >> +       };
> > Again, where did these sets of registers come from?
> >
> These are the set of registers that are supported by the pore engine. Although
> our kernel only needs a subset of them, the whole list is sent out to indicate
> what is supported.

That doesn't really help. I'll assume that by "pore" you mean what's
in libpore/p9_*.[CH] which writes a pile of stuff into the HOMER, but
what consumes that? What I want to know is whether updates to some
other bit of firmware can change the list of supported registers or
not.

> >> +       self_save_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
> >> +       self_restore_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
> > In general I prefer people avoid doing allocations until they're
> > actually needed. If any of the checks below fail then we've done a
> > bunch of work for no reason so...      do the checks first?
>
> Yeah makes sense, I can move the allocations down later when I really need them.
> It will also get rid of all the goto bail and I can just use a return instead.
>
> >
> >> +       dt_add_property_string(self_restore, "status", "enabled");
> >> +
> >> +       dt_add_property(self_restore, "sprn-bitmask", *self_restore_map,
> >> +                       SPR_BITMAP_LENGTH / 8);
> >> +
> >> +       self_save = dt_new(power_mgt, "self-save");
> >> +       if (!self_save) {
> >> +               prerror("OCC: Failed to create self save node");
> >> +               goto bail;
> >> +       }
> >> +       if (proc_gen == proc_gen_p9) {
> >> +               dt_add_property_string(self_save, "status", "enabled");
> >> +
> >> +               dt_add_property(self_save, "sprn-bitmask", *self_save_map,
> >> +                               SPR_BITMAP_LENGTH / 8);
> > If skiboot is compiled as little endian this is going to break. It
> > works today because we always build skiboot BE, but that may not
> > always be true.
> >
> Okay, I think I understand what you mean. I should run my bitmask through a
> loop 32 bits at a time with a be32_to_cpu while populating it?

Pretty much. You can check if it works by building skiboot in LE mode
using: make clean && make LITTLE_ENDIAN=1

>
> >> +       } else {
> >> +               dt_add_property_string(self_save, "status", "disabled");
> > I still don't understand why we're adding anything to the DT if the
> > feature isn't supported.
>
> Today in the kernel, if the node doesn't exist we assume that we are running an
> older firmware on a newer kernel and make the self-restore call as it was
> always supported in legacy.
>
> Now let's take an example of a case where self-restore is actually unsupported,
> in that case if the node does not exist the kernel will assume it does and make
> the call anyways to see it fail later.

The case I'm concerned about is old-kernel / new-firmware since the
legacy API is going to be broken if there's no self-restore
capability. This series seems to be ignoring that fact when we're
populating the DT so an older kernel is going to assume the legacy API
is supported. What is the fallout from that?

> Not that it is a bad thing for it to fail later and then cut support for stop
> states, however, I'm trying to avoid this try-catch scenario. If you feel that
> I should let it fail and handle it later, I'll do that instead.
>
> If you consider opposite scenario instead, that we allow self-restore only if
> the node is present, then using legacy skiboot deep stop states will be
> disabled which is again an undesired behavior.
>
> --
>
> Pratik
>
Pratik R. Sampat Feb. 10, 2020, 11:35 a.m. UTC | #4
>>> Again, where did these sets of registers come from?
>>>
>> These are the set of registers that are supported by the pore engine. Although
>> our kernel only needs a subset of them, the whole list is sent out to indicate
>> what is supported.
> That doesn't really help. I'll assume that by "pore" you mean what's
> in libpore/p9_*.[CH] which writes a pile of stuff into the HOMER, but
> what consumes that? What I want to know is whether updates to some
> other bit of firmware can change the list of supported registers or
> not.

Well this is a list that the firmware currently supports. There is nothing to
stop them from making changes to the support.

The solution is that our self save now has support for versioning. So whenever
something changes, the versions are updated and the compatibility of the SPRs
can be determined and updated accordingly.

>>>> +       self_save_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
>>>> +       self_restore_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
>>> In general I prefer people avoid doing allocations until they're
>>> actually needed. If any of the checks below fail then we've done a
>>> bunch of work for no reason so...      do the checks first?
>> Yeah makes sense, I can move the allocations down later when I really need them.
>> It will also get rid of all the goto bail and I can just use a return instead.
>>
>>>> +       dt_add_property_string(self_restore, "status", "enabled");
>>>> +
>>>> +       dt_add_property(self_restore, "sprn-bitmask", *self_restore_map,
>>>> +                       SPR_BITMAP_LENGTH / 8);
>>>> +
>>>> +       self_save = dt_new(power_mgt, "self-save");
>>>> +       if (!self_save) {
>>>> +               prerror("OCC: Failed to create self save node");
>>>> +               goto bail;
>>>> +       }
>>>> +       if (proc_gen == proc_gen_p9) {
>>>> +               dt_add_property_string(self_save, "status", "enabled");
>>>> +
>>>> +               dt_add_property(self_save, "sprn-bitmask", *self_save_map,
>>>> +                               SPR_BITMAP_LENGTH / 8);
>>> If skiboot is compiled as little endian this is going to break. It
>>> works today because we always build skiboot BE, but that may not
>>> always be true.
>>>
>> Okay, I think I understand what you mean. I should run my bitmask through a
>> loop 32 bits at a time with a be32_to_cpu while populating it?
> Pretty much. You can check if it works by building skiboot in LE mode
> using: make clean && make LITTLE_ENDIAN=1

Sure. I'll try that out

>>>> +       } else {
>>>> +               dt_add_property_string(self_save, "status", "disabled");
>>> I still don't understand why we're adding anything to the DT if the
>>> feature isn't supported.
>> Today in the kernel, if the node doesn't exist we assume that we are running an
>> older firmware on a newer kernel and make the self-restore call as it was
>> always supported in legacy.
>>
>> Now let's take an example of a case where self-restore is actually unsupported,
>> in that case if the node does not exist the kernel will assume it does and make
>> the call anyways to see it fail later.
> The case I'm concerned about is old-kernel / new-firmware since the
> legacy API is going to be broken if there's no self-restore
> capability. This series seems to be ignoring that fact when we're
> populating the DT so an older kernel is going to assume the legacy API
> is supported. What is the fallout from that?

Your case is perfectly valid. However, isn't this what happens currently too?
If for some reason, the self-restore call fails today, the kernel does handle
the consequences.
The try-catch sort of an approach is inevitable in this scenario as there is no
way from which the kernel can know.

I don't mind getting rid of it. I'll do a through test once to see if there's a
corner case I'm missing and I'll remove it then.

Thanks
Pratik
diff mbox series

Patch

diff --git a/hw/slw.c b/hw/slw.c
index 5efa2168..087a8619 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -23,12 +23,15 @@ 
 #include <nvram.h>
 #include <sbe-p8.h>
 #include <xive.h>
+#include <bitmap.h>
 
 #include <p9_stop_api.H>
 #include <p8_pore_table_gen_api.H>
 #include <sbe_xip_image.h>
 
 static uint32_t slw_saved_reset[0x100];
+#define MAX_RESET_PATCH_SIZE	64
+#define SPR_BITMAP_LENGTH	2048
 
 static bool slw_current_le = false;
 
@@ -750,6 +753,84 @@  static void slw_late_init_p9(struct proc_chip *chip)
 	}
 }
 
+/* Add device tree properties to determine self-save | restore */
+void add_cpu_self_save_properties()
+{
+	int i;
+	struct dt_node *self_restore, *self_save, *power_mgt;
+	bitmap_t *self_restore_map, *self_save_map;
+
+	const uint64_t self_restore_regs[] = {
+		P8_SPR_HRMOR,
+		P8_SPR_HMEER,
+		P8_SPR_PMICR,
+		P8_SPR_PMCR,
+		P8_SPR_HID0,
+		P8_SPR_HID1,
+		P8_SPR_HID4,
+		P8_SPR_HID5,
+		P8_SPR_HSPRG0,
+		P8_SPR_LPCR,
+		P8_SPR_PSSCR,
+		P8_MSR_MSR
+	};
+
+	const uint64_t self_save_regs[] = {
+		P9_STOP_SPR_DAWR,
+		P9_STOP_SPR_HSPRG0,
+		P9_STOP_SPR_LDBAR,
+		P9_STOP_SPR_LPCR,
+		P9_STOP_SPR_PSSCR,
+		P9_STOP_SPR_MSR,
+		P9_STOP_SPR_HRMOR,
+		P9_STOP_SPR_HMEER,
+		P9_STOP_SPR_PMCR,
+		P9_STOP_SPR_PTCR
+	};
+
+	self_save_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
+	self_restore_map = zalloc(BITMAP_BYTES(SPR_BITMAP_LENGTH));
+
+	for (i = 0; i < ARRAY_SIZE(self_save_regs); i++)
+		bitmap_set_bit(*self_save_map, self_save_regs[i]);
+
+	for (i = 0; i < ARRAY_SIZE(self_restore_regs); i++)
+		bitmap_set_bit(*self_restore_map, self_restore_regs[i]);
+
+	power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt");
+	if (!power_mgt) {
+		prerror("OCC: dt node /ibm,opal/power-mgt not found\n");
+		goto bail;
+	}
+
+	self_restore = dt_new(power_mgt, "self-restore");
+	if (!self_restore) {
+		prerror("OCC: Failed to create self restore node");
+		goto bail;
+	}
+	dt_add_property_string(self_restore, "status", "enabled");
+
+	dt_add_property(self_restore, "sprn-bitmask", *self_restore_map,
+			SPR_BITMAP_LENGTH / 8);
+
+	self_save = dt_new(power_mgt, "self-save");
+	if (!self_save) {
+		prerror("OCC: Failed to create self save node");
+		goto bail;
+	}
+	if (proc_gen == proc_gen_p9) {
+		dt_add_property_string(self_save, "status", "enabled");
+
+		dt_add_property(self_save, "sprn-bitmask", *self_save_map,
+				SPR_BITMAP_LENGTH / 8);
+	} else {
+		dt_add_property_string(self_save, "status", "disabled");
+	}
+bail:
+	free(self_save_map);
+	free(self_restore_map);
+}
+
 /* Add device tree properties to describe idle states */
 void add_cpu_idle_state_properties(void)
 {
@@ -1563,4 +1644,6 @@  void slw_init(void)
 		}
 	}
 	add_cpu_idle_state_properties();
+	if (has_deep_states)
+		add_cpu_self_save_properties();
 }
diff --git a/include/skiboot.h b/include/skiboot.h
index 072ce589..676211ad 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -209,6 +209,7 @@  extern void early_uart_init(void);
 extern void homer_init(void);
 extern void slw_init(void);
 extern void add_cpu_idle_state_properties(void);
+extern void add_cpu_self_save_properties(void);
 extern void lpc_rtc_init(void);
 
 /* flash support */
diff --git a/libpore/p8_pore_table_gen_api.H b/libpore/p8_pore_table_gen_api.H
index 63081ca5..d72fee90 100644
--- a/libpore/p8_pore_table_gen_api.H
+++ b/libpore/p8_pore_table_gen_api.H
@@ -233,6 +233,7 @@  enum  {
   P8_SPR_HRMOR  =  313,
   P8_SPR_HMEER  =  337,
   P8_SPR_PMICR  =  852,
+  P8_SPR_PSSCR  =  855,
   P8_SPR_PMCR   =  884,
   P8_SPR_HID0   = 1008,
   P8_SPR_HID1   = 1009,