From 8b56d67e124693b5f9b05a339bdc2fbb452c9acf Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:34:22 -0400 Subject: [PATCH] pmdomain: apple: Preserve active T6050 control state Preserve a firmware-active PMGR control word until a real state change is requested. Keep the current firmware-owned, inherited-on, strict-state, and reset behavior intact. Source snapshot: t6050-gpu ee68abb105200e99636a5edfc647a5cfc1d89954 Integration base: 9cbdd0734d9e186c5f5f5949a21b909d10c2571b Signed-off-by: Dj <43033058+DjDeveloperr@users.noreply.github.com> --- .../bindings/power/apple,pmgr-pwrstate.yaml | 9 +++ drivers/pmdomain/apple/pmgr-pwrstate.c | 47 +++++++++++- tools/testing/selftests/aurora-apif/pmgr.py | 73 +++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/aurora-apif/pmgr.py diff --git a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml index 919e53113fd2a..cd8748b93174d 100644 --- a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml +++ b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml @@ -132,6 +132,15 @@ properties: unlike apple,firmware-owned, their registers remain writable. type: boolean + apple,preserve-active-control: + description: + Firmware hands off this domain with an active control word that must be + retained until an actual power transition is requested. Do not enable + auto-PM at probe or rewrite a reset-free, non-busy control word whose + target and actual state already satisfy an active request. Power-down + and requests that require a state change retain the normal sequence. + type: boolean + apple,min-state: description: Specifies the minimum power state for auto-PM. diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index bfcfb5ccbf1a4..fe8dc7eb46926 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -39,6 +39,11 @@ #define APPLE_PMGR_RESET_TIME 1 #define APPLE_PMGR_T8132_RESET_TIME 10 +/* Opt-in investigation: normal power transitions do not log. */ +static bool diagnostic_trace; +module_param(diagnostic_trace, bool, 0444); +MODULE_PARM_DESC(diagnostic_trace, "Trace native power handoff and register writes (diagnostic)"); + struct apple_pmgr_ps { struct device *dev; struct generic_pm_domain genpd; @@ -58,6 +63,7 @@ struct apple_pmgr_ps { bool needs_reset_idle_wait; bool strict_state; u32 reset_time_us; + bool preserve_active_control; }; #define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) @@ -70,10 +76,27 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a u32 reg, cur; u32 timeout = ps->strict_state ? 10000 : APPLE_PMGR_PS_SET_TIMEOUT; + if (diagnostic_trace) + dev_info(ps->dev, "PMGR begin %s offset=%#x requested=%#x auto=%u\n", + genpd->name, ps->offset, pstate, auto_enable); ret = regmap_read(ps->regmap, ps->offset, ®); if (ret < 0) return ret; + if (diagnostic_trace) + dev_info(ps->dev, "PMGR read %s offset=%#x value=%#x\n", + genpd->name, ps->offset, reg); + + /* Preserve the explicit firmware handoff when the requested active state + * is already satisfied. Real transitions retain the ordinary sequence. + */ + if (ps->preserve_active_control && pstate == APPLE_PMGR_PS_ACTIVE && + FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == pstate && + FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate && + !(reg & (APPLE_PMGR_RESET | APPLE_PMGR_PS_RESET | + APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_BUSY))) + return 0; + /* Resets are synchronous, and only work if the device is powered and clocked. */ if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE) dev_err_ratelimited(ps->dev, "PS %s: powering off with RESET active\n", @@ -88,7 +111,13 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a if (ps->force_reset) reg_pre |= APPLE_PMGR_PS_RESET; + if (diagnostic_trace) + dev_info(ps->dev, "PMGR pre-write %s offset=%#x value=%#x begin\n", + genpd->name, ps->offset, reg_pre); regmap_write(ps->regmap, ps->offset, reg_pre); + if (diagnostic_trace) + dev_info(ps->dev, "PMGR pre-write %s offset=%#x completed\n", + genpd->name, ps->offset); ret = regmap_read_poll_timeout_atomic( ps->regmap, ps->offset, cur, @@ -110,10 +139,16 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg); + if (diagnostic_trace) + dev_info(ps->dev, "PMGR write %s offset=%#x value=%#x begin\n", + genpd->name, ps->offset, reg); ret = regmap_write(ps->regmap, ps->offset, reg); if (ret && ps->strict_state) return ret; cur = reg; + if (diagnostic_trace) + dev_info(ps->dev, "PMGR write %s offset=%#x completed\n", + genpd->name, ps->offset); if (ps->externally_clocked && pstate == APPLE_PMGR_PS_ACTIVE) { /* @@ -147,7 +182,13 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a reg |= APPLE_PMGR_AUTO_ENABLE; if (ps->strict_state) return regmap_write(ps->regmap, ps->offset, reg); + if (diagnostic_trace) + dev_info(ps->dev, "PMGR auto-write %s offset=%#x value=%#x begin\n", + genpd->name, ps->offset, reg); regmap_write(ps->regmap, ps->offset, reg); + if (diagnostic_trace) + dev_info(ps->dev, "PMGR auto-write %s offset=%#x completed\n", + genpd->name, ps->offset); } return ret; @@ -465,6 +506,9 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) if (of_property_read_bool(node, "apple,externally-clocked")) ps->externally_clocked = true; + ps->preserve_active_control = + of_property_read_bool(node, "apple,preserve-active-control"); + /* A cold-started coprocessor CPU must not be gated behind its firmware. */ ps->no_auto_pm = of_property_read_bool(node, "apple,no-auto-pm"); @@ -512,7 +556,8 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) } /* Turn on auto-PM if the domain is already on, unless it must stay off */ - if (active && !ps->firmware_owned && !ps->inherited_on && !ps->no_auto_enable) + if (active && !ps->firmware_owned && !ps->inherited_on && !ps->no_auto_enable && + !ps->preserve_active_control) regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, ps->no_auto_pm ? 0 : APPLE_PMGR_AUTO_ENABLE); diff --git a/tools/testing/selftests/aurora-apif/pmgr.py b/tools/testing/selftests/aurora-apif/pmgr.py new file mode 100755 index 0000000000000..03caa422318ac --- /dev/null +++ b/tools/testing/selftests/aurora-apif/pmgr.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Exercise the production retained-active PMGR contract with a register test double.""" +from pathlib import Path +import subprocess,tempfile,sys +root=Path(__file__).resolve().parents[4] +p=Path(sys.argv[1]) if len(sys.argv)>1 else root/'drivers/pmdomain/apple/pmgr-pwrstate.c' +s=p.read_text();start=s.index('static int apple_pmgr_ps_set(');brace=s.index('{',start);end=brace+1;depth=1 +while depth: + depth+=(s[end]=='{')-(s[end]=='}');end+=1 +fn=s[start:end] +defines='\n'.join(l for l in s.splitlines() if l.startswith('#define APPLE_PMGR_')) +code=r''' +#include +#include +#include +#include +#include +typedef uint32_t u32; +#define BIT(n) (1U<<(n)) +#define GENMASK(h,l) ((~0U>>(31-(h)))&(~0U<<(l))) +#define FIELD_GET(mask,v) (((v)&(mask))>>__builtin_ctz(mask)) +#define FIELD_PREP(mask,v) (((v)<<__builtin_ctz(mask))&(mask)) +#define dev_info(...) ((void)0) +#define dev_dbg(...) ((void)0) +#define dev_err(...) ((void)0) +#define dev_err_ratelimited(...) ((void)0) +static bool diagnostic_trace; +''' + defines + r''' +struct device {}; +struct generic_pm_domain { const char *name; }; +struct regmap { u32 value; unsigned writes; u32 written[8]; int fail_read; }; +struct apple_pmgr_ps { struct device *dev; struct generic_pm_domain genpd; struct regmap *regmap; u32 offset; bool force_disable,force_reset,externally_clocked,preserve_active_control,strict_state; }; +#define genpd_to_apple_pmgr_ps(g) ((struct apple_pmgr_ps *)((char *)(g)-offsetof(struct apple_pmgr_ps,genpd))) +static int regmap_read(struct regmap *m,u32 offset,u32 *value) { + assert(offset==0x180);if(m->fail_read)return m->fail_read;*value=m->value;return 0; +} +static int regmap_write(struct regmap *m,u32 offset,u32 value) { + assert(offset==0x180&&m->writes<8);m->written[m->writes++]=value; + /* Test double: control transitions reach their requested actual state. */ + m->value=(value&~APPLE_PMGR_PS_ACTUAL)|FIELD_PREP(APPLE_PMGR_PS_ACTUAL,FIELD_GET(APPLE_PMGR_PS_TARGET,value));return 0; +} +#define regmap_read_poll_timeout_atomic(map,offset,cur,condition,delay,timeout) \ + ({ int err=regmap_read(map,offset,&cur);err?err:((condition)?0:-110); }) +''' + fn + r''' +int main(void) { + struct regmap m={.value=0x510302ff}; + struct apple_pmgr_ps ps={.genpd={.name="retained"},.regmap=&m,.offset=0x180,.preserve_active_control=true}; + u32 original=m.value; + assert(!apple_pmgr_ps_set(&ps.genpd,15,true));assert(m.writes==0&&m.value==original); + ps.preserve_active_control=false;m=(struct regmap){.value=original}; + assert(!apple_pmgr_ps_set(&ps.genpd,15,true));assert(m.writes==2); + ps.preserve_active_control=true;m=(struct regmap){.value=0}; + assert(!apple_pmgr_ps_set(&ps.genpd,15,true));assert(m.writes==2); + assert(FIELD_GET(APPLE_PMGR_PS_TARGET,m.written[0])==15); + m=(struct regmap){.value=original}; + assert(!apple_pmgr_ps_set(&ps.genpd,0,false));assert(m.writes==1); + assert(FIELD_GET(APPLE_PMGR_PS_TARGET,m.written[0])==0); + for(unsigned i=0;i<4;i++) { + const u32 invalid[]={APPLE_PMGR_BUSY,APPLE_PMGR_RESET,APPLE_PMGR_PS_RESET,APPLE_PMGR_DEV_DISABLE}; + m=(struct regmap){.value=original|invalid[i]}; + assert(!apple_pmgr_ps_set(&ps.genpd,15,true));assert(m.writes==2); + assert(!(m.written[0]&(APPLE_PMGR_BUSY|APPLE_PMGR_FLAGS|APPLE_PMGR_AUTO_ENABLE))); + } + m=(struct regmap){.value=original,.fail_read=-5}; + assert(apple_pmgr_ps_set(&ps.genpd,15,true)==-5);assert(m.writes==0); + puts("PMGR retained-active and real-transition contracts pass"); +} +''' +with tempfile.TemporaryDirectory(prefix='m5-pmgr-host-') as tmp: + t=Path(tmp);(t/'test.c').write_text(code) + subprocess.run(['cc','-std=gnu11','-fsanitize=address,undefined','-fno-omit-frame-pointer',str(t/'test.c'),'-o',str(t/'test')],check=True) + subprocess.run([str(t/'test')],check=True)