{"id":801004,"url":"http://patchwork.ozlabs.org/api/patches/801004/?format=json","web_url":"http://patchwork.ozlabs.org/project/skiboot/patch/20170813135959.23863-5-npiggin@gmail.com/","project":{"id":44,"url":"http://patchwork.ozlabs.org/api/projects/44/?format=json","name":"skiboot firmware development","link_name":"skiboot","list_id":"skiboot.lists.ozlabs.org","list_email":"skiboot@lists.ozlabs.org","web_url":"http://github.com/open-power/skiboot","scm_url":"http://github.com/open-power/skiboot","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170813135959.23863-5-npiggin@gmail.com>","list_archive_url":null,"date":"2017-08-13T13:59:59","name":"[RFC,4/4] cpu: initial power management implementation for POWER9","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"87859e64211f47283a4d9bceaa43e3dfd93dfede","submitter":{"id":69518,"url":"http://patchwork.ozlabs.org/api/people/69518/?format=json","name":"Nicholas Piggin","email":"npiggin@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/skiboot/patch/20170813135959.23863-5-npiggin@gmail.com/mbox/","series":[],"comments":"http://patchwork.ozlabs.org/api/patches/801004/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/801004/checks/","tags":{},"related":[],"headers":{"Return-Path":"<skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>","X-Original-To":["incoming@patchwork.ozlabs.org","skiboot@lists.ozlabs.org"],"Delivered-To":["patchwork-incoming@bilbo.ozlabs.org","skiboot@lists.ozlabs.org"],"Received":["from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3])\n\t(using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xVgQb5jDZz9sCZ\n\tfor <incoming@patchwork.ozlabs.org>;\n\tMon, 14 Aug 2017 00:01:43 +1000 (AEST)","from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 3xVgQb4QDLzDrFw\n\tfor <incoming@patchwork.ozlabs.org>;\n\tMon, 14 Aug 2017 00:01:43 +1000 (AEST)","from mail-pf0-x242.google.com (mail-pf0-x242.google.com\n\t[IPv6:2607:f8b0:400e:c00::242])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128\n\tbits)) (No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 3xVgP96qtQzDrCd\n\tfor <skiboot@lists.ozlabs.org>; Mon, 14 Aug 2017 00:00:29 +1000 (AEST)","by mail-pf0-x242.google.com with SMTP id t83so7713320pfj.3\n\tfor <skiboot@lists.ozlabs.org>; Sun, 13 Aug 2017 07:00:29 -0700 (PDT)","from roar.local0.net (203-219-56-202.tpgi.com.au. [203.219.56.202])\n\tby smtp.gmail.com with ESMTPSA id\n\t67sm9122283pfa.75.2017.08.13.07.00.23\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tSun, 13 Aug 2017 07:00:26 -0700 (PDT)"],"Authentication-Results":["ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"dFZPuBV2\"; dkim-atps=neutral","lists.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"dFZPuBV2\"; dkim-atps=neutral","lists.ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"dFZPuBV2\"; dkim-atps=neutral"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=Kx7ODYeJzW9EW4wnY2DiSnGCw9rycxGP3iXWm3V9Ej4=;\n\tb=dFZPuBV2Nq5hVwoI/MXlel970ozGh9aTq8HhOXT73FKvXuUqLo1MEfMiD6ZjZm3m/z\n\tAmAcfXCCNsYZspdEjPWsVgWw9byFNb2shqjw24SzvfVf2RsN7mKrq9ELU3kIhYzuXbYy\n\t6lgWtVRbtL33N0rZTcir7Z7z9PsldFVmDY92JzUwSwyhYcjzopoiZmayZDkh4eqCzCxv\n\tusvpApien1Gyo5RNRlw0lczEyzrbYYmXu01te89BrwMUi9LziTe/tryaFbUeHZZ78+9A\n\tOZxGTFMXvDKOBUZwyWJUdzfXZ3U15h+zL/bUrRQetzsepfX7AzFg/feN2yrgFPhu7ADW\n\t+45Q==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=Kx7ODYeJzW9EW4wnY2DiSnGCw9rycxGP3iXWm3V9Ej4=;\n\tb=Acb8JH1eqUZVTSnjYTm01Jfxkz+oplK/HUgqOP5CKV4gjwhj5rVsBegQSa16F7KHmj\n\th9Wh+52r3DFfXJUuw31Ivw8AlWun3ZVgS1RJMgkrxk8Fo5OFO7DkDlJAytx6Z7FKmXy+\n\tDL/SkfOyaBIDpGa4+rTuouK93LHsF/p0vJLypD3e1xLoGInKMWz19THfSH0qza6youcU\n\tmc+rJodsJ03xN5V6gm1JCAFzl24rgyQ+qJdHt8KjkSmpMxBVxnSAnN1ORjZ3Lqq9dcmU\n\tAzmNj45L/aZP7FvQyqoguYi8ENDWiOmj4CJ4HxJ+amoypXgOgRFBQTqw8lFDhDIHVpOU\n\tjsYw==","X-Gm-Message-State":"AHYfb5gsXgHVDwFGfUTwcdEc15SmLLfQAHtM+Gy21ot7KhVMEtymW8Ic\n\tWDRInpA6/YIbVoXB","X-Received":"by 10.84.231.202 with SMTP id g10mr24204844pln.259.1502632827800;\n\tSun, 13 Aug 2017 07:00:27 -0700 (PDT)","From":"Nicholas Piggin <npiggin@gmail.com>","To":"skiboot@lists.ozlabs.org","Date":"Sun, 13 Aug 2017 23:59:59 +1000","Message-Id":"<20170813135959.23863-5-npiggin@gmail.com>","X-Mailer":"git-send-email 2.13.3","In-Reply-To":"<20170813135959.23863-1-npiggin@gmail.com>","References":"<20170813135959.23863-1-npiggin@gmail.com>","Subject":"[Skiboot] [RFC PATCH 4/4] cpu: initial power management\n\timplementation for POWER9","X-BeenThere":"skiboot@lists.ozlabs.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"Mailing list for skiboot development <skiboot.lists.ozlabs.org>","List-Unsubscribe":"<https://lists.ozlabs.org/options/skiboot>,\n\t<mailto:skiboot-request@lists.ozlabs.org?subject=unsubscribe>","List-Archive":"<http://lists.ozlabs.org/pipermail/skiboot/>","List-Post":"<mailto:skiboot@lists.ozlabs.org>","List-Help":"<mailto:skiboot-request@lists.ozlabs.org?subject=help>","List-Subscribe":"<https://lists.ozlabs.org/listinfo/skiboot>,\n\t<mailto:skiboot-request@lists.ozlabs.org?subject=subscribe>","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org","Sender":"\"Skiboot\"\n\t<skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>"},"content":"This is an RFC patch only at the moment, it should be split into at\nleast 2 patches, and has not been tested on real hardware yet.\n\nThe idea is to add basic stop support to POWER9 for OPAL's idle loops.\nTo do this, we split the \"pm enable\" into two parts, enablement for\nIPI facility, and enablement for sreset vector wakeups. POWER9 can use\nthe EC=ESL=0 (lite) stop when sreset is not installed. It can also use\nglobal doorbell for IPIs before the interrupt controller is set up, so\nthere are some differences from P8.\n\nThe EC=ESL=1 state that is enabled when we have a sreset wakeup is\ncurrently just level 3 which is like nap. It should allow thread\nswitch but not full state loss and core power down.\n---\n asm/head.S          |  70 +++++++++++++------\n core/cpu.c          | 198 +++++++++++++++++++++++++++++++++++++++++++++-------\n core/fast-reboot.c  |   3 +-\n core/init.c         |  12 +++-\n hdata/cpu-common.c  |  12 ----\n hw/slw.c            |   2 +-\n include/cpu.h       |   8 ++-\n include/processor.h |  44 ++++++++++++\n include/skiboot.h   |   4 +-\n 9 files changed, 286 insertions(+), 67 deletions(-)","diff":"diff --git a/asm/head.S b/asm/head.S\nindex badb567c..d6b58be9 100644\n--- a/asm/head.S\n+++ b/asm/head.S\n@@ -28,6 +28,8 @@\n #define PPC_INST_SLEEP\t\t.long 0x4c0003a4\n #define PPC_INST_RVWINKLE\t.long 0x4c0003e4\n \n+#define PPC_INST_STOP\t\t.long 0x4c0002e4\n+\n #define GET_STACK(stack_reg,pir_reg)\t\t\t\t\\\n \tsldi\tstack_reg,pir_reg,STACK_SHIFT;\t\t\t\\\n \taddis\tstack_reg,stack_reg,CPU_STACKS_OFFSET@ha;\t\\\n@@ -471,27 +473,7 @@ call_relocate:\n        .long 0xa6037b7d; /* mtsrr1 r11                         */ \\\n        .long 0x2400004c  /* rfid                               */\n \n-.global enter_pm_state\n-enter_pm_state:\n-\t/* Before entering map or rvwinkle, we create a stack frame\n-\t * and save our non-volatile registers.\n-\t *\n-\t * We also save these SPRs:\n-\t *\n-\t *  - HSPRG0\tin GPR0 slot\n-\t *  - HSPRG1\tin GPR1 slot\n-\t *\n-\t *  - xxx TODO: HIDs\n-\t *  - TODO: Mask MSR:ME during the process\n-\t *\n-\t * On entry, r3 indicates:\n-\t *\n-\t *    0 = nap\n-\t *    1 = rvwinkle\n-\t */\n-\tmflr\t%r0\n-\tstd\t%r0,16(%r1)\n-\tstdu\t%r1,-STACK_FRAMESIZE(%r1)\n+pm_save_regs:\n \tSAVE_GPR(2,%r1)\n \tSAVE_GPR(14,%r1)\n \tSAVE_GPR(15,%r1)\n@@ -519,6 +501,31 @@ enter_pm_state:\n \tstw\t%r5,STACK_XER(%r1)\n \tstd\t%r6,STACK_GPR0(%r1)\n \tstd\t%r7,STACK_GPR1(%r1)\n+\tblr\n+\n+.global enter_p8_pm_state\n+enter_p8_pm_state:\n+\t/* Before entering map or rvwinkle, we create a stack frame\n+\t * and save our non-volatile registers.\n+\t *\n+\t * We also save these SPRs:\n+\t *\n+\t *  - HSPRG0\tin GPR0 slot\n+\t *  - HSPRG1\tin GPR1 slot\n+\t *\n+\t *  - xxx TODO: HIDs\n+\t *  - TODO: Mask MSR:ME during the process\n+\t *\n+\t * On entry, r3 indicates:\n+\t *\n+\t *    0 = nap\n+\t *    1 = rvwinkle\n+\t */\n+\tmflr\t%r0\n+\tstd\t%r0,16(%r1)\n+\tstdu\t%r1,-STACK_FRAMESIZE(%r1)\n+\n+\tbl\tpm_save_regs\n \n \t/* Save stack pointer in struct cpu_thread */\n \tstd\t%r1,CPUTHREAD_SAVE_R1(%r13)\n@@ -543,6 +550,27 @@ enter_pm_state:\n \tPPC_INST_RVWINKLE\n \tb\t.\n \n+.global enter_p9_pm_lite_state\n+enter_p9_pm_lite_state:\n+\tmtspr\tSPR_PSSCR,%r3\n+\tPPC_INST_STOP\n+\tblr\n+\n+.global enter_p9_pm_state\n+enter_p9_pm_state:\n+\tmflr\t%r0\n+\tstd\t%r0,16(%r1)\n+\tstdu\t%r1,-STACK_FRAMESIZE(%r1)\n+\n+\tbl\tpm_save_regs\n+\n+\t/* Save stack pointer in struct cpu_thread */\n+\tstd\t%r1,CPUTHREAD_SAVE_R1(%r13)\n+\n+\tmtspr\tSPR_PSSCR,%r3\n+\tPPC_INST_STOP\n+\tb\t.\n+\n /* This is a little piece of code that is copied down to\n  * 0x100 for handling power management wakeups\n  */\ndiff --git a/core/cpu.c b/core/cpu.c\nindex 2a95dff6..927ef45e 100644\n--- a/core/cpu.c\n+++ b/core/cpu.c\n@@ -52,6 +52,8 @@ static bool hile_supported;\n static bool radix_supported;\n static unsigned long hid0_hile;\n static unsigned long hid0_attn;\n+static bool sreset_enabled;\n+static bool ipi_enabled;\n static bool pm_enabled;\n static bool current_hile_mode;\n static bool current_radix_mode;\n@@ -92,8 +94,12 @@ static void cpu_wake(struct cpu_thread *cpu)\n \tif (!cpu->in_idle)\n \t\treturn;\n \n-\t/* Poke IPI */\n-\ticp_kick_cpu(cpu);\n+\tif (proc_gen == proc_gen_p8) {\n+\t\t/* Poke IPI */\n+\t\ticp_kick_cpu(cpu);\n+\t} else if (proc_gen == proc_gen_p9) {\n+\t\tp9_dbell_send(cpu->pir);\n+\t}\n }\n \n static struct cpu_thread *cpu_find_job_target(void)\n@@ -317,6 +323,7 @@ static void cpu_idle_p8(enum cpu_wake_cause wake_on)\n \t\tif (cpu_check_jobs(cpu) || !pm_enabled)\n \t\t\tgoto skip_sleep;\n \n+\t\t/* Setup wakup cause in LPCR: EE (for IPI) */\n \t\tlpcr |= SPR_LPCR_P8_PECE2;\n \t\tmtspr(SPR_LPCR, lpcr);\n \n@@ -331,12 +338,13 @@ static void cpu_idle_p8(enum cpu_wake_cause wake_on)\n \t\tif (!pm_enabled)\n \t\t\tgoto skip_sleep;\n \n+\t\t/* EE and DEC */\n \t\tlpcr |= SPR_LPCR_P8_PECE2 | SPR_LPCR_P8_PECE3;\n \t\tmtspr(SPR_LPCR, lpcr);\n \t}\n \n \t/* Enter nap */\n-\tenter_pm_state(false);\n+\tenter_p8_pm_state(false);\n \n skip_sleep:\n \t/* Restore */\n@@ -346,33 +354,67 @@ skip_sleep:\n \treset_cpu_icp();\n }\n \n-void cpu_set_pm_enable(bool enabled)\n+static void cpu_idle_p9(enum cpu_wake_cause wake_on)\n {\n-\tstruct cpu_thread *cpu;\n-\n-\tprlog(PR_INFO, \"CPU: %sing power management\\n\",\n-\t      enabled ? \"enabl\" : \"disabl\");\n+\tuint64_t lpcr = mfspr(SPR_LPCR) & ~SPR_LPCR_P9_PECE;\n+\tuint64_t psscr;\n+\tstruct cpu_thread *cpu = this_cpu();\n \n-\tif (proc_gen != proc_gen_p8)\n+\tif (!pm_enabled) {\n+\t\tprlog_once(PR_DEBUG, \"cpu_idle_p9 called pm disabled\\n\");\n \t\treturn;\n+\t}\n \n-\t/* Public P8 Mambo has broken NAP */\n-\tif (chip_quirk(QUIRK_MAMBO_CALLOUTS))\n-\t\treturn;\n+\tmsgclr(); /* flush pending messages */\n \n-\tpm_enabled = enabled;\n+\t/* Synchronize with wakers */\n+\tif (wake_on == cpu_wake_on_job) {\n+\t\t/* Mark ourselves in idle so other CPUs know to send an IPI */\n+\t\tcpu->in_idle = true;\n+\t\tsync();\n \n-\tif (enabled)\n-\t\treturn;\n+\t\t/* Check for jobs again */\n+\t\tif (cpu_check_jobs(cpu) || !pm_enabled)\n+\t\t\tgoto skip_sleep;\n \n-\t/* If disabling, take everybody out of PM */\n-\tsync();\n-\tfor_each_available_cpu(cpu) {\n-\t\twhile (cpu->in_sleep || cpu->in_idle) {\n-\t\t\ticp_kick_cpu(cpu);\n-\t\t\tcpu_relax();\n-\t\t}\n+\t\t/* HV DBELL for IPI */\n+\t\tlpcr |= SPR_LPCR_P9_PECEL1;\n+\t} else {\n+\t\t/* Mark outselves sleeping so cpu_set_pm_enable knows to\n+\t\t * send an IPI\n+\t\t */\n+\t\tcpu->in_sleep = true;\n+\t\tsync();\n+\n+\t\t/* Check if PM got disabled */\n+\t\tif (!pm_enabled)\n+\t\t\tgoto skip_sleep;\n+\n+\t\t/* HV DBELL and DEC */\n+\t\tlpcr |= SPR_LPCR_P9_PECEL1 | SPR_LPCR_P9_PECEL3;\n+\t\tmtspr(SPR_LPCR, lpcr);\n \t}\n+\n+\tmtspr(SPR_LPCR, lpcr);\n+\n+\tif (sreset_enabled) {\n+\t\t/* stop with EC=1 (sreset) and ESL=1 (enable thread switch). */\n+\t\t/* PSSCR SD=0 ESL=1 EC=1 PSSL=0 TR=3 MTL=0 RL=3 */\n+\t\tpsscr = PPC_BIT(42) | PPC_BIT(43) |\n+\t\t\tPPC_BITMASK(54, 55) | PPC_BITMASK(62,63);\n+\t\tenter_p9_pm_state(psscr);\n+\t} else {\n+\t\t/* stop with EC=0 (resumes) which does not require sreset. */\n+\t\t/* PSSCR SD=0 ESL=0 EC=0 PSSL=0 TR=3 MTL=0 RL=3 */\n+\t\tpsscr = PPC_BITMASK(54, 55) | PPC_BITMASK(62,63);\n+\t\tenter_p9_pm_lite_state(psscr);\n+\t}\n+\n+skip_sleep:\n+\t/* Restore */\n+\tcpu->in_idle = false;\n+\tcpu->in_sleep = false;\n+\tp9_dbell_receive();\n }\n \n static void cpu_idle_pm(enum cpu_wake_cause wake_on)\n@@ -381,6 +423,9 @@ static void cpu_idle_pm(enum cpu_wake_cause wake_on)\n \tcase proc_gen_p8:\n \t\tcpu_idle_p8(wake_on);\n \t\tbreak;\n+\tcase proc_gen_p9:\n+\t\tcpu_idle_p9(wake_on);\n+\t\tbreak;\n \tdefault:\n \t\tprlog_once(PR_DEBUG, \"cpu_idle_pm called with bad processor type\\n\");\n \t\tbreak;\n@@ -396,8 +441,11 @@ void cpu_idle_job(void)\n \n \t\tsmt_lowest();\n \t\t/* Check for jobs again */\n-\t\twhile (!cpu_check_jobs(cpu))\n+\t\twhile (!cpu_check_jobs(cpu)) {\n+\t\t\tif (pm_enabled)\n+\t\t\t\tbreak;\n \t\t\tbarrier();\n+\t\t}\n \t\tsmt_medium();\n \t}\n }\n@@ -409,6 +457,7 @@ void cpu_idle_delay(unsigned long delay)\n \tunsigned long min_pm = usecs_to_tb(10);\n \n \tif (pm_enabled && delay > min_pm) {\n+pm:\n \t\tfor (;;) {\n \t\t\tif (delay >= 0x7fffffff)\n \t\t\t\tdelay = 0x7fffffff;\n@@ -419,17 +468,114 @@ void cpu_idle_delay(unsigned long delay)\n \t\t\tnow = mftb();\n \t\t\tif (tb_compare(now, end) == TB_AAFTERB)\n \t\t\t\tbreak;\n-\n \t\t\tdelay = end - now;\n+\t\t\tif (!(pm_enabled && delay > min_pm))\n+\t\t\t\tgoto no_pm;\n \t\t}\n \t} else {\n+no_pm:\n \t\tsmt_lowest();\n-\t\twhile (tb_compare(mftb(), end) != TB_AAFTERB)\n-\t\t\tbarrier();\n+\t\tfor (;;) {\n+\t\t\tnow = mftb();\n+\t\t\tif (tb_compare(now, end) == TB_AAFTERB)\n+\t\t\t\tbreak;\n+\t\t\tdelay = end - now;\n+\t\t\tif (pm_enabled && delay > min_pm)\n+\t\t\t\tgoto pm;\n+\t\t}\n \t\tsmt_medium();\n \t}\n }\n \n+static void cpu_pm_disable(void)\n+{\n+\tstruct cpu_thread *cpu;\n+\n+\tpm_enabled = false;\n+\tsync();\n+\n+\tif (proc_gen == proc_gen_p8) {\n+\t\tfor_each_available_cpu(cpu) {\n+\t\t\twhile (cpu->in_sleep || cpu->in_idle) {\n+\t\t\t\ticp_kick_cpu(cpu);\n+\t\t\t\tcpu_relax();\n+\t\t\t}\n+\t\t}\n+\t} else if (proc_gen == proc_gen_p9) {\n+\t\tfor_each_available_cpu(cpu) {\n+\t\t\tif (cpu->in_sleep || cpu->in_idle)\n+\t\t\t\tp9_dbell_send(cpu->pir);\n+\t\t}\n+\n+\t\tsmt_lowest();\n+\t\tfor_each_available_cpu(cpu) {\n+\t\t\twhile (cpu->in_sleep || cpu->in_idle)\n+\t\t\t\tbarrier();\n+\t\t}\n+\t\tsmt_medium();\n+\t}\n+}\n+\n+void cpu_set_sreset_enable(bool enabled)\n+{\n+\tif (sreset_enabled == enabled)\n+\t\treturn;\n+\n+\tif (proc_gen == proc_gen_p8) {\n+\t\t/* Public P8 Mambo has broken NAP */\n+\t\tif (chip_quirk(QUIRK_MAMBO_CALLOUTS))\n+\t\t\treturn;\n+\n+\t\tsreset_enabled = enabled;\n+\t\tsync();\n+\n+\t\tif (!enabled) {\n+\t\t\tcpu_pm_disable();\n+\t\t} else {\n+\t\t\tif (ipi_enabled)\n+\t\t\t\tpm_enabled = true;\n+\t\t}\n+\t} else if (proc_gen == proc_gen_p9) {\n+\t\tsreset_enabled = enabled;\n+\t\tsync();\n+\t\t/*\n+\t\t * Kick everybody out of PM so they can adjust the PM\n+\t\t * mode they are using (EC=0/1).\n+\t\t */\n+\t\tcpu_pm_disable();\n+\t\tif (ipi_enabled)\n+\t\t\tpm_enabled = true;\n+\t}\n+}\n+\n+void cpu_set_ipi_enable(bool enabled)\n+{\n+\tif (ipi_enabled == enabled)\n+\t\treturn;\n+\n+\tif (proc_gen == proc_gen_p8) {\n+\t\tipi_enabled = enabled;\n+\t\tsync();\n+\t\tif (!enabled) {\n+\t\t\tcpu_pm_disable();\n+\t\t} else {\n+\t\t\tif (sreset_enabled)\n+\t\t\t\tpm_enabled = true;\n+\t\t}\n+\t} else if (proc_gen == proc_gen_p9) {\n+\t\t/* DD1 has global doorbell msgsync missing */\n+\t\tuint32_t version = mfspr(SPR_PVR);\n+\t\tif ((PVR_VERS_MAJ(version) == 1) && is_power9n(version))\n+\t\t\treturn;\n+\t\tipi_enabled = enabled;\n+\t\tsync();\n+\t\tif (!enabled)\n+\t\t\tcpu_pm_disable();\n+\t\telse\n+\t\t\tpm_enabled = true;\n+\t}\n+}\n+\n void cpu_process_local_jobs(void)\n {\n \tstruct cpu_thread *cpu = first_available_cpu();\ndiff --git a/core/fast-reboot.c b/core/fast-reboot.c\nindex 7bfc06de..8af5c590 100644\n--- a/core/fast-reboot.c\n+++ b/core/fast-reboot.c\n@@ -564,7 +564,8 @@ void __noreturn fast_reboot_entry(void)\n \tcpu_fast_reboot_complete();\n \n \t/* We can now do NAP mode */\n-\tcpu_set_pm_enable(true);\n+\tcpu_set_sreset_enable(true);\n+\tcpu_set_ipi_enable(true);\n \n \t/* Start preloading kernel and ramdisk */\n \tstart_preload_kernel();\ndiff --git a/core/init.c b/core/init.c\nindex 01fe12c9..dbdcc465 100644\n--- a/core/init.c\n+++ b/core/init.c\n@@ -380,7 +380,7 @@ static bool load_kernel(void)\n \t\t * by our vectors.\n \t\t */\n \t\tif (kernel_entry < 0x2000) {\n-\t\t\tcpu_set_pm_enable(false);\n+\t\t\tcpu_set_sreset_enable(false);\n \t\t\tmemcpy(NULL, old_vectors, 0x2000);\n \t\t\tsync_icache();\n \t\t}\n@@ -543,7 +543,8 @@ void __noreturn load_and_boot_kernel(bool is_reboot)\n \tmem_dump_free();\n \n \t/* Take processours out of nap */\n-\tcpu_set_pm_enable(false);\n+\tcpu_set_sreset_enable(false);\n+\tcpu_set_ipi_enable(false);\n \n \t/* Dump the selected console */\n \tstdoutp = dt_prop_get_def(dt_chosen, \"linux,stdout-path\", NULL);\n@@ -723,6 +724,7 @@ void setup_reset_vector(void)\n \twhile(src < &reset_patch_end)\n \t\t*(dst++) = *(src++);\n \tsync_icache();\n+\tcpu_set_sreset_enable(true);\n }\n \n void copy_exception_vectors(void)\n@@ -918,12 +920,16 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)\n \n \t/* Initialize the rest of the cpu thread structs */\n \tinit_all_cpus();\n+\tif (proc_gen == proc_gen_p9)\n+\t\tcpu_set_ipi_enable(true);\n \n \t/* Allocate our split trace buffers now. Depends add_opal_node() */\n \tinit_trace_buffers();\n \n \t/* On P7/P8, get the ICPs and make sure they are in a sane state */\n \tinit_interrupts();\n+\tif (proc_gen == proc_gen_p7 || proc_gen == proc_gen_p8)\n+\t\tcpu_set_ipi_enable(true);\n \n \t/* On P9, initialize XIVE */\n \tinit_xive();\n@@ -949,7 +955,7 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)\n \tsetup_reset_vector();\n \n \t/* We can now do NAP mode */\n-\tcpu_set_pm_enable(true);\n+\tcpu_set_sreset_enable(true);\n \n \t/*\n \t * Synchronize time bases. Thi resets all the TB values to a small\ndiff --git a/hdata/cpu-common.c b/hdata/cpu-common.c\nindex a1a93121..f40d08b3 100644\n--- a/hdata/cpu-common.c\n+++ b/hdata/cpu-common.c\n@@ -21,18 +21,6 @@\n \n #include \"hdata.h\"\n \n-static bool is_power9n(uint32_t version)\n-{\n-\t/*\n-\t * Bit 13 tells us:\n-\t *   0 = Scale out (aka Nimbus)\n-\t *   1 = Scale up  (aka Cumulus)\n-\t */\n-\tif ((version >> 13) & 1)\n-\t\treturn false;\n-\treturn true;\n-}\n-\n struct dt_node * add_core_common(struct dt_node *cpus,\n \t\t\t\t const struct sppcia_cpu_cache *cache,\n \t\t\t\t const struct sppaca_cpu_timebase *tb,\ndiff --git a/hw/slw.c b/hw/slw.c\nindex c0ab9dea..6452e93a 100644\n--- a/hw/slw.c\n+++ b/hw/slw.c\n@@ -82,7 +82,7 @@ static void slw_do_rvwinkle(void *data)\n \t/* Tell that we got it */\n \tcpu->state = cpu_state_rvwinkle;\n \n-\tenter_pm_state(1);\n+\tenter_p8_pm_state(1);\n \n \t/* Restore SPRs */\n \tinit_shared_sprs();\ndiff --git a/include/cpu.h b/include/cpu.h\nindex f08940f7..0b873178 100644\n--- a/include/cpu.h\n+++ b/include/cpu.h\n@@ -256,8 +256,12 @@ extern void cpu_process_jobs(void);\n extern void cpu_process_local_jobs(void);\n /* Check if there's any job pending */\n bool cpu_check_jobs(struct cpu_thread *cpu);\n-/* Enable/disable PM */\n-void cpu_set_pm_enable(bool pm_enabled);\n+\n+/* OPAL sreset vector in place at 0x100 */\n+void cpu_set_sreset_enable(bool sreset_enabled);\n+\n+/* IPI for PM modes is enabled */\n+void cpu_set_ipi_enable(bool sreset_enabled);\n \n static inline void cpu_give_self_os(void)\n {\ndiff --git a/include/processor.h b/include/processor.h\nindex 2e1ac37d..11992248 100644\n--- a/include/processor.h\n+++ b/include/processor.h\n@@ -77,6 +77,7 @@\n #define SPR_HMER\t0x150\t/* Hypervisor Maintenance Exception */\n #define SPR_HMEER\t0x151\t/* HMER interrupt enable mask */\n #define SPR_AMOR\t0x15d\n+#define SPR_PSSCR\t0x357   /* RW: Stop status and control (ISA 3) */\n #define SPR_TSCR\t0x399\n #define SPR_HID0\t0x3f0\n #define SPR_HID1\t0x3f1\n@@ -85,6 +86,7 @@\n #define SPR_HID5\t0x3f6\n #define SPR_PIR\t\t0x3ff\t/* RO: Processor Identification */\n \n+\n /* Bits in LPCR */\n \n /* Powersave Exit Cause Enable is different for P7 and P8 */\n@@ -99,6 +101,14 @@\n #define SPR_LPCR_P8_PECE2\tPPC_BIT(49)   /* Wake on external interrupts */\n #define SPR_LPCR_P8_PECE3\tPPC_BIT(50)   /* Wake on decrementer */\n #define SPR_LPCR_P8_PECE4\tPPC_BIT(51)   /* Wake on MCs, HMIs, etc... */\n+\n+#define SPR_LPCR_P9_PECE\t(PPC_BITMASK(47,51) | PPC_BITMASK(17,17))\n+#define SPR_LPCR_P9_PECEU0\tPPC_BIT(17)   /* Wake on HVI */\n+#define SPR_LPCR_P9_PECEL0\tPPC_BIT(47)   /* Wake on priv doorbell */\n+#define SPR_LPCR_P9_PECEL1\tPPC_BIT(48)   /* Wake on hv doorbell */\n+#define SPR_LPCR_P9_PECEL2\tPPC_BIT(49)   /* Wake on external interrupts */\n+#define SPR_LPCR_P9_PECEL3\tPPC_BIT(50)   /* Wake on decrementer */\n+#define SPR_LPCR_P9_PECEL4\tPPC_BIT(51)   /* Wake on MCs, HMIs, etc... */\n #define SPR_LPCR_P9_LD\t\tPPC_BIT(46)   /* Large decrementer mode bit */\n \n \n@@ -206,6 +216,19 @@\n \n #include <compiler.h>\n #include <stdint.h>\n+#include <stdbool.h>\n+\n+static inline bool is_power9n(uint32_t version)\n+{\n+\t/*\n+\t * Bit 13 tells us:\n+\t *   0 = Scale out (aka Nimbus)\n+\t *   1 = Scale up  (aka Cumulus)\n+\t */\n+\tif ((version >> 13) & 1)\n+\t\treturn false;\n+\treturn true;\n+}\n \n /*\n  * SMT priority\n@@ -294,6 +317,27 @@ static inline void sync_icache(void)\n \tasm volatile(\"sync; icbi 0,%0; sync; isync\" : : \"r\" (0) : \"memory\");\n }\n \n+/*\n+ * Doorbells\n+ */\n+static inline void msgclr(void)\n+{\n+\tuint64_t rb = (0x05 << (63-36));\n+\tasm volatile(\"msgclr %0\" : : \"r\"(rb));\n+}\n+\n+static inline void p9_dbell_receive(void)\n+{\n+\tuint64_t rb = (0x05 << (63-36));\n+\t/* msgclr ; msgsync ; lwsync */\n+\tasm volatile(\"msgclr %0 ; .long 0x7c0006ec ; lwsync\" : : \"r\"(rb));\n+}\n+\n+static inline void p9_dbell_send(uint32_t pir)\n+{\n+\tuint64_t rb = (0x05 << (63-36)) | pir;\n+\tasm volatile(\"sync ; msgsnd %0\" : : \"r\"(rb));\n+}\n \n /*\n  * Byteswap load/stores\ndiff --git a/include/skiboot.h b/include/skiboot.h\nindex 4b7d5197..2ed18397 100644\n--- a/include/skiboot.h\n+++ b/include/skiboot.h\n@@ -301,7 +301,9 @@ extern void fast_sleep_exit(void);\n extern void fake_rtc_init(void);\n \n /* Assembly in head.S */\n-extern void enter_pm_state(bool winkle);\n+extern void enter_p8_pm_state(bool winkle);\n+extern void enter_p9_pm_state(uint64_t psscr);\n+extern void enter_p9_pm_lite_state(uint64_t psscr);\n extern uint32_t reset_patch_start;\n extern uint32_t reset_patch_end;\n \n","prefixes":["RFC","4/4"]}