All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15
@ 2021-07-01 23:26 Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing Dmitry Osipenko
                   ` (37 more replies)
  0 siblings, 38 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Hello,

This series continues the work on enabling voltage scaling of the core
power domain for NVIDIA Tegra SoCs. All the previous grounding work around
improving OPP API and adding the initial core power domain driver is now
landing into 5.14 kernel and already available in linux-next.

The biggest challenge left so far is the power management of the PLLs
and system clocks which don't have a dedicated hardware unit. I made a lot
of changes around it by going back to a variant where individual device
drivers are responsible for the power management in oppose to a recent
variant where the most of power management was done by the clk controller
driver. This allowed to drop the need of having a special lockdep
annotation for the power domain's mutex, which Ulf Hansson didn't like
previously. We don't have situations of a nested GENPD accesses anymore.
It also allowed to minimize the clk patch a lot. Now clk driver manages
power of the internal clocks only.

This series enables a full power management for Tegra20 and Tegra30 SoCs.
It doesn't include all the device-tree binding patches. I'll try to split
this series into a smaller parts that could be applied individually. For
now I want to get a feedback at least on the first six patches.

Example /sys/kernel/debug/pm_genpd/pm_genpd_summary from Tegra20 Acer A500:

domain                          status          children                           performance
    /device                                             runtime status
----------------------------------------------------------------------------------------------
mpe                             off-0                                              0
    /devices/soc0/50000000.host1x/54040000.mpe          suspended                  0
vdec                            off-0                                              0
    /devices/soc0/6001a000.vde                          suspended                  0
venc                            off-0                                              0
    /devices/soc0/50000000.host1x/54080000.vi           suspended                  0
    /devices/soc0/50000000.host1x/54100000.isp          suspended                  0
3d                              off-0                                              0
    /devices/soc0/50000000.host1x/54180000.gr3d         suspended                  0
core-domain                     on                                                 1100000
                                                3d, venc, vdec, mpe
    /devices/soc0/50000000.host1x                       active                     1100000
    /devices/soc0/50000000.host1x/540c0000.epp          suspended                  0
    /devices/soc0/50000000.host1x/54140000.gr2d         suspended                  0
    /devices/soc0/50000000.host1x/54280000.hdmi         suspended                  0
    /devices/soc0/c5000000.usb                          active                     1100000
    /devices/soc0/c5008000.usb                          active                     1100000
    /devices/soc0/c8000000.mmc                          active                     1000000
    /devices/soc0/c8000400.mmc                          active                     1000000
    /devices/soc0/c8000600.mmc                          active                     1000000
    /devices/soc0/7000f400.memory-controller            active                     1000000
    /devices/platform/tegra_clk_sclk                    active                     950000
    /devices/soc0/50000000.host1x/54200000.dc           active                     950000
    /devices/soc0/50000000.host1x/54240000.dc           suspended                  0


Example /sys/kernel/debug/pm_genpd/pm_genpd_summary from Tegra30 Nexus 7:

domain                          status          children                           performance
    /device                                             runtime status
----------------------------------------------------------------------------------------------
heg                             on                                                 1000000
    /devices/soc0/50000000.host1x                       active                     1000000
    /devices/soc0/50000000.host1x/540c0000.epp          suspended                  0
    /devices/soc0/50000000.host1x/54140000.gr2d         suspended                  0
mpe                             off-0                                              0
    /devices/soc0/50000000.host1x/54040000.mpe          suspended                  0
vdec                            off-0                                              0
    /devices/soc0/6001a000.vde                          suspended                  0
venc                            off-0                                              0
    /devices/soc0/50000000.host1x/54080000.vi           suspended                  0
    /devices/soc0/50000000.host1x/54100000.isp          suspended                  0
3d1                             off-0                                              0
    /devices/genpd:1:54180000.gr3d                      suspended                  0
3d0                             off-0                                              0
    /devices/genpd:0:54180000.gr3d                      suspended                  0
core-domain                     on                                                 1000000
                                                3d0, 3d1, venc, vdec, mpe, heg
    /devices/soc0/7d000000.usb                          active                     1000000
    /devices/soc0/78000400.mmc                          active                     950000
    /devices/soc0/7000f400.memory-controller            active                     1000000
    /devices/soc0/7000a000.pwm                          active                     1000000
    /devices/platform/tegra_clk_pll_c                   active                     1000000
    /devices/platform/tegra_clk_pll_e                   suspended                  0
    /devices/platform/tegra_clk_pll_m                   active                     1000000
    /devices/platform/tegra_clk_sclk                    active                     1000000
    /devices/soc0/7000f800.fuse                         suspended                  0
    /devices/soc0/50000000.host1x/54240000.dc           suspended                  0
    /devices/soc0/50000000.host1x/54200000.dc           active                     1000000

Dmitry Osipenko (37):
  soc/tegra: pmc: Temporarily disable PMC state syncing
  soc/tegra: pmc: Implement attach_dev() of power domain drivers
  soc/tegra: Don't print error message when OPPs not available
  soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple()
  dt-bindings: clock: tegra-car: Document new tegra-clocks node
  clk: tegra: Support runtime PM and power domain
  dt-bindings: host1x: Document OPP and power domain properties
  dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and
    GR3D
  gpu: host1x: Add host1x_channel_stop()
  gpu: host1x: Add runtime PM support
  gpu: host1x: Add stub driver for MPE, VI, EPP and ISP
  drm/tegra: dc: Support OPP and SoC core voltage scaling
  drm/tegra: hdmi: Add OPP support
  drm/tegra: gr2d: Support OPP and power management
  drm/tegra: gr3d: Support OPP and power management
  drm/tegra: vic: Stop channel before suspending
  usb: chipidea: tegra: Add runtime PM support
  bus: tegra-gmi: Add runtime PM support
  pwm: tegra: Add runtime PM and OPP support
  mmc: sdhci-tegra: Add runtime PM and OPP support
  mtd: rawnand: tegra: Add runtime PM support
  soc/tegra: fuse: Clear fuse->clk on driver probe failure
  soc/tegra: fuse: Add runtime PM support
  soc/tegra: fuse: Enable fuse clock on suspend
  clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock
  spi: tegra20-slink: Improve runtime PM usage
  spi: tegra20-slink: Add OPP support
  memory: tegra20-emc: Add minimal runtime PM support
  memory: tegra30-emc: Add minimal runtime PM support
  media: dt: bindings: tegra-vde: Convert to schema
  media: dt: bindings: tegra-vde: Document OPP and power domain
  media: staging: tegra-vde: Support generic power domain
  ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees
  ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees
  ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x
  ARM: tegra: Add Memory Client resets to Tegra30 GR2D, GR3D and Host1x
  soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30

 .../bindings/clock/nvidia,tegra20-car.yaml    |   51 +
 .../display/tegra/nvidia,tegra20-host1x.txt   |   53 +
 .../bindings/media/nvidia,tegra-vde.txt       |   64 -
 .../bindings/media/nvidia,tegra-vde.yaml      |  119 ++
 .../boot/dts/tegra20-acer-a500-picasso.dts    |    1 +
 arch/arm/boot/dts/tegra20-colibri.dtsi        |    3 +-
 arch/arm/boot/dts/tegra20-harmony.dts         |    3 +-
 arch/arm/boot/dts/tegra20-paz00.dts           |    1 +
 .../arm/boot/dts/tegra20-peripherals-opp.dtsi |  941 +++++++++++
 arch/arm/boot/dts/tegra20-seaboard.dts        |    3 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi       |    3 +-
 arch/arm/boot/dts/tegra20-trimslice.dts       |    9 +
 arch/arm/boot/dts/tegra20-ventana.dts         |    1 +
 arch/arm/boot/dts/tegra20.dtsi                |  115 +-
 .../tegra30-asus-nexus7-grouper-common.dtsi   |    1 +
 arch/arm/boot/dts/tegra30-beaver.dts          |    1 +
 arch/arm/boot/dts/tegra30-cardhu.dtsi         |    1 +
 arch/arm/boot/dts/tegra30-colibri.dtsi        |   17 +-
 arch/arm/boot/dts/tegra30-ouya.dts            |    1 +
 .../arm/boot/dts/tegra30-peripherals-opp.dtsi | 1412 +++++++++++++++++
 arch/arm/boot/dts/tegra30.dtsi                |  170 +-
 drivers/bus/tegra-gmi.c                       |   44 +-
 drivers/clk/tegra/Makefile                    |    1 +
 drivers/clk/tegra/clk-device.c                |  222 +++
 drivers/clk/tegra/clk-pll.c                   |    2 +-
 drivers/clk/tegra/clk-super.c                 |    2 +-
 drivers/clk/tegra/clk-tegra-periph.c          |    6 +-
 drivers/clk/tegra/clk-tegra20.c               |   39 +-
 drivers/clk/tegra/clk-tegra30.c               |   70 +-
 drivers/clk/tegra/clk.c                       |   66 +
 drivers/clk/tegra/clk.h                       |    2 +
 drivers/gpu/drm/tegra/dc.c                    |   69 +
 drivers/gpu/drm/tegra/dc.h                    |    2 +
 drivers/gpu/drm/tegra/gr2d.c                  |  156 +-
 drivers/gpu/drm/tegra/gr3d.c                  |  401 ++++-
 drivers/gpu/drm/tegra/hdmi.c                  |    9 +-
 drivers/gpu/drm/tegra/vic.c                   |   15 +
 drivers/gpu/host1x/channel.c                  |    8 +
 drivers/gpu/host1x/debug.c                    |   15 +
 drivers/gpu/host1x/dev.c                      |  184 ++-
 drivers/gpu/host1x/dev.h                      |    3 +-
 drivers/gpu/host1x/hw/channel_hw.c            |   44 +-
 drivers/gpu/host1x/intr.c                     |    3 -
 drivers/gpu/host1x/syncpt.c                   |    5 +-
 drivers/memory/tegra/tegra20-emc.c            |   31 +
 drivers/memory/tegra/tegra30-emc.c            |   31 +
 drivers/mmc/host/sdhci-tegra.c                |   81 +-
 drivers/mtd/nand/raw/tegra_nand.c             |   44 +-
 drivers/pwm/pwm-tegra.c                       |  104 +-
 drivers/soc/tegra/common.c                    |    4 +-
 drivers/soc/tegra/fuse/fuse-tegra.c           |   62 +
 drivers/soc/tegra/fuse/fuse-tegra20.c         |   10 +-
 drivers/soc/tegra/fuse/fuse-tegra30.c         |   10 +-
 drivers/soc/tegra/fuse/fuse.h                 |    2 +
 drivers/soc/tegra/pmc.c                       |  164 ++
 drivers/spi/spi-tegra20-slink.c               |   76 +-
 drivers/staging/media/tegra-vde/vde.c         |   59 +-
 drivers/usb/chipidea/ci_hdrc_tegra.c          |   50 +-
 include/linux/host1x.h                        |    1 +
 include/soc/tegra/common.h                    |   13 +
 60 files changed, 4710 insertions(+), 370 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
 create mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
 create mode 100644 drivers/clk/tegra/clk-device.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-08-12 16:02   ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers Dmitry Osipenko
                   ` (36 subsequent siblings)
  37 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Disable PMC state syncing in order to ensure that we won't break older
kernels once device-trees will be updated with the addition of the power
domains. Previously this was unnecessary because the plan was to make clk
device that will attach to the domain for each clock, but the plan changed
and now we're going make a better GENPD implementation that will require
to update each device driver with the runtime PM and OPP support before
we could safely enable the state syncing.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/pmc.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index ea62f84d1c8b..f63dfb2ca3f9 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -360,6 +360,7 @@ struct tegra_pmc_soc {
 	unsigned int num_pmc_clks;
 	bool has_blink_output;
 	bool has_usb_sleepwalk;
+	bool supports_core_domain;
 };
 
 /**
@@ -3029,6 +3030,7 @@ static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
 }
 
 static const struct tegra_pmc_soc tegra20_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = ARRAY_SIZE(tegra20_powergates),
 	.powergates = tegra20_powergates,
 	.num_cpu_powergates = 0,
@@ -3089,6 +3091,7 @@ static const char * const tegra30_reset_sources[] = {
 };
 
 static const struct tegra_pmc_soc tegra30_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = ARRAY_SIZE(tegra30_powergates),
 	.powergates = tegra30_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
@@ -3145,6 +3148,7 @@ static const u8 tegra114_cpu_powergates[] = {
 };
 
 static const struct tegra_pmc_soc tegra114_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = ARRAY_SIZE(tegra114_powergates),
 	.powergates = tegra114_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates),
@@ -3261,6 +3265,7 @@ static const struct pinctrl_pin_desc tegra124_pin_descs[] = {
 };
 
 static const struct tegra_pmc_soc tegra124_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = ARRAY_SIZE(tegra124_powergates),
 	.powergates = tegra124_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates),
@@ -3386,6 +3391,7 @@ static const struct tegra_wake_event tegra210_wake_events[] = {
 };
 
 static const struct tegra_pmc_soc tegra210_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = ARRAY_SIZE(tegra210_powergates),
 	.powergates = tegra210_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
@@ -3543,6 +3549,7 @@ static const struct tegra_wake_event tegra186_wake_events[] = {
 };
 
 static const struct tegra_pmc_soc tegra186_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = 0,
 	.powergates = NULL,
 	.num_cpu_powergates = 0,
@@ -3677,6 +3684,7 @@ static const struct tegra_wake_event tegra194_wake_events[] = {
 };
 
 static const struct tegra_pmc_soc tegra194_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = 0,
 	.powergates = NULL,
 	.num_cpu_powergates = 0,
@@ -3745,6 +3753,7 @@ static const char * const tegra234_reset_sources[] = {
 };
 
 static const struct tegra_pmc_soc tegra234_pmc_soc = {
+	.supports_core_domain = false,
 	.num_powergates = 0,
 	.powergates = NULL,
 	.num_cpu_powergates = 0,
@@ -3791,6 +3800,14 @@ static void tegra_pmc_sync_state(struct device *dev)
 {
 	int err;
 
+	/*
+	 * Newer device-trees have power domains, but we need to prepare all
+	 * device drivers with runtime PM and OPP support first, otherwise
+	 * state syncing is unsafe.
+	 */
+	if (!pmc->soc->supports_core_domain)
+		return;
+
 	/*
 	 * Older device-trees don't have core PD, and thus, there are
 	 * no dependencies that will block the state syncing. We shouldn't
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-08-02 14:48   ` Ulf Hansson
  2021-07-01 23:26 ` [PATCH v7 03/37] soc/tegra: Don't print error message when OPPs not available Dmitry Osipenko
                   ` (35 subsequent siblings)
  37 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Implement attach_dev() callback of power domain drivers that initializes
the domain's performance state. GENPD core will apply the performance
state on the first runtime PM resume of the attached device.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/pmc.c | 147 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 147 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index f63dfb2ca3f9..ebafb818b08e 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -506,6 +506,151 @@ static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value,
 		writel(value, pmc->scratch + offset);
 }
 
+static const char * const tegra_emc_compats[] = {
+	"nvidia,tegra20-emc",
+	"nvidia,tegra30-emc",
+	NULL,
+};
+
+/*
+ * This GENPD callback is used by both powergate and core domains.
+ *
+ * We retrieve clock rate of the attached device and initialize domain's
+ * performance state in accordance to the clock rate.
+ */
+static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
+				   struct device *dev)
+{
+	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
+	struct opp_table *opp_table, *pd_opp_table;
+	struct generic_pm_domain *core_genpd;
+	struct dev_pm_opp *opp, *pd_opp;
+	unsigned long rate, state;
+	struct gpd_link *link;
+	struct clk *clk;
+	u32 hw_version;
+	int ret;
+
+	/*
+	 * Tegra114+ SocS don't support OPP yet.  But if they will get OPP
+	 * support, then we want to skip OPP for older kernels to preserve
+	 * compatibility of newer DTBs with older kernels.
+	 */
+	if (!pmc->soc->supports_core_domain)
+		return 0;
+
+	/*
+	 * The EMC devices are a special case because we have a protection
+	 * from non-EMC drivers getting clock handle before EMC driver is
+	 * fully initialized.  The goal of the protection is to prevent
+	 * devfreq driver from getting failures if it will try to change
+	 * EMC clock rate until clock is fully initialized.  The EMC drivers
+	 * will initialize the performance state by themselves.
+	 */
+	if (of_device_compatible_match(dev->of_node, tegra_emc_compats))
+		return 0;
+
+	clk = clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&genpd->dev, "failed to get clk of %s: %pe\n",
+			dev_name(dev), clk);
+		return PTR_ERR(clk);
+	}
+
+	rate = clk_get_rate(clk);
+	if (!rate) {
+		dev_err(&genpd->dev, "failed to get clk rate of %s\n",
+			dev_name(dev));
+		ret = -EINVAL;
+		goto put_clk;
+	}
+
+	if (of_machine_is_compatible("nvidia,tegra20"))
+		hw_version = BIT(tegra_sku_info.soc_process_id);
+	else
+		hw_version = BIT(tegra_sku_info.soc_speedo_id);
+
+	opp_table = dev_pm_opp_set_supported_hw(dev, &hw_version, 1);
+	if (IS_ERR(opp_table)) {
+		dev_err(&genpd->dev, "failed to set OPP supported HW for %s: %d\n",
+			dev_name(dev), ret);
+		ret = PTR_ERR(opp_table);
+		goto put_clk;
+	}
+
+	ret = dev_pm_opp_of_add_table(dev);
+	if (ret) {
+		/* older DTBs that don't have OPPs will get -ENODEV here */
+		if (ret != -ENODEV)
+			dev_err(&genpd->dev, "failed to get OPP table of %s: %d\n",
+				dev_name(dev), ret);
+		else
+			ret = 0;
+
+		goto put_supported_hw;
+	}
+
+	/* find suitable OPP for the rate */
+	opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+
+	if (opp == ERR_PTR(-ERANGE))
+		opp = dev_pm_opp_find_freq_floor(dev, &rate);
+
+	if (IS_ERR(opp)) {
+		dev_err(&genpd->dev, "failed to find OPP for %luHz of %s: %pe\n",
+			rate, dev_name(dev), opp);
+		ret = PTR_ERR(opp);
+		goto remove_dev_table;
+	}
+
+	if (!list_empty(&genpd->child_links)) {
+		link = list_first_entry(&genpd->child_links, struct gpd_link,
+					child_node);
+		core_genpd = link->parent;
+	} else {
+		core_genpd = genpd;
+	}
+
+	pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
+	if (IS_ERR(pd_opp_table)) {
+		dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
+			dev_name(&core_genpd->dev), pd_opp_table);
+		ret = PTR_ERR(pd_opp_table);
+		goto put_dev_opp;
+	}
+
+	pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
+	if (IS_ERR(pd_opp)) {
+		dev_err(&genpd->dev,
+			"failed to xlate required OPP for %luHz of %s: %pe\n",
+			rate, dev_name(dev), pd_opp);
+		ret = PTR_ERR(pd_opp);
+		goto put_pd_opp_table;
+	}
+
+	/*
+	 * The initialized state will be applied by GENPD core on the first
+	 * RPM-resume of the device.  This means that drivers don't need to
+	 * explicitly initialize performance state.
+	 */
+	state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
+	gpd_data->rpm_pstate = state;
+	dev_pm_opp_put(pd_opp);
+
+put_pd_opp_table:
+	dev_pm_opp_put_opp_table(pd_opp_table);
+put_dev_opp:
+	dev_pm_opp_put(opp);
+remove_dev_table:
+	dev_pm_opp_of_remove_table(dev);
+put_supported_hw:
+	dev_pm_opp_put_supported_hw(opp_table);
+put_clk:
+	clk_put(clk);
+
+	return ret;
+}
+
 /*
  * TODO Figure out a way to call this with the struct tegra_pmc * passed in.
  * This currently doesn't work because readx_poll_timeout() can only operate
@@ -1238,6 +1383,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 
 	pg->id = id;
 	pg->genpd.name = np->name;
+	pg->genpd.attach_dev = tegra_pmc_pd_attach_dev;
 	pg->genpd.power_off = tegra_genpd_power_off;
 	pg->genpd.power_on = tegra_genpd_power_on;
 	pg->pmc = pmc;
@@ -1354,6 +1500,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
 		return -ENOMEM;
 
 	genpd->name = np->name;
+	genpd->attach_dev = tegra_pmc_pd_attach_dev;
 	genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
 	genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 03/37] soc/tegra: Don't print error message when OPPs not available
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 04/37] soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple() Dmitry Osipenko
                   ` (34 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Previously we assumed that devm_tegra_core_dev_init_opp_table() will
be used only by drivers that will always have device with OPP table,
but this is not true anymore. For example now Tegra30 will have OPP table
for PWM, but Tegra20 not and both use the same driver. Hence let's not
print the error message about missing OPP table in the common helper,
we can print it somewhere else.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/common.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c
index cd33e99249c3..a42d4f98c078 100644
--- a/drivers/soc/tegra/common.c
+++ b/drivers/soc/tegra/common.c
@@ -111,9 +111,7 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev,
 	 */
 	err = devm_pm_opp_of_add_table(dev);
 	if (err) {
-		if (err == -ENODEV)
-			dev_err_once(dev, "OPP table not found, please update device-tree\n");
-		else
+		if (err != -ENODEV)
 			dev_err(dev, "failed to add OPP table: %d\n", err);
 
 		return err;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 04/37] soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple()
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (2 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 03/37] soc/tegra: Don't print error message when OPPs not available Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 05/37] dt-bindings: clock: tegra-car: Document new tegra-clocks node Dmitry Osipenko
                   ` (33 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Only couple drivers need to get the -ENODEV error code and explicitly
initialize the performance state. Add new helper that allows to avoid
the extra boilerplate code in majority of drivers.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 include/soc/tegra/common.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h
index af41ad80ec21..265ad90e45a2 100644
--- a/include/soc/tegra/common.h
+++ b/include/soc/tegra/common.h
@@ -39,4 +39,17 @@ devm_tegra_core_dev_init_opp_table(struct device *dev,
 }
 #endif
 
+static inline int
+devm_tegra_core_dev_init_opp_table_simple(struct device *dev)
+{
+	struct tegra_core_opp_params params = {};
+	int err;
+
+	err = devm_tegra_core_dev_init_opp_table(dev, &params);
+	if (err != -ENODEV)
+		return err;
+
+	return 0;
+}
+
 #endif /* __SOC_TEGRA_COMMON_H__ */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 05/37] dt-bindings: clock: tegra-car: Document new tegra-clocks node
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (3 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 04/37] soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple() Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 06/37] clk: tegra: Support runtime PM and power domain Dmitry Osipenko
                   ` (32 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Document tegra-clocks sub-node which describes Tegra SoC clocks that
require a higher voltage of the core power domain in order to operate
properly on a higher clock rates.  Each node contains a phandle to OPP
table and power domain.

The root PLLs and system clocks don't have any specific device dedicated
to them, clock controller is in charge of managing power for them.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../bindings/clock/nvidia,tegra20-car.yaml    | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
index 459d2a525393..89c388782d5b 100644
--- a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
+++ b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
@@ -42,6 +42,48 @@ properties:
   "#reset-cells":
     const: 1
 
+  tegra-clocks:
+    description: child nodes are the output clocks from the CAR
+    type: object
+
+    patternProperties:
+      "^[a-z]+[0-9]+$":
+        type: object
+        properties:
+          compatible:
+            - items:
+              - enum:
+                - nvidia,tegra20-sclk
+                - nvidia,tegra30-sclk
+                - nvidia,tegra30-pllc
+                - nvidia,tegra30-plle
+                - nvidia,tegra30-pllm
+              - enum:
+                - nvidia,tegra-clock
+
+          operating-points-v2:
+            $ref: /schemas/types.yaml#/definitions/phandle
+            description:
+              Phandle to OPP table that contains frequencies, voltages and
+              opp-supported-hw property, which is a bitfield indicating
+              SoC process or speedo ID mask.
+
+          clocks:
+            items:
+              - description: node's clock
+
+          power-domains:
+            maxItems: 1
+            description: phandle to the core SoC power domain
+
+        required:
+          - compatible
+          - operating-points-v2
+          - clocks
+          - power-domains
+
+        additionalProperties: false
+
 required:
   - compatible
   - reg
@@ -59,6 +101,15 @@ examples:
         reg = <0x60006000 0x1000>;
         #clock-cells = <1>;
         #reset-cells = <1>;
+
+        tegra-clocks {
+            sclk {
+                compatible = "nvidia,tegra20-sclk", "nvidia,tegra-clock";
+                operating-points-v2 = <&opp_table>;
+                clocks = <&tegra_car TEGRA20_CLK_SCLK>;
+                power-domains = <&domain>;
+            };
+        };
     };
 
     usb-controller@c5004000 {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 06/37] clk: tegra: Support runtime PM and power domain
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (4 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 05/37] dt-bindings: clock: tegra-car: Document new tegra-clocks node Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 07/37] dt-bindings: host1x: Document OPP and power domain properties Dmitry Osipenko
                   ` (31 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The Clock-and-Reset controller resides in a core power domain on NVIDIA
Tegra SoCs.  In order to support voltage scaling of the core power domain,
we hook up DVFS-capable clocks to the core GENPD for managing of the
GENPD's performance state based on the clock changes.

Some clocks don't have any specific physical hardware unit that backs
them, like root PLLs and system clock and they have theirs own voltage
requirements.  This patch adds new clk-device driver that backs the clocks
and provides runtime PM functionality for them.  A virtual clk-device is
created for each such DVFS-capable clock at the clock's registration time
by the new tegra_clk_register() helper.  Driver changes clock's device
GENPD performance state based on clk-rate notifications.

In result we have this sequence of events:

  1. Clock driver creates virtual device for selective clocks, enables
     runtime PM for the created device and registers the clock.
  2. Clk-device driver starts to listen to clock rate changes.
  3. Something changes clk rate or enables/disables clk.
  4. CCF core propagates the change through the clk tree.
  5. Clk-device driver gets clock rate-change notification or GENPD core
     handles prepare/unprepare of the clock.
  6. Clk-device driver changes GENPD performance state on clock rate
     change.
  7. GENPD driver changes voltage regulator state change.
  8. The regulator state is committed to hardware via I2C.

We rely on fact that DVFS is not needed for Tegra I2C.  Hence I2C
subsystem stays independent from the clk power management and there
are no deadlock spots in the sequence.

Currently all clocks are registered very early during kernel boot when the
device driver core isn't available yet.  The clk-device can't be created
at that time.  This patch splits the registration of the clocks in two
phases:

  1. Register all essential clocks which don't use RPM and are needed
     during early boot.

  2. Register at a later boot time the rest of clocks.

This patch adds power management support for Tegra20 and Tegra30 clocks.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/clk/tegra/Makefile      |   1 +
 drivers/clk/tegra/clk-device.c  | 222 ++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-pll.c     |   2 +-
 drivers/clk/tegra/clk-super.c   |   2 +-
 drivers/clk/tegra/clk-tegra20.c |  39 ++++--
 drivers/clk/tegra/clk-tegra30.c |  70 ++++++----
 drivers/clk/tegra/clk.c         |  66 ++++++++++
 drivers/clk/tegra/clk.h         |   2 +
 8 files changed, 368 insertions(+), 36 deletions(-)
 create mode 100644 drivers/clk/tegra/clk-device.c

diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 7b1816856eb5..a0715cdfc1a4 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-y					+= clk.o
 obj-y					+= clk-audio-sync.o
+obj-y					+= clk-device.o
 obj-y					+= clk-dfll.o
 obj-y					+= clk-divider.o
 obj-y					+= clk-periph.o
diff --git a/drivers/clk/tegra/clk-device.c b/drivers/clk/tegra/clk-device.c
new file mode 100644
index 000000000000..1399eaba1c91
--- /dev/null
+++ b/drivers/clk/tegra/clk-device.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/common.h>
+
+#include "clk.h"
+
+/*
+ * This driver manages performance state of the core power domain for the
+ * independent PLLs and system clocks.  We created a virtual clock device
+ * for such clocks, see tegra_clk_register().
+ */
+
+struct tegra_clk_device {
+	struct notifier_block clk_nb;
+	struct device *dev;
+	struct clk_hw *hw;
+	struct mutex lock;
+};
+
+static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev,
+				    unsigned long rate)
+{
+	struct device *dev = clk_dev->dev;
+	struct dev_pm_opp *opp;
+	unsigned int pstate;
+
+	opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+	if (opp == ERR_PTR(-ERANGE)) {
+		dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate);
+		opp = dev_pm_opp_find_freq_floor(dev, &rate);
+	}
+
+	if (IS_ERR(opp)) {
+		dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp);
+		return PTR_ERR(opp);
+	}
+
+	pstate = dev_pm_opp_get_required_pstate(opp, 0);
+	dev_pm_opp_put(opp);
+
+	return dev_pm_genpd_set_performance_state(dev, pstate);
+}
+
+static int tegra_clock_change_notify(struct notifier_block *nb,
+				     unsigned long msg, void *data)
+{
+	struct clk_notifier_data *cnd = data;
+	struct tegra_clk_device *clk_dev;
+	int err = 0;
+
+	clk_dev = container_of(nb, struct tegra_clk_device, clk_nb);
+
+	mutex_lock(&clk_dev->lock);
+	switch (msg) {
+	case PRE_RATE_CHANGE:
+		if (cnd->new_rate > cnd->old_rate)
+			err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
+		break;
+
+	case ABORT_RATE_CHANGE:
+		err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate);
+		break;
+
+	case POST_RATE_CHANGE:
+		if (cnd->new_rate < cnd->old_rate)
+			err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
+		break;
+
+	default:
+		break;
+	}
+	mutex_unlock(&clk_dev->lock);
+
+	return notifier_from_errno(err);
+}
+
+static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev)
+{
+	unsigned long rate;
+	int ret = 0;
+
+	mutex_lock(&clk_dev->lock);
+
+	if (!pm_runtime_status_suspended(clk_dev->dev)) {
+		rate = clk_hw_get_rate(clk_dev->hw);
+		ret = tegra_clock_set_pd_state(clk_dev, rate);
+	}
+
+	mutex_unlock(&clk_dev->lock);
+
+	return ret;
+}
+
+static int tegra_clock_probe(struct platform_device *pdev)
+{
+	struct tegra_clk_device *clk_dev;
+	struct device *dev = &pdev->dev;
+	struct clk *clk;
+	int err;
+
+	if (!dev->pm_domain)
+		return -EINVAL;
+
+	clk_dev = devm_kzalloc(dev, sizeof(*clk_dev), GFP_KERNEL);
+	if (!clk_dev)
+		return -ENOMEM;
+
+	clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	clk_dev->dev = dev;
+	clk_dev->hw = __clk_get_hw(clk);
+	clk_dev->clk_nb.notifier_call = tegra_clock_change_notify;
+	mutex_init(&clk_dev->lock);
+
+	platform_set_drvdata(pdev, clk_dev);
+
+	err = devm_tegra_core_dev_init_opp_table_simple(dev);
+	if (err)
+		return err;
+
+	err = clk_notifier_register(clk, &clk_dev->clk_nb);
+	if (err) {
+		dev_err(dev, "failed to register clk notifier: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * The driver is attaching to a potentially active/resumed clock, hence
+	 * we need to sync the power domain performance state in a accordance to
+	 * the clock rate if clock is resumed.
+	 */
+	err = tegra_clock_sync_pd_state(clk_dev);
+	if (err)
+		goto unreg_clk;
+
+	return 0;
+
+unreg_clk:
+	clk_notifier_unregister(clk, &clk_dev->clk_nb);
+
+	return err;
+}
+
+static __maybe_unused int tegra_clock_pm_suspend(struct device *dev)
+{
+	struct tegra_clk_device *clk_dev = dev_get_drvdata(dev);
+
+	/*
+	 * Power management of the clock is entangled with the Tegra PMC
+	 * GENPD because PMC driver enables/disables clocks for toggling
+	 * of the PD's on/off state.
+	 *
+	 * The PMC GENPD is resumed in NOIRQ phase, before RPM of the clocks
+	 * becomes available, hence PMC can't use clocks at the early resume
+	 * phase if RPM is involved. For example when 3d clock is enabled,
+	 * it may enable the parent PLL clock that needs to be RPM-resumed.
+	 *
+	 * Secondly, the PLL clocks may be enabled by the low level suspend
+	 * code, so we need to assume that PLL is in enabled state during
+	 * suspend.
+	 *
+	 * We will keep PLLs and system clock resumed during suspend time.
+	 * All PLLs on all SoCs are low power and system clock is always-on,
+	 * so practically not much is changed here.
+	 */
+
+	return clk_prepare(clk_dev->hw->clk);
+}
+
+static __maybe_unused int tegra_clock_pm_resume(struct device *dev)
+{
+	struct tegra_clk_device *clk_dev = dev_get_drvdata(dev);
+
+	clk_unprepare(clk_dev->hw->clk);
+
+	return 0;
+}
+
+static void tegra_clock_shutdown(struct platform_device *pdev)
+{
+	struct tegra_clk_device *clk_dev = platform_get_drvdata(pdev);
+
+	clk_prepare(clk_dev->hw->clk);
+}
+
+static const struct dev_pm_ops tegra_clock_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(tegra_clock_pm_suspend,
+				tegra_clock_pm_resume)
+};
+
+static const struct of_device_id tegra_clock_match[] = {
+	{ .compatible = "nvidia,tegra20-sclk" },
+	{ .compatible = "nvidia,tegra30-sclk" },
+	{ .compatible = "nvidia,tegra30-pllc" },
+	{ .compatible = "nvidia,tegra30-plle" },
+	{ .compatible = "nvidia,tegra30-pllm" },
+	{ }
+};
+
+static struct platform_driver tegra_clock_driver = {
+	.driver = {
+		.name = "tegra-clock",
+		.of_match_table = tegra_clock_match,
+		.pm = &tegra_clock_pm,
+		.suppress_bind_attrs = true,
+	},
+	.probe = tegra_clock_probe,
+	.shutdown = tegra_clock_shutdown,
+};
+builtin_platform_driver(tegra_clock_driver);
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index eaa079c177c3..131efc53659d 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -1914,7 +1914,7 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
 	/* Data in .init is copied by clk_register(), so stack variable OK */
 	pll->hw.init = &init;
 
-	return clk_register(NULL, &pll->hw);
+	return tegra_clk_register(&pll->hw);
 }
 
 struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index 6099c6e9acd4..d1af4817051e 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -226,7 +226,7 @@ struct clk *tegra_clk_register_super_mux(const char *name,
 	/* Data in .init is copied by clk_register(), so stack variable OK */
 	super->hw.init = &init;
 
-	clk = clk_register(NULL, &super->hw);
+	clk = tegra_clk_register(&super->hw);
 	if (IS_ERR(clk))
 		kfree(super);
 
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index 3664593a5ba4..cf92c0f4db61 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -710,13 +710,6 @@ static void tegra20_super_clk_init(void)
 			      NULL);
 	clks[TEGRA20_CLK_CCLK] = clk;
 
-	/* SCLK */
-	clk = tegra_clk_register_super_mux("sclk", sclk_parents,
-			      ARRAY_SIZE(sclk_parents),
-			      CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
-			      clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
-	clks[TEGRA20_CLK_SCLK] = clk;
-
 	/* twd */
 	clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4);
 	clks[TEGRA20_CLK_TWD] = clk;
@@ -1146,13 +1139,39 @@ static void __init tegra20_clock_init(struct device_node *np)
 	tegra20_periph_clk_init();
 	tegra20_audio_clk_init();
 
-	tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX);
-
 	tegra_add_of_provider(np, tegra20_clk_src_onecell_get);
-	tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
 
 	tegra_clk_apply_init_table = tegra20_clock_apply_init_table;
 
 	tegra_cpu_car_ops = &tegra20_cpu_car_ops;
 }
 CLK_OF_DECLARE(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
+
+/*
+ * Clocks that use runtime PM can't be created at the CLK_OF_DECLARE
+ * stage because drivers base isn't initialized yet, and thus platform
+ * devices can't be created for the clocks.  Hence we need to split the
+ * registration of the clocks into two phases.  The first phase registers
+ * essential clocks which don't require RPM and are actually used during
+ * early boot.  The second phase registers clocks which use RPM and this
+ * is done when device drivers core API is ready.
+ */
+static int __init tegra20_init_runtime_pm_clocks(void)
+{
+	struct clk *clk;
+
+	if (!of_machine_is_compatible("nvidia,tegra20"))
+		return 0;
+
+	clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+			      ARRAY_SIZE(sclk_parents),
+			      CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			      clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
+	clks[TEGRA20_CLK_SCLK] = clk;
+
+	tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX);
+	tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+
+	return 0;
+}
+postcore_initcall_sync(tegra20_init_runtime_pm_clocks);
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index 64121bc66d85..a8ca313f3c6f 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -812,11 +812,6 @@ static void __init tegra30_pll_init(void)
 {
 	struct clk *clk;
 
-	/* PLLC */
-	clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
-				     &pll_c_params, NULL);
-	clks[TEGRA30_CLK_PLL_C] = clk;
-
 	/* PLLC_OUT1 */
 	clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
 				clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@@ -826,11 +821,6 @@ static void __init tegra30_pll_init(void)
 				0, NULL);
 	clks[TEGRA30_CLK_PLL_C_OUT1] = clk;
 
-	/* PLLM */
-	clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
-			    CLK_SET_RATE_GATE, &pll_m_params, NULL);
-	clks[TEGRA30_CLK_PLL_M] = clk;
-
 	/* PLLM_OUT1 */
 	clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
 				clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@@ -880,9 +870,6 @@ static void __init tegra30_pll_init(void)
 			       ARRAY_SIZE(pll_e_parents),
 			       CLK_SET_RATE_NO_REPARENT,
 			       clk_base + PLLE_AUX, 2, 1, 0, NULL);
-	clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
-			     CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
-	clks[TEGRA30_CLK_PLL_E] = clk;
 }
 
 static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
@@ -971,14 +958,6 @@ static void __init tegra30_super_clk_init(void)
 			      NULL);
 	clks[TEGRA30_CLK_CCLK_LP] = clk;
 
-	/* SCLK */
-	clk = tegra_clk_register_super_mux("sclk", sclk_parents,
-				  ARRAY_SIZE(sclk_parents),
-				  CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
-				  clk_base + SCLK_BURST_POLICY,
-				  0, 4, 0, 0, NULL);
-	clks[TEGRA30_CLK_SCLK] = clk;
-
 	/* twd */
 	clk = clk_register_fixed_factor(NULL, "twd", "cclk_g",
 					CLK_SET_RATE_PARENT, 1, 2);
@@ -1354,13 +1333,56 @@ static void __init tegra30_clock_init(struct device_node *np)
 			     tegra30_audio_plls,
 			     ARRAY_SIZE(tegra30_audio_plls), 24000000);
 
-	tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
-
 	tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
-	tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
 
 	tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
 
 	tegra_cpu_car_ops = &tegra30_cpu_car_ops;
 }
 CLK_OF_DECLARE(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
+
+/*
+ * Clocks that use runtime PM can't be created at the CLK_OF_DECLARE
+ * stage because drivers base isn't initialized yet, and thus platform
+ * devices can't be created for the clocks.  Hence we need to split the
+ * registration of the clocks into two phases.  The first phase registers
+ * essential clocks which don't require RPM and are actually used during
+ * early boot.  The second phase registers clocks which use RPM and this
+ * is done when device drivers core API is ready.
+ */
+static int __init tegra30_init_runtime_pm_clocks(void)
+{
+	struct clk *clk;
+
+	if (!of_machine_is_compatible("nvidia,tegra30"))
+		return 0;
+
+	/* PLLC */
+	clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
+				     &pll_c_params, NULL);
+	clks[TEGRA30_CLK_PLL_C] = clk;
+
+	/* PLLE */
+	clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
+				      CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
+	clks[TEGRA30_CLK_PLL_E] = clk;
+
+	/* PLLM */
+	clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
+				     CLK_SET_RATE_GATE, &pll_m_params, NULL);
+	clks[TEGRA30_CLK_PLL_M] = clk;
+
+	/* SCLK */
+	clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+					   ARRAY_SIZE(sclk_parents),
+					   CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+					   clk_base + SCLK_BURST_POLICY,
+					   0, 4, 0, 0, NULL);
+	clks[TEGRA30_CLK_SCLK] = clk;
+
+	tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
+	tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+
+	return 0;
+}
+postcore_initcall_sync(tegra30_init_runtime_pm_clocks);
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index f6cdce441cf7..dda27dc14993 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -9,14 +9,19 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/clk/tegra.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset-controller.h>
+#include <linux/string.h>
 
 #include <soc/tegra/fuse.h>
 
 #include "clk.h"
 
 /* Global data of Tegra CPU CAR ops */
+static struct device_node *tegra_car_np;
 static struct tegra_cpu_car_ops dummy_car_ops;
 struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
 
@@ -320,6 +325,8 @@ void __init tegra_add_of_provider(struct device_node *np,
 {
 	int i;
 
+	tegra_car_np = np;
+
 	for (i = 0; i < clk_num; i++) {
 		if (IS_ERR(clks[i])) {
 			pr_err
@@ -372,6 +379,65 @@ struct clk ** __init tegra_lookup_dt_id(int clk_id,
 		return NULL;
 }
 
+static struct device_node *tegra_clk_get_of_node(struct clk_hw *hw)
+{
+	struct device_node *np, *root;
+
+	if (!tegra_car_np)
+		return NULL;
+
+	root = of_get_child_by_name(tegra_car_np, "tegra-clocks");
+	if (!root)
+		return NULL;
+
+	for_each_child_of_node(root, np) {
+		if (strcmp(np->name, hw->init->name))
+			continue;
+
+		if (!of_device_is_compatible(np, "nvidia,tegra-clock"))
+			continue;
+
+		return np;
+	}
+
+	of_node_put(root);
+
+	return NULL;
+}
+
+struct clk *tegra_clk_register(struct clk_hw *hw)
+{
+	struct platform_device *pdev;
+	struct device *dev = NULL;
+	struct device_node *np;
+	const char *dev_name;
+
+	np = tegra_clk_get_of_node(hw);
+
+	if (!of_device_is_available(np))
+		goto reg_clk;
+
+	dev_name = kasprintf(GFP_KERNEL, "tegra_clk_%s", hw->init->name);
+	if (!dev_name) {
+		of_node_put(np);
+		goto reg_clk;
+	}
+
+	pdev = of_platform_device_create(np, dev_name, NULL);
+	if (!pdev) {
+		pr_err("%s: failed to create device for %pOF\n", __func__, np);
+		kfree(dev_name);
+		of_node_put(np);
+		goto reg_clk;
+	}
+
+	dev = &pdev->dev;
+	pm_runtime_enable(dev);
+
+reg_clk:
+	return clk_register(dev, hw);
+}
+
 tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
 
 static int __init tegra_clocks_apply_init_table(void)
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 0c3ba0ccce1a..4fab4aa9fa31 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -927,4 +927,6 @@ struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
 struct clk *tegra210_clk_register_emc(struct device_node *np,
 				      void __iomem *regs);
 
+struct clk *tegra_clk_register(struct clk_hw *hw);
+
 #endif /* TEGRA_CLK_H */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 07/37] dt-bindings: host1x: Document OPP and power domain properties
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (5 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 06/37] clk: tegra: Support runtime PM and power domain Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:26 ` [PATCH v7 08/37] dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and GR3D Dmitry Osipenko
                   ` (30 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Document new DVFS OPP table and power domain properties of the Host1x bus
and devices sitting on the bus.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../display/tegra/nvidia,tegra20-host1x.txt   | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
index 8a6d3e1ee306..62861a8fb5c6 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
@@ -20,6 +20,18 @@ Required properties:
 - reset-names: Must include the following entries:
   - host1x
 
+Optional properties:
+- operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to HEG or core power domain.
+
+For each opp entry in 'operating-points-v2' table of host1x and its modules:
+- opp-supported-hw: One bitfield indicating:
+	On Tegra20: SoC process ID mask
+	On Tegra30+: SoC speedo ID mask
+
+	A bitwise AND is performed against the value and if any bit
+	matches, the OPP gets enabled.
+
 Each host1x client module having to perform DMA through the Memory Controller
 should have the interconnect endpoints set to the Memory Client and External
 Memory respectively.
@@ -45,6 +57,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to MPE power domain.
 
 - vi: video input
 
@@ -128,6 +142,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to VENC power domain.
 
 - epp: encoder pre-processor
 
@@ -147,6 +163,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to HEG or core power domain.
 
 - isp: image signal processor
 
@@ -166,6 +184,7 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - power-domains: Phandle to VENC or core power domain.
 
 - gr2d: 2D graphics engine
 
@@ -185,6 +204,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to HEG or core power domain.
 
 - gr3d: 3D graphics engine
 
@@ -209,6 +230,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandles to 3D or core power domain.
 
 - dc: display controller
 
@@ -241,6 +264,8 @@ of the following host1x client modules:
   - interconnect-names: Must include name of the interconnect path for each
     interconnect entry. Consult TRM documentation for information about
     available memory clients, see MEMORY CONTROLLER section.
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to core power domain.
 
 - hdmi: High Definition Multimedia Interface
 
@@ -267,6 +292,7 @@ of the following host1x client modules:
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
   - nvidia,panel: phandle of a display panel
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
 
 - tvo: TV encoder output
 
@@ -277,6 +303,10 @@ of the following host1x client modules:
   - clocks: Must contain one entry, for the module clock.
     See ../clocks/clock-bindings.txt for details.
 
+  Optional properties:
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
+  - power-domains: Phandle to core power domain.
+
 - dsi: display serial interface
 
   Required properties:
@@ -305,6 +335,7 @@ of the following host1x client modules:
   - nvidia,panel: phandle of a display panel
   - nvidia,ganged-mode: contains a phandle to a second DSI controller to gang
     up with in order to support up to 8 data lanes
+  - operating-points-v2: See ../bindings/opp/opp.txt for details.
 
 - sor: serial output resource
 
@@ -408,6 +439,8 @@ Example:
 		clocks = <&tegra_car TEGRA20_CLK_HOST1X>;
 		resets = <&tegra_car 28>;
 		reset-names = "host1x";
+		operating-points-v2 = <&dvfs_opp_table>;
+		power-domains = <&domain>;
 
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -421,6 +454,8 @@ Example:
 			clocks = <&tegra_car TEGRA20_CLK_MPE>;
 			resets = <&tegra_car 60>;
 			reset-names = "mpe";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 		};
 
 		vi@54080000 {
@@ -429,6 +464,7 @@ Example:
 			interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
 			assigned-clocks = <&tegra_car TEGRA210_CLK_VI>;
 			assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_C4_OUT0>;
+			operating-points-v2 = <&dvfs_opp_table>;
 
 			clocks = <&tegra_car TEGRA210_CLK_VI>;
 			power-domains = <&pd_venc>;
@@ -510,6 +546,8 @@ Example:
 			clocks = <&tegra_car TEGRA20_CLK_EPP>;
 			resets = <&tegra_car 19>;
 			reset-names = "epp";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 		};
 
 		isp {
@@ -528,6 +566,8 @@ Example:
 			clocks = <&tegra_car TEGRA20_CLK_GR2D>;
 			resets = <&tegra_car 21>;
 			reset-names = "2d";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 		};
 
 		gr3d {
@@ -536,6 +576,8 @@ Example:
 			clocks = <&tegra_car TEGRA20_CLK_GR3D>;
 			resets = <&tegra_car 24>;
 			reset-names = "3d";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 		};
 
 		dc@54200000 {
@@ -547,6 +589,8 @@ Example:
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 27>;
 			reset-names = "dc";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 
 			interconnects = <&mc TEGRA20_MC_DISPLAY0A &emc>,
 					<&mc TEGRA20_MC_DISPLAY0B &emc>,
@@ -571,6 +615,8 @@ Example:
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 26>;
 			reset-names = "dc";
+			operating-points-v2 = <&dvfs_opp_table>;
+			power-domains = <&domain>;
 
 			interconnects = <&mc TEGRA20_MC_DISPLAY0AB &emc>,
 					<&mc TEGRA20_MC_DISPLAY0BB &emc>,
@@ -596,6 +642,7 @@ Example:
 			resets = <&tegra_car 51>;
 			reset-names = "hdmi";
 			status = "disabled";
+			operating-points-v2 = <&dvfs_opp_table>;
 		};
 
 		tvo {
@@ -604,6 +651,7 @@ Example:
 			interrupts = <0 76 0x04>;
 			clocks = <&tegra_car TEGRA20_CLK_TVO>;
 			status = "disabled";
+			operating-points-v2 = <&dvfs_opp_table>;
 		};
 
 		dsi {
@@ -615,6 +663,7 @@ Example:
 			resets = <&tegra_car 48>;
 			reset-names = "dsi";
 			status = "disabled";
+			operating-points-v2 = <&dvfs_opp_table>;
 		};
 	};
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 08/37] dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and GR3D
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (6 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 07/37] dt-bindings: host1x: Document OPP and power domain properties Dmitry Osipenko
@ 2021-07-01 23:26 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 09/37] gpu: host1x: Add host1x_channel_stop() Dmitry Osipenko
                   ` (29 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:26 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Memory Client should be blocked before hardware reset is asserted in order
to prevent memory corruption and hanging of memory controller.

Document Memory Client resets of Host1x, GR2D and GR3D hardware units.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../bindings/display/tegra/nvidia,tegra20-host1x.txt          | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
index 62861a8fb5c6..07a08653798b 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
@@ -18,6 +18,7 @@ Required properties:
 - resets: Must contain an entry for each entry in reset-names.
   See ../reset/reset.txt for details.
 - reset-names: Must include the following entries:
+  - mc
   - host1x
 
 Optional properties:
@@ -197,6 +198,7 @@ of the following host1x client modules:
   - resets: Must contain an entry for each entry in reset-names.
     See ../reset/reset.txt for details.
   - reset-names: Must include the following entries:
+    - mc
     - 2d
 
   Optional properties:
@@ -222,6 +224,8 @@ of the following host1x client modules:
   - resets: Must contain an entry for each entry in reset-names.
     See ../reset/reset.txt for details.
   - reset-names: Must include the following entries:
+    - mc
+    - mc2 (Only required on SoCs with two 3D clocks)
     - 3d
     - 3d2 (Only required on SoCs with two 3D clocks)
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 09/37] gpu: host1x: Add host1x_channel_stop()
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (7 preceding siblings ...)
  2021-07-01 23:26 ` [PATCH v7 08/37] dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and GR3D Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 10/37] gpu: host1x: Add runtime PM support Dmitry Osipenko
                   ` (28 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add host1x_channel_stop() which waits till channel becomes idle and then
stops the channel hardware. This is needed for supporting suspend/resume
by host1x drivers since the hardware state is lost after power-gating,
thus the channel needs to be stopped before client enters into suspend.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/host1x/channel.c | 8 ++++++++
 include/linux/host1x.h       | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/host1x/channel.c b/drivers/gpu/host1x/channel.c
index 4cd212bb570d..2a9a3a8d5931 100644
--- a/drivers/gpu/host1x/channel.c
+++ b/drivers/gpu/host1x/channel.c
@@ -75,6 +75,14 @@ struct host1x_channel *host1x_channel_get_index(struct host1x *host,
 	return ch;
 }
 
+void host1x_channel_stop(struct host1x_channel *channel)
+{
+	struct host1x *host = dev_get_drvdata(channel->dev->parent);
+
+	host1x_hw_cdma_stop(host, &channel->cdma);
+}
+EXPORT_SYMBOL(host1x_channel_stop);
+
 static void release_channel(struct kref *kref)
 {
 	struct host1x_channel *channel =
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 9b0487c88571..789aee540700 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -179,6 +179,7 @@ struct host1x_job;
 
 struct host1x_channel *host1x_channel_request(struct host1x_client *client);
 struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
+void host1x_channel_stop(struct host1x_channel *channel);
 void host1x_channel_put(struct host1x_channel *channel);
 int host1x_job_submit(struct host1x_job *job);
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 10/37] gpu: host1x: Add runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (8 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 09/37] gpu: host1x: Add host1x_channel_stop() Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 11/37] gpu: host1x: Add stub driver for MPE, VI, EPP and ISP Dmitry Osipenko
                   ` (27 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add runtime PM support to the Host1x driver. This is required for enabling
system-wide DVFS and supporting dynamic power management using a generic
power domain. For the starter we will keep host1x always-on because
dynamic power management require a major refactoring of the driver code
since lot's of code paths will need the RPM handling and we're going to
remove some of these paths in the future. Host1x doesn't consume much
power so it is good enough, we at least need to resume Host1x in order
to initialize the power state.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/host1x/debug.c         |  15 +++
 drivers/gpu/host1x/dev.c           | 145 +++++++++++++++++++++++------
 drivers/gpu/host1x/dev.h           |   3 +-
 drivers/gpu/host1x/hw/channel_hw.c |  44 ++++-----
 drivers/gpu/host1x/intr.c          |   3 -
 drivers/gpu/host1x/syncpt.c        |   5 +-
 6 files changed, 159 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c
index 8a14880c61bb..18d9c8d206e3 100644
--- a/drivers/gpu/host1x/debug.c
+++ b/drivers/gpu/host1x/debug.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 
@@ -52,6 +53,11 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
 {
 	struct host1x *m = dev_get_drvdata(ch->dev->parent);
 	struct output *o = data;
+	int err;
+
+	err = pm_runtime_resume_and_get(m->dev);
+	if (err < 0)
+		return err;
 
 	mutex_lock(&ch->cdma.lock);
 	mutex_lock(&debug_lock);
@@ -64,6 +70,8 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
 	mutex_unlock(&debug_lock);
 	mutex_unlock(&ch->cdma.lock);
 
+	pm_runtime_put(m->dev);
+
 	return 0;
 }
 
@@ -71,9 +79,14 @@ static void show_syncpts(struct host1x *m, struct output *o)
 {
 	struct list_head *pos;
 	unsigned int i;
+	int err;
 
 	host1x_debug_output(o, "---- syncpts ----\n");
 
+	err = pm_runtime_resume_and_get(m->dev);
+	if (err < 0)
+		return;
+
 	for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
 		u32 max = host1x_syncpt_read_max(m->syncpt + i);
 		u32 min = host1x_syncpt_load(m->syncpt + i);
@@ -101,6 +114,8 @@ static void show_syncpts(struct host1x *m, struct output *o)
 					    base_val);
 	}
 
+	pm_runtime_put(m->dev);
+
 	host1x_debug_output(o, "\n");
 }
 
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index fbb6447b8659..0945df6039af 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -6,12 +6,15 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define CREATE_TRACE_POINTS
@@ -190,6 +193,9 @@ static void host1x_setup_sid_table(struct host1x *host)
 	const struct host1x_info *info = host->info;
 	unsigned int i;
 
+	if (!info->has_hypervisor)
+		return;
+
 	for (i = 0; i < info->num_sid_entries; i++) {
 		const struct host1x_sid_entry *entry = &info->sid_table[i];
 
@@ -347,6 +353,27 @@ static void host1x_iommu_exit(struct host1x *host)
 	}
 }
 
+static int host1x_get_resets(struct host1x *host)
+{
+	int err;
+
+	host->resets[0].id = "mc";
+	host->resets[1].id = "host1x";
+	host->nresets = ARRAY_SIZE(host->resets);
+
+	err = devm_reset_control_bulk_get_optional_exclusive_released(
+				host->dev, host->nresets, host->resets);
+	if (err) {
+		dev_err(host->dev, "failed to get reset: %d\n", err);
+		return err;
+	}
+
+	if (WARN_ON(!host->resets[1].rstc))
+		return -EINVAL;
+
+	return 0;
+}
+
 static int host1x_probe(struct platform_device *pdev)
 {
 	struct host1x *host;
@@ -423,12 +450,9 @@ static int host1x_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	host->rst = devm_reset_control_get(&pdev->dev, "host1x");
-	if (IS_ERR(host->rst)) {
-		err = PTR_ERR(host->rst);
-		dev_err(&pdev->dev, "failed to get reset: %d\n", err);
+	err = host1x_get_resets(host);
+	if (err)
 		return err;
-	}
 
 	err = host1x_iommu_init(host);
 	if (err < 0) {
@@ -443,22 +467,10 @@ static int host1x_probe(struct platform_device *pdev)
 		goto iommu_exit;
 	}
 
-	err = clk_prepare_enable(host->clk);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to enable clock\n");
-		goto free_channels;
-	}
-
-	err = reset_control_deassert(host->rst);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to deassert reset: %d\n", err);
-		goto unprepare_disable;
-	}
-
 	err = host1x_syncpt_init(host);
 	if (err) {
 		dev_err(&pdev->dev, "failed to initialize syncpts\n");
-		goto reset_assert;
+		goto free_channels;
 	}
 
 	err = host1x_intr_init(host, syncpt_irq);
@@ -467,10 +479,14 @@ static int host1x_probe(struct platform_device *pdev)
 		goto deinit_syncpt;
 	}
 
-	host1x_debug_init(host);
+	pm_runtime_enable(&pdev->dev);
+
+	/* the driver's code isn't ready yet for the dynamic RPM */
+	err = pm_runtime_resume_and_get(&pdev->dev);
+	if (err)
+		goto deinit_intr;
 
-	if (host->info->has_hypervisor)
-		host1x_setup_sid_table(host);
+	host1x_debug_init(host);
 
 	err = host1x_register(host);
 	if (err < 0)
@@ -486,13 +502,13 @@ static int host1x_probe(struct platform_device *pdev)
 	host1x_unregister(host);
 deinit_debugfs:
 	host1x_debug_deinit(host);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+deinit_intr:
 	host1x_intr_deinit(host);
 deinit_syncpt:
 	host1x_syncpt_deinit(host);
-reset_assert:
-	reset_control_assert(host->rst);
-unprepare_disable:
-	clk_disable_unprepare(host->clk);
 free_channels:
 	host1x_channel_list_free(&host->channel_list);
 iommu_exit:
@@ -507,19 +523,94 @@ static int host1x_remove(struct platform_device *pdev)
 
 	host1x_unregister(host);
 	host1x_debug_deinit(host);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
 	host1x_intr_deinit(host);
 	host1x_syncpt_deinit(host);
-	reset_control_assert(host->rst);
-	clk_disable_unprepare(host->clk);
 	host1x_iommu_exit(host);
 
 	return 0;
 }
 
+static int __maybe_unused host1x_runtime_suspend(struct device *dev)
+{
+	struct host1x *host = dev_get_drvdata(dev);
+	int err;
+
+	host1x_intr_stop(host);
+	host1x_syncpt_save(host);
+
+	err = reset_control_bulk_assert(host->nresets, host->resets);
+	if (err) {
+		dev_err(dev, "failed to assert reset: %d\n", err);
+		goto resume_host1x;
+	}
+
+	usleep_range(1000, 2000);
+
+	clk_disable_unprepare(host->clk);
+	reset_control_bulk_release(host->nresets, host->resets);
+
+	return 0;
+
+resume_host1x:
+	host1x_setup_sid_table(host);
+	host1x_syncpt_restore(host);
+	host1x_intr_start(host);
+
+	return err;
+}
+
+static int __maybe_unused host1x_runtime_resume(struct device *dev)
+{
+	struct host1x *host = dev_get_drvdata(dev);
+	int err;
+
+	err = reset_control_bulk_acquire(host->nresets, host->resets);
+	if (err) {
+		dev_err(dev, "failed to acquire reset: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(host->clk);
+	if (err) {
+		dev_err(dev, "failed to enable clock: %d\n", err);
+		goto release_reset;
+	}
+
+	err = reset_control_bulk_deassert(host->nresets, host->resets);
+	if (err < 0) {
+		dev_err(dev, "failed to deassert reset: %d\n", err);
+		goto disable_clk;
+	}
+
+	host1x_setup_sid_table(host);
+	host1x_syncpt_restore(host);
+	host1x_intr_start(host);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(host->clk);
+release_reset:
+	reset_control_bulk_release(host->nresets, host->resets);
+
+	return err;
+}
+
+static const struct dev_pm_ops host1x_pm = {
+	SET_RUNTIME_PM_OPS(host1x_runtime_suspend, host1x_runtime_resume,
+			   NULL)
+	/* TODO: add system suspend-resume once driver will be ready for that */
+};
+
 static struct platform_driver tegra_host1x_driver = {
 	.driver = {
 		.name = "tegra-host1x",
 		.of_match_table = host1x_of_match,
+		.pm = &host1x_pm,
 	},
 	.probe = host1x_probe,
 	.remove = host1x_remove,
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index fa6d4bc46e98..41a7a63514c4 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -118,7 +118,8 @@ struct host1x {
 	struct host1x_syncpt_base *bases;
 	struct device *dev;
 	struct clk *clk;
-	struct reset_control *rst;
+	struct reset_control_bulk_data resets[2];
+	unsigned int nresets;
 
 	struct iommu_group *group;
 	struct iommu_domain *domain;
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index d4c28faf27d1..27290d0d7eaf 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -114,6 +114,27 @@ static void host1x_channel_set_streamid(struct host1x_channel *channel)
 #endif
 }
 
+static void host1x_enable_gather_filter(struct host1x_channel *ch)
+{
+#if HOST1X_HW >= 6
+	struct host1x *host = dev_get_drvdata(ch->dev->parent);
+	u32 val;
+
+	if (!host->hv_regs)
+		return;
+
+	val = host1x_hypervisor_readl(
+		host, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
+	val |= BIT(ch->id % 32);
+	host1x_hypervisor_writel(
+		host, val, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
+#elif HOST1X_HW >= 4
+	host1x_ch_writel(ch,
+			 HOST1X_CHANNEL_CHANNELCTRL_KERNEL_FILTER_GBUFFER(1),
+			 HOST1X_CHANNEL_CHANNELCTRL);
+#endif
+}
+
 static int channel_submit(struct host1x_job *job)
 {
 	struct host1x_channel *ch = job->channel;
@@ -145,6 +166,7 @@ static int channel_submit(struct host1x_job *job)
 	}
 
 	host1x_channel_set_streamid(ch);
+	host1x_enable_gather_filter(ch);
 
 	/* begin a CDMA submit */
 	err = host1x_cdma_begin(&ch->cdma, job);
@@ -204,27 +226,6 @@ static int channel_submit(struct host1x_job *job)
 	return err;
 }
 
-static void enable_gather_filter(struct host1x *host,
-				 struct host1x_channel *ch)
-{
-#if HOST1X_HW >= 6
-	u32 val;
-
-	if (!host->hv_regs)
-		return;
-
-	val = host1x_hypervisor_readl(
-		host, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
-	val |= BIT(ch->id % 32);
-	host1x_hypervisor_writel(
-		host, val, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
-#elif HOST1X_HW >= 4
-	host1x_ch_writel(ch,
-			 HOST1X_CHANNEL_CHANNELCTRL_KERNEL_FILTER_GBUFFER(1),
-			 HOST1X_CHANNEL_CHANNELCTRL);
-#endif
-}
-
 static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
 			       unsigned int index)
 {
@@ -233,7 +234,6 @@ static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
 #else
 	ch->regs = dev->regs + index * 0x100;
 #endif
-	enable_gather_filter(dev, ch);
 	return 0;
 }
 
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index 6d1f3c0fdbe7..1600de3ccceb 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -288,14 +288,11 @@ int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
 			 "host1x_sp_%02u", id);
 	}
 
-	host1x_intr_start(host);
-
 	return 0;
 }
 
 void host1x_intr_deinit(struct host1x *host)
 {
-	host1x_intr_stop(host);
 }
 
 void host1x_intr_start(struct host1x *host)
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index e648ebbb2027..b5ca3fdf33b2 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -143,6 +143,8 @@ void host1x_syncpt_restore(struct host1x *host)
 	for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
 		host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
 
+	host1x_hw_syncpt_enable_protection(host);
+
 	wmb();
 }
 
@@ -366,9 +368,6 @@ int host1x_syncpt_init(struct host1x *host)
 	host->syncpt = syncpt;
 	host->bases = bases;
 
-	host1x_syncpt_restore(host);
-	host1x_hw_syncpt_enable_protection(host);
-
 	/* Allocate sync point to use for clearing waits for expired fences */
 	host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
 	if (!host->nop_sp)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 11/37] gpu: host1x: Add stub driver for MPE, VI, EPP and ISP
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (9 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 10/37] gpu: host1x: Add runtime PM support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 12/37] drm/tegra: dc: Support OPP and SoC core voltage scaling Dmitry Osipenko
                   ` (26 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

We never had drivers for MPE, VI, EPP and ISP hardware units on Tegra20
and Tegra30 and they are specified in the device-trees. Thus, a device is
getting created for them by host1x bus, but driver is never getting bound.
After adding support for generic power domains, we now have a situation
where the state of PMC driver is never synced because consumer device
never becomes ready due to the missing drivers and it needs to be synced
in order to allow scaling of SoC core voltage. Add a stub driver in order
to resolve the problem.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/host1x/dev.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 0945df6039af..400a3b9d8857 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -616,7 +616,46 @@ static struct platform_driver tegra_host1x_driver = {
 	.remove = host1x_remove,
 };
 
+/*
+ * We never had drivers for MPE, VI, EPP and ISP hardware units on Tegra20
+ * and Tegra30, but they are specified in the device-trees, and thus,
+ * device entity is getting created for them by host1x bus a, but driver
+ * is never getting bound.  After adding support for generic power domains
+ * on Tegra20/30, we now have a situation where the state of PMC driver is
+ * never synced because consumer device never becomes ready due to the
+ * missing drivers.  The PMC state needs to be synced in order to allow
+ * scaling of the SoC core voltage.  In order to solve this problem,
+ * we will create and bind a dummy driver to the offending devices until
+ * we will have a real driver for them.
+ */
+static const struct of_device_id host1x_stub_of_matches[] = {
+	{ .compatible = "nvidia,tegra20-mpe", },
+	{ .compatible = "nvidia,tegra30-mpe", },
+	{ .compatible = "nvidia,tegra20-epp", },
+	{ .compatible = "nvidia,tegra30-epp", },
+	{ .compatible = "nvidia,tegra20-vi", },
+	{ .compatible = "nvidia,tegra30-vi", },
+	{ .compatible = "nvidia,tegra20-isp", },
+	{ .compatible = "nvidia,tegra30-isp", },
+	{ /* sentinel */ }
+};
+
+static int host1x_stub_probe(struct platform_device *pdev)
+{
+	pm_runtime_enable(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver tegra_host1x_stub_driver = {
+	.driver = {
+		.name = "tegra-host1x-stub",
+		.of_match_table = host1x_stub_of_matches,
+	},
+	.probe = host1x_stub_probe,
+};
+
 static struct platform_driver * const drivers[] = {
+	&tegra_host1x_stub_driver,
 	&tegra_host1x_driver,
 	&tegra_mipi_driver,
 };
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 12/37] drm/tegra: dc: Support OPP and SoC core voltage scaling
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (10 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 11/37] gpu: host1x: Add stub driver for MPE, VI, EPP and ISP Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 13/37] drm/tegra: hdmi: Add OPP support Dmitry Osipenko
                   ` (25 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add OPP and SoC core voltage scaling support to the display controller
driver. This is required for enabling system-wide DVFS on pre-Tegra186
SoCs.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/drm/tegra/dc.c | 69 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.h |  2 ++
 2 files changed, 71 insertions(+)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 51bbbc42a144..fa9cbeb7a958 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -10,9 +10,12 @@
 #include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
+#include <soc/tegra/common.h>
 #include <soc/tegra/pmc.h>
 
 #include <drm/drm_atomic.h>
@@ -1727,6 +1730,47 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 	return 0;
 }
 
+static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
+					  struct tegra_dc_state *state)
+{
+	unsigned long rate, pstate;
+	struct dev_pm_opp *opp;
+	int err;
+
+	if (!dc->has_opp_table)
+		return;
+
+	/* calculate actual pixel clock rate which depends on internal divider */
+	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
+
+	/* find suitable OPP for the rate */
+	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
+
+	if (opp == ERR_PTR(-ERANGE))
+		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
+
+	if (IS_ERR(opp)) {
+		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
+			rate, opp);
+		return;
+	}
+
+	pstate = dev_pm_opp_get_required_pstate(opp, 0);
+	dev_pm_opp_put(opp);
+
+	/*
+	 * The minimum core voltage depends on the pixel clock rate (which
+	 * depends on internal clock divider of the CRTC) and not on the
+	 * rate of the display controller clock. This is why we're not using
+	 * dev_pm_opp_set_rate() API and instead controlling the power domain
+	 * directly.
+	 */
+	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
+	if (err)
+		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
+			pstate, err);
+}
+
 static void tegra_dc_commit_state(struct tegra_dc *dc,
 				  struct tegra_dc_state *state)
 {
@@ -1766,6 +1810,8 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,
 		value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
 	}
+
+	tegra_dc_update_voltage_state(dc, state);
 }
 
 static void tegra_dc_stop(struct tegra_dc *dc)
@@ -1859,6 +1905,8 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
 	err = host1x_client_suspend(&dc->client);
 	if (err < 0)
 		dev_err(dc->dev, "failed to suspend: %d\n", err);
+
+	dev_pm_genpd_set_performance_state(dc->dev, 0);
 }
 
 static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -2622,6 +2670,23 @@ static int tegra_dc_couple(struct tegra_dc *dc)
 	return 0;
 }
 
+static int tegra_dc_init_opp_table(struct tegra_dc *dc)
+{
+	struct tegra_core_opp_params opp_params = {};
+	int err;
+
+	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
+	if (err && err != -ENODEV)
+		return err;
+
+	if (err)
+		dc->has_opp_table = false;
+	else
+		dc->has_opp_table = true;
+
+	return 0;
+}
+
 static int tegra_dc_probe(struct platform_device *pdev)
 {
 	u64 dma_mask = dma_get_mask(pdev->dev.parent);
@@ -2687,6 +2752,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
 		tegra_powergate_power_off(dc->powergate);
 	}
 
+	err = tegra_dc_init_opp_table(dc);
+	if (err < 0)
+		return err;
+
 	dc->regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(dc->regs))
 		return PTR_ERR(dc->regs);
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 5e13f1cfd749..88fc9c634c1d 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -91,6 +91,8 @@ struct tegra_dc {
 	struct drm_info_list *debugfs_files;
 
 	const struct tegra_dc_soc_info *soc;
+
+	bool has_opp_table;
 };
 
 static inline struct tegra_dc *
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 13/37] drm/tegra: hdmi: Add OPP support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (11 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 12/37] drm/tegra: dc: Support OPP and SoC core voltage scaling Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 14/37] drm/tegra: gr2d: Support OPP and power management Dmitry Osipenko
                   ` (24 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The HDMI on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now HDMI driver must use
OPP API for driving the controller's clock rate because OPP API takes
care of reconfiguring the domain's performance state in accordance to
the clock rate. Add OPP support to the HDMI driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/drm/tegra/hdmi.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index e5d2a4026028..5b5d174c5a2b 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -11,10 +11,13 @@
 #include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_opp.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 
+#include <soc/tegra/common.h>
+
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_debugfs.h>
@@ -1195,7 +1198,7 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
 	h_back_porch = mode->htotal - mode->hsync_end;
 	h_front_porch = mode->hsync_start - mode->hdisplay;
 
-	err = clk_set_rate(hdmi->clk, hdmi->pixel_clock);
+	err = dev_pm_opp_set_rate(hdmi->dev, hdmi->pixel_clock);
 	if (err < 0) {
 		dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n",
 			err);
@@ -1708,6 +1711,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
 	hdmi->output.dev = &pdev->dev;
 
+	err = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+	if (err)
+		return err;
+
 	err = tegra_output_probe(&hdmi->output);
 	if (err < 0)
 		return err;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 14/37] drm/tegra: gr2d: Support OPP and power management
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (12 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 13/37] drm/tegra: hdmi: Add OPP support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 15/37] drm/tegra: gr3d: " Dmitry Osipenko
                   ` (23 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add OPP and PM support to the GR2D driver. This is required for enabling
system-wide DVFS and supporting dynamic power management using a generic
power domain.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/drm/tegra/gr2d.c | 156 +++++++++++++++++++++++++++++++++--
 1 file changed, 148 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index de288cba3905..099fd7e2a462 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -7,11 +7,22 @@
 #include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <soc/tegra/common.h>
 
 #include "drm.h"
 #include "gem.h"
 #include "gr2d.h"
 
+enum {
+	RST_GR2D_MC,
+	RST_GR2D,
+	RST_GR2D_MAX,
+};
+
 struct gr2d_soc {
 	unsigned int version;
 };
@@ -21,6 +32,9 @@ struct gr2d {
 	struct host1x_channel *channel;
 	struct clk *clk;
 
+	struct reset_control_bulk_data resets[RST_GR2D_MAX];
+	unsigned int nresets;
+
 	const struct gr2d_soc *soc;
 
 	DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
@@ -101,16 +115,24 @@ static int gr2d_open_channel(struct tegra_drm_client *client,
 			     struct tegra_drm_context *context)
 {
 	struct gr2d *gr2d = to_gr2d(client);
+	int err;
 
 	context->channel = host1x_channel_get(gr2d->channel);
 	if (!context->channel)
 		return -ENOMEM;
 
+	err = pm_runtime_resume_and_get(client->base.dev);
+	if (err) {
+		host1x_channel_put(context->channel);
+		return err;
+	}
+
 	return 0;
 }
 
 static void gr2d_close_channel(struct tegra_drm_context *context)
 {
+	pm_runtime_put_sync(context->client->base.dev);
 	host1x_channel_put(context->channel);
 }
 
@@ -190,6 +212,27 @@ static const u32 gr2d_addr_regs[] = {
 	GR2D_VA_BASE_ADDR_SB,
 };
 
+static int gr2d_get_resets(struct device *dev, struct gr2d *gr2d)
+{
+	int err;
+
+	gr2d->resets[RST_GR2D_MC].id = "mc";
+	gr2d->resets[RST_GR2D].id = "2d";
+	gr2d->nresets = RST_GR2D_MAX;
+
+	err = devm_reset_control_bulk_get_optional_exclusive_released(
+				dev, gr2d->nresets, gr2d->resets);
+	if (err) {
+		dev_err(dev, "failed to get reset: %d\n", err);
+		return err;
+	}
+
+	if (WARN_ON(!gr2d->resets[RST_GR2D].rstc))
+		return -EINVAL;
+
+	return 0;
+}
+
 static int gr2d_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -202,6 +245,8 @@ static int gr2d_probe(struct platform_device *pdev)
 	if (!gr2d)
 		return -ENOMEM;
 
+	platform_set_drvdata(pdev, gr2d);
+
 	gr2d->soc = of_device_get_match_data(dev);
 
 	syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
@@ -214,11 +259,13 @@ static int gr2d_probe(struct platform_device *pdev)
 		return PTR_ERR(gr2d->clk);
 	}
 
-	err = clk_prepare_enable(gr2d->clk);
-	if (err) {
-		dev_err(dev, "cannot turn on clock\n");
+	err = devm_tegra_core_dev_init_opp_table_simple(dev);
+	if (err)
+		return err;
+
+	err = gr2d_get_resets(dev, gr2d);
+	if (err)
 		return err;
-	}
 
 	INIT_LIST_HEAD(&gr2d->client.base.list);
 	gr2d->client.base.ops = &gr2d_client_ops;
@@ -231,20 +278,27 @@ static int gr2d_probe(struct platform_device *pdev)
 	gr2d->client.version = gr2d->soc->version;
 	gr2d->client.ops = &gr2d_ops;
 
+	pm_runtime_enable(dev);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_autosuspend_delay(dev, 200);
+
 	err = host1x_client_register(&gr2d->client.base);
 	if (err < 0) {
 		dev_err(dev, "failed to register host1x client: %d\n", err);
-		clk_disable_unprepare(gr2d->clk);
-		return err;
+		goto disable_rpm;
 	}
 
 	/* initialize address register map */
 	for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
 		set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
 
-	platform_set_drvdata(pdev, gr2d);
-
 	return 0;
+
+disable_rpm:
+	pm_runtime_dont_use_autosuspend(dev);
+	pm_runtime_disable(dev);
+
+	return err;
 }
 
 static int gr2d_remove(struct platform_device *pdev)
@@ -259,15 +313,101 @@ static int gr2d_remove(struct platform_device *pdev)
 		return err;
 	}
 
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused gr2d_runtime_suspend(struct device *dev)
+{
+	struct gr2d *gr2d = dev_get_drvdata(dev);
+	int err;
+
+	host1x_channel_stop(gr2d->channel);
+	reset_control_bulk_release(gr2d->nresets, gr2d->resets);
+
+	/*
+	 * GR2D module shouldn't be reset while hardware is idling, otherwise
+	 * host1x's cmdproc will stuck on trying to access any G2 register
+	 * after reset. GR2D module could be either hot-reset or reset after
+	 * power-gating of the HEG partition. Hence we will put in reset only
+	 * the memory client part of the module, the HEG GENPD will take care
+	 * of resetting GR2D module across power-gating.
+	 *
+	 * On Tegra20 there is no HEG partition, but it's okay to have
+	 * undetermined h/w state since userspace is expected to reprogram
+	 * the state on each job submission anyways.
+	 */
+	err = reset_control_acquire(gr2d->resets[RST_GR2D_MC].rstc);
+	if (err) {
+		dev_err(dev, "failed to acquire MC reset: %d\n", err);
+		goto acquire_reset;
+	}
+
+	err = reset_control_assert(gr2d->resets[RST_GR2D_MC].rstc);
+	reset_control_release(gr2d->resets[RST_GR2D_MC].rstc);
+	if (err) {
+		dev_err(dev, "failed to assert MC reset: %d\n", err);
+		goto acquire_reset;
+	}
+
 	clk_disable_unprepare(gr2d->clk);
 
 	return 0;
+
+acquire_reset:
+	reset_control_bulk_acquire(gr2d->nresets, gr2d->resets);
+	reset_control_bulk_deassert(gr2d->nresets, gr2d->resets);
+
+	return err;
+}
+
+static int __maybe_unused gr2d_runtime_resume(struct device *dev)
+{
+	struct gr2d *gr2d = dev_get_drvdata(dev);
+	int err;
+
+	err = reset_control_bulk_acquire(gr2d->nresets, gr2d->resets);
+	if (err) {
+		dev_err(dev, "failed to acquire reset: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(gr2d->clk);
+	if (err) {
+		dev_err(dev, "failed to enable clock: %d\n", err);
+		goto release_reset;
+	}
+
+	/* this is a reset array which deasserts both 2D MC and 2D itself */
+	err = reset_control_bulk_deassert(gr2d->nresets, gr2d->resets);
+	if (err) {
+		dev_err(dev, "failed to deassert reset: %d\n", err);
+		goto disable_clk;
+	}
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(gr2d->clk);
+release_reset:
+	reset_control_bulk_release(gr2d->nresets, gr2d->resets);
+
+	return err;
 }
 
+static const struct dev_pm_ops tegra_gr2d_pm = {
+	SET_RUNTIME_PM_OPS(gr2d_runtime_suspend, gr2d_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+};
+
 struct platform_driver tegra_gr2d_driver = {
 	.driver = {
 		.name = "tegra-gr2d",
 		.of_match_table = gr2d_match,
+		.pm = &tegra_gr2d_pm,
 	},
 	.probe = gr2d_probe,
 	.remove = gr2d_remove,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 15/37] drm/tegra: gr3d: Support OPP and power management
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (13 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 14/37] drm/tegra: gr2d: Support OPP and power management Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 16/37] drm/tegra: vic: Stop channel before suspending Dmitry Osipenko
                   ` (22 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add OPP and add PM support to the GR3D driver. This is required for
enabling system-wide DVFS and supporting dynamic power management using
a generic power domain.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/drm/tegra/gr3d.c | 401 ++++++++++++++++++++++++++++++-----
 1 file changed, 347 insertions(+), 54 deletions(-)

diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 24442ade0da3..b450f1299c3a 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -5,32 +5,48 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/host1x.h>
 #include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
+#include <soc/tegra/common.h>
 #include <soc/tegra/pmc.h>
 
 #include "drm.h"
 #include "gem.h"
 #include "gr3d.h"
 
+enum {
+	RST_GR3D_MC,
+	RST_GR3D,
+	RST_GR3D2_MC,
+	RST_GR3D2,
+	RST_GR3D_MAX,
+};
+
 struct gr3d_soc {
 	unsigned int version;
+	unsigned int num_clocks;
+	unsigned int num_resets;
 };
 
 struct gr3d {
 	struct tegra_drm_client client;
 	struct host1x_channel *channel;
-	struct clk *clk_secondary;
-	struct clk *clk;
-	struct reset_control *rst_secondary;
-	struct reset_control *rst;
 
 	const struct gr3d_soc *soc;
+	struct clk_bulk_data *clocks;
+	unsigned int nclocks;
+	struct reset_control_bulk_data resets[RST_GR3D_MAX];
+	unsigned int nresets;
+	bool legacy_pd;
 
 	DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
 };
@@ -109,16 +125,24 @@ static int gr3d_open_channel(struct tegra_drm_client *client,
 			     struct tegra_drm_context *context)
 {
 	struct gr3d *gr3d = to_gr3d(client);
+	int err;
 
 	context->channel = host1x_channel_get(gr3d->channel);
 	if (!context->channel)
 		return -ENOMEM;
 
+	err = pm_runtime_resume_and_get(client->base.dev);
+	if (err) {
+		host1x_channel_put(context->channel);
+		return err;
+	}
+
 	return 0;
 }
 
 static void gr3d_close_channel(struct tegra_drm_context *context)
 {
+	pm_runtime_put_sync(context->client->base.dev);
 	host1x_channel_put(context->channel);
 }
 
@@ -155,14 +179,20 @@ static const struct tegra_drm_client_ops gr3d_ops = {
 
 static const struct gr3d_soc tegra20_gr3d_soc = {
 	.version = 0x20,
+	.num_clocks = 1,
+	.num_resets = 2,
 };
 
 static const struct gr3d_soc tegra30_gr3d_soc = {
 	.version = 0x30,
+	.num_clocks = 2,
+	.num_resets = 4,
 };
 
 static const struct gr3d_soc tegra114_gr3d_soc = {
 	.version = 0x35,
+	.num_clocks = 1,
+	.num_resets = 2,
 };
 
 static const struct of_device_id tegra_gr3d_match[] = {
@@ -278,9 +308,138 @@ static const u32 gr3d_addr_regs[] = {
 	GR3D_GLOBAL_SAMP23SURFADDR(15),
 };
 
+static int gr3d_link_power_domain(struct device *dev, struct device *pd_dev)
+{
+	const u32 link_flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
+	struct device_link *link;
+	int err;
+
+	link = device_link_add(dev, pd_dev, link_flags);
+	if (!link) {
+		dev_err(dev, "failed to link to %s\n", dev_name(pd_dev));
+		return -EINVAL;
+	}
+
+	err = devm_add_action_or_reset(dev, (void *)device_link_del, link);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
+{
+	const char *opp_genpd_names[] = { "3d0", "3d1", NULL };
+	struct device **opp_virt_dev;
+	struct device *pd_dev;
+	unsigned int i;
+	int err;
+
+	err = of_count_phandle_with_args(dev->of_node, "power-domains",
+					 "#power-domain-cells");
+	if (err < 0) {
+		if (err != -ENOENT)
+			return err;
+
+		/*
+		 * Older device-trees don't use GENPD. In this case we should
+		 * toggle power domain manually.
+		 */
+		gr3d->legacy_pd = true;
+		return 0;
+	}
+
+	/*
+	 * The PM domain core automatically attaches a single power domain,
+	 * otherwise it skips attaching completely. We have a single domain
+	 * on Tegra20 and two domains on Tegra30+.
+	 */
+	if (dev->pm_domain)
+		return 0;
+
+	err = devm_pm_opp_attach_genpd(dev, opp_genpd_names, &opp_virt_dev);
+	if (err)
+		return err;
+
+	for (i = 0; opp_genpd_names[i]; i++) {
+		pd_dev = opp_virt_dev[i];
+		if (!pd_dev) {
+			dev_err(dev, "failed to get %s power domain\n",
+				opp_genpd_names[i]);
+			return -EINVAL;
+		}
+
+		err = gr3d_link_power_domain(dev, pd_dev);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int gr3d_set_opp(struct dev_pm_set_opp_data *data)
+{
+	struct gr3d *gr3d = dev_get_drvdata(data->dev);
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < gr3d->nclocks; i++) {
+		err = clk_set_rate(gr3d->clocks[i].clk, data->new_opp.rate);
+		if (err) {
+			dev_err(data->dev, "failed to set %s rate to %lu: %d\n",
+				gr3d->clocks[i].id, data->new_opp.rate, err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static int gr3d_get_clocks(struct device *dev, struct gr3d *gr3d)
+{
+	int err;
+
+	err = devm_clk_bulk_get_all(dev, &gr3d->clocks);
+	if (err < 0) {
+		dev_err(dev, "failed to get clock: %d\n", err);
+		return err;
+	}
+	gr3d->nclocks = err;
+
+	if (gr3d->nclocks != gr3d->soc->num_clocks) {
+		dev_err(dev, "invalid number of clocks: %u\n", gr3d->nclocks);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int gr3d_get_resets(struct device *dev, struct gr3d *gr3d)
+{
+	int err;
+
+	gr3d->nresets = gr3d->soc->num_resets;
+	gr3d->resets[RST_GR3D2_MC].id = "mc2";
+	gr3d->resets[RST_GR3D_MC].id = "mc";
+	gr3d->resets[RST_GR3D2].id = "3d2";
+	gr3d->resets[RST_GR3D].id = "3d";
+
+	err = devm_reset_control_bulk_get_optional_exclusive_released(
+				dev, gr3d->nresets, gr3d->resets);
+	if (err) {
+		dev_err(dev, "failed to get reset: %d\n", err);
+		return err;
+	}
+
+	if (WARN_ON(!gr3d->resets[RST_GR3D].rstc) ||
+	    WARN_ON(!gr3d->resets[RST_GR3D2].rstc && gr3d->nresets == 4))
+		return -EINVAL;
+
+	return 0;
+}
+
 static int gr3d_probe(struct platform_device *pdev)
 {
-	struct device_node *np = pdev->dev.of_node;
 	struct host1x_syncpt **syncpts;
 	struct gr3d *gr3d;
 	unsigned int i;
@@ -290,56 +449,33 @@ static int gr3d_probe(struct platform_device *pdev)
 	if (!gr3d)
 		return -ENOMEM;
 
+	platform_set_drvdata(pdev, gr3d);
+
 	gr3d->soc = of_device_get_match_data(&pdev->dev);
 
 	syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL);
 	if (!syncpts)
 		return -ENOMEM;
 
-	gr3d->clk = devm_clk_get(&pdev->dev, NULL);
-	if (IS_ERR(gr3d->clk)) {
-		dev_err(&pdev->dev, "cannot get clock\n");
-		return PTR_ERR(gr3d->clk);
-	}
-
-	gr3d->rst = devm_reset_control_get(&pdev->dev, "3d");
-	if (IS_ERR(gr3d->rst)) {
-		dev_err(&pdev->dev, "cannot get reset\n");
-		return PTR_ERR(gr3d->rst);
-	}
+	err = gr3d_get_clocks(&pdev->dev, gr3d);
+	if (err)
+		return err;
 
-	if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) {
-		gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2");
-		if (IS_ERR(gr3d->clk_secondary)) {
-			dev_err(&pdev->dev, "cannot get secondary clock\n");
-			return PTR_ERR(gr3d->clk_secondary);
-		}
+	err = gr3d_get_resets(&pdev->dev, gr3d);
+	if (err)
+		return err;
 
-		gr3d->rst_secondary = devm_reset_control_get(&pdev->dev,
-								"3d2");
-		if (IS_ERR(gr3d->rst_secondary)) {
-			dev_err(&pdev->dev, "cannot get secondary reset\n");
-			return PTR_ERR(gr3d->rst_secondary);
-		}
-	}
+	err = gr3d_init_power(&pdev->dev, gr3d);
+	if (err)
+		return err;
 
-	err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk,
-						gr3d->rst);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to power up 3D unit\n");
+	err = devm_pm_opp_register_set_opp_helper(&pdev->dev, gr3d_set_opp);
+	if (err)
 		return err;
-	}
 
-	if (gr3d->clk_secondary) {
-		err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1,
-							gr3d->clk_secondary,
-							gr3d->rst_secondary);
-		if (err < 0) {
-			dev_err(&pdev->dev,
-				"failed to power up secondary 3D unit\n");
-			return err;
-		}
-	}
+	err = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+	if (err)
+		return err;
 
 	INIT_LIST_HEAD(&gr3d->client.base.list);
 	gr3d->client.base.ops = &gr3d_client_ops;
@@ -352,20 +488,28 @@ static int gr3d_probe(struct platform_device *pdev)
 	gr3d->client.version = gr3d->soc->version;
 	gr3d->client.ops = &gr3d_ops;
 
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, 200);
+
 	err = host1x_client_register(&gr3d->client.base);
 	if (err < 0) {
 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
 			err);
-		return err;
+		goto disable_rpm;
 	}
 
 	/* initialize address register map */
 	for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
 		set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
 
-	platform_set_drvdata(pdev, gr3d);
-
 	return 0;
+
+disable_rpm:
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return err;
 }
 
 static int gr3d_remove(struct platform_device *pdev)
@@ -380,23 +524,172 @@ static int gr3d_remove(struct platform_device *pdev)
 		return err;
 	}
 
-	if (gr3d->clk_secondary) {
-		reset_control_assert(gr3d->rst_secondary);
-		tegra_powergate_power_off(TEGRA_POWERGATE_3D1);
-		clk_disable_unprepare(gr3d->clk_secondary);
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int gr3d_legacy_domain_power_up(struct device *dev, const char *name,
+				       unsigned int id)
+{
+	struct gr3d *gr3d = dev_get_drvdata(dev);
+	struct reset_control *reset;
+	struct clk *clk;
+	unsigned int i;
+	int err;
+
+	/*
+	 * Tegra20 device-tree doesn't specify 3d clock name and there is only
+	 * one clock for Tegra20. Tegra30+ device-trees always specified names
+	 * for the clocks.
+	 */
+	if (gr3d->nclocks == 1) {
+		if (id == TEGRA_POWERGATE_3D1)
+			return 0;
+
+		clk = gr3d->clocks[0].clk;
+	} else {
+		for (i = 0; i < gr3d->nclocks; i++) {
+			if (WARN_ON_ONCE(!gr3d->clocks[i].id))
+				continue;
+
+			if (!strcmp(gr3d->clocks[i].id, name)) {
+				clk = gr3d->clocks[i].clk;
+				break;
+			}
+		}
+
+		if (WARN_ON_ONCE(i == gr3d->nclocks))
+			return -EINVAL;
+	}
+
+	/*
+	 * We use array of resets, which includes MC resets, and MC
+	 * reset shouldn't be asserted while hardware is gated because
+	 * MC flushing will fail for gated hardware. Hence for legacy
+	 * PD we request the individual reset separately.
+	 */
+	reset = reset_control_get_exclusive_released(dev, name);
+	if (IS_ERR(reset))
+		return PTR_ERR(reset);
+
+	err = reset_control_acquire(reset);
+	if (err) {
+		dev_err(dev, "failed to acquire %s reset: %d\n", name, err);
+	} else {
+		err = tegra_powergate_sequence_power_up(id, clk, reset);
+		reset_control_release(reset);
 	}
 
-	reset_control_assert(gr3d->rst);
-	tegra_powergate_power_off(TEGRA_POWERGATE_3D);
-	clk_disable_unprepare(gr3d->clk);
+	reset_control_put(reset);
+	if (err)
+		return err;
+
+	/*
+	 * tegra_powergate_sequence_power_up() leaves clocks enabled
+	 * while GENPD not, hence keep clock-enable balanced.
+	 */
+	clk_disable_unprepare(clk);
 
 	return 0;
 }
 
+static int gr3d_legacy_power_up(struct device *dev)
+{
+	struct gr3d *gr3d = dev_get_drvdata(dev);
+	int err;
+
+	if (gr3d->legacy_pd) {
+		err = gr3d_legacy_domain_power_up(dev, "3d",
+						  TEGRA_POWERGATE_3D);
+		if (err)
+			return err;
+
+		err = gr3d_legacy_domain_power_up(dev, "3d2",
+						  TEGRA_POWERGATE_3D1);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused gr3d_runtime_suspend(struct device *dev)
+{
+	struct gr3d *gr3d = dev_get_drvdata(dev);
+	int err;
+
+	host1x_channel_stop(gr3d->channel);
+
+	err = reset_control_bulk_assert(gr3d->nresets, gr3d->resets);
+	if (err) {
+		dev_err(dev, "failed to assert reset: %d\n", err);
+		return err;
+	}
+
+	usleep_range(10, 20);
+
+	/*
+	 * Older device-trees don't specify MC resets and power-gating can't
+	 * be done safely in that case. Hence we will keep the power ungated
+	 * for older DTBs. For newer DTBs, GENPD will perform the power-gating.
+	 */
+
+	clk_bulk_disable_unprepare(gr3d->nclocks, gr3d->clocks);
+	reset_control_bulk_release(gr3d->nresets, gr3d->resets);
+
+	return 0;
+}
+
+static int __maybe_unused gr3d_runtime_resume(struct device *dev)
+{
+	struct gr3d *gr3d = dev_get_drvdata(dev);
+	int err;
+
+	err = gr3d_legacy_power_up(dev);
+	if (err)
+		return err;
+
+	err = reset_control_bulk_acquire(gr3d->nresets, gr3d->resets);
+	if (err) {
+		dev_err(dev, "failed to acquire reset: %d\n", err);
+		return err;
+	}
+
+	err = clk_bulk_prepare_enable(gr3d->nclocks, gr3d->clocks);
+	if (err) {
+		dev_err(dev, "failed to enable clock: %d\n", err);
+		goto release_reset;
+	}
+
+	err = reset_control_bulk_deassert(gr3d->nresets, gr3d->resets);
+	if (err) {
+		dev_err(dev, "failed to deassert reset: %d\n", err);
+		goto disable_clk;
+	}
+
+	return 0;
+
+disable_clk:
+	clk_bulk_disable_unprepare(gr3d->nclocks, gr3d->clocks);
+release_reset:
+	reset_control_bulk_release(gr3d->nresets, gr3d->resets);
+
+	return err;
+}
+
+static const struct dev_pm_ops tegra_gr3d_pm = {
+	SET_RUNTIME_PM_OPS(gr3d_runtime_suspend, gr3d_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+};
+
 struct platform_driver tegra_gr3d_driver = {
 	.driver = {
 		.name = "tegra-gr3d",
 		.of_match_table = tegra_gr3d_match,
+		.pm = &tegra_gr3d_pm,
 	},
 	.probe = gr3d_probe,
 	.remove = gr3d_remove,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 16/37] drm/tegra: vic: Stop channel before suspending
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (14 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 15/37] drm/tegra: gr3d: " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 17/37] usb: chipidea: tegra: Add runtime PM support Dmitry Osipenko
                   ` (21 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Host1x channel should be idling before hardware is turned off, hence
stop the channel in the suspend callback.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/drm/tegra/vic.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index c9d55a9a3180..3bf08893b561 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -525,8 +525,23 @@ static int vic_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static __maybe_unused int vic_suspend(struct device *dev)
+{
+	struct vic *vic = dev_get_drvdata(dev);
+	int err;
+
+	host1x_channel_stop(vic->channel);
+
+	err = pm_runtime_force_suspend(dev);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 static const struct dev_pm_ops vic_pm_ops = {
 	SET_RUNTIME_PM_OPS(vic_runtime_suspend, vic_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(vic_suspend, pm_runtime_force_resume)
 };
 
 struct platform_driver tegra_vic_driver = {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 17/37] usb: chipidea: tegra: Add runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (15 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 16/37] drm/tegra: vic: Stop channel before suspending Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 18/37] bus: tegra-gmi: " Dmitry Osipenko
                   ` (20 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The Tegra USB controller belongs to the core power domain and we're going
to enable GENPD support for the core domain. Now USB controller must be
resumed using runtime PM API in order to initialize the USB power state.
We already support runtime PM for the CI device, but CI's PM is separated
from the RPM managed by tegra-usb driver. Add runtime PM support to the
main USB driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/usb/chipidea/ci_hdrc_tegra.c | 50 +++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 60361141ac04..456628d72706 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -7,6 +7,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
 #include <linux/usb.h>
@@ -278,6 +279,8 @@ static int tegra_usb_probe(struct platform_device *pdev)
 	if (!usb)
 		return -ENOMEM;
 
+	platform_set_drvdata(pdev, usb);
+
 	soc = of_device_get_match_data(&pdev->dev);
 	if (!soc) {
 		dev_err(&pdev->dev, "failed to match OF data\n");
@@ -296,11 +299,10 @@ static int tegra_usb_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	err = clk_prepare_enable(usb->clk);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
-		return err;
-	}
+	pm_runtime_enable(&pdev->dev);
+	err = pm_runtime_resume_and_get(&pdev->dev);
+	if (err)
+		goto disable_pm;
 
 	if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
 		usb->needs_double_reset = true;
@@ -320,8 +322,6 @@ static int tegra_usb_probe(struct platform_device *pdev)
 	if (err)
 		goto fail_power_off;
 
-	platform_set_drvdata(pdev, usb);
-
 	/* setup and register ChipIdea HDRC device */
 	usb->soc = soc;
 	usb->data.name = "tegra-usb";
@@ -350,7 +350,10 @@ static int tegra_usb_probe(struct platform_device *pdev)
 phy_shutdown:
 	usb_phy_shutdown(usb->phy);
 fail_power_off:
-	clk_disable_unprepare(usb->clk);
+	pm_runtime_put(&pdev->dev);
+disable_pm:
+	pm_runtime_disable(&pdev->dev);
+
 	return err;
 }
 
@@ -360,15 +363,46 @@ static int tegra_usb_remove(struct platform_device *pdev)
 
 	ci_hdrc_remove_device(usb->dev);
 	usb_phy_shutdown(usb->phy);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
+{
+	struct tegra_usb *usb = dev_get_drvdata(dev);
+	int err;
+
+	err = clk_prepare_enable(usb->clk);
+	if (err < 0) {
+		dev_err(dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
+{
+	struct tegra_usb *usb = dev_get_drvdata(dev);
+
 	clk_disable_unprepare(usb->clk);
 
 	return 0;
 }
 
+static const struct dev_pm_ops tegra_usb_pm = {
+	SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
+			   NULL)
+};
+
 static struct platform_driver tegra_usb_driver = {
 	.driver = {
 		.name = "tegra-usb",
 		.of_match_table = tegra_usb_of_match,
+		.pm = &tegra_usb_pm,
 	},
 	.probe = tegra_usb_probe,
 	.remove = tegra_usb_remove,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 18/37] bus: tegra-gmi: Add runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (16 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 17/37] usb: chipidea: tegra: Add runtime PM support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 19/37] pwm: tegra: Add runtime PM and OPP support Dmitry Osipenko
                   ` (19 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The GMI bus on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now GMI must be resumed using
runtime PM API in order to initialize the GMI power state. Add runtime PM
support to the GMI driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/bus/tegra-gmi.c | 44 +++++++++++++++++++++++++++++++++++------
 1 file changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/bus/tegra-gmi.c b/drivers/bus/tegra-gmi.c
index a6570789f7af..c3ff84e05de9 100644
--- a/drivers/bus/tegra-gmi.c
+++ b/drivers/bus/tegra-gmi.c
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
 #define TEGRA_GMI_CONFIG		0x00
@@ -54,9 +55,10 @@ static int tegra_gmi_enable(struct tegra_gmi *gmi)
 {
 	int err;
 
-	err = clk_prepare_enable(gmi->clk);
-	if (err < 0) {
-		dev_err(gmi->dev, "failed to enable clock: %d\n", err);
+	pm_runtime_enable(gmi->dev);
+	err = pm_runtime_resume_and_get(gmi->dev);
+	if (err) {
+		pm_runtime_disable(gmi->dev);
 		return err;
 	}
 
@@ -83,7 +85,9 @@ static void tegra_gmi_disable(struct tegra_gmi *gmi)
 	writel(config, gmi->base + TEGRA_GMI_CONFIG);
 
 	reset_control_assert(gmi->rst);
-	clk_disable_unprepare(gmi->clk);
+
+	pm_runtime_put(gmi->dev);
+	pm_runtime_disable(gmi->dev);
 }
 
 static int tegra_gmi_parse_dt(struct tegra_gmi *gmi)
@@ -213,6 +217,7 @@ static int tegra_gmi_probe(struct platform_device *pdev)
 	if (!gmi)
 		return -ENOMEM;
 
+	platform_set_drvdata(pdev, gmi);
 	gmi->dev = dev;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -247,8 +252,6 @@ static int tegra_gmi_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	platform_set_drvdata(pdev, gmi);
-
 	return 0;
 }
 
@@ -262,6 +265,34 @@ static int tegra_gmi_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev)
+{
+	struct tegra_gmi *gmi = dev_get_drvdata(dev);
+	int err;
+
+	err = clk_prepare_enable(gmi->clk);
+	if (err < 0) {
+		dev_err(gmi->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused tegra_gmi_runtime_suspend(struct device *dev)
+{
+	struct tegra_gmi *gmi = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(gmi->clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra_gmi_pm = {
+	SET_RUNTIME_PM_OPS(tegra_gmi_runtime_suspend, tegra_gmi_runtime_resume,
+			   NULL)
+};
+
 static const struct of_device_id tegra_gmi_id_table[] = {
 	{ .compatible = "nvidia,tegra20-gmi", },
 	{ .compatible = "nvidia,tegra30-gmi", },
@@ -275,6 +306,7 @@ static struct platform_driver tegra_gmi_driver = {
 	.driver = {
 		.name		= "tegra-gmi",
 		.of_match_table	= tegra_gmi_id_table,
+		.pm = &tegra_gmi_pm,
 	},
 };
 module_platform_driver(tegra_gmi_driver);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 19/37] pwm: tegra: Add runtime PM and OPP support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (17 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 18/37] bus: tegra-gmi: " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 20/37] mmc: sdhci-tegra: " Dmitry Osipenko
                   ` (18 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The PWM on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now PWM must be resumed using
runtime PM API in order to initialize the PWM power state. The PWM clock
rate must be changed using OPP API that will reconfigure the power domain
performance state in accordance to the rate. Add runtime PM and OPP
support to the PWM driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/pwm/pwm-tegra.c | 104 ++++++++++++++++++++++++++++++++--------
 1 file changed, 85 insertions(+), 19 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 11a10b575ace..ea124d40f272 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -42,12 +42,16 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pm_opp.h>
 #include <linux/pwm.h>
 #include <linux/platform_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/reset.h>
 
+#include <soc/tegra/common.h>
+
 #define PWM_ENABLE	(1 << 31)
 #define PWM_DUTY_WIDTH	8
 #define PWM_DUTY_SHIFT	16
@@ -145,12 +149,25 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		required_clk_rate =
 			(NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH;
 
-		err = clk_set_rate(pc->clk, required_clk_rate);
-		if (err < 0)
+		/*
+		 * GENPD performance state should be changed only for
+		 * the resumed device because GENPD core tracks performance
+		 * state and drops/restores the state on RPM suspend/resume.
+		 */
+		err = pm_runtime_resume_and_get(pc->dev);
+		if (err)
+			return err;
+
+		err = dev_pm_opp_set_rate(pc->dev, required_clk_rate);
+		if (err < 0) {
+			pm_runtime_put(pc->dev);
 			return -EINVAL;
+		}
 
 		/* Store the new rate for further references */
 		pc->clk_rate = clk_get_rate(pc->clk);
+
+		pm_runtime_put(pc->dev);
 	}
 
 	rate = pc->clk_rate >> PWM_DUTY_WIDTH;
@@ -181,8 +198,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * before writing the register. Otherwise, keep it enabled.
 	 */
 	if (!pwm_is_enabled(pwm)) {
-		err = clk_prepare_enable(pc->clk);
-		if (err < 0)
+		err = pm_runtime_resume_and_get(pc->dev);
+		if (err)
 			return err;
 	} else
 		val |= PWM_ENABLE;
@@ -193,7 +210,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * If the PWM is not enabled, turn the clock off again to save power.
 	 */
 	if (!pwm_is_enabled(pwm))
-		clk_disable_unprepare(pc->clk);
+		pm_runtime_put(pc->dev);
 
 	return 0;
 }
@@ -204,8 +221,8 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 	int rc = 0;
 	u32 val;
 
-	rc = clk_prepare_enable(pc->clk);
-	if (rc < 0)
+	rc = pm_runtime_resume_and_get(pc->dev);
+	if (rc)
 		return rc;
 
 	val = pwm_readl(pc, pwm->hwpwm);
@@ -224,7 +241,7 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 	val &= ~PWM_ENABLE;
 	pwm_writel(pc, pwm->hwpwm, val);
 
-	clk_disable_unprepare(pc->clk);
+	pm_runtime_put(pc->dev);
 }
 
 static const struct pwm_ops tegra_pwm_ops = {
@@ -256,11 +273,21 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 	if (IS_ERR(pwm->clk))
 		return PTR_ERR(pwm->clk);
 
+	ret = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+	if (ret)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = pm_runtime_resume_and_get(&pdev->dev);
+	if (ret)
+		goto disable_pm;
+
 	/* Set maximum frequency of the IP */
-	ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency);
+	ret = dev_pm_opp_set_rate(pwm->dev, pwm->soc->max_frequency);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
-		return ret;
+		goto put_pm;
 	}
 
 	/*
@@ -278,7 +305,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 	if (IS_ERR(pwm->rst)) {
 		ret = PTR_ERR(pwm->rst);
 		dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
-		return ret;
+		goto put_pm;
 	}
 
 	reset_control_deassert(pwm->rst);
@@ -291,34 +318,70 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
 		reset_control_assert(pwm->rst);
-		return ret;
+		goto put_pm;
 	}
 
+	pm_runtime_put(&pdev->dev);
+
 	return 0;
+put_pm:
+	pm_runtime_put(&pdev->dev);
+disable_pm:
+	pm_runtime_disable(&pdev->dev);
+	return ret;
 }
 
 static int tegra_pwm_remove(struct platform_device *pdev)
 {
 	struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
+	int err;
 
 	pwmchip_remove(&pc->chip);
 
+	err = pm_runtime_resume_and_get(pc->dev);
+	if (err)
+		return err;
+
 	reset_control_assert(pc->rst);
+	pm_runtime_put(pc->dev);
+	pm_runtime_disable(pc->dev);
 
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int tegra_pwm_suspend(struct device *dev)
+static int __maybe_unused tegra_pwm_runtime_suspend(struct device *dev)
 {
-	return pinctrl_pm_select_sleep_state(dev);
+	struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
+	int err;
+
+	clk_disable_unprepare(pc->clk);
+
+	err = pinctrl_pm_select_sleep_state(dev);
+	if (err) {
+		clk_prepare_enable(pc->clk);
+		return err;
+	}
+
+	return 0;
 }
 
-static int tegra_pwm_resume(struct device *dev)
+static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
 {
-	return pinctrl_pm_select_default_state(dev);
+	struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
+	int err;
+
+	err = pinctrl_pm_select_default_state(dev);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(pc->clk);
+	if (err) {
+		pinctrl_pm_select_sleep_state(dev);
+		return err;
+	}
+
+	return 0;
 }
-#endif
 
 static const struct tegra_pwm_soc tegra20_pwm_soc = {
 	.num_channels = 4,
@@ -344,7 +407,10 @@ static const struct of_device_id tegra_pwm_of_match[] = {
 MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
 
 static const struct dev_pm_ops tegra_pwm_pm_ops = {
-	SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume)
+	SET_RUNTIME_PM_OPS(tegra_pwm_runtime_suspend, tegra_pwm_runtime_resume,
+			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
 };
 
 static struct platform_driver tegra_pwm_driver = {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 20/37] mmc: sdhci-tegra: Add runtime PM and OPP support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (18 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 19/37] pwm: tegra: Add runtime PM and OPP support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-02  3:39     ` kernel test robot
  2021-07-01 23:27 ` [PATCH v7 21/37] mtd: rawnand: tegra: Add runtime PM support Dmitry Osipenko
                   ` (17 subsequent siblings)
  37 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The SDHCI on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now SDHCI must be resumed using
runtime PM API in order to initialize the SDHCI power state. The SDHCI
clock rate must be changed using OPP API that will reconfigure the power
domain performance state in accordance to the rate. Add runtime PM and OPP
support to the SDHCI driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/mmc/host/sdhci-tegra.c | 81 +++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 16 deletions(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 387ce9cdbd7c..19be096b182a 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -15,6 +15,8 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/mmc/card.h>
@@ -24,6 +26,8 @@
 #include <linux/gpio/consumer.h>
 #include <linux/ktime.h>
 
+#include <soc/tegra/common.h>
+
 #include "sdhci-pltfm.h"
 #include "cqhci.h"
 
@@ -758,10 +762,15 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
+	struct device *dev = mmc_dev(host->mmc);
 	unsigned long host_clk;
+	int err;
 
-	if (!clock)
-		return sdhci_set_clock(host, clock);
+	if (!clock) {
+		sdhci_set_clock(host, clock);
+		dev_pm_opp_set_rate(dev, clock);
+		return;
+	}
 
 	/*
 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
@@ -776,7 +785,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 	 * from clk_get_rate() is used.
 	 */
 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
-	clk_set_rate(pltfm_host->clk, host_clk);
+
+	err = dev_pm_opp_set_rate(dev, host_clk);
+	if (err)
+		dev_err(dev, "failed to set clk rate to %luHz: %d\n",
+			host_clk, err);
+
 	tegra_host->curr_clk_rate = host_clk;
 	if (tegra_host->ddr_signaling)
 		host->max_clk = host_clk;
@@ -1696,7 +1710,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 				   "failed to get clock\n");
 		goto err_clk_get;
 	}
-	clk_prepare_enable(clk);
 	pltfm_host->clk = clk;
 
 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
@@ -1704,9 +1717,18 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 	if (IS_ERR(tegra_host->rst)) {
 		rc = PTR_ERR(tegra_host->rst);
 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
-		goto err_rst_get;
+		goto err_clk_get;
 	}
 
+	rc = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+	if (rc)
+		goto err_clk_get;
+
+	pm_runtime_enable(&pdev->dev);
+	rc = pm_runtime_resume_and_get(&pdev->dev);
+	if (rc)
+		goto err_pm_get;
+
 	rc = reset_control_assert(tegra_host->rst);
 	if (rc)
 		goto err_rst_get;
@@ -1728,7 +1750,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 err_add_host:
 	reset_control_assert(tegra_host->rst);
 err_rst_get:
-	clk_disable_unprepare(pltfm_host->clk);
+	pm_runtime_put(&pdev->dev);
+err_pm_get:
+	pm_runtime_disable(&pdev->dev);
 err_clk_get:
 	clk_disable_unprepare(tegra_host->tmclk);
 err_power_req:
@@ -1747,7 +1771,9 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
 
 	reset_control_assert(tegra_host->rst);
 	usleep_range(2000, 4000);
-	clk_disable_unprepare(pltfm_host->clk);
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
 	clk_disable_unprepare(tegra_host->tmclk);
 
 	sdhci_pltfm_free(pdev);
@@ -1755,11 +1781,27 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
+static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+	clk_disable_unprepare(pltfm_host->clk);
+
+	return 0;
+}
+
+static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+	return clk_prepare_enable(pltfm_host->clk);
+}
+
+static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
 	int ret;
 
 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
@@ -1774,17 +1816,22 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
 		return ret;
 	}
 
-	clk_disable_unprepare(pltfm_host->clk);
+	ret = pm_runtime_force_suspend(dev);
+	if (ret) {
+		sdhci_resume_host(host);
+		cqhci_resume(host->mmc);
+		return ret;
+	}
+
 	return 0;
 }
 
 static int __maybe_unused sdhci_tegra_resume(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
-	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	int ret;
 
-	ret = clk_prepare_enable(pltfm_host->clk);
+	ret = pm_runtime_force_resume(dev);
 	if (ret)
 		return ret;
 
@@ -1803,13 +1850,15 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
 suspend_host:
 	sdhci_suspend_host(host);
 disable_clk:
-	clk_disable_unprepare(pltfm_host->clk);
+	pm_runtime_force_suspend(dev);
 	return ret;
 }
-#endif
 
-static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
-			 sdhci_tegra_resume);
+static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
+	SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
+			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
+};
 
 static struct platform_driver sdhci_tegra_driver = {
 	.driver		= {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 21/37] mtd: rawnand: tegra: Add runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (19 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 20/37] mmc: sdhci-tegra: " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 22/37] soc/tegra: fuse: Clear fuse->clk on driver probe failure Dmitry Osipenko
                   ` (16 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The NAND on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now NAND must be resumed using
runtime PM API in order to initialize the NAND power state. Add runtime PM
support to the NAND driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/mtd/nand/raw/tegra_nand.c | 44 ++++++++++++++++++++++++-------
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
index fbf67722a049..044c7143af7a 100644
--- a/drivers/mtd/nand/raw/tegra_nand.c
+++ b/drivers/mtd/nand/raw/tegra_nand.c
@@ -17,6 +17,7 @@
 #include <linux/mtd/rawnand.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
 #define COMMAND					0x00
@@ -1152,6 +1153,7 @@ static int tegra_nand_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	ctrl->dev = &pdev->dev;
+	platform_set_drvdata(pdev, ctrl);
 	nand_controller_init(&ctrl->controller);
 	ctrl->controller.ops = &tegra_nand_controller_ops;
 
@@ -1168,14 +1170,15 @@ static int tegra_nand_probe(struct platform_device *pdev)
 	if (IS_ERR(ctrl->clk))
 		return PTR_ERR(ctrl->clk);
 
-	err = clk_prepare_enable(ctrl->clk);
+	pm_runtime_enable(ctrl->dev);
+	err = pm_runtime_resume_and_get(ctrl->dev);
 	if (err)
-		return err;
+		goto err_disable_pm;
 
 	err = reset_control_reset(rst);
 	if (err) {
 		dev_err(ctrl->dev, "Failed to reset HW: %d\n", err);
-		goto err_disable_clk;
+		goto err_put_pm;
 	}
 
 	writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD);
@@ -1190,21 +1193,21 @@ static int tegra_nand_probe(struct platform_device *pdev)
 			       dev_name(&pdev->dev), ctrl);
 	if (err) {
 		dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err);
-		goto err_disable_clk;
+		goto err_put_pm;
 	}
 
 	writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL);
 
 	err = tegra_nand_chips_init(ctrl->dev, ctrl);
 	if (err)
-		goto err_disable_clk;
-
-	platform_set_drvdata(pdev, ctrl);
+		goto err_put_pm;
 
 	return 0;
 
-err_disable_clk:
-	clk_disable_unprepare(ctrl->clk);
+err_put_pm:
+	pm_runtime_put(ctrl->dev);
+err_disable_pm:
+	pm_runtime_disable(ctrl->dev);
 	return err;
 }
 
@@ -1221,11 +1224,33 @@ static int tegra_nand_remove(struct platform_device *pdev)
 
 	nand_cleanup(chip);
 
+	pm_runtime_put(ctrl->dev);
+	pm_runtime_disable(ctrl->dev);
+
+	return 0;
+}
+
+static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)
+{
+	struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
+
+	return clk_prepare_enable(ctrl->clk);
+}
+
+static int __maybe_unused tegra_nand_runtime_suspend(struct device *dev)
+{
+	struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
+
 	clk_disable_unprepare(ctrl->clk);
 
 	return 0;
 }
 
+static const struct dev_pm_ops tegra_nand_pm = {
+	SET_RUNTIME_PM_OPS(tegra_nand_runtime_suspend, tegra_nand_runtime_resume,
+			   NULL)
+};
+
 static const struct of_device_id tegra_nand_of_match[] = {
 	{ .compatible = "nvidia,tegra20-nand" },
 	{ /* sentinel */ }
@@ -1236,6 +1261,7 @@ static struct platform_driver tegra_nand_driver = {
 	.driver = {
 		.name = "tegra-nand",
 		.of_match_table = tegra_nand_of_match,
+		.pm = &tegra_nand_pm,
 	},
 	.probe = tegra_nand_probe,
 	.remove = tegra_nand_remove,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 22/37] soc/tegra: fuse: Clear fuse->clk on driver probe failure
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (20 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 21/37] mtd: rawnand: tegra: Add runtime PM support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 23/37] soc/tegra: fuse: Add runtime PM support Dmitry Osipenko
                   ` (15 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The fuse->clk must be cleared if FUSE driver fails to probe, otherwise
tegra_fuse_readl() will crash.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/fuse/fuse-tegra.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index 3d9da3d359da..2434c570b53c 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -246,6 +246,7 @@ static int tegra_fuse_probe(struct platform_device *pdev)
 	return 0;
 
 restore:
+	fuse->clk = NULL;
 	fuse->base = base;
 	return err;
 }
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 23/37] soc/tegra: fuse: Add runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (21 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 22/37] soc/tegra: fuse: Clear fuse->clk on driver probe failure Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend Dmitry Osipenko
                   ` (14 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The Tegra FUSE belongs to the core power domain and we're going to enable
GENPD support for the core domain. Now FUSE device must be resumed using
runtime PM API in order to initialize the FUSE power state. Add runtime PM
support to the FUSE driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/fuse/fuse-tegra.c   | 30 +++++++++++++++++++++++++++
 drivers/soc/tegra/fuse/fuse-tegra20.c | 10 +++++----
 drivers/soc/tegra/fuse/fuse-tegra30.c |  9 ++++----
 3 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index 2434c570b53c..747237865aff 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 
@@ -210,6 +211,8 @@ static int tegra_fuse_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, fuse);
 	fuse->dev = &pdev->dev;
 
+	pm_runtime_enable(&pdev->dev);
+
 	if (fuse->soc->probe) {
 		err = fuse->soc->probe(fuse);
 		if (err < 0)
@@ -248,13 +251,40 @@ static int tegra_fuse_probe(struct platform_device *pdev)
 restore:
 	fuse->clk = NULL;
 	fuse->base = base;
+	pm_runtime_disable(&pdev->dev);
 	return err;
 }
 
+static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev)
+{
+	int err;
+
+	err = clk_prepare_enable(fuse->clk);
+	if (err < 0) {
+		dev_err(dev, "failed to enable FUSE clock: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev)
+{
+	clk_disable_unprepare(fuse->clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra_fuse_pm = {
+	SET_RUNTIME_PM_OPS(tegra_fuse_runtime_suspend, tegra_fuse_runtime_resume,
+			   NULL)
+};
+
 static struct platform_driver tegra_fuse_driver = {
 	.driver = {
 		.name = "tegra-fuse",
 		.of_match_table = tegra_fuse_match,
+		.pm = &tegra_fuse_pm,
 		.suppress_bind_attrs = true,
 	},
 	.probe = tegra_fuse_probe,
diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c
index 16aaa28573ac..cd6a273707fe 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra20.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra20.c
@@ -16,6 +16,7 @@
 #include <linux/kobject.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/random.h>
 
 #include <soc/tegra/fuse.h>
@@ -46,6 +47,10 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
 	u32 value = 0;
 	int err;
 
+	err = pm_runtime_resume_and_get(fuse->dev);
+	if (err)
+		return err;
+
 	mutex_lock(&fuse->apbdma.lock);
 
 	fuse->apbdma.config.src_addr = fuse->phys + FUSE_BEGIN + offset;
@@ -66,8 +71,6 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
 
 	reinit_completion(&fuse->apbdma.wait);
 
-	clk_prepare_enable(fuse->clk);
-
 	dmaengine_submit(dma_desc);
 	dma_async_issue_pending(fuse->apbdma.chan);
 	time_left = wait_for_completion_timeout(&fuse->apbdma.wait,
@@ -78,10 +81,9 @@ static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
 	else
 		value = *fuse->apbdma.virt;
 
-	clk_disable_unprepare(fuse->clk);
-
 out:
 	mutex_unlock(&fuse->apbdma.lock);
+	pm_runtime_put(fuse->dev);
 	return value;
 }
 
diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c
index c1aa7815bd6e..dd03565a39a4 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra30.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra30.c
@@ -12,6 +12,7 @@
 #include <linux/of_device.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/random.h>
 
 #include <soc/tegra/fuse.h>
@@ -52,15 +53,13 @@ static u32 tegra30_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
 	u32 value;
 	int err;
 
-	err = clk_prepare_enable(fuse->clk);
-	if (err < 0) {
-		dev_err(fuse->dev, "failed to enable FUSE clock: %d\n", err);
+	err = pm_runtime_resume_and_get(fuse->dev);
+	if (err)
 		return 0;
-	}
 
 	value = readl_relaxed(fuse->base + FUSE_BEGIN + offset);
 
-	clk_disable_unprepare(fuse->clk);
+	pm_runtime_put(fuse->dev);
 
 	return value;
 }
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (22 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 23/37] soc/tegra: fuse: Add runtime PM support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-21 16:00   ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 25/37] clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock Dmitry Osipenko
                   ` (13 subsequent siblings)
  37 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The FUSE clock should be enabled during suspend on Tegra124. Currently
clk driver enables it on all SoCs, but FUSE may require a higher core
voltage on Tegra30 while enabled. Move the quirk into the FUSE driver
and make it specific to Tegra124.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/fuse/fuse-tegra.c   | 31 +++++++++++++++++++++++++++
 drivers/soc/tegra/fuse/fuse-tegra30.c |  1 +
 drivers/soc/tegra/fuse/fuse.h         |  2 ++
 3 files changed, 34 insertions(+)

diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c
index 747237865aff..f660a3557478 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra.c
@@ -275,9 +275,40 @@ static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev)
 	return 0;
 }
 
+static int __maybe_unused tegra_fuse_resume(struct device *dev)
+{
+	int err;
+
+	/*
+	 * Critical for RAM re-repair operation, which must occur on resume
+	 * from LP1 system suspend and as part of CCPLEX cluster switching.
+	 */
+	if (fuse->soc->clk_suspend_on) {
+		err = pm_runtime_force_resume(dev);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused tegra_fuse_suspend(struct device *dev)
+{
+	int err;
+
+	if (fuse->soc->clk_suspend_on) {
+		err = pm_runtime_force_suspend(dev);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 static const struct dev_pm_ops tegra_fuse_pm = {
 	SET_RUNTIME_PM_OPS(tegra_fuse_runtime_suspend, tegra_fuse_runtime_resume,
 			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume)
 };
 
 static struct platform_driver tegra_fuse_driver = {
diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c
index dd03565a39a4..e1f1db3b0526 100644
--- a/drivers/soc/tegra/fuse/fuse-tegra30.c
+++ b/drivers/soc/tegra/fuse/fuse-tegra30.c
@@ -208,6 +208,7 @@ const struct tegra_fuse_soc tegra124_fuse_soc = {
 	.lookups = tegra124_fuse_lookups,
 	.num_lookups = ARRAY_SIZE(tegra124_fuse_lookups),
 	.soc_attr_group = &tegra_soc_attr_group,
+	.clk_suspend_on = true,
 };
 #endif
 
diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h
index e057a58e2060..de58feba0435 100644
--- a/drivers/soc/tegra/fuse/fuse.h
+++ b/drivers/soc/tegra/fuse/fuse.h
@@ -34,6 +34,8 @@ struct tegra_fuse_soc {
 	unsigned int num_lookups;
 
 	const struct attribute_group *soc_attr_group;
+
+	bool clk_suspend_on;
 };
 
 struct tegra_fuse {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 25/37] clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (23 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage Dmitry Osipenko
                   ` (12 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

FUSE driver now takes care of keeping the clock enabled when necessary.
Remove the CLK_IS_CRITICAL flag from the clock.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/clk/tegra/clk-tegra-periph.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c
index 292d6269daf1..4dcf7f7cb8a0 100644
--- a/drivers/clk/tegra/clk-tegra-periph.c
+++ b/drivers/clk/tegra/clk-tegra-periph.c
@@ -777,11 +777,7 @@ static struct tegra_periph_init_data gate_clks[] = {
 	GATE("ahbdma", "hclk", 33, 0, tegra_clk_ahbdma, 0),
 	GATE("apbdma", "pclk", 34, 0, tegra_clk_apbdma, 0),
 	GATE("kbc", "clk_32k", 36, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_kbc, 0),
-	/*
-	 * Critical for RAM re-repair operation, which must occur on resume
-	 * from LP1 system suspend and as part of CCPLEX cluster switching.
-	 */
-	GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, CLK_IS_CRITICAL),
+	GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),
 	GATE("fuse_burn", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse_burn, 0),
 	GATE("kfuse", "clk_m", 40, TEGRA_PERIPH_ON_APB, tegra_clk_kfuse, 0),
 	GATE("apbif", "clk_m", 107, TEGRA_PERIPH_ON_APB, tegra_clk_apbif, 0),
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (24 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 25/37] clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-02  8:29     ` kernel test robot
  2021-07-01 23:27 ` [PATCH v7 27/37] spi: tegra20-slink: Add OPP support Dmitry Osipenko
                   ` (11 subsequent siblings)
  37 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The Tegra SPI driver supports runtime PM, which controls the clock
enable state, but the clk is also enabled separately from the RPM
at the driver probe time, and thus, stays always on. Fix it.

Runtime PM now is always available on Tegra, hence there is no need to
check the RPM presence in the driver anymore. Remove these checks.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/spi/spi-tegra20-slink.c | 67 +++++++++++----------------------
 1 file changed, 22 insertions(+), 45 deletions(-)

diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index 6a726c95ac7a..cbf0b4a0820a 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1061,27 +1061,6 @@ static int tegra_slink_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "Can not get clock %d\n", ret);
 		goto exit_free_master;
 	}
-	ret = clk_prepare(tspi->clk);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Clock prepare failed %d\n", ret);
-		goto exit_free_master;
-	}
-	ret = clk_enable(tspi->clk);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Clock enable failed %d\n", ret);
-		goto exit_clk_unprepare;
-	}
-
-	spi_irq = platform_get_irq(pdev, 0);
-	tspi->irq = spi_irq;
-	ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
-			tegra_slink_isr_thread, IRQF_ONESHOT,
-			dev_name(&pdev->dev), tspi);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
-					tspi->irq);
-		goto exit_clk_disable;
-	}
 
 	tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
 	if (IS_ERR(tspi->rst)) {
@@ -1106,16 +1085,9 @@ static int tegra_slink_probe(struct platform_device *pdev)
 	init_completion(&tspi->xfer_completion);
 
 	pm_runtime_enable(&pdev->dev);
-	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = tegra_slink_runtime_resume(&pdev->dev);
-		if (ret)
-			goto exit_pm_disable;
-	}
-
-	ret = pm_runtime_get_sync(&pdev->dev);
-	if (ret < 0) {
+	ret = pm_runtime_resume_and_get(&pdev->dev);
+	if (ret) {
 		dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
-		pm_runtime_put_noidle(&pdev->dev);
 		goto exit_pm_disable;
 	}
 
@@ -1123,33 +1095,43 @@ static int tegra_slink_probe(struct platform_device *pdev)
 	udelay(2);
 	reset_control_deassert(tspi->rst);
 
+	spi_irq = platform_get_irq(pdev, 0);
+	tspi->irq = spi_irq;
+	ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
+				   tegra_slink_isr_thread, IRQF_ONESHOT,
+				   dev_name(&pdev->dev), tspi);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+			tspi->irq);
+		goto exit_pm_put;
+	}
+
 	tspi->def_command_reg  = SLINK_M_S;
 	tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN;
 	tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
 	tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
-	pm_runtime_put(&pdev->dev);
 
 	master->dev.of_node = pdev->dev.of_node;
 	ret = devm_spi_register_master(&pdev->dev, master);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "can not register to master err %d\n", ret);
-		goto exit_pm_disable;
+		goto exit_free_irq;
 	}
+
+	pm_runtime_put(&pdev->dev);
+
 	return ret;
 
+exit_free_irq:
+	free_irq(spi_irq, tspi);
+exit_pm_put:
+	pm_runtime_put(&pdev->dev);
 exit_pm_disable:
 	pm_runtime_disable(&pdev->dev);
-	if (!pm_runtime_status_suspended(&pdev->dev))
-		tegra_slink_runtime_suspend(&pdev->dev);
+
 	tegra_slink_deinit_dma_param(tspi, false);
 exit_rx_dma_free:
 	tegra_slink_deinit_dma_param(tspi, true);
-exit_free_irq:
-	free_irq(spi_irq, tspi);
-exit_clk_disable:
-	clk_disable(tspi->clk);
-exit_clk_unprepare:
-	clk_unprepare(tspi->clk);
 exit_free_master:
 	spi_master_put(master);
 	return ret;
@@ -1162,9 +1144,6 @@ static int tegra_slink_remove(struct platform_device *pdev)
 
 	free_irq(tspi->irq, tspi);
 
-	clk_disable(tspi->clk);
-	clk_unprepare(tspi->clk);
-
 	if (tspi->tx_dma_chan)
 		tegra_slink_deinit_dma_param(tspi, false);
 
@@ -1172,8 +1151,6 @@ static int tegra_slink_remove(struct platform_device *pdev)
 		tegra_slink_deinit_dma_param(tspi, true);
 
 	pm_runtime_disable(&pdev->dev);
-	if (!pm_runtime_status_suspended(&pdev->dev))
-		tegra_slink_runtime_suspend(&pdev->dev);
 
 	return 0;
 }
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 27/37] spi: tegra20-slink: Add OPP support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (25 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 28/37] memory: tegra20-emc: Add minimal runtime PM support Dmitry Osipenko
                   ` (10 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

The SPI on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now SPI driver must use OPP
API for driving the controller's clock rate because OPP API takes care
of reconfiguring the domain's performance state in accordance to the
rate. Add OPP support to the driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/spi/spi-tegra20-slink.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index cbf0b4a0820a..87b850e45863 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -18,12 +18,15 @@
 #include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_opp.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/reset.h>
 #include <linux/spi/spi.h>
 
+#include <soc/tegra/common.h>
+
 #define SLINK_COMMAND			0x000
 #define SLINK_BIT_LENGTH(x)		(((x) & 0x1f) << 0)
 #define SLINK_WORD_SIZE(x)		(((x) & 0x1f) << 5)
@@ -683,7 +686,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi,
 	bits_per_word = t->bits_per_word;
 	speed = t->speed_hz;
 	if (speed != tspi->cur_speed) {
-		clk_set_rate(tspi->clk, speed * 4);
+		dev_pm_opp_set_rate(tspi->dev, speed * 4);
 		tspi->cur_speed = speed;
 	}
 
@@ -1054,6 +1057,10 @@ static int tegra_slink_probe(struct platform_device *pdev)
 		goto exit_free_master;
 	}
 
+	ret = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+	if (ret)
+		return ret;
+
 	/* disabled clock may cause interrupt storm upon request */
 	tspi->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(tspi->clk)) {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 28/37] memory: tegra20-emc: Add minimal runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (26 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 27/37] spi: tegra20-slink: Add OPP support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 29/37] memory: tegra30-emc: " Dmitry Osipenko
                   ` (9 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

EMC is always enabled, mark it as enabled. This makes the OPP and GENPD
API usage more consistent since otherwise we're changing performance state
of a power domain for the device that doesn't support power management.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 31 ++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index c3462dbc8c22..22087009fa0c 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -20,6 +20,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
 #include <linux/types.h>
@@ -1028,6 +1029,32 @@ static int tegra_emc_devfreq_init(struct tegra_emc *emc)
 	return 0;
 }
 
+static void devm_tegra_emc_disable_runtime_pm(void *dev)
+{
+	pm_runtime_put(dev);
+	pm_runtime_disable(dev);
+}
+
+static int tegra_emc_init_pm(struct tegra_emc *emc)
+{
+	int err;
+
+	pm_runtime_enable(emc->dev);
+	err = pm_runtime_resume_and_get(emc->dev);
+	if (err) {
+		pm_runtime_disable(emc->dev);
+		return err;
+	}
+
+	err = devm_add_action_or_reset(emc->dev,
+				       devm_tegra_emc_disable_runtime_pm,
+				       emc->dev);
+	if (err)
+		return err;
+
+	return 0;
+}
+
 static int tegra_emc_probe(struct platform_device *pdev)
 {
 	struct tegra_core_opp_params opp_params = {};
@@ -1076,6 +1103,10 @@ static int tegra_emc_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = tegra_emc_init_pm(emc);
+	if (err)
+		return err;
+
 	opp_params.init_state = true;
 
 	err = devm_tegra_core_dev_init_opp_table(&pdev->dev, &opp_params);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 29/37] memory: tegra30-emc: Add minimal runtime PM support
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (27 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 28/37] memory: tegra20-emc: Add minimal runtime PM support Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 30/37] media: dt: bindings: tegra-vde: Convert to schema Dmitry Osipenko
                   ` (8 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

EMC is always enabled, mark it as enabled. This makes the OPP and GENPD
API usage more consistent since otherwise we're changing performance state
of a power domain for the device that doesn't support power management.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra30-emc.c | 31 ++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c
index 7e21a852f2e1..5a311feb5c20 100644
--- a/drivers/memory/tegra/tegra30-emc.c
+++ b/drivers/memory/tegra/tegra30-emc.c
@@ -24,6 +24,7 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
 #include <linux/types.h>
@@ -1519,6 +1520,32 @@ static int tegra_emc_init_clk(struct tegra_emc *emc)
 	return 0;
 }
 
+static void devm_tegra_emc_disable_runtime_pm(void *dev)
+{
+	pm_runtime_put(dev);
+	pm_runtime_disable(dev);
+}
+
+static int tegra_emc_init_pm(struct tegra_emc *emc)
+{
+	int err;
+
+	pm_runtime_enable(emc->dev);
+	err = pm_runtime_resume_and_get(emc->dev);
+	if (err) {
+		pm_runtime_disable(emc->dev);
+		return err;
+	}
+
+	err = devm_add_action_or_reset(emc->dev,
+				       devm_tegra_emc_disable_runtime_pm,
+				       emc->dev);
+	if (err)
+		return err;
+
+	return 0;
+}
+
 static int tegra_emc_probe(struct platform_device *pdev)
 {
 	struct tegra_core_opp_params opp_params = {};
@@ -1571,6 +1598,10 @@ static int tegra_emc_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = tegra_emc_init_pm(emc);
+	if (err)
+		return err;
+
 	opp_params.init_state = true;
 
 	err = devm_tegra_core_dev_init_opp_table(&pdev->dev, &opp_params);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 30/37] media: dt: bindings: tegra-vde: Convert to schema
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (28 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 29/37] memory: tegra30-emc: " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 31/37] media: dt: bindings: tegra-vde: Document OPP and power domain Dmitry Osipenko
                   ` (7 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Convert NVIDIA Tegra video decoder binding to schema.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../bindings/media/nvidia,tegra-vde.txt       |  64 -----------
 .../bindings/media/nvidia,tegra-vde.yaml      | 107 ++++++++++++++++++
 2 files changed, 107 insertions(+), 64 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
 create mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml

diff --git a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
deleted file mode 100644
index 602169b8aa19..000000000000
--- a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-NVIDIA Tegra Video Decoder Engine
-
-Required properties:
-- compatible : Must contain one of the following values:
-   - "nvidia,tegra20-vde"
-   - "nvidia,tegra30-vde"
-   - "nvidia,tegra114-vde"
-   - "nvidia,tegra124-vde"
-   - "nvidia,tegra132-vde"
-- reg : Must contain an entry for each entry in reg-names.
-- reg-names : Must include the following entries:
-  - sxe
-  - bsev
-  - mbe
-  - ppe
-  - mce
-  - tfe
-  - ppb
-  - vdma
-  - frameid
-- iram : Must contain phandle to the mmio-sram device node that represents
-         IRAM region used by VDE.
-- interrupts : Must contain an entry for each entry in interrupt-names.
-- interrupt-names : Must include the following entries:
-  - sync-token
-  - bsev
-  - sxe
-- clocks : Must include the following entries:
-  - vde
-- resets : Must contain an entry for each entry in reset-names.
-- reset-names : Should include the following entries:
-  - vde
-
-Optional properties:
-- resets : Must contain an entry for each entry in reset-names.
-- reset-names : Must include the following entries:
-  - mc
-- iommus: Must contain phandle to the IOMMU device node.
-
-Example:
-
-video-codec@6001a000 {
-	compatible = "nvidia,tegra20-vde";
-	reg = <0x6001a000 0x1000 /* Syntax Engine */
-	       0x6001b000 0x1000 /* Video Bitstream Engine */
-	       0x6001c000  0x100 /* Macroblock Engine */
-	       0x6001c200  0x100 /* Post-processing Engine */
-	       0x6001c400  0x100 /* Motion Compensation Engine */
-	       0x6001c600  0x100 /* Transform Engine */
-	       0x6001c800  0x100 /* Pixel prediction block */
-	       0x6001ca00  0x100 /* Video DMA */
-	       0x6001d800  0x300 /* Video frame controls */>;
-	reg-names = "sxe", "bsev", "mbe", "ppe", "mce",
-		    "tfe", "ppb", "vdma", "frameid";
-	iram = <&vde_pool>; /* IRAM region */
-	interrupts = <GIC_SPI  9 IRQ_TYPE_LEVEL_HIGH>, /* Sync token interrupt */
-		     <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>, /* BSE-V interrupt */
-		     <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>; /* SXE interrupt */
-	interrupt-names = "sync-token", "bsev", "sxe";
-	clocks = <&tegra_car TEGRA20_CLK_VDE>;
-	reset-names = "vde", "mc";
-	resets = <&tegra_car 61>, <&mc TEGRA20_MC_RESET_VDE>;
-	iommus = <&mc TEGRA_SWGROUP_VDE>;
-};
diff --git a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
new file mode 100644
index 000000000000..3b6c1f031e04
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/nvidia,tegra-vde.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra Video Decoder Engine
+
+maintainers:
+  - Dmitry Osipenko <digetx@gmail.com>
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - nvidia,tegra132-vde
+              - nvidia,tegra124-vde
+              - nvidia,tegra114-vde
+              - nvidia,tegra30-vde
+          - enum:
+              - nvidia,tegra20-vde
+      - items:
+          - const: nvidia,tegra20-vde
+
+  reg:
+    maxItems: 9
+
+  reg-names:
+    items:
+      - const: sxe
+      - const: bsev
+      - const: mbe
+      - const: ppe
+      - const: mce
+      - const: tfe
+      - const: ppb
+      - const: vdma
+      - const: frameid
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 2
+
+  reset-names:
+    items:
+      - const: vde
+      - const: mc
+
+  interrupts:
+    maxItems: 3
+
+  interrupt-names:
+    items:
+      - const: sync-token
+      - const: bsev
+      - const: sxe
+
+  iommus:
+    maxItems: 1
+
+  iram:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle of the SRAM MMIO node.
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - clocks
+  - resets
+  - reset-names
+  - interrupts
+  - interrupt-names
+
+additionalProperties: false
+
+examples:
+  - |
+    video-codec@6001a000 {
+      compatible = "nvidia,tegra20-vde";
+      reg = <0x6001a000 0x1000>, /* Syntax Engine */
+            <0x6001b000 0x1000>, /* Video Bitstream Engine */
+            <0x6001c000  0x100>, /* Macroblock Engine */
+            <0x6001c200  0x100>, /* Post-processing Engine */
+            <0x6001c400  0x100>, /* Motion Compensation Engine */
+            <0x6001c600  0x100>, /* Transform Engine */
+            <0x6001c800  0x100>, /* Pixel prediction block */
+            <0x6001ca00  0x100>, /* Video DMA */
+            <0x6001d800  0x300>; /* Video frame controls */
+      reg-names = "sxe", "bsev", "mbe", "ppe", "mce",
+                  "tfe", "ppb", "vdma", "frameid";
+      iram = <&iram>; /* IRAM MMIO region */
+      interrupts = <0  9 4>, /* Sync token */
+                   <0 10 4>, /* BSE-V */
+                   <0 12 4>; /* SXE */
+      interrupt-names = "sync-token", "bsev", "sxe";
+      clocks = <&clk 61>;
+      reset-names = "vde", "mc";
+      resets = <&rst 61>, <&mem 13>;
+      iommus = <&mem 15>;
+    };
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 31/37] media: dt: bindings: tegra-vde: Document OPP and power domain
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (29 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 30/37] media: dt: bindings: tegra-vde: Convert to schema Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 32/37] media: staging: tegra-vde: Support generic " Dmitry Osipenko
                   ` (6 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Document new OPP table and power domain properties of the video decoder
hardware.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../devicetree/bindings/media/nvidia,tegra-vde.yaml  | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
index 3b6c1f031e04..0b7d4d815707 100644
--- a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
+++ b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
@@ -68,6 +68,16 @@ properties:
     description:
       Phandle of the SRAM MMIO node.
 
+  operating-points-v2:
+    description:
+      Should contain freqs and voltages and opp-supported-hw property,
+      which is a bitfield indicating SoC speedo or process ID mask.
+
+  power-domains:
+    maxItems: 1
+    description:
+      Phandle to the SoC core power domain.
+
 required:
   - compatible
   - reg
@@ -104,4 +114,6 @@ examples:
       reset-names = "vde", "mc";
       resets = <&rst 61>, <&mem 13>;
       iommus = <&mem 15>;
+      operating-points-v2 = <&dvfs_opp_table>;
+      power-domains = <&domain>;
     };
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 32/37] media: staging: tegra-vde: Support generic power domain
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (30 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 31/37] media: dt: bindings: tegra-vde: Document OPP and power domain Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 33/37] ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees Dmitry Osipenko
                   ` (5 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Currently driver supports legacy power domain API, this patch adds generic
power domain support. This allows us to utilize a modern GENPD API for
newer device-trees and support DVFS of the video decoder hardware.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/staging/media/tegra-vde/vde.c | 59 ++++++++++++++++++++++-----
 1 file changed, 48 insertions(+), 11 deletions(-)

diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c
index ed4c1250b303..fc7a32ed1821 100644
--- a/drivers/staging/media/tegra-vde/vde.c
+++ b/drivers/staging/media/tegra-vde/vde.c
@@ -15,6 +15,8 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
@@ -920,13 +922,17 @@ static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
 	struct tegra_vde *vde = dev_get_drvdata(dev);
 	int err;
 
-	err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
-	if (err) {
-		dev_err(dev, "Failed to power down HW: %d\n", err);
-		return err;
+	if (!dev->pm_domain) {
+		err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
+		if (err) {
+			dev_err(dev, "Failed to power down HW: %d\n", err);
+			return err;
+		}
 	}
 
 	clk_disable_unprepare(vde->clk);
+	reset_control_release(vde->rst);
+	reset_control_release(vde->rst_mc);
 
 	return 0;
 }
@@ -936,14 +942,41 @@ static __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
 	struct tegra_vde *vde = dev_get_drvdata(dev);
 	int err;
 
-	err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
-						vde->clk, vde->rst);
+	err = reset_control_acquire(vde->rst_mc);
 	if (err) {
-		dev_err(dev, "Failed to power up HW : %d\n", err);
+		dev_err(dev, "Failed to acquire mc reset: %d\n", err);
 		return err;
 	}
 
+	err = reset_control_acquire(vde->rst);
+	if (err) {
+		dev_err(dev, "Failed to acquire reset: %d\n", err);
+		goto release_mc_reset;
+	}
+
+	if (!dev->pm_domain) {
+		err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
+							vde->clk, vde->rst);
+		if (err) {
+			dev_err(dev, "Failed to power up HW : %d\n", err);
+			goto release_reset;
+		}
+	}
+
+	err = clk_prepare_enable(vde->clk);
+	if (err) {
+		dev_err(dev, "Failed to enable clock: %d\n", err);
+		goto release_reset;
+	}
+
 	return 0;
+
+release_reset:
+	reset_control_release(vde->rst);
+release_mc_reset:
+	reset_control_release(vde->rst_mc);
+
+	return err;
 }
 
 static int tegra_vde_probe(struct platform_device *pdev)
@@ -1001,14 +1034,14 @@ static int tegra_vde_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	vde->rst = devm_reset_control_get(dev, NULL);
+	vde->rst = devm_reset_control_get_exclusive_released(dev, NULL);
 	if (IS_ERR(vde->rst)) {
 		err = PTR_ERR(vde->rst);
 		dev_err(dev, "Could not get VDE reset %d\n", err);
 		return err;
 	}
 
-	vde->rst_mc = devm_reset_control_get_optional(dev, "mc");
+	vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc");
 	if (IS_ERR(vde->rst_mc)) {
 		err = PTR_ERR(vde->rst_mc);
 		dev_err(dev, "Could not get MC reset %d\n", err);
@@ -1133,8 +1166,12 @@ static void tegra_vde_shutdown(struct platform_device *pdev)
 	 * On some devices bootloader isn't ready to a power-gated VDE on
 	 * a warm-reboot, machine will hang in that case.
 	 */
-	if (pm_runtime_status_suspended(&pdev->dev))
-		tegra_vde_runtime_resume(&pdev->dev);
+	if (pm_runtime_status_suspended(&pdev->dev)) {
+		if (pdev->dev.pm_domain)
+			dev_pm_genpd_resume(&pdev->dev);
+		else
+			tegra_vde_runtime_resume(&pdev->dev);
+	}
 }
 
 static __maybe_unused int tegra_vde_pm_suspend(struct device *dev)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 33/37] ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (31 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 32/37] media: staging: tegra-vde: Support generic " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 34/37] ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees Dmitry Osipenko
                   ` (4 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add OPP tables and power domains to all peripheral devices which
support power management on Tegra20 SoC.

Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../boot/dts/tegra20-acer-a500-picasso.dts    |   1 +
 arch/arm/boot/dts/tegra20-colibri.dtsi        |   3 +-
 arch/arm/boot/dts/tegra20-harmony.dts         |   3 +-
 arch/arm/boot/dts/tegra20-paz00.dts           |   1 +
 .../arm/boot/dts/tegra20-peripherals-opp.dtsi | 941 ++++++++++++++++++
 arch/arm/boot/dts/tegra20-seaboard.dts        |   3 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi       |   3 +-
 arch/arm/boot/dts/tegra20-trimslice.dts       |   9 +
 arch/arm/boot/dts/tegra20-ventana.dts         |   1 +
 arch/arm/boot/dts/tegra20.dtsi                | 103 ++
 10 files changed, 1064 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts b/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
index 1976c383912a..e58ef1e0dffa 100644
--- a/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
+++ b/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
@@ -705,6 +705,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <458>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	usb@c5000000 {
diff --git a/arch/arm/boot/dts/tegra20-colibri.dtsi b/arch/arm/boot/dts/tegra20-colibri.dtsi
index 585a5b441cf6..4ec403aa5f2e 100644
--- a/arch/arm/boot/dts/tegra20-colibri.dtsi
+++ b/arch/arm/boot/dts/tegra20-colibri.dtsi
@@ -495,7 +495,7 @@ reg_3v3_vsys: sys {
 					regulator-always-on;
 				};
 
-				sm0 {
+				vdd_core: sm0 {
 					regulator-name = "VDD_CORE_1.2V";
 					regulator-min-microvolt = <1200000>;
 					regulator-max-microvolt = <1200000>;
@@ -601,6 +601,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <3875>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 
 		/* Set SLEEP MODE bit in SUPPLYENE register of TPS658643 PMIC */
 		i2c-thermtrip {
diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts
index ae4312eedcbd..b21bab437ebd 100644
--- a/arch/arm/boot/dts/tegra20-harmony.dts
+++ b/arch/arm/boot/dts/tegra20-harmony.dts
@@ -339,7 +339,7 @@ sys_reg: sys {
 					regulator-always-on;
 				};
 
-				sm0 {
+				vdd_core: sm0 {
 					regulator-name = "vdd_sm0,vdd_core";
 					regulator-min-microvolt = <1200000>;
 					regulator-max-microvolt = <1200000>;
@@ -565,6 +565,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <3875>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	pcie@80003000 {
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index 3180bff90756..082fbc56facc 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -515,6 +515,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <0>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&core_vdd_reg>;
 	};
 
 	usb@c5000000 {
diff --git a/arch/arm/boot/dts/tegra20-peripherals-opp.dtsi b/arch/arm/boot/dts/tegra20-peripherals-opp.dtsi
index ef3ad2e5f270..ff8c3dcba8e9 100644
--- a/arch/arm/boot/dts/tegra20-peripherals-opp.dtsi
+++ b/arch/arm/boot/dts/tegra20-peripherals-opp.dtsi
@@ -1,6 +1,46 @@
 // SPDX-License-Identifier: GPL-2.0
 
 / {
+	core_opp_table: core-power-domain-opp-table {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		core_opp_950: opp@950000 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-level = <950000>;
+		};
+
+		core_opp_1000: opp@1000000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-level = <1000000>;
+		};
+
+		core_opp_1100: opp@1100000 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-level = <1100000>;
+		};
+
+		core_opp_1200: opp@1200000 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-level = <1200000>;
+		};
+
+		core_opp_1225: opp@1225000 {
+			opp-microvolt = <1225000 1225000 1300000>;
+			opp-level = <1225000>;
+		};
+
+		core_opp_1275: opp@1275000 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-level = <1275000>;
+		};
+
+		core_opp_1300: opp@1300000 {
+			opp-microvolt = <1300000 1300000 1300000>;
+			opp-level = <1300000>;
+		};
+	};
+
 	emc_icc_dvfs_opp_table: emc-dvfs-opp-table {
 		compatible = "operating-points-v2";
 
@@ -8,66 +48,77 @@ opp@36000000 {
 			opp-microvolt = <950000 950000 1300000>;
 			opp-hz = /bits/ 64 <36000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@47500000 {
 			opp-microvolt = <950000 950000 1300000>;
 			opp-hz = /bits/ 64 <47500000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@50000000 {
 			opp-microvolt = <950000 950000 1300000>;
 			opp-hz = /bits/ 64 <50000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@54000000 {
 			opp-microvolt = <950000 950000 1300000>;
 			opp-hz = /bits/ 64 <54000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@57000000 {
 			opp-microvolt = <950000 950000 1300000>;
 			opp-hz = /bits/ 64 <57000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@100000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <100000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@108000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <108000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@126666000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <126666000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@150000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <150000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@190000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <190000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@216000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <216000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 			opp-suspend;
 		};
 
@@ -75,36 +126,926 @@ opp@300000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <300000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@333000000 {
 			opp-microvolt = <1000000 1000000 1300000>;
 			opp-hz = /bits/ 64 <333000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@380000000 {
 			opp-microvolt = <1100000 1100000 1300000>;
 			opp-hz = /bits/ 64 <380000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
 		};
 
 		opp@600000000 {
 			opp-microvolt = <1200000 1200000 1300000>;
 			opp-hz = /bits/ 64 <600000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@666000000 {
 			opp-microvolt = <1200000 1200000 1300000>;
 			opp-hz = /bits/ 64 <666000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@760000000 {
 			opp-microvolt = <1300000 1300000 1300000>;
 			opp-hz = /bits/ 64 <760000000>;
 			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1300>;
+		};
+	};
+
+	gr2d_dvfs_opp_table: gr2d-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@133000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <133000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@171000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <171000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@247000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@300000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	gr3d_dvfs_opp_table: gr3d-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@114000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <114000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@161500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <161500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@161500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <161500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@209000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <209000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@218500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <218500000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@247000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@247000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@256500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <256500000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@285000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@285000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@304000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@323000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <323000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@333500000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <333500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@333500000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <333500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@351500000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <351500000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@361000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@380000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@400000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <400000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@400000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <400000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	disp1_dvfs_opp_table: disp1-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@158000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <158000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@190000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	disp2_dvfs_opp_table: disp2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@158000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <158000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@190000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	dsi_dvfs_opp_table: dsi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@100000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@500000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <500000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	epp_dvfs_opp_table: epp-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@133000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <133000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@171000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <171000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@247000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@300000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	hdmi_dvfs_opp_table: hdmi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@148500000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <148500000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	host1x_dvfs_opp_table: host1x-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@104500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <104500000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@133000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <133000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@166000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <166000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	ide_dvfs_opp_table: ide-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@100000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	mipi_dvfs_opp_table: mipi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@40000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <40000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1275>;
+		};
+	};
+
+	mpe_dvfs_opp_table: mpe-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@104500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <104500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@142500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <142500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@152000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <152000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@190000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@190000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@228000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <228000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@228000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <228000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@237500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <237500000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@266000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <266000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@275500000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <275500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@300000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@300000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	ndflash_dvfs_opp_table: ndflash-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@130000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <130000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@150000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <150000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@158000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <158000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@164000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <164000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	nor_dvfs_opp_table: nor-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@92000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <92000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	pcie_dvfs_opp_table: pcie-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@250000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <250000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sdmmc1_dvfs_opp_table: sdmmc1-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@44000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <44000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sdmmc2_dvfs_opp_table: sdmmc2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@44000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <44000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sdmmc3_dvfs_opp_table: sdmmc3-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@44000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <44000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sdmmc4_dvfs_opp_table: sdmmc4-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@44000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <44000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sclk_dvfs_opp_table: sclk-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@95000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <95000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@123500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <123500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@133000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <133000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@152000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <152000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@159500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <159500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@171000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <171000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@180500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <180500000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@190000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@207000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <207000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@218500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <218500000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@222500000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <222500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@229500000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <229500000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@240000000,1225 {
+			opp-microvolt = <1225000 1225000 1300000>;
+			opp-hz = /bits/ 64 <240000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1225>;
+		};
+
+		opp@240000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <240000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@247000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@256500000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <256500000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@260000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <260000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@262000000,1300 {
+			opp-microvolt = <1300000 1300000 1300000>;
+			opp-hz = /bits/ 64 <262000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@264000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <264000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@277500000,1300 {
+			opp-microvolt = <1300000 1300000 1300000>;
+			opp-hz = /bits/ 64 <277500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@285000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@292500000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <292500000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@300000000,1300 {
+			opp-microvolt = <1300000 1300000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@300000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1275>;
+		};
+	};
+
+	tvo_dvfs_opp_table: tvo-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@250000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <250000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	usbd_dvfs_opp_table: usbd-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	usb2_dvfs_opp_table: usb2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	usb3_dvfs_opp_table: usb3-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	vde_dvfs_opp_table: vde-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@95000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <95000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@123500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <123500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@123500000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <123500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@152000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <152000000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@152000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <152000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@171000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <171000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@209000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <209000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@209000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <209000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@218500000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <218500000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@237500000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <237500000>;
+			opp-supported-hw = <0x0002>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@275500000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <275500000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@285000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@300000000,1275 {
+			opp-microvolt = <1275000 1275000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1275>;
+		};
+
+		opp@300000000,1200 {
+			opp-microvolt = <1200000 1200000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@300000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1100>;
+		};
+	};
+
+	vi_dvfs_opp_table: vi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@85000000,950 {
+			opp-microvolt = <950000 950000 1300000>;
+			opp-hz = /bits/ 64 <85000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@100000000,1000 {
+			opp-microvolt = <1000000 1000000 1300000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@150000000,1100 {
+			opp-microvolt = <1100000 1100000 1300000>;
+			opp-hz = /bits/ 64 <150000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
 		};
 	};
 };
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index 92d494b8c3d2..5aeb7bb6c415 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -444,7 +444,7 @@ sys_reg: sys {
 					regulator-always-on;
 				};
 
-				sm0 {
+				vdd_core: sm0 {
 					regulator-name = "vdd_sm0,vdd_core";
 					regulator-min-microvolt = <1300000>;
 					regulator-max-microvolt = <1300000>;
@@ -689,6 +689,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <3875>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	memory-controller@7000f400 {
diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi
index 95e6bccdb4f6..ff2fb335ad10 100644
--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi
+++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi
@@ -357,7 +357,7 @@ sys_reg: sys {
 					regulator-always-on;
 				};
 
-				sm0 {
+				vdd_core: sm0 {
 					regulator-name = "vdd_sys_sm0,vdd_core";
 					regulator-min-microvolt = <1200000>;
 					regulator-max-microvolt = <1200000>;
@@ -477,6 +477,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <3875>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	pcie@80003000 {
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index 4bc87bc0c2a4..582dc7910ff8 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -321,6 +321,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <3875>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	pcie@80003000 {
@@ -444,6 +445,14 @@ pci_vdd_reg: regulator@4 {
 		regulator-always-on;
 	};
 
+	vdd_core: regulator@5 {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_core";
+		regulator-min-microvolt = <1300000>;
+		regulator-max-microvolt = <1300000>;
+		regulator-always-on;
+	};
+
 	sound {
 		compatible = "nvidia,tegra-audio-trimslice";
 		nvidia,i2s-controller = <&tegra_i2s1>;
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index 5a2578b3707f..e41ce1b1ec3f 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -544,6 +544,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-good-time = <3845 3845>;
 		nvidia,core-pwr-off-time = <458>;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	usb@c5000000 {
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 6ce498178105..5c74cc76b5e3 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -42,6 +42,8 @@ host1x@50000000 {
 		clock-names = "host1x";
 		resets = <&tegra_car 28>;
 		reset-names = "host1x";
+		operating-points-v2 = <&host1x_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -55,6 +57,8 @@ mpe@54040000 {
 			clocks = <&tegra_car TEGRA20_CLK_MPE>;
 			resets = <&tegra_car 60>;
 			reset-names = "mpe";
+			operating-points-v2 = <&mpe_dvfs_opp_table>;
+			power-domains = <&pd_mpe>;
 		};
 
 		vi@54080000 {
@@ -64,6 +68,8 @@ vi@54080000 {
 			clocks = <&tegra_car TEGRA20_CLK_VI>;
 			resets = <&tegra_car 20>;
 			reset-names = "vi";
+			operating-points-v2 = <&vi_dvfs_opp_table>;
+			power-domains = <&pd_venc>;
 		};
 
 		epp@540c0000 {
@@ -73,6 +79,8 @@ epp@540c0000 {
 			clocks = <&tegra_car TEGRA20_CLK_EPP>;
 			resets = <&tegra_car 19>;
 			reset-names = "epp";
+			operating-points-v2 = <&epp_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 		};
 
 		isp@54100000 {
@@ -82,6 +90,7 @@ isp@54100000 {
 			clocks = <&tegra_car TEGRA20_CLK_ISP>;
 			resets = <&tegra_car 23>;
 			reset-names = "isp";
+			power-domains = <&pd_venc>;
 		};
 
 		gr2d@54140000 {
@@ -91,6 +100,8 @@ gr2d@54140000 {
 			clocks = <&tegra_car TEGRA20_CLK_GR2D>;
 			resets = <&tegra_car 21>;
 			reset-names = "2d";
+			operating-points-v2 = <&gr2d_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 		};
 
 		gr3d@54180000 {
@@ -99,6 +110,8 @@ gr3d@54180000 {
 			clocks = <&tegra_car TEGRA20_CLK_GR3D>;
 			resets = <&tegra_car 24>;
 			reset-names = "3d";
+			operating-points-v2 = <&gr3d_dvfs_opp_table>;
+			power-domains = <&pd_3d>;
 		};
 
 		dc@54200000 {
@@ -110,6 +123,8 @@ dc@54200000 {
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 27>;
 			reset-names = "dc";
+			operating-points-v2 = <&disp1_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 
 			nvidia,head = <0>;
 
@@ -138,6 +153,8 @@ dc@54240000 {
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 26>;
 			reset-names = "dc";
+			operating-points-v2 = <&disp2_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 
 			nvidia,head = <1>;
 
@@ -166,6 +183,8 @@ hdmi@54280000 {
 			clock-names = "hdmi", "parent";
 			resets = <&tegra_car 51>;
 			reset-names = "hdmi";
+			operating-points-v2 = <&hdmi_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 
@@ -174,6 +193,8 @@ tvo@542c0000 {
 			reg = <0x542c0000 0x00040000>;
 			interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&tegra_car TEGRA20_CLK_TVO>;
+			operating-points-v2 = <&tvo_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 
@@ -185,6 +206,8 @@ dsi@54300000 {
 			clock-names = "dsi", "parent";
 			resets = <&tegra_car 48>;
 			reset-names = "dsi";
+			operating-points-v2 = <&dsi_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 	};
@@ -242,6 +265,16 @@ tegra_car: clock@60006000 {
 		reg = <0x60006000 0x1000>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
+
+		tegra-clocks {
+			sclk {
+				compatible = "nvidia,tegra20-sclk",
+					     "nvidia,tegra-clock";
+				operating-points-v2 = <&sclk_dvfs_opp_table>;
+				clocks = <&tegra_car TEGRA20_CLK_SCLK>;
+				power-domains = <&pd_core>;
+			};
+		};
 	};
 
 	flow-controller@60007000 {
@@ -319,6 +352,8 @@ vde@6001a000 {
 		clocks = <&tegra_car TEGRA20_CLK_VDE>;
 		reset-names = "vde", "mc";
 		resets = <&tegra_car 61>, <&mc TEGRA20_MC_RESET_VDE>;
+		operating-points-v2 = <&vde_dvfs_opp_table>;
+		power-domains = <&pd_vde>;
 	};
 
 	apbmisc@70000800 {
@@ -460,6 +495,8 @@ nand-controller@70008000 {
 		reset-names = "nand";
 		assigned-clocks = <&tegra_car TEGRA20_CLK_NDFLASH>;
 		assigned-clock-rates = <150000000>;
+		operating-points-v2 = <&ndflash_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -473,6 +510,8 @@ gmi@70009000 {
 		clock-names = "gmi";
 		resets = <&tegra_car 42>;
 		reset-names = "gmi";
+		operating-points-v2 = <&nor_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -643,6 +682,52 @@ tegra_pmc: pmc@7000e400 {
 		clocks = <&tegra_car TEGRA20_CLK_PCLK>, <&clk32k_in>;
 		clock-names = "pclk", "clk32k_in";
 		#clock-cells = <1>;
+
+		pd_core: core-domain {
+			operating-points-v2 = <&core_opp_table>;
+			#power-domain-cells = <0>;
+		};
+
+		powergates {
+			pd_3d: 3d {
+				clocks = <&tegra_car TEGRA20_CLK_GR3D>;
+				resets = <&mc TEGRA20_MC_RESET_3D>,
+					 <&tegra_car TEGRA20_CLK_GR3D>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_venc: venc {
+				clocks = <&tegra_car TEGRA20_CLK_ISP>,
+					 <&tegra_car TEGRA20_CLK_VI>,
+					 <&tegra_car TEGRA20_CLK_CSI>;
+				resets = <&mc TEGRA20_MC_RESET_ISP>,
+					 <&mc TEGRA20_MC_RESET_VI>,
+					 <&tegra_car TEGRA20_CLK_ISP>,
+					 <&tegra_car 20 /* VI */>,
+					 <&tegra_car TEGRA20_CLK_CSI>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_vde: vdec {
+				clocks = <&tegra_car TEGRA20_CLK_VDE>;
+				resets = <&mc TEGRA20_MC_RESET_VDE>,
+					 <&tegra_car TEGRA20_CLK_VDE>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_mpe: mpe {
+				clocks = <&tegra_car TEGRA20_CLK_MPE>;
+				resets = <&mc TEGRA20_MC_RESET_MPEA>,
+					 <&mc TEGRA20_MC_RESET_MPEB>,
+					 <&mc TEGRA20_MC_RESET_MPEC>,
+					 <&tegra_car TEGRA20_CLK_MPE>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+		};
 	};
 
 	mc: memory-controller@7000f000 {
@@ -662,6 +747,7 @@ emc: memory-controller@7000f400 {
 		reg = <0x7000f400 0x400>;
 		interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&tegra_car TEGRA20_CLK_EMC>;
+		power-domains = <&pd_core>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 		#interconnect-cells = <0>;
@@ -712,6 +798,9 @@ pcie@80003000 {
 			 <&tegra_car 72>,
 			 <&tegra_car 74>;
 		reset-names = "pex", "afi", "pcie_x";
+		operating-points-v2 = <&pcie_dvfs_opp_table>;
+		power-domains = <&pd_core>;
+
 		status = "disabled";
 
 		pci@1,0 {
@@ -754,6 +843,8 @@ usb@c5000000 {
 		reset-names = "usb";
 		nvidia,needs-double-reset;
 		nvidia,phy = <&phy1>;
+		operating-points-v2 = <&usbd_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -791,6 +882,8 @@ usb@c5004000 {
 		resets = <&tegra_car 58>;
 		reset-names = "usb";
 		nvidia,phy = <&phy2>;
+		operating-points-v2 = <&usb2_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -817,6 +910,8 @@ usb@c5008000 {
 		resets = <&tegra_car 59>;
 		reset-names = "usb";
 		nvidia,phy = <&phy3>;
+		operating-points-v2 = <&usb3_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -851,6 +946,8 @@ mmc@c8000000 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 14>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc1_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -862,6 +959,8 @@ mmc@c8000200 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 9>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc2_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -873,6 +972,8 @@ mmc@c8000400 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 69>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc3_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -884,6 +985,8 @@ mmc@c8000600 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 15>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc4_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 34/37] ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (32 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 33/37] ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 35/37] ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x Dmitry Osipenko
                   ` (3 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Add OPP tables and power domains to all peripheral devices which
support power management on Tegra30 SoC.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../tegra30-asus-nexus7-grouper-common.dtsi   |    1 +
 arch/arm/boot/dts/tegra30-beaver.dts          |    1 +
 arch/arm/boot/dts/tegra30-cardhu.dtsi         |    1 +
 arch/arm/boot/dts/tegra30-colibri.dtsi        |   17 +-
 arch/arm/boot/dts/tegra30-ouya.dts            |    1 +
 .../arm/boot/dts/tegra30-peripherals-opp.dtsi | 1412 +++++++++++++++++
 arch/arm/boot/dts/tegra30.dtsi                |  156 ++
 7 files changed, 1586 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/tegra30-asus-nexus7-grouper-common.dtsi b/arch/arm/boot/dts/tegra30-asus-nexus7-grouper-common.dtsi
index ae8300baa2d4..cd62e44fd571 100644
--- a/arch/arm/boot/dts/tegra30-asus-nexus7-grouper-common.dtsi
+++ b/arch/arm/boot/dts/tegra30-asus-nexus7-grouper-common.dtsi
@@ -953,6 +953,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-off-time = <0>;
 		nvidia,core-power-req-active-high;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	ahub@70080000 {
diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts
index e159feeedef7..b54cbb24c4d3 100644
--- a/arch/arm/boot/dts/tegra30-beaver.dts
+++ b/arch/arm/boot/dts/tegra30-beaver.dts
@@ -1915,6 +1915,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-off-time = <0>;
 		nvidia,core-power-req-active-high;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&core_vdd_reg>;
 	};
 
 	ahub@70080000 {
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index d9dd11569d4b..ff49bf17b119 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -391,6 +391,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-off-time = <0>;
 		nvidia,core-power-req-active-high;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	ahub@70080000 {
diff --git a/arch/arm/boot/dts/tegra30-colibri.dtsi b/arch/arm/boot/dts/tegra30-colibri.dtsi
index 413e35215804..0627b64f044d 100644
--- a/arch/arm/boot/dts/tegra30-colibri.dtsi
+++ b/arch/arm/boot/dts/tegra30-colibri.dtsi
@@ -765,9 +765,14 @@ vdd1_reg: vdd1 {
 
 				vddctrl_reg: vddctrl {
 					regulator-name = "+V1.0_VDD_CPU";
-					regulator-min-microvolt = <1150000>;
-					regulator-max-microvolt = <1150000>;
+					regulator-min-microvolt = <800000>;
+					regulator-max-microvolt = <1250000>;
+					regulator-coupled-with = <&vdd_core>;
+					regulator-coupled-max-spread = <300000>;
+					regulator-max-step-microvolt = <100000>;
 					regulator-always-on;
+
+					nvidia,tegra-cpu-regulator;
 				};
 
 				reg_1v8_vio: vio {
@@ -890,18 +895,23 @@ temp-sensor@4c {
 		};
 
 		/* SW: +V1.2_VDD_CORE */
-		regulator@60 {
+		vdd_core: regulator@60 {
 			compatible = "ti,tps62362";
 			reg = <0x60>;
 
 			regulator-name = "tps62362-vout";
 			regulator-min-microvolt = <900000>;
 			regulator-max-microvolt = <1400000>;
+			regulator-coupled-with = <&vddctrl_reg>;
+			regulator-coupled-max-spread = <300000>;
+			regulator-max-step-microvolt = <100000>;
 			regulator-boot-on;
 			regulator-always-on;
 			ti,vsel0-state-low;
 			/* VSEL1: EN_CORE_DVFS_N low for DVFS */
 			ti,vsel1-state-low;
+
+			nvidia,tegra-core-regulator;
 		};
 	};
 
@@ -914,6 +924,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-off-time = <0>;
 		nvidia,core-power-req-active-high;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 
 		/* Set DEV_OFF bit in DCDC control register of TPS65911 PMIC */
 		i2c-thermtrip {
diff --git a/arch/arm/boot/dts/tegra30-ouya.dts b/arch/arm/boot/dts/tegra30-ouya.dts
index ab8744f3d72d..2494a933f2b4 100644
--- a/arch/arm/boot/dts/tegra30-ouya.dts
+++ b/arch/arm/boot/dts/tegra30-ouya.dts
@@ -275,6 +275,7 @@ pmc@7000e400 {
 		nvidia,core-pwr-off-time = <458>;
 		nvidia,core-power-req-active-high;
 		nvidia,sys-clock-req-active-high;
+		core-supply = <&vdd_core>;
 	};
 
 	mc_timings: memory-controller@7000f000 {
diff --git a/arch/arm/boot/dts/tegra30-peripherals-opp.dtsi b/arch/arm/boot/dts/tegra30-peripherals-opp.dtsi
index 2c9780319725..788dcac2079e 100644
--- a/arch/arm/boot/dts/tegra30-peripherals-opp.dtsi
+++ b/arch/arm/boot/dts/tegra30-peripherals-opp.dtsi
@@ -1,6 +1,56 @@
 // SPDX-License-Identifier: GPL-2.0
 
 / {
+	core_opp_table: core-power-domain-opp-table {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		core_opp_950: opp@950000 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-level = <950000>;
+		};
+
+		core_opp_1000: opp@1000000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-level = <1000000>;
+		};
+
+		core_opp_1050: opp@1050000 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-level = <1050000>;
+		};
+
+		core_opp_1100: opp@1100000 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-level = <1100000>;
+		};
+
+		core_opp_1150: opp@1150000 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-level = <1150000>;
+		};
+
+		core_opp_1200: opp@1200000 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-level = <1200000>;
+		};
+
+		core_opp_1250: opp@1250000 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-level = <1250000>;
+		};
+
+		core_opp_1300: opp@1300000 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-level = <1300000>;
+		};
+
+		core_opp_1350: opp@1350000 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-level = <1350000>;
+		};
+	};
+
 	emc_icc_dvfs_opp_table: emc-dvfs-opp-table {
 		compatible = "operating-points-v2";
 
@@ -8,126 +58,147 @@ opp@12750000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <12750000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@12750000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <12750000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@12750000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <12750000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@25500000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <25500000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@25500000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <25500000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@25500000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <25500000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@27000000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <27000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@27000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <27000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@27000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <27000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@51000000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <51000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@51000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <51000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@51000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <51000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@54000000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <54000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@54000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <54000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@54000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <54000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@102000000,950 {
 			opp-microvolt = <950000 950000 1350000>;
 			opp-hz = /bits/ 64 <102000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
 		};
 
 		opp@102000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <102000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@102000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <102000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@108000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <108000000>;
 			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@108000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <108000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@204000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <204000000>;
 			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
 			opp-suspend;
 		};
 
@@ -135,6 +206,7 @@ opp@204000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <204000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 			opp-suspend;
 		};
 
@@ -142,126 +214,147 @@ opp@333500000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <333500000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@333500000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <333500000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@333500000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <333500000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@375000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <375000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@375000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <375000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@375000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <375000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@400000000,1000 {
 			opp-microvolt = <1000000 1000000 1350000>;
 			opp-hz = /bits/ 64 <400000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
 		};
 
 		opp@400000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <400000000>;
 			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@400000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <400000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@416000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <416000000>;
 			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@416000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <416000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@450000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <450000000>;
 			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@450000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <450000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@533000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <533000000>;
 			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@533000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <533000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@625000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <625000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@625000000,1250 {
 			opp-microvolt = <1250000 1250000 1350000>;
 			opp-hz = /bits/ 64 <625000000>;
 			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
 		};
 
 		opp@667000000,1200 {
 			opp-microvolt = <1200000 1200000 1350000>;
 			opp-hz = /bits/ 64 <667000000>;
 			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
 		};
 
 		opp@750000000,1300 {
 			opp-microvolt = <1300000 1300000 1350000>;
 			opp-hz = /bits/ 64 <750000000>;
 			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
 		};
 
 		opp@800000000,1300 {
 			opp-microvolt = <1300000 1300000 1350000>;
 			opp-hz = /bits/ 64 <800000000>;
 			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
 		};
 
 		opp@900000000,1350 {
 			opp-microvolt = <1350000 1350000 1350000>;
 			opp-hz = /bits/ 64 <900000000>;
 			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
 		};
 	};
 
@@ -383,4 +476,1323 @@ opp@900000000 {
 			opp-peak-kBps = <7200000>;
 		};
 	};
+
+	gr2d_dvfs_opp_table: gr2d-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@267000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@361000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@408000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@446000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <446000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	gr3d_dvfs_opp_table: gr3d-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@234000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <234000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1000>, <&core_opp_1000>;
+		};
+
+		opp@247000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>, <&core_opp_1000>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>, <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>, <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>, <&core_opp_1100>;
+		};
+
+		opp@361000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>, <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>, <&core_opp_1150>;
+		};
+
+		opp@408000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>, <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>, <&core_opp_1200>;
+		};
+
+		opp@446000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <446000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>, <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1250>, <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>, <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>, <&core_opp_1350>;
+		};
+	};
+
+	afi_dvfs_opp_table: afi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@250000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <250000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	cve_dvfs_opp_table: cve-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@297000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <297000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+	};
+
+	disp1_dvfs_opp_table: disp1-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@120000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <120000000>;
+			opp-supported-hw = <0x0009>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@155000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <155000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@190000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x0009>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@268000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <268000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1050>;
+		};
+	};
+
+	disp2_dvfs_opp_table: disp2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@120000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <120000000>;
+			opp-supported-hw = <0x0009>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@155000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <155000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@190000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <190000000>;
+			opp-supported-hw = <0x0009>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@268000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <268000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1050>;
+		};
+	};
+
+	dsia_dvfs_opp_table: dsia-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@275000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <275000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	dsib_dvfs_opp_table: dsib-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@275000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <275000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	epp_dvfs_opp_table: epp-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@267000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@361000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@408000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@446000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <446000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	fuse_burn_dvfs_opp_table: fuse_burn-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@26000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <26000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1150>;
+		};
+	};
+
+	hdmi_dvfs_opp_table: hdmi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@148500000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <148500000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	host1x_dvfs_opp_table: host1x-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@152000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <152000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@188000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <188000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@222000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <222000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@242000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <242000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@254000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <254000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@267000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@300000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	mipi_dvfs_opp_table: mipi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@60000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	mpe_dvfs_opp_table: mpe-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@234000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <234000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@247000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@361000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@408000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@446000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <446000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	sclk_dvfs_opp_table: sclk-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@51000000,950 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-hz = /bits/ 64 <51000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@136000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <136000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@164000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <164000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@191000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <191000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@205000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <205000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@216000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <216000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@227000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <227000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@267000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@334000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <334000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@378000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <378000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
+		};
+	};
+
+	se_dvfs_opp_table: se-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@267000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@361000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <361000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@408000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@446000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <446000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+
+		opp@625000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <625000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
+		};
+	};
+
+	ndflash_dvfs_opp_table: ndflash-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@120000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <120000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@200000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <200000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1150>;
+		};
+	};
+
+	nor_dvfs_opp_table: nor-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@108000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <108000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@115000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <115000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@130000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <130000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@133000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <133000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1150>;
+		};
+	};
+
+	pcie_dvfs_opp_table: pcie-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@250000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <250000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	pll_c_dvfs_opp_table: pll_c-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@533000000,950 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-hz = /bits/ 64 <533000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@667000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <667000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@800000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <800000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@1066000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <1066000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@1200000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <1200000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	pll_e_dvfs_opp_table: pll_e-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@100000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	pll_m_dvfs_opp_table: pll_m-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@533000000,950 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-hz = /bits/ 64 <533000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@667000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <667000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@800000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <800000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@1066000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <1066000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	pwm_dvfs_opp_table: pwm-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@408000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <408000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sata_dvfs_opp_table: sata-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@216000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <216000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sata_oob_dvfs_opp_table: sata_oob-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@216000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <216000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	sbc1_dvfs_opp_table: sbc1-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sbc2_dvfs_opp_table: sbc2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sbc3_dvfs_opp_table: sbc3-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sbc4_dvfs_opp_table: sbc4-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sbc5_dvfs_opp_table: sbc5-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sbc6_dvfs_opp_table: sbc6-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@52000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <52000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@60000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <60000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@100000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <100000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sdmmc1_dvfs_opp_table: sdmmc1-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@104000000,950 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-hz = /bits/ 64 <104000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@208000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <208000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	sdmmc3_dvfs_opp_table: sdmmc3-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@104000000,950 {
+			opp-microvolt = <950000 950000 1350000>;
+			opp-hz = /bits/ 64 <104000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_950>;
+		};
+
+		opp@208000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <208000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1200>;
+		};
+	};
+
+	spdif_out_dvfs_opp_table: spdif-out-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@26000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <26000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	tvdac_dvfs_opp_table: tvdac-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@220000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <220000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	tvo_dvfs_opp_table: tvo-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@297000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <297000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1050>;
+		};
+	};
+
+	usbd_dvfs_opp_table: usbd-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	usb2_dvfs_opp_table: usb2-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	usb3_dvfs_opp_table: usb3-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@480000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <480000000>;
+			opp-supported-hw = <0x000F>;
+			required-opps = <&core_opp_1000>;
+		};
+	};
+
+	vde_dvfs_opp_table: vde-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@228000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <228000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@247000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <247000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@275000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <275000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@304000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <304000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@332000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <332000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@352000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <352000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@380000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <380000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@400000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <400000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@416000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <416000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@437000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <437000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@484000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <484000000>;
+			opp-supported-hw = <0x000C>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@520000000,1300 {
+			opp-microvolt = <1300000 1300000 1350000>;
+			opp-hz = /bits/ 64 <520000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1300>;
+		};
+
+		opp@600000000,1350 {
+			opp-microvolt = <1350000 1350000 1350000>;
+			opp-hz = /bits/ 64 <600000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1350>;
+		};
+	};
+
+	vi_dvfs_opp_table: vi-opp-table {
+		compatible = "operating-points-v2";
+
+		opp@216000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <216000000>;
+			opp-supported-hw = <0x0003>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@219000000,1000 {
+			opp-microvolt = <1000000 1000000 1350000>;
+			opp-hz = /bits/ 64 <219000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1000>;
+		};
+
+		opp@267000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <267000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@285000000,1050 {
+			opp-microvolt = <1050000 1050000 1350000>;
+			opp-hz = /bits/ 64 <285000000>;
+			opp-supported-hw = <0x0001>;
+			required-opps = <&core_opp_1050>;
+		};
+
+		opp@300000000,1100 {
+			opp-microvolt = <1100000 1100000 1350000>;
+			opp-hz = /bits/ 64 <300000000>;
+			opp-supported-hw = <0x0007>;
+			required-opps = <&core_opp_1100>;
+		};
+
+		opp@371000000,1150 {
+			opp-microvolt = <1150000 1150000 1350000>;
+			opp-hz = /bits/ 64 <371000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1150>;
+		};
+
+		opp@409000000,1200 {
+			opp-microvolt = <1200000 1200000 1350000>;
+			opp-hz = /bits/ 64 <409000000>;
+			opp-supported-hw = <0x0006>;
+			required-opps = <&core_opp_1200>;
+		};
+
+		opp@425000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <425000000>;
+			opp-supported-hw = <0x0004>;
+			required-opps = <&core_opp_1250>;
+		};
+
+		opp@470000000,1250 {
+			opp-microvolt = <1250000 1250000 1350000>;
+			opp-hz = /bits/ 64 <470000000>;
+			opp-supported-hw = <0x0008>;
+			required-opps = <&core_opp_1250>;
+		};
+	};
 };
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index c577c191be4b..6e760faaff29 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -54,6 +54,8 @@ pcie@3000 {
 			 <&tegra_car 72>,
 			 <&tegra_car 74>;
 		reset-names = "pex", "afi", "pcie_x";
+		operating-points-v2 = <&pcie_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 
 		pci@1,0 {
@@ -123,6 +125,8 @@ host1x@50000000 {
 		resets = <&tegra_car 28>;
 		reset-names = "host1x";
 		iommus = <&mc TEGRA_SWGROUP_HC>;
+		operating-points-v2 = <&host1x_dvfs_opp_table>;
+		power-domains = <&pd_heg>;
 
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -136,6 +140,7 @@ mpe@54040000 {
 			clocks = <&tegra_car TEGRA30_CLK_MPE>;
 			resets = <&tegra_car 60>;
 			reset-names = "mpe";
+			power-domains = <&pd_mpe>;
 
 			iommus = <&mc TEGRA_SWGROUP_MPE>;
 		};
@@ -147,6 +152,7 @@ vi@54080000 {
 			clocks = <&tegra_car TEGRA30_CLK_VI>;
 			resets = <&tegra_car 20>;
 			reset-names = "vi";
+			power-domains = <&pd_venc>;
 
 			iommus = <&mc TEGRA_SWGROUP_VI>;
 		};
@@ -158,6 +164,7 @@ epp@540c0000 {
 			clocks = <&tegra_car TEGRA30_CLK_EPP>;
 			resets = <&tegra_car 19>;
 			reset-names = "epp";
+			power-domains = <&pd_heg>;
 
 			iommus = <&mc TEGRA_SWGROUP_EPP>;
 		};
@@ -169,6 +176,7 @@ isp@54100000 {
 			clocks = <&tegra_car TEGRA30_CLK_ISP>;
 			resets = <&tegra_car 23>;
 			reset-names = "isp";
+			power-domains = <&pd_venc>;
 
 			iommus = <&mc TEGRA_SWGROUP_ISP>;
 		};
@@ -180,6 +188,8 @@ gr2d@54140000 {
 			clocks = <&tegra_car TEGRA30_CLK_GR2D>;
 			resets = <&tegra_car 21>;
 			reset-names = "2d";
+			operating-points-v2 = <&gr2d_dvfs_opp_table>;
+			power-domains = <&pd_heg>;
 
 			iommus = <&mc TEGRA_SWGROUP_G2>;
 		};
@@ -193,6 +203,9 @@ gr3d@54180000 {
 			resets = <&tegra_car 24>,
 				 <&tegra_car 98>;
 			reset-names = "3d", "3d2";
+			operating-points-v2 = <&gr3d_dvfs_opp_table>;
+			power-domains = <&pd_3d0>, <&pd_3d1>;
+			power-domain-names = "3d0", "3d1";
 
 			iommus = <&mc TEGRA_SWGROUP_NV>,
 				 <&mc TEGRA_SWGROUP_NV2>;
@@ -207,6 +220,8 @@ dc@54200000 {
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 27>;
 			reset-names = "dc";
+			operating-points-v2 = <&disp1_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 
 			iommus = <&mc TEGRA_SWGROUP_DC>;
 
@@ -237,6 +252,8 @@ dc@54240000 {
 			clock-names = "dc", "parent";
 			resets = <&tegra_car 26>;
 			reset-names = "dc";
+			operating-points-v2 = <&disp2_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 
 			iommus = <&mc TEGRA_SWGROUP_DCB>;
 
@@ -267,6 +284,8 @@ hdmi@54280000 {
 			clock-names = "hdmi", "parent";
 			resets = <&tegra_car 51>;
 			reset-names = "hdmi";
+			operating-points-v2 = <&hdmi_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 
@@ -275,6 +294,8 @@ tvo@542c0000 {
 			reg = <0x542c0000 0x00040000>;
 			interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&tegra_car TEGRA30_CLK_TVO>;
+			operating-points-v2 = <&tvo_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 
@@ -286,6 +307,8 @@ dsi@54300000 {
 			clock-names = "dsi", "parent";
 			resets = <&tegra_car 48>;
 			reset-names = "dsi";
+			operating-points-v2 = <&dsia_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 
@@ -297,6 +320,8 @@ dsi@54400000 {
 			clock-names = "dsi", "parent";
 			resets = <&tegra_car 84>;
 			reset-names = "dsi";
+			operating-points-v2 = <&dsib_dvfs_opp_table>;
+			power-domains = <&pd_core>;
 			status = "disabled";
 		};
 	};
@@ -357,6 +382,40 @@ tegra_car: clock@60006000 {
 		reg = <0x60006000 0x1000>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
+
+		tegra-clocks {
+			sclk {
+				compatible = "nvidia,tegra30-sclk",
+					     "nvidia,tegra-clock";
+				operating-points-v2 = <&sclk_dvfs_opp_table>;
+				clocks = <&tegra_car TEGRA30_CLK_SCLK>;
+				power-domains = <&pd_core>;
+			};
+
+			pll_c {
+				compatible = "nvidia,tegra30-pllc",
+					     "nvidia,tegra-clock";
+				operating-points-v2 = <&pll_c_dvfs_opp_table>;
+				clocks = <&tegra_car TEGRA30_CLK_PLL_C>;
+				power-domains = <&pd_core>;
+			};
+
+			pll_e {
+				compatible = "nvidia,tegra30-plle",
+					     "nvidia,tegra-clock";
+				operating-points-v2 = <&pll_e_dvfs_opp_table>;
+				clocks = <&tegra_car TEGRA30_CLK_PLL_E>;
+				power-domains = <&pd_core>;
+			};
+
+			pll_m {
+				compatible = "nvidia,tegra30-pllm",
+					     "nvidia,tegra-clock";
+				operating-points-v2 = <&pll_m_dvfs_opp_table>;
+				clocks = <&tegra_car TEGRA30_CLK_PLL_M>;
+				power-domains = <&pd_core>;
+			};
+		};
 	};
 
 	flow-controller@60007000 {
@@ -467,6 +526,8 @@ vde@6001a000 {
 		reset-names = "vde", "mc";
 		resets = <&tegra_car 61>, <&mc TEGRA30_MC_RESET_VDE>;
 		iommus = <&mc TEGRA_SWGROUP_VDE>;
+		operating-points-v2 = <&vde_dvfs_opp_table>;
+		power-domains = <&pd_vde>;
 	};
 
 	apbmisc@70000800 {
@@ -564,6 +625,8 @@ gmi@70009000 {
 		clock-names = "gmi";
 		resets = <&tegra_car 42>;
 		reset-names = "gmi";
+		operating-points-v2 = <&nor_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -574,6 +637,8 @@ pwm: pwm@7000a000 {
 		clocks = <&tegra_car TEGRA30_CLK_PWM>;
 		resets = <&tegra_car 17>;
 		reset-names = "pwm";
+		operating-points-v2 = <&pwm_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -675,6 +740,8 @@ spi@7000d400 {
 		reset-names = "spi";
 		dmas = <&apbdma 15>, <&apbdma 15>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc1_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -689,6 +756,8 @@ spi@7000d600 {
 		reset-names = "spi";
 		dmas = <&apbdma 16>, <&apbdma 16>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc2_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -703,6 +772,8 @@ spi@7000d800 {
 		reset-names = "spi";
 		dmas = <&apbdma 17>, <&apbdma 17>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc3_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -717,6 +788,8 @@ spi@7000da00 {
 		reset-names = "spi";
 		dmas = <&apbdma 18>, <&apbdma 18>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc4_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -731,6 +804,8 @@ spi@7000dc00 {
 		reset-names = "spi";
 		dmas = <&apbdma 27>, <&apbdma 27>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc5_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -745,6 +820,8 @@ spi@7000de00 {
 		reset-names = "spi";
 		dmas = <&apbdma 28>, <&apbdma 28>;
 		dma-names = "rx", "tx";
+		operating-points-v2 = <&sbc6_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -764,6 +841,72 @@ tegra_pmc: pmc@7000e400 {
 		clocks = <&tegra_car TEGRA30_CLK_PCLK>, <&clk32k_in>;
 		clock-names = "pclk", "clk32k_in";
 		#clock-cells = <1>;
+
+		pd_core: core-domain {
+			operating-points-v2 = <&core_opp_table>;
+			#power-domain-cells = <0>;
+		};
+
+		powergates {
+			pd_3d0: 3d0 {
+				clocks = <&tegra_car TEGRA30_CLK_GR3D>;
+				resets = <&mc TEGRA30_MC_RESET_3D>,
+					 <&tegra_car TEGRA30_CLK_GR3D>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_3d1: 3d1 {
+				clocks = <&tegra_car TEGRA30_CLK_GR3D2>;
+				resets = <&mc TEGRA30_MC_RESET_3D2>,
+					 <&tegra_car TEGRA30_CLK_GR3D2>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_venc: venc {
+				clocks = <&tegra_car TEGRA30_CLK_ISP>,
+					 <&tegra_car TEGRA30_CLK_VI>,
+					 <&tegra_car TEGRA30_CLK_CSI>;
+				resets = <&mc TEGRA30_MC_RESET_ISP>,
+					 <&mc TEGRA30_MC_RESET_VI>,
+					 <&tegra_car TEGRA30_CLK_ISP>,
+					 <&tegra_car 20 /* VI */>,
+					 <&tegra_car TEGRA30_CLK_CSI>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_vde: vdec {
+				clocks = <&tegra_car TEGRA30_CLK_VDE>;
+				resets = <&mc TEGRA30_MC_RESET_VDE>,
+					 <&tegra_car TEGRA30_CLK_VDE>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_mpe: mpe {
+				clocks = <&tegra_car TEGRA30_CLK_MPE>;
+				resets = <&mc TEGRA30_MC_RESET_MPE>,
+					 <&tegra_car TEGRA30_CLK_MPE>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+
+			pd_heg: heg {
+				clocks = <&tegra_car TEGRA30_CLK_GR2D>,
+					 <&tegra_car TEGRA30_CLK_EPP>,
+					 <&tegra_car TEGRA30_CLK_HOST1X>;
+				resets = <&mc TEGRA30_MC_RESET_2D>,
+					 <&mc TEGRA30_MC_RESET_EPP>,
+					 <&mc TEGRA30_MC_RESET_HC>,
+					 <&tegra_car TEGRA30_CLK_GR2D>,
+					 <&tegra_car TEGRA30_CLK_EPP>,
+					 <&tegra_car TEGRA30_CLK_HOST1X>;
+				power-domains = <&pd_core>;
+				#power-domain-cells = <0>;
+			};
+		};
 	};
 
 	mc: memory-controller@7000f000 {
@@ -784,6 +927,7 @@ emc: memory-controller@7000f400 {
 		reg = <0x7000f400 0x400>;
 		interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&tegra_car TEGRA30_CLK_EMC>;
+		power-domains = <&pd_core>;
 
 		nvidia,memory-controller = <&mc>;
 		operating-points-v2 = <&emc_icc_dvfs_opp_table>;
@@ -798,6 +942,8 @@ fuse@7000f800 {
 		clock-names = "fuse";
 		resets = <&tegra_car 39>;
 		reset-names = "fuse";
+		operating-points-v2 = <&fuse_burn_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 	};
 
 	hda@70030000 {
@@ -906,6 +1052,8 @@ mmc@78000000 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 14>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc1_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -928,6 +1076,8 @@ mmc@78000400 {
 		clock-names = "sdhci";
 		resets = <&tegra_car 69>;
 		reset-names = "sdhci";
+		operating-points-v2 = <&sdmmc3_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -952,6 +1102,8 @@ usb@7d000000 {
 		reset-names = "usb";
 		nvidia,needs-double-reset;
 		nvidia,phy = <&phy1>;
+		operating-points-v2 = <&usbd_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -991,6 +1143,8 @@ usb@7d004000 {
 		resets = <&tegra_car 58>;
 		reset-names = "usb";
 		nvidia,phy = <&phy2>;
+		operating-points-v2 = <&usb2_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
@@ -1029,6 +1183,8 @@ usb@7d008000 {
 		resets = <&tegra_car 59>;
 		reset-names = "usb";
 		nvidia,phy = <&phy3>;
+		operating-points-v2 = <&usb3_dvfs_opp_table>;
+		power-domains = <&pd_core>;
 		status = "disabled";
 	};
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 35/37] ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (33 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 34/37] ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 36/37] ARM: tegra: Add Memory Client resets to Tegra30 " Dmitry Osipenko
                   ` (2 subsequent siblings)
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Memory access must be blocked before hardware reset is asserted and before
power is gated, otherwise a serious hardware fault is inevitable. Add
reset for memory clients to the GR2D, GR3D and Host1x nodes.

Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/boot/dts/tegra20.dtsi | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 5c74cc76b5e3..2cb31bdd9eea 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -40,8 +40,8 @@ host1x@50000000 {
 		interrupt-names = "syncpt", "host1x";
 		clocks = <&tegra_car TEGRA20_CLK_HOST1X>;
 		clock-names = "host1x";
-		resets = <&tegra_car 28>;
-		reset-names = "host1x";
+		resets = <&tegra_car 28>, <&mc TEGRA20_MC_RESET_HC>;
+		reset-names = "host1x", "mc";
 		operating-points-v2 = <&host1x_dvfs_opp_table>;
 		power-domains = <&pd_core>;
 
@@ -98,8 +98,8 @@ gr2d@54140000 {
 			reg = <0x54140000 0x00040000>;
 			interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&tegra_car TEGRA20_CLK_GR2D>;
-			resets = <&tegra_car 21>;
-			reset-names = "2d";
+			resets = <&tegra_car 21>, <&mc TEGRA20_MC_RESET_2D>;
+			reset-names = "2d", "mc";
 			operating-points-v2 = <&gr2d_dvfs_opp_table>;
 			power-domains = <&pd_core>;
 		};
@@ -108,8 +108,8 @@ gr3d@54180000 {
 			compatible = "nvidia,tegra20-gr3d";
 			reg = <0x54180000 0x00040000>;
 			clocks = <&tegra_car TEGRA20_CLK_GR3D>;
-			resets = <&tegra_car 24>;
-			reset-names = "3d";
+			resets = <&tegra_car 24>, <&mc TEGRA20_MC_RESET_3D>;
+			reset-names = "3d", "mc";
 			operating-points-v2 = <&gr3d_dvfs_opp_table>;
 			power-domains = <&pd_3d>;
 		};
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 36/37] ARM: tegra: Add Memory Client resets to Tegra30 GR2D, GR3D and Host1x
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (34 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 35/37] ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-01 23:27 ` [PATCH v7 37/37] soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30 Dmitry Osipenko
  2021-07-09 13:01 ` [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Ulf Hansson
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

Memory access must be blocked before hardware reset is asserted and before
power is gated, otherwise a serious hardware fault is inevitable. Add
reset for memory clients to the GR2D, GR3D and Host1x nodes.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/boot/dts/tegra30.dtsi | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 6e760faaff29..9a9292e31e9f 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -122,8 +122,8 @@ host1x@50000000 {
 		interrupt-names = "syncpt", "host1x";
 		clocks = <&tegra_car TEGRA30_CLK_HOST1X>;
 		clock-names = "host1x";
-		resets = <&tegra_car 28>;
-		reset-names = "host1x";
+		resets = <&tegra_car 28>, <&mc TEGRA30_MC_RESET_HC>;
+		reset-names = "host1x", "mc";
 		iommus = <&mc TEGRA_SWGROUP_HC>;
 		operating-points-v2 = <&host1x_dvfs_opp_table>;
 		power-domains = <&pd_heg>;
@@ -186,8 +186,8 @@ gr2d@54140000 {
 			reg = <0x54140000 0x00040000>;
 			interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&tegra_car TEGRA30_CLK_GR2D>;
-			resets = <&tegra_car 21>;
-			reset-names = "2d";
+			resets = <&tegra_car 21>, <&mc TEGRA30_MC_RESET_2D>;
+			reset-names = "2d", "mc";
 			operating-points-v2 = <&gr2d_dvfs_opp_table>;
 			power-domains = <&pd_heg>;
 
@@ -201,8 +201,10 @@ gr3d@54180000 {
 				 <&tegra_car TEGRA30_CLK_GR3D2>;
 			clock-names = "3d", "3d2";
 			resets = <&tegra_car 24>,
-				 <&tegra_car 98>;
-			reset-names = "3d", "3d2";
+				 <&tegra_car 98>,
+				 <&mc TEGRA30_MC_RESET_3D>,
+				 <&mc TEGRA30_MC_RESET_3D2>;
+			reset-names = "3d", "3d2", "mc", "mc2";
 			operating-points-v2 = <&gr3d_dvfs_opp_table>;
 			power-domains = <&pd_3d0>, <&pd_3d1>;
 			power-domain-names = "3d0", "3d1";
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 37/37] soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (35 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 36/37] ARM: tegra: Add Memory Client resets to Tegra30 " Dmitry Osipenko
@ 2021-07-01 23:27 ` Dmitry Osipenko
  2021-07-09 13:01 ` [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Ulf Hansson
  37 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-01 23:27 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

All device drivers got runtime PM and OPP support. Flip the core domain
support status for Tegra20 and Tegra30 SoCs.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/pmc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index ebafb818b08e..1f6872f7b211 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -3177,7 +3177,7 @@ static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
 }
 
 static const struct tegra_pmc_soc tegra20_pmc_soc = {
-	.supports_core_domain = false,
+	.supports_core_domain = true,
 	.num_powergates = ARRAY_SIZE(tegra20_powergates),
 	.powergates = tegra20_powergates,
 	.num_cpu_powergates = 0,
@@ -3238,7 +3238,7 @@ static const char * const tegra30_reset_sources[] = {
 };
 
 static const struct tegra_pmc_soc tegra30_pmc_soc = {
-	.supports_core_domain = false,
+	.supports_core_domain = true,
 	.num_powergates = ARRAY_SIZE(tegra30_powergates),
 	.powergates = tegra30_powergates,
 	.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 20/37] mmc: sdhci-tegra: Add runtime PM and OPP support
  2021-07-01 23:27 ` [PATCH v7 20/37] mmc: sdhci-tegra: " Dmitry Osipenko
@ 2021-07-02  3:39     ` kernel test robot
  0 siblings, 0 replies; 58+ messages in thread
From: kernel test robot @ 2021-07-02  3:39 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Ulf Hansson,
	Viresh Kumar, Stephen Boyd, Peter De Schrijver
  Cc: kbuild-all, linux-kernel, linux-tegra, linux-pm

[-- Attachment #1: Type: text/plain, Size: 4243 bytes --]

Hi Dmitry,

I love your patch! Yet something to improve:

[auto build test ERROR on next-20210701]
[cannot apply to tegra/for-next robh/for-next tegra-drm/drm/tegra/for-next v5.13 v5.13-rc7 v5.13-rc6 v5.13]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
base:    fb0ca446157a86b75502c1636b0d81e642fe6bf1
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/bd90f252831cdc7191a0fef94041489c066e286a
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
        git checkout bd90f252831cdc7191a0fef94041489c066e286a
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/mmc/host/sdhci-tegra.c: In function 'sdhci_tegra_suspend':
>> drivers/mmc/host/sdhci-tegra.c:1813:8: error: implicit declaration of function 'sdhci_suspend_host'; did you mean 'sdhci_add_host'? [-Werror=implicit-function-declaration]
    1813 |  ret = sdhci_suspend_host(host);
         |        ^~~~~~~~~~~~~~~~~~
         |        sdhci_add_host
>> drivers/mmc/host/sdhci-tegra.c:1821:3: error: implicit declaration of function 'sdhci_resume_host'; did you mean 'sdhci_remove_host'? [-Werror=implicit-function-declaration]
    1821 |   sdhci_resume_host(host);
         |   ^~~~~~~~~~~~~~~~~
         |   sdhci_remove_host
   cc1: some warnings being treated as errors


vim +1813 drivers/mmc/host/sdhci-tegra.c

bd90f252831cdc Dmitry Osipenko     2021-07-02  1801  
bd90f252831cdc Dmitry Osipenko     2021-07-02  1802  static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
bd90f252831cdc Dmitry Osipenko     2021-07-02  1803  {
bd90f252831cdc Dmitry Osipenko     2021-07-02  1804  	struct sdhci_host *host = dev_get_drvdata(dev);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1805  	int ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1806  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1807  	if (host->mmc->caps2 & MMC_CAP2_CQE) {
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1808  		ret = cqhci_suspend(host->mmc);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1809  		if (ret)
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1810  			return ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1811  	}
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1812  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11 @1813  	ret = sdhci_suspend_host(host);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1814  	if (ret) {
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1815  		cqhci_resume(host->mmc);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1816  		return ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1817  	}
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1818  
bd90f252831cdc Dmitry Osipenko     2021-07-02  1819  	ret = pm_runtime_force_suspend(dev);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1820  	if (ret) {
bd90f252831cdc Dmitry Osipenko     2021-07-02 @1821  		sdhci_resume_host(host);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1822  		cqhci_resume(host->mmc);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1823  		return ret;
bd90f252831cdc Dmitry Osipenko     2021-07-02  1824  	}
bd90f252831cdc Dmitry Osipenko     2021-07-02  1825  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1826  	return 0;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1827  }
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1828  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 60867 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 20/37] mmc: sdhci-tegra: Add runtime PM and OPP support
@ 2021-07-02  3:39     ` kernel test robot
  0 siblings, 0 replies; 58+ messages in thread
From: kernel test robot @ 2021-07-02  3:39 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4320 bytes --]

Hi Dmitry,

I love your patch! Yet something to improve:

[auto build test ERROR on next-20210701]
[cannot apply to tegra/for-next robh/for-next tegra-drm/drm/tegra/for-next v5.13 v5.13-rc7 v5.13-rc6 v5.13]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
base:    fb0ca446157a86b75502c1636b0d81e642fe6bf1
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/bd90f252831cdc7191a0fef94041489c066e286a
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
        git checkout bd90f252831cdc7191a0fef94041489c066e286a
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/mmc/host/sdhci-tegra.c: In function 'sdhci_tegra_suspend':
>> drivers/mmc/host/sdhci-tegra.c:1813:8: error: implicit declaration of function 'sdhci_suspend_host'; did you mean 'sdhci_add_host'? [-Werror=implicit-function-declaration]
    1813 |  ret = sdhci_suspend_host(host);
         |        ^~~~~~~~~~~~~~~~~~
         |        sdhci_add_host
>> drivers/mmc/host/sdhci-tegra.c:1821:3: error: implicit declaration of function 'sdhci_resume_host'; did you mean 'sdhci_remove_host'? [-Werror=implicit-function-declaration]
    1821 |   sdhci_resume_host(host);
         |   ^~~~~~~~~~~~~~~~~
         |   sdhci_remove_host
   cc1: some warnings being treated as errors


vim +1813 drivers/mmc/host/sdhci-tegra.c

bd90f252831cdc Dmitry Osipenko     2021-07-02  1801  
bd90f252831cdc Dmitry Osipenko     2021-07-02  1802  static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
bd90f252831cdc Dmitry Osipenko     2021-07-02  1803  {
bd90f252831cdc Dmitry Osipenko     2021-07-02  1804  	struct sdhci_host *host = dev_get_drvdata(dev);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1805  	int ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1806  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1807  	if (host->mmc->caps2 & MMC_CAP2_CQE) {
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1808  		ret = cqhci_suspend(host->mmc);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1809  		if (ret)
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1810  			return ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1811  	}
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1812  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11 @1813  	ret = sdhci_suspend_host(host);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1814  	if (ret) {
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1815  		cqhci_resume(host->mmc);
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1816  		return ret;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1817  	}
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1818  
bd90f252831cdc Dmitry Osipenko     2021-07-02  1819  	ret = pm_runtime_force_suspend(dev);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1820  	if (ret) {
bd90f252831cdc Dmitry Osipenko     2021-07-02 @1821  		sdhci_resume_host(host);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1822  		cqhci_resume(host->mmc);
bd90f252831cdc Dmitry Osipenko     2021-07-02  1823  		return ret;
bd90f252831cdc Dmitry Osipenko     2021-07-02  1824  	}
bd90f252831cdc Dmitry Osipenko     2021-07-02  1825  
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1826  	return 0;
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1827  }
71c733c4e1aeb8 Sowjanya Komatineni 2019-04-11  1828  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 60867 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage
  2021-07-01 23:27 ` [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage Dmitry Osipenko
@ 2021-07-02  8:29     ` kernel test robot
  0 siblings, 0 replies; 58+ messages in thread
From: kernel test robot @ 2021-07-02  8:29 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, Jonathan Hunter, Ulf Hansson,
	Viresh Kumar, Stephen Boyd, Peter De Schrijver
  Cc: clang-built-linux, kbuild-all, linux-kernel, linux-tegra, linux-pm

[-- Attachment #1: Type: text/plain, Size: 13706 bytes --]

Hi Dmitry,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20210701]
[cannot apply to tegra/for-next robh/for-next tegra-drm/drm/tegra/for-next v5.13 v5.13-rc7 v5.13-rc6 v5.13]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
base:    fb0ca446157a86b75502c1636b0d81e642fe6bf1
config: x86_64-randconfig-b001-20210630 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 9eb613b2de3163686b1a4bd1160f15ac56a4b083)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/eae252678e4ba361dc1513283e282268a7548af6
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
        git checkout eae252678e4ba361dc1513283e282268a7548af6
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/spi/spi-tegra20-slink.c:1076:6: warning: variable 'spi_irq' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (ret < 0)
               ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1126:11: note: uninitialized use occurs here
           free_irq(spi_irq, tspi);
                    ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1076:2: note: remove the 'if' if its condition is always false
           if (ret < 0)
           ^~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1066:6: warning: variable 'spi_irq' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (IS_ERR(tspi->rst)) {
               ^~~~~~~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1126:11: note: uninitialized use occurs here
           free_irq(spi_irq, tspi);
                    ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1066:2: note: remove the 'if' if its condition is always false
           if (IS_ERR(tspi->rst)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1007:18: note: initialize the variable 'spi_irq' to silence this warning
           int ret, spi_irq;
                           ^
                            = 0
   2 warnings generated.


vim +1076 drivers/spi/spi-tegra20-slink.c

dc4dc36056392c Laxman Dewangan 2012-10-30  1001  
fd4a319bc933ae Grant Likely    2012-12-07  1002  static int tegra_slink_probe(struct platform_device *pdev)
dc4dc36056392c Laxman Dewangan 2012-10-30  1003  {
dc4dc36056392c Laxman Dewangan 2012-10-30  1004  	struct spi_master	*master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1005  	struct tegra_slink_data	*tspi;
dc4dc36056392c Laxman Dewangan 2012-10-30  1006  	struct resource		*r;
dc4dc36056392c Laxman Dewangan 2012-10-30  1007  	int ret, spi_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1008  	const struct tegra_slink_chip_data *cdata = NULL;
dc4dc36056392c Laxman Dewangan 2012-10-30  1009  	const struct of_device_id *match;
dc4dc36056392c Laxman Dewangan 2012-10-30  1010  
c60fea02141167 Stephen Warren  2013-02-15  1011  	match = of_match_device(tegra_slink_of_match, &pdev->dev);
dc4dc36056392c Laxman Dewangan 2012-10-30  1012  	if (!match) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1013  		dev_err(&pdev->dev, "Error: No device match found\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1014  		return -ENODEV;
dc4dc36056392c Laxman Dewangan 2012-10-30  1015  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1016  	cdata = match->data;
dc4dc36056392c Laxman Dewangan 2012-10-30  1017  
dc4dc36056392c Laxman Dewangan 2012-10-30  1018  	master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
dc4dc36056392c Laxman Dewangan 2012-10-30  1019  	if (!master) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1020  		dev_err(&pdev->dev, "master allocation failed\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1021  		return -ENOMEM;
dc4dc36056392c Laxman Dewangan 2012-10-30  1022  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1023  
dc4dc36056392c Laxman Dewangan 2012-10-30  1024  	/* the spi->mode bits understood by this driver: */
dc4dc36056392c Laxman Dewangan 2012-10-30  1025  	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
dc4dc36056392c Laxman Dewangan 2012-10-30  1026  	master->setup = tegra_slink_setup;
63fc184cde2d77 Mark Brown      2013-10-05  1027  	master->prepare_message = tegra_slink_prepare_message;
63fc184cde2d77 Mark Brown      2013-10-05  1028  	master->transfer_one = tegra_slink_transfer_one;
63fc184cde2d77 Mark Brown      2013-10-05  1029  	master->unprepare_message = tegra_slink_unprepare_message;
ce74ac80d25bcb Mark Brown      2013-07-28  1030  	master->auto_runtime_pm = true;
dc4dc36056392c Laxman Dewangan 2012-10-30  1031  	master->num_chipselect = MAX_CHIP_SELECT;
dc4dc36056392c Laxman Dewangan 2012-10-30  1032  
24b5a82cf5709a Jingoo Han      2013-05-23  1033  	platform_set_drvdata(pdev, master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1034  	tspi = spi_master_get_devdata(master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1035  	tspi->master = master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1036  	tspi->dev = &pdev->dev;
dc4dc36056392c Laxman Dewangan 2012-10-30  1037  	tspi->chip_data = cdata;
dc4dc36056392c Laxman Dewangan 2012-10-30  1038  	spin_lock_init(&tspi->lock);
dc4dc36056392c Laxman Dewangan 2012-10-30  1039  
3c604de496d756 Axel Lin        2014-02-10  1040  	if (of_property_read_u32(tspi->dev->of_node, "spi-max-frequency",
3c604de496d756 Axel Lin        2014-02-10  1041  				 &master->max_speed_hz))
3c604de496d756 Axel Lin        2014-02-10  1042  		master->max_speed_hz = 25000000; /* 25MHz */
c60fea02141167 Stephen Warren  2013-02-15  1043  
dc4dc36056392c Laxman Dewangan 2012-10-30  1044  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dc4dc36056392c Laxman Dewangan 2012-10-30  1045  	if (!r) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1046  		dev_err(&pdev->dev, "No IO memory resource\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1047  		ret = -ENODEV;
dc4dc36056392c Laxman Dewangan 2012-10-30  1048  		goto exit_free_master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1049  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1050  	tspi->phys = r->start;
b0ee5605234a24 Thierry Reding  2013-01-21  1051  	tspi->base = devm_ioremap_resource(&pdev->dev, r);
b0ee5605234a24 Thierry Reding  2013-01-21  1052  	if (IS_ERR(tspi->base)) {
b0ee5605234a24 Thierry Reding  2013-01-21  1053  		ret = PTR_ERR(tspi->base);
dc4dc36056392c Laxman Dewangan 2012-10-30  1054  		goto exit_free_master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1055  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1056  
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1057  	/* disabled clock may cause interrupt storm upon request */
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1058  	tspi->clk = devm_clk_get(&pdev->dev, NULL);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1059  	if (IS_ERR(tspi->clk)) {
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1060  		ret = PTR_ERR(tspi->clk);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1061  		dev_err(&pdev->dev, "Can not get clock %d\n", ret);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1062  		goto exit_free_master;
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1063  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1064  
73b32756cec312 Philipp Zabel   2017-07-19  1065  	tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
ff2251e3de37b0 Stephen Warren  2013-11-06  1066  	if (IS_ERR(tspi->rst)) {
ff2251e3de37b0 Stephen Warren  2013-11-06  1067  		dev_err(&pdev->dev, "can not get reset\n");
ff2251e3de37b0 Stephen Warren  2013-11-06  1068  		ret = PTR_ERR(tspi->rst);
ff2251e3de37b0 Stephen Warren  2013-11-06  1069  		goto exit_free_irq;
ff2251e3de37b0 Stephen Warren  2013-11-06  1070  	}
ff2251e3de37b0 Stephen Warren  2013-11-06  1071  
dc4dc36056392c Laxman Dewangan 2012-10-30  1072  	tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
dc4dc36056392c Laxman Dewangan 2012-10-30  1073  	tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
dc4dc36056392c Laxman Dewangan 2012-10-30  1074  
dc4dc36056392c Laxman Dewangan 2012-10-30  1075  	ret = tegra_slink_init_dma_param(tspi, true);
a915d150f68d8f Stephen Warren  2013-11-11 @1076  	if (ret < 0)
dc4dc36056392c Laxman Dewangan 2012-10-30  1077  		goto exit_free_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1078  	ret = tegra_slink_init_dma_param(tspi, false);
a915d150f68d8f Stephen Warren  2013-11-11  1079  	if (ret < 0)
dc4dc36056392c Laxman Dewangan 2012-10-30  1080  		goto exit_rx_dma_free;
dc4dc36056392c Laxman Dewangan 2012-10-30  1081  	tspi->max_buf_size = tspi->dma_buf_size;
dc4dc36056392c Laxman Dewangan 2012-10-30  1082  	init_completion(&tspi->tx_dma_complete);
dc4dc36056392c Laxman Dewangan 2012-10-30  1083  	init_completion(&tspi->rx_dma_complete);
dc4dc36056392c Laxman Dewangan 2012-10-30  1084  
dc4dc36056392c Laxman Dewangan 2012-10-30  1085  	init_completion(&tspi->xfer_completion);
dc4dc36056392c Laxman Dewangan 2012-10-30  1086  
dc4dc36056392c Laxman Dewangan 2012-10-30  1087  	pm_runtime_enable(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1088  	ret = pm_runtime_resume_and_get(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1089  	if (ret) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1090  		dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
dc4dc36056392c Laxman Dewangan 2012-10-30  1091  		goto exit_pm_disable;
dc4dc36056392c Laxman Dewangan 2012-10-30  1092  	}
aceda401e84115 Jon Hunter      2021-06-08  1093  
aceda401e84115 Jon Hunter      2021-06-08  1094  	reset_control_assert(tspi->rst);
aceda401e84115 Jon Hunter      2021-06-08  1095  	udelay(2);
aceda401e84115 Jon Hunter      2021-06-08  1096  	reset_control_deassert(tspi->rst);
aceda401e84115 Jon Hunter      2021-06-08  1097  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1098  	spi_irq = platform_get_irq(pdev, 0);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1099  	tspi->irq = spi_irq;
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1100  	ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1101  				   tegra_slink_isr_thread, IRQF_ONESHOT,
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1102  				   dev_name(&pdev->dev), tspi);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1103  	if (ret < 0) {
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1104  		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1105  			tspi->irq);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1106  		goto exit_pm_put;
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1107  	}
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1108  
dc4dc36056392c Laxman Dewangan 2012-10-30  1109  	tspi->def_command_reg  = SLINK_M_S;
dc4dc36056392c Laxman Dewangan 2012-10-30  1110  	tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN;
dc4dc36056392c Laxman Dewangan 2012-10-30  1111  	tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
dc4dc36056392c Laxman Dewangan 2012-10-30  1112  	tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
dc4dc36056392c Laxman Dewangan 2012-10-30  1113  
dc4dc36056392c Laxman Dewangan 2012-10-30  1114  	master->dev.of_node = pdev->dev.of_node;
716db5d64f5f9b Jingoo Han      2013-09-24  1115  	ret = devm_spi_register_master(&pdev->dev, master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1116  	if (ret < 0) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1117  		dev_err(&pdev->dev, "can not register to master err %d\n", ret);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1118  		goto exit_free_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1119  	}
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1120  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1121  	pm_runtime_put(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1122  
dc4dc36056392c Laxman Dewangan 2012-10-30  1123  	return ret;
dc4dc36056392c Laxman Dewangan 2012-10-30  1124  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1125  exit_free_irq:
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1126  	free_irq(spi_irq, tspi);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1127  exit_pm_put:
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1128  	pm_runtime_put(&pdev->dev);
dc4dc36056392c Laxman Dewangan 2012-10-30  1129  exit_pm_disable:
dc4dc36056392c Laxman Dewangan 2012-10-30  1130  	pm_runtime_disable(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1131  
dc4dc36056392c Laxman Dewangan 2012-10-30  1132  	tegra_slink_deinit_dma_param(tspi, false);
dc4dc36056392c Laxman Dewangan 2012-10-30  1133  exit_rx_dma_free:
dc4dc36056392c Laxman Dewangan 2012-10-30  1134  	tegra_slink_deinit_dma_param(tspi, true);
dc4dc36056392c Laxman Dewangan 2012-10-30  1135  exit_free_master:
dc4dc36056392c Laxman Dewangan 2012-10-30  1136  	spi_master_put(master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1137  	return ret;
dc4dc36056392c Laxman Dewangan 2012-10-30  1138  }
dc4dc36056392c Laxman Dewangan 2012-10-30  1139  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 41381 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage
@ 2021-07-02  8:29     ` kernel test robot
  0 siblings, 0 replies; 58+ messages in thread
From: kernel test robot @ 2021-07-02  8:29 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 13909 bytes --]

Hi Dmitry,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20210701]
[cannot apply to tegra/for-next robh/for-next tegra-drm/drm/tegra/for-next v5.13 v5.13-rc7 v5.13-rc6 v5.13]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
base:    fb0ca446157a86b75502c1636b0d81e642fe6bf1
config: x86_64-randconfig-b001-20210630 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 9eb613b2de3163686b1a4bd1160f15ac56a4b083)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/eae252678e4ba361dc1513283e282268a7548af6
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmitry-Osipenko/NVIDIA-Tegra-power-management-patches-for-5-15/20210702-073048
        git checkout eae252678e4ba361dc1513283e282268a7548af6
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/spi/spi-tegra20-slink.c:1076:6: warning: variable 'spi_irq' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (ret < 0)
               ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1126:11: note: uninitialized use occurs here
           free_irq(spi_irq, tspi);
                    ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1076:2: note: remove the 'if' if its condition is always false
           if (ret < 0)
           ^~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1066:6: warning: variable 'spi_irq' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (IS_ERR(tspi->rst)) {
               ^~~~~~~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1126:11: note: uninitialized use occurs here
           free_irq(spi_irq, tspi);
                    ^~~~~~~
   drivers/spi/spi-tegra20-slink.c:1066:2: note: remove the 'if' if its condition is always false
           if (IS_ERR(tspi->rst)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/spi/spi-tegra20-slink.c:1007:18: note: initialize the variable 'spi_irq' to silence this warning
           int ret, spi_irq;
                           ^
                            = 0
   2 warnings generated.


vim +1076 drivers/spi/spi-tegra20-slink.c

dc4dc36056392c Laxman Dewangan 2012-10-30  1001  
fd4a319bc933ae Grant Likely    2012-12-07  1002  static int tegra_slink_probe(struct platform_device *pdev)
dc4dc36056392c Laxman Dewangan 2012-10-30  1003  {
dc4dc36056392c Laxman Dewangan 2012-10-30  1004  	struct spi_master	*master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1005  	struct tegra_slink_data	*tspi;
dc4dc36056392c Laxman Dewangan 2012-10-30  1006  	struct resource		*r;
dc4dc36056392c Laxman Dewangan 2012-10-30  1007  	int ret, spi_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1008  	const struct tegra_slink_chip_data *cdata = NULL;
dc4dc36056392c Laxman Dewangan 2012-10-30  1009  	const struct of_device_id *match;
dc4dc36056392c Laxman Dewangan 2012-10-30  1010  
c60fea02141167 Stephen Warren  2013-02-15  1011  	match = of_match_device(tegra_slink_of_match, &pdev->dev);
dc4dc36056392c Laxman Dewangan 2012-10-30  1012  	if (!match) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1013  		dev_err(&pdev->dev, "Error: No device match found\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1014  		return -ENODEV;
dc4dc36056392c Laxman Dewangan 2012-10-30  1015  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1016  	cdata = match->data;
dc4dc36056392c Laxman Dewangan 2012-10-30  1017  
dc4dc36056392c Laxman Dewangan 2012-10-30  1018  	master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
dc4dc36056392c Laxman Dewangan 2012-10-30  1019  	if (!master) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1020  		dev_err(&pdev->dev, "master allocation failed\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1021  		return -ENOMEM;
dc4dc36056392c Laxman Dewangan 2012-10-30  1022  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1023  
dc4dc36056392c Laxman Dewangan 2012-10-30  1024  	/* the spi->mode bits understood by this driver: */
dc4dc36056392c Laxman Dewangan 2012-10-30  1025  	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
dc4dc36056392c Laxman Dewangan 2012-10-30  1026  	master->setup = tegra_slink_setup;
63fc184cde2d77 Mark Brown      2013-10-05  1027  	master->prepare_message = tegra_slink_prepare_message;
63fc184cde2d77 Mark Brown      2013-10-05  1028  	master->transfer_one = tegra_slink_transfer_one;
63fc184cde2d77 Mark Brown      2013-10-05  1029  	master->unprepare_message = tegra_slink_unprepare_message;
ce74ac80d25bcb Mark Brown      2013-07-28  1030  	master->auto_runtime_pm = true;
dc4dc36056392c Laxman Dewangan 2012-10-30  1031  	master->num_chipselect = MAX_CHIP_SELECT;
dc4dc36056392c Laxman Dewangan 2012-10-30  1032  
24b5a82cf5709a Jingoo Han      2013-05-23  1033  	platform_set_drvdata(pdev, master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1034  	tspi = spi_master_get_devdata(master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1035  	tspi->master = master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1036  	tspi->dev = &pdev->dev;
dc4dc36056392c Laxman Dewangan 2012-10-30  1037  	tspi->chip_data = cdata;
dc4dc36056392c Laxman Dewangan 2012-10-30  1038  	spin_lock_init(&tspi->lock);
dc4dc36056392c Laxman Dewangan 2012-10-30  1039  
3c604de496d756 Axel Lin        2014-02-10  1040  	if (of_property_read_u32(tspi->dev->of_node, "spi-max-frequency",
3c604de496d756 Axel Lin        2014-02-10  1041  				 &master->max_speed_hz))
3c604de496d756 Axel Lin        2014-02-10  1042  		master->max_speed_hz = 25000000; /* 25MHz */
c60fea02141167 Stephen Warren  2013-02-15  1043  
dc4dc36056392c Laxman Dewangan 2012-10-30  1044  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dc4dc36056392c Laxman Dewangan 2012-10-30  1045  	if (!r) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1046  		dev_err(&pdev->dev, "No IO memory resource\n");
dc4dc36056392c Laxman Dewangan 2012-10-30  1047  		ret = -ENODEV;
dc4dc36056392c Laxman Dewangan 2012-10-30  1048  		goto exit_free_master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1049  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1050  	tspi->phys = r->start;
b0ee5605234a24 Thierry Reding  2013-01-21  1051  	tspi->base = devm_ioremap_resource(&pdev->dev, r);
b0ee5605234a24 Thierry Reding  2013-01-21  1052  	if (IS_ERR(tspi->base)) {
b0ee5605234a24 Thierry Reding  2013-01-21  1053  		ret = PTR_ERR(tspi->base);
dc4dc36056392c Laxman Dewangan 2012-10-30  1054  		goto exit_free_master;
dc4dc36056392c Laxman Dewangan 2012-10-30  1055  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1056  
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1057  	/* disabled clock may cause interrupt storm upon request */
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1058  	tspi->clk = devm_clk_get(&pdev->dev, NULL);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1059  	if (IS_ERR(tspi->clk)) {
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1060  		ret = PTR_ERR(tspi->clk);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1061  		dev_err(&pdev->dev, "Can not get clock %d\n", ret);
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1062  		goto exit_free_master;
7001cab1dabc0b Marcel Ziswiler 2018-08-29  1063  	}
dc4dc36056392c Laxman Dewangan 2012-10-30  1064  
73b32756cec312 Philipp Zabel   2017-07-19  1065  	tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
ff2251e3de37b0 Stephen Warren  2013-11-06  1066  	if (IS_ERR(tspi->rst)) {
ff2251e3de37b0 Stephen Warren  2013-11-06  1067  		dev_err(&pdev->dev, "can not get reset\n");
ff2251e3de37b0 Stephen Warren  2013-11-06  1068  		ret = PTR_ERR(tspi->rst);
ff2251e3de37b0 Stephen Warren  2013-11-06  1069  		goto exit_free_irq;
ff2251e3de37b0 Stephen Warren  2013-11-06  1070  	}
ff2251e3de37b0 Stephen Warren  2013-11-06  1071  
dc4dc36056392c Laxman Dewangan 2012-10-30  1072  	tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
dc4dc36056392c Laxman Dewangan 2012-10-30  1073  	tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
dc4dc36056392c Laxman Dewangan 2012-10-30  1074  
dc4dc36056392c Laxman Dewangan 2012-10-30  1075  	ret = tegra_slink_init_dma_param(tspi, true);
a915d150f68d8f Stephen Warren  2013-11-11 @1076  	if (ret < 0)
dc4dc36056392c Laxman Dewangan 2012-10-30  1077  		goto exit_free_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1078  	ret = tegra_slink_init_dma_param(tspi, false);
a915d150f68d8f Stephen Warren  2013-11-11  1079  	if (ret < 0)
dc4dc36056392c Laxman Dewangan 2012-10-30  1080  		goto exit_rx_dma_free;
dc4dc36056392c Laxman Dewangan 2012-10-30  1081  	tspi->max_buf_size = tspi->dma_buf_size;
dc4dc36056392c Laxman Dewangan 2012-10-30  1082  	init_completion(&tspi->tx_dma_complete);
dc4dc36056392c Laxman Dewangan 2012-10-30  1083  	init_completion(&tspi->rx_dma_complete);
dc4dc36056392c Laxman Dewangan 2012-10-30  1084  
dc4dc36056392c Laxman Dewangan 2012-10-30  1085  	init_completion(&tspi->xfer_completion);
dc4dc36056392c Laxman Dewangan 2012-10-30  1086  
dc4dc36056392c Laxman Dewangan 2012-10-30  1087  	pm_runtime_enable(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1088  	ret = pm_runtime_resume_and_get(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1089  	if (ret) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1090  		dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
dc4dc36056392c Laxman Dewangan 2012-10-30  1091  		goto exit_pm_disable;
dc4dc36056392c Laxman Dewangan 2012-10-30  1092  	}
aceda401e84115 Jon Hunter      2021-06-08  1093  
aceda401e84115 Jon Hunter      2021-06-08  1094  	reset_control_assert(tspi->rst);
aceda401e84115 Jon Hunter      2021-06-08  1095  	udelay(2);
aceda401e84115 Jon Hunter      2021-06-08  1096  	reset_control_deassert(tspi->rst);
aceda401e84115 Jon Hunter      2021-06-08  1097  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1098  	spi_irq = platform_get_irq(pdev, 0);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1099  	tspi->irq = spi_irq;
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1100  	ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1101  				   tegra_slink_isr_thread, IRQF_ONESHOT,
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1102  				   dev_name(&pdev->dev), tspi);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1103  	if (ret < 0) {
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1104  		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1105  			tspi->irq);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1106  		goto exit_pm_put;
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1107  	}
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1108  
dc4dc36056392c Laxman Dewangan 2012-10-30  1109  	tspi->def_command_reg  = SLINK_M_S;
dc4dc36056392c Laxman Dewangan 2012-10-30  1110  	tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN;
dc4dc36056392c Laxman Dewangan 2012-10-30  1111  	tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
dc4dc36056392c Laxman Dewangan 2012-10-30  1112  	tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
dc4dc36056392c Laxman Dewangan 2012-10-30  1113  
dc4dc36056392c Laxman Dewangan 2012-10-30  1114  	master->dev.of_node = pdev->dev.of_node;
716db5d64f5f9b Jingoo Han      2013-09-24  1115  	ret = devm_spi_register_master(&pdev->dev, master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1116  	if (ret < 0) {
dc4dc36056392c Laxman Dewangan 2012-10-30  1117  		dev_err(&pdev->dev, "can not register to master err %d\n", ret);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1118  		goto exit_free_irq;
dc4dc36056392c Laxman Dewangan 2012-10-30  1119  	}
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1120  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1121  	pm_runtime_put(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1122  
dc4dc36056392c Laxman Dewangan 2012-10-30  1123  	return ret;
dc4dc36056392c Laxman Dewangan 2012-10-30  1124  
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1125  exit_free_irq:
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1126  	free_irq(spi_irq, tspi);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1127  exit_pm_put:
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1128  	pm_runtime_put(&pdev->dev);
dc4dc36056392c Laxman Dewangan 2012-10-30  1129  exit_pm_disable:
dc4dc36056392c Laxman Dewangan 2012-10-30  1130  	pm_runtime_disable(&pdev->dev);
eae252678e4ba3 Dmitry Osipenko 2021-07-02  1131  
dc4dc36056392c Laxman Dewangan 2012-10-30  1132  	tegra_slink_deinit_dma_param(tspi, false);
dc4dc36056392c Laxman Dewangan 2012-10-30  1133  exit_rx_dma_free:
dc4dc36056392c Laxman Dewangan 2012-10-30  1134  	tegra_slink_deinit_dma_param(tspi, true);
dc4dc36056392c Laxman Dewangan 2012-10-30  1135  exit_free_master:
dc4dc36056392c Laxman Dewangan 2012-10-30  1136  	spi_master_put(master);
dc4dc36056392c Laxman Dewangan 2012-10-30  1137  	return ret;
dc4dc36056392c Laxman Dewangan 2012-10-30  1138  }
dc4dc36056392c Laxman Dewangan 2012-10-30  1139  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 41381 bytes --]

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15
  2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
                   ` (36 preceding siblings ...)
  2021-07-01 23:27 ` [PATCH v7 37/37] soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30 Dmitry Osipenko
@ 2021-07-09 13:01 ` Ulf Hansson
  2021-07-09 21:23   ` Dmitry Osipenko
  37 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-07-09 13:01 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Fri, 2 Jul 2021 at 01:28, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> Hello,
>
> This series continues the work on enabling voltage scaling of the core
> power domain for NVIDIA Tegra SoCs. All the previous grounding work around
> improving OPP API and adding the initial core power domain driver is now
> landing into 5.14 kernel and already available in linux-next.
>
> The biggest challenge left so far is the power management of the PLLs
> and system clocks which don't have a dedicated hardware unit. I made a lot
> of changes around it by going back to a variant where individual device
> drivers are responsible for the power management in oppose to a recent
> variant where the most of power management was done by the clk controller
> driver. This allowed to drop the need of having a special lockdep
> annotation for the power domain's mutex, which Ulf Hansson didn't like
> previously. We don't have situations of a nested GENPD accesses anymore.
> It also allowed to minimize the clk patch a lot. Now clk driver manages
> power of the internal clocks only.
>
> This series enables a full power management for Tegra20 and Tegra30 SoCs.
> It doesn't include all the device-tree binding patches. I'll try to split
> this series into a smaller parts that could be applied individually. For
> now I want to get a feedback at least on the first six patches.
>
> Example /sys/kernel/debug/pm_genpd/pm_genpd_summary from Tegra20 Acer A500:
>
> domain                          status          children                           performance
>     /device                                             runtime status
> ----------------------------------------------------------------------------------------------
> mpe                             off-0                                              0
>     /devices/soc0/50000000.host1x/54040000.mpe          suspended                  0
> vdec                            off-0                                              0
>     /devices/soc0/6001a000.vde                          suspended                  0
> venc                            off-0                                              0
>     /devices/soc0/50000000.host1x/54080000.vi           suspended                  0
>     /devices/soc0/50000000.host1x/54100000.isp          suspended                  0
> 3d                              off-0                                              0
>     /devices/soc0/50000000.host1x/54180000.gr3d         suspended                  0
> core-domain                     on                                                 1100000
>                                                 3d, venc, vdec, mpe
>     /devices/soc0/50000000.host1x                       active                     1100000
>     /devices/soc0/50000000.host1x/540c0000.epp          suspended                  0
>     /devices/soc0/50000000.host1x/54140000.gr2d         suspended                  0
>     /devices/soc0/50000000.host1x/54280000.hdmi         suspended                  0
>     /devices/soc0/c5000000.usb                          active                     1100000
>     /devices/soc0/c5008000.usb                          active                     1100000
>     /devices/soc0/c8000000.mmc                          active                     1000000
>     /devices/soc0/c8000400.mmc                          active                     1000000
>     /devices/soc0/c8000600.mmc                          active                     1000000
>     /devices/soc0/7000f400.memory-controller            active                     1000000
>     /devices/platform/tegra_clk_sclk                    active                     950000
>     /devices/soc0/50000000.host1x/54200000.dc           active                     950000
>     /devices/soc0/50000000.host1x/54240000.dc           suspended                  0
>
>
> Example /sys/kernel/debug/pm_genpd/pm_genpd_summary from Tegra30 Nexus 7:
>
> domain                          status          children                           performance
>     /device                                             runtime status
> ----------------------------------------------------------------------------------------------
> heg                             on                                                 1000000
>     /devices/soc0/50000000.host1x                       active                     1000000
>     /devices/soc0/50000000.host1x/540c0000.epp          suspended                  0
>     /devices/soc0/50000000.host1x/54140000.gr2d         suspended                  0
> mpe                             off-0                                              0
>     /devices/soc0/50000000.host1x/54040000.mpe          suspended                  0
> vdec                            off-0                                              0
>     /devices/soc0/6001a000.vde                          suspended                  0
> venc                            off-0                                              0
>     /devices/soc0/50000000.host1x/54080000.vi           suspended                  0
>     /devices/soc0/50000000.host1x/54100000.isp          suspended                  0
> 3d1                             off-0                                              0
>     /devices/genpd:1:54180000.gr3d                      suspended                  0
> 3d0                             off-0                                              0
>     /devices/genpd:0:54180000.gr3d                      suspended                  0
> core-domain                     on                                                 1000000
>                                                 3d0, 3d1, venc, vdec, mpe, heg
>     /devices/soc0/7d000000.usb                          active                     1000000
>     /devices/soc0/78000400.mmc                          active                     950000
>     /devices/soc0/7000f400.memory-controller            active                     1000000
>     /devices/soc0/7000a000.pwm                          active                     1000000
>     /devices/platform/tegra_clk_pll_c                   active                     1000000
>     /devices/platform/tegra_clk_pll_e                   suspended                  0
>     /devices/platform/tegra_clk_pll_m                   active                     1000000
>     /devices/platform/tegra_clk_sclk                    active                     1000000
>     /devices/soc0/7000f800.fuse                         suspended                  0
>     /devices/soc0/50000000.host1x/54240000.dc           suspended                  0
>     /devices/soc0/50000000.host1x/54200000.dc           active                     1000000
>
> Dmitry Osipenko (37):
>   soc/tegra: pmc: Temporarily disable PMC state syncing
>   soc/tegra: pmc: Implement attach_dev() of power domain drivers
>   soc/tegra: Don't print error message when OPPs not available
>   soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple()
>   dt-bindings: clock: tegra-car: Document new tegra-clocks node
>   clk: tegra: Support runtime PM and power domain
>   dt-bindings: host1x: Document OPP and power domain properties
>   dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and
>     GR3D
>   gpu: host1x: Add host1x_channel_stop()
>   gpu: host1x: Add runtime PM support
>   gpu: host1x: Add stub driver for MPE, VI, EPP and ISP
>   drm/tegra: dc: Support OPP and SoC core voltage scaling
>   drm/tegra: hdmi: Add OPP support
>   drm/tegra: gr2d: Support OPP and power management
>   drm/tegra: gr3d: Support OPP and power management
>   drm/tegra: vic: Stop channel before suspending
>   usb: chipidea: tegra: Add runtime PM support
>   bus: tegra-gmi: Add runtime PM support
>   pwm: tegra: Add runtime PM and OPP support
>   mmc: sdhci-tegra: Add runtime PM and OPP support
>   mtd: rawnand: tegra: Add runtime PM support
>   soc/tegra: fuse: Clear fuse->clk on driver probe failure
>   soc/tegra: fuse: Add runtime PM support
>   soc/tegra: fuse: Enable fuse clock on suspend
>   clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock
>   spi: tegra20-slink: Improve runtime PM usage
>   spi: tegra20-slink: Add OPP support
>   memory: tegra20-emc: Add minimal runtime PM support
>   memory: tegra30-emc: Add minimal runtime PM support
>   media: dt: bindings: tegra-vde: Convert to schema
>   media: dt: bindings: tegra-vde: Document OPP and power domain
>   media: staging: tegra-vde: Support generic power domain
>   ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees
>   ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees
>   ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x
>   ARM: tegra: Add Memory Client resets to Tegra30 GR2D, GR3D and Host1x
>   soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30
>
>  .../bindings/clock/nvidia,tegra20-car.yaml    |   51 +
>  .../display/tegra/nvidia,tegra20-host1x.txt   |   53 +
>  .../bindings/media/nvidia,tegra-vde.txt       |   64 -
>  .../bindings/media/nvidia,tegra-vde.yaml      |  119 ++
>  .../boot/dts/tegra20-acer-a500-picasso.dts    |    1 +
>  arch/arm/boot/dts/tegra20-colibri.dtsi        |    3 +-
>  arch/arm/boot/dts/tegra20-harmony.dts         |    3 +-
>  arch/arm/boot/dts/tegra20-paz00.dts           |    1 +
>  .../arm/boot/dts/tegra20-peripherals-opp.dtsi |  941 +++++++++++
>  arch/arm/boot/dts/tegra20-seaboard.dts        |    3 +-
>  arch/arm/boot/dts/tegra20-tamonten.dtsi       |    3 +-
>  arch/arm/boot/dts/tegra20-trimslice.dts       |    9 +
>  arch/arm/boot/dts/tegra20-ventana.dts         |    1 +
>  arch/arm/boot/dts/tegra20.dtsi                |  115 +-
>  .../tegra30-asus-nexus7-grouper-common.dtsi   |    1 +
>  arch/arm/boot/dts/tegra30-beaver.dts          |    1 +
>  arch/arm/boot/dts/tegra30-cardhu.dtsi         |    1 +
>  arch/arm/boot/dts/tegra30-colibri.dtsi        |   17 +-
>  arch/arm/boot/dts/tegra30-ouya.dts            |    1 +
>  .../arm/boot/dts/tegra30-peripherals-opp.dtsi | 1412 +++++++++++++++++
>  arch/arm/boot/dts/tegra30.dtsi                |  170 +-
>  drivers/bus/tegra-gmi.c                       |   44 +-
>  drivers/clk/tegra/Makefile                    |    1 +
>  drivers/clk/tegra/clk-device.c                |  222 +++
>  drivers/clk/tegra/clk-pll.c                   |    2 +-
>  drivers/clk/tegra/clk-super.c                 |    2 +-
>  drivers/clk/tegra/clk-tegra-periph.c          |    6 +-
>  drivers/clk/tegra/clk-tegra20.c               |   39 +-
>  drivers/clk/tegra/clk-tegra30.c               |   70 +-
>  drivers/clk/tegra/clk.c                       |   66 +
>  drivers/clk/tegra/clk.h                       |    2 +
>  drivers/gpu/drm/tegra/dc.c                    |   69 +
>  drivers/gpu/drm/tegra/dc.h                    |    2 +
>  drivers/gpu/drm/tegra/gr2d.c                  |  156 +-
>  drivers/gpu/drm/tegra/gr3d.c                  |  401 ++++-
>  drivers/gpu/drm/tegra/hdmi.c                  |    9 +-
>  drivers/gpu/drm/tegra/vic.c                   |   15 +
>  drivers/gpu/host1x/channel.c                  |    8 +
>  drivers/gpu/host1x/debug.c                    |   15 +
>  drivers/gpu/host1x/dev.c                      |  184 ++-
>  drivers/gpu/host1x/dev.h                      |    3 +-
>  drivers/gpu/host1x/hw/channel_hw.c            |   44 +-
>  drivers/gpu/host1x/intr.c                     |    3 -
>  drivers/gpu/host1x/syncpt.c                   |    5 +-
>  drivers/memory/tegra/tegra20-emc.c            |   31 +
>  drivers/memory/tegra/tegra30-emc.c            |   31 +
>  drivers/mmc/host/sdhci-tegra.c                |   81 +-
>  drivers/mtd/nand/raw/tegra_nand.c             |   44 +-
>  drivers/pwm/pwm-tegra.c                       |  104 +-
>  drivers/soc/tegra/common.c                    |    4 +-
>  drivers/soc/tegra/fuse/fuse-tegra.c           |   62 +
>  drivers/soc/tegra/fuse/fuse-tegra20.c         |   10 +-
>  drivers/soc/tegra/fuse/fuse-tegra30.c         |   10 +-
>  drivers/soc/tegra/fuse/fuse.h                 |    2 +
>  drivers/soc/tegra/pmc.c                       |  164 ++
>  drivers/spi/spi-tegra20-slink.c               |   76 +-
>  drivers/staging/media/tegra-vde/vde.c         |   59 +-
>  drivers/usb/chipidea/ci_hdrc_tegra.c          |   50 +-
>  include/linux/host1x.h                        |    1 +
>  include/soc/tegra/common.h                    |   13 +
>  60 files changed, 4710 insertions(+), 370 deletions(-)
>  delete mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt
>  create mode 100644 Documentation/devicetree/bindings/media/nvidia,tegra-vde.yaml
>  create mode 100644 drivers/clk/tegra/clk-device.c
>

Dmitry, I am happy to help to review this, but unfortunately(?) I am
moving into holiday mode for a couple of weeks, very soon. I will do
my best to move forward with the review, but please be patient.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15
  2021-07-09 13:01 ` [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Ulf Hansson
@ 2021-07-09 21:23   ` Dmitry Osipenko
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-09 21:23 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

09.07.2021 16:01, Ulf Hansson пишет:
...
> 
> Dmitry, I am happy to help to review this, but unfortunately(?) I am
> moving into holiday mode for a couple of weeks, very soon. I will do
> my best to move forward with the review, but please be patient.

Thank you for letting me know, have a good holidays!

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend
  2021-07-01 23:27 ` [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend Dmitry Osipenko
@ 2021-07-21 16:00   ` Dmitry Osipenko
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-07-21 16:00 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm, Viresh Kumar, Stephen Boyd,
	Ulf Hansson

02.07.2021 02:27, Dmitry Osipenko пишет:
> +static int __maybe_unused tegra_fuse_resume(struct device *dev)
> +{
> +	int err;
> +
> +	/*
> +	 * Critical for RAM re-repair operation, which must occur on resume
> +	 * from LP1 system suspend and as part of CCPLEX cluster switching.
> +	 */
> +	if (fuse->soc->clk_suspend_on) {
> +		err = pm_runtime_force_resume(dev);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused tegra_fuse_suspend(struct device *dev)
> +{
> +	int err;
> +
> +	if (fuse->soc->clk_suspend_on) {
> +		err = pm_runtime_force_suspend(dev);
> +		if (err)
> +			return err;
> +	}

I just noticed that something gone wrong with this patch, the condition
should have been inverted. I'll fix it in the next version.

I may also try to factor out these fuse and some other patches which
could become a 5.15 material.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-07-01 23:26 ` [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers Dmitry Osipenko
@ 2021-08-02 14:48   ` Ulf Hansson
  2021-08-02 18:23     ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-08-02 14:48 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Fri, 2 Jul 2021 at 01:28, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> Implement attach_dev() callback of power domain drivers that initializes
> the domain's performance state. GENPD core will apply the performance
> state on the first runtime PM resume of the attached device.
>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/soc/tegra/pmc.c | 147 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 147 insertions(+)
>
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index f63dfb2ca3f9..ebafb818b08e 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -506,6 +506,151 @@ static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value,
>                 writel(value, pmc->scratch + offset);
>  }
>
> +static const char * const tegra_emc_compats[] = {
> +       "nvidia,tegra20-emc",
> +       "nvidia,tegra30-emc",
> +       NULL,
> +};
> +
> +/*
> + * This GENPD callback is used by both powergate and core domains.
> + *
> + * We retrieve clock rate of the attached device and initialize domain's
> + * performance state in accordance to the clock rate.
> + */
> +static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
> +                                  struct device *dev)
> +{
> +       struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
> +       struct opp_table *opp_table, *pd_opp_table;
> +       struct generic_pm_domain *core_genpd;
> +       struct dev_pm_opp *opp, *pd_opp;
> +       unsigned long rate, state;
> +       struct gpd_link *link;
> +       struct clk *clk;
> +       u32 hw_version;
> +       int ret;
> +
> +       /*
> +        * Tegra114+ SocS don't support OPP yet.  But if they will get OPP
> +        * support, then we want to skip OPP for older kernels to preserve
> +        * compatibility of newer DTBs with older kernels.
> +        */
> +       if (!pmc->soc->supports_core_domain)
> +               return 0;
> +
> +       /*
> +        * The EMC devices are a special case because we have a protection
> +        * from non-EMC drivers getting clock handle before EMC driver is
> +        * fully initialized.  The goal of the protection is to prevent
> +        * devfreq driver from getting failures if it will try to change
> +        * EMC clock rate until clock is fully initialized.  The EMC drivers
> +        * will initialize the performance state by themselves.
> +        */
> +       if (of_device_compatible_match(dev->of_node, tegra_emc_compats))
> +               return 0;
> +
> +       clk = clk_get(dev, NULL);
> +       if (IS_ERR(clk)) {
> +               dev_err(&genpd->dev, "failed to get clk of %s: %pe\n",
> +                       dev_name(dev), clk);
> +               return PTR_ERR(clk);
> +       }
> +
> +       rate = clk_get_rate(clk);
> +       if (!rate) {
> +               dev_err(&genpd->dev, "failed to get clk rate of %s\n",
> +                       dev_name(dev));
> +               ret = -EINVAL;
> +               goto put_clk;
> +       }
> +
> +       if (of_machine_is_compatible("nvidia,tegra20"))
> +               hw_version = BIT(tegra_sku_info.soc_process_id);
> +       else
> +               hw_version = BIT(tegra_sku_info.soc_speedo_id);
> +
> +       opp_table = dev_pm_opp_set_supported_hw(dev, &hw_version, 1);
> +       if (IS_ERR(opp_table)) {
> +               dev_err(&genpd->dev, "failed to set OPP supported HW for %s: %d\n",
> +                       dev_name(dev), ret);
> +               ret = PTR_ERR(opp_table);
> +               goto put_clk;
> +       }
> +
> +       ret = dev_pm_opp_of_add_table(dev);
> +       if (ret) {
> +               /* older DTBs that don't have OPPs will get -ENODEV here */
> +               if (ret != -ENODEV)
> +                       dev_err(&genpd->dev, "failed to get OPP table of %s: %d\n",
> +                               dev_name(dev), ret);
> +               else
> +                       ret = 0;
> +
> +               goto put_supported_hw;
> +       }
> +
> +       /* find suitable OPP for the rate */
> +       opp = dev_pm_opp_find_freq_ceil(dev, &rate);
> +
> +       if (opp == ERR_PTR(-ERANGE))
> +               opp = dev_pm_opp_find_freq_floor(dev, &rate);
> +
> +       if (IS_ERR(opp)) {
> +               dev_err(&genpd->dev, "failed to find OPP for %luHz of %s: %pe\n",
> +                       rate, dev_name(dev), opp);
> +               ret = PTR_ERR(opp);
> +               goto remove_dev_table;
> +       }
> +
> +       if (!list_empty(&genpd->child_links)) {
> +               link = list_first_entry(&genpd->child_links, struct gpd_link,
> +                                       child_node);
> +               core_genpd = link->parent;
> +       } else {
> +               core_genpd = genpd;
> +       }

This looks a bit odd to me. A genpd provider shouldn't need to walk
these links as these are considered internals to genpd. Normally this
needs lockings, etc.

Why exactly do you need this?

> +
> +       pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
> +       if (IS_ERR(pd_opp_table)) {
> +               dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
> +                       dev_name(&core_genpd->dev), pd_opp_table);
> +               ret = PTR_ERR(pd_opp_table);
> +               goto put_dev_opp;
> +       }
> +
> +       pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
> +       if (IS_ERR(pd_opp)) {
> +               dev_err(&genpd->dev,
> +                       "failed to xlate required OPP for %luHz of %s: %pe\n",
> +                       rate, dev_name(dev), pd_opp);
> +               ret = PTR_ERR(pd_opp);
> +               goto put_pd_opp_table;
> +       }
> +
> +       /*
> +        * The initialized state will be applied by GENPD core on the first
> +        * RPM-resume of the device.  This means that drivers don't need to
> +        * explicitly initialize performance state.
> +        */
> +       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
> +       gpd_data->rpm_pstate = state;

Could the above be replaced with Rajendra's suggestion [1], which
changes genpd to internally during attach, to set a default
performance state when there is a "required-opp" specified in the
device  node?

[...]

Kind regards
Uffe

[1]
https://lkml.org/lkml/2021/7/20/99

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-02 14:48   ` Ulf Hansson
@ 2021-08-02 18:23     ` Dmitry Osipenko
  2021-08-04  9:59       ` Ulf Hansson
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-02 18:23 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

02.08.2021 17:48, Ulf Hansson пишет:
...
>> +       if (!list_empty(&genpd->child_links)) {
>> +               link = list_first_entry(&genpd->child_links, struct gpd_link,
>> +                                       child_node);
>> +               core_genpd = link->parent;
>> +       } else {
>> +               core_genpd = genpd;
>> +       }
> 
> This looks a bit odd to me. A genpd provider shouldn't need to walk
> these links as these are considered internals to genpd. Normally this
> needs lockings, etc.
> 
> Why exactly do you need this?

We have a chain of PMC domain -> core domain, both domains are created
and liked together by this PMC driver. Devices are attached to either
PMC domain or to core domain. PMC domain doesn't handle the performance
changes, performance requests go down to the core domain.

This is needed in order to translate the device's OPP into performance
state of the core domain, based on the domain to which device is attached.

>> +
>> +       pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
>> +       if (IS_ERR(pd_opp_table)) {
>> +               dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
>> +                       dev_name(&core_genpd->dev), pd_opp_table);
>> +               ret = PTR_ERR(pd_opp_table);
>> +               goto put_dev_opp;
>> +       }
>> +
>> +       pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
>> +       if (IS_ERR(pd_opp)) {
>> +               dev_err(&genpd->dev,
>> +                       "failed to xlate required OPP for %luHz of %s: %pe\n",
>> +                       rate, dev_name(dev), pd_opp);
>> +               ret = PTR_ERR(pd_opp);
>> +               goto put_pd_opp_table;
>> +       }
>> +
>> +       /*
>> +        * The initialized state will be applied by GENPD core on the first
>> +        * RPM-resume of the device.  This means that drivers don't need to
>> +        * explicitly initialize performance state.
>> +        */
>> +       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
>> +       gpd_data->rpm_pstate = state;
> 
> Could the above be replaced with Rajendra's suggestion [1], which
> changes genpd to internally during attach, to set a default
> performance state when there is a "required-opp" specified in the
> device  node?

It's not a "static" performance level here, but any level depending on
h/w state left from bootloader and etc. The performance level
corresponds to the voltage of the core domain, hence we need to
initialize the voltage vote before device is resumed.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-02 18:23     ` Dmitry Osipenko
@ 2021-08-04  9:59       ` Ulf Hansson
  2021-08-04 21:16         ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-08-04  9:59 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Mon, 2 Aug 2021 at 20:23, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> 02.08.2021 17:48, Ulf Hansson пишет:
> ...
> >> +       if (!list_empty(&genpd->child_links)) {
> >> +               link = list_first_entry(&genpd->child_links, struct gpd_link,
> >> +                                       child_node);
> >> +               core_genpd = link->parent;
> >> +       } else {
> >> +               core_genpd = genpd;
> >> +       }
> >
> > This looks a bit odd to me. A genpd provider shouldn't need to walk
> > these links as these are considered internals to genpd. Normally this
> > needs lockings, etc.
> >
> > Why exactly do you need this?
>
> We have a chain of PMC domain -> core domain, both domains are created
> and liked together by this PMC driver. Devices are attached to either
> PMC domain or to core domain. PMC domain doesn't handle the performance
> changes, performance requests go down to the core domain.

Did I get this right? The core domain is the parent to the PMC domain?

>
> This is needed in order to translate the device's OPP into performance
> state of the core domain, based on the domain to which device is attached.

So, the PMC domain doesn't have an OPP table associated with it, but
some of its attached devices may still have available OPPs, which
should be managed through the parent domain (core domain). Correct?

Is there a DT patch in the series that I can look at that shows how
this is encoded?

Hmm, I have the feeling that we should try to manage in some generic
way in genpd, rather than having to deal with it here.

>
> >> +
> >> +       pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
> >> +       if (IS_ERR(pd_opp_table)) {
> >> +               dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
> >> +                       dev_name(&core_genpd->dev), pd_opp_table);
> >> +               ret = PTR_ERR(pd_opp_table);
> >> +               goto put_dev_opp;
> >> +       }
> >> +
> >> +       pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
> >> +       if (IS_ERR(pd_opp)) {
> >> +               dev_err(&genpd->dev,
> >> +                       "failed to xlate required OPP for %luHz of %s: %pe\n",
> >> +                       rate, dev_name(dev), pd_opp);
> >> +               ret = PTR_ERR(pd_opp);
> >> +               goto put_pd_opp_table;
> >> +       }
> >> +
> >> +       /*
> >> +        * The initialized state will be applied by GENPD core on the first
> >> +        * RPM-resume of the device.  This means that drivers don't need to
> >> +        * explicitly initialize performance state.
> >> +        */
> >> +       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
> >> +       gpd_data->rpm_pstate = state;
> >
> > Could the above be replaced with Rajendra's suggestion [1], which
> > changes genpd to internally during attach, to set a default
> > performance state when there is a "required-opp" specified in the
> > device  node?
>
> It's not a "static" performance level here, but any level depending on
> h/w state left from bootloader and etc. The performance level
> corresponds to the voltage of the core domain, hence we need to
> initialize the voltage vote before device is resumed.

Why not let the driver deal with this instead? It should be able to
probe its device, no matter what state the bootloader has put the
device into.

To me, it sounds like a call to dev_pm_genpd_set_performance_state()
(perhaps via dev_pm_opp_set_opp() or dev_pm_opp_set_rate()) from the
driver itself, should be sufficient?

I understand that it means the domain may change the OPP during boot,
without respecting a vote for a device that has not been probed yet.
But is there a problem with this?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-04  9:59       ` Ulf Hansson
@ 2021-08-04 21:16         ` Dmitry Osipenko
  2021-08-09 14:15           ` Ulf Hansson
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-04 21:16 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

04.08.2021 12:59, Ulf Hansson пишет:
> On Mon, 2 Aug 2021 at 20:23, Dmitry Osipenko <digetx@gmail.com> wrote:
>>
>> 02.08.2021 17:48, Ulf Hansson пишет:
>> ...
>>>> +       if (!list_empty(&genpd->child_links)) {
>>>> +               link = list_first_entry(&genpd->child_links, struct gpd_link,
>>>> +                                       child_node);
>>>> +               core_genpd = link->parent;
>>>> +       } else {
>>>> +               core_genpd = genpd;
>>>> +       }
>>>
>>> This looks a bit odd to me. A genpd provider shouldn't need to walk
>>> these links as these are considered internals to genpd. Normally this
>>> needs lockings, etc.
>>>
>>> Why exactly do you need this?
>>
>> We have a chain of PMC domain -> core domain, both domains are created
>> and liked together by this PMC driver. Devices are attached to either
>> PMC domain or to core domain. PMC domain doesn't handle the performance
>> changes, performance requests go down to the core domain.
> 
> Did I get this right? The core domain is the parent to the PMC domain?

You got this right.

>>
>> This is needed in order to translate the device's OPP into performance
>> state of the core domain, based on the domain to which device is attached.
> 
> So, the PMC domain doesn't have an OPP table associated with it, but
> some of its attached devices may still have available OPPs, which
> should be managed through the parent domain (core domain). Correct?

Yes, the OPPs are specified only for the core domain.

> Is there a DT patch in the series that I can look at that shows how
> this is encoded?

See patches which are adding domains and OPPs to DTs:

https://patchwork.ozlabs.org/project/linux-tegra/patch/20210701232728.23591-34-digetx@gmail.com/

https://patchwork.ozlabs.org/project/linux-tegra/patch/20210701232728.23591-35-digetx@gmail.com/

> Hmm, I have the feeling that we should try to manage in some generic
> way in genpd, rather than having to deal with it here.

Still it requires a platform-specific knowledge. It could be some new
genpd hook for the initialization. But I don't know what other platforms
may want to initialize, so it's not clear to me how to make it generic.

>>>> +
>>>> +       pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
>>>> +       if (IS_ERR(pd_opp_table)) {
>>>> +               dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
>>>> +                       dev_name(&core_genpd->dev), pd_opp_table);
>>>> +               ret = PTR_ERR(pd_opp_table);
>>>> +               goto put_dev_opp;
>>>> +       }
>>>> +
>>>> +       pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
>>>> +       if (IS_ERR(pd_opp)) {
>>>> +               dev_err(&genpd->dev,
>>>> +                       "failed to xlate required OPP for %luHz of %s: %pe\n",
>>>> +                       rate, dev_name(dev), pd_opp);
>>>> +               ret = PTR_ERR(pd_opp);
>>>> +               goto put_pd_opp_table;
>>>> +       }
>>>> +
>>>> +       /*
>>>> +        * The initialized state will be applied by GENPD core on the first
>>>> +        * RPM-resume of the device.  This means that drivers don't need to
>>>> +        * explicitly initialize performance state.
>>>> +        */
>>>> +       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
>>>> +       gpd_data->rpm_pstate = state;
>>>
>>> Could the above be replaced with Rajendra's suggestion [1], which
>>> changes genpd to internally during attach, to set a default
>>> performance state when there is a "required-opp" specified in the
>>> device  node?
>>
>> It's not a "static" performance level here, but any level depending on
>> h/w state left from bootloader and etc. The performance level
>> corresponds to the voltage of the core domain, hence we need to
>> initialize the voltage vote before device is resumed.
> 
> Why not let the driver deal with this instead? It should be able to
> probe its device, no matter what state the bootloader has put the
> device into.
> 
> To me, it sounds like a call to dev_pm_genpd_set_performance_state()
> (perhaps via dev_pm_opp_set_opp() or dev_pm_opp_set_rate()) from the
> driver itself, should be sufficient?

We did that in a previous versions of this series where drivers were
calling devm_tegra_core_dev_init_opp_table() helper during the probe to
initialize performance state of the domain. Moving OPP state
initialization into central place made drivers cleaner by removing the
boilerplate code.

I can revert back to the previous variant, although this variant works
well too.

> I understand that it means the domain may change the OPP during boot,
> without respecting a vote for a device that has not been probed yet.
> But is there a problem with this?

Domains themselves don't change OPP, there is no problem with that. The
point is to have cleaner code in the drivers.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-04 21:16         ` Dmitry Osipenko
@ 2021-08-09 14:15           ` Ulf Hansson
  2021-08-09 23:56             ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-08-09 14:15 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Wed, 4 Aug 2021 at 23:17, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> 04.08.2021 12:59, Ulf Hansson пишет:
> > On Mon, 2 Aug 2021 at 20:23, Dmitry Osipenko <digetx@gmail.com> wrote:
> >>
> >> 02.08.2021 17:48, Ulf Hansson пишет:
> >> ...
> >>>> +       if (!list_empty(&genpd->child_links)) {
> >>>> +               link = list_first_entry(&genpd->child_links, struct gpd_link,
> >>>> +                                       child_node);
> >>>> +               core_genpd = link->parent;
> >>>> +       } else {
> >>>> +               core_genpd = genpd;
> >>>> +       }
> >>>
> >>> This looks a bit odd to me. A genpd provider shouldn't need to walk
> >>> these links as these are considered internals to genpd. Normally this
> >>> needs lockings, etc.
> >>>
> >>> Why exactly do you need this?
> >>
> >> We have a chain of PMC domain -> core domain, both domains are created
> >> and liked together by this PMC driver. Devices are attached to either
> >> PMC domain or to core domain. PMC domain doesn't handle the performance
> >> changes, performance requests go down to the core domain.
> >
> > Did I get this right? The core domain is the parent to the PMC domain?
>
> You got this right.
>
> >>
> >> This is needed in order to translate the device's OPP into performance
> >> state of the core domain, based on the domain to which device is attached.
> >
> > So, the PMC domain doesn't have an OPP table associated with it, but
> > some of its attached devices may still have available OPPs, which
> > should be managed through the parent domain (core domain). Correct?
>
> Yes, the OPPs are specified only for the core domain.
>
> > Is there a DT patch in the series that I can look at that shows how
> > this is encoded?
>
> See patches which are adding domains and OPPs to DTs:
>
> https://patchwork.ozlabs.org/project/linux-tegra/patch/20210701232728.23591-34-digetx@gmail.com/
>
> https://patchwork.ozlabs.org/project/linux-tegra/patch/20210701232728.23591-35-digetx@gmail.com/

Thanks, this looks sane to me!

>
> > Hmm, I have the feeling that we should try to manage in some generic
> > way in genpd, rather than having to deal with it here.
>
> Still it requires a platform-specific knowledge. It could be some new
> genpd hook for the initialization. But I don't know what other platforms
> may want to initialize, so it's not clear to me how to make it generic.

Right. We may need some more thinking around this, if needed at all (see below).

>
> >>>> +
> >>>> +       pd_opp_table = dev_pm_opp_get_opp_table(&core_genpd->dev);
> >>>> +       if (IS_ERR(pd_opp_table)) {
> >>>> +               dev_err(&genpd->dev, "failed to get OPP table of %s: %pe\n",
> >>>> +                       dev_name(&core_genpd->dev), pd_opp_table);
> >>>> +               ret = PTR_ERR(pd_opp_table);
> >>>> +               goto put_dev_opp;
> >>>> +       }
> >>>> +
> >>>> +       pd_opp = dev_pm_opp_xlate_required_opp(opp_table, pd_opp_table, opp);
> >>>> +       if (IS_ERR(pd_opp)) {
> >>>> +               dev_err(&genpd->dev,
> >>>> +                       "failed to xlate required OPP for %luHz of %s: %pe\n",
> >>>> +                       rate, dev_name(dev), pd_opp);
> >>>> +               ret = PTR_ERR(pd_opp);
> >>>> +               goto put_pd_opp_table;
> >>>> +       }
> >>>> +
> >>>> +       /*
> >>>> +        * The initialized state will be applied by GENPD core on the first
> >>>> +        * RPM-resume of the device.  This means that drivers don't need to
> >>>> +        * explicitly initialize performance state.
> >>>> +        */
> >>>> +       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
> >>>> +       gpd_data->rpm_pstate = state;
> >>>
> >>> Could the above be replaced with Rajendra's suggestion [1], which
> >>> changes genpd to internally during attach, to set a default
> >>> performance state when there is a "required-opp" specified in the
> >>> device  node?
> >>
> >> It's not a "static" performance level here, but any level depending on
> >> h/w state left from bootloader and etc. The performance level
> >> corresponds to the voltage of the core domain, hence we need to
> >> initialize the voltage vote before device is resumed.
> >
> > Why not let the driver deal with this instead? It should be able to
> > probe its device, no matter what state the bootloader has put the
> > device into.
> >
> > To me, it sounds like a call to dev_pm_genpd_set_performance_state()
> > (perhaps via dev_pm_opp_set_opp() or dev_pm_opp_set_rate()) from the
> > driver itself, should be sufficient?
>
> We did that in a previous versions of this series where drivers were
> calling devm_tegra_core_dev_init_opp_table() helper during the probe to
> initialize performance state of the domain. Moving OPP state
> initialization into central place made drivers cleaner by removing the
> boilerplate code.

I am not against doing this in a central place, like $subject patch
suggests. As a matter of fact, it makes perfect sense to me.

However, what I am concerned about, is that you require to use genpd
internal data structures to do it. I think we should try to avoid
that.

>
> I can revert back to the previous variant, although this variant works
> well too.

I looked at that code and in that path we end up calling
dev_pm_opp_set_rate(), after it has initialized the opp table for the
device.

Rather than doing the OF parsing above to find out the current state
for the device, why can't you just call dev_pm_opp_set_rate() to
initialize a proper vote instead?

>
> > I understand that it means the domain may change the OPP during boot,
> > without respecting a vote for a device that has not been probed yet.
> > But is there a problem with this?
>
> Domains themselves don't change OPP, there is no problem with that. The
> point is to have cleaner code in the drivers.

Alright, this makes sense to me as well.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-09 14:15           ` Ulf Hansson
@ 2021-08-09 23:56             ` Dmitry Osipenko
  2021-08-10 10:51               ` Ulf Hansson
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-09 23:56 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

09.08.2021 17:15, Ulf Hansson пишет:
>> We did that in a previous versions of this series where drivers were
>> calling devm_tegra_core_dev_init_opp_table() helper during the probe to
>> initialize performance state of the domain. Moving OPP state
>> initialization into central place made drivers cleaner by removing the
>> boilerplate code.
> I am not against doing this in a central place, like $subject patch
> suggests. As a matter of fact, it makes perfect sense to me.
> 
> However, what I am concerned about, is that you require to use genpd
> internal data structures to do it. I think we should try to avoid
> that.

Alright, what do you think about this:

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index a934c679e6ce..5faed62075e9 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2669,12 +2669,37 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
 	dev->pm_domain->detach = genpd_dev_pm_detach;
 	dev->pm_domain->sync = genpd_dev_pm_sync;
 
+	if (pd->default_performance_state) {
+		unsigned int default_pstate;
+
+		ret = pd->default_performance_state(pd, dev);
+		if (ret < 0) {
+			dev_err(dev, "failed to get default performance state for PM domain %s: %d\n",
+				pd->name, ret);
+			goto out;
+		}
+
+		default_pstate = ret;
+
+		if (power_on) {
+			ret = dev_pm_genpd_set_performance_state(dev, default_pstate);
+			if (ret) {
+				dev_err(dev, "failed to set default performance state %u for PM domain %s: %d\n",
+					default_pstate, pd->name, ret);
+				goto out;
+			}
+		} else {
+			dev_gpd_data(dev)->rpm_pstate = default_pstate;
+		}
+	}
+
 	if (power_on) {
 		genpd_lock(pd);
 		ret = genpd_power_on(pd, 0);
 		genpd_unlock(pd);
 	}
 
+out:
 	if (ret)
 		genpd_remove_device(pd, dev);
 
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 81d1f019fa0c..9efb55f52462 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -518,15 +518,14 @@ static const char * const tegra_emc_compats[] = {
  * We retrieve clock rate of the attached device and initialize domain's
  * performance state in accordance to the clock rate.
  */
-static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
-				   struct device *dev)
+static int tegra_pmc_genpd_default_perf_state(struct generic_pm_domain *genpd,
+					      struct device *dev)
 {
-	struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
 	struct opp_table *opp_table, *pd_opp_table;
 	struct generic_pm_domain *core_genpd;
 	struct dev_pm_opp *opp, *pd_opp;
-	unsigned long rate, state;
 	struct gpd_link *link;
+	unsigned long rate;
 	struct clk *clk;
 	u32 hw_version;
 	int ret;
@@ -633,8 +632,7 @@ static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
 	 * RPM-resume of the device.  This means that drivers don't need to
 	 * explicitly initialize performance state.
 	 */
-	state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
-	gpd_data->rpm_pstate = state;
+	ret = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
 	dev_pm_opp_put(pd_opp);
 
 put_pd_opp_table:
@@ -1383,7 +1381,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 
 	pg->id = id;
 	pg->genpd.name = np->name;
-	pg->genpd.attach_dev = tegra_pmc_pd_attach_dev;
+	pg->genpd.default_performance_state = tegra_pmc_genpd_default_perf_state;
 	pg->genpd.power_off = tegra_genpd_power_off;
 	pg->genpd.power_on = tegra_genpd_power_on;
 	pg->pmc = pmc;
@@ -1500,7 +1498,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
 		return -ENOMEM;
 
 	genpd->name = np->name;
-	genpd->attach_dev = tegra_pmc_pd_attach_dev;
+	genpd->default_performance_state = tegra_pmc_genpd_default_perf_state;
 	genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
 	genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
 
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 21a0577305ef..cd4867817ca5 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -143,6 +143,8 @@ struct generic_pm_domain {
 			  struct device *dev);
 	void (*detach_dev)(struct generic_pm_domain *domain,
 			   struct device *dev);
+	int (*default_performance_state)(struct generic_pm_domain *domain,
+					 struct device *dev);
 	unsigned int flags;		/* Bit field of configs for genpd */
 	struct genpd_power_state *states;
 	void (*free_states)(struct genpd_power_state *states,

>> I can revert back to the previous variant, although this variant works
>> well too.
> I looked at that code and in that path we end up calling
> dev_pm_opp_set_rate(), after it has initialized the opp table for the
> device.
> 
> Rather than doing the OF parsing above to find out the current state
> for the device, why can't you just call dev_pm_opp_set_rate() to
> initialize a proper vote instead?
> 

For some devices clock rate is either preset by bootloader, or by clk driver, or by assigned-clocks in a device-tree. And then I don't see what's the difference in comparison to initialization for the current rate.

For some devices, like memory controller, we can't just change the clock rate because it's a complex procedure and some boards will use fixed rate, but the power vote still must be initialized.

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-09 23:56             ` Dmitry Osipenko
@ 2021-08-10 10:51               ` Ulf Hansson
  2021-08-11 19:30                 ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-08-10 10:51 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Tue, 10 Aug 2021 at 01:56, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> 09.08.2021 17:15, Ulf Hansson пишет:
> >> We did that in a previous versions of this series where drivers were
> >> calling devm_tegra_core_dev_init_opp_table() helper during the probe to
> >> initialize performance state of the domain. Moving OPP state
> >> initialization into central place made drivers cleaner by removing the
> >> boilerplate code.
> > I am not against doing this in a central place, like $subject patch
> > suggests. As a matter of fact, it makes perfect sense to me.
> >
> > However, what I am concerned about, is that you require to use genpd
> > internal data structures to do it. I think we should try to avoid
> > that.
>
> Alright, what do you think about this:
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index a934c679e6ce..5faed62075e9 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -2669,12 +2669,37 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
>         dev->pm_domain->detach = genpd_dev_pm_detach;
>         dev->pm_domain->sync = genpd_dev_pm_sync;
>
> +       if (pd->default_performance_state) {
> +               unsigned int default_pstate;
> +
> +               ret = pd->default_performance_state(pd, dev);
> +               if (ret < 0) {
> +                       dev_err(dev, "failed to get default performance state for PM domain %s: %d\n",
> +                               pd->name, ret);
> +                       goto out;
> +               }

Adding a new callback seems reasonable to support this.

> +
> +               default_pstate = ret;
> +
> +               if (power_on) {
> +                       ret = dev_pm_genpd_set_performance_state(dev, default_pstate);

However, this is more questionable to me.

First, I don't think we should care about whether this is "power_on"
or not. At this point, performance states are treated orthogonal to
idle states in genpd. We may decide to change that in some way, but
that deserves a different change.

Second, I don't think we should call
dev_pm_genpd_set_performance_state() from here. It's probably better
handled from the genpd callback itself, if/when needed.

That said, perhaps the new callback should just return a regular error
code and zero on success, rather than the current performance state.
See more below.

> +                       if (ret) {
> +                               dev_err(dev, "failed to set default performance state %u for PM domain %s: %d\n",
> +                                       default_pstate, pd->name, ret);
> +                               goto out;
> +                       }
> +               } else {
> +                       dev_gpd_data(dev)->rpm_pstate = default_pstate;

No, this isn't the right thing to do.

It looks like you are trying to use the ->rpm_pstate for
synchronization with runtime PM for consumer drivers. This is fragile
as it depends on the runtime PM deployment in the consumer driver. I
think you should look at ->rpm_pstate as a variable solely for
managing save/restore of the performance state for the device, during
runtime suspend/resume in genpd.

Synchronization of a vote for a performance state for a device, needs
to be managed by calling dev_pm_genpd_set_performance_state() - or by
calling an OPP function that calls it, like dev_pm_opp_set_rate(), for
example.

> +               }
> +       }
> +
>         if (power_on) {
>                 genpd_lock(pd);
>                 ret = genpd_power_on(pd, 0);
>                 genpd_unlock(pd);
>         }
>
> +out:
>         if (ret)
>                 genpd_remove_device(pd, dev);
>
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index 81d1f019fa0c..9efb55f52462 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -518,15 +518,14 @@ static const char * const tegra_emc_compats[] = {
>   * We retrieve clock rate of the attached device and initialize domain's
>   * performance state in accordance to the clock rate.
>   */
> -static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
> -                                  struct device *dev)
> +static int tegra_pmc_genpd_default_perf_state(struct generic_pm_domain *genpd,
> +                                             struct device *dev)
>  {
> -       struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
>         struct opp_table *opp_table, *pd_opp_table;
>         struct generic_pm_domain *core_genpd;
>         struct dev_pm_opp *opp, *pd_opp;
> -       unsigned long rate, state;
>         struct gpd_link *link;
> +       unsigned long rate;
>         struct clk *clk;
>         u32 hw_version;
>         int ret;
> @@ -633,8 +632,7 @@ static int tegra_pmc_pd_attach_dev(struct generic_pm_domain *genpd,
>          * RPM-resume of the device.  This means that drivers don't need to
>          * explicitly initialize performance state.
>          */
> -       state = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);
> -       gpd_data->rpm_pstate = state;
> +       ret = pm_genpd_opp_to_performance_state(&core_genpd->dev, pd_opp);

I don't see how this avoids tegra_pmc_genpd_default_perf_state() from
having to walk &genpd->child_links.

That's still an issue, right?

>         dev_pm_opp_put(pd_opp);
>
>  put_pd_opp_table:
> @@ -1383,7 +1381,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
>
>         pg->id = id;
>         pg->genpd.name = np->name;
> -       pg->genpd.attach_dev = tegra_pmc_pd_attach_dev;
> +       pg->genpd.default_performance_state = tegra_pmc_genpd_default_perf_state;
>         pg->genpd.power_off = tegra_genpd_power_off;
>         pg->genpd.power_on = tegra_genpd_power_on;
>         pg->pmc = pmc;
> @@ -1500,7 +1498,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
>                 return -ENOMEM;
>
>         genpd->name = np->name;
> -       genpd->attach_dev = tegra_pmc_pd_attach_dev;
> +       genpd->default_performance_state = tegra_pmc_genpd_default_perf_state;
>         genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
>         genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
>
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 21a0577305ef..cd4867817ca5 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -143,6 +143,8 @@ struct generic_pm_domain {
>                           struct device *dev);
>         void (*detach_dev)(struct generic_pm_domain *domain,
>                            struct device *dev);
> +       int (*default_performance_state)(struct generic_pm_domain *domain,
> +                                        struct device *dev);
>         unsigned int flags;             /* Bit field of configs for genpd */
>         struct genpd_power_state *states;
>         void (*free_states)(struct genpd_power_state *states,
>
> >> I can revert back to the previous variant, although this variant works
> >> well too.
> > I looked at that code and in that path we end up calling
> > dev_pm_opp_set_rate(), after it has initialized the opp table for the
> > device.
> >
> > Rather than doing the OF parsing above to find out the current state
> > for the device, why can't you just call dev_pm_opp_set_rate() to
> > initialize a proper vote instead?
> >
>
> For some devices clock rate is either preset by bootloader, or by clk driver, or by assigned-clocks in a device-tree. And then I don't see what's the difference in comparison to initialization for the current rate.
>
> For some devices, like memory controller, we can't just change the clock rate because it's a complex procedure and some boards will use fixed rate, but the power vote still must be initialized.

I am not saying you should change the clock rate. The current code
path that runs via devm_tegra_core_dev_init_opp_table() just calls
clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
vote for the corresponding OPP level. Right?

Isn't this exactly what you want? No?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-10 10:51               ` Ulf Hansson
@ 2021-08-11 19:30                 ` Dmitry Osipenko
  2021-08-11 22:41                   ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-11 19:30 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

10.08.2021 13:51, Ulf Hansson пишет:
...
>> +               if (power_on) {
>> +                       ret = dev_pm_genpd_set_performance_state(dev, default_pstate);
> 
> However, this is more questionable to me.
> 
> First, I don't think we should care about whether this is "power_on"
> or not. At this point, performance states are treated orthogonal to
> idle states in genpd. We may decide to change that in some way, but
> that deserves a different change.

Okay

> Second, I don't think we should call
> dev_pm_genpd_set_performance_state() from here. It's probably better
> handled from the genpd callback itself, if/when needed.
Indeed

> That said, perhaps the new callback should just return a regular error
> code and zero on success, rather than the current performance state.
> See more below.
> 
>> +                       if (ret) {
>> +                               dev_err(dev, "failed to set default performance state %u for PM domain %s: %d\n",
>> +                                       default_pstate, pd->name, ret);
>> +                               goto out;
>> +                       }
>> +               } else {
>> +                       dev_gpd_data(dev)->rpm_pstate = default_pstate;
> 
> No, this isn't the right thing to do.
> 
> It looks like you are trying to use the ->rpm_pstate for
> synchronization with runtime PM for consumer drivers. This is fragile
> as it depends on the runtime PM deployment in the consumer driver. I
> think you should look at ->rpm_pstate as a variable solely for
> managing save/restore of the performance state for the device, during
> runtime suspend/resume in genpd.
> 
> Synchronization of a vote for a performance state for a device, needs
> to be managed by calling dev_pm_genpd_set_performance_state() - or by
> calling an OPP function that calls it, like dev_pm_opp_set_rate(), for
> example.

The !power_on case should be skipped at all because common core code
can't handle it properly, hence rpm_pstate shouldn't be touched. I
realized it only after sending out this email.

...
>>> Rather than doing the OF parsing above to find out the current state
>>> for the device, why can't you just call dev_pm_opp_set_rate() to
>>> initialize a proper vote instead?
>>>
>>
>> For some devices clock rate is either preset by bootloader, or by clk driver, or by assigned-clocks in a device-tree. And then I don't see what's the difference in comparison to initialization for the current rate.
>>
>> For some devices, like memory controller, we can't just change the clock rate because it's a complex procedure and some boards will use fixed rate, but the power vote still must be initialized.
> 
> I am not saying you should change the clock rate. The current code
> path that runs via devm_tegra_core_dev_init_opp_table() just calls
> clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
> vote for the corresponding OPP level. Right?
> 
> Isn't this exactly what you want? No?

I see now what you meant, it's actually a simpler variant and it works
too. Thank you for the suggestion, I'll prepare v8.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-11 19:30                 ` Dmitry Osipenko
@ 2021-08-11 22:41                   ` Dmitry Osipenko
  2021-08-12  1:40                     ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-11 22:41 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

11.08.2021 22:30, Dmitry Osipenko пишет:
> 10.08.2021 13:51, Ulf Hansson пишет:
> ...
>>> +               if (power_on) {
>>> +                       ret = dev_pm_genpd_set_performance_state(dev, default_pstate);
>>
>> However, this is more questionable to me.
>>
>> First, I don't think we should care about whether this is "power_on"
>> or not. At this point, performance states are treated orthogonal to
>> idle states in genpd. We may decide to change that in some way, but
>> that deserves a different change.
> 
> Okay
> 
>> Second, I don't think we should call
>> dev_pm_genpd_set_performance_state() from here. It's probably better
>> handled from the genpd callback itself, if/when needed.
> Indeed
> 
>> That said, perhaps the new callback should just return a regular error
>> code and zero on success, rather than the current performance state.
>> See more below.
>>
>>> +                       if (ret) {
>>> +                               dev_err(dev, "failed to set default performance state %u for PM domain %s: %d\n",
>>> +                                       default_pstate, pd->name, ret);
>>> +                               goto out;
>>> +                       }
>>> +               } else {
>>> +                       dev_gpd_data(dev)->rpm_pstate = default_pstate;
>>
>> No, this isn't the right thing to do.
>>
>> It looks like you are trying to use the ->rpm_pstate for
>> synchronization with runtime PM for consumer drivers. This is fragile
>> as it depends on the runtime PM deployment in the consumer driver. I
>> think you should look at ->rpm_pstate as a variable solely for
>> managing save/restore of the performance state for the device, during
>> runtime suspend/resume in genpd.
>>
>> Synchronization of a vote for a performance state for a device, needs
>> to be managed by calling dev_pm_genpd_set_performance_state() - or by
>> calling an OPP function that calls it, like dev_pm_opp_set_rate(), for
>> example.
> 
> The !power_on case should be skipped at all because common core code
> can't handle it properly, hence rpm_pstate shouldn't be touched. I
> realized it only after sending out this email.
> 
> ...
>>>> Rather than doing the OF parsing above to find out the current state
>>>> for the device, why can't you just call dev_pm_opp_set_rate() to
>>>> initialize a proper vote instead?
>>>>
>>>
>>> For some devices clock rate is either preset by bootloader, or by clk driver, or by assigned-clocks in a device-tree. And then I don't see what's the difference in comparison to initialization for the current rate.
>>>
>>> For some devices, like memory controller, we can't just change the clock rate because it's a complex procedure and some boards will use fixed rate, but the power vote still must be initialized.
>>
>> I am not saying you should change the clock rate. The current code
>> path that runs via devm_tegra_core_dev_init_opp_table() just calls
>> clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
>> vote for the corresponding OPP level. Right?
>>
>> Isn't this exactly what you want? No?
> 
> I see now what you meant, it's actually a simpler variant and it works
> too. Thank you for the suggestion, I'll prepare v8.
> 

My bad, it doesn't work at all. I actually need to use the rpm_pstate or
something else because performance state is coupled with the enable
state of the device. If device is never rpm-suspended by consumer
driver, then the initialized performance state is never dropped. Hence I
want to initialize the state which is set only when device is resumed.

I'll need to think more about it.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-11 22:41                   ` Dmitry Osipenko
@ 2021-08-12  1:40                     ` Dmitry Osipenko
  2021-08-12 11:17                       ` Ulf Hansson
  0 siblings, 1 reply; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-12  1:40 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

12.08.2021 01:41, Dmitry Osipenko пишет:
>>> I am not saying you should change the clock rate. The current code
>>> path that runs via devm_tegra_core_dev_init_opp_table() just calls
>>> clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
>>> vote for the corresponding OPP level. Right?
>>>
>>> Isn't this exactly what you want? No?
>> I see now what you meant, it's actually a simpler variant and it works
>> too. Thank you for the suggestion, I'll prepare v8.
>>
> My bad, it doesn't work at all. I actually need to use the rpm_pstate or
> something else because performance state is coupled with the enable
> state of the device. If device is never rpm-suspended by consumer
> driver, then the initialized performance state is never dropped. Hence I
> want to initialize the state which is set only when device is resumed.
> 
> I'll need to think more about it.

GENPD core has these false assumptions:

1. It assumes that by default all devices are at zero performance level
at a boot time. This is not true for Tegra because hardware is
pre-initialized independently from GENPD.

2. It assumes that nothing depends on performance level and devices can
operate at any level at any time. Not true for Tegra and other platforms
where performance level is coupled with clocks state of attached
devices. OPP framework glues clock and performance level together for
us, which works good so far.

Hence I either need to patch every driver to use dev_pm_opp_set_rate in
order to sync clk rate with the perf level at device runtime, or I need
to preset the rpm perf level to allow GENPD core to set the level before
device is resumed.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-12  1:40                     ` Dmitry Osipenko
@ 2021-08-12 11:17                       ` Ulf Hansson
  2021-08-12 16:24                         ` Dmitry Osipenko
  0 siblings, 1 reply; 58+ messages in thread
From: Ulf Hansson @ 2021-08-12 11:17 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

On Thu, 12 Aug 2021 at 03:40, Dmitry Osipenko <digetx@gmail.com> wrote:
>
> 12.08.2021 01:41, Dmitry Osipenko пишет:
> >>> I am not saying you should change the clock rate. The current code
> >>> path that runs via devm_tegra_core_dev_init_opp_table() just calls
> >>> clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
> >>> vote for the corresponding OPP level. Right?
> >>>
> >>> Isn't this exactly what you want? No?
> >> I see now what you meant, it's actually a simpler variant and it works
> >> too. Thank you for the suggestion, I'll prepare v8.
> >>
> > My bad, it doesn't work at all. I actually need to use the rpm_pstate or
> > something else because performance state is coupled with the enable
> > state of the device. If device is never rpm-suspended by consumer
> > driver, then the initialized performance state is never dropped. Hence I
> > want to initialize the state which is set only when device is resumed.
> >
> > I'll need to think more about it.
>
> GENPD core has these false assumptions:
>
> 1. It assumes that by default all devices are at zero performance level
> at a boot time. This is not true for Tegra because hardware is
> pre-initialized independently from GENPD.

Right, which is similar to other SoCs.

>
> 2. It assumes that nothing depends on performance level and devices can
> operate at any level at any time. Not true for Tegra and other platforms
> where performance level is coupled with clocks state of attached
> devices. OPP framework glues clock and performance level together for
> us, which works good so far.

Right, OPPs need to be managed differently depending on the SoC.
That's why genpd is there to help and to model this as "performance
states" and to allow operations to be set through SoC specific
callabacks, for example.

More importantly, the assumption is that in general, consumer drivers
should use the OPP library to vote/set OPP levels, they shouldn't call
dev_pm_genpd_set_performance_state() - unless they know exactly what
they are doing.

>
> Hence I either need to patch every driver to use dev_pm_opp_set_rate in
> order to sync clk rate with the perf level at device runtime, or I need
> to preset the rpm perf level to allow GENPD core to set the level before
> device is resumed.

When the device is getting attached to its genpd (during the probe
sequence and for a single PM domain case), runtime PM is disabled for
the device. If you would call dev_pm_opp_set_rate() from a genpd
callback during attach (without changing the rate), this means you
would update/synchronize the vote. In this way, the vote is set before
the device is runtime resumed by the driver, right?

On the other hand, patching the driver should also be quite simple and
you need to do that anyways, don't you?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing
  2021-07-01 23:26 ` [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing Dmitry Osipenko
@ 2021-08-12 16:02   ` Dmitry Osipenko
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-12 16:02 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Ulf Hansson, Viresh Kumar,
	Stephen Boyd, Peter De Schrijver
  Cc: linux-kernel, linux-tegra, linux-pm

02.07.2021 02:26, Dmitry Osipenko пишет:
> Disable PMC state syncing in order to ensure that we won't break older
> kernels once device-trees will be updated with the addition of the power
> domains. Previously this was unnecessary because the plan was to make clk
> device that will attach to the domain for each clock, but the plan changed
> and now we're going make a better GENPD implementation that will require
> to update each device driver with the runtime PM and OPP support before
> we could safely enable the state syncing.
> 
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/soc/tegra/pmc.c | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index ea62f84d1c8b..f63dfb2ca3f9 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -360,6 +360,7 @@ struct tegra_pmc_soc {
>  	unsigned int num_pmc_clks;
>  	bool has_blink_output;
>  	bool has_usb_sleepwalk;
> +	bool supports_core_domain;
>  };
>  
>  /**
> @@ -3029,6 +3030,7 @@ static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
>  }
>  
>  static const struct tegra_pmc_soc tegra20_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = ARRAY_SIZE(tegra20_powergates),
>  	.powergates = tegra20_powergates,
>  	.num_cpu_powergates = 0,
> @@ -3089,6 +3091,7 @@ static const char * const tegra30_reset_sources[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra30_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = ARRAY_SIZE(tegra30_powergates),
>  	.powergates = tegra30_powergates,
>  	.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
> @@ -3145,6 +3148,7 @@ static const u8 tegra114_cpu_powergates[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra114_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = ARRAY_SIZE(tegra114_powergates),
>  	.powergates = tegra114_powergates,
>  	.num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates),
> @@ -3261,6 +3265,7 @@ static const struct pinctrl_pin_desc tegra124_pin_descs[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra124_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = ARRAY_SIZE(tegra124_powergates),
>  	.powergates = tegra124_powergates,
>  	.num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates),
> @@ -3386,6 +3391,7 @@ static const struct tegra_wake_event tegra210_wake_events[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra210_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = ARRAY_SIZE(tegra210_powergates),
>  	.powergates = tegra210_powergates,
>  	.num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
> @@ -3543,6 +3549,7 @@ static const struct tegra_wake_event tegra186_wake_events[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra186_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = 0,
>  	.powergates = NULL,
>  	.num_cpu_powergates = 0,
> @@ -3677,6 +3684,7 @@ static const struct tegra_wake_event tegra194_wake_events[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra194_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = 0,
>  	.powergates = NULL,
>  	.num_cpu_powergates = 0,
> @@ -3745,6 +3753,7 @@ static const char * const tegra234_reset_sources[] = {
>  };
>  
>  static const struct tegra_pmc_soc tegra234_pmc_soc = {
> +	.supports_core_domain = false,
>  	.num_powergates = 0,
>  	.powergates = NULL,
>  	.num_cpu_powergates = 0,
> @@ -3791,6 +3800,14 @@ static void tegra_pmc_sync_state(struct device *dev)
>  {
>  	int err;
>  
> +	/*
> +	 * Newer device-trees have power domains, but we need to prepare all
> +	 * device drivers with runtime PM and OPP support first, otherwise
> +	 * state syncing is unsafe.
> +	 */
> +	if (!pmc->soc->supports_core_domain)
> +		return;
> +
>  	/*
>  	 * Older device-trees don't have core PD, and thus, there are
>  	 * no dependencies that will block the state syncing. We shouldn't
> 

Thierry, could you please take this patch for 5.15? It should ease
further applying of the rest of the patches, thanks.

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers
  2021-08-12 11:17                       ` Ulf Hansson
@ 2021-08-12 16:24                         ` Dmitry Osipenko
  0 siblings, 0 replies; 58+ messages in thread
From: Dmitry Osipenko @ 2021-08-12 16:24 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Thierry Reding, Jonathan Hunter, Viresh Kumar, Stephen Boyd,
	Peter De Schrijver, Linux Kernel Mailing List, linux-tegra,
	Linux PM

12.08.2021 14:17, Ulf Hansson пишет:
> On Thu, 12 Aug 2021 at 03:40, Dmitry Osipenko <digetx@gmail.com> wrote:
>>
>> 12.08.2021 01:41, Dmitry Osipenko пишет:
>>>>> I am not saying you should change the clock rate. The current code
>>>>> path that runs via devm_tegra_core_dev_init_opp_table() just calls
>>>>> clk_get_rate and then dev_pm_opp_set_rate() with the current rate to
>>>>> vote for the corresponding OPP level. Right?
>>>>>
>>>>> Isn't this exactly what you want? No?
>>>> I see now what you meant, it's actually a simpler variant and it works
>>>> too. Thank you for the suggestion, I'll prepare v8.
>>>>
>>> My bad, it doesn't work at all. I actually need to use the rpm_pstate or
>>> something else because performance state is coupled with the enable
>>> state of the device. If device is never rpm-suspended by consumer
>>> driver, then the initialized performance state is never dropped. Hence I
>>> want to initialize the state which is set only when device is resumed.
>>>
>>> I'll need to think more about it.
>>
>> GENPD core has these false assumptions:
>>
>> 1. It assumes that by default all devices are at zero performance level
>> at a boot time. This is not true for Tegra because hardware is
>> pre-initialized independently from GENPD.
> 
> Right, which is similar to other SoCs.
> 
>>
>> 2. It assumes that nothing depends on performance level and devices can
>> operate at any level at any time. Not true for Tegra and other platforms
>> where performance level is coupled with clocks state of attached
>> devices. OPP framework glues clock and performance level together for
>> us, which works good so far.
> 
> Right, OPPs need to be managed differently depending on the SoC.
> That's why genpd is there to help and to model this as "performance
> states" and to allow operations to be set through SoC specific
> callabacks, for example.
> 
> More importantly, the assumption is that in general, consumer drivers
> should use the OPP library to vote/set OPP levels, they shouldn't call
> dev_pm_genpd_set_performance_state() - unless they know exactly what
> they are doing.
> 
>>
>> Hence I either need to patch every driver to use dev_pm_opp_set_rate in
>> order to sync clk rate with the perf level at device runtime, or I need
>> to preset the rpm perf level to allow GENPD core to set the level before
>> device is resumed.
> 
> When the device is getting attached to its genpd (during the probe
> sequence and for a single PM domain case), runtime PM is disabled for
> the device. If you would call dev_pm_opp_set_rate() from a genpd
> callback during attach (without changing the rate), this means you
> would update/synchronize the vote. In this way, the vote is set before
> the device is runtime resumed by the driver, right?

I was doing that previously, but then realized that for some devices the
state needs to be synced only once device is rpm-resumed. Making RPM
driver to perform the syncing worked well.

> On the other hand, patching the driver should also be quite simple and
> you need to do that anyways, don't you?

It will be extra boilerplate code in the drivers, but perhaps it's the
best option for today, I'll consider it for v8. Thank you for helping
with the review.

^ permalink raw reply	[flat|nested] 58+ messages in thread

end of thread, other threads:[~2021-08-12 16:24 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-01 23:26 [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 01/37] soc/tegra: pmc: Temporarily disable PMC state syncing Dmitry Osipenko
2021-08-12 16:02   ` Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 02/37] soc/tegra: pmc: Implement attach_dev() of power domain drivers Dmitry Osipenko
2021-08-02 14:48   ` Ulf Hansson
2021-08-02 18:23     ` Dmitry Osipenko
2021-08-04  9:59       ` Ulf Hansson
2021-08-04 21:16         ` Dmitry Osipenko
2021-08-09 14:15           ` Ulf Hansson
2021-08-09 23:56             ` Dmitry Osipenko
2021-08-10 10:51               ` Ulf Hansson
2021-08-11 19:30                 ` Dmitry Osipenko
2021-08-11 22:41                   ` Dmitry Osipenko
2021-08-12  1:40                     ` Dmitry Osipenko
2021-08-12 11:17                       ` Ulf Hansson
2021-08-12 16:24                         ` Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 03/37] soc/tegra: Don't print error message when OPPs not available Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 04/37] soc/tegra: Add devm_tegra_core_dev_init_opp_table_simple() Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 05/37] dt-bindings: clock: tegra-car: Document new tegra-clocks node Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 06/37] clk: tegra: Support runtime PM and power domain Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 07/37] dt-bindings: host1x: Document OPP and power domain properties Dmitry Osipenko
2021-07-01 23:26 ` [PATCH v7 08/37] dt-bindings: host1x: Document Memory Client resets of Host1x, GR2D and GR3D Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 09/37] gpu: host1x: Add host1x_channel_stop() Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 10/37] gpu: host1x: Add runtime PM support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 11/37] gpu: host1x: Add stub driver for MPE, VI, EPP and ISP Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 12/37] drm/tegra: dc: Support OPP and SoC core voltage scaling Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 13/37] drm/tegra: hdmi: Add OPP support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 14/37] drm/tegra: gr2d: Support OPP and power management Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 15/37] drm/tegra: gr3d: " Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 16/37] drm/tegra: vic: Stop channel before suspending Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 17/37] usb: chipidea: tegra: Add runtime PM support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 18/37] bus: tegra-gmi: " Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 19/37] pwm: tegra: Add runtime PM and OPP support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 20/37] mmc: sdhci-tegra: " Dmitry Osipenko
2021-07-02  3:39   ` kernel test robot
2021-07-02  3:39     ` kernel test robot
2021-07-01 23:27 ` [PATCH v7 21/37] mtd: rawnand: tegra: Add runtime PM support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 22/37] soc/tegra: fuse: Clear fuse->clk on driver probe failure Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 23/37] soc/tegra: fuse: Add runtime PM support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 24/37] soc/tegra: fuse: Enable fuse clock on suspend Dmitry Osipenko
2021-07-21 16:00   ` Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 25/37] clk: tegra: Remove CLK_IS_CRITICAL flag from fuse clock Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 26/37] spi: tegra20-slink: Improve runtime PM usage Dmitry Osipenko
2021-07-02  8:29   ` kernel test robot
2021-07-02  8:29     ` kernel test robot
2021-07-01 23:27 ` [PATCH v7 27/37] spi: tegra20-slink: Add OPP support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 28/37] memory: tegra20-emc: Add minimal runtime PM support Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 29/37] memory: tegra30-emc: " Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 30/37] media: dt: bindings: tegra-vde: Convert to schema Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 31/37] media: dt: bindings: tegra-vde: Document OPP and power domain Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 32/37] media: staging: tegra-vde: Support generic " Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 33/37] ARM: tegra: Add OPP tables and power domains to Tegra20 device-trees Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 34/37] ARM: tegra: Add OPP tables and power domains to Tegra30 device-trees Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 35/37] ARM: tegra: Add Memory Client resets to Tegra20 GR2D, GR3D and Host1x Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 36/37] ARM: tegra: Add Memory Client resets to Tegra30 " Dmitry Osipenko
2021-07-01 23:27 ` [PATCH v7 37/37] soc/tegra: pmc: Enable core domain support on Tegra20 and Tegra30 Dmitry Osipenko
2021-07-09 13:01 ` [PATCH v7 00/37] NVIDIA Tegra power management patches for 5.15 Ulf Hansson
2021-07-09 21:23   ` Dmitry Osipenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.