Merge tag 'pmdomain-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm

Pull pmdomain updates from Ulf Hansson:
 "pmdomain core:
   - Allow power-off for out-of-band wakeup-capable devices
   - Drop the redundant call to dev_pm_domain_detach() for the amba bus
   - Extend the genpd governor for CPUs to account for IPIs

  pmdomain providers:
   - bcm: Add support for BCM2712
   - mediatek: Add support for MFlexGraphics power domains
   - mediatek: Add support for MT8196 power domains
   - qcom: Add RPMh power domain support for Kaanapali
   - rockchip: Add support for RV1126B

  pmdomain consumers:
   - usb: dwc3: Enable out of band wakeup for i.MX95
   - usb: chipidea: Enable out of band wakeup for i.MX95"

* tag 'pmdomain-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm: (26 commits)
  pmdomain: Extend the genpd governor for CPUs to account for IPIs
  smp: Introduce a helper function to check for pending IPIs
  pmdomain: mediatek: convert from clk round_rate() to determine_rate()
  amba: bus: Drop dev_pm_domain_detach() call
  pmdomain: bcm: bcm2835-power: Prepare to support BCM2712
  pmdomain: mediatek: mtk-mfg: select MAILBOX in Kconfig
  pmdomain: mediatek: Add support for MFlexGraphics
  pmdomain: mediatek: Fix build-errors
  cpuidle: psci: Replace deprecated strcpy in psci_idle_init_cpu
  pmdomain: rockchip: Add support for RV1126B
  pmdomain: mediatek: Add support for MT8196 HFRPSYS power domains
  pmdomain: mediatek: Add support for MT8196 SCPSYS power domains
  pmdomain: mediatek: Add support for secure HWCCF infra power on
  pmdomain: mediatek: Add support for Hardware Voter power domains
  pmdomain: qcom: rpmhpd: Add RPMh power domain support for Kaanapali
  usb: dwc3: imx8mp: Set out of band wakeup for i.MX95
  usb: chipidea: ci_hdrc_imx: Set out of band wakeup for i.MX95
  usb: chipidea: core: detach power domain for ci_hdrc platform device
  pmdomain: core: Allow power-off for out-of-band wakeup-capable devices
  PM: wakeup: Add out-of-band system wakeup support for devices
  ...
This commit is contained in:
Linus Torvalds
2025-12-04 13:50:39 -08:00
30 changed files with 2417 additions and 60 deletions
@@ -0,0 +1,117 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/mediatek,mt8196-gpufreq.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek MFlexGraphics Power and Frequency Controller
maintainers:
- Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
description:
A special-purpose embedded MCU to control power and frequency of GPU devices
using MediaTek Flexible Graphics integration hardware.
properties:
$nodename:
pattern: '^power-controller@[a-f0-9]+$'
compatible:
enum:
- mediatek,mt8196-gpufreq
reg:
items:
- description: GPR memory area
- description: RPC memory area
- description: SoC variant ID register
reg-names:
items:
- const: gpr
- const: rpc
- const: hw-revision
clocks:
items:
- description: main clock of the embedded controller (EB)
- description: core PLL
- description: stack 0 PLL
- description: stack 1 PLL
clock-names:
items:
- const: eb
- const: core
- const: stack0
- const: stack1
mboxes:
items:
- description: FastDVFS events
- description: frequency control
- description: sleep control
- description: timer control
- description: frequency hopping control
- description: hardware voter control
- description: FastDVFS control
mbox-names:
items:
- const: fast-dvfs-event
- const: gpufreq
- const: sleep
- const: timer
- const: fhctl
- const: ccf
- const: fast-dvfs
memory-region:
items:
- description: phandle to the GPUEB shared memory
"#clock-cells":
const: 1
"#power-domain-cells":
const: 0
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- mboxes
- mbox-names
- memory-region
- "#clock-cells"
- "#power-domain-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mediatek,mt8196-clock.h>
power-controller@4b09fd00 {
compatible = "mediatek,mt8196-gpufreq";
reg = <0x4b09fd00 0x80>,
<0x4b800000 0x1000>,
<0x4b860128 0x4>;
reg-names = "gpr", "rpc", "hw-revision";
clocks = <&topckgen CLK_TOP_MFG_EB>,
<&mfgpll CLK_MFG_AO_MFGPLL>,
<&mfgpll_sc0 CLK_MFGSC0_AO_MFGPLL_SC0>,
<&mfgpll_sc1 CLK_MFGSC1_AO_MFGPLL_SC1>;
clock-names = "eb", "core", "stack0", "stack1";
mboxes = <&gpueb_mbox 0>, <&gpueb_mbox 1>, <&gpueb_mbox 2>,
<&gpueb_mbox 3>, <&gpueb_mbox 4>, <&gpueb_mbox 5>,
<&gpueb_mbox 7>;
mbox-names = "fast-dvfs-event", "gpufreq", "sleep", "timer", "fhctl",
"ccf", "fast-dvfs";
memory-region = <&gpueb_shared_memory>;
#clock-cells = <1>;
#power-domain-cells = <0>;
};
@@ -33,6 +33,9 @@ properties:
- mediatek,mt8188-power-controller
- mediatek,mt8192-power-controller
- mediatek,mt8195-power-controller
- mediatek,mt8196-hwv-hfrp-power-controller
- mediatek,mt8196-hwv-scp-power-controller
- mediatek,mt8196-power-controller
- mediatek,mt8365-power-controller
'#power-domain-cells':
@@ -157,6 +160,7 @@ allOf:
contains:
enum:
- mediatek,mt8183-power-controller
- mediatek,mt8196-power-controller
then:
properties:
access-controllers:
@@ -18,6 +18,7 @@ properties:
oneOf:
- enum:
- qcom,glymur-rpmhpd
- qcom,kaanapali-rpmhpd
- qcom,mdm9607-rpmpd
- qcom,milos-rpmhpd
- qcom,msm8226-rpmpd
@@ -46,6 +46,7 @@ properties:
- rockchip,rk3576-power-controller
- rockchip,rk3588-power-controller
- rockchip,rv1126-power-controller
- rockchip,rv1126b-power-controller
"#power-domain-cells":
const: 1
@@ -126,6 +127,7 @@ $defs:
"include/dt-bindings/power/rk3568-power.h"
"include/dt-bindings/power/rk3588-power.h"
"include/dt-bindings/power/rockchip,rv1126-power.h"
"include/dt-bindings/power/rockchip,rv1126b-power-controller.h"
clocks:
minItems: 1
@@ -13,23 +13,21 @@ description: |
maintainers:
- Nicolas Saenz Julienne <nsaenz@kernel.org>
allOf:
- $ref: /schemas/watchdog/watchdog.yaml#
properties:
compatible:
items:
- enum:
- brcm,bcm2835-pm
- brcm,bcm2711-pm
- brcm,bcm2712-pm
- const: brcm,bcm2835-pm-wdt
reg:
minItems: 2
minItems: 1
maxItems: 3
reg-names:
minItems: 2
minItems: 1
items:
- const: pm
- const: asb
@@ -62,7 +60,35 @@ required:
- reg
- "#power-domain-cells"
- "#reset-cells"
- clocks
allOf:
- $ref: /schemas/watchdog/watchdog.yaml#
- if:
properties:
compatible:
contains:
enum:
- brcm,bcm2835-pm
- brcm,bcm2711-pm
then:
required:
- clocks
properties:
reg:
minItems: 2
reg-names:
minItems: 2
else:
properties:
reg:
maxItems: 1
reg-names:
maxItems: 1
additionalProperties: false
+3 -6
View File
@@ -291,15 +291,14 @@ static int amba_probe(struct device *dev)
if (ret < 0)
break;
ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON);
ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
PD_FLAG_DETACH_POWER_OFF);
if (ret)
break;
ret = amba_get_enable_pclk(pcdev);
if (ret) {
dev_pm_domain_detach(dev, true);
if (ret)
break;
}
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
@@ -314,7 +313,6 @@ static int amba_probe(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
dev_pm_domain_detach(dev, true);
} while (0);
return ret;
@@ -336,7 +334,6 @@ static void amba_remove(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
dev_pm_domain_detach(dev, true);
}
static void amba_shutdown(struct device *dev)
+1
View File
@@ -2148,6 +2148,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
dev->power.wakeup_path = false;
dev->power.out_band_wakeup = false;
if (dev->power.no_pm_callbacks)
goto unlock;
+2 -2
View File
@@ -382,8 +382,8 @@ static int psci_idle_init_cpu(struct device *dev, int cpu)
drv->states[0].exit_latency = 1;
drv->states[0].target_residency = 1;
drv->states[0].power_usage = UINT_MAX;
strcpy(drv->states[0].name, "WFI");
strcpy(drv->states[0].desc, "ARM WFI");
strscpy(drv->states[0].name, "WFI");
strscpy(drv->states[0].desc, "ARM WFI");
/*
* If no DT idle states are detected (ret == 0) let the driver
+13 -4
View File
@@ -79,6 +79,7 @@
#define PM_IMAGE 0x108
#define PM_GRAFX 0x10c
#define PM_PROC 0x110
#define PM_GRAFX_2712 0x304
#define PM_ENAB BIT(12)
#define PM_ISPRSTN BIT(8)
#define PM_H264RSTN BIT(7)
@@ -381,6 +382,9 @@ static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain)
return bcm2835_power_power_on(pd, PM_GRAFX);
case BCM2835_POWER_DOMAIN_GRAFX_V3D:
if (!power->asb)
return bcm2835_asb_power_on(pd, PM_GRAFX_2712,
0, 0, PM_V3DRSTN);
return bcm2835_asb_power_on(pd, PM_GRAFX,
ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
PM_V3DRSTN);
@@ -447,6 +451,9 @@ static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain)
return bcm2835_power_power_off(pd, PM_GRAFX);
case BCM2835_POWER_DOMAIN_GRAFX_V3D:
if (!power->asb)
return bcm2835_asb_power_off(pd, PM_GRAFX_2712,
0, 0, PM_V3DRSTN);
return bcm2835_asb_power_off(pd, PM_GRAFX,
ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
PM_V3DRSTN);
@@ -635,10 +642,12 @@ static int bcm2835_power_probe(struct platform_device *pdev)
power->asb = pm->asb;
power->rpivid_asb = pm->rpivid_asb;
id = readl(power->asb + ASB_AXI_BRDG_ID);
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
dev_err(dev, "ASB register ID returned 0x%08x\n", id);
return -ENODEV;
if (power->asb) {
id = readl(power->asb + ASB_AXI_BRDG_ID);
if (id != BCM2835_BRDG_ID /* "BRDG" */) {
dev_err(dev, "ASB register ID returned 0x%08x\n", id);
return -ENODEV;
}
}
if (power->rpivid_asb) {
+4 -2
View File
@@ -1551,7 +1551,8 @@ static int genpd_finish_suspend(struct device *dev,
if (ret)
return ret;
if (device_awake_path(dev) && genpd_is_active_wakeup(genpd))
if (device_awake_path(dev) && genpd_is_active_wakeup(genpd) &&
!device_out_band_wakeup(dev))
return 0;
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
@@ -1606,7 +1607,8 @@ static int genpd_finish_resume(struct device *dev,
if (IS_ERR(genpd))
return -EINVAL;
if (device_awake_path(dev) && genpd_is_active_wakeup(genpd))
if (device_awake_path(dev) && genpd_is_active_wakeup(genpd) &&
!device_out_band_wakeup(dev))
return resume_noirq(dev);
genpd_lock(genpd);
+13 -7
View File
@@ -408,15 +408,21 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd)
if ((idle_duration_ns >= (genpd->states[i].residency_ns +
genpd->states[i].power_off_latency_ns)) &&
(global_constraint >= (genpd->states[i].power_on_latency_ns +
genpd->states[i].power_off_latency_ns))) {
genpd->state_idx = i;
genpd->gd->last_enter = now;
genpd->gd->reflect_residency = true;
return true;
}
genpd->states[i].power_off_latency_ns)))
break;
} while (--i >= 0);
return false;
if (i < 0)
return false;
if (cpus_peek_for_pending_ipi(genpd->cpus))
return false;
genpd->state_idx = i;
genpd->gd->last_enter = now;
genpd->gd->reflect_residency = true;
return true;
}
static bool cpu_system_power_down_ok(struct dev_pm_domain *pd)
+17
View File
@@ -26,6 +26,23 @@ config MTK_SCPSYS_PM_DOMAINS
Control Processor System (SCPSYS) has several power management related
tasks in the system.
config MTK_MFG_PM_DOMAIN
bool "MediaTek MFlexGraphics power domain"
default ARCH_MEDIATEK
depends on PM
depends on OF
depends on COMMON_CLK
select MAILBOX
select PM_GENERIC_DOMAINS
imply MTK_GPUEB_MBOX
help
Say y or m here to enable the power domains driver for MediaTek
MFlexGraphics. This driver allows for power and frequency control of
GPUs on MediaTek SoCs such as the MT8196 or MT6991.
This driver is required for the Mali GPU to work at all on MT8196 and
MT6991.
config AIROHA_CPU_PM_DOMAIN
tristate "Airoha CPU power domain"
default ARCH_AIROHA
+1
View File
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MTK_MFG_PM_DOMAIN) += mtk-mfg-pmdomain.o
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
obj-$(CONFIG_AIROHA_CPU_PM_DOMAIN) += airoha-cpu-pmdomain.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+278 -28
View File
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2020 Collabora Ltd.
*/
#include <linux/arm-smccc.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
@@ -15,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soc/mediatek/infracfg.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
#include "mt6735-pm-domains.h"
#include "mt6795-pm-domains.h"
@@ -26,11 +28,18 @@
#include "mt8188-pm-domains.h"
#include "mt8192-pm-domains.h"
#include "mt8195-pm-domains.h"
#include "mt8196-pm-domains.h"
#include "mt8365-pm-domains.h"
#define MTK_POLL_DELAY_US 10
#define MTK_POLL_TIMEOUT USEC_PER_SEC
#define MTK_HWV_POLL_DELAY_US 5
#define MTK_HWV_POLL_TIMEOUT (300 * USEC_PER_MSEC)
#define MTK_HWV_PREPARE_DELAY_US 1
#define MTK_HWV_PREPARE_TIMEOUT (3 * USEC_PER_MSEC)
#define PWR_RST_B_BIT BIT(0)
#define PWR_ISO_BIT BIT(1)
#define PWR_ON_BIT BIT(2)
@@ -45,9 +54,12 @@
#define PWR_RTFF_SAVE_FLAG BIT(27)
#define PWR_RTFF_UFS_CLK_DIS BIT(28)
#define MTK_SIP_KERNEL_HWCCF_CONTROL MTK_SIP_SMC_CMD(0x540)
struct scpsys_domain {
struct generic_pm_domain genpd;
const struct scpsys_domain_data *data;
const struct scpsys_hwv_domain_data *hwv_data;
struct scpsys *scpsys;
int num_clks;
struct clk_bulk_data *clks;
@@ -71,18 +83,56 @@ struct scpsys {
static bool scpsys_domain_is_on(struct scpsys_domain *pd)
{
struct scpsys *scpsys = pd->scpsys;
u32 status, status2;
u32 mask = pd->data->sta_mask;
u32 status, status2, mask2;
mask2 = pd->data->sta2nd_mask ? pd->data->sta2nd_mask : mask;
regmap_read(scpsys->base, pd->data->pwr_sta_offs, &status);
status &= pd->data->sta_mask;
status &= mask;
regmap_read(scpsys->base, pd->data->pwr_sta2nd_offs, &status2);
status2 &= pd->data->sta_mask;
status2 &= mask2;
/* A domain is on when both status bits are set. */
return status && status2;
}
static bool scpsys_hwv_domain_is_disable_done(struct scpsys_domain *pd)
{
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
u32 regs[2] = { hwv->done, hwv->clr_sta };
u32 val[2];
u32 mask = BIT(hwv->setclr_bit);
regmap_multi_reg_read(pd->scpsys->base, regs, val, 2);
/* Disable is done when the bit is set in DONE, cleared in CLR_STA */
return (val[0] & mask) && !(val[1] & mask);
}
static bool scpsys_hwv_domain_is_enable_done(struct scpsys_domain *pd)
{
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
u32 regs[3] = { hwv->done, hwv->en, hwv->set_sta };
u32 val[3];
u32 mask = BIT(hwv->setclr_bit);
regmap_multi_reg_read(pd->scpsys->base, regs, val, 3);
/* Enable is done when the bit is set in DONE and EN, cleared in SET_STA */
return (val[0] & mask) && (val[1] & mask) && !(val[2] & mask);
}
static int scpsys_sec_infra_power_on(bool on)
{
struct arm_smccc_res res;
unsigned long cmd = on ? 1 : 0;
arm_smccc_smc(MTK_SIP_KERNEL_HWCCF_CONTROL, cmd, 0, 0, 0, 0, 0, 0, &res);
return res.a0;
}
static int scpsys_sram_enable(struct scpsys_domain *pd)
{
u32 expected_ack, pdn_ack = pd->data->sram_pdn_ack_bits;
@@ -250,6 +300,161 @@ static int scpsys_regulator_disable(struct regulator *supply)
return supply ? regulator_disable(supply) : 0;
}
static int scpsys_hwv_power_on(struct generic_pm_domain *genpd)
{
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
struct scpsys *scpsys = pd->scpsys;
u32 val;
int ret;
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL)) {
ret = scpsys_sec_infra_power_on(true);
if (ret)
return ret;
}
ret = scpsys_regulator_enable(pd->supply);
if (ret)
goto err_infra;
ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
if (ret)
goto err_reg;
/* For HWV the subsys clocks refer to the HWV low power subsystem */
ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks);
if (ret)
goto err_disable_clks;
/* Make sure the HW Voter is idle and able to accept commands */
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val,
val & BIT(hwv->setclr_bit),
MTK_HWV_POLL_DELAY_US,
MTK_HWV_POLL_TIMEOUT);
if (ret) {
dev_err(scpsys->dev, "Failed to power on: HW Voter busy.\n");
goto err_disable_subsys_clks;
}
/*
* Instruct the HWV to power on the MTCMOS (power domain): after that,
* the same bit will be unset immediately by the hardware.
*/
regmap_write(scpsys->base, hwv->set, BIT(hwv->setclr_bit));
/*
* Wait until the HWV sets the bit again, signalling that its internal
* state machine was started and it now processing the vote command.
*/
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->set, val,
val & BIT(hwv->setclr_bit),
MTK_HWV_PREPARE_DELAY_US,
MTK_HWV_PREPARE_TIMEOUT);
if (ret) {
dev_err(scpsys->dev, "Failed to power on: HW Voter not starting.\n");
goto err_disable_subsys_clks;
}
/* Wait for ACK, signalling that the MTCMOS was enabled */
ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_enable_done, pd, val, val,
MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT);
if (ret) {
dev_err(scpsys->dev, "Failed to power on: HW Voter ACK timeout.\n");
goto err_disable_subsys_clks;
}
/* It's done! Disable the HWV low power subsystem clocks */
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL))
scpsys_sec_infra_power_on(false);
return 0;
err_disable_subsys_clks:
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
err_disable_clks:
clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
err_reg:
scpsys_regulator_disable(pd->supply);
err_infra:
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL))
scpsys_sec_infra_power_on(false);
return ret;
};
static int scpsys_hwv_power_off(struct generic_pm_domain *genpd)
{
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
struct scpsys *scpsys = pd->scpsys;
u32 val;
int ret;
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL)) {
ret = scpsys_sec_infra_power_on(true);
if (ret)
return ret;
}
ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks);
if (ret)
goto err_infra;
/* Make sure the HW Voter is idle and able to accept commands */
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val,
val & BIT(hwv->setclr_bit),
MTK_HWV_POLL_DELAY_US,
MTK_HWV_POLL_TIMEOUT);
if (ret)
goto err_disable_subsys_clks;
/*
* Instruct the HWV to power off the MTCMOS (power domain): differently
* from poweron, the bit will be kept set.
*/
regmap_write(scpsys->base, hwv->clr, BIT(hwv->setclr_bit));
/*
* Wait until the HWV clears the bit, signalling that its internal
* state machine was started and it now processing the clear command.
*/
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->clr, val,
!(val & BIT(hwv->setclr_bit)),
MTK_HWV_PREPARE_DELAY_US,
MTK_HWV_PREPARE_TIMEOUT);
if (ret)
goto err_disable_subsys_clks;
/* Poweroff needs 100us for the HW to stabilize */
udelay(100);
/* Wait for ACK, signalling that the MTCMOS was disabled */
ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_disable_done, pd, val, val,
MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT);
if (ret)
goto err_disable_subsys_clks;
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
scpsys_regulator_disable(pd->supply);
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL))
scpsys_sec_infra_power_on(false);
return 0;
err_disable_subsys_clks:
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
err_infra:
if (MTK_SCPD_CAPS(pd, MTK_SCPD_INFRA_PWR_CTL))
scpsys_sec_infra_power_on(false);
return ret;
};
static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd)
{
struct scpsys *scpsys = pd->scpsys;
@@ -514,6 +719,7 @@ static struct
generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node)
{
const struct scpsys_domain_data *domain_data;
const struct scpsys_hwv_domain_data *hwv_domain_data;
struct scpsys_domain *pd;
struct property *prop;
const char *clk_name;
@@ -529,14 +735,33 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
return ERR_PTR(-EINVAL);
}
if (id >= scpsys->soc_data->num_domains) {
dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id);
return ERR_PTR(-EINVAL);
}
switch (scpsys->soc_data->type) {
case SCPSYS_MTCMOS_TYPE_DIRECT_CTL:
if (id >= scpsys->soc_data->num_domains) {
dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id);
return ERR_PTR(-EINVAL);
}
domain_data = &scpsys->soc_data->domains_data[id];
if (domain_data->sta_mask == 0) {
dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id);
domain_data = &scpsys->soc_data->domains_data[id];
hwv_domain_data = NULL;
if (domain_data->sta_mask == 0) {
dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id);
return ERR_PTR(-EINVAL);
}
break;
case SCPSYS_MTCMOS_TYPE_HW_VOTER:
if (id >= scpsys->soc_data->num_hwv_domains) {
dev_err(scpsys->dev, "%pOF: invalid HWV domain id %d\n", node, id);
return ERR_PTR(-EINVAL);
}
domain_data = NULL;
hwv_domain_data = &scpsys->soc_data->hwv_domains_data[id];
break;
default:
return ERR_PTR(-EINVAL);
}
@@ -545,6 +770,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
return ERR_PTR(-ENOMEM);
pd->data = domain_data;
pd->hwv_data = hwv_domain_data;
pd->scpsys = scpsys;
if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) {
@@ -604,6 +830,31 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
pd->subsys_clks[i].clk = clk;
}
if (scpsys->domains[id]) {
ret = -EINVAL;
dev_err(scpsys->dev,
"power domain with id %d already exists, check your device-tree\n", id);
goto err_put_subsys_clocks;
}
if (pd->data && pd->data->name)
pd->genpd.name = pd->data->name;
else if (pd->hwv_data && pd->hwv_data->name)
pd->genpd.name = pd->hwv_data->name;
else
pd->genpd.name = node->name;
if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_DIRECT_CTL) {
pd->genpd.power_off = scpsys_power_off;
pd->genpd.power_on = scpsys_power_on;
} else {
pd->genpd.power_off = scpsys_hwv_power_off;
pd->genpd.power_on = scpsys_hwv_power_on;
/* HW-Voter code can be invoked in atomic context */
pd->genpd.flags |= GENPD_FLAG_IRQ_SAFE;
}
/*
* Initially turn on all domains to make the domains usable
* with !CONFIG_PM and to get the hardware in sync with the
@@ -615,7 +866,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
dev_warn(scpsys->dev,
"%pOF: A default off power domain has been ON\n", node);
} else {
ret = scpsys_power_on(&pd->genpd);
ret = pd->genpd.power_on(&pd->genpd);
if (ret < 0) {
dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret);
goto err_put_subsys_clocks;
@@ -625,21 +876,6 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
}
if (scpsys->domains[id]) {
ret = -EINVAL;
dev_err(scpsys->dev,
"power domain with id %d already exists, check your device-tree\n", id);
goto err_put_subsys_clocks;
}
if (!pd->data->name)
pd->genpd.name = node->name;
else
pd->genpd.name = pd->data->name;
pd->genpd.power_off = scpsys_power_off;
pd->genpd.power_on = scpsys_power_on;
if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP))
pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
@@ -931,6 +1167,18 @@ static const struct of_device_id scpsys_of_match[] = {
.compatible = "mediatek,mt8195-power-controller",
.data = &mt8195_scpsys_data,
},
{
.compatible = "mediatek,mt8196-power-controller",
.data = &mt8196_scpsys_data,
},
{
.compatible = "mediatek,mt8196-hwv-hfrp-power-controller",
.data = &mt8196_hfrpsys_hwv_data,
},
{
.compatible = "mediatek,mt8196-hwv-scp-power-controller",
.data = &mt8196_scpsys_hwv_data,
},
{
.compatible = "mediatek,mt8365-power-controller",
.data = &mt8365_scpsys_data,
@@ -946,7 +1194,7 @@ static int scpsys_probe(struct platform_device *pdev)
struct device_node *node;
struct device *parent;
struct scpsys *scpsys;
int ret;
int num_domains, ret;
soc = of_device_get_match_data(&pdev->dev);
if (!soc) {
@@ -954,7 +1202,9 @@ static int scpsys_probe(struct platform_device *pdev)
return -EINVAL;
}
scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL);
num_domains = soc->num_domains + soc->num_hwv_domains;
scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, num_domains), GFP_KERNEL);
if (!scpsys)
return -ENOMEM;
+48 -1
View File
@@ -16,7 +16,10 @@
#define MTK_SCPD_SRAM_PDN_INVERTED BIT(9)
#define MTK_SCPD_MODEM_PWRSEQ BIT(10)
#define MTK_SCPD_SKIP_RESET_B BIT(11)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x))
#define MTK_SCPD_INFRA_PWR_CTL BIT(12)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data ? \
(_scpd)->data->caps & (_x) : \
(_scpd)->hwv_data->caps & (_x))
#define SPM_VDE_PWR_CON 0x0210
#define SPM_MFG_PWR_CON 0x0214
@@ -59,6 +62,7 @@ enum scpsys_bus_prot_block {
BUS_PROT_BLOCK_INFRA,
BUS_PROT_BLOCK_INFRA_NAO,
BUS_PROT_BLOCK_SMI,
BUS_PROT_BLOCK_SPM,
BUS_PROT_BLOCK_COUNT,
};
@@ -124,10 +128,23 @@ enum scpsys_rtff_type {
SCPSYS_RTFF_TYPE_MAX
};
/**
* enum scpsys_mtcmos_type - Type of power domain controller
* @SCPSYS_MTCMOS_TYPE_DIRECT_CTL: Power domains are controlled with direct access
* @SCPSYS_MTCMOS_TYPE_HW_VOTER: Hardware-assisted voted power domain control
* @SCPSYS_MTCMOS_TYPE_MAX: Number of supported power domain types
*/
enum scpsys_mtcmos_type {
SCPSYS_MTCMOS_TYPE_DIRECT_CTL = 0,
SCPSYS_MTCMOS_TYPE_HW_VOTER,
SCPSYS_MTCMOS_TYPE_MAX
};
/**
* struct scpsys_domain_data - scp domain data for power on/off flow
* @name: The name of the power domain.
* @sta_mask: The mask for power on/off status bit.
* @sta2nd_mask: The mask for second power on/off status bit.
* @ctl_offs: The offset for main power control register.
* @sram_pdn_bits: The mask for sram power control bits.
* @sram_pdn_ack_bits: The mask for sram power control acked bits.
@@ -140,6 +157,7 @@ enum scpsys_rtff_type {
struct scpsys_domain_data {
const char *name;
u32 sta_mask;
u32 sta2nd_mask;
int ctl_offs;
u32 sram_pdn_bits;
u32 sram_pdn_ack_bits;
@@ -152,11 +170,40 @@ struct scpsys_domain_data {
int pwr_sta2nd_offs;
};
/**
* struct scpsys_hwv_domain_data - Hardware Voter power domain data
* @name: Name of the power domain
* @set: Offset of the HWV SET register
* @clr: Offset of the HWV CLEAR register
* @done: Offset of the HWV DONE register
* @en: Offset of the HWV ENABLE register
* @set_sta: Offset of the HWV SET STATUS register
* @clr_sta: Offset of the HWV CLEAR STATUS register
* @setclr_bit: The SET/CLR bit to enable/disable the power domain
* @sta_bit: The SET/CLR STA bit to check for on/off ACK
* @caps: The flag for active wake-up action
*/
struct scpsys_hwv_domain_data {
const char *name;
u16 set;
u16 clr;
u16 done;
u16 en;
u16 set_sta;
u16 clr_sta;
u8 setclr_bit;
u8 sta_bit;
u16 caps;
};
struct scpsys_soc_data {
const struct scpsys_domain_data *domains_data;
int num_domains;
const struct scpsys_hwv_domain_data *hwv_domains_data;
int num_hwv_domains;
enum scpsys_bus_prot_block *bus_prot_blocks;
int num_bus_prot_blocks;
enum scpsys_mtcmos_type type;
};
#endif /* __SOC_MEDIATEK_MTK_PM_DOMAINS_H */
+27 -1
View File
@@ -19,7 +19,7 @@
#define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd)
#define RPMH_ARC_MAX_LEVELS 16
#define RPMH_ARC_MAX_LEVELS 32
/**
* struct rpmhpd - top level RPMh power domain resource data structure
@@ -595,6 +595,31 @@ static const struct rpmhpd_desc sm8750_desc = {
.num_pds = ARRAY_SIZE(sm8750_rpmhpds),
};
/* KAANAPALI RPMH powerdomains */
static struct rpmhpd *kaanapali_rpmhpds[] = {
[RPMHPD_CX] = &cx,
[RPMHPD_CX_AO] = &cx_ao,
[RPMHPD_EBI] = &ebi,
[RPMHPD_GFX] = &gfx,
[RPMHPD_GMXC] = &gmxc,
[RPMHPD_LCX] = &lcx,
[RPMHPD_LMX] = &lmx,
[RPMHPD_MX] = &mx,
[RPMHPD_MX_AO] = &mx_ao,
[RPMHPD_MMCX] = &mmcx,
[RPMHPD_MMCX_AO] = &mmcx_ao,
[RPMHPD_MSS] = &mss,
[RPMHPD_MXC] = &mxc,
[RPMHPD_MXC_AO] = &mxc_ao,
[RPMHPD_NSP] = &nsp,
[RPMHPD_NSP2] = &nsp2,
};
static const struct rpmhpd_desc kaanapali_desc = {
.rpmhpds = kaanapali_rpmhpds,
.num_pds = ARRAY_SIZE(kaanapali_rpmhpds),
};
/* QDU1000/QRU1000 RPMH powerdomains */
static struct rpmhpd *qdu1000_rpmhpds[] = {
[QDU1000_CX] = &cx,
@@ -767,6 +792,7 @@ static const struct rpmhpd_desc qcs615_desc = {
static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,glymur-rpmhpd", .data = &glymur_desc },
{ .compatible = "qcom,kaanapali-rpmhpd", .data = &kaanapali_desc },
{ .compatible = "qcom,milos-rpmhpd", .data = &milos_desc },
{ .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc },
{ .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc },
+41
View File
@@ -25,6 +25,7 @@
#include <soc/rockchip/rockchip_sip.h>
#include <dt-bindings/power/px30-power.h>
#include <dt-bindings/power/rockchip,rv1126-power.h>
#include <dt-bindings/power/rockchip,rv1126b-power-controller.h>
#include <dt-bindings/power/rk3036-power.h>
#include <dt-bindings/power/rk3066-power.h>
#include <dt-bindings/power/rk3128-power.h>
@@ -137,6 +138,20 @@ struct rockchip_pmu {
.active_wakeup = wakeup, \
}
#define DOMAIN_M_G(_name, pwr, status, req, idle, ack, g_mask, wakeup, keepon) \
{ \
.name = _name, \
.pwr_w_mask = (pwr) << 16, \
.pwr_mask = (pwr), \
.status_mask = (status), \
.req_w_mask = (req) << 16, \
.req_mask = (req), \
.idle_mask = (idle), \
.ack_mask = (ack), \
.clk_ungate_mask = (g_mask), \
.active_wakeup = wakeup, \
}
#define DOMAIN_M_G_SD(_name, pwr, status, req, idle, ack, g_mask, mem, wakeup, keepon) \
{ \
.name = _name, \
@@ -205,6 +220,9 @@ struct rockchip_pmu {
#define DOMAIN_RV1126(name, pwr, req, idle, wakeup) \
DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup)
#define DOMAIN_RV1126B(name, pwr, req, wakeup) \
DOMAIN_M_G(name, pwr, pwr, req, req, req, req, wakeup, true)
#define DOMAIN_RK3288(name, pwr, status, req, wakeup) \
DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup)
@@ -1104,6 +1122,13 @@ static const struct rockchip_domain_info rv1126_pm_domains[] = {
[RV1126_PD_USB] = DOMAIN_RV1126("usb", BIT(9), BIT(15), BIT(15), false),
};
static const struct rockchip_domain_info rv1126b_pm_domains[] = {
/* name pwr req wakeup */
[RV1126B_PD_NPU] = DOMAIN_RV1126B("npu", BIT(0), BIT(8), false),
[RV1126B_PD_VDO] = DOMAIN_RV1126B("vdo", BIT(1), BIT(9), false),
[RV1126B_PD_AIISP] = DOMAIN_RV1126B("aiisp", BIT(2), BIT(10), false),
};
static const struct rockchip_domain_info rk3036_pm_domains[] = {
[RK3036_PD_MSCH] = DOMAIN_RK3036("msch", BIT(14), BIT(23), BIT(30), true),
[RK3036_PD_CORE] = DOMAIN_RK3036("core", BIT(13), BIT(17), BIT(24), false),
@@ -1516,6 +1541,18 @@ static const struct rockchip_pmu_info rv1126_pmu = {
.domain_info = rv1126_pm_domains,
};
static const struct rockchip_pmu_info rv1126b_pmu = {
.pwr_offset = 0x210,
.status_offset = 0x230,
.req_offset = 0x110,
.idle_offset = 0x128,
.ack_offset = 0x120,
.clk_ungate_offset = 0x140,
.num_domains = ARRAY_SIZE(rv1126b_pm_domains),
.domain_info = rv1126b_pm_domains,
};
static const struct of_device_id rockchip_pm_domain_dt_match[] = {
{
.compatible = "rockchip,px30-power-controller",
@@ -1585,6 +1622,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = {
.compatible = "rockchip,rv1126-power-controller",
.data = (void *)&rv1126_pmu,
},
{
.compatible = "rockchip,rv1126b-power-controller",
.data = (void *)&rv1126b_pmu,
},
{ /* sentinel */ },
};
+10 -1
View File
@@ -79,6 +79,10 @@ static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
CI_HDRC_HAS_PORTSC_PEC_MISSED,
};
static const struct ci_hdrc_imx_platform_flag imx95_usb_data = {
.flags = CI_HDRC_SUPPORTS_RUNTIME_PM | CI_HDRC_OUT_BAND_WAKEUP,
};
static const struct ci_hdrc_imx_platform_flag s32g_usb_data = {
.flags = CI_HDRC_DISABLE_HOST_STREAMING,
};
@@ -94,6 +98,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
{ .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data},
{ .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data},
{ .compatible = "fsl,imx95-usb", .data = &imx95_usb_data},
{ .compatible = "nxp,s32g2-usb", .data = &s32g_usb_data},
{ /* sentinel */ }
};
@@ -704,9 +709,13 @@ static int ci_hdrc_imx_suspend(struct device *dev)
pinctrl_pm_select_sleep_state(dev);
if (data->wakeup_irq > 0 && device_may_wakeup(dev))
if (data->wakeup_irq > 0 && device_may_wakeup(dev)) {
enable_irq_wake(data->wakeup_irq);
if (data->plat_data->flags & CI_HDRC_OUT_BAND_WAKEUP)
device_set_out_band_wakeup(dev);
}
return ret;
}

Some files were not shown because too many files have changed in this diff Show More