Add generic PM domain support for Tegra SoCs
Subsystems (12)
Jon Hunter
maintainerv3 cover Add generic PM domain support for Tegra SoCs
This series is based upon the V2 series from Vince Hsu [1]. The code for the MC flush operations and PMC code for the generic PM domains has been re-written to simplify and consolidate legacy/existing support for power-domains on the Tegra SoC.
This series has been boot tested on various 32-bit Tegra platforms as well as compile tested for 64-bit ARM. A summary of the results is available here [2]. Would be great to get some more testing for PCI, SATA, GR3D and DRM.
Summary:
Patch 1: Adds the new function of_reset_control_get_by_index(). Patch 2-5: Add the MC flush operations for Tegra30/Tegra114/Tegra124. Patch 6: Is a PLLD fix for Tegra114. Patch 7-9: PMC preparation for generic PM domains. Patch 10-13: Update drivers to support generic PM domains Patch 14: Add DT documentation for tegra generic PM domains Patch 15: Add PMC generic DM domains support Patch 16: Remove legacy functions Patch 17-18: Add DT bindings for tegra124 Patch 19: Enable generic PM domains for tegra
Changes:
v3:
* Re-wrote and simplified MC flush code
* Re-wrote PMC code for generic PM domains to permit devices to use legacy DT blobs without generic PM domain info.
* Re-worked DRM, PCI, AHCI and GR3D drivers to allow then to work with and without generic PM domains.
* Added DT documentation for tegra generic PM domain bindings.
* Dropped DT bindings for T30 and T114 (plan to add later)
v2 (Vince Hsu):
* Add support for Tegra30/Tegra114
* Address Alex's comments
[1] https://lkml.org/lkml/2015/3/12/284
[2] https://nvtb.github.io//epmt/jonathanh-tester/test_20150713051933_58fc293c9467f52c51f1e52e5d5a8da174b8550a/20150713051933/
Jon Hunter (12):
memory: tegra: Add MC flush support
soc: tegra: pmc: Wait for powergate state to change
soc: tegra: pmc: Clean-up PMC helper functions
soc: tegra: pmc: Prepare for migrating to generic PM domains drm/tegra: dc: Prepare for generic PM domains
PCI: tegra: Add support for generic PM domains
ata: ahci_tegra: Add support for generic PM domains drm/tegra: gr3d: Add support for generic PM domains
Documentation: DT: bindings: Add power domain info for NVIDIA PMC
soc: tegra: pmc: Add generic PM domain support
soc: tegra: pmc: Remove the deprecated powergate APIs
ARM: tegra: Add PM domain device nodes to Tegra124 DT
Vince Hsu (7):
reset: add of_reset_control_get_by_index()
memory: tegra: add flush operation for Tegra30 memory clients
memory: tegra: add flush operation for Tegra114 memory clients
memory: tegra: add flush operation for Tegra124 memory clients
clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
ARM: tegra: add GPU power supply to Jetson TK1 DT
ARM: tegra: select PM_GENERIC_DOMAINS
.../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 99 ++++ arch/arm/boot/dts/tegra124-jetson-tk1.dts | 6 +- arch/arm/boot/dts/tegra124.dtsi | 80 +++ arch/arm/mach-tegra/Kconfig | 1 + arch/arm/mach-tegra/platsmp.c | 18 +- drivers/ata/ahci_tegra.c | 51 +- drivers/clk/tegra/clk-tegra114.c | 6 +- drivers/gpu/drm/tegra/dc.c | 94 +-- drivers/gpu/drm/tegra/gr3d.c | 57 +- drivers/memory/tegra/mc.c | 110 ++++ drivers/memory/tegra/mc.h | 2 + drivers/memory/tegra/tegra114.c | 21 + drivers/memory/tegra/tegra124.c | 31 + drivers/memory/tegra/tegra30.c | 24 + drivers/pci/host/pci-tegra.c | 49 +- drivers/reset/core.c | 40 +- drivers/soc/tegra/pmc.c | 647 ++++++++++++++++++--- include/dt-bindings/power/tegra-powergate.h | 42 ++ include/linux/reset.h | 9 + include/soc/tegra/mc.h | 34 ++ include/soc/tegra/pmc.h | 74 +-- 21 files changed, 1264 insertions(+), 231 deletions(-) create mode 100644 include/dt-bindings/power/tegra-powergate.h
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 1/19 reset: add of_reset_control_get_by_index()
From: Vince Hsu <[email protected]>
Add of_reset_control_get_by_index() to allow the drivers to get reset device without knowing its name.
[jonathanh at nvidia.com: Updated stub function to return -ENOTSUPP instead
of -ENOSYS which should only be used for system calls.]
---
v3: addressed comments from Philipp
v2: minor changes according to Alex's comments
--- drivers/reset/core.c | 40 +++++++++++++++++++++++++++++----------- include/linux/reset.h | 9 +++++++++ 2 files changed, 38 insertions(+), 11 deletions(-)
drivers/reset/core.c +29 -11
@@ -141,27 +141,24 @@ int reset_control_status(struct reset_control *rstc)EXPORT_SYMBOL_GPL (reset_control_status);/** -* of_reset_control_get - Lookup and obtain a reference to a reset controller. +* of_reset_control_get_by_index - Lookup and obtain a reference to a reset +* controller by index. * @node: device to be reset by the controller -* @id: reset line name -* -* Returns a struct reset_control or IS_ERR() condition containing errno. +* @index: index of the reset controller * -* Use of id names is optional. +* This is to be used to perform a list of resets for a device or power domain +* in whatever order. Returns a struct reset_control or IS_ERR() condition +* containing errno. */ -struct reset_control *of_reset_control_get (struct device_node *node, -const char *id) +struct reset_control *of_reset_control_get_by_index (struct device_node *node, +int index) {struct reset_control *rstc =ERR_PTR (-EPROBE_DEFER );struct reset_controller_dev *r, *rcdev;struct of_phandle_args args; -int index =0 ;int rstc_id;int ret; -if (id) - index =of_property_match_string (node, -"reset-names" , id); ret =of_parse_phandle_with_args (node,"resets" ,"#reset-cells" , index, &args);if (ret)
@@ -202,6 +199,27 @@ struct reset_control *of_reset_control_get(struct device_node *node,return rstc; } +EXPORT_SYMBOL_GPL (of_reset_control_get_by_index); + +/** +* of_reset_control_get - Lookup and obtain a reference to a reset controller. +* @node: device to be reset by the controller +* @id: reset line name +* +* Returns a struct reset_control or IS_ERR() condition containing errno. +* +* Use of id names is optional. +*/ +struct reset_control *of_reset_control_get (struct device_node *node, +const char *id) +{ +int index =0 ; + +if (id) + index =of_property_match_string (node, +"reset-names" , id); +return of_reset_control_get_by_index (node, index); +}EXPORT_SYMBOL_GPL (of_reset_control_get);/**
include/linux/reset.h +9 -0
@@ -38,6 +38,9 @@ static inline struct reset_control *devm_reset_control_get_optional(struct reset_control *of_reset_control_get (struct device_node *node,const char *id); +struct reset_control *of_reset_control_get_by_index ( +struct device_node *node,int index); +#else static inline int reset_control_reset (struct reset_control *rstc)
@@ -92,6 +95,12 @@ static inline struct reset_control *of_reset_control_get(return ERR_PTR (-ENOSYS ); } +static inline struct reset_control *of_reset_control_get_by_index ( +struct device_node *node,int index) +{ +return ERR_PTR (-ENOTSUPP ); +} +#endif /* CONFIG_RESET_CONTROLLER */ #endif
Signature · 1 line
2.1.4
Philipp Zabel
maintainerPATCH v3 1/19 reset: add of_reset_control_get_by_index()
Am Montag, den 13.07.2015, 13:39 +0100 schrieb Jon Hunter:
Quoted 9 lines
From: Vince Hsu <[email protected]>
Add
of_reset_control_get_by_index()to allow the drivers to get reset device without knowing its name.Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Updated stub function to return
-ENOTSUPPinstead
of-ENOSYSwhich should only be used for system calls.]Signed-off-by Jon Hunter <[email protected]>
regards
Philipp
Quoted 103 lines
---
v3: addressed comments from Philipp
v2: minor changes according to Alex's comments--- drivers/reset/core.c | 40 +++++++++++++++++++++++++++++----------- include/linux/reset.h | 9 +++++++++ 2 files changed, 38 insertions(+), 11 deletions(-)drivers/reset/core.c +29
-11@@ -141,27 +141,24 @@ int reset_control_status(struct reset_control *rstc)EXPORT_SYMBOL_GPL (reset_control_status);/** -* of_reset_control_get - Lookup and obtain a reference to a reset controller. +* of_reset_control_get_by_index - Lookup and obtain a reference to a reset +* controller by index. * @node: device to be reset by the controller -* @id: reset line name -* -* Returns a struct reset_control or IS_ERR() condition containing errno. +* @index: index of the reset controller * -* Use of id names is optional. +* This is to be used to perform a list of resets for a device or power domain +* in whatever order. Returns a struct reset_control or IS_ERR() condition +* containing errno. */ -struct reset_control *of_reset_control_get (struct device_node *node, -const char *id) +struct reset_control *of_reset_control_get_by_index (struct device_node *node, +int index) {struct reset_control *rstc =ERR_PTR (-EPROBE_DEFER );struct reset_controller_dev *r, *rcdev;struct of_phandle_args args; -int index =0 ;int rstc_id;int ret; -if (id) - index =of_property_match_string (node, -"reset-names" , id); ret =of_parse_phandle_with_args (node,"resets" ,"#reset-cells" , index, &args);if (ret)@@ -202,6 +199,27 @@ struct reset_control *of_reset_control_get(struct device_node *node,return rstc; } +EXPORT_SYMBOL_GPL (of_reset_control_get_by_index); + +/** +* of_reset_control_get - Lookup and obtain a reference to a reset controller. +* @node: device to be reset by the controller +* @id: reset line name +* +* Returns a struct reset_control or IS_ERR() condition containing errno. +* +* Use of id names is optional. +*/ +struct reset_control *of_reset_control_get (struct device_node *node, +const char *id) +{ +int index =0 ; + +if (id) + index =of_property_match_string (node, +"reset-names" , id); +return of_reset_control_get_by_index (node, index); +}EXPORT_SYMBOL_GPL (of_reset_control_get);/** include/linux/reset.h +9
-0@@ -38,6 +38,9 @@ static inline struct reset_control *devm_reset_control_get_optional(struct reset_control *of_reset_control_get (struct device_node *node,const char *id); +struct reset_control *of_reset_control_get_by_index ( +struct device_node *node,int index); +#else static inline int reset_control_reset (struct reset_control *rstc)@@ -92,6 +95,12 @@ static inline struct reset_control *of_reset_control_get(return ERR_PTR (-ENOSYS ); } +static inline struct reset_control *of_reset_control_get_by_index ( +struct device_node *node,int index) +{ +return ERR_PTR (-ENOTSUPP ); +} +#endif /* CONFIG_RESET_CONTROLLER */ #endif
Jon Hunter
maintainerPATCH v3 2/19 memory: tegra: Add MC flush support
In reply to Jon HunterThe Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
This is based upon a change by Vince Hsu <[email protected]>.
--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)
drivers/memory/tegra/mc.c +110 -0
@@ -7,6 +7,7 @@*/ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h>
@@ -71,6 +72,107 @@ static const struct of_device_id tegra_mc_of_match[] = { };MODULE_DEVICE_TABLE (of, tegra_mc_of_match); +const struct tegra_mc_flush *tegra_mc_flush_get (struct tegra_mc *mc, +unsigned int swgroup) +{ +const struct tegra_mc_flush *flush =NULL ; +int i; + +mutex_lock (&mc->lock); + +for (i =0 ; i < mc->soc->num_flushes; i++) { +if (mc->soc->flushes[i].swgroup == swgroup) { +if (mc->flush_reserved[i] == false) { + mc->flush_reserved[i] = true; + flush = &mc->soc->flushes[i]; + } +break ; + } + } + +mutex_unlock (&mc->lock); + +return flush; +} +EXPORT_SYMBOL (tegra_mc_flush_get); + +static bool tegra_mc_flush_done (struct tegra_mc *mc, +const struct tegra_mc_flush *flush) +{ +unsigned long timeout = jiffies +msecs_to_jiffies (100 ); +int i; +u32 val; + +while (time_before (jiffies, timeout)) { + val =mc_readl (mc, flush->status); + +/* +* If the flush bit is still set it +* is not done and so wait then retry. +*/ +if (val &BIT (flush->bit)) + goto retry; + +/* +* Depending on the tegra SoC, it may be necessary to read +* the status register multiple times to ensure the value +* read is correct. Some tegra devices have a HW issue where +* reading the status register shortly after writing the +* control register (on the order of 5 cycles) may return +* an incorrect value. +*/ +for (i =0 ; i < mc->soc->metastable_flush_reads; i++) { +if (mc_readl (mc, flush->status) != val) + goto retry; + } + +/* +* The flush is complete and so return. +*/ +return 0 ; +retry: +udelay (10 ); + } + +return -ETIMEDOUT ; +} + +int tegra_mc_flush (struct tegra_mc *mc,const struct tegra_mc_flush *flush, +bool enable) +{ +int ret =0 ; +u32 val; + +if (!mc || !flush) +return -EINVAL ; + +mutex_lock (&mc->lock); + + val =mc_readl (mc, flush->ctrl); + +if (enable) + val |=BIT (flush->bit); +else + val &= ~BIT (flush->bit); + +mc_writel (mc, val, flush->ctrl); +mc_readl (mc, flush->ctrl); + +/* +* If activating the flush, poll the +* status register until the flush is done. +*/ +if (enable) + ret =tegra_mc_flush_done (mc, flush); + +mutex_unlock (&mc->lock); + +dev_dbg (mc->dev,"%s bit %d\n" , __func__, flush->bit); + +return ret; +} +EXPORT_SYMBOL (tegra_mc_flush); +static int tegra_mc_setup_latency_allowance (struct tegra_mc *mc) {unsigned long long tick;
@@ -359,6 +461,12 @@ static int tegra_mc_probe(struct platform_device *pdev) mc->soc = match->data; mc->dev = &pdev->dev; + mc->flush_reserved =devm_kcalloc (&pdev->dev, mc->soc->num_flushes, +sizeof (mc->flush_reserved), +GFP_KERNEL ); +if (!mc->flush_reserved) +return -ENOMEM ; +/* length of MC tick in nanoseconds */ mc->tick =30 ;
@@ -410,6 +518,8 @@ static int tegra_mc_probe(struct platform_device *pdev)return err; } +mutex_init (&mc->lock); + value =MC_INT_DECERR_MTS |MC_INT_SECERR_SEC |MC_INT_DECERR_VPR |MC_INT_INVALID_APB_ASID_UPDATE |MC_INT_INVALID_SMMU_PAGE |MC_INT_SECURITY_VIOLATION |MC_INT_DECERR_EMEM ;
drivers/memory/tegra/mc.h +2 -0
@@ -14,6 +14,8 @@#include <soc/tegra/mc.h> +#define MC_FLUSH_METASTABLE_READS 5 +static inline u32 mc_readl (struct tegra_mc *mc,unsigned long offset) {return readl (mc->regs + offset);
include/soc/tegra/mc.h +34 -0
@@ -45,6 +45,13 @@ struct tegra_mc_client {struct tegra_mc_la la; }; +struct tegra_mc_flush { +unsigned int swgroup; +unsigned int ctrl; +unsigned int status; +unsigned int bit; +}; +struct tegra_smmu_swgroup {const char *name;unsigned int swgroup;
@@ -96,6 +103,10 @@ struct tegra_mc_soc {const struct tegra_mc_client *clients;unsigned int num_clients; +const struct tegra_mc_flush *flushes; +unsigned int num_flushes; +unsigned int metastable_flush_reads; +const unsigned long *emem_regs;unsigned int num_emem_regs;
@@ -117,9 +128,32 @@ struct tegra_mc {struct tegra_mc_timing *timings;unsigned int num_timings; + +bool *flush_reserved; + + struct mutex lock; };void tegra_mc_write_emem_configuration (struct tegra_mc *mc,unsigned long rate);unsigned int tegra_mc_get_emem_device_count (struct tegra_mc *mc); +#ifdef CONFIG_TEGRA_MC +const struct tegra_mc_flush *tegra_mc_flush_get (struct tegra_mc *mc, +unsigned int swgroup); +int tegra_mc_flush (struct tegra_mc *mc,const struct tegra_mc_flush *s, +bool enable); +#else +const struct tegra_mc_flush *tegra_mc_flush_get (struct tegra_mc *mc, +unsigned int swgroup) +{ +return NULL ; +} + +int tegra_mc_flush (struct tegra_mc *mc,const struct tegra_mc_flush *s, +bool enable) +{ +return -ENOTSUPP ; +} +#endif +#endif /* __SOC_TEGRA_MC_H__ */
Signature · 1 line
2.1.4
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)
Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/ffe99bd6/attachment.sig>
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 20 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)
Quoted 9 lines
Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
Cheers,
Peter.
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 31 lines · Peter De Schrijver
Quoted 20 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Quoted 9 lines
Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Quoted 4 lines
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/78b5bd92/attachment-0001.sig>
Jon Hunter
maintainerPATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 34 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 31 lines · Peter De Schrijver
Quoted 29 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Quoted 7 lines
Quoted 4 lines
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
I see your point and it is interesting. The trouble is that we would need to test every memory client in every power domain to prove this. So I don't think that is a trivial thing to do. Furthermore, looking at what we have done in kernel used for android products (which probably stress PM the most) this is done and so I don't know of any shipping product that stresses PM that does not do this. May be someone else might. I personally would not be comfortable removing this without testing, but as I mentioned it is not a trivial thing to test correctly. However, I will let you and the other maintainers decide what's best here.
Jon
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 44 lines · Jon Hunter
Quoted 34 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 31 lines · Peter De Schrijver
Quoted 29 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Quoted 7 lines
Quoted 4 lines
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
Quoted 9 lines
I see your point and it is interesting. The trouble is that we would need to test every memory client in every power domain to prove this. So I don't think that is a trivial thing to do. Furthermore, looking at what we have done in kernel used for android products (which probably stress PM the most) this is done and so I don't know of any shipping product that stresses PM that does not do this. May be someone else might. I personally would not be comfortable removing this without testing, but as I mentioned it is not a trivial thing to test correctly. However, I will let you and the other maintainers decide what's best here.
Quite frankly, I'm fairly sure most of this is untested anyway. There isn't a good way to test gr3d for example.
Generally I lean towards not merging any code that we don't know is needed. Merely because Android kernels do something doesn't mean it is necessary to do it (or even sane in some cases).
Now I understand that it might be difficult to test this, but that's all the more reason not to include the code. If we find that we have bugs that this can fix, then we can come up with tests to trigger it and validate that the fix actually fixes something.
In other words, I don't think it makes sense to implement mechanisms to recover from situations that we have no way of triggering in the first place.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150720/0c490974/attachment.sig>
PATCH v3 2/19 memory: tegra: Add MC flush support
In reply to Thierry RedingQuoted 34 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 31 lines · Peter De Schrijver
Quoted 29 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Quoted 8 lines
Quoted 4 lines
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
The peripheral driver doesn't know how long a request is queued in the memory controller. So it can't be responsible for ensuring this.
Cheers,
Peter.
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 40 lines · Peter De Schrijver
Quoted 39 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 35 lines · Peter De Schrijver
Quoted 20 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Quoted 9 lines
Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
Quoted 6 lines
Quoted 3 lines
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
The peripheral driver doesn't know how long a request is queued in the memory controller. So it can't be responsible for ensuring this.
Surely whenever the peripheral reports that the operation is done (be that via some DMA controller interrupt or syncpoint increment) the operation would have flushed from the memory controller. Drivers are already supposed to make sure this is the case when they are removed or suspended, so this would mean that we'd be adding all this code for no real gain.
It would also explain why we're currently not seeing any such problems.
Also note that I'm not saying no to this, I merely want to make sure that it's necessary, and in order to prove so we need tests to produce enough traffic for this to be exposed, and then we can also verify that the patches actually do what they're supposed to.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150720/5cb9197d/attachment-0001.sig>
PATCH v3 2/19 memory: tegra: Add MC flush support
Quoted 50 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 40 lines · Peter De Schrijver
Quoted 39 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 35 lines · Peter De Schrijver
Quoted 20 lines · Thierry Reding
Quoted 1 line
Old Signed by an unknown key
Quoted 8 lines · Jon Hunter
The Tegra memory controller implements a flush feature to flush pending accesses and prevent further accesses from occurring. This feature is used when powering down IP blocks to ensure the IP block is in a good state. The flushes are organised by software groups and IP blocks are assigned in hardware to the different software groups. Add helper functions for requesting a handle to an MC flush for a given software group and enabling/disabling the MC flush itself.
Quoted 8 lines
This is based upon a change by Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/mc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/memory/tegra/mc.h | 2 + include/soc/tegra/mc.h | 34 ++++++++++++++ 3 files changed, 146 insertions(+)Quoted 9 lines
Do we know if this is actually necessary? I remember having a discussion with Arnd Bergmann a while ago, and the Linux driver model kind of assumes that by the time a device is disabled all outstanding accesses will have stopped.
Do we have a way to determine that this even makes a difference? Can we trigger a case where not doing this would cause breakage and see that adding this fixes that particular issue?
Most likely it is. The memory controller can still be processing requests when the peripheral domain is powergated. This would mean the response cannot be delivered in that case. So we need to be sure there are no outstanding requests before shutting down the domain.
Quoted 6 lines
Quoted 3 lines
My point is that that's the driver's responsibility anyway, hence making the explicit flush unnecessary.
The peripheral driver doesn't know how long a request is queued in the memory controller. So it can't be responsible for ensuring this.
Quoted 7 lines
Surely whenever the peripheral reports that the operation is done (be that via some DMA controller interrupt or syncpoint increment) the operation would have flushed from the memory controller. Drivers are already supposed to make sure this is the case when they are removed or suspended, so this would mean that we'd be adding all this code for no real gain.
That highly depends on how the peripheral is implemented. I'm not sure we can assume things work this way.
Quoted 2 lines
It would also explain why we're currently not seeing any such problems.
I don't think we are doing agressive enough powergating today to see any problems. That might also mean we're just lucky.
Cheers,
Peter.
Jon Hunter
maintainerPATCH v3 3/19 memory: tegra: add flush operation for Tegra30 memory clients
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra30.
[jonathanh at nvidia.com: Removed tegra_mc_ops and added
metastable_flush_reads.]
---
v3: removal of tegra_mc_ops
--- drivers/memory/tegra/tegra30.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
drivers/memory/tegra/tegra30.c +24 -0
@@ -6,6 +6,7 @@* published by the Free Software Foundation. */ +#include <linux/device.h> #include <linux/of.h> #include <linux/mm.h>
@@ -936,6 +937,26 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, }; +static struct tegra_mc_flush tegra30_mc_flush[] = { + {TEGRA_SWGROUP_AFI ,0x200 ,0x204 ,0 }, + {TEGRA_SWGROUP_AVPC ,0x200 ,0x204 ,1 }, + {TEGRA_SWGROUP_DC ,0x200 ,0x204 ,2 }, + {TEGRA_SWGROUP_DCB ,0x200 ,0x204 ,3 }, + {TEGRA_SWGROUP_EPP ,0x200 ,0x204 ,4 }, + {TEGRA_SWGROUP_G2 ,0x200 ,0x204 ,5 }, + {TEGRA_SWGROUP_HC ,0x200 ,0x204 ,6 }, + {TEGRA_SWGROUP_HDA ,0x200 ,0x204 ,7 }, + {TEGRA_SWGROUP_ISP ,0x200 ,0x204 ,8 }, + {TEGRA_SWGROUP_MPCORE ,0x200 ,0x204 ,9 }, + {TEGRA_SWGROUP_MPCORELP ,0x200 ,0x204 ,10 }, + {TEGRA_SWGROUP_MPE ,0x200 ,0x204 ,11 }, + {TEGRA_SWGROUP_NV ,0x200 ,0x204 ,12 }, + {TEGRA_SWGROUP_NV2 ,0x200 ,0x204 ,13 }, + {TEGRA_SWGROUP_PPCS ,0x200 ,0x204 ,14 }, + {TEGRA_SWGROUP_VDE ,0x200 ,0x204 ,16 }, + {TEGRA_SWGROUP_VI ,0x200 ,0x204 ,17 }, +}; +static void tegra30_flush_dcache (struct page *page,unsigned long offset,size_t size) {
@@ -967,4 +988,7 @@ const struct tegra_mc_soc tegra30_mc_soc = { .num_address_bits = 32, .atom_size = 16, .smmu = &tegra30_smmu_soc, + .flushes = tegra30_mc_flush, + .num_flushes = ARRAY_SIZE(tegra30_mc_flush), + .metastable_flush_reads = 0, };
Signature · 1 line
2.1.4
PATCH v3 3/19 memory: tegra: add flush operation for Tegra30 memory clients
Quoted 21 lines · Jon Hunter
From: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra30.
Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Removed
tegra_mc_opsand added
metastable_flush_reads.]Signed-off-by Jon Hunter <[email protected]>---
v3: removal oftegra_mc_ops--- drivers/memory/tegra/tegra30.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)drivers/memory/tegra/tegra30.c +0
-0
drivers/memory/tegra/tegra30.c line 9
Quoted 4 lines
drivers/memory/tegra/tegra30.c +1
-0@@ -6,6 +6,7 @@* published by the Free Software Foundation. */ +#include <linux/device.h>
What do you need this for?
drivers/memory/tegra/tegra30.c static const struct tegra_smmu_swgroup tegra30_swgroups[] = { · line 940
Quoted 8 lines
drivers/memory/tegra/tegra30.c +1
-0#include <linux/of.h> #include <linux/mm.h> @@ -936,6 +937,26 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, }; +static struct tegra_mc_flush tegra30_mc_flush[] = {
const
Quoted 7 lines
drivers/memory/tegra/tegra30.c +7
-0+ {TEGRA_SWGROUP_AFI, 0x200, 0x204, 0}, + {TEGRA_SWGROUP_AVPC, 0x200, 0x204, 1}, + {TEGRA_SWGROUP_DC, 0x200, 0x204, 2}, + {TEGRA_SWGROUP_DCB, 0x200, 0x204, 3}, + {TEGRA_SWGROUP_EPP, 0x200, 0x204, 4}, + {TEGRA_SWGROUP_G2, 0x200, 0x204, 5}, + {TEGRA_SWGROUP_HC, 0x200, 0x204, 6},
drivers/memory/tegra/tegra30.c static const struct tegra_smmu_swgroup tegra30_swgroups[] = { · line 957
Quoted 10 lines
drivers/memory/tegra/tegra30.c +10
-0+ {TEGRA_SWGROUP_HDA, 0x200, 0x204, 7}, + {TEGRA_SWGROUP_ISP, 0x200, 0x204, 8}, + {TEGRA_SWGROUP_MPCORE, 0x200, 0x204, 9}, + {TEGRA_SWGROUP_MPCORELP, 0x200, 0x204, 10}, + {TEGRA_SWGROUP_MPE, 0x200, 0x204, 11}, + {TEGRA_SWGROUP_NV, 0x200, 0x204, 12}, + {TEGRA_SWGROUP_NV2, 0x200, 0x204, 13}, + {TEGRA_SWGROUP_PPCS, 0x200, 0x204, 14}, + {TEGRA_SWGROUP_VDE, 0x200, 0x204, 16}, + {TEGRA_SWGROUP_VI, 0x200, 0x204, 17},
Spaces around { and }, please.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/c897e0ba/attachment.sig>
Jon Hunter
maintainerPATCH v3 3/19 memory: tegra: add flush operation for Tegra30 memory clients
Quoted 27 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 24 lines · Jon Hunter
From: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra30.
Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Removed
tegra_mc_opsand added
metastable_flush_reads.]Signed-off-by Jon Hunter <[email protected]>---
v3: removal oftegra_mc_ops--- drivers/memory/tegra/tegra30.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)drivers/memory/tegra/tegra30.c +0
-0@@ -6,6 +6,7 @@* published by the Free Software Foundation. */
Quoted 3 lines
drivers/memory/tegra/tegra30.c line 9
Quoted 1 line
drivers/memory/tegra/tegra30.c +1
-0+#include <linux/device.h> What do you need this for?
Looks like I forgot to remove this, when removing the tegra_mc_ops. Thanks, will remove.
Quoted 10 lines
drivers/memory/tegra/tegra30.c static const struct tegra_smmu_swgroup tegra30_swgroups[] = { · line 940
Quoted 8 lines
drivers/memory/tegra/tegra30.c +1
-0#include <linux/of.h> #include <linux/mm.h> @@ -936,6 +937,26 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, }; +static struct tegra_mc_flush tegra30_mc_flush[] = {const
Yes, will fix.
Quoted 11 lines
Quoted 11 lines
drivers/memory/tegra/tegra30.c +11
-0+ {TEGRA_SWGROUP_AFI, 0x200, 0x204, 0}, + {TEGRA_SWGROUP_AVPC, 0x200, 0x204, 1}, + {TEGRA_SWGROUP_DC, 0x200, 0x204, 2}, + {TEGRA_SWGROUP_DCB, 0x200, 0x204, 3}, + {TEGRA_SWGROUP_EPP, 0x200, 0x204, 4}, + {TEGRA_SWGROUP_G2, 0x200, 0x204, 5}, + {TEGRA_SWGROUP_HC, 0x200, 0x204, 6}, + {TEGRA_SWGROUP_HDA, 0x200, 0x204, 7}, + {TEGRA_SWGROUP_ISP, 0x200, 0x204, 8}, + {TEGRA_SWGROUP_MPCORE, 0x200, 0x204, 9}, + {TEGRA_SWGROUP_MPCORELP, 0x200, 0x204, 10},
Quoted 8 lines
drivers/memory/tegra/tegra30.c static const struct tegra_smmu_swgroup tegra30_swgroups[] = { · line 957
Quoted 6 lines
drivers/memory/tegra/tegra30.c +6
-0+ {TEGRA_SWGROUP_MPE, 0x200, 0x204, 11}, + {TEGRA_SWGROUP_NV, 0x200, 0x204, 12}, + {TEGRA_SWGROUP_NV2, 0x200, 0x204, 13}, + {TEGRA_SWGROUP_PPCS, 0x200, 0x204, 14}, + {TEGRA_SWGROUP_VDE, 0x200, 0x204, 16}, + {TEGRA_SWGROUP_VI, 0x200, 0x204, 17},Spaces around { and }, please.
Ok.
Cheers
Jon
Jon Hunter
maintainerPATCH v3 4/19 memory: tegra: add flush operation for Tegra114 memory clients
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra114.
[jonathanh at nvidia.com: Removed tegra_mc_ops and added
metastable_flush_reads.]
--- drivers/memory/tegra/tegra114.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
drivers/memory/tegra/tegra114.c +21 -0
@@ -914,6 +914,24 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, }; +static struct tegra_mc_flush tegra114_mc_flush[] = { + {TEGRA_SWGROUP_AVPC ,0x200 ,0x204 ,1 }, + {TEGRA_SWGROUP_DC ,0x200 ,0x204 ,2 }, + {TEGRA_SWGROUP_DCB ,0x200 ,0x204 ,3 }, + {TEGRA_SWGROUP_EPP ,0x200 ,0x204 ,4 }, + {TEGRA_SWGROUP_G2 ,0x200 ,0x204 ,5 }, + {TEGRA_SWGROUP_HC ,0x200 ,0x204 ,6 }, + {TEGRA_SWGROUP_HDA ,0x200 ,0x204 ,7 }, + {TEGRA_SWGROUP_ISP ,0x200 ,0x204 ,8 }, + {TEGRA_SWGROUP_MPCORE ,0x200 ,0x204 ,9 }, + {TEGRA_SWGROUP_MPCORELP ,0x200 ,0x204 ,10 }, + {TEGRA_SWGROUP_MSENC ,0x200 ,0x204 ,11 }, + {TEGRA_SWGROUP_NV ,0x200 ,0x204 ,12 }, + {TEGRA_SWGROUP_PPCS ,0x200 ,0x204 ,14 }, + {TEGRA_SWGROUP_VDE ,0x200 ,0x204 ,16 }, + {TEGRA_SWGROUP_VI ,0x200 ,0x204 ,17 }, +}; +static void tegra114_flush_dcache (struct page *page,unsigned long offset,size_t size) {
@@ -945,4 +963,7 @@ const struct tegra_mc_soc tegra114_mc_soc = { .num_address_bits = 32, .atom_size = 32, .smmu = &tegra114_smmu_soc, + .flushes = tegra114_mc_flush, + .num_flushes = ARRAY_SIZE(tegra114_mc_flush), + .metastable_flush_reads = MC_FLUSH_METASTABLE_READS, };
Signature · 1 line
2.1.4
PATCH v3 4/19 memory: tegra: add flush operation for Tegra114 memory clients
Quoted 18 lines · Jon Hunter
From: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra114.
Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Removed
tegra_mc_opsand added
metastable_flush_reads.]Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/tegra114.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)drivers/memory/tegra/tegra114.c +0
-0
drivers/memory/tegra/tegra114.c static const struct tegra_smmu_swgroup tegra114_swgroups[] = { · line 917
Quoted 4 lines
drivers/memory/tegra/tegra114.c +1
-0@@ -914,6 +914,24 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, }; +static struct tegra_mc_flush tegra114_mc_flush[] = {
const
Quoted 6 lines
drivers/memory/tegra/tegra114.c +6
-0+ {TEGRA_SWGROUP_AVPC, 0x200, 0x204, 1}, + {TEGRA_SWGROUP_DC, 0x200, 0x204, 2}, + {TEGRA_SWGROUP_DCB, 0x200, 0x204, 3}, + {TEGRA_SWGROUP_EPP, 0x200, 0x204, 4}, + {TEGRA_SWGROUP_G2, 0x200, 0x204, 5}, + {TEGRA_SWGROUP_HC, 0x200, 0x204, 6},
drivers/memory/tegra/tegra114.c static const struct tegra_smmu_swgroup tegra114_swgroups[] = { · line 933
Quoted 10 lines
drivers/memory/tegra/tegra114.c +10
-0+ {TEGRA_SWGROUP_HDA, 0x200, 0x204, 7}, + {TEGRA_SWGROUP_ISP, 0x200, 0x204, 8}, + {TEGRA_SWGROUP_MPCORE, 0x200, 0x204, 9}, + {TEGRA_SWGROUP_MPCORELP, 0x200, 0x204, 10}, + {TEGRA_SWGROUP_MSENC, 0x200, 0x204, 11}, + {TEGRA_SWGROUP_NV, 0x200, 0x204, 12}, + {TEGRA_SWGROUP_PPCS, 0x200, 0x204, 14}, + {TEGRA_SWGROUP_VDE, 0x200, 0x204, 16}, + {TEGRA_SWGROUP_VI, 0x200, 0x204, 17}, +};
And spaces after { and before }.
drivers/memory/tegra/tegra114.c const struct tegra_mc_soc tegra114_mc_soc = { · line 968
Quoted 11 lines
drivers/memory/tegra/tegra114.c +4
-0+static void tegra114_flush_dcache (struct page *page,unsigned long offset,size_t size) {@@ -945,4 +963,7 @@ const struct tegra_mc_soc tegra114_mc_soc = { .num_address_bits = 32, .atom_size = 32, .smmu = &tegra114_smmu_soc, + .flushes = tegra114_mc_flush, + .num_flushes = ARRAY_SIZE(tegra114_mc_flush), + .metastable_flush_reads = MC_FLUSH_METASTABLE_READS,
I don't think it's useful to have this define. Just use the literal value here. Much of the purpose of having this per-SoC parameter is to provide the context that otherwise the macro symbol would provide.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/397468ae/attachment.sig>
Jon Hunter
maintainerPATCH v3 5/19 memory: tegra: add flush operation for Tegra124 memory clients
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra124.
[jonathanh at nvidia.com: Removed tegra_mc_ops and added
metastable_flush_reads.]
--- drivers/memory/tegra/tegra124.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
drivers/memory/tegra/tegra124.c +31 -0
@@ -1002,6 +1002,34 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = { };#ifdef CONFIG_ARCH_TEGRA_124_SOC + +static struct tegra_mc_flush tegra124_mc_flush[] = { + {TEGRA_SWGROUP_AFI ,0x200 ,0x204 ,0 }, + {TEGRA_SWGROUP_AVPC ,0x200 ,0x204 ,1 }, + {TEGRA_SWGROUP_DC ,0x200 ,0x204 ,2 }, + {TEGRA_SWGROUP_DCB ,0x200 ,0x204 ,3 }, + {TEGRA_SWGROUP_HC ,0x200 ,0x204 ,6 }, + {TEGRA_SWGROUP_HDA ,0x200 ,0x204 ,7 }, + {TEGRA_SWGROUP_ISP2 ,0x200 ,0x204 ,8 }, + {TEGRA_SWGROUP_MPCORE ,0x200 ,0x204 ,9 }, + {TEGRA_SWGROUP_MPCORELP ,0x200 ,0x204 ,10 }, + {TEGRA_SWGROUP_MSENC ,0x200 ,0x204 ,11 }, + {TEGRA_SWGROUP_PPCS ,0x200 ,0x204 ,14 }, + {TEGRA_SWGROUP_SATA ,0x200 ,0x204 ,15 }, + {TEGRA_SWGROUP_VDE ,0x200 ,0x204 ,16 }, + {TEGRA_SWGROUP_VI ,0x200 ,0x204 ,17 }, + {TEGRA_SWGROUP_VIC ,0x200 ,0x204 ,18 }, + {TEGRA_SWGROUP_XUSB_HOST ,0x200 ,0x204 ,19 }, + {TEGRA_SWGROUP_XUSB_DEV ,0x200 ,0x204 ,20 }, + {TEGRA_SWGROUP_TSEC ,0x200 ,0x204 ,22 }, + {TEGRA_SWGROUP_SDMMC1A ,0x200 ,0x204 ,29 }, + {TEGRA_SWGROUP_SDMMC2A ,0x200 ,0x204 ,30 }, + {TEGRA_SWGROUP_SDMMC3A ,0x200 ,0x204 ,31 }, + {TEGRA_SWGROUP_SDMMC4A ,0x970 ,0x974 ,0 }, + {TEGRA_SWGROUP_ISP2B ,0x970 ,0x974 ,1 }, + {TEGRA_SWGROUP_GPU ,0x970 ,0x974 ,2 }, +}; +static void tegra124_flush_dcache (struct page *page,unsigned long offset,size_t size) {
@@ -1035,6 +1063,9 @@ const struct tegra_mc_soc tegra124_mc_soc = { .smmu = &tegra124_smmu_soc, .emem_regs = tegra124_mc_emem_regs, .num_emem_regs = ARRAY_SIZE(tegra124_mc_emem_regs), + .flushes = tegra124_mc_flush, + .num_flushes = ARRAY_SIZE(tegra124_mc_flush), + .metastable_flush_reads = MC_FLUSH_METASTABLE_READS, };#endif /* CONFIG_ARCH_TEGRA_124_SOC */
Signature · 1 line
2.1.4
PATCH v3 5/19 memory: tegra: add flush operation for Tegra124 memory clients
Quoted 5 lines · Jon Hunter
From: Vince Hsu <[email protected]>
This patch adds the hot reset register table and flush related callback functions for Tegra124.
Quoted 7 lines
Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Removed
tegra_mc_opsand added
metastable_flush_reads.]Signed-off-by Jon Hunter <[email protected]>--- drivers/memory/tegra/tegra124.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
Same comments as for Tegra114.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/7bccb043/attachment.sig>
Jon Hunter
maintainerPATCH v3 6/19 clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
Tegra114 has a HW bug that the PLLD/PLLD2 lock bit cannot be asserted when the DIS power domain is during up-powergating process but the clamp to this domain is not removed yet. That causes a timeout and aborts the power sequence, although the PLLD/PLLD2 has already locked. To remove the false alarm, we don't use the lock for PLLD/PLLD2. Just wait 1ms and treat the clocks as locked.
--- drivers/clk/tegra/clk-tegra114.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/clk/tegra/clk-tegra114.c +2 -4
@@ -456,8 +456,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct tegra_clk_pll_params pll_d2_params = {
@@ -474,8 +473,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct pdiv_map pllu_p[] = {
Signature · 1 line
2.1.4
PATCH v3 6/19 clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Tegra114 has a HW bug that the PLLD/PLLD2 lock bit cannot be asserted when the DIS power domain is during up-powergating process but the clamp to this
I think there is missing 'off' in this sentence?
ie. ... 'the DIS power domain is off?during up-powergating process'
Also 'un-powergating sequence' would be nicer.
Quoted 38 lines
domain is not removed yet. That causes a timeout and aborts the power sequence, although the PLLD/PLLD2 has already locked. To remove the false alarm, we don't use the lock for PLLD/PLLD2. Just wait 1ms and treat the clocks as locked.
Signed-off-by Vince Hsu <[email protected]>Signed-off-by Jon Hunter <[email protected]>--- drivers/clk/tegra/clk-tegra114.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)drivers/clk/tegra/clk-tegra114.c +2
-4@@ -456,8 +456,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct tegra_clk_pll_params pll_d2_params = {@@ -474,8 +473,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct pdiv_map pllu_p[] = {Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 6/19 clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
Quoted 11 lines · Peter De Schrijver
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Tegra114 has a HW bug that the PLLD/PLLD2 lock bit cannot be asserted when the DIS power domain is during up-powergating process but the clamp to this
I think there is missing 'off' in this sentence?
ie. ... 'the DIS power domain is off during up-powergating process'
Also 'un-powergating sequence' would be nicer.
Yes agree. I will re-word that.
Thanks
Jon
Jon Hunter
maintainerPATCH v3 6/19 clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
Quoted 9 lines · Jon Hunter
Quoted 6 lines · Peter De Schrijver
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Tegra114 has a HW bug that the PLLD/PLLD2 lock bit cannot be asserted when the DIS power domain is during up-powergating process but the clamp to this
Quoted 7 lines
Quoted 5 lines
I think there is missing 'off' in this sentence?
ie. ... 'the DIS power domain is off during up-powergating process'
Also 'un-powergating sequence' would be nicer.
Yes agree. I will re-word that.
Updated version ...
Quoted 1 line
From
d5cbecd4e97332cd8373b9c4893731eb8e063660Mon Sep 17 00:00:00 2001
From: Vince Hsu <[email protected]>
Date: Wed, 11 Mar 2015 16:46:04 +0800
Subject: [PATCH 06/19] clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
Tegra114 has a HW bug where the PLLD/PLLD2 lock bit cannot be asserted while turning on the Display power domain and before the clamp to this domain has been removed. This issue causes a timeout and aborts the power up sequence, even though the PLLD/PLLD2 has already locked. To avoid this, don't use the lock for PLLD/PLLD2, just wait 1ms and treat the clocks as locked.
--- drivers/clk/tegra/clk-tegra114.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/clk/tegra/clk-tegra114.c +2 -4
@@ -456,8 +456,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct tegra_clk_pll_params pll_d2_params = {
@@ -474,8 +473,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct pdiv_map pllu_p[] = {
Signature · 1 line
2.1.4
PATCH v3 6/19 clk: tegra: remove TEGRA_PLL_USE_LOCK for PLLD/PLLD2
Quoted 34 lines · Jon Hunter
Quoted 9 lines · Jon Hunter
Quoted 6 lines · Peter De Schrijver
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Tegra114 has a HW bug that the PLLD/PLLD2 lock bit cannot be asserted when the DIS power domain is during up-powergating process but the clamp to this
Quoted 7 lines
Quoted 5 lines
I think there is missing 'off' in this sentence?
ie. ... 'the DIS power domain is off during up-powergating process'
Also 'un-powergating sequence' would be nicer.
Yes agree. I will re-word that.
Updated version ...
From
d5cbecd4e97332cd8373b9c4893731eb8e063660Mon Sep 17 00:00:00 2001
From: Vince Hsu <[email protected]>
Date: Wed, 11 Mar 2015 16:46:04 +0800
Subject: [PATCH 06/19] clk: tegra: removeTEGRA_PLL_USE_LOCKfor PLLD/PLLD2Tegra114 has a HW bug where the PLLD/PLLD2 lock bit cannot be asserted while turning on the Display power domain and before the clamp to this domain has been removed. This issue causes a timeout and aborts the power up sequence, even though the PLLD/PLLD2 has already locked. To avoid this, don't use the lock for PLLD/PLLD2, just wait 1ms and treat the clocks as locked.
Quoted 3 lines
Signed-off-by Vince Hsu <[email protected]>[jonathanh at nvidia.com: Updated the changelog description]Signed-off-by Jon Hunter <[email protected]>
Quoted 31 lines
--- drivers/clk/tegra/clk-tegra114.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)drivers/clk/tegra/clk-tegra114.c +2
-4@@ -456,8 +456,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct tegra_clk_pll_params pll_d2_params = {@@ -474,8 +473,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_delay = 1000, .div_nmp = &pllp_nmp, .freq_table = pll_d_freq_table, - .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | - TEGRA_PLL_USE_LOCK, + .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON, };static struct pdiv_map pllu_p[] = {Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 7/19 soc: tegra: pmc: Wait for powergate state to change
In reply to Jon HunterCurrently, the function tegra_powergate_set() simply sets the desired powergate state but does not wait for the state to change. In some circumstances this can be desirable. However, in most cases we should wait for the state to change before proceeding. Therefore, add a parameter to tegra_powergate_set() to indicate whether we should wait for the state to change.
By adding this feature, we can also eliminate the polling loop from tegra30_boot_secondary().
--- arch/arm/mach-tegra/platsmp.c | 18 ++++-------------- drivers/soc/tegra/pmc.c | 29 +++++++++++++++++++++++------ include/soc/tegra/pmc.h | 2 +- 3 files changed, 28 insertions(+), 21 deletions(-)
arch/arm/mach-tegra/platsmp.c +4 -14
@@ -108,19 +108,9 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)* be un-gated by un-toggling the power gate register * manually. */ -if (!tegra_pmc_cpu_is_powered (cpu)) { - ret =tegra_pmc_cpu_power_on (cpu); -if (ret) -return ret; - -/* Wait for the power to come up. */ - timeout = jiffies +msecs_to_jiffies (100 ); -while (!tegra_pmc_cpu_is_powered (cpu)) { -if (time_after (jiffies, timeout)) -return -ETIMEDOUT ; -udelay (10 ); - } - } + ret =tegra_pmc_cpu_power_on (cpu, true); +if (ret) +return ret; remove_clamps:/* CPU partition is powered. Enable the CPU clock. */
@@ -162,7 +152,7 @@ static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)* also initial power state in flow controller. After that, * the CPU's power state is maintained by flow controller. */ - ret =tegra_pmc_cpu_power_on (cpu); + ret =tegra_pmc_cpu_power_on (cpu, false); }return ret;
drivers/soc/tegra/pmc.c +23 -6
@@ -175,9 +175,11 @@ 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 (int id,bool new_state,bool wait) { +unsigned long timeout;bool status; +int ret =0 ;mutex_lock (&pmc->powergates_lock);
@@ -190,9 +192,23 @@ static int tegra_powergate_set(int id, bool new_state)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE ); +if (wait) { + timeout = jiffies +msecs_to_jiffies (100 ); + ret = -ETIMEDOUT ; + +while (time_before (jiffies, timeout)) { + status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); +if (status == new_state) { + ret =0 ; +break ; + } +udelay (10 ); + } + } +mutex_unlock (&pmc->powergates_lock); -return 0 ; +return ret; }/**
@@ -204,7 +220,7 @@ int tegra_powergate_power_on(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, true); +return tegra_powergate_set (id, true, true); }/**
@@ -216,7 +232,7 @@ int tegra_powergate_power_off(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, false); +return tegra_powergate_set (id, false, true); }EXPORT_SYMBOL (tegra_powergate_power_off);
@@ -351,8 +367,9 @@ bool tegra_pmc_cpu_is_powered(int cpuid)/** * tegra_pmc_cpu_power_on() - power on CPU partition * @cpuid: CPU partition ID +* @wait: Wait for CPU state to transition */ -int tegra_pmc_cpu_power_on (int cpuid) +int tegra_pmc_cpu_power_on (int cpuid,bool wait) {int id;
@@ -360,7 +377,7 @@ int tegra_pmc_cpu_power_on(int cpuid)if (id <0 )return id; -return tegra_powergate_set (id, true); +return tegra_powergate_set (id, true, wait); }/**
include/soc/tegra/pmc.h +1 -1
@@ -34,7 +34,7 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);#ifdef CONFIG_SMP bool tegra_pmc_cpu_is_powered (int cpuid); -int tegra_pmc_cpu_power_on (int cpuid); +int tegra_pmc_cpu_power_on (int cpuid,bool wait);int tegra_pmc_cpu_remove_clamping (int cpuid);#endif /* CONFIG_SMP */
Signature · 1 line
2.1.4
PATCH v3 7/19 soc: tegra: pmc: Wait for powergate state to change
Quoted 58 lines · Jon Hunter
Currently, the function
tegra_powergate_set()simply sets the desired powergate state but does not wait for the state to change. In some circumstances this can be desirable. However, in most cases we should wait for the state to change before proceeding. Therefore, add a parameter totegra_powergate_set()to indicate whether we should wait for the state to change.By adding this feature, we can also eliminate the polling loop from
tegra30_boot_secondary().Signed-off-by Jon Hunter <[email protected]>--- arch/arm/mach-tegra/platsmp.c | 18 ++++-------------- drivers/soc/tegra/pmc.c | 29 +++++++++++++++++++++++------ include/soc/tegra/pmc.h | 2 +- 3 files changed, 28 insertions(+), 21 deletions(-)arch/arm/mach-tegra/platsmp.c +4
-14@@ -108,19 +108,9 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)* be un-gated by un-toggling the power gate register * manually. */ -if (!tegra_pmc_cpu_is_powered (cpu)) { - ret =tegra_pmc_cpu_power_on (cpu); -if (ret) -return ret; - -/* Wait for the power to come up. */ - timeout = jiffies +msecs_to_jiffies (100 ); -while (!tegra_pmc_cpu_is_powered (cpu)) { -if (time_after (jiffies, timeout)) -return -ETIMEDOUT ; -udelay (10 ); - } - } + ret =tegra_pmc_cpu_power_on (cpu, true); +if (ret) +return ret; remove_clamps:/* CPU partition is powered. Enable the CPU clock. */ @@ -162,7 +152,7 @@ static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)* also initial power state in flow controller. After that, * the CPU's power state is maintained by flow controller. */ - ret =tegra_pmc_cpu_power_on (cpu); + ret =tegra_pmc_cpu_power_on (cpu, false); }return ret;drivers/soc/tegra/pmc.c +0
-0
drivers/soc/tegra/pmc.c static void tegra_pmc_writel(u32 value, unsigned long offset) · line 176
Quoted 2 lines
drivers/soc/tegra/pmc.c +0
-0@@ -175,9 +175,11 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)* @id: partition ID * @new_state: new state of the partition
The comment here isn't updated.
drivers/soc/tegra/pmc.c static void tegra_pmc_writel(u32 value, unsigned long offset) · line 178
Quoted 3 lines
drivers/soc/tegra/pmc.c +1
-1*/ -static int tegra_powergate_set (int id,bool new_state) +static int tegra_powergate_set (int id,bool new_state,bool wait)
Can we please not chain boolean parameters, it makes the call sites impossible to parse for humans. Instead, can we simply leave tegra_powergate_set() as it is and add another function, such as tegra_powergate_set_sync() or tegra_powergate_set_and_wait(), to achieve the same? You may have to split out a tegra_powergate_set_unlocked() or similar to make sure you get to keep the lock across both operations:
static int tegra_powergate_set_unlocked(int id, bool new_state)
{
...
}
static int tegra_powergate_set(int id, bool new_state)
{
int err;
mutex_lock(&pmc->powergates_lock);
err = tegra_powergate_set_unlocked(id, new_state);
mutex_unlock(&pmc->powergates_lock);
return err;
}
/*
* Must be called with pmc->powergates_lock mutex held.
*/
static int tegra_powergate_wait(int id, bool new_state)
{
...
}
static int tegra_powergate_set_and_wait(int id, bool new_state)
{
int err;
mutex_lock(&pmc->powergates_lock);
err = tegra_powergate_set_unlocked(id, new_state);
if (err < 0)
goto unlock;
err = tegra_powergate_wait(id, new_state);
if (err < 0)
goto unlock;
unlock:
mutex_unlock(&pmc->powergates_lock);
return err;
}Quoted 51 lines
drivers/soc/tegra/pmc.c +19
-3{ +unsigned long timeout;bool status; +int ret =0 ;mutex_lock (&pmc->powergates_lock);@@ -190,9 +192,23 @@ static int tegra_powergate_set(int id, bool new_state)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE ); +if (wait) { + timeout = jiffies +msecs_to_jiffies (100 ); + ret = -ETIMEDOUT ; + +while (time_before (jiffies, timeout)) { + status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); +if (status == new_state) { + ret =0 ; +break ; + } +udelay (10 ); + } + } +mutex_unlock (&pmc->powergates_lock); -return 0 ; +return ret; }/** @@ -204,7 +220,7 @@ int tegra_powergate_power_on(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, true); +return tegra_powergate_set (id, true, true); }/** @@ -216,7 +232,7 @@ int tegra_powergate_power_off(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, false); +return tegra_powergate_set (id, false, true); }EXPORT_SYMBOL (tegra_powergate_power_off);
drivers/soc/tegra/pmc.c bool tegra_pmc_cpu_is_powered(int cpuid) · line 372
Quoted 7 lines
drivers/soc/tegra/pmc.c +2
-1@@ -351,8 +367,9 @@ bool tegra_pmc_cpu_is_powered(int cpuid)/** * tegra_pmc_cpu_power_on() - power on CPU partition * @cpuid: CPU partition ID +* @wait: Wait for CPU state to transition */ -int tegra_pmc_cpu_power_on (int cpuid) +int tegra_pmc_cpu_power_on (int cpuid,bool wait)
This one is probably fine since it's the only boolean parameter so far. That said, I see that we call this exactly twice, so I wonder if there'd be any harm in having the second occurrence wait as well and hence get rid of the parameter.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/90379f1b/attachment.sig>
Jon Hunter
maintainerPATCH v3 7/19 soc: tegra: pmc: Wait for powergate state to change
Quoted 170 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 58 lines · Jon Hunter
Currently, the function
tegra_powergate_set()simply sets the desired powergate state but does not wait for the state to change. In some circumstances this can be desirable. However, in most cases we should wait for the state to change before proceeding. Therefore, add a parameter totegra_powergate_set()to indicate whether we should wait for the state to change.By adding this feature, we can also eliminate the polling loop from
tegra30_boot_secondary().Signed-off-by Jon Hunter <[email protected]>--- arch/arm/mach-tegra/platsmp.c | 18 ++++-------------- drivers/soc/tegra/pmc.c | 29 +++++++++++++++++++++++------ include/soc/tegra/pmc.h | 2 +- 3 files changed, 28 insertions(+), 21 deletions(-)arch/arm/mach-tegra/platsmp.c +4
-14@@ -108,19 +108,9 @@ static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)* be un-gated by un-toggling the power gate register * manually. */ -if (!tegra_pmc_cpu_is_powered (cpu)) { - ret =tegra_pmc_cpu_power_on (cpu); -if (ret) -return ret; - -/* Wait for the power to come up. */ - timeout = jiffies +msecs_to_jiffies (100 ); -while (!tegra_pmc_cpu_is_powered (cpu)) { -if (time_after (jiffies, timeout)) -return -ETIMEDOUT ; -udelay (10 ); - } - } + ret =tegra_pmc_cpu_power_on (cpu, true); +if (ret) +return ret; remove_clamps:/* CPU partition is powered. Enable the CPU clock. */ @@ -162,7 +152,7 @@ static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)* also initial power state in flow controller. After that, * the CPU's power state is maintained by flow controller. */ - ret =tegra_pmc_cpu_power_on (cpu); + ret =tegra_pmc_cpu_power_on (cpu, false); }return ret;drivers/soc/tegra/pmc.c +0
-0drivers/soc/tegra/pmc.c static void tegra_pmc_writel(u32 value, unsigned long offset) · line 176
Quoted 2 lines
drivers/soc/tegra/pmc.c +0
-0@@ -175,9 +175,11 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)* @id: partition ID * @new_state: new state of the partition The comment here isn't updated.
drivers/soc/tegra/pmc.c static void tegra_pmc_writel(u32 value, unsigned long offset) · line 178
Quoted 3 lines
drivers/soc/tegra/pmc.c +1
-1*/ -static int tegra_powergate_set (int id,bool new_state) +static int tegra_powergate_set (int id,bool new_state,bool wait)Can we please not chain boolean parameters, it makes the call sites impossible to parse for humans. Instead, can we simply leave
tegra_powergate_set()as it is and add another function, such astegra_powergate_set_sync()ortegra_powergate_set_and_wait(), to achieve the same? You may have to split out ategra_powergate_set_unlocked()or similar to make sure you get to keep the lock across both operations:static int tegra_powergate_set_unlocked(int id, bool new_state) { ... } static int tegra_powergate_set(int id, bool new_state) { int err; mutex_lock(&pmc->powergates_lock); err = tegra_powergate_set_unlocked(id, new_state); mutex_unlock(&pmc->powergates_lock); return err; } /* * Must be called with pmc->powergates_lock mutex held. */ static int tegra_powergate_wait(int id, bool new_state) { ... } static int tegra_powergate_set_and_wait(int id, bool new_state) { int err; mutex_lock(&pmc->powergates_lock); err = tegra_powergate_set_unlocked(id, new_state); if (err < 0) goto unlock; err = tegra_powergate_wait(id, new_state); if (err < 0) goto unlock; unlock: mutex_unlock(&pmc->powergates_lock); return err; }Quoted 50 lines
drivers/soc/tegra/pmc.c +19
-3{ +unsigned long timeout;bool status; +int ret =0 ;mutex_lock (&pmc->powergates_lock);@@ -190,9 +192,23 @@ static int tegra_powergate_set(int id, bool new_state)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE ); +if (wait) { + timeout = jiffies +msecs_to_jiffies (100 ); + ret = -ETIMEDOUT ; + +while (time_before (jiffies, timeout)) { + status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); +if (status == new_state) { + ret =0 ; +break ; + } +udelay (10 ); + } + } +mutex_unlock (&pmc->powergates_lock); -return 0 ; +return ret; }/** @@ -204,7 +220,7 @@ int tegra_powergate_power_on(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, true); +return tegra_powergate_set (id, true, true); }/** @@ -216,7 +232,7 @@ int tegra_powergate_power_off(int id)if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; -return tegra_powergate_set (id, false); +return tegra_powergate_set (id, false, true); }EXPORT_SYMBOL (tegra_powergate_power_off);
Quoted 13 lines
drivers/soc/tegra/pmc.c bool tegra_pmc_cpu_is_powered(int cpuid) · line 372
Quoted 8 lines
drivers/soc/tegra/pmc.c +2
-1@@ -351,8 +367,9 @@ bool tegra_pmc_cpu_is_powered(int cpuid)/** * tegra_pmc_cpu_power_on() - power on CPU partition * @cpuid: CPU partition ID +* @wait: Wait for CPU state to transition */ -int tegra_pmc_cpu_power_on (int cpuid) +int tegra_pmc_cpu_power_on (int cpuid,bool wait)This one is probably fine since it's the only boolean parameter so far. That said, I see that we call this exactly twice, so I wonder if there'd be any harm in having the second occurrence wait as well and hence get rid of the parameter.
I would agree and think we should drop the 2nd parameter and just always wait. This is only done at boot time and so would only add a little delay at that point.
Cheers
Jon
Jon Hunter
maintainerPATCH v3 8/19 soc: tegra: pmc: Clean-up PMC helper functions
In reply to Jon HunterThe following clean-ups have been made to the PMC code:
1. Add a macro for determining the state of a PMC powergate
2. Use the readx_poll_timeout() function instead of implementing a local polling loop with a timeout.
3. Use a case-statement in the function that removes the powergate clamp instead of multiple if-statements to improve readability.
--- drivers/soc/tegra/pmc.c | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 37 deletions(-)
drivers/soc/tegra/pmc.c +35 -37
@@ -28,6 +28,7 @@#include <linux/export.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h>
@@ -56,6 +57,8 @@#define PWRGATE_TOGGLE_START (1 << 8)#define REMOVE_CLAMPING 0x34 +#define REMOVE_CLAMPING_VDEC (1 << 3) +#define REMOVE_CLAMPING_PCIE (1 << 4)#define PWRGATE_STATUS 0x38
@@ -101,6 +104,8 @@#define GPU_RG_CNTRL 0x2d4 +#define PMC_PWRGATE_STATE (val, id) (!!(val & BIT(id))) +struct tegra_pmc_soc {unsigned int num_powergates;const char *const *powergates;
@@ -177,15 +182,14 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)*/ static int tegra_powergate_set (int id,bool new_state,bool wait) { -unsigned long timeout; -bool status;int ret =0 ; +u32 val;mutex_lock (&pmc->powergates_lock); - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); + val =tegra_pmc_readl (PWRGATE_STATUS ); -if (status == new_state) { +if (PMC_PWRGATE_STATE (val, id) == new_state) {mutex_unlock (&pmc->powergates_lock);return 0 ; }
@@ -193,17 +197,9 @@ static int tegra_powergate_set(int id, bool new_state, bool wait)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE );if (wait) { - timeout = jiffies +msecs_to_jiffies (100 ); - ret = -ETIMEDOUT ; - -while (time_before (jiffies, timeout)) { - status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); -if (status == new_state) { - ret =0 ; -break ; - } -udelay (10 ); - } + ret =readx_poll_timeout (tegra_pmc_readl,PWRGATE_STATUS , + val,PMC_PWRGATE_STATE (val, id) == new_state, +10 ,100000 ); }mutex_unlock (&pmc->powergates_lock);
@@ -242,13 +238,10 @@ EXPORT_SYMBOL(tegra_powergate_power_off);*/ int tegra_powergate_is_powered (int id) { -u32 status; -if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); -return !!status; +return PMC_PWRGATE_STATE (tegra_pmc_readl (PWRGATE_STATUS ), id); }/**
@@ -257,34 +250,39 @@ int tegra_powergate_is_powered(int id)*/ int tegra_powergate_remove_clamping (int id) { -u32 mask; +u32 val, reg;if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ;/* -* On Tegra124 and later, the clamps for the GPU are controlled by a -* separate register (with different semantics). +* In most cases the bit for removing the IO clamping is the same as +* the powergate partition ID, however, this is not always the case. +* Furthermore, on Tegra124 and later, the clamps for the GPU are +* controlled by a separate register (with different semantics). The +* following case-statement handles these exceptions. */ -if (id ==TEGRA_POWERGATE_3D ) { + val =1 << id; + reg =REMOVE_CLAMPING ; + +switch (id) { +case TEGRA_POWERGATE_3D :if (pmc->soc->has_gpu_clamps) { -tegra_pmc_writel (0 ,GPU_RG_CNTRL ); -return 0 ; + val =0 ; + reg =GPU_RG_CNTRL ; } +break ; +case TEGRA_POWERGATE_VDEC : + val =REMOVE_CLAMPING_VDEC ; +break ; +case TEGRA_POWERGATE_PCIE : + val =REMOVE_CLAMPING_PCIE ; +break ; +default : +break ; } -/* -* Tegra 2 has a bug where PCIE and VDE clamping masks are -* swapped relatively to the partition ids -*/ -if (id ==TEGRA_POWERGATE_VDEC ) - mask = (1 <<TEGRA_POWERGATE_PCIE ); -else if (id ==TEGRA_POWERGATE_PCIE ) - mask = (1 <<TEGRA_POWERGATE_VDEC ); -else - mask = (1 << id); - -tegra_pmc_writel (mask,REMOVE_CLAMPING ); +tegra_pmc_writel (val, reg);return 0 ; }
Signature · 1 line
2.1.4
PATCH v3 8/19 soc: tegra: pmc: Clean-up PMC helper functions
Quoted 35 lines · Jon Hunter
The following clean-ups have been made to the PMC code:
1. Add a macro for determining the state of a PMC powergate
2. Use thereadx_poll_timeout()function instead of implementing a local polling loop with a timeout.
3. Use a case-statement in the function that removes the powergate clamp instead of multiple if-statements to improve readability.Signed-off-by Jon Hunter <[email protected]>--- drivers/soc/tegra/pmc.c | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 37 deletions(-)drivers/soc/tegra/pmc.c +3
-0@@ -28,6 +28,7 @@#include <linux/export.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -56,6 +57,8 @@#define PWRGATE_TOGGLE_START (1 << 8)#define REMOVE_CLAMPING 0x34 +#define REMOVE_CLAMPING_VDEC (1 << 3) +#define REMOVE_CLAMPING_PCIE (1 << 4)#define PWRGATE_STATUS 0x38
drivers/soc/tegra/pmc.c line 107
Quoted 4 lines
drivers/soc/tegra/pmc.c +1
-0@@ -101,6 +104,8 @@#define GPU_RG_CNTRL 0x2d4 +#define PMC_PWRGATE_STATE (val, id) (!!(val & BIT(id)))
I find double-negations very difficult to read. ((value & BIT(id)) != 0) is clearer in my opinion. Also I'd suggest status or value as the first parameter name, for consistency with other parts of the driver.
Quoted 46 lines
drivers/soc/tegra/pmc.c +7
-15+struct tegra_pmc_soc {unsigned int num_powergates;const char *const *powergates;@@ -177,15 +182,14 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)*/ static int tegra_powergate_set (int id,bool new_state,bool wait) { -unsigned long timeout; -bool status;int ret =0 ; +u32 val;mutex_lock (&pmc->powergates_lock); - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); + val =tegra_pmc_readl (PWRGATE_STATUS ); -if (status == new_state) { +if (PMC_PWRGATE_STATE (val, id) == new_state) {mutex_unlock (&pmc->powergates_lock);return 0 ; }@@ -193,17 +197,9 @@ static int tegra_powergate_set(int id, bool new_state, bool wait)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE );if (wait) { - timeout = jiffies +msecs_to_jiffies (100 ); - ret = -ETIMEDOUT ; - -while (time_before (jiffies, timeout)) { - status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); -if (status == new_state) { - ret =0 ; -break ; - } -udelay (10 ); - } + ret =readx_poll_timeout (tegra_pmc_readl,PWRGATE_STATUS , + val,PMC_PWRGATE_STATE (val, id) == new_state, +10 ,100000 ); }mutex_unlock (&pmc->powergates_lock);@@ -242,13 +238,10 @@ EXPORT_SYMBOL(tegra_powergate_power_off);*/
drivers/soc/tegra/pmc.c EXPORT_SYMBOL(tegra_powergate_power_off); · line 244
Quoted 10 lines
drivers/soc/tegra/pmc.c +1
-4int tegra_powergate_is_powered (int id) { -u32 status; -if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); -return !!status; +return PMC_PWRGATE_STATE (tegra_pmc_readl (PWRGATE_STATUS ), id);
I'd prefer to keep the two steps here. As a general rule I try never to mix reading or writing a register with other code on the same line.
drivers/soc/tegra/pmc.c int tegra_powergate_is_powered(int id) · line 253
Quoted 9 lines
drivers/soc/tegra/pmc.c +1
-1}/** @@ -257,34 +250,39 @@ int tegra_powergate_is_powered(int id)*/ int tegra_powergate_remove_clamping (int id) { -u32 mask; +u32 val, reg;
Side note: Please use value instead of val since that's the form used elsewhere in the driver. Also reg would be more appropriately called offset or similar because that's what it really is. It would also be more naturally an unsized type, such as unsigned int. But...
Quoted 39 lines
drivers/soc/tegra/pmc.c +21
-8if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ;/* -* On Tegra124 and later, the clamps for the GPU are controlled by a -* separate register (with different semantics). +* In most cases the bit for removing the IO clamping is the same as +* the powergate partition ID, however, this is not always the case. +* Furthermore, on Tegra124 and later, the clamps for the GPU are +* controlled by a separate register (with different semantics). The +* following case-statement handles these exceptions. */ -if (id ==TEGRA_POWERGATE_3D ) { + val =1 << id; + reg =REMOVE_CLAMPING ; + +switch (id) { +case TEGRA_POWERGATE_3D :if (pmc->soc->has_gpu_clamps) { -tegra_pmc_writel (0 ,GPU_RG_CNTRL ); -return 0 ; + val =0 ; + reg =GPU_RG_CNTRL ; } +break ; +case TEGRA_POWERGATE_VDEC : + val =REMOVE_CLAMPING_VDEC ; +break ; +case TEGRA_POWERGATE_PCIE : + val =REMOVE_CLAMPING_PCIE ; +break ; +default : +break ; } -/* -* Tegra 2 has a bug where PCIE and VDE clamping masks are -* swapped relatively to the partition ids
drivers/soc/tegra/pmc.c int tegra_powergate_is_powered(int id) · line 285
Quoted 10 lines
drivers/soc/tegra/pmc.c +1
-9-*/ -if (id ==TEGRA_POWERGATE_VDEC ) - mask = (1 <<TEGRA_POWERGATE_PCIE ); -else if (id ==TEGRA_POWERGATE_PCIE ) - mask = (1 <<TEGRA_POWERGATE_VDEC ); -else - mask = (1 << id); - -tegra_pmc_writel (mask,REMOVE_CLAMPING ); +tegra_pmc_writel (val, reg);
... to be honest, I think the previous code was more straightforward, so I'd prefer if you dropped this particular hunk.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/a6a93632/attachment.sig>
Jon Hunter
maintainerPATCH v3 8/19 soc: tegra: pmc: Clean-up PMC helper functions
Quoted 158 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 35 lines · Jon Hunter
The following clean-ups have been made to the PMC code:
1. Add a macro for determining the state of a PMC powergate
2. Use thereadx_poll_timeout()function instead of implementing a local polling loop with a timeout.
3. Use a case-statement in the function that removes the powergate clamp instead of multiple if-statements to improve readability.Signed-off-by Jon Hunter <[email protected]>--- drivers/soc/tegra/pmc.c | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 37 deletions(-)drivers/soc/tegra/pmc.c +3
-0@@ -28,6 +28,7 @@#include <linux/export.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -56,6 +57,8 @@#define PWRGATE_TOGGLE_START (1 << 8)#define REMOVE_CLAMPING 0x34 +#define REMOVE_CLAMPING_VDEC (1 << 3) +#define REMOVE_CLAMPING_PCIE (1 << 4)#define PWRGATE_STATUS 0x38drivers/soc/tegra/pmc.c line 107
Quoted 4 lines
drivers/soc/tegra/pmc.c +1
-0@@ -101,6 +104,8 @@#define GPU_RG_CNTRL 0x2d4 +#define PMC_PWRGATE_STATE (val, id) (!!(val & BIT(id)))I find double-negations very difficult to read. ((value & BIT(id)) != 0) is clearer in my opinion. Also I'd suggest status or value as the first parameter name, for consistency with other parts of the driver.
Quoted 46 lines
drivers/soc/tegra/pmc.c +7
-15+struct tegra_pmc_soc {unsigned int num_powergates;const char *const *powergates;@@ -177,15 +182,14 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)*/ static int tegra_powergate_set (int id,bool new_state,bool wait) { -unsigned long timeout; -bool status;int ret =0 ; +u32 val;mutex_lock (&pmc->powergates_lock); - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); + val =tegra_pmc_readl (PWRGATE_STATUS ); -if (status == new_state) { +if (PMC_PWRGATE_STATE (val, id) == new_state) {mutex_unlock (&pmc->powergates_lock);return 0 ; }@@ -193,17 +197,9 @@ static int tegra_powergate_set(int id, bool new_state, bool wait)tegra_pmc_writel (PWRGATE_TOGGLE_START | id,PWRGATE_TOGGLE );if (wait) { - timeout = jiffies +msecs_to_jiffies (100 ); - ret = -ETIMEDOUT ; - -while (time_before (jiffies, timeout)) { - status = !!(tegra_pmc_readl (PWRGATE_STATUS ) &BIT (id)); -if (status == new_state) { - ret =0 ; -break ; - } -udelay (10 ); - } + ret =readx_poll_timeout (tegra_pmc_readl,PWRGATE_STATUS , + val,PMC_PWRGATE_STATE (val, id) == new_state, +10 ,100000 ); }mutex_unlock (&pmc->powergates_lock);@@ -242,13 +238,10 @@ EXPORT_SYMBOL(tegra_powergate_power_off);*/ drivers/soc/tegra/pmc.c EXPORT_SYMBOL(tegra_powergate_power_off); · line 244
Quoted 10 lines
drivers/soc/tegra/pmc.c +1
-4int tegra_powergate_is_powered (int id) { -u32 status; -if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ; - status =tegra_pmc_readl (PWRGATE_STATUS ) & (1 << id); -return !!status; +return PMC_PWRGATE_STATE (tegra_pmc_readl (PWRGATE_STATUS ), id);I'd prefer to keep the two steps here. As a general rule I try never to mix reading or writing a register with other code on the same line.
drivers/soc/tegra/pmc.c int tegra_powergate_is_powered(int id) · line 253
Quoted 9 lines
drivers/soc/tegra/pmc.c +1
-1}/** @@ -257,34 +250,39 @@ int tegra_powergate_is_powered(int id)*/ int tegra_powergate_remove_clamping (int id) { -u32 mask; +u32 val, reg;Side note: Please use value instead of val since that's the form used elsewhere in the driver. Also reg would be more appropriately called offset or similar because that's what it really is. It would also be more naturally an unsized type, such as unsigned int. But...
Quoted 36 lines
drivers/soc/tegra/pmc.c +21
-5if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates)return -EINVAL ;/* -* On Tegra124 and later, the clamps for the GPU are controlled by a -* separate register (with different semantics). +* In most cases the bit for removing the IO clamping is the same as +* the powergate partition ID, however, this is not always the case. +* Furthermore, on Tegra124 and later, the clamps for the GPU are +* controlled by a separate register (with different semantics). The +* following case-statement handles these exceptions. */ -if (id ==TEGRA_POWERGATE_3D ) { + val =1 << id; + reg =REMOVE_CLAMPING ; + +switch (id) { +case TEGRA_POWERGATE_3D :if (pmc->soc->has_gpu_clamps) { -tegra_pmc_writel (0 ,GPU_RG_CNTRL ); -return 0 ; + val =0 ; + reg =GPU_RG_CNTRL ; } +break ; +case TEGRA_POWERGATE_VDEC : + val =REMOVE_CLAMPING_VDEC ; +break ; +case TEGRA_POWERGATE_PCIE : + val =REMOVE_CLAMPING_PCIE ; +break ; +default : +break ; }
Quoted 16 lines
drivers/soc/tegra/pmc.c int tegra_powergate_is_powered(int id) · line 285
Quoted 13 lines
drivers/soc/tegra/pmc.c +1
-12-/* -* Tegra 2 has a bug where PCIE and VDE clamping masks are -* swapped relatively to the partition ids -*/ -if (id ==TEGRA_POWERGATE_VDEC ) - mask = (1 <<TEGRA_POWERGATE_PCIE ); -else if (id ==TEGRA_POWERGATE_PCIE ) - mask = (1 <<TEGRA_POWERGATE_VDEC ); -else - mask = (1 << id); - -tegra_pmc_writel (mask,REMOVE_CLAMPING ); +tegra_pmc_writel (val, reg);... to be honest, I think the previous code was more straightforward, so I'd prefer if you dropped this particular hunk.
Ok, I will drop that part. Jon
Jon Hunter
maintainerPATCH v3 9/19 soc: tegra: pmc: Prepare for migrating to generic PM domains
In reply to Jon HunterWhen turning on and off power-domains it is necessary to enable specific clocks in order to de-assert/assert various reset signals to place the logic into a good state.
Currently, clocks required for turning on a power-domain are left enabled by the PMC driver because these clocks are also needed by the IP blocks to operated.
When migrating to the generic PM domain infrastructure, control of the powergates is abstracted from the drivers and therefore, it is desirable to disabled the clocks after turning on the power-domain and let the drivers enable the clocks it needs later. However, because this behaviour is different it is necessary to add a new API that drivers can use whether generic PM domains are used or not so that the behaviour is the same. Hence, add a new API that disables the clocks after turning on the power domain.
Similarly, for disabling a power-domain add a new API to abstract the management of the clocks so that drivers can be migrated to generic PM domains.
--- drivers/soc/tegra/pmc.c | 23 +++++++++++++++++++++++ include/soc/tegra/pmc.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+)
drivers/soc/tegra/pmc.c +23 -0
@@ -331,6 +331,29 @@ err_power: }EXPORT_SYMBOL (tegra_powergate_sequence_power_up); +int tegra_powergate_power_off_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +int ret; + + ret =clk_prepare_enable (clk); +if (ret) +return ret; + +usleep_range (10 ,20 ); + +reset_control_assert (rst); + +usleep_range (10 ,20 ); + +clk_disable_unprepare (clk); + +usleep_range (10 ,20 ); + +return tegra_powergate_power_off (id); +} +EXPORT_SYMBOL (tegra_powergate_power_off_legacy); +#ifdef CONFIG_SMP /** * tegra_get_cpu_powergate_id() - convert from CPU ID to partition ID
include/soc/tegra/pmc.h +30 -0
@@ -19,6 +19,8 @@#ifndef __SOC_TEGRA_PMC_H__#define __SOC_TEGRA_PMC_H__ +#include <linux/clk.h> +#include <linux/delay.h> #include <linux/reboot.h> #include <soc/tegra/pm.h>
@@ -117,6 +119,22 @@ int tegra_powergate_remove_clamping(int id);int tegra_powergate_sequence_power_up (int id,struct clk *clk,struct reset_control *rst); +static inline int tegra_powergate_power_on_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +int err =tegra_powergate_sequence_power_up (id, clk, rst); + +if (!err) { +usleep_range (10 ,20 ); +clk_disable_unprepare (clk); + } + +return err; +} + +int tegra_powergate_power_off_legacy (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);#else
@@ -155,6 +173,18 @@ static inline int tegra_io_rail_power_off(int id) {return -ENOSYS ; } + +static inline int tegra_powergate_power_on_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +return -ENOTSUPP ; +} + +static inline int tegra_powergate_power_off_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +return -ENOTSUPP ; +}#endif /* CONFIG_ARCH_TEGRA */ #endif /* __SOC_TEGRA_PMC_H__ */
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 10/19 drm/tegra: dc: Prepare for generic PM domains
In reply to Jon HunterAdd support to the tegra dc driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and pm_runtime_enable() should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() functions instead of the current tegra_powergate_sequence_power_up() and tegra_powergate_power_off(). The purpose of the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
3. The main difference between the tegra_powergate_sequence_power_up() API and the tegra_powergate_power_on_legacy() is that the clock used to enable the powergate is not kept enabled when using the tegra_powergate_power_on_legacy() API. Therefore, drivers must enable the clocks they need after calling tegra_powergate_power_on_legacy() and disable these clocks before calling tegra_powergate_power_off_legacy().
Helper functions have been added to the dc driver for handling power on and off the power-domains with and without generic PM domain support.
--- drivers/gpu/drm/tegra/dc.c | 94 +++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 35 deletions(-)
drivers/gpu/drm/tegra/dc.c +59 -35
@@ -10,6 +10,7 @@#include <linux/clk.h> #include <linux/debugfs.h> #include <linux/iommu.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <soc/tegra/pmc.h>
@@ -1880,6 +1881,49 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)return 0 ; } +static void tegra_dc_power_off (struct tegra_dc *dc) +{ +if (!dc->soc->has_powergate) +reset_control_assert (dc->rst); + +clk_disable_unprepare (dc->clk); + +if (dc->soc->has_powergate) { +if (pm_runtime_enabled (dc->dev)) +pm_runtime_put_sync (dc->dev); +else +tegra_powergate_power_off_legacy (dc->powergate, + dc->clk, dc->rst); + } +} + +static int tegra_dc_power_on (struct tegra_dc *dc) +{ +int err; + +if (dc->soc->has_powergate) { +if (pm_runtime_enabled (dc->dev)) + err =pm_runtime_get_sync (dc->dev); +else + err =tegra_powergate_power_on_legacy (dc->powergate, + dc->clk, dc->rst); +if (err <0 ) { +dev_err (dc->dev,"failed to power partition: %d\n" , + err); +return err; + } + } + + err =clk_prepare_enable (dc->clk); +if (err <0 ) +dev_err (dc->dev,"failed to enable clock: %d\n" , err); + +if (!dc->soc->has_powergate) +reset_control_deassert (dc->rst); + +return err; +} +static int tegra_dc_probe (struct platform_device *pdev) {unsigned long flags =HOST1X_SYNCPT_CLIENT_MANAGED ;
@@ -1917,35 +1961,6 @@ static int tegra_dc_probe(struct platform_device *pdev)return PTR_ERR (dc->rst); } -if (dc->soc->has_powergate) { -if (dc->pipe ==0 ) - dc->powergate =TEGRA_POWERGATE_DIS ; -else - dc->powergate =TEGRA_POWERGATE_DISB ; - - err =tegra_powergate_sequence_power_up (dc->powergate, dc->clk, - dc->rst); -if (err <0 ) { -dev_err (&pdev->dev,"failed to power partition: %d\n" , - err); -return err; - } - }else { - err =clk_prepare_enable (dc->clk); -if (err <0 ) { -dev_err (&pdev->dev,"failed to enable clock: %d\n" , - err); -return err; - } - - err =reset_control_deassert (dc->rst); -if (err <0 ) { -dev_err (&pdev->dev,"failed to deassert reset: %d\n" , - err); -return err; - } - } - regs =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); dc->regs =devm_ioremap_resource (&pdev->dev, regs);if (IS_ERR (dc->regs))
@@ -1961,6 +1976,20 @@ static int tegra_dc_probe(struct platform_device *pdev) dc->client.ops = &dc_client_ops; dc->client.dev = &pdev->dev; +if (dc->soc->has_powergate) { +if (dc->pipe ==0 ) + dc->powergate =TEGRA_POWERGATE_DIS ; +else + dc->powergate =TEGRA_POWERGATE_DISB ; + +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) +pm_runtime_enable (&pdev->dev); + } + + err =tegra_dc_power_on (dc); +if (err <0 ) +return err; + err =tegra_dc_rgb_probe (dc);if (err <0 && err != -ENODEV ) {dev_err (&pdev->dev,"failed to probe RGB output: %d\n" , err);
@@ -2003,12 +2032,7 @@ static int tegra_dc_remove(struct platform_device *pdev)return err; } -reset_control_assert (dc->rst); - -if (dc->soc->has_powergate) -tegra_powergate_power_off (dc->powergate); - -clk_disable_unprepare (dc->clk); +tegra_dc_power_off (dc);return 0 ; }
Signature · 1 line
2.1.4
PATCH v3 10/19 drm/tegra: dc: Prepare for generic PM domains
Quoted 10 lines · Jon Hunter
Add support to the tegra dc driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and
pm_runtime_enable()should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.
The device could be PM runtime enabled even if no power-domains property is set. Couldn't we check something more direct, like for example if the dev->pm_domain is non-NULL?
Perhaps it'd be worth converting the driver to use runtime PM first, and move all the powergate and clock handling into suspend/resume functions, and then we can conditionalize whether or not we call the legacy API iff dev->pm_domain == NULL?
Quoted 10 lines
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the
tegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()functions instead of the currenttegra_powergate_sequence_power_up()andtegra_powergate_power_off(). The purpose of thetegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
Quoted 7 lines
3. The main difference between the
tegra_powergate_sequence_power_up()API and thetegra_powergate_power_on_legacy()is that the clock used to enable the powergate is not kept enabled when using thetegra_powergate_power_on_legacy()API. Therefore, drivers must enable the clocks they need after callingtegra_powergate_power_on_legacy()and disable these clocks before callingtegra_powergate_power_off_legacy().
This seems like it should go into the previous patch. I'm not sure I understand why this is necessary. Wouldn't it be easier to update the drivers to properly cope with this? We'll need to move them to runtime PM at some point anyway, so if we do that first, we should be able to hide all these details within the driver's suspend/resume functions but without the need for the API churn here.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/0275a247/attachment.sig>
Jon Hunter
maintainerPATCH v3 10/19 drm/tegra: dc: Prepare for generic PM domains
Quoted 9 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 6 lines · Jon Hunter
Add support to the tegra dc driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
Quoted 8 lines
Quoted 4 lines
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and
pm_runtime_enable()should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.The device could be PM runtime enabled even if no power-domains property is set. Couldn't we check something more direct, like for example if the
dev->pm_domainis non-NULL?
Yes could do that instead.
Quoted 4 lines
Perhaps it'd be worth converting the driver to use runtime PM first, and move all the powergate and clock handling into suspend/resume functions, and then we can conditionalize whether or not we call the legacy API iff
dev->pm_domain== NULL?
May be that would be a cleaner transition than trying to do it all in one go.
Quoted 18 lines
Quoted 10 lines
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the
tegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()functions instead of the currenttegra_powergate_sequence_power_up()andtegra_powergate_power_off(). The purpose of thetegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.Quoted 7 lines
3. The main difference between the
tegra_powergate_sequence_power_up()API and thetegra_powergate_power_on_legacy()is that the clock used to enable the powergate is not kept enabled when using thetegra_powergate_power_on_legacy()API. Therefore, drivers must enable the clocks they need after callingtegra_powergate_power_on_legacy()and disable these clocks before callingtegra_powergate_power_off_legacy().
Quoted 6 lines
This seems like it should go into the previous patch. I'm not sure I understand why this is necessary. Wouldn't it be easier to update the drivers to properly cope with this? We'll need to move them to runtime PM at some point anyway, so if we do that first, we should be able to hide all these details within the driver's suspend/resume functions but without the need for the API churn here.
I will take a look at that. I was trying to get the clock handling in the driver to be consistent when generic power domains are used and when they are not. However, may be that is not a big deal.
Jon
PATCH v3 10/19 drm/tegra: dc: Prepare for generic PM domains
Quoted 22 lines · Jon Hunter
Quoted 9 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 6 lines · Jon Hunter
Add support to the tegra dc driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
Quoted 8 lines
Quoted 4 lines
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and
pm_runtime_enable()should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.The device could be PM runtime enabled even if no power-domains property is set. Couldn't we check something more direct, like for example if the
dev->pm_domainis non-NULL?Yes could do that instead.
Quoted 7 lines
Quoted 4 lines
Perhaps it'd be worth converting the driver to use runtime PM first, and move all the powergate and clock handling into suspend/resume functions, and then we can conditionalize whether or not we call the legacy API iff
dev->pm_domain== NULL?May be that would be a cleaner transition than trying to do it all in one go.
I have a couple of patches in my tree to do this for DRM as part of an effort to restore DPMS. It's fairly tricky to get right in DRM and requires all sorts of changes to the driver.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150728/f7735d82/attachment.sig>
Jon Hunter
maintainerPATCH v3 10/19 drm/tegra: dc: Prepare for generic PM domains
Quoted 7 lines · Thierry Reding
Quoted 2 lines · Jon Hunter
May be that would be a cleaner transition than trying to do it all in one go.
I have a couple of patches in my tree to do this for DRM as part of an effort to restore DPMS. It's fairly tricky to get right in DRM and requires all sorts of changes to the driver.
Hmmm ... I was just thinking about moving what is currently there in the pm-runtime helpers ...
drivers/gpu/drm/tegra/dc.c +59 -23
@@ -10,6 +10,7 @@#include <linux/clk.h> #include <linux/debugfs.h> #include <linux/iommu.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <soc/tegra/pmc.h>
@@ -1922,29 +1923,12 @@ static int tegra_dc_probe(struct platform_device *pdev) dc->powergate =TEGRA_POWERGATE_DIS ; else dc->powergate = TEGRA_POWERGATE_DISB; + } - err =tegra_powergate_sequence_power_up (dc->powergate, dc->clk, - dc->rst); -if (err <0 ) { -dev_err (&pdev->dev,"failed to power partition: %d\n" , - err); -return err; - } - }else { - err =clk_prepare_enable (dc->clk); -if (err <0 ) { -dev_err (&pdev->dev,"failed to enable clock: %d\n" , - err); -return err; - } +platform_set_drvdata (pdev, dc); - err =reset_control_deassert (dc->rst); -if (err <0 ) { -dev_err (&pdev->dev,"failed to deassert reset: %d\n" , - err); -return err; - } - } +pm_runtime_enable (&pdev->dev); +pm_runtime_get_sync (&pdev->dev); regs =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); dc->regs =devm_ioremap_resource (&pdev->dev, regs);
@@ -1978,8 +1962,6 @@ static int tegra_dc_probe(struct platform_device *pdev)if (!dc->syncpt)dev_warn (&pdev->dev,"failed to allocate syncpoint\n" ); -platform_set_drvdata (pdev, dc); -return 0 ; }
@@ -2003,6 +1985,17 @@ static int tegra_dc_remove(struct platform_device *pdev)return err; } +pm_runtime_put (&pdev->dev); + +return 0 ; +} + +#ifdef CONFIG_PM +static int tegra_dc_runtime_suspend (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct tegra_dc *dc =platform_get_drvdata (pdev); +reset_control_assert (dc->rst);if (dc->soc->has_powergate)
@@ -2013,11 +2006,54 @@ static int tegra_dc_remove(struct platform_device *pdev)return 0 ; } +static int tegra_dc_runtime_resume (struct device *dev) +{ +struct platform_device *pdev =to_platform_device (dev); +struct tegra_dc *dc =platform_get_drvdata (pdev); +int err; + +if (dc->soc->has_powergate) { + err =tegra_powergate_sequence_power_up (dc->powergate, dc->clk, + dc->rst); +if (err <0 ) { +dev_err (&pdev->dev,"failed to power partition: %d\n" , + err); +return err; + } + }else { + err =clk_prepare_enable (dc->clk); +if (err <0 ) { +dev_err (&pdev->dev,"failed to enable clock: %d\n" , + err); +return err; + } + + err =reset_control_deassert (dc->rst); +if (err <0 ) { +dev_err (&pdev->dev,"failed to deassert reset: %d\n" , + err); +return err; + } + } + +return 0 ; +} + +static struct dev_pm_ops tegra_dc_pm_ops = { +SET_RUNTIME_PM_OPS (tegra_dc_runtime_suspend, + tegra_dc_runtime_resume,NULL ) +}; +#define TEGRA_DC_PM_OPS (&tegra_dc_pm_ops) +#else +#define TEGRA_DC_PM_OPS NULL +#endif /* CONFIG_PM */ +struct platform_driver tegra_dc_driver = { .driver = { .name ="tegra-dc" , .owner =THIS_MODULE , .of_match_table = tegra_dc_of_match, + .pm =TEGRA_DC_PM_OPS , }, .probe = tegra_dc_probe, .remove = tegra_dc_remove,
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 11/19 PCI: tegra: Add support for generic PM domains
In reply to Jon HunterAdd support to the tegra PCI driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and pm_runtime_enable() should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() functions instead of the current tegra_powergate_sequence_power_up() and tegra_powergate_power_off(). The purpose of the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
3. The main difference between the tegra_powergate_sequence_power_up() API and the tegra_powergate_power_on_legacy() is that the clock used to enable the powergate is not kept enabled when using the tegra_powergate_power_on_legacy() API. Therefore, drivers must enable the clocks they need after calling tegra_powergate_power_on_legacy() and disable these clocks before calling tegra_powergate_power_off_legacy().
The helper functions for handling the powering on and off of the PCI controller have been updated to support generic PM domains. The tegra_pcie_power_off() was missing code to disable the clocks enabled in the tegra_pcie_power_on() function and so this has been added.
--- drivers/pci/host/pci-tegra.c | 49 +++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-)
drivers/pci/host/pci-tegra.c +37 -12
@@ -40,6 +40,7 @@#include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/sizes.h> #include <linux/slab.h>
@@ -908,19 +909,32 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)static void tegra_pcie_power_off (struct tegra_pcie *pcie) { +const struct tegra_pcie_soc_data *soc = pcie->soc_data;int err; -/* TODO: disable and unprepare clocks? */ - err =phy_power_off (pcie->phy);if (err <0 )dev_warn (pcie->dev,"failed to power off PHY: %d\n" , err);reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE ); +clk_disable_unprepare (pcie->pll_e); +if (soc->has_cml_clk) +clk_disable_unprepare (pcie->cml_clk); +clk_disable_unprepare (pcie->afi_clk); +clk_disable_unprepare (pcie->pex_clk); + +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_put_sync (pcie->dev); + }else { + err =tegra_powergate_power_off_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) +dev_warn (pcie->dev,"powergate power-down failed: %d\n" , err); err =regulator_bulk_disable (pcie->num_supplies, pcie->supplies);if (err <0 )
@@ -934,20 +948,28 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); - -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE );/* enable regulators */ err =regulator_bulk_enable (pcie->num_supplies, pcie->supplies);if (err <0 )dev_err (pcie->dev,"failed to enable regulators: %d\n" , err); - err =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_PCIE , - pcie->pex_clk, - pcie->pex_rst); -if (err) { -dev_err (pcie->dev,"powerup sequence failed: %d\n" , err); +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_get_sync (pcie->dev); + }else { + err =tegra_powergate_power_on_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) { +dev_err (pcie->dev,"powergate power-up failed: %d\n" , err); +return err; + } + + err =clk_prepare_enable (pcie->pex_clk); +if (err <0 ) { +dev_err (pcie->dev,"failed to enable PEX clock: %d\n" , err);return err; }
@@ -2001,6 +2023,9 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcibios_min_mem =0 ; +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) +pm_runtime_enable (&pdev->dev); + err =tegra_pcie_get_resources (pcie);if (err <0 ) {dev_err (&pdev->dev,"failed to request resources: %d\n" , err);
Signature · 1 line
2.1.4
PATCH v3 11/19 PCI: tegra: Add support for generic PM domains
Quoted 21 lines · Jon Hunter
Add support to the tegra PCI driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and
pm_runtime_enable()should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the
tegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()functions instead of the currenttegra_powergate_sequence_power_up()andtegra_powergate_power_off(). The purpose of thetegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
Quoted 7 lines
3. The main difference between the
tegra_powergate_sequence_power_up()API and thetegra_powergate_power_on_legacy()is that the clock used to enable the powergate is not kept enabled when using thetegra_powergate_power_on_legacy()API. Therefore, drivers must enable the clocks they need after callingtegra_powergate_power_on_legacy()and disable these clocks before callingtegra_powergate_power_off_legacy().
This is the same comment as in the DRM patch, and it applies to all devices that use powergating, so it should move into the preparatory patch rather than be repeated in all patches.
Quoted 87 lines
The helper functions for handling the powering on and off of the PCI controller have been updated to support generic PM domains. The
tegra_pcie_power_off()was missing code to disable the clocks enabled in thetegra_pcie_power_on()function and so this has been added.Signed-off-by Jon Hunter <[email protected]>--- drivers/pci/host/pci-tegra.c | 49 +++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-)drivers/pci/host/pci-tegra.c +27
-12@@ -40,6 +40,7 @@#include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -908,19 +909,32 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)static void tegra_pcie_power_off (struct tegra_pcie *pcie) { +const struct tegra_pcie_soc_data *soc = pcie->soc_data;int err; -/* TODO: disable and unprepare clocks? */ - err =phy_power_off (pcie->phy);if (err <0 )dev_warn (pcie->dev,"failed to power off PHY: %d\n" , err);reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE ); +clk_disable_unprepare (pcie->pll_e); +if (soc->has_cml_clk) +clk_disable_unprepare (pcie->cml_clk); +clk_disable_unprepare (pcie->afi_clk); +clk_disable_unprepare (pcie->pex_clk); + +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_put_sync (pcie->dev); + }else { + err =tegra_powergate_power_off_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) +dev_warn (pcie->dev,"powergate power-down failed: %d\n" , err); err =regulator_bulk_disable (pcie->num_supplies, pcie->supplies);if (err <0 )@@ -934,20 +948,28 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); - -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE );/* enable regulators */ err =regulator_bulk_enable (pcie->num_supplies, pcie->supplies);if (err <0 )dev_err (pcie->dev,"failed to enable regulators: %d\n" , err); - err =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_PCIE , - pcie->pex_clk, - pcie->pex_rst); -if (err) { -dev_err (pcie->dev,"powerup sequence failed: %d\n" , err); +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_get_sync (pcie->dev); + }else { + err =tegra_powergate_power_on_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) {
drivers/pci/host/pci-tegra.c static int tegra_pcie_power_on(struct tegra_pcie *pcie) · line 975
Quoted 10 lines
drivers/pci/host/pci-tegra.c +7
-0+dev_err (pcie->dev,"powergate power-up failed: %d\n" , err); +return err; + } + + err =clk_prepare_enable (pcie->pex_clk); +if (err <0 ) { +dev_err (pcie->dev,"failed to enable PEX clock: %d\n" , err);return err; }
Couldn't we simply move the above code into suspend/resume functions and then call pm_runtime_get() and pm_runtime_put() instead of tegra_pcie_power_on() and tegra_pcie_power_off()?
drivers/pci/host/pci-tegra.c static int tegra_pcie_probe(struct platform_device *pdev) · line 2027
Quoted 6 lines
drivers/pci/host/pci-tegra.c +2
-0@@ -2001,6 +2023,9 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcibios_min_mem =0 ; +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) +pm_runtime_enable (&pdev->dev);
Then of course we'd have to call pm_runtime_enable() irrespective of the property and check for the presence of power domain support via another method (like the dev->pm_domain field).
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/baca9294/attachment.sig>
Jon Hunter
maintainerPATCH v3 11/19 PCI: tegra: Add support for generic PM domains
Quoted 129 lines · Thierry Reding
* PGP Signed by an unknown key
Quoted 21 lines · Jon Hunter
Add support to the tegra PCI driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and
pm_runtime_enable()should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the
tegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()functions instead of the currenttegra_powergate_sequence_power_up()andtegra_powergate_power_off(). The purpose of thetegra_powergate_power_on_legacy()andtegra_powergate_power_off_legacy()APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.Quoted 7 lines
3. The main difference between the
tegra_powergate_sequence_power_up()API and thetegra_powergate_power_on_legacy()is that the clock used to enable the powergate is not kept enabled when using thetegra_powergate_power_on_legacy()API. Therefore, drivers must enable the clocks they need after callingtegra_powergate_power_on_legacy()and disable these clocks before callingtegra_powergate_power_off_legacy().This is the same comment as in the DRM patch, and it applies to all devices that use powergating, so it should move into the preparatory patch rather than be repeated in all patches.
Quoted 93 lines
The helper functions for handling the powering on and off of the PCI controller have been updated to support generic PM domains. The
tegra_pcie_power_off()was missing code to disable the clocks enabled in thetegra_pcie_power_on()function and so this has been added.Signed-off-by Jon Hunter <[email protected]>--- drivers/pci/host/pci-tegra.c | 49 +++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-)drivers/pci/host/pci-tegra.c +33
-12@@ -40,6 +40,7 @@#include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -908,19 +909,32 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)static void tegra_pcie_power_off (struct tegra_pcie *pcie) { +const struct tegra_pcie_soc_data *soc = pcie->soc_data;int err; -/* TODO: disable and unprepare clocks? */ - err =phy_power_off (pcie->phy);if (err <0 )dev_warn (pcie->dev,"failed to power off PHY: %d\n" , err);reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE ); +clk_disable_unprepare (pcie->pll_e); +if (soc->has_cml_clk) +clk_disable_unprepare (pcie->cml_clk); +clk_disable_unprepare (pcie->afi_clk); +clk_disable_unprepare (pcie->pex_clk); + +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_put_sync (pcie->dev); + }else { + err =tegra_powergate_power_off_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) +dev_warn (pcie->dev,"powergate power-down failed: %d\n" , err); err =regulator_bulk_disable (pcie->num_supplies, pcie->supplies);if (err <0 )@@ -934,20 +948,28 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)reset_control_assert (pcie->pcie_xrst);reset_control_assert (pcie->afi_rst); -reset_control_assert (pcie->pex_rst); - -tegra_powergate_power_off (TEGRA_POWERGATE_PCIE );/* enable regulators */ err =regulator_bulk_enable (pcie->num_supplies, pcie->supplies);if (err <0 )dev_err (pcie->dev,"failed to enable regulators: %d\n" , err); - err =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_PCIE , - pcie->pex_clk, - pcie->pex_rst); -if (err) { -dev_err (pcie->dev,"powerup sequence failed: %d\n" , err); +if (pm_runtime_enabled (pcie->dev)) { + err =pm_runtime_get_sync (pcie->dev); + }else { + err =tegra_powergate_power_on_legacy (TEGRA_POWERGATE_PCIE , + pcie->pex_clk, + pcie->pex_rst); + } + +if (err <0 ) { +dev_err (pcie->dev,"powergate power-up failed: %d\n" , err); +return err; + } + + err =clk_prepare_enable (pcie->pex_clk); +if (err <0 ) {
Quoted 8 lines
drivers/pci/host/pci-tegra.c static int tegra_pcie_power_on(struct tegra_pcie *pcie) · line 975
Quoted 4 lines
drivers/pci/host/pci-tegra.c +1
-0+dev_err (pcie->dev,"failed to enable PEX clock: %d\n" , err);return err; }Couldn't we simply move the above code into suspend/resume functions and then call
pm_runtime_get()andpm_runtime_put()instead oftegra_pcie_power_on()andtegra_pcie_power_off()?
Possibly, however, the tricky part here is the regulators. We cannot put the regulators into the runtime resume/suspend hooks, because this would mean that the regulators are turned on after and turned off before the power domain. We need to have the regulator on before turning on the power domain.
So there are two options here AFAICT:
1. Move everything above after the regulators are turned on in the runtime resume function.
2. Move handling of all the regulators in the generic power domain code.
What do you think?
Quoted 10 lines
drivers/pci/host/pci-tegra.c static int tegra_pcie_probe(struct platform_device *pdev) · line 2027
Quoted 6 lines
drivers/pci/host/pci-tegra.c +2
-0@@ -2001,6 +2023,9 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcibios_min_mem =0 ; +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) +pm_runtime_enable (&pdev->dev);Then of course we'd have to call
pm_runtime_enable()irrespective of the property and check for the presence of power domain support via another method (like thedev->pm_domainfield).
Ok.
Jon
Jon Hunter
maintainerPATCH v3 12/19 ata: ahci_tegra: Add support for generic PM domains
In reply to Jon HunterAdd support to the tegra AHCI driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and the variable has_genpd should be set. The ahci_platform_get_resources()/put_resources() calls the appropriate pm_runtime functions and so no further changes are needed for rpm support.
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() functions instead of the current tegra_powergate_sequence_power_up() and tegra_powergate_power_off(). The purpose of the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
3. The main difference between the tegra_powergate_sequence_power_up() API and the tegra_powergate_power_on_legacy() is that the clock used to enable the powergate is not kept enabled when using the tegra_powergate_power_on_legacy() API. Therefore, drivers must enable the clocks they need after calling tegra_powergate_power_on_legacy() and disable these clocks before calling tegra_powergate_power_off_legacy().
The helper functions for handling the powering on and off of the AHCI controller have been updated to support generic PM domains and the following changes have been made:
a). The clocks for the AHCI controller are managed by
the ahci_platform_enable_resources() and ahci_platform_disable_resources() and so the calls to clock_disable_unprepare() are not needed and can be removed.
b). The tegra->sata_rst is handled by the tegra powergate code and
so does not need to be controlled by the driver.
c). AHCI controller resets should be applied before the clocks are
disabled and so the call to ahci_platform_disable_resources() has been moved to after the reset assertion.
--- drivers/ata/ahci_tegra.c | 51 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 15 deletions(-)
drivers/ata/ahci_tegra.c +36 -15
@@ -23,6 +23,7 @@#include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h>
@@ -108,6 +109,7 @@ struct tegra_ahci_priv {/* Needs special handling, cannot use ahci_platform */ struct clk *sata_clk;struct regulator_bulk_data supplies[5 ]; +bool has_genpd; };static int tegra_ahci_power_on (struct ahci_host_priv *hpriv)
@@ -120,11 +122,13 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)if (ret)return ret; - ret =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_SATA , - tegra->sata_clk, - tegra->sata_rst); -if (ret) - goto disable_regulators; +if (!tegra->has_genpd) { + ret =tegra_powergate_power_on_legacy (TEGRA_POWERGATE_SATA , + tegra->sata_clk, + tegra->sata_rst); +if (ret) + goto disable_regulators; + }reset_control_assert (tegra->sata_oob_rst);reset_control_assert (tegra->sata_cold_rst);
@@ -139,9 +143,8 @@ static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)return 0 ; disable_power: -clk_disable_unprepare (tegra->sata_clk); - -tegra_powergate_power_off (TEGRA_POWERGATE_SATA ); +tegra_powergate_power_off_legacy (TEGRA_POWERGATE_SATA , tegra->sata_clk, + tegra->sata_rst); disable_regulators:regulator_bulk_disable (ARRAY_SIZE (tegra->supplies), tegra->supplies);
@@ -149,20 +152,27 @@ disable_regulators:return ret; } -static void tegra_ahci_power_off (struct ahci_host_priv *hpriv) +static int tegra_ahci_power_off (struct ahci_host_priv *hpriv) {struct tegra_ahci_priv *tegra = hpriv->plat_data; +int ret; -ahci_platform_disable_resources (hpriv); - -reset_control_assert (tegra->sata_rst);reset_control_assert (tegra->sata_oob_rst);reset_control_assert (tegra->sata_cold_rst); -clk_disable_unprepare (tegra->sata_clk); -tegra_powergate_power_off (TEGRA_POWERGATE_SATA ); +ahci_platform_disable_resources (hpriv); + +if (!tegra->has_genpd) { + ret =tegra_powergate_power_off_legacy (TEGRA_POWERGATE_SATA , + tegra->sata_clk, + tegra->sata_rst); +if (ret) +return ret; + }regulator_bulk_disable (ARRAY_SIZE (tegra->supplies), tegra->supplies); + +return 0 ; }static int tegra_ahci_controller_init (struct ahci_host_priv *hpriv)
@@ -263,7 +273,15 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)static void tegra_ahci_controller_deinit (struct ahci_host_priv *hpriv) { -tegra_ahci_power_off (hpriv); +struct tegra_ahci_priv *tegra = hpriv->plat_data; +int ret; + + ret =tegra_ahci_power_off (hpriv); + +if (ret) +dev_err (&tegra->pdev->dev, +"failed to power off AHCI controller: %d\n" , ret); + }static void tegra_ahci_host_stop (struct ata_host *host)
@@ -356,6 +374,9 @@ static int tegra_ahci_probe(struct platform_device *pdev)return ret; } +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) + tegra->has_genpd = true; + ret =tegra_ahci_controller_init (hpriv);if (ret)return ret;
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 13/19 drm/tegra: gr3d: Add support for generic PM domains
In reply to Jon HunterAdd support to the tegra gr3d driver for generic PM domains. However, to ensure backward compatibility with older device tree blobs ensure that the driver can work with or without generic PM domains. In order to migrate to generic PM domain infrastructure the necessary changes are:
1. If the "power-domains" property is present in the DT device node then generic PM domains is supported and pm_runtime_enable() should be called for the device. Furthermore, the enabling and disabling of the power-domain is handled via calling pm_runtime_get/put, respectively.
2. To ensure that clocks are managed consistently when generic PM domains are used and are not used, drivers should be migrated to use the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() functions instead of the current tegra_powergate_sequence_power_up() and tegra_powergate_power_off(). The purpose of the tegra_powergate_power_on_legacy() and tegra_powergate_power_off_legacy() APIs is to mimick the behaviour of the tegra generic power-domain code, such that if generic power domains are not supported the functionality is the same.
3. The main difference between the tegra_powergate_sequence_power_up() API and the tegra_powergate_power_on_legacy() is that the clock used to enable the powergate is not kept enabled when using the tegra_powergate_power_on_legacy() API. Therefore, drivers must enable the clocks they need after calling tegra_powergate_power_on_legacy() and disable these clocks before calling tegra_powergate_power_off_legacy().
Helper functions have been added to the gr3d driver for handling power on and off the power-domains with and without generic PM domain support.
--- drivers/gpu/drm/tegra/gr3d.c | 57 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-)
drivers/gpu/drm/tegra/gr3d.c +50 -7
@@ -11,6 +11,7 @@#include <linux/host1x.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <soc/tegra/pmc.h>
@@ -238,6 +239,32 @@ static const u32 gr3d_addr_regs[] = { GR3D_GLOBAL_SAMP23SURFADDR(15), }; +static int gr3d_powergate_on (struct platform_device *pdev,int powergate_id, +struct clk *clk,struct reset_control *rst) +{ +int err; + +if (pm_runtime_enabled (&pdev->dev)) + err =pm_runtime_get_sync (&pdev->dev); +else + err =tegra_powergate_power_on_legacy (powergate_id, + clk, rst); +return err; +} + +static int gr3d_powergate_off (struct platform_device *pdev,int powergate_id, +struct clk *clk,struct reset_control *rst) +{ +int err; + +if (pm_runtime_enabled (&pdev->dev)) + err =pm_runtime_put_sync (&pdev->dev); +else + err =tegra_powergate_power_off_legacy (powergate_id, + clk, rst); +return err; +} +static int gr3d_probe (struct platform_device *pdev) {struct device_node *np = pdev->dev.of_node;
@@ -281,22 +308,37 @@ static int gr3d_probe(struct platform_device *pdev) } } - err =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_3D , gr3d->clk, - gr3d->rst); +if (of_property_read_bool (pdev->dev.of_node,"power-domains" )) +pm_runtime_enable (&pdev->dev); + + err =gr3d_powergate_on (pdev,TEGRA_POWERGATE_3D , gr3d->clk, gr3d->rst);if (err <0 ) {dev_err (&pdev->dev,"failed to power up 3D unit\n" );return err; } + err =clk_prepare_enable (gr3d->clk); +if (err <0 ) { +dev_err (&pdev->dev,"failed to enable 3D unit clock\n" ); +return err; + } +if (gr3d->clk_secondary) { - err =tegra_powergate_sequence_power_up (TEGRA_POWERGATE_3D1 , - gr3d->clk_secondary, - gr3d->rst_secondary); + err =gr3d_powergate_on (pdev,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 =clk_prepare_enable (gr3d->clk_secondary); +if (err <0 ) { +dev_err (&pdev->dev, +"failed to enable secondary 3D unit clock\n" ); +return err; + } }INIT_LIST_HEAD (&gr3d->client.base.list);
@@ -338,12 +380,13 @@ static int gr3d_remove(struct platform_device *pdev) }if (gr3d->clk_secondary) { -tegra_powergate_power_off (TEGRA_POWERGATE_3D1 );clk_disable_unprepare (gr3d->clk_secondary); +gr3d_powergate_off (pdev,TEGRA_POWERGATE_3D1 , + gr3d->clk_secondary, gr3d->rst_secondary); } -tegra_powergate_power_off (TEGRA_POWERGATE_3D );clk_disable_unprepare (gr3d->clk); +gr3d_powergate_off (pdev,TEGRA_POWERGATE_3D , gr3d->clk, gr3d->rst);return 0 ; }
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 14/19 Documentation: DT: bindings: Add power domain info for NVIDIA PMC
In reply to Jon HunterAdd power-domain binding documentation for the NVIDIA PMC driver in order to support generic power-domains.
--- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+)
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +99 -0
@@ -1,5 +1,7 @@ NVIDIA Tegra Power Management Controller (PMC) +== Power Management Controller Node == + The PMC block interacts with an external Power Management Unit. The PMC mostly controls the entry and exit of the system from different sleep modes. It provides power-gating controllers for SoC and CPU power-islands.
@@ -68,6 +70,10 @@ Optional properties for hardware-triggered thermal reset (inside 'i2c-thermtrip' Defaults to 0. Valid values are described in section 12.5.2 "Pinmux Support" of the Tegra4 Technical Reference Manual. +Optional nodes: +- pm-domains : This node contains a hierarchy of PM domain nodes, which should + match the power-domains on the Tegra SoC. + Example: / SoC dts including file
@@ -113,3 +119,96 @@ pmc at 7000f400 { }; ... }; + + +== PM Domain Nodes == + +Each of the PM domain nodes represents a power-domain on the Tegra SoC +that can be power-gated by the PMC and should be named appropriately. + +Required properties: + - clocks: Must contain an entry for each clock required by + the PMC for controlling a power-gate. See + ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each reset required by + the PMC for controlling a power-gate. See + ../reset/reset.txt for details. + - nvidia,powergate: Integer cell that contains an identifier for the + PMC power-gate that is associated with the + power-domain. Please refer to the Tegra TRM for + more details. + - #power-domain-cells: Must be 0. + +Optional properties: + - nvidia,swgroups: Provides details of the software groups that are + associated with a specific power-domain. The + software group specifier consists of a phandle + pointing to the memory controller and a list of + one or more integer cells for each software group + associated with the power domain. The length of + the list of integer cells is specified by + #nvidia,swgroup-cells. + - #nvidia,swgroup-cells: Must be 1 or more. See nvidia,swgroups for + more details. + +Example: + + pmc at 0,7000e400 { + compatible = "nvidia,tegra124-pmc"; + reg = <0x0 0x7000e400 0x0 0x400>; + clocks = <&tegra_car TEGRA124_CLK_PCLK>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; + + pm-domains { + + ... + + pd_sor: sor-power-domain { + clocks = <&tegra_car TEGRA124_CLK_SOR0>, + <&tegra_car TEGRA124_CLK_DSIA>, + <&tegra_car TEGRA124_CLK_DSIB>, + <&tegra_car TEGRA124_CLK_HDMI>, + <&tegra_car TEGRA124_CLK_MIPI_CAL>, + <&tegra_car TEGRA124_CLK_DPAUX>; + resets = <&tegra_car 182>, + <&tegra_car 48>, + <&tegra_car 82>, + <&tegra_car 51>, + <&tegra_car 56>; + nvidia,powergate = <TEGRA_POWERGATE_SOR>; + #power-domain-cells = <0>; + + pd_dc: dc-power-domain { + clocks = <&tegra_car TEGRA124_CLK_DISP1>; + resets = <&tegra_car 27>; + nvidia,powergate = <TEGRA_POWERGATE_DIS>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_DC>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + + pd_dcb: dcb-power-domain { + clocks = <&tegra_car TEGRA124_CLK_DISP2>; + resets = <&tegra_car 26>; + nvidia,powergate = <TEGRA_POWERGATE_DISB>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_DCB>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + }; + }; + }; + }; + }; + + +== PM Domain Consumers == + +Hardware blocks belonging to a PM domain should contain a "power-domains" +property that is a phandle pointing to the corresponding PM domain node. + +Example: + + sor at 0,54540000 { + ... + power-domains = <&pd_sor>; + ... + };
Signature · 1 line
2.1.4
PATCH v3 14/19 Documentation: DT: bindings: Add power domain info for NVIDIA PMC
Quoted 21 lines · Jon Hunter
Add power-domain binding documentation for the NVIDIA PMC driver in order to support generic power-domains.
Signed-off-by Jon Hunter <[email protected]>--- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+)Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +2
-0@@ -1,5 +1,7 @@ NVIDIA Tegra Power Management Controller (PMC) +== Power Management Controller Node == + The PMC block interacts with an external Power Management Unit. The PMC mostly controls the entry and exit of the system from different sleep modes. It provides power-gating controllers for SoC and CPU power-islands.
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt Optional properties for hardware-triggered thermal reset (inside 'i2c-thermtrip' · line 75
Quoted 6 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +3
-0@@ -68,6 +70,10 @@ Optional properties for hardware-triggered thermal reset (inside 'i2c-thermtrip' Defaults to 0. Valid values are described in section 12.5.2 "Pinmux Support" of the Tegra4 Technical Reference Manual. +Optional nodes: +- pm-domains : This node contains a hierarchy of PM domain nodes, which should + match the power-domains on the Tegra SoC.
Perhaps call this simply power-domains to match the property name in consumer nodes?
Quoted 16 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +9
-0+ Example: / SoC dts including file@@ -113,3 +119,96 @@ pmc at 7000f400 { }; }; + + +== PM Domain Nodes == + +Each of the PM domain nodes represents a power-domain on the Tegra SoC +that can be power-gated by the PMC and should be named appropriately. + +Required properties:
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt pmc at 7000f400 { · line 139
Quoted 10 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +10
-0+ - clocks: Must contain an entry for each clock required by + the PMC for controlling a power-gate. See + ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each reset required by + the PMC for controlling a power-gate. See + ../reset/reset.txt for details. + - nvidia,powergate: Integer cell that contains an identifier for the + PMC power-gate that is associated with the + power-domain. Please refer to the Tegra TRM for + more details.
I find this really difficult to read. Can we use a more conventional format such as:
- clocks: Must contain...
... for details.
?
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt pmc at 7000f400 { · line 152
Quoted 13 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +13
-0+ - #power-domain-cells: Must be 0. + +Optional properties: + - nvidia,swgroups: Provides details of the software groups that are + associated with a specific power-domain. The + software group specifier consists of a phandle + pointing to the memory controller and a list of + one or more integer cells for each software group + associated with the power domain. The length of + the list of integer cells is specified by + #nvidia,swgroup-cells. + - #nvidia,swgroup-cells: Must be 1 or more. See nvidia,swgroups for + more details.
That's not how this usually works. #*-cells properties determine the number of cells required per <phandle spec> pair. So I think this should be more along the lines of:
- #nvidia,swgroups: A list of software groups associated with a specific power domain. This consists of a list of phandle and SWGROUP ID pairs, where the phandle points to the memory controller node.
- #nvidia,swgroup-cells: Must be 1. The single cell in the specifier denotes the ID of the software group.
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt pmc at 7000f400 { · line 162
Quoted 10 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +10
-0+ +Example: + + pmc at 0,7000e400 { + compatible = "nvidia,tegra124-pmc"; + reg = <0x0 0x7000e400 0x0 0x400>; + clocks = <&tegra_car TEGRA124_CLK_PCLK>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; + + pm-domains {
power-domains would convey more clearly what this is about.
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt pmc at 7000f400 { · line 166
Quoted 3 lines
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +3
-0+ ... + + pd_sor: sor-power-domain {
I don't think it's useful to repeat the -power-domain suffix for all these nodes.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/9a76a930/attachment.sig>
Jon Hunter
maintainerPATCH v3 15/19 soc: tegra: pmc: Add generic PM domain support
In reply to Jon HunterAdds generic PM support to the PMC driver where the PM domains are populated from device-tree and the PM domain consumer devices are bound to their relevant PM domains via device-tree as well.
Update the tegra_powergate_power_on_legacy/off_legacy() APIs so that internally they call the same tegra_powergate_xxx functions that are used by the tegra generic power domain code for consistency.
This is based upon work by Thierry Reding <[email protected]> and Vince Hsu <[email protected]>.
--- drivers/soc/tegra/pmc.c | 545 +++++++++++++++++++++++++++- include/dt-bindings/power/tegra-powergate.h | 42 +++ include/soc/tegra/pmc.h | 52 +-- 3 files changed, 580 insertions(+), 59 deletions(-) create mode 100644 include/dt-bindings/power/tegra-powergate.h
drivers/soc/tegra/pmc.c +533 -12
@@ -19,8 +19,11 @@#define pr_fmt (fmt) "tegra-pmc: " fmt +#include <dt-bindings/power/tegra-powergate.h> +#include <linux/kernel.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/clk/tegra.h> #include <linux/debugfs.h> #include <linux/delay.h>
@@ -30,17 +33,24 @@#include <linux/io.h> #include <linux/iopoll.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/reboot.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <soc/tegra/common.h> #include <soc/tegra/fuse.h> +#include <soc/tegra/mc.h> #include <soc/tegra/pmc.h> +#define PMC_POWERGATE_ARRAY_MAX 10 +#define PMC_CNTRL 0x0#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10)/* sys clk polarity */ #define PMC_CNTRL_SYSCLK_OE (1 << 11)/* system clock enable */
@@ -106,6 +116,22 @@#define PMC_PWRGATE_STATE (val, id) (!!(val & BIT(id))) +struct tegra_powergate { +struct generic_pm_domain genpd; +struct tegra_pmc *pmc; +struct tegra_mc *mc; +unsigned int id; +struct list_head node; +struct device_node *of_node; +struct regulator *vdd; +struct clk **clks; +struct reset_control **resets; +const struct tegra_mc_flush **flushes; +u32 num_clks; +u32 num_resets; +u32 num_flushes; +}; +struct tegra_pmc_soc {unsigned int num_powergates;const char *const *powergates;
@@ -118,8 +144,10 @@ struct tegra_pmc_soc {/** * struct tegra_pmc - NVIDIA Tegra PMC +* @dev: pointer to parent device * @base: pointer to I/O remapped register region * @clk: pointer to pclk clock +* @soc: SoC-specific data * @rate: currently configured rate of pclk * @suspend_mode: lowest suspend mode available * @cpu_good_time: CPU power good time (in microseconds)
@@ -133,6 +161,7 @@ struct tegra_pmc_soc {* @cpu_pwr_good_en: CPU power good signal is enabled * @lp0_vec_phys: physical base address of the LP0 warm boot code * @lp0_vec_size: size of the LP0 warm boot code +* @powergates_list: list of power gates * @powergates_lock: mutex for power gate register access */ struct tegra_pmc {
@@ -157,6 +186,7 @@ struct tegra_pmc {u32 lp0_vec_phys;u32 lp0_vec_size; +struct list_head powergates_list;struct mutex powergates_lock; };
@@ -165,6 +195,12 @@ static struct tegra_pmc *pmc = &(struct tegra_pmc) { .suspend_mode = TEGRA_SUSPEND_NONE, }; +static inline struct tegra_powergate * +to_powergate (struct generic_pm_domain *domain) +{ + return container_of(domain, struct tegra_powergate, genpd); +} +static u32 tegra_pmc_readl (unsigned long offset) {return readl (pmc->base + offset);
@@ -175,6 +211,37 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)writel (value, pmc->base + offset); } +static int tegra_powergate_get_regulator (struct tegra_powergate *powergate) +{ +struct platform_device *pdev; + +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return -EINVAL ; + + pdev =of_find_device_by_node (powergate->of_node); +if (!pdev) +return -EINVAL ; + + powergate->vdd =devm_regulator_get_optional (&pdev->dev,"vdd" ); +if (IS_ERR (powergate->vdd)) +return PTR_ERR (powergate->vdd); + +return 0 ; +} + +static int tegra_powergate_enable_regulator (struct tegra_powergate *powergate) +{ +int ret =0 ; + +if (IS_ERR_OR_NULL (powergate->vdd)) + ret =tegra_powergate_get_regulator (powergate); + +if (!ret) + ret =regulator_enable (powergate->vdd); + +return ret; +} +/** * tegra_powergate_set() - set the state of a partition * @id: partition ID
@@ -207,6 +274,215 @@ static int tegra_powergate_set(int id, bool new_state, bool wait)return ret; } +static int tegra_powergate_enable (struct tegra_powergate *powergate, +bool enable) +{ +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return tegra_powergate_set (powergate->id, enable, true); + +if (enable) +return tegra_powergate_enable_regulator (powergate); +else +return regulator_disable (powergate->vdd); +} + +static bool tegra_powergate_is_supported (int id) +{ +switch (id) { +case TEGRA_POWERGATE_CPU : +case TEGRA_POWERGATE_CPU1 : +case TEGRA_POWERGATE_CPU2 : +case TEGRA_POWERGATE_CPU3 : +case TEGRA_POWERGATE_CPU0 : +case TEGRA_POWERGATE_C0NC : +case TEGRA_POWERGATE_IRAM : +return false; +default : +return true; + } +} + +static bool _tegra_powergate_is_powered (struct tegra_powergate *powergate) +{ +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return tegra_powergate_is_powered (powergate->id); + +if (IS_ERR (powergate->vdd)) +return false; +else +return regulator_is_enabled (powergate->vdd); +} + +static void tegra_powergate_disable_clocks (struct tegra_powergate *powergate) +{ +int i; + +for (i =0 ; i < powergate->num_clks; i++) +clk_disable_unprepare (powergate->clks[i]); +} + +static int tegra_powergate_enable_clocks (struct tegra_powergate *powergate) +{ +int err, i; + +for (i =0 ; i < powergate->num_clks; i++) { + err =clk_prepare_enable (powergate->clks[i]); +if (err) + goto out; + } + +return 0 ; + +out: +while (i--) +clk_disable_unprepare (powergate->clks[i]); + +return err; +} + +static int tegra_powergate_mc_flush (struct tegra_powergate *powergate, +bool enable) +{ +int i, err; + +for (i =0 ; i < powergate->num_flushes; i++) { + err =tegra_mc_flush (powergate->mc, powergate->flushes[i], + enable); +if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_reset_assert (struct tegra_powergate *powergate) +{ +int err, i; + +for (i =0 ; i < powergate->num_resets; i++) { + err =reset_control_assert (powergate->resets[i]); +if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_reset_deassert (struct tegra_powergate *powergate) +{ +int err, i; + +for (i =0 ; i < powergate->num_resets; i++) { + err =reset_control_deassert (powergate->resets[i]); +if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_seq_power_up (struct tegra_powergate *powergate) +{ +int err; + + err =tegra_powergate_reset_assert (powergate); +if (err) + goto out; +udelay (10 ); + + err =tegra_powergate_enable (powergate, true); +if (err <0 ) + goto out; +udelay (10 ); + + err =tegra_powergate_enable_clocks (powergate); +if (err) + goto out; +udelay (10 ); + +tegra_powergate_remove_clamping (powergate->id); + +udelay (10 ); + + err =tegra_powergate_reset_deassert (powergate); +if (err) + goto out; +udelay (10 ); + + err =tegra_powergate_mc_flush (powergate, false); +if (err) + goto out; +udelay (10 ); + +tegra_powergate_disable_clocks (powergate); + +return 0 ; + +out: +return err; +} + +static int tegra_powergate_seq_power_down (struct tegra_powergate *powergate) +{ +int err; + + err =tegra_powergate_enable_clocks (powergate); +if (err) + goto out; +udelay (10 ); + + err =tegra_powergate_mc_flush (powergate, true); +if (err) + goto out; +udelay (10 ); + + err =tegra_powergate_reset_assert (powergate); +if (err) + goto out; +udelay (10 ); + +tegra_powergate_disable_clocks (powergate); + +udelay (10 ); + + err =tegra_powergate_enable (powergate, false); +if (err) + goto out; + +return 0 ; + +out: +return err; +} + +static int tegra_genpd_power_on (struct generic_pm_domain *domain) +{ +struct tegra_powergate *powergate =to_powergate (domain); +struct tegra_pmc *pmc = powergate->pmc; +int err; + + err =tegra_powergate_seq_power_up (powergate); + +if (err) +dev_err (pmc->dev,"Failed to turn on PM Domain (%d)\n" , err); + +return 0 ; +} + +static int tegra_genpd_power_off (struct generic_pm_domain *domain) +{ +struct tegra_powergate *powergate =to_powergate (domain); +struct tegra_pmc *pmc = powergate->pmc; +int err; + + err =tegra_powergate_seq_power_down (powergate); + +if (err) +dev_err (pmc->dev,"Failed to turn off PM Domain (%d)\n" , err); + +return err; +} +/** * tegra_powergate_power_on() - power on partition * @id: partition ID
@@ -247,6 +523,8 @@ int tegra_powergate_is_powered(int id)/** * tegra_powergate_remove_clamping() - remove power clamps for partition * @id: partition ID +* +* TODO: make this function static once we get rid of all outside callers */ int tegra_powergate_remove_clamping (int id) {
@@ -331,26 +609,41 @@ err_power: }EXPORT_SYMBOL (tegra_powergate_sequence_power_up); -int tegra_powergate_power_off_legacy (int id,struct clk *clk, -struct reset_control *rst) +int tegra_powergate_power_on_legacy (int id,struct clk *clk, +struct reset_control *rst) { -int ret; +struct tegra_powergate powergate; - ret =clk_prepare_enable (clk); -if (ret) -return ret; +if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) +return -EINVAL ; -usleep_range (10 ,20 ); + powergate.id = id; + powergate.clks = &clk; + powergate.resets = &rst; + powergate.num_clks =1 ; + powergate.num_resets =1 ; + powergate.num_flushes =0 ; -reset_control_assert (rst); +return tegra_powergate_seq_power_up (&powergate); +} +EXPORT_SYMBOL (tegra_powergate_power_on_legacy); -usleep_range (10 ,20 ); +int tegra_powergate_power_off_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +struct tegra_powergate powergate; -clk_disable_unprepare (clk); +if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) +return -EINVAL ; -usleep_range (10 ,20 ); + powergate.id = id; + powergate.clks = &clk; + powergate.resets = &rst; + powergate.num_clks =1 ; + powergate.num_resets =1 ; + powergate.num_flushes =0 ; -return tegra_powergate_power_off (id); +return tegra_powergate_seq_power_down (&powergate); }EXPORT_SYMBOL (tegra_powergate_power_off_legacy);
@@ -493,6 +786,228 @@ static int tegra_powergate_debugfs_init(void)return 0 ; } +static int tegra_powergate_of_get_clks (struct device *dev, +struct tegra_powergate *powergate) +{ +struct clk *clks[PMC_POWERGATE_ARRAY_MAX ]; +int i =0 ; + +for (i =0 ; i <PMC_POWERGATE_ARRAY_MAX ; i++) { + clks[i] =of_clk_get (powergate->of_node, i); +if (IS_ERR (clks[i])) { +if (PTR_ERR (clks[i]) != -ENOENT ) +return PTR_ERR (clks[i]); +break ; + } + } + +if (PTR_ERR (clks[i]) != -ENOENT ) { +dev_err (dev,"unsupported number of clocks defined\n" ); +return -EINVAL ; + } + + powergate->clks =devm_kcalloc (dev, i,sizeof (*clks),GFP_KERNEL ); +if (!powergate->clks) +return -ENOMEM ; + +memcpy (powergate->clks, clks,sizeof (*clks) * i); + powergate->num_clks = i; + +return 0 ; +} + +static int tegra_powergate_of_get_resets (struct device *dev, +struct tegra_powergate *powergate) +{ +struct reset_control *rsts[PMC_POWERGATE_ARRAY_MAX ]; +int i =0 ; + +for (i =0 ; i <PMC_POWERGATE_ARRAY_MAX ; i++) { + rsts[i] =of_reset_control_get_by_index (powergate->of_node, i); +if (IS_ERR (rsts[i])) { +if (PTR_ERR (rsts[i]) != -ENOENT ) +return PTR_ERR (rsts[i]); +break ; + } + } + +if (PTR_ERR (rsts[i]) != -ENOENT ) { +dev_err (dev,"unsupported number of resets defined\n" ); +return -EINVAL ; + } + + powergate->resets =devm_kcalloc (dev, i,sizeof (*rsts),GFP_KERNEL ); +if (!powergate->resets) +return -ENOMEM ; + +memcpy (powergate->resets, rsts,sizeof (*rsts) * i); + powergate->num_resets = i; + +return 0 ; +} + +static int tegra_powergate_of_get_mc_flush (struct device *dev, +struct tegra_powergate *powergate) +{ +struct platform_device *pdev; +struct of_phandle_args args; +int i =0 , ret =0 ; + + ret =of_parse_phandle_with_args (dev->of_node,"nvidia-swgroups" , +"#nvidia,swgroup-cells" ,0 , &args); +if (ret <0 ) { +/* +* The nvidia-swgroups property is +* optional and so if missing simply return. +*/ +if (ret == -ENOENT ) +return 0 ; + +dev_err (dev,"nvidia-swgroups property invalid for %s (%d)\n" , + powergate->of_node->name, ret); +return ret; + } + + powergate->flushes =devm_kcalloc (dev, args.args_count, +sizeof (powergate->flushes), +GFP_KERNEL ); +if (!powergate->flushes) { +dev_err (dev,"failed to allocate memory for powergate flush\n" ); +return -ENOMEM ; + } + + pdev =of_find_device_by_node (args.np); +if (!pdev) +return -ENODEV ; + + powergate->mc =platform_get_drvdata (pdev); +if (!powergate->mc) +return -EINVAL ; + +for (i =0 ; i < args.args_count; i++) { + powergate->flushes[i] =tegra_mc_flush_get (powergate->mc, + args.args[i]); +if (!powergate->flushes[i]) +return -EINVAL ; + } + + powergate->num_flushes = args.args_count; + +return 0 ; +} + +static int tegra_powergate_init_powerdomain (struct tegra_pmc *pmc, +struct device_node *np, +struct tegra_powergate *pg) +{ +bool off; +int err; + + err =of_property_read_u32 (np,"nvidia,powergate" , &pg->id); +if (err) { +dev_err (pmc->dev,"no powergate ID for domain\n" ); + goto err; + } + +if (tegra_powergate_is_supported (pg->id) == false) { +dev_warn (pmc->dev,"%s not currently supported by genpd\n" , + np->name); +return -EINVAL ; + } + +if (pg->id ==TEGRA_POWERGATE_EXT ) { + err =tegra_powergate_get_regulator (pg); +if (err) { +/* +* The regulator might not be ready yet, so just +* give a warning instead of failing the whole +* init. +*/ +dev_warn (pmc->dev,"couldn't locate regulator\n" ); + } + } + + pg->of_node = np; + pg->genpd.name = np->name; + pg->genpd.power_off = tegra_genpd_power_off; + pg->genpd.power_on = tegra_genpd_power_on; + pg->pmc = pmc; + + err =tegra_powergate_of_get_clks (pmc->dev, pg); +if (err) + goto err; + + err =tegra_powergate_of_get_resets (pmc->dev, pg); +if (err) + goto err; + + err =tegra_powergate_of_get_mc_flush (pmc->dev, pg); +if (err) + goto err; + +list_add_tail (&pg->node, &pmc->powergates_list); + +if (!IS_ERR (pg->vdd) || pg->id !=TEGRA_POWERGATE_EXT ) +tegra_genpd_power_off (&pg->genpd); + + off = !_tegra_powergate_is_powered (pg); + +pm_genpd_init (&pg->genpd,NULL , off); + +dev_info (pmc->dev,"added power domain %s\n" , np->name); + +return 0 ; + +err: +dev_err (pmc->dev,"failed to add power domain for node %s\n" , + np->name); +return err; +} + +static int tegra_powergate_add_powerdomains (struct tegra_pmc *pmc, +struct device_node *parent, +struct generic_pm_domain *pd_parent) +{ +struct device_node *np; +int ret; + +for_each_child_of_node (parent, np) { +struct tegra_powergate *pg; + + pg =devm_kzalloc (pmc->dev,sizeof (*pg),GFP_KERNEL ); +if (!pg) +return -ENOMEM ; + + ret =tegra_powergate_init_powerdomain (pmc, np, pg); +if (ret) +return ret; + +if (pd_parent) +pm_genpd_add_subdomain (pd_parent, &pg->genpd); + +of_genpd_add_provider_simple (np, &pg->genpd); + +tegra_powergate_add_powerdomains (pmc, np, &pg->genpd); + } + +return 0 ; +} + +static int tegra_powergate_init (struct tegra_pmc *pmc) +{ +struct device_node *np; + +INIT_LIST_HEAD (&pmc->powergates_list); + + np =of_get_child_by_name (pmc->dev->of_node,"pm-domains" ); +if (!np) { +dev_dbg (pmc->dev,"power-domains node not found\n" ); +return 0 ; + } + +return tegra_powergate_add_powerdomains (pmc, np,NULL ); +} +static int tegra_io_rail_prepare (int id,unsigned long *request,unsigned long *status,unsigned int *bit) {
@@ -875,6 +1390,12 @@ static int tegra_pmc_probe(struct platform_device *pdev)tegra_pmc_init_tsense_reset (pmc); +if (IS_ENABLED (CONFIG_PM_GENERIC_DOMAINS )) { + err =tegra_powergate_init (pmc); +if (err <0 ) +return err; + } +if (IS_ENABLED (CONFIG_DEBUG_FS )) { err =tegra_powergate_debugfs_init ();if (err <0 )
include/dt-bindings/power/tegra-powergate.h new file +42 -0
42 lines not on this page · raw message
include/soc/tegra/pmc.h +5 -47
71 lines not on this page · raw message
Signature · 1 line
2.1.4
PATCH v3 15/19 soc: tegra: pmc: Add generic PM domain support
Quoted 24 lines · Jon Hunter
Adds generic PM support to the PMC driver where the PM domains are populated from device-tree and the PM domain consumer devices are bound to their relevant PM domains via device-tree as well.
Update the tegra_powergate_power_on_legacy/off_legacy() APIs so that internally they call the same
tegra_powergate_xxxfunctions that are used by the tegra generic power domain code for consistency.This is based upon work by Thierry Reding <[email protected]> and Vince Hsu <[email protected]>.
Signed-off-by Jon Hunter <[email protected]>--- drivers/soc/tegra/pmc.c | 545 +++++++++++++++++++++++++++- include/dt-bindings/power/tegra-powergate.h | 42 +++ include/soc/tegra/pmc.h | 52 +-- 3 files changed, 580 insertions(+), 59 deletions(-) create mode 100644 include/dt-bindings/power/tegra-powergate.hdrivers/soc/tegra/pmc.c +0
-0
drivers/soc/tegra/pmc.c line 26
Quoted 8 lines
drivers/soc/tegra/pmc.c +3
-0@@ -19,8 +19,11 @@#define pr_fmt (fmt) "tegra-pmc: " fmt +#include <dt-bindings/power/tegra-powergate.h> +#include <linux/kernel.h> #include <linux/clk.h> +#include <linux/clk-provider.h>
Why are we now providing clocks?
Quoted 5 lines
drivers/soc/tegra/pmc.c +0
-0#include <linux/clk/tegra.h> #include <linux/debugfs.h> #include <linux/delay.h> @@ -30,17 +33,24 @@#include <linux/io.h>
drivers/soc/tegra/pmc.c line 43
Quoted 10 lines
drivers/soc/tegra/pmc.c +4
-0#include <linux/iopoll.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/reboot.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> +#include <linux/sched.h>
What do we need this for?
drivers/soc/tegra/pmc.c line 52
Quoted 9 lines
drivers/soc/tegra/pmc.c +2
-0#include <linux/seq_file.h> #include <linux/spinlock.h> #include <soc/tegra/common.h> #include <soc/tegra/fuse.h> +#include <soc/tegra/mc.h> #include <soc/tegra/pmc.h> +#define PMC_POWERGATE_ARRAY_MAX 10
There should be no need for this.
Quoted 12 lines
drivers/soc/tegra/pmc.c +5
-0+#define PMC_CNTRL 0x0#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10)/* sys clk polarity */ #define PMC_CNTRL_SYSCLK_OE (1 << 11)/* system clock enable */ @@ -106,6 +116,22 @@#define PMC_PWRGATE_STATE (val, id) (!!(val & BIT(id))) +struct tegra_powergate { +struct generic_pm_domain genpd; +struct tegra_pmc *pmc; +struct tegra_mc *mc;
drivers/soc/tegra/pmc.c line 132
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+unsigned int id; +struct list_head node; +struct device_node *of_node; +struct regulator *vdd; +struct clk **clks; +struct reset_control **resets; +const struct tegra_mc_flush **flushes; +u32 num_clks; +u32 num_resets; +u32 num_flushes;
These should be unsigned int, and please group them with the arrays that they define the sizes of.
drivers/soc/tegra/pmc.c struct tegra_pmc_soc { · line 150
Quoted 13 lines
drivers/soc/tegra/pmc.c +4
-0+}; +struct tegra_pmc_soc {unsigned int num_powergates;const char *const *powergates;@@ -118,8 +144,10 @@ struct tegra_pmc_soc {/** * struct tegra_pmc - NVIDIA Tegra PMC +* @dev: pointer to parent device * @base: pointer to I/O remapped register region * @clk: pointer to pclk clock +* @soc: SoC-specific data
This could be a separate patch because it isn't related to the power domain work.
Quoted 50 lines
drivers/soc/tegra/pmc.c +22
-0* @rate: currently configured rate of pclk * @suspend_mode: lowest suspend mode available * @cpu_good_time: CPU power good time (in microseconds) @@ -133,6 +161,7 @@ struct tegra_pmc_soc {* @cpu_pwr_good_en: CPU power good signal is enabled * @lp0_vec_phys: physical base address of the LP0 warm boot code * @lp0_vec_size: size of the LP0 warm boot code +* @powergates_list: list of power gates * @powergates_lock: mutex for power gate register access */ struct tegra_pmc {@@ -157,6 +186,7 @@ struct tegra_pmc {u32 lp0_vec_phys;u32 lp0_vec_size; +struct list_head powergates_list;struct mutex powergates_lock; };@@ -165,6 +195,12 @@ static struct tegra_pmc *pmc = &(struct tegra_pmc) { .suspend_mode = TEGRA_SUSPEND_NONE, }; +static inline struct tegra_powergate * +to_powergate (struct generic_pm_domain *domain) +{ + return container_of(domain, struct tegra_powergate, genpd); +} +static u32 tegra_pmc_readl (unsigned long offset) {return readl (pmc->base + offset);@@ -175,6 +211,37 @@ static void tegra_pmc_writel(u32 value, unsigned long offset)writel (value, pmc->base + offset); } +static int tegra_powergate_get_regulator (struct tegra_powergate *powergate) +{ +struct platform_device *pdev; + +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return -EINVAL ; + + pdev =of_find_device_by_node (powergate->of_node); +if (!pdev) +return -EINVAL ; + + powergate->vdd =devm_regulator_get_optional (&pdev->dev,"vdd" ); +if (IS_ERR (powergate->vdd)) +return PTR_ERR (powergate->vdd);
drivers/soc/tegra/pmc.c static void tegra_pmc_writel(u32 value, unsigned long offset) · line 237
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ +return 0 ; +} + +static int tegra_powergate_enable_regulator (struct tegra_powergate *powergate) +{ +int ret =0 ; + +if (IS_ERR_OR_NULL (powergate->vdd)) + ret =tegra_powergate_get_regulator (powergate);
Why don't you get the regulator at the time when the powergate is created? That way you still have a chance of deferring probe to get another chance at getting it. If you don't do that, then if ever you happen to want to enable the powergate before the regulator is ready you'll error out and your consumer will have to deal with that.
Quoted 46 lines
drivers/soc/tegra/pmc.c +39
-0+ +if (!ret) + ret =regulator_enable (powergate->vdd); + +return ret; +} +/** * tegra_powergate_set() - set the state of a partition * @id: partition ID @@ -207,6 +274,215 @@ static int tegra_powergate_set(int id, bool new_state, bool wait)return ret; } +static int tegra_powergate_enable (struct tegra_powergate *powergate, +bool enable) +{ +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return tegra_powergate_set (powergate->id, enable, true); + +if (enable) +return tegra_powergate_enable_regulator (powergate); +else +return regulator_disable (powergate->vdd); +} + +static bool tegra_powergate_is_supported (int id) +{ +switch (id) { +case TEGRA_POWERGATE_CPU : +case TEGRA_POWERGATE_CPU1 : +case TEGRA_POWERGATE_CPU2 : +case TEGRA_POWERGATE_CPU3 : +case TEGRA_POWERGATE_CPU0 : +case TEGRA_POWERGATE_C0NC : +case TEGRA_POWERGATE_IRAM : +return false; +default : +return true; + } +} + +static bool _tegra_powergate_is_powered (struct tegra_powergate *powergate) +{ +if (powergate->id !=TEGRA_POWERGATE_EXT ) +return tegra_powergate_is_powered (powergate->id);
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 318
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ +if (IS_ERR (powergate->vdd)) +return false; +else +return regulator_is_enabled (powergate->vdd); +} + +static void tegra_powergate_disable_clocks (struct tegra_powergate *powergate) +{ +int i;
Should match the type of powergate->num_clks, that is unsigned int.
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 326
Quoted 8 lines
drivers/soc/tegra/pmc.c +8
-0+ +for (i =0 ; i < powergate->num_clks; i++) +clk_disable_unprepare (powergate->clks[i]); +} + +static int tegra_powergate_enable_clocks (struct tegra_powergate *powergate) +{ +int err, i;
Same here.
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ +for (i =0 ; i < powergate->num_clks; i++) { + err =clk_prepare_enable (powergate->clks[i]); +if (err) + goto out; + } + +return 0 ; + +out:
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 346
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+while (i--) +clk_disable_unprepare (powergate->clks[i]); + +return err; +} + +static int tegra_powergate_mc_flush (struct tegra_powergate *powergate, +bool enable) +{ +int i, err;
And here.
Quoted 4 lines
drivers/soc/tegra/pmc.c +4
-0+ +for (i =0 ; i < powergate->num_flushes; i++) { + err =tegra_mc_flush (powergate->mc, powergate->flushes[i], + enable);
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 360
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_reset_assert (struct tegra_powergate *powergate) +{ +int err, i;
As well as here.
Quoted 14 lines
drivers/soc/tegra/pmc.c +14
-0+ +for (i =0 ; i < powergate->num_resets; i++) { + err =reset_control_assert (powergate->resets[i]); +if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_reset_deassert (struct tegra_powergate *powergate) +{ +int err, i; +
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 384
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+for (i =0 ; i < powergate->num_resets; i++) { + err =reset_control_deassert (powergate->resets[i]); +if (err) +return err; + } + +return 0 ; +} + +static int tegra_powergate_seq_power_up (struct tegra_powergate *powergate)
What does the _seq buy us here? Why not simply call this tegra_powergate_power_up()?
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 391
Quoted 7 lines
drivers/soc/tegra/pmc.c +7
-0+{ +int err; + + err =tegra_powergate_reset_assert (powergate); +if (err) + goto out; +udelay (10 );
There should be a space between the above two lines. Also, is there a reason why we can't use usleep_range() here?
Quoted 13 lines
drivers/soc/tegra/pmc.c +13
-0+ + err =tegra_powergate_enable (powergate, true); +if (err <0 ) + goto out; +udelay (10 ); + + err =tegra_powergate_enable_clocks (powergate); +if (err) + goto out; +udelay (10 ); + +tegra_powergate_remove_clamping (powergate->id); +
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 414
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+udelay (10 ); + + err =tegra_powergate_reset_deassert (powergate); +if (err) + goto out; +udelay (10 ); + + err =tegra_powergate_mc_flush (powergate, false); +if (err) + goto out;
I mentioned this before, but I think it'd be good to split out the flush changes from this patch, so that we can test the series without the flushing to see if it's at all necessary.
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 466
Quoted 9 lines
drivers/soc/tegra/pmc.c +9
-0+static int tegra_genpd_power_on (struct generic_pm_domain *domain) +{ +struct tegra_powergate *powergate =to_powergate (domain); +struct tegra_pmc *pmc = powergate->pmc; +int err; + + err =tegra_powergate_seq_power_up (powergate); + +if (err)
There should be no blank line between the above.
drivers/soc/tegra/pmc.c static int tegra_powergate_set(int id, bool new_state, bool wait) · line 467
Quoted 1 line
drivers/soc/tegra/pmc.c +1
-0+dev_err (pmc->dev,"Failed to turn on PM Domain (%d)\n" , err);
For consistency with other error messages, "Failed" should be "failed". And "Domain" should be "domain". Also typically the error is separated from the message with a colon: "failed ... domain: %d\n".
Quoted 39 lines
drivers/soc/tegra/pmc.c +21
-11-int tegra_powergate_power_off_legacy (int id,struct clk *clk, -struct reset_control *rst) +int tegra_powergate_power_on_legacy (int id,struct clk *clk, +struct reset_control *rst) { -int ret; +struct tegra_powergate powergate; - ret =clk_prepare_enable (clk); -if (ret) -return ret; +if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) +return -EINVAL ; -usleep_range (10 ,20 ); + powergate.id = id; + powergate.clks = &clk; + powergate.resets = &rst; + powergate.num_clks =1 ; + powergate.num_resets =1 ; + powergate.num_flushes =0 ; -reset_control_assert (rst); +return tegra_powergate_seq_power_up (&powergate); +} +EXPORT_SYMBOL (tegra_powergate_power_on_legacy); -usleep_range (10 ,20 ); +int tegra_powergate_power_off_legacy (int id,struct clk *clk, +struct reset_control *rst) +{ +struct tegra_powergate powergate; -clk_disable_unprepare (clk); +if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) +return -EINVAL ; -usleep_range (10 ,20 ); + powergate.id = id;
drivers/soc/tegra/pmc.c err_power: · line 648
Quoted 10 lines
drivers/soc/tegra/pmc.c +6
-1+ powergate.clks = &clk; + powergate.resets = &rst; + powergate.num_clks =1 ; + powergate.num_resets =1 ; + powergate.num_flushes =0 ; -return tegra_powergate_power_off (id); +return tegra_powergate_seq_power_down (&powergate); }EXPORT_SYMBOL (tegra_powergate_power_off_legacy);
It looks as if you decided to reorder the two functions here, but that makes the diff very hard to read.
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 793
Quoted 10 lines
drivers/soc/tegra/pmc.c +5
-0@@ -493,6 +786,228 @@ static int tegra_powergate_debugfs_init(void)return 0 ; } +static int tegra_powergate_of_get_clks (struct device *dev, +struct tegra_powergate *powergate) +{ +struct clk *clks[PMC_POWERGATE_ARRAY_MAX ]; +int i =0 ;
unsigned int, and no need to initialize here.
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 802
Quoted 9 lines
drivers/soc/tegra/pmc.c +9
-0+ +for (i =0 ; i <PMC_POWERGATE_ARRAY_MAX ; i++) { + clks[i] =of_clk_get (powergate->of_node, i); +if (IS_ERR (clks[i])) { +if (PTR_ERR (clks[i]) != -ENOENT ) +return PTR_ERR (clks[i]); +break ; + } + }
Can we perhaps prepend this with a loop that determines the number of clocks specified in DT? That way we can avoid the statically sized array altogether.
Perhaps to make it a little easier to read, keep a local variable clk and assign to the array only when it's been sanitized:
for (...) {
clk = of_clk_get(...);
if (IS_ERR(clk)) {
...
}
powergate->clks[i] = clk;
}Quoted 5 lines
drivers/soc/tegra/pmc.c +5
-0+ +if (PTR_ERR (clks[i]) != -ENOENT ) { +dev_err (dev,"unsupported number of clocks defined\n" ); +return -EINVAL ; + }
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 817
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ + powergate->clks =devm_kcalloc (dev, i,sizeof (*clks),GFP_KERNEL ); +if (!powergate->clks) +return -ENOMEM ; + +memcpy (powergate->clks, clks,sizeof (*clks) * i); + powergate->num_clks = i; + +return 0 ; +}
Don't you need to put the clocks that you've obtained above to avoid leaking them?
Quoted 20 lines
drivers/soc/tegra/pmc.c +20
-0+ +static int tegra_powergate_of_get_resets (struct device *dev, +struct tegra_powergate *powergate) +{ +struct reset_control *rsts[PMC_POWERGATE_ARRAY_MAX ]; +int i =0 ; + +for (i =0 ; i <PMC_POWERGATE_ARRAY_MAX ; i++) { + rsts[i] =of_reset_control_get_by_index (powergate->of_node, i); +if (IS_ERR (rsts[i])) { +if (PTR_ERR (rsts[i]) != -ENOENT ) +return PTR_ERR (rsts[i]); +break ; + } + } + +if (PTR_ERR (rsts[i]) != -ENOENT ) { +dev_err (dev,"unsupported number of resets defined\n" ); +return -EINVAL ; + }
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 847
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ + powergate->resets =devm_kcalloc (dev, i,sizeof (*rsts),GFP_KERNEL ); +if (!powergate->resets) +return -ENOMEM ; + +memcpy (powergate->resets, rsts,sizeof (*rsts) * i); + powergate->num_resets = i; + +return 0 ; +}
Same comments as for clocks.
Quoted 31 lines
drivers/soc/tegra/pmc.c +31
-0+static int tegra_powergate_of_get_mc_flush (struct device *dev, +struct tegra_powergate *powergate) +{ +struct platform_device *pdev; +struct of_phandle_args args; +int i =0 , ret =0 ; + + ret =of_parse_phandle_with_args (dev->of_node,"nvidia-swgroups" , +"#nvidia,swgroup-cells" ,0 , &args); +if (ret <0 ) { +/* +* The nvidia-swgroups property is +* optional and so if missing simply return. +*/ +if (ret == -ENOENT ) +return 0 ; + +dev_err (dev,"nvidia-swgroups property invalid for %s (%d)\n" , + powergate->of_node->name, ret); +return ret; + } + + powergate->flushes =devm_kcalloc (dev, args.args_count, +sizeof (powergate->flushes), +GFP_KERNEL ); +if (!powergate->flushes) { +dev_err (dev,"failed to allocate memory for powergate flush\n" ); +return -ENOMEM ; + } + + pdev =of_find_device_by_node (args.np);
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 889
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+if (!pdev) +return -ENODEV ; + + powergate->mc =platform_get_drvdata (pdev); +if (!powergate->mc) +return -EINVAL ; + +for (i =0 ; i < args.args_count; i++) { + powergate->flushes[i] =tegra_mc_flush_get (powergate->mc, + args.args[i]);
Urgs... might be better to make this a struct device *-based API, so that you don't have to reach into the private structures of the MC driver. Well, it'd technically have to be struct device_node *-based because we don't have a struct device * for the power domains. Perhaps something like this:
const struct tegra_mc_flush *tegra_mc_flush_get(struct device_node *np, unsigned int index);
Then the MC driver can access its own internals, rather than the other way around.
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 901
Quoted 12 lines
drivers/soc/tegra/pmc.c +12
-0+if (!powergate->flushes[i]) +return -EINVAL ; + } + + powergate->num_flushes = args.args_count; + +return 0 ; +} + +static int tegra_powergate_init_powerdomain (struct tegra_pmc *pmc, +struct device_node *np, +struct tegra_powergate *pg)
This is slightly confusing. Why not something like this:
struct tegra_powergate *tegra_powergate_create(struct tegra_pmc *pmc,
struct device_node *np)
?
Quoted 15 lines
drivers/soc/tegra/pmc.c +15
-0+{ +bool off; +int err; + + err =of_property_read_u32 (np,"nvidia,powergate" , &pg->id); +if (err) { +dev_err (pmc->dev,"no powergate ID for domain\n" ); + goto err; + } + +if (tegra_powergate_is_supported (pg->id) == false) { +dev_warn (pmc->dev,"%s not currently supported by genpd\n" , + np->name); +return -EINVAL ; + }
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 926
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+ +if (pg->id ==TEGRA_POWERGATE_EXT ) { + err =tegra_powergate_get_regulator (pg); +if (err) { +/* +* The regulator might not be ready yet, so just +* give a warning instead of failing the whole +* init. +*/ +dev_warn (pmc->dev,"couldn't locate regulator\n" );
Why won't regular probe deferral work?
Quoted 28 lines
drivers/soc/tegra/pmc.c +28
-0+ } + } + + pg->of_node = np; + pg->genpd.name = np->name; + pg->genpd.power_off = tegra_genpd_power_off; + pg->genpd.power_on = tegra_genpd_power_on; + pg->pmc = pmc; + + err =tegra_powergate_of_get_clks (pmc->dev, pg); +if (err) + goto err; + + err =tegra_powergate_of_get_resets (pmc->dev, pg); +if (err) + goto err; + + err =tegra_powergate_of_get_mc_flush (pmc->dev, pg); +if (err) + goto err; + +list_add_tail (&pg->node, &pmc->powergates_list); + +if (!IS_ERR (pg->vdd) || pg->id !=TEGRA_POWERGATE_EXT ) +tegra_genpd_power_off (&pg->genpd); + + off = !_tegra_powergate_is_powered (pg); +
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 964
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+pm_genpd_init (&pg->genpd,NULL , off); + +dev_info (pmc->dev,"added power domain %s\n" , np->name); + +return 0 ; + +err: +dev_err (pmc->dev,"failed to add power domain for node %s\n" , + np->name); +return err;
This doesn't clean up any of the requested resources on failure.
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 972
Quoted 8 lines
drivers/soc/tegra/pmc.c +8
-0+} + +static int tegra_powergate_add_powerdomains (struct tegra_pmc *pmc, +struct device_node *parent, +struct generic_pm_domain *pd_parent) +{ +struct device_node *np; +int ret;
Please use err consistently as the name for error codes. This patch uses a mixture of both.
Quoted 9 lines
drivers/soc/tegra/pmc.c +9
-0+ +for_each_child_of_node (parent, np) { +struct tegra_powergate *pg; + + pg =devm_kzalloc (pmc->dev,sizeof (*pg),GFP_KERNEL ); +if (!pg) +return -ENOMEM ; + + ret =tegra_powergate_init_powerdomain (pmc, np, pg);
drivers/soc/tegra/pmc.c static int tegra_powergate_debugfs_init(void) · line 991
Quoted 10 lines
drivers/soc/tegra/pmc.c +10
-0+if (ret) +return ret; + +if (pd_parent) +pm_genpd_add_subdomain (pd_parent, &pg->genpd); + +of_genpd_add_provider_simple (np, &pg->genpd); + +tegra_powergate_add_powerdomains (pmc, np, &pg->genpd); + }
Nice, I like the recursive nature of this and how it matches up well with the Tegra hardware.
Quoted 70 lines
drivers/soc/tegra/pmc.c +25
-0+ +return 0 ; +} + +static int tegra_powergate_init (struct tegra_pmc *pmc) +{ +struct device_node *np; + +INIT_LIST_HEAD (&pmc->powergates_list); + + np =of_get_child_by_name (pmc->dev->of_node,"pm-domains" ); +if (!np) { +dev_dbg (pmc->dev,"power-domains node not found\n" ); +return 0 ; + } + +return tegra_powergate_add_powerdomains (pmc, np,NULL ); +} +static int tegra_io_rail_prepare (int id,unsigned long *request,unsigned long *status,unsigned int *bit) {@@ -875,6 +1390,12 @@ static int tegra_pmc_probe(struct platform_device *pdev)tegra_pmc_init_tsense_reset (pmc); +if (IS_ENABLED (CONFIG_PM_GENERIC_DOMAINS )) { + err =tegra_powergate_init (pmc); +if (err <0 ) +return err; + } +if (IS_ENABLED (CONFIG_DEBUG_FS )) { err =tegra_powergate_debugfs_init ();if (err <0 )include/dt-bindings/power/tegra-powergate.h new file +29
-0@@ -0,0 +1,42 @@ +#ifndef _DT_BINDINGS_POWER_TEGRA_POWERGATE_H +#define _DT_BINDINGS_POWER_TEGRA_POWERGATE_H + +#define TEGRA_POWERGATE_CPU 0 +#define TEGRA_POWERGATE_3D 1 +#define TEGRA_POWERGATE_VENC 2 +#define TEGRA_POWERGATE_PCIE 3 +#define TEGRA_POWERGATE_VDEC 4 +#define TEGRA_POWERGATE_L2 5 +#define TEGRA_POWERGATE_MPE 6 +#define TEGRA_POWERGATE_HEG 7 +#define TEGRA_POWERGATE_SATA 8 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 +#define TEGRA_POWERGATE_CELP 12 +#define TEGRA_POWERGATE_3D1 13 +#define TEGRA_POWERGATE_CPU0 14 +#define TEGRA_POWERGATE_C0NC 15 +#define TEGRA_POWERGATE_C1NC 16 +#define TEGRA_POWERGATE_SOR 17 +#define TEGRA_POWERGATE_DIS 18 +#define TEGRA_POWERGATE_DISB 19 +#define TEGRA_POWERGATE_XUSBA 20 +#define TEGRA_POWERGATE_XUSBB 21 +#define TEGRA_POWERGATE_XUSBC 22 +#define TEGRA_POWERGATE_VIC 23 +#define TEGRA_POWERGATE_IRAM 24 +#define TEGRA_POWERGATE_NVDEC 25
include/dt-bindings/power/tegra-powergate.h line 39
Quoted 10 lines
include/dt-bindings/power/tegra-powergate.h new file +10
-0+#define TEGRA_POWERGATE_NVJPG 26 +#define TEGRA_POWERGATE_AUD 27 +#define TEGRA_POWERGATE_DFD 28 +#define TEGRA_POWERGATE_VE2 29 + +/* +* Pseudo powergate for power-domains that are power-gated externally. +* Make this a large number to allow other powergates to be added. +*/ +#define TEGRA_POWERGATE_EXT 1000
Can't we simply cover this case by not specifying the nvidia,powergate property in the first place? Or make it conditional on the existence of a vdd-supply property.
include/dt-bindings/power/tegra-powergate.h line 41
Quoted 2 lines
include/dt-bindings/power/tegra-powergate.h new file +2
-0+ +
There's a gratuitous blank line here.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/120b7be1/attachment.sig>
Jon Hunter
maintainerPATCH v3 16/19 soc: tegra: pmc: Remove the deprecated powergate APIs
In reply to Jon HunterDrivers using the deprecated tegra powergate APIs have been updated and so now we can remove these unused deprecated APIs.
--- drivers/soc/tegra/pmc.c | 68 ------------------------------------------------- include/soc/tegra/pmc.h | 22 ---------------- 2 files changed, 90 deletions(-)
drivers/soc/tegra/pmc.c +0 -68
@@ -484,31 +484,6 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain) }/** -* tegra_powergate_power_on() - power on partition -* @id: partition ID -*/ -int tegra_powergate_power_on (int id) -{ -if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) -return -EINVAL ; - -return tegra_powergate_set (id, true, true); -} - -/** -* tegra_powergate_power_off() - power off partition -* @id: partition ID -*/ -int tegra_powergate_power_off (int id) -{ -if (!pmc->soc || id <0 || id >= pmc->soc->num_powergates) -return -EINVAL ; - -return tegra_powergate_set (id, false, true); -} -EXPORT_SYMBOL (tegra_powergate_power_off); - -/** * tegra_powergate_is_powered() - check if partition is powered * @id: partition ID */
@@ -566,49 +541,6 @@ int tegra_powergate_remove_clamping(int id) }EXPORT_SYMBOL (tegra_powergate_remove_clamping); -/** -* tegra_powergate_sequence_power_up() - power up partition -* @id: partition ID -* @clk: clock for partition -* @rst: reset for partition -* -* Must be called with clk disabled, and returns with clk enabled. -*/ -int tegra_powergate_sequence_power_up (int id,struct clk *clk, -struct reset_control *rst) -{ -int ret; - -reset_control_assert (rst); - - ret =tegra_powergate_power_on (id); -if (ret) - goto err_power; - - ret =clk_prepare_enable (clk); -if (ret) - goto err_clk; - -usleep_range (10 ,20 ); - - ret =tegra_powergate_remove_clamping (id); -if (ret) - goto err_clamp; - -usleep_range (10 ,20 ); -reset_control_deassert (rst); - -return 0 ; - -err_clamp: -clk_disable_unprepare (clk); -err_clk: -tegra_powergate_power_off (id); -err_power: -return ret; -} -EXPORT_SYMBOL (tegra_powergate_sequence_power_up); -int tegra_powergate_power_on_legacy (int id,struct clk *clk,struct reset_control *rst) {
include/soc/tegra/pmc.h +0 -22
@@ -79,14 +79,8 @@ int tegra_pmc_cpu_remove_clamping(int cpuid);#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); -/* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up (int id,struct clk *clk, -struct reset_control *rst); -int tegra_powergate_power_on_legacy (int id,struct clk *clk,struct reset_control *rst);
@@ -101,27 +95,11 @@ static inline int tegra_powergate_is_powered(int id)return -ENOSYS ; } -static inline int tegra_powergate_power_on (int id) -{ -return -ENOSYS ; -} - -static inline int tegra_powergate_power_off (int id) -{ -return -ENOSYS ; -} -static inline int tegra_powergate_remove_clamping (int id) {return -ENOSYS ; } -static inline int tegra_powergate_sequence_power_up (int id,struct clk *clk, -struct reset_control *rst) -{ -return -ENOSYS ; -} -static inline int tegra_io_rail_power_on (int id) {return -ENOSYS ;
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 17/19 ARM: tegra: Add PM domain device nodes to Tegra124 DT
In reply to Jon HunterAdd tegra124 pm-domains provider and consumer nodes.
--- arch/arm/boot/dts/tegra124.dtsi | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+)
arch/arm/boot/dts/tegra124.dtsi +80 -0
@@ -3,6 +3,7 @@ #include <dt-bindings/memory/tegra124-mc.h> #include <dt-bindings/pinctrl/pinctrl-tegra.h> #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> +#include <dt-bindings/power/tegra-powergate.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/reset/tegra124-car.h> #include <dt-bindings/thermal/tegra124-soctherm.h>
@@ -40,6 +41,8 @@ 0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */ 0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */ + power-domains = <&pd_pcie>; + clocks = <&tegra_car TEGRA124_CLK_PCIE>, <&tegra_car TEGRA124_CLK_AFI>, <&tegra_car TEGRA124_CLK_PLL_E>,
@@ -99,6 +102,7 @@ compatible = "nvidia,tegra124-dc"; reg = <0x0 0x54200000 0x0 0x00040000>; interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pd_dc>; clocks = <&tegra_car TEGRA124_CLK_DISP1>, <&tegra_car TEGRA124_CLK_PLL_P>; clock-names = "dc", "parent";
@@ -114,6 +118,7 @@ compatible = "nvidia,tegra124-dc"; reg = <0x0 0x54240000 0x0 0x00040000>; interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pd_dcb>; clocks = <&tegra_car TEGRA124_CLK_DISP2>, <&tegra_car TEGRA124_CLK_PLL_P>; clock-names = "dc", "parent";
@@ -141,6 +146,7 @@ compatible = "nvidia,tegra124-sor"; reg = <0x0 0x54540000 0x0 0x00040000>; interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pd_sor>; clocks = <&tegra_car TEGRA124_CLK_SOR0>, <&tegra_car TEGRA124_CLK_PLL_D_OUT0>, <&tegra_car TEGRA124_CLK_PLL_DP>,
@@ -155,6 +161,7 @@ compatible = "nvidia,tegra124-dpaux"; reg = <0x0 0x545c0000 0x0 0x00040000>; interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pd_sor>; clocks = <&tegra_car TEGRA124_CLK_DPAUX>, <&tegra_car TEGRA124_CLK_PLL_DP>; clock-names = "dpaux", "parent";
@@ -184,6 +191,7 @@ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "stall", "nonstall"; + power-domains = <&pd_gpu>; clocks = <&tegra_car TEGRA124_CLK_GPU>, <&tegra_car TEGRA124_CLK_PLL_P_OUT5>; clock-names = "gpu", "pwr";
@@ -573,6 +581,76 @@ reg = <0x0 0x7000e400 0x0 0x400>; clocks = <&tegra_car TEGRA124_CLK_PCLK>, <&clk32k_in>; clock-names = "pclk", "clk32k_in"; + + pm-domains { + pd_gpu: gpu-power-domain { + clocks = <&tegra_car TEGRA124_CLK_GPU>, + <&tegra_car TEGRA124_CLK_PLL_P_OUT5>; + resets = <&tegra_car 184>; + nvidia,powergate = <TEGRA_POWERGATE_EXT>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_GPU>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + }; + + pd_pcie: pcie-power-domain { + clocks = <&tegra_car TEGRA124_CLK_PCIE>, + <&tegra_car TEGRA124_CLK_AFI>; + resets = <&tegra_car 70>, + <&tegra_car 72>; + nvidia,powergate = <TEGRA_POWERGATE_PCIE>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_AFI>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + }; + + pd_sata: sata-power-domain { + clocks = <&tegra_car TEGRA124_CLK_SATA>, + <&tegra_car TEGRA124_CLK_SATA_OOB>, + <&tegra_car TEGRA124_CLK_CML1>; + resets = <&tegra_car 124>, + <&tegra_car 123>, + <&tegra_car 129>; + nvidia,powergate = <TEGRA_POWERGATE_SATA>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_SATA>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + }; + + pd_sor: sor-power-domain { + clocks = <&tegra_car TEGRA124_CLK_SOR0>, + <&tegra_car TEGRA124_CLK_DSIA>, + <&tegra_car TEGRA124_CLK_DSIB>, + <&tegra_car TEGRA124_CLK_HDMI>, + <&tegra_car TEGRA124_CLK_MIPI_CAL>, + <&tegra_car TEGRA124_CLK_DPAUX>; + resets = <&tegra_car 182>, + <&tegra_car 48>, + <&tegra_car 82>, + <&tegra_car 51>, + <&tegra_car 56>; + nvidia,powergate = <TEGRA_POWERGATE_SOR>; + #power-domain-cells = <0>; + + pd_dc: dc-power-domain { + clocks = <&tegra_car TEGRA124_CLK_DISP1>; + resets = <&tegra_car 27>; + nvidia,powergate = <TEGRA_POWERGATE_DIS>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_DC>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + + pd_dcb: dcb-power-domain { + clocks = <&tegra_car TEGRA124_CLK_DISP2>; + resets = <&tegra_car 26>; + nvidia,powergate = <TEGRA_POWERGATE_DISB>; + nvidia,swgroups = <&mc TEGRA_SWGROUP_DCB>; + #nvidia,swgroup-cells = <1>; + #power-domain-cells = <0>; + }; + }; + }; + }; }; fuse at 0,7000f800 {
@@ -621,6 +699,8 @@ <&tegra_car 129>; reset-names = "sata", "sata-oob", "sata-cold"; + power-domains = <&pd_sata>; + phys = <&padctl TEGRA_XUSB_PADCTL_SATA>; phy-names = "sata-phy";
Signature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 18/19 ARM: tegra: add GPU power supply to Jetson TK1 DT
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
Add power supply information which is board dependent for GK20A.
--- arch/arm/boot/dts/tegra124-jetson-tk1.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
arch/arm/boot/dts/tegra124-jetson-tk1.dts +5 -1
@@ -1514,7 +1514,7 @@ regulator-always-on; }; - sd6 { + vdd_gpu: sd6 { regulator-name = "+VDD_GPU_AP"; regulator-min-microvolt = <650000>; regulator-max-microvolt = <1200000>;
@@ -1635,6 +1635,10 @@ }; }; + gpu-power-domain { + vdd-supply = <&vdd_gpu>; + }; + /* Serial ATA */ sata at 0,70020000 { status = "okay";
Signature · 1 line
2.1.4
PATCH v3 18/19 ARM: tegra: add GPU power supply to Jetson TK1 DT
Quoted 24 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Add power supply information which is board dependent for GK20A.
Signed-off-by Vince Hsu <[email protected]>Signed-off-by Jon Hunter <[email protected]>--- arch/arm/boot/dts/tegra124-jetson-tk1.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)arch/arm/boot/dts/tegra124-jetson-tk1.dts +1
-1@@ -1514,7 +1514,7 @@ regulator-always-on; }; - sd6 { + vdd_gpu: sd6 { regulator-name = "+VDD_GPU_AP"; regulator-min-microvolt = <650000>; regulator-max-microvolt = <1200000>;
arch/arm/boot/dts/tegra124-jetson-tk1.dts line 1640
Quoted 6 lines
arch/arm/boot/dts/tegra124-jetson-tk1.dts +3
-0@@ -1635,6 +1635,10 @@ }; }; + gpu-power-domain { + vdd-supply = <&vdd_gpu>; + };
This doesn't seem to match the path to the original node.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150717/6e42b0a0/attachment.sig>
Jon Hunter
maintainerPATCH v3 19/19 ARM: tegra: select PM_GENERIC_DOMAINS
In reply to Jon HunterFrom: Vince Hsu <[email protected]>
We have added generic power domain support for Tegra SoCs. So now the option M_GENERIC_DOMAINS must be enabled by default to have proper power sequence.
--- arch/arm/mach-tegra/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/arm/mach-tegra/Kconfig +1 -0
@@ -9,6 +9,7 @@ menuconfig ARCH_TEGRA select HAVE_ARM_TWD if SMP select PINCTRL select PM_OPP + select PM_GENERIC_DOMAINS if PM select ARCH_HAS_RESET_CONTROLLER select RESET_CONTROLLER select SOC_BUS
Signature · 1 line
2.1.4
PATCH v3 19/19 ARM: tegra: select PM_GENERIC_DOMAINS
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
We have added generic power domain support for Tegra SoCs. So now the option
M_GENERIC_DOMAINSmust be enabled by default to have proper power
Missing P :)
Quoted 23 lines
sequence.
Signed-off-by Vince Hsu <[email protected]>Signed-off-by Jon Hunter <[email protected]>--- arch/arm/mach-tegra/Kconfig | 1 + 1 file changed, 1 insertion(+)arch/arm/mach-tegra/Kconfig +1
-0@@ -9,6 +9,7 @@ menuconfig ARCH_TEGRA select HAVE_ARM_TWD if SMP select PINCTRL select PM_OPP + select PM_GENERIC_DOMAINS if PM select ARCH_HAS_RESET_CONTROLLER select RESET_CONTROLLER select SOC_BUSSignature · 1 line
2.1.4
Jon Hunter
maintainerPATCH v3 19/19 ARM: tegra: select PM_GENERIC_DOMAINS
Quoted 7 lines · Peter De Schrijver
Quoted 4 lines · Jon Hunter
From: Vince Hsu <[email protected]>
We have added generic power domain support for Tegra SoCs. So now the option
M_GENERIC_DOMAINSmust be enabled by default to have proper powerMissing P :)
Ooops. I think introduced that error. Will fix.
Cheers
Jon
Jon Hunter
maintainerPATCH v3 19/19 ARM: tegra: select PM_GENERIC_DOMAINS
Quoted 4 lines · Jon Hunter
Quoted 1 line · Peter De Schrijver
On Mon, Jul 13, 2015 at 01:39:57PM +0100, Jon Hunter wrote:
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
From: Vince Hsu <[email protected]>
We have added generic power domain support for Tegra SoCs. So now the option
M_GENERIC_DOMAINSmust be enabled by default to have proper powerMissing P :)
Ooops. I think introduced that error. Will fix.
Updated version ...
Quoted 1 line
From
f7b951cd05973d74fc25e33a1aaec5a6e77bfdc0Mon Sep 17 00:00:00 2001
From: Vince Hsu <[email protected]>
Date: Wed, 11 Mar 2015 13:40:46 +0800
Subject: [PATCH 19/19] ARM: tegra: select PM_GENERIC_DOMAINS
We have added generic power domain support for Tegra SoCs. So now the option PM_GENERIC_DOMAINS must be enabled by default to have proper power sequence.
--- arch/arm/mach-tegra/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/arm/mach-tegra/Kconfig +1 -0
@@ -9,6 +9,7 @@ menuconfig ARCH_TEGRA select HAVE_ARM_TWD if SMP select PINCTRL select PM_OPP + select PM_GENERIC_DOMAINS if PM select ARCH_HAS_RESET_CONTROLLER select RESET_CONTROLLER select SOC_BUS
Signature · 1 line
2.1.4
PATCH v3 19/19 ARM: tegra: select PM_GENERIC_DOMAINS
Quoted 9 lines · Jon Hunter
Quoted 6 lines · Jon Hunter
Quoted 3 lines · Peter De Schrijver
Quoted 2 lines · Jon Hunter
From: Vince Hsu <[email protected]>
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
We have added generic power domain support for Tegra SoCs. So now the option
M_GENERIC_DOMAINSmust be enabled by default to have proper powerMissing P :)
Ooops. I think introduced that error. Will fix.
Updated version ...
Quoted 31 lines
From
f7b951cd05973d74fc25e33a1aaec5a6e77bfdc0Mon Sep 17 00:00:00 2001
From: Vince Hsu <[email protected]>
Date: Wed, 11 Mar 2015 13:40:46 +0800
Subject: [PATCH 19/19] ARM: tegra: selectPM_GENERIC_DOMAINSWe have added generic power domain support for Tegra SoCs. So now the option
PM_GENERIC_DOMAINSmust be enabled by default to have proper power sequence.Signed-off-by Vince Hsu <[email protected]>Signed-off-by Jon Hunter <[email protected]>--- arch/arm/mach-tegra/Kconfig | 1 + 1 file changed, 1 insertion(+)arch/arm/mach-tegra/Kconfig +1
-0@@ -9,6 +9,7 @@ menuconfig ARCH_TEGRA select HAVE_ARM_TWD if SMP select PINCTRL select PM_OPP + select PM_GENERIC_DOMAINS if PM select ARCH_HAS_RESET_CONTROLLER select RESET_CONTROLLER select SOC_BUSSignature · 1 line
2.1.4