[PATCH V5 00/14] Add generic PM domain support for Tegra

STALE3896d

Revision v5 of 2 in this series.

9 messages, 2 authors, 2016-02-01 · open the first message on its own page

[PATCH V5 00/14] Add generic PM domain support for Tegra

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:38

Adds generic PM domain support for Tegra SoCs but this series only
enables support for it on Tegra 64-bit devices. There is no reason why
this cannot be enable for Tegra 32-bit devices, but to keep the patch
series to a minimum only 64-bit devices are enabled so far.

This series has been boot tested on Tegra210 as well as various 32-bit
Tegra platforms.

Summary of changes since V4 [1]:
- Re-worked fix to handle base address on probe failure
- Added patch to lock around simultaneuous accesses to PMC registers
- Added patch to change powergate and rail IDs to unsigned type
- Added patch to fix testing of powergate state
- Updated patch to check for valid powergates to use a bitmap
- Updated Tegra DT PMC bindings per Rob H's feedback
- Updated Tegra power domains binding per Thierry's feedback
- Updated Tegra generic power domain support per Thierry's feedback

Summary of changes since V3 [0]:
- Dropped tegra124 support for now
- Removed MC flush support per feedback from Thierry
- Cleaned up the PMC changes per feedback from Thierry
- Added support for tegra210

Series Summary:
Patch 1-7:  PMC clean-up and fixes
Patch 8:    Adds function to remove a generic PM domain
Patch 9:    Updates DT documentation for tegra PMC
Patch 10:   Adds DT documentation for tegra generic PM domains
Patch 11:   Adds PMC generic DM domains support
Patch 12:   Adds audio clock for Tegra210 audio power-domain
Patch 13:   Adds DT bindings for Tegra210 audio power-domain
Patch 14:   Enable generic PM domains for tegra64

[0] http://comments.gmane.org/gmane.linux.ports.tegra/22944
[1] http://marc.info/?l=linux-tegra&m=144924153600529&w=2

Jon Hunter (14):
  soc: tegra: pmc: Restore base address on probe failure
  soc: tegra: pmc: Protect public functions from potential race
    conditions
  soc: tegra: pmc: Change powergate and rail IDs to be an unsigned type
  soc: tegra: pmc: Fix testing of powergate state
  soc: tegra: pmc: Wait for powergate state to change
  soc: tegra: pmc: Fix checking of valid partitions
  soc: tegra: pmc: Ensure partitions can be toggled on/off by PMC
  PM / Domains: Add function to remove a pm-domain
  Documentation: DT: bindings: Update NVIDIA PMC for Tegra
  Documentation: DT: bindings: Add power domain info for NVIDIA PMC
  soc: tegra: pmc: Add generic PM domain support
  clk: tegra210: Add the APB2APE audio clock
  ARM64: tegra: Add audio PM domain device node for Tegra210
  ARM64: tegra: select PM_GENERIC_DOMAINS

 .../bindings/arm/tegra/nvidia,tegra20-pmc.txt      |  67 ++-
 arch/arm/mach-tegra/platsmp.c                      |  16 +-
 arch/arm64/Kconfig.platforms                       |   2 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi           |  11 +-
 drivers/base/power/domain.c                        |  26 +
 drivers/clk/tegra/clk-id.h                         |   1 +
 drivers/clk/tegra/clk-tegra-periph.c               |   1 +
 drivers/clk/tegra/clk-tegra210.c                   |   1 +
 drivers/gpu/drm/tegra/drm.h                        |   2 +-
 drivers/soc/tegra/pmc.c                            | 603 ++++++++++++++++++---
 include/dt-bindings/clock/tegra210-car.h           |   2 +-
 include/dt-bindings/power/tegra-powergate.h        |  36 ++
 include/linux/pm_domain.h                          |   5 +
 include/soc/tegra/pmc.h                            |  73 +--
 14 files changed, 709 insertions(+), 137 deletions(-)
 create mode 100644 include/dt-bindings/power/tegra-powergate.h

-- 
2.1.4

[PATCH V5 02/14] soc: tegra: pmc: Protect public functions from potential race conditions

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:40

The PMC base address pointer is initialised during early boot so that
early platform code may used the PMC public functions. During the probe
of the PMC driver the base address pointer is mapped again and the initial
mapping is freed. This exposes a window where a device accessing the PMC
registers via one of the public functions, could race with the updating
of the pointer and lead to a invalid access. Furthermore, the only
protection between multiple devices attempting to access the PMC registers
is when setting the powergate state to on or off. None of the other public
functions that access the PMC registers are protected.

Use the existing mutex to protect paths that may race with regard to
accessing the PMC registers.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 44 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 85b4e166273a..f8cdb7ce9755 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -235,7 +235,10 @@ int tegra_powergate_is_powered(int id)
 	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
+	mutex_lock(&pmc->powergates_lock);
 	status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
+	mutex_unlock(&pmc->powergates_lock);
+
 	return !!status;
 }
 
@@ -250,6 +253,8 @@ int tegra_powergate_remove_clamping(int id)
 	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
+	mutex_lock(&pmc->powergates_lock);
+
 	/*
 	 * On Tegra124 and later, the clamps for the GPU are controlled by a
 	 * separate register (with different semantics).
@@ -257,7 +262,7 @@ int tegra_powergate_remove_clamping(int id)
 	if (id == TEGRA_POWERGATE_3D) {
 		if (pmc->soc->has_gpu_clamps) {
 			tegra_pmc_writel(0, GPU_RG_CNTRL);
-			return 0;
+			goto out;
 		}
 	}
 
@@ -274,6 +279,9 @@ int tegra_powergate_remove_clamping(int id)
 
 	tegra_pmc_writel(mask, REMOVE_CLAMPING);
 
+out:
+	mutex_unlock(&pmc->powergates_lock);
+
 	return 0;
 }
 EXPORT_SYMBOL(tegra_powergate_remove_clamping);
@@ -520,9 +528,11 @@ int tegra_io_rail_power_on(int id)
 	unsigned int bit, mask;
 	int err;
 
+	mutex_lock(&pmc->powergates_lock);
+
 	err = tegra_io_rail_prepare(id, &request, &status, &bit);
 	if (err < 0)
-		return err;
+		goto error;
 
 	mask = 1 << bit;
 
@@ -535,12 +545,15 @@ int tegra_io_rail_power_on(int id)
 	err = tegra_io_rail_poll(status, mask, 0, 250);
 	if (err < 0) {
 		pr_info("tegra_io_rail_poll() failed: %d\n", err);
-		return err;
+		goto error;
 	}
 
 	tegra_io_rail_unprepare();
 
-	return 0;
+error:
+	mutex_unlock(&pmc->powergates_lock);
+
+	return err < 0 ? err : 0;
 }
 EXPORT_SYMBOL(tegra_io_rail_power_on);
 
@@ -550,10 +563,12 @@ int tegra_io_rail_power_off(int id)
 	unsigned int bit, mask;
 	int err;
 
+	mutex_lock(&pmc->powergates_lock);
+
 	err = tegra_io_rail_prepare(id, &request, &status, &bit);
 	if (err < 0) {
 		pr_info("tegra_io_rail_prepare() failed: %d\n", err);
-		return err;
+		goto error;
 	}
 
 	mask = 1 << bit;
@@ -566,11 +581,14 @@ int tegra_io_rail_power_off(int id)
 
 	err = tegra_io_rail_poll(status, mask, mask, 250);
 	if (err < 0)
-		return err;
+		goto error;
 
 	tegra_io_rail_unprepare();
 
-	return 0;
+error:
+	mutex_unlock(&pmc->powergates_lock);
+
+	return err < 0 ? err : 0;
 }
 EXPORT_SYMBOL(tegra_io_rail_power_off);
 
@@ -817,9 +835,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)
 
 	/* take over the memory region from the early initialization */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	mutex_lock(&pmc->powergates_lock);
 	pmc->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(pmc->base))
-		return PTR_ERR(pmc->base);
+	mutex_unlock(&pmc->powergates_lock);
+
+	if (IS_ERR(pmc->base)) {
+		err = PTR_ERR(pmc->base);
+		goto error;
+	}
 
 	pmc->clk = devm_clk_get(&pdev->dev, "pclk");
 	if (IS_ERR(pmc->clk)) {
@@ -853,7 +877,9 @@ static int tegra_pmc_probe(struct platform_device *pdev)
 	return 0;
 
 error:
+	mutex_lock(&pmc->powergates_lock);
 	pmc->base = base;
+	mutex_unlock(&pmc->powergates_lock);
 
 	return err;
 }
-- 
2.1.4

[PATCH V5 03/14] soc: tegra: pmc: Change powergate and rail IDs to be an unsigned type

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:41

The tegra powergate and rail IDs are always positive values and so change
the type to be unsigned and remove the tests to see if the ID is less
than zero. Update the Tegra DC powergate type to be an unsigned as well.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.h |  2 +-
 drivers/soc/tegra/pmc.c     | 36 ++++++++++++++++++------------------
 include/soc/tegra/pmc.h     | 35 ++++++++++++++++++-----------------
 3 files changed, 37 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index c088f2f67eda..6431fe2397c1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -121,7 +121,7 @@ struct tegra_dc {
 	spinlock_t lock;
 
 	struct drm_crtc base;
-	int powergate;
+	unsigned int powergate;
 	int pipe;
 
 	struct clk *clk;
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index f8cdb7ce9755..1dbdcf06a9ad 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -179,7 +179,7 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)
  * @id: partition ID
  * @new_state: new state of the partition
  */
-static int tegra_powergate_set(int id, bool new_state)
+static int tegra_powergate_set(unsigned int id, bool new_state)
 {
 	bool status;
 
@@ -203,9 +203,9 @@ static int tegra_powergate_set(int id, bool new_state)
  * tegra_powergate_power_on() - power on partition
  * @id: partition ID
  */
-int tegra_powergate_power_on(int id)
+int tegra_powergate_power_on(unsigned int id)
 {
-	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
+	if (!pmc->soc || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
 	return tegra_powergate_set(id, true);
@@ -215,9 +215,9 @@ int tegra_powergate_power_on(int id)
  * tegra_powergate_power_off() - power off partition
  * @id: partition ID
  */
-int tegra_powergate_power_off(int id)
+int tegra_powergate_power_off(unsigned int id)
 {
-	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
+	if (!pmc->soc || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
 	return tegra_powergate_set(id, false);
@@ -228,11 +228,11 @@ EXPORT_SYMBOL(tegra_powergate_power_off);
  * tegra_powergate_is_powered() - check if partition is powered
  * @id: partition ID
  */
-int tegra_powergate_is_powered(int id)
+int tegra_powergate_is_powered(unsigned int id)
 {
 	u32 status;
 
-	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
+	if (!pmc->soc || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
 	mutex_lock(&pmc->powergates_lock);
@@ -246,11 +246,11 @@ int tegra_powergate_is_powered(int id)
  * tegra_powergate_remove_clamping() - remove power clamps for partition
  * @id: partition ID
  */
-int tegra_powergate_remove_clamping(int id)
+int tegra_powergate_remove_clamping(unsigned int id)
 {
 	u32 mask;
 
-	if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
+	if (!pmc->soc || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
 	mutex_lock(&pmc->powergates_lock);
@@ -294,7 +294,7 @@ EXPORT_SYMBOL(tegra_powergate_remove_clamping);
  *
  * Must be called with clk disabled, and returns with clk enabled.
  */
-int tegra_powergate_sequence_power_up(int id, struct clk *clk,
+int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
 				      struct reset_control *rst)
 {
 	int ret;
@@ -337,9 +337,9 @@ EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
  * Returns the partition ID corresponding to the CPU partition ID or a
  * negative error code on failure.
  */
-static int tegra_get_cpu_powergate_id(int cpuid)
+static int tegra_get_cpu_powergate_id(unsigned int cpuid)
 {
-	if (pmc->soc && cpuid > 0 && cpuid < pmc->soc->num_cpu_powergates)
+	if (pmc->soc && cpuid < pmc->soc->num_cpu_powergates)
 		return pmc->soc->cpu_powergates[cpuid];
 
 	return -EINVAL;
@@ -349,7 +349,7 @@ static int tegra_get_cpu_powergate_id(int cpuid)
  * tegra_pmc_cpu_is_powered() - check if CPU partition is powered
  * @cpuid: CPU partition ID
  */
-bool tegra_pmc_cpu_is_powered(int cpuid)
+bool tegra_pmc_cpu_is_powered(unsigned int cpuid)
 {
 	int id;
 
@@ -364,7 +364,7 @@ bool tegra_pmc_cpu_is_powered(int cpuid)
  * tegra_pmc_cpu_power_on() - power on CPU partition
  * @cpuid: CPU partition ID
  */
-int tegra_pmc_cpu_power_on(int cpuid)
+int tegra_pmc_cpu_power_on(unsigned int cpuid)
 {
 	int id;
 
@@ -379,7 +379,7 @@ int tegra_pmc_cpu_power_on(int cpuid)
  * tegra_pmc_cpu_remove_clamping() - remove power clamps for CPU partition
  * @cpuid: CPU partition ID
  */
-int tegra_pmc_cpu_remove_clamping(int cpuid)
+int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
 {
 	int id;
 
@@ -465,7 +465,7 @@ static int tegra_powergate_debugfs_init(void)
 	return 0;
 }
 
-static int tegra_io_rail_prepare(int id, unsigned long *request,
+static int tegra_io_rail_prepare(unsigned int id, unsigned long *request,
 				 unsigned long *status, unsigned int *bit)
 {
 	unsigned long rate, value;
@@ -522,7 +522,7 @@ static void tegra_io_rail_unprepare(void)
 	tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE);
 }
 
-int tegra_io_rail_power_on(int id)
+int tegra_io_rail_power_on(unsigned int id)
 {
 	unsigned long request, status, value;
 	unsigned int bit, mask;
@@ -557,7 +557,7 @@ error:
 }
 EXPORT_SYMBOL(tegra_io_rail_power_on);
 
-int tegra_io_rail_power_off(int id)
+int tegra_io_rail_power_off(unsigned int id)
 {
 	unsigned long request, status, value;
 	unsigned int bit, mask;
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h
index d18efe402ff1..07e332dd44fb 100644
--- a/include/soc/tegra/pmc.h
+++ b/include/soc/tegra/pmc.h
@@ -33,9 +33,9 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_SMP
-bool tegra_pmc_cpu_is_powered(int cpuid);
-int tegra_pmc_cpu_power_on(int cpuid);
-int tegra_pmc_cpu_remove_clamping(int cpuid);
+bool tegra_pmc_cpu_is_powered(unsigned int cpuid);
+int tegra_pmc_cpu_power_on(unsigned int cpuid);
+int tegra_pmc_cpu_remove_clamping(unsigned int cpuid);
 #endif /* CONFIG_SMP */
 
 /*
@@ -108,50 +108,51 @@ int tegra_pmc_cpu_remove_clamping(int cpuid);
 #define TEGRA_IO_RAIL_SYS_DDC	58
 
 #ifdef CONFIG_ARCH_TEGRA
-int tegra_powergate_is_powered(int id);
-int tegra_powergate_power_on(int id);
-int tegra_powergate_power_off(int id);
-int tegra_powergate_remove_clamping(int id);
+int tegra_powergate_is_powered(unsigned int id);
+int tegra_powergate_power_on(unsigned int id);
+int tegra_powergate_power_off(unsigned int id);
+int tegra_powergate_remove_clamping(unsigned int id);
 
 /* Must be called with clk disabled, and returns with clk enabled */
-int tegra_powergate_sequence_power_up(int id, struct clk *clk,
+int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
 				      struct reset_control *rst);
 
-int tegra_io_rail_power_on(int id);
-int tegra_io_rail_power_off(int id);
+int tegra_io_rail_power_on(unsigned int id);
+int tegra_io_rail_power_off(unsigned int id);
 #else
-static inline int tegra_powergate_is_powered(int id)
+static inline int tegra_powergate_is_powered(unsigned int id)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_powergate_power_on(int id)
+static inline int tegra_powergate_power_on(unsigned int id)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_powergate_power_off(int id)
+static inline int tegra_powergate_power_off(unsigned int id)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_powergate_remove_clamping(int id)
+static inline int tegra_powergate_remove_clamping(unsigned int id)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk,
+static inline int tegra_powergate_sequence_power_up(unsigned int id,
+						    struct clk *clk,
 						    struct reset_control *rst)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_io_rail_power_on(int id)
+static inline int tegra_io_rail_power_on(unsigned int id)
 {
 	return -ENOSYS;
 }
 
-static inline int tegra_io_rail_power_off(int id)
+static inline int tegra_io_rail_power_off(unsigned int id)
 {
 	return -ENOSYS;
 }
-- 
2.1.4

[PATCH V5 04/14] soc: tegra: pmc: Fix testing of powergate state

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:42

In tegra_powergate_set() the state of the powergates is read and OR'ed
with the bit for the powergate of interest. This unsigned 32-bit value
is then compared with a boolean value to test if the powergate is
already in the desired state. When turning on a powergate, apart from
the powergate that is represented by bit 0, this test will always
return false and so we may attempt to turn on the powergate when it is
already on.

After OR'ing the bit for the powergate, check if the result is not equal
to zero before comparing with the boolean value. Add a helper function
to return the current state of a powergate.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 1dbdcf06a9ad..99cb2fdd29e1 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -174,6 +174,11 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)
 	writel(value, pmc->base + offset);
 }
 
+static inline bool tegra_powergate_state(int id)
+{
+	return (tegra_pmc_readl(PWRGATE_STATUS) & BIT(id)) != 0;
+}
+
 /**
  * tegra_powergate_set() - set the state of a partition
  * @id: partition ID
@@ -181,13 +186,9 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)
  */
 static int tegra_powergate_set(unsigned int id, bool new_state)
 {
-	bool status;
-
 	mutex_lock(&pmc->powergates_lock);
 
-	status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
-
-	if (status == new_state) {
+	if (tegra_powergate_state(id) == new_state) {
 		mutex_unlock(&pmc->powergates_lock);
 		return 0;
 	}
@@ -230,16 +231,16 @@ EXPORT_SYMBOL(tegra_powergate_power_off);
  */
 int tegra_powergate_is_powered(unsigned int id)
 {
-	u32 status;
+	int status;
 
 	if (!pmc->soc || id >= pmc->soc->num_powergates)
 		return -EINVAL;
 
 	mutex_lock(&pmc->powergates_lock);
-	status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
+	status = tegra_powergate_state(id);
 	mutex_unlock(&pmc->powergates_lock);
 
-	return !!status;
+	return status;
 }
 
 /**
-- 
2.1.4

[PATCH V5 07/14] soc: tegra: pmc: Ensure partitions can be toggled on/off by PMC

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:45

For Tegra124 and Tegra210, the GPU partition cannot be toggled on and off
via the APBDEV_PMC_PWRGATE_TOGGLE_0 register. For these devices, the
partition is simply powered up and down via an external regulator.
Describe in the PMC SoC data in which devices the GPU partition can be
controlled via the APBDEV_PMC_PWRGATE_TOGGLE_0 register and ensure that
no one can incorrectly try to toggle the GPU partition via the
APBDEV_PMC_PWRGATE_TOGGLE_0 register.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 032dd5c17130..ecb4f66819fd 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -110,6 +110,7 @@ struct tegra_pmc_soc {
 
 	bool has_tsense_reset;
 	bool has_gpu_clamps;
+	bool has_gpu_toggle;
 };
 
 /**
@@ -197,6 +198,9 @@ static int tegra_powergate_set(unsigned int id, bool new_state)
 	bool status;
 	int err;
 
+	if (id == TEGRA_POWERGATE_3D && !pmc->soc->has_gpu_toggle)
+		return -EINVAL;
+
 	mutex_lock(&pmc->powergates_lock);
 
 	if (tegra_powergate_state(id) == new_state) {
@@ -250,6 +254,9 @@ int tegra_powergate_is_powered(unsigned int id)
 	if (!tegra_powergate_is_valid(id))
 		return -EINVAL;
 
+	if (id == TEGRA_POWERGATE_3D && !pmc->soc->has_gpu_toggle)
+		return -EINVAL;
+
 	mutex_lock(&pmc->powergates_lock);
 	status = tegra_powergate_state(id);
 	mutex_unlock(&pmc->powergates_lock);
@@ -968,6 +975,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
 	.cpu_powergates = tegra30_cpu_powergates,
 	.has_tsense_reset = true,
 	.has_gpu_clamps = false,
+	.has_gpu_toggle = true,
 };
 
 static const char * const tegra114_powergates[] = {
@@ -1005,6 +1013,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
 	.cpu_powergates = tegra114_cpu_powergates,
 	.has_tsense_reset = true,
 	.has_gpu_clamps = false,
+	.has_gpu_toggle = true,
 };
 
 static const char * const tegra124_powergates[] = {
@@ -1048,6 +1057,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
 	.cpu_powergates = tegra124_cpu_powergates,
 	.has_tsense_reset = true,
 	.has_gpu_clamps = true,
+	.has_gpu_toggle = false,
 };
 
 static const char * const tegra210_powergates[] = {
@@ -1091,6 +1101,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
 	.cpu_powergates = tegra210_cpu_powergates,
 	.has_tsense_reset = true,
 	.has_gpu_clamps = true,
+	.has_gpu_toggle = false,
 };
 
 static const struct of_device_id tegra_pmc_match[] = {
-- 
2.1.4

[PATCH V5 08/14] PM / Domains: Add function to remove a pm-domain

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:46

The genpd framework allows users to add power-domains via the
pm_genpd_init() function, however, there is no corresponding function
to remove a power-domain. For most devices this may be fine as the power
domains are never removed, however, for devices that wish to populate
the power-domains from within a driver, having the ability to remove a
power domain if the probing of the device fails or the driver is unloaded
is necessary. Therefore, add a function to remove a power-domain. Please
note that the power domain can only be removed if there are no devices
using the power-domain and it is not linked to another domain.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/base/power/domain.c | 26 ++++++++++++++++++++++++++
 include/linux/pm_domain.h   |  5 +++++
 2 files changed, 31 insertions(+)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 45e3641b427d..b4120121bcac 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1529,6 +1529,32 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
 }
 EXPORT_SYMBOL_GPL(pm_genpd_init);
 
+/**
+ * pm_genpd_remove - Remove a generic I/O PM domain object.
+ * @genpd: PM domain object to remove.
+ */
+int pm_genpd_remove(struct generic_pm_domain *genpd)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (!list_empty(&genpd->master_links)
+	    || !list_empty(&genpd->slave_links) || genpd->device_count) {
+		mutex_unlock(&genpd->lock);
+		return -EBUSY;
+	}
+
+	mutex_lock_nested(&gpd_list_lock, SINGLE_DEPTH_NESTING);
+	list_del(&genpd->gpd_list_node);
+	mutex_unlock(&gpd_list_lock);
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_remove);
+
 #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
 /*
  * Device Tree based PM domain providers.
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index db21d3995f7e..0d661998fa74 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -123,6 +123,7 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 				     struct generic_pm_domain *target);
 extern void pm_genpd_init(struct generic_pm_domain *genpd,
 			  struct dev_power_governor *gov, bool is_off);
+extern int pm_genpd_remove(struct generic_pm_domain *genpd);
 
 extern struct dev_power_governor simple_qos_governor;
 extern struct dev_power_governor pm_domain_always_on_gov;
@@ -161,6 +162,10 @@ static inline void pm_genpd_init(struct generic_pm_domain *genpd,
 				 struct dev_power_governor *gov, bool is_off)
 {
 }
+static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
+{
+	return -ENOTSUPP;
+}
 #endif
 
 static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
-- 
2.1.4

[PATCH V5 14/14] ARM64: tegra: select PM_GENERIC_DOMAINS

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-01-28 16:33:52

Enable PM_GENERIC_DOMAINS for tegra 64-bit devices. To ensure that devices
dependent upon a particular power-domain are only probed when that power
domain has been powered up, requires that PM is made mandatory for tegra
64-bit devices and so select this option for tegra as well.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 arch/arm64/Kconfig.platforms | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 8a0952275291..cdec2dfae3aa 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -102,6 +102,8 @@ config ARCH_TEGRA
 	select GENERIC_CLOCKEVENTS
 	select HAVE_CLK
 	select PINCTRL
+	select PM
+	select PM_GENERIC_DOMAINS
 	select RESET_CONTROLLER
 	help
 	  This enables support for the NVIDIA Tegra SoC family.
-- 
2.1.4

Re: [PATCH V5 02/14] soc: tegra: pmc: Protect public functions from potential race conditions

From: Mathieu Poirier <mathieu.poirier@linaro.org>
Date: 2016-01-29 16:20:47

On 28 January 2016 at 09:33, Jon Hunter [off-list ref] wrote:
quoted hunk
The PMC base address pointer is initialised during early boot so that
early platform code may used the PMC public functions. During the probe
of the PMC driver the base address pointer is mapped again and the initial
mapping is freed. This exposes a window where a device accessing the PMC
registers via one of the public functions, could race with the updating
of the pointer and lead to a invalid access. Furthermore, the only
protection between multiple devices attempting to access the PMC registers
is when setting the powergate state to on or off. None of the other public
functions that access the PMC registers are protected.

Use the existing mutex to protect paths that may race with regard to
accessing the PMC registers.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 44 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 85b4e166273a..f8cdb7ce9755 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -235,7 +235,10 @@ int tegra_powergate_is_powered(int id)
        if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
                return -EINVAL;

+       mutex_lock(&pmc->powergates_lock);
        status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
+       mutex_unlock(&pmc->powergates_lock);
+
        return !!status;
 }
@@ -250,6 +253,8 @@ int tegra_powergate_remove_clamping(int id)
        if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
                return -EINVAL;

+       mutex_lock(&pmc->powergates_lock);
+
        /*
         * On Tegra124 and later, the clamps for the GPU are controlled by a
         * separate register (with different semantics).
@@ -257,7 +262,7 @@ int tegra_powergate_remove_clamping(int id)
        if (id == TEGRA_POWERGATE_3D) {
                if (pmc->soc->has_gpu_clamps) {
                        tegra_pmc_writel(0, GPU_RG_CNTRL);
-                       return 0;
+                       goto out;
                }
        }
@@ -274,6 +279,9 @@ int tegra_powergate_remove_clamping(int id)

        tegra_pmc_writel(mask, REMOVE_CLAMPING);

+out:
+       mutex_unlock(&pmc->powergates_lock);
+
        return 0;
 }
 EXPORT_SYMBOL(tegra_powergate_remove_clamping);
@@ -520,9 +528,11 @@ int tegra_io_rail_power_on(int id)
        unsigned int bit, mask;
        int err;

+       mutex_lock(&pmc->powergates_lock);
+
        err = tegra_io_rail_prepare(id, &request, &status, &bit);
        if (err < 0)
-               return err;
+               goto error;

        mask = 1 << bit;
@@ -535,12 +545,15 @@ int tegra_io_rail_power_on(int id)
        err = tegra_io_rail_poll(status, mask, 0, 250);
        if (err < 0) {
                pr_info("tegra_io_rail_poll() failed: %d\n", err);
-               return err;
+               goto error;
        }

        tegra_io_rail_unprepare();

-       return 0;
+error:
+       mutex_unlock(&pmc->powergates_lock);
+
+       return err < 0 ? err : 0;
Is this necessary?  Why simply not returning 'err'?  From what I see
'tegra_io_rail_power_on()' can only return a negative value or '0'.
quoted hunk
 }
 EXPORT_SYMBOL(tegra_io_rail_power_on);
@@ -550,10 +563,12 @@ int tegra_io_rail_power_off(int id)
        unsigned int bit, mask;
        int err;

+       mutex_lock(&pmc->powergates_lock);
+
        err = tegra_io_rail_prepare(id, &request, &status, &bit);
        if (err < 0) {
                pr_info("tegra_io_rail_prepare() failed: %d\n", err);
-               return err;
+               goto error;
        }

        mask = 1 << bit;
@@ -566,11 +581,14 @@ int tegra_io_rail_power_off(int id)

        err = tegra_io_rail_poll(status, mask, mask, 250);
        if (err < 0)
-               return err;
+               goto error;

        tegra_io_rail_unprepare();

-       return 0;
+error:
+       mutex_unlock(&pmc->powergates_lock);
+
+       return err < 0 ? err : 0;
Same comment as above.
quoted hunk
 }
 EXPORT_SYMBOL(tegra_io_rail_power_off);
@@ -817,9 +835,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)

        /* take over the memory region from the early initialization */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       mutex_lock(&pmc->powergates_lock);
        pmc->base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(pmc->base))
-               return PTR_ERR(pmc->base);
+       mutex_unlock(&pmc->powergates_lock);
Since the mutex is released there is a window of opportunity for
devices to access an erroneous pointer.  A better approach might be to
use a temporary variable, do all the initialisation that is required
and when things look good set pmc-base to that temporary variable.

Mathieu
quoted hunk
+
+       if (IS_ERR(pmc->base)) {
+               err = PTR_ERR(pmc->base);
+               goto error;
+       }

        pmc->clk = devm_clk_get(&pdev->dev, "pclk");
        if (IS_ERR(pmc->clk)) {
@@ -853,7 +877,9 @@ static int tegra_pmc_probe(struct platform_device *pdev)
        return 0;

 error:
+       mutex_lock(&pmc->powergates_lock);
        pmc->base = base;
+       mutex_unlock(&pmc->powergates_lock);

        return err;
 }
--
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH V5 02/14] soc: tegra: pmc: Protect public functions from potential race conditions

From: Jon Hunter <jonathanh@nvidia.com>
Date: 2016-02-01 13:42:55

On 29/01/16 16:20, Mathieu Poirier wrote:
On 28 January 2016 at 09:33, Jon Hunter [off-list ref] wrote:
quoted
The PMC base address pointer is initialised during early boot so that
early platform code may used the PMC public functions. During the probe
of the PMC driver the base address pointer is mapped again and the initial
mapping is freed. This exposes a window where a device accessing the PMC
registers via one of the public functions, could race with the updating
of the pointer and lead to a invalid access. Furthermore, the only
protection between multiple devices attempting to access the PMC registers
is when setting the powergate state to on or off. None of the other public
functions that access the PMC registers are protected.

Use the existing mutex to protect paths that may race with regard to
accessing the PMC registers.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 44 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 85b4e166273a..f8cdb7ce9755 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -235,7 +235,10 @@ int tegra_powergate_is_powered(int id)
        if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
                return -EINVAL;

+       mutex_lock(&pmc->powergates_lock);
        status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id);
+       mutex_unlock(&pmc->powergates_lock);
+
        return !!status;
 }
@@ -250,6 +253,8 @@ int tegra_powergate_remove_clamping(int id)
        if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates)
                return -EINVAL;

+       mutex_lock(&pmc->powergates_lock);
+
        /*
         * On Tegra124 and later, the clamps for the GPU are controlled by a
         * separate register (with different semantics).
@@ -257,7 +262,7 @@ int tegra_powergate_remove_clamping(int id)
        if (id == TEGRA_POWERGATE_3D) {
                if (pmc->soc->has_gpu_clamps) {
                        tegra_pmc_writel(0, GPU_RG_CNTRL);
-                       return 0;
+                       goto out;
                }
        }
@@ -274,6 +279,9 @@ int tegra_powergate_remove_clamping(int id)

        tegra_pmc_writel(mask, REMOVE_CLAMPING);

+out:
+       mutex_unlock(&pmc->powergates_lock);
+
        return 0;
 }
 EXPORT_SYMBOL(tegra_powergate_remove_clamping);
@@ -520,9 +528,11 @@ int tegra_io_rail_power_on(int id)
        unsigned int bit, mask;
        int err;

+       mutex_lock(&pmc->powergates_lock);
+
        err = tegra_io_rail_prepare(id, &request, &status, &bit);
        if (err < 0)
-               return err;
+               goto error;

        mask = 1 << bit;
@@ -535,12 +545,15 @@ int tegra_io_rail_power_on(int id)
        err = tegra_io_rail_poll(status, mask, 0, 250);
        if (err < 0) {
                pr_info("tegra_io_rail_poll() failed: %d\n", err);
-               return err;
+               goto error;
        }

        tegra_io_rail_unprepare();

-       return 0;
+error:
+       mutex_unlock(&pmc->powergates_lock);
+
+       return err < 0 ? err : 0;
Is this necessary?  Why simply not returning 'err'?  From what I see
'tegra_io_rail_power_on()' can only return a negative value or '0'.
Right, this is probably not necessary and so I could simplify this.
quoted
 }
 EXPORT_SYMBOL(tegra_io_rail_power_on);
@@ -550,10 +563,12 @@ int tegra_io_rail_power_off(int id)
        unsigned int bit, mask;
        int err;

+       mutex_lock(&pmc->powergates_lock);
+
        err = tegra_io_rail_prepare(id, &request, &status, &bit);
        if (err < 0) {
                pr_info("tegra_io_rail_prepare() failed: %d\n", err);
-               return err;
+               goto error;
        }

        mask = 1 << bit;
@@ -566,11 +581,14 @@ int tegra_io_rail_power_off(int id)

        err = tegra_io_rail_poll(status, mask, mask, 250);
        if (err < 0)
-               return err;
+               goto error;

        tegra_io_rail_unprepare();

-       return 0;
+error:
+       mutex_unlock(&pmc->powergates_lock);
+
+       return err < 0 ? err : 0;
Same comment as above.
quoted
 }
 EXPORT_SYMBOL(tegra_io_rail_power_off);
@@ -817,9 +835,15 @@ static int tegra_pmc_probe(struct platform_device *pdev)

        /* take over the memory region from the early initialization */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       mutex_lock(&pmc->powergates_lock);
        pmc->base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(pmc->base))
-               return PTR_ERR(pmc->base);
+       mutex_unlock(&pmc->powergates_lock);
Since the mutex is released there is a window of opportunity for
devices to access an erroneous pointer.  A better approach might be to
use a temporary variable, do all the initialisation that is required
and when things look good set pmc-base to that temporary variable.
Thanks. Not sure what I was thinking here. I will fix that.

Cheers
Jon
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help