PM / Domains: DT support for domain idle states & atomic PM domains
Subsystems (10)
v4 cover PM / Domains: DT support for domain idle states & atomic PM domains
Hi all,
Changes since v3 [5] -
- Added "domain-idle-state" compatible to arm/idle-states.txt
- Updated documentation for defining PM domain idle states
- Updated documentation for devices and PM domains.
- Updated code to look for "domain-idle-state" instead of "arm,idle-state" compatible.
- The series is available at [6].
Changes since v2 [3] -
- Addressed review comments from v2.
- domain-idle-states documentation updated
- fixed compiler issues with imx driver
- minor code change in pm_domains.c
- The series is available at [4].
Changes since v1 [2] -
- Addressed review comments from v1.
- Fixes around dynamic allocation of genpd states
- Used OF method for iterating phandles
- Updated documentation, examples
- Rename state variable (provider -> fwnode)
- The series is available at [3].
The changes from [1] are -
- Allocating memory for domain idle states dynamically
- Conform to naming conventions for internal and exported genpd functions
- DT binding example for domain-idle-state
- Use fwnode instead of of_node
- Handle atomic case for removal of PM Domain
- Rebase on top of Rafael's pm/genpd tree
Thanks,
Lina
Lina Iyer (9):
PM / Domains: Make genpd state allocation dynamic
PM / Domain: Add residency property to genpd states PM / Domains: Allow domain power states to be read from DT
dt-bindings: add domain-idle-state compatible to arm,idle-state
dt-bindings: Update binding for PM domain idle states PM / Domains: Save the fwnode in genpd_power_state
PM / Domains: Abstract genpd locking
PM / Domains: Support IRQ safe PM domains
PM / doc: Update device documentation for devices in IRQ safe PM
domains
.../devicetree/bindings/arm/idle-states.txt | 3 + .../devicetree/bindings/power/power_domain.txt | 44 +++ Documentation/power/devices.txt | 14 +- arch/arm/mach-imx/gpc.c | 17 +- drivers/base/power/domain.c | 358 +++++++++++++++++---- include/linux/pm_domain.h | 28 +- 6 files changed, 392 insertions(+), 72 deletions(-)
Signature · 8 lines
2.7.4 [1]. https://www.spinics.net/lists/arm-kernel/msg526814.html [2]. http://www.spinics.net/lists/arm-kernel/msg535106.html [3]. https://git.linaro.org/people/lina.iyer/linux-next.git/shortlog/refs/heads/genpd-v2 [4]. https://git.linaro.org/people/lina.iyer/linux-next.git/shortlog/refs/heads/genpd-v3 [5]. http://www.spinics.net/lists/arm-kernel/msg536181.html [6]. http://git.linaro.org/people/lina.iyer/linux-next.git/shortlog/refs/heads/genpd-v4
PATCH v4 1/9 PM / Domains: Make genpd state allocation dynamic
Allow PM Domain states to be defined dynamically by the drivers. This removes the limitation on the maximum number of states possible for a domain.
--- arch/arm/mach-imx/gpc.c | 17 ++++++++++------- drivers/base/power/domain.c | 35 +++++++++++++++++++++++------------ include/linux/pm_domain.h | 5 ++--- 3 files changed, 35 insertions(+), 22 deletions(-)
arch/arm/mach-imx/gpc.c +10 -7
@@ -380,13 +380,6 @@ static struct pu_domain imx6q_pu_domain = { .name = "PU", .power_off = imx6q_pm_pu_power_off, .power_on = imx6q_pm_pu_power_on, - .states = { - [0] = { - .power_off_latency_ns = 25000, - .power_on_latency_ns = 2000000, - }, - }, - .state_count = 1, }, };
@@ -430,6 +423,16 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)if (!IS_ENABLED (CONFIG_PM_GENERIC_DOMAINS ))return 0 ; + imx6q_pu_domain.base.states =devm_kzalloc (dev, +sizeof (*imx6q_pu_domain.base.states), +GFP_KERNEL ); +if (!imx6q_pu_domain.base.states) +return -ENOMEM ; + + imx6q_pu_domain.base.states[0 ].power_off_latency_ns =25000 ; + imx6q_pu_domain.base.states[0 ].power_on_latency_ns =2000000 ; + imx6q_pu_domain.base.state_count =1 ; +pm_genpd_init (&imx6q_pu_domain.base,NULL , false);return of_genpd_add_provider_onecell (dev->of_node, &imx_gpc_onecell_data);
drivers/base/power/domain.c +23 -12
@@ -1282,6 +1282,21 @@ out: }EXPORT_SYMBOL_GPL (pm_genpd_remove_subdomain); +static int genpd_set_default_power_state (struct generic_pm_domain *genpd) +{ +struct genpd_power_state *state; + + state =kzalloc (sizeof (*state),GFP_KERNEL ); +if (!state) +return -ENOMEM ; + + genpd->states = state; + genpd->state_count =1 ; + genpd->free = state; + +return 0 ; +} +/** * pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize.
@@ -1293,6 +1308,8 @@ EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off) { +int ret; +if (IS_ERR_OR_NULL (genpd))return -EINVAL ;
@@ -1325,19 +1342,12 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->dev_ops.start = pm_clk_resume; } -if (genpd->state_idx >=GENPD_MAX_NUM_STATES ) { -pr_warn ("Initial state index out of bounds.\n" ); - genpd->state_idx =GENPD_MAX_NUM_STATES -1 ; - } - -if (genpd->state_count >GENPD_MAX_NUM_STATES ) { -pr_warn ("Limiting states to %d\n" ,GENPD_MAX_NUM_STATES ); - genpd->state_count =GENPD_MAX_NUM_STATES ; - } -/* Use only one "off" state if there were no states declared */ -if (genpd->state_count ==0 ) - genpd->state_count =1 ; +if (genpd->state_count ==0 ) { + ret =genpd_set_default_power_state (genpd); +if (ret) +return ret; + }mutex_lock (&gpd_list_lock);list_add (&genpd->gpd_list_node, &gpd_list);
@@ -1377,6 +1387,7 @@ static int genpd_remove(struct generic_pm_domain *genpd)list_del (&genpd->gpd_list_node);mutex_unlock (&genpd->lock);cancel_work_sync (&genpd->power_off_work); +kfree (genpd->free);pr_debug ("%s: removed %s\n" , __func__, genpd->name);return 0 ;
include/linux/pm_domain.h +2 -3
@@ -19,8 +19,6 @@/* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */ -#define GENPD_MAX_NUM_STATES 8/* Number of possible low power states */ -enum gpd_status {GPD_STATE_ACTIVE =0 ,/* PM domain is active */ GPD_STATE_POWER_OFF ,/* PM domain is off */
@@ -70,9 +68,10 @@ struct generic_pm_domain {void (*detach_dev)(struct generic_pm_domain *domain,struct device *dev);unsigned int flags;/* Bit field of configs for genpd */ -struct genpd_power_state states[GENPD_MAX_NUM_STATES ]; +struct genpd_power_state *states;unsigned int state_count;/* number of states */ unsigned int state_idx;/* state that genpd will go to when off */ +void *free;/* Free the state that was allocated for default */ };
Signature · 1 line
2.7.4
PATCH v4 2/9 PM / Domain: Add residency property to genpd states
In reply to Lina IyerResidency of a domain's idle state indicates that the minimum idle time for the domain's idle state to be beneficial for power. Add the parameter to the state node. Future patches, will use the residency value in the genpd governor to determine if it is worth while to enter an idle state.
--- include/linux/pm_domain.h | 1 + 1 file changed, 1 insertion(+)
include/linux/pm_domain.h +1 -0
@@ -38,6 +38,7 @@ struct gpd_dev_ops {struct genpd_power_state {s64 power_off_latency_ns;s64 power_on_latency_ns; +s64 residency_ns; };struct generic_pm_domain {
Signature · 1 line
2.7.4
PATCH v4 3/9 PM / Domains: Allow domain power states to be read from DT
In reply to Lina IyerThis patch allows domains to define idle states in the DT. SoC's can define domain idle states in DT using the "domain-idle-states" property of the domain provider. Add API to read the idle states from DT that can be set in the genpd object.
This patch is based on the original patch by Marc Titinger.
--- drivers/base/power/domain.c | 94 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 8 ++++ 2 files changed, 102 insertions(+)
drivers/base/power/domain.c +94 -0
@@ -1916,6 +1916,100 @@ out:return ret ? -EPROBE_DEFER :0 ; }EXPORT_SYMBOL_GPL (genpd_dev_pm_attach); + +static const struct of_device_id idle_state_match[] = { + { .compatible ="domain-idle-state" , }, + { } +}; + +static int genpd_parse_state (struct genpd_power_state *genpd_state, +struct device_node *state_node) +{ +int err; +u32 residency; +u32 entry_latency, exit_latency; +const struct of_device_id *match_id; + + match_id =of_match_node (idle_state_match, state_node); +if (!match_id) +return -EINVAL ; + + err =of_property_read_u32 (state_node,"entry-latency-us" , + &entry_latency); +if (err) { +pr_debug (" * %s missing entry-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + err =of_property_read_u32 (state_node,"exit-latency-us" , + &exit_latency); +if (err) { +pr_debug (" * %s missing exit-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + err =of_property_read_u32 (state_node,"min-residency-us" , &residency); +if (!err) + genpd_state->residency_ns =1000 * residency; + + genpd_state->power_on_latency_ns =1000 * exit_latency; + genpd_state->power_off_latency_ns =1000 * entry_latency; + +return 0 ; +} + +/** +* of_genpd_parse_idle_states: Return array of idle states for the genpd. +* +* @dn: The genpd device node +* @states: The pointer to which the state array will be saved. +* @n: The count of elements in the array returned from this function. +* +* Returns the device states parsed from the OF node. The memory for the states +* is allocated by this function and is the responsibility of the caller to +* free the memory after use. +*/ +int of_genpd_parse_idle_states (struct device_node *dn, +struct genpd_power_state **states,int *n) +{ +struct genpd_power_state *st; +struct device_node *np; +int i =0 ; +int err, ret; +int count; +struct of_phandle_iterator it; + + count =of_count_phandle_with_args (dn,"domain-idle-states" ,NULL ); +if (!count) +return -EINVAL ; + + st =kcalloc (count,sizeof (*st),GFP_KERNEL ); +if (!st) +return -ENOMEM ; + +/* Loop over the phandles until all the requested entry is found */ +of_for_each_phandle (&it, err, dn,"domain-idle-states" ,NULL ,0 ) { + np = it.node; + ret =genpd_parse_state (&st[i++], np); +if (ret) { +pr_err + ("Parsing idle state node %s failed with err %d\n" , + np->full_name, ret); +of_node_put (np); +kfree (st); +return ret; + } + } + + *n = count; + *states = st; + +return 0 ; +} +EXPORT_SYMBOL_GPL (of_genpd_parse_idle_states); +#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
include/linux/pm_domain.h +8 -0
@@ -205,6 +205,8 @@ extern int of_genpd_add_device(struct of_phandle_args *args,extern int of_genpd_add_subdomain (struct of_phandle_args *parent,struct of_phandle_args *new_subdomain);extern struct generic_pm_domain *of_genpd_remove_last (struct device_node *np); +extern int of_genpd_parse_idle_states (struct device_node *dn, +struct genpd_power_state **states,int *n);int genpd_dev_pm_attach (struct device *dev);#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
@@ -234,6 +236,12 @@ static inline int of_genpd_add_subdomain(struct of_phandle_args *parent,return -ENODEV ; } +static inline int of_genpd_parse_idle_states (struct device_node *dn, +struct genpd_power_state **states,int *n) +{ +return -ENODEV ; +} +static inline int genpd_dev_pm_attach (struct device *dev) {return -ENODEV ;
Signature · 1 line
2.7.4
PATCH v4 4/9 dt-bindings: add domain-idle-state compatible to arm, idle-state
In reply to Lina IyerCPU's idle states are defined by the arm,idle-state compatible flag. See [1]. PM domains that can contains devices and other domains also has similar definition for its idle state. Reuse the definition of arm,idle-state for PM domains by allowing an addition compatible string ("domain-idle-state") to denote idle states that are specific to PM Domains.
[1]. Documentation/devicetree/bindings/arm/idle-states.txt
--- Documentation/devicetree/bindings/arm/idle-states.txt | 3 +++ 1 file changed, 3 insertions(+)
Documentation/devicetree/bindings/arm/idle-states.txt +3 -0
@@ -271,6 +271,9 @@ follows: Usage: Required Value type: <stringlist> Definition: Must be "arm,idle-state". + Additionally, nodes that are used to describe a + idle-state of PM domain must also define + "domain-idle-state" as compatible string. - local-timer-stop Usage: See definition
Signature · 1 line
2.7.4
PATCH v4 5/9 dt-bindings: Update binding for PM domain idle states
In reply to Lina IyerUpdate DT bindings to describe idle states of PM domains.
This patch is based on the original patch by Marc Titinger.
--- .../devicetree/bindings/power/power_domain.txt | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+)
Documentation/devicetree/bindings/power/power_domain.txt +44 -0
@@ -29,6 +29,16 @@ Optional properties: specified by this binding. More details about power domain specifier are available in the next section. +- domain-idle-states : A phandle of an idle-state that shall be soaked into a + generic domain power state. The idle state definitions must be + compatible with "domain-idle-state" as well as + "arm,idle-state" as defined in [1]. + The domain-idle-state property reflects the idle state of this PM domain and + not the idle states of the devices or sub-domains in the PM domain. Devices + and sub-domains have their own idle-states independent of the parent + domain's idle states. In the absence of this property, the domain would be + considered as capable of being powered-on or powered-off. + Example: power: power-controller at 12340000 {
@@ -59,6 +69,38 @@ The nodes above define two power controllers: 'parent' and 'child'. Domains created by the 'child' power controller are subdomains of '0' power domain provided by the 'parent' power controller. +Example 3: + parent: power-controller at 12340000 { + compatible = "foo,power-controller"; + reg = <0x12340000 0x1000>; + #power-domain-cells = <0>; + domain-idle-states = <&DOMAIN_RET>, <&DOMAIN_PWR_DN>; + }; + + child: power-controller at 12341000 { + compatible = "foo,power-controller"; + reg = <0x12341000 0x1000>; + power-domains = <&parent 0>; + #power-domain-cells = <0>; + domain-idle-states = <&DOMAIN_PWR_DN>; + }; + + DOMAIN_RET: state at 0 { + compatible = "domain-idle-state", "arm,idle-state"; + reg = <0x0>; + entry-latency-us = <1000>; + exit-latency-us = <2000>; + min-residency-us = <10000>; + }; + + DOMAIN_PWR_DN: state at 1 { + compatible = "domain-idle-state", "arm,idle-state"; + reg = <0x1>; + entry-latency-us = <5000>; + exit-latency-us = <8000>; + min-residency-us = <7000>; + }; + ==PM domain consumers== Required properties:
@@ -76,3 +118,5 @@ Example: The node above defines a typical PM domain consumer device, which is located inside a PM domain with index 0 of a power controller represented by a node with the label "power". + +[1]. Documentation/devicetree/bindings/arm/idle-states.txt
Signature · 1 line
2.7.4
PATCH v4 6/9 PM / Domains: Save the fwnode in genpd_power_state
In reply to Lina IyerSave the fwnode for the genpd state in the state node. PM Domain clients may use the fwnode to read in the platform specific domain state properties and associate them with the state.
--- drivers/base/power/domain.c | 1 + include/linux/pm_domain.h | 1 + 2 files changed, 2 insertions(+)
drivers/base/power/domain.c +1 -0
@@ -1956,6 +1956,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, genpd_state->power_on_latency_ns =1000 * exit_latency; genpd_state->power_off_latency_ns =1000 * entry_latency; + genpd_state->fwnode = &state_node->fwnode;return 0 ; }
include/linux/pm_domain.h +1 -0
@@ -39,6 +39,7 @@ struct genpd_power_state {s64 power_off_latency_ns;s64 power_on_latency_ns;s64 residency_ns; +struct fwnode_handle *fwnode; };struct generic_pm_domain {
Signature · 1 line
2.7.4
PATCH v4 7/9 PM / Domains: Abstract genpd locking
In reply to Lina IyerAbstract genpd lock/unlock calls, in preparation for domain specific locks added in the following patches.
--- drivers/base/power/domain.c | 121 +++++++++++++++++++++++++++++--------------- include/linux/pm_domain.h | 5 +- 2 files changed, 85 insertions(+), 41 deletions(-)
drivers/base/power/domain.c +81 -40
@@ -39,6 +39,46 @@static LIST_HEAD (gpd_list);static DEFINE_MUTEX (gpd_list_lock); +struct genpd_lock_ops { +void (*lock)(struct generic_pm_domain *genpd); +void (*lock_nested)(struct generic_pm_domain *genpd,int depth); +int (*lock_interruptible)(struct generic_pm_domain *genpd); +void (*unlock)(struct generic_pm_domain *genpd); +}; + +static void genpd_lock_mtx (struct generic_pm_domain *genpd) +{ +mutex_lock (&genpd->mlock); +} + +static void genpd_lock_nested_mtx (struct generic_pm_domain *genpd, +int depth) +{ +mutex_lock_nested (&genpd->mlock, depth); +} + +static int genpd_lock_interruptible_mtx (struct generic_pm_domain *genpd) +{ +return mutex_lock_interruptible (&genpd->mlock); +} + +static void genpd_unlock_mtx (struct generic_pm_domain *genpd) +{ +return mutex_unlock (&genpd->mlock); +} + +static const struct genpd_lock_ops genpd_mtx_ops = { + .lock = genpd_lock_mtx, + .lock_nested = genpd_lock_nested_mtx, + .lock_interruptible = genpd_lock_interruptible_mtx, + .unlock = genpd_unlock_mtx, +}; + +#define genpd_lock (p) p->lock_ops->lock(p) +#define genpd_lock_nested (p, d) p->lock_ops->lock_nested(p, d) +#define genpd_lock_interruptible (p) p->lock_ops->lock_interruptible(p) +#define genpd_unlock (p) p->lock_ops->unlock(p) +/* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer,
@@ -200,9 +240,9 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)genpd_sd_counter_inc (master); -mutex_lock_nested (&master->lock, depth +1 ); +genpd_lock_nested (master, depth +1 ); ret =genpd_poweron (master, depth +1 ); -mutex_unlock (&master->lock); +genpd_unlock (master);if (ret) {genpd_sd_counter_dec (master);
@@ -255,9 +295,9 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,spin_unlock_irq (&dev->power.lock);if (!IS_ERR (genpd)) { -mutex_lock (&genpd->lock); +genpd_lock (genpd); genpd->max_off_time_changed = true; -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); } dev = dev->parent;
@@ -354,9 +394,9 @@ static void genpd_power_off_work_fn(struct work_struct *work) genpd = container_of(work, struct generic_pm_domain, power_off_work); -mutex_lock (&genpd->lock); +genpd_lock (genpd);genpd_poweroff (genpd, true); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); }/**
@@ -472,9 +512,9 @@ static int genpd_runtime_suspend(struct device *dev)if (dev->power.irq_safe)return 0 ; -mutex_lock (&genpd->lock); +genpd_lock (genpd);genpd_poweroff (genpd, false); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);return 0 ; }
@@ -509,9 +549,9 @@ static int genpd_runtime_resume(struct device *dev) goto out; } -mutex_lock (&genpd->lock); +genpd_lock (genpd); ret =genpd_poweron (genpd,0 ); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);if (ret)return ret;
@@ -547,9 +587,9 @@ err_stop:genpd_stop_dev (genpd, dev); err_poweroff:if (!dev->power.irq_safe) { -mutex_lock (&genpd->lock); +genpd_lock (genpd);genpd_poweroff (genpd,0 ); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); }return ret;
@@ -732,20 +772,20 @@ static int pm_genpd_prepare(struct device *dev)if (resume_needed (dev, genpd))pm_runtime_resume (dev); -mutex_lock (&genpd->lock); +genpd_lock (genpd);if (genpd->prepared_count++ ==0 ) genpd->suspended_count =0 ; -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); ret =pm_generic_prepare (dev);if (ret) { -mutex_lock (&genpd->lock); +genpd_lock (genpd); genpd->prepared_count--; -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); }return ret;
@@ -936,13 +976,13 @@ static void pm_genpd_complete(struct device *dev)pm_generic_complete (dev); -mutex_lock (&genpd->lock); +genpd_lock (genpd); genpd->prepared_count--;if (!genpd->prepared_count)genpd_queue_power_off_work (genpd); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd); }/**
@@ -1071,7 +1111,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,if (IS_ERR (gpd_data))return PTR_ERR (gpd_data); -mutex_lock (&genpd->lock); +genpd_lock (genpd);if (genpd->prepared_count >0 ) { ret = -EAGAIN ;
@@ -1088,7 +1128,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,list_add_tail (&gpd_data->base.list_node, &genpd->dev_list); out: -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);if (ret)genpd_free_dev_data (dev, gpd_data);
@@ -1130,7 +1170,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, gpd_data =to_gpd_data (pdd);dev_pm_qos_remove_notifier (dev, &gpd_data->nb); -mutex_lock (&genpd->lock); +genpd_lock (genpd);if (genpd->prepared_count >0 ) { ret = -EAGAIN ;
@@ -1145,14 +1185,14 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,list_del_init (&pdd->list_node); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);genpd_free_dev_data (dev, gpd_data);return 0 ; out: -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);dev_pm_qos_add_notifier (dev, &gpd_data->nb);return ret;
@@ -1187,8 +1227,8 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,if (!link)return -ENOMEM ; -mutex_lock (&subdomain->lock); -mutex_lock_nested (&genpd->lock,SINGLE_DEPTH_NESTING ); +genpd_lock (subdomain); +genpd_lock_nested (genpd,SINGLE_DEPTH_NESTING );if (genpd->status ==GPD_STATE_POWER_OFF && subdomain->status !=GPD_STATE_POWER_OFF ) {
@@ -1211,8 +1251,8 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,genpd_sd_counter_inc (genpd); out: -mutex_unlock (&genpd->lock); -mutex_unlock (&subdomain->lock); +genpd_unlock (genpd); +genpd_unlock (subdomain);if (ret)kfree (link);return ret;
@@ -1250,8 +1290,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,if (IS_ERR_OR_NULL (genpd) ||IS_ERR_OR_NULL (subdomain))return -EINVAL ; -mutex_lock (&subdomain->lock); -mutex_lock_nested (&genpd->lock,SINGLE_DEPTH_NESTING ); +genpd_lock (subdomain); +genpd_lock_nested (genpd,SINGLE_DEPTH_NESTING );if (!list_empty (&subdomain->master_links) || subdomain->device_count) { pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
@@ -1275,8 +1315,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, } out: -mutex_unlock (&genpd->lock); -mutex_unlock (&subdomain->lock); +genpd_unlock (genpd); +genpd_unlock (subdomain);return ret; }
@@ -1316,7 +1356,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd,INIT_LIST_HEAD (&genpd->master_links);INIT_LIST_HEAD (&genpd->slave_links);INIT_LIST_HEAD (&genpd->dev_list); -mutex_init (&genpd->lock); +mutex_init (&genpd->mlock); + genpd->lock_ops = &genpd_mtx_ops; genpd->gov = gov;INIT_WORK (&genpd->power_off_work, genpd_power_off_work_fn);atomic_set (&genpd->sd_count,0 );
@@ -1364,16 +1405,16 @@ static int genpd_remove(struct generic_pm_domain *genpd)if (IS_ERR_OR_NULL (genpd))return -EINVAL ; -mutex_lock (&genpd->lock); +genpd_lock (genpd);if (genpd->has_provider) { -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);pr_err ("Provider present, unable to remove %s\n" , genpd->name);return -EBUSY ; }if (!list_empty (&genpd->master_links) || genpd->device_count) { -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);pr_err ("%s: unable to remove %s\n" , __func__, genpd->name);return -EBUSY ; }
@@ -1385,7 +1426,7 @@ static int genpd_remove(struct generic_pm_domain *genpd) }list_del (&genpd->gpd_list_node); -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);cancel_work_sync (&genpd->power_off_work);kfree (genpd->free);pr_debug ("%s: removed %s\n" , __func__, genpd->name);
@@ -1909,9 +1950,9 @@ int genpd_dev_pm_attach(struct device *dev) dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; -mutex_lock (&pd->lock); +genpd_lock (pd); ret =genpd_poweron (pd,0 ); -mutex_unlock (&pd->lock); +genpd_unlock (pd); out:return ret ? -EPROBE_DEFER :0 ; }
@@ -2064,7 +2105,7 @@ static int pm_genpd_summary_one(struct seq_file *s,char state[16 ];int ret; - ret =mutex_lock_interruptible (&genpd->lock); + ret =genpd_lock_interruptible (genpd);if (ret)return -ERESTARTSYS ;
@@ -2101,7 +2142,7 @@ static int pm_genpd_summary_one(struct seq_file *s,seq_puts (s,"\n" ); exit: -mutex_unlock (&genpd->lock); +genpd_unlock (genpd);return 0 ; }
include/linux/pm_domain.h +4 -1
@@ -42,13 +42,14 @@ struct genpd_power_state {struct fwnode_handle *fwnode; }; +struct genpd_lock_ops ; +struct generic_pm_domain {struct dev_pm_domain domain;/* PM domain operations */ struct list_head gpd_list_node;/* Node in the global PM domains list */ struct list_head master_links;/* Links with PM domain as a master */ struct list_head slave_links;/* Links with PM domain as a slave */ struct list_head dev_list;/* List of devices */ -struct mutex lock;struct dev_power_governor *gov;struct work_struct power_off_work;struct fwnode_handle *provider;/* Identity of the domain provider */
@@ -74,6 +75,8 @@ struct generic_pm_domain {unsigned int state_count;/* number of states */ unsigned int state_idx;/* state that genpd will go to when off */ void *free;/* Free the state that was allocated for default */ +const struct genpd_lock_ops *lock_ops; +struct mutex mlock; };
Signature · 1 line
2.7.4
PATCH v4 8/9 PM / Domains: Support IRQ safe PM domains
In reply to Lina IyerGeneric Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
To achieve domain specific locking, set the domain's ->flag to GENPD_FLAG_IRQ_SAFE while defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted through genpd_lock() and genpd_unlock() functions that use the flag to determine the appropriate lock to be used for that domain.
Domains that have lower latency to suspend and resume and can operate with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that non-IRQ safe domains may not have IRQ-safe subdomains, but IRQ safe domains may have IRQ safe and non-IRQ safe subdomains and devices.
--- drivers/base/power/domain.c | 111 ++++++++++++++++++++++++++++++++++++++++---- include/linux/pm_domain.h | 10 +++- 2 files changed, 110 insertions(+), 11 deletions(-)
drivers/base/power/domain.c +101 -10
@@ -74,11 +74,70 @@ static const struct genpd_lock_ops genpd_mtx_ops = { .unlock = genpd_unlock_mtx, }; +static void genpd_lock_spin (struct generic_pm_domain *genpd) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave (&genpd->slock, flags); + genpd->lock_flags = flags; +} + +static void genpd_lock_nested_spin (struct generic_pm_domain *genpd, +int depth) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave_nested (&genpd->slock, flags, depth); + genpd->lock_flags = flags; +} + +static int genpd_lock_interruptible_spin (struct generic_pm_domain *genpd) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave (&genpd->slock, flags); + genpd->lock_flags = flags; +return 0 ; +} + +static void genpd_unlock_spin (struct generic_pm_domain *genpd) + __releases(&genpd->slock) +{ +spin_unlock_irqrestore (&genpd->slock, genpd->lock_flags); +} + +static const struct genpd_lock_ops genpd_spin_ops = { + .lock = genpd_lock_spin, + .lock_nested = genpd_lock_nested_spin, + .lock_interruptible = genpd_lock_interruptible_spin, + .unlock = genpd_unlock_spin, +}; +#define genpd_lock (p) p->lock_ops->lock(p)#define genpd_lock_nested (p, d) p->lock_ops->lock_nested(p, d)#define genpd_lock_interruptible (p) p->lock_ops->lock_interruptible(p)#define genpd_unlock (p) p->lock_ops->unlock(p) +#define genpd_is_irq_safe (genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) + +static inline bool irq_safe_dev_in_no_sleep_domain (struct device *dev, +struct generic_pm_domain *genpd) +{ +bool ret; + + ret =pm_runtime_is_irq_safe (dev) && !genpd_is_irq_safe (genpd); + +/* Warn once for each IRQ safe dev in no sleep domain */ +if (ret) +dev_warn_once (dev,"PM domain %s will not be powered off\n" , + genpd->name); + +return ret; +} +/* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer,
@@ -343,7 +402,12 @@ static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async)if (stat >PM_QOS_FLAGS_NONE )return -EBUSY ; -if (!pm_runtime_suspended (pdd->dev) || pdd->dev->power.irq_safe) +/* +* Do not allow PM domain to be powered off, when an IRQ safe +* device is part of a non-IRQ safe domain. +*/ +if (!pm_runtime_suspended (pdd->dev) || +irq_safe_dev_in_no_sleep_domain (pdd->dev, genpd)) not_suspended++; }
@@ -506,10 +570,10 @@ static int genpd_runtime_suspend(struct device *dev) }/* -* If power.irq_safe is set, this routine will be run with interrupts -* off, so it can't use mutexes. +* If power.irq_safe is set, this routine may be run with +* IRQs disabled, so suspend only if the PM domain also is irq_safe. */ -if (dev->power.irq_safe) +if (irq_safe_dev_in_no_sleep_domain (dev, genpd))return 0 ;genpd_lock (genpd);
@@ -543,8 +607,11 @@ static int genpd_runtime_resume(struct device *dev)if (IS_ERR (genpd))return -EINVAL ; -/* If power.irq_safe, the PM domain is never powered off. */ -if (dev->power.irq_safe) { +/* +* As we don't power off a non IRQ safe domain, which holds +* an IRQ safe device, we don't need to restore power to it. +*/ +if (irq_safe_dev_in_no_sleep_domain (dev, genpd)) { timed = false; goto out; }
@@ -586,7 +653,8 @@ static int genpd_runtime_resume(struct device *dev) err_stop:genpd_stop_dev (genpd, dev); err_poweroff: -if (!dev->power.irq_safe) { +if (!pm_runtime_is_irq_safe (dev) || + (pm_runtime_is_irq_safe (dev) &&genpd_is_irq_safe (genpd))) {genpd_lock (genpd);genpd_poweroff (genpd,0 );genpd_unlock (genpd);
@@ -1223,6 +1291,17 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd, || genpd == subdomain)return -EINVAL ; +/* +* If the domain can be powered on/off in an IRQ safe +* context, ensure that the subdomain can also be +* powered on/off in that context. +*/ +if (!genpd_is_irq_safe (genpd) &&genpd_is_irq_safe (subdomain)) { +WARN ("Parent %s of subdomain %s must be IRQ safe\n" , + genpd->name, subdomain->name); +return -EINVAL ; + } + link =kzalloc (sizeof (*link),GFP_KERNEL );if (!link)return -ENOMEM ;
@@ -1337,6 +1416,17 @@ static int genpd_set_default_power_state(struct generic_pm_domain *genpd)return 0 ; } +static void genpd_lock_init (struct generic_pm_domain *genpd) +{ +if (genpd->flags &GENPD_FLAG_IRQ_SAFE ) { +spin_lock_init (&genpd->slock); + genpd->lock_ops = &genpd_spin_ops; + }else { +mutex_init (&genpd->mlock); + genpd->lock_ops = &genpd_mtx_ops; + } +} +/** * pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize.
@@ -1356,8 +1446,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,INIT_LIST_HEAD (&genpd->master_links);INIT_LIST_HEAD (&genpd->slave_links);INIT_LIST_HEAD (&genpd->dev_list); -mutex_init (&genpd->mlock); - genpd->lock_ops = &genpd_mtx_ops; +genpd_lock_init (genpd); genpd->gov = gov;INIT_WORK (&genpd->power_off_work, genpd_power_off_work_fn);atomic_set (&genpd->sd_count,0 );
@@ -2131,7 +2220,9 @@ static int pm_genpd_summary_one(struct seq_file *s, }list_for_each_entry (pm_data, &genpd->dev_list, list_node) { - kobj_path =kobject_get_path (&pm_data->dev->kobj,GFP_KERNEL ); + kobj_path =kobject_get_path (&pm_data->dev->kobj, +genpd_is_irq_safe (genpd) ? +GFP_ATOMIC :GFP_KERNEL );if (kobj_path ==NULL )continue ;
include/linux/pm_domain.h +9 -1
@@ -15,9 +15,11 @@#include <linux/err.h> #include <linux/of.h> #include <linux/notifier.h> +#include <linux/spinlock.h> /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */ +#define GENPD_FLAG_IRQ_SAFE (1U << 1)/* PM domain operates in atomic */ enum gpd_status {GPD_STATE_ACTIVE =0 ,/* PM domain is active */
@@ -76,7 +78,13 @@ struct generic_pm_domain {unsigned int state_idx;/* state that genpd will go to when off */ void *free;/* Free the state that was allocated for default */ const struct genpd_lock_ops *lock_ops; -struct mutex mlock; +union { +struct mutex mlock; +struct { +spinlock_t slock; +unsigned long lock_flags; + }; + }; };
Signature · 1 line
2.7.4
PATCH v4 8/9 PM / Domains: Support IRQ safe PM domains
Hi Lina,
Quoted 41 lines · Lina Iyer
Generic Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
To achieve domain specific locking, set the domain's ->flag to
GENPD_FLAG_IRQ_SAFEwhile defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted throughgenpd_lock()andgenpd_unlock()functions that use the flag to determine the appropriate lock to be used for that domain.Domains that have lower latency to suspend and resume and can operate with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that non-IRQ safe domains may not have IRQ-safe subdomains, but IRQ safe domains may have IRQ safe and non-IRQ safe subdomains and devices.
Cc Ulf Hansson <[email protected]>Cc Kevin Hilman <[email protected]>Cc Rafael J. Wysocki <[email protected]>Signed-off-by Lina Iyer <[email protected]>Acked-by Ulf Hansson <[email protected]>--- drivers/base/power/domain.c | 111 ++++++++++++++++++++++++++++++++++++++++---- include/linux/pm_domain.h | 10 +++- 2 files changed, 110 insertions(+), 11 deletions(-)drivers/base/power/domain.c +0
-0
drivers/base/power/domain.c static const struct genpd_lock_ops genpd_mtx_ops = { · line 82
Quoted 9 lines
drivers/base/power/domain.c +8
-0@@ -74,11 +74,70 @@ static const struct genpd_lock_ops genpd_mtx_ops = { +static inline bool irq_safe_dev_in_no_sleep_domain (struct device *dev, +struct generic_pm_domain *genpd) +{ +bool ret; + + ret =pm_runtime_is_irq_safe (dev) && !genpd_is_irq_safe (genpd); + +/* Warn once for each IRQ safe dev in no sleep domain */
This comment is not correct, as dev_warn_once() will print the warning once, not once per device. Hence users will not be informed if there are multiple IRQ safe devices.
drivers/base/power/domain.c static const struct genpd_lock_ops genpd_mtx_ops = { · line 136
Quoted 3 lines
drivers/base/power/domain.c +3
-0+if (ret) +dev_warn_once (dev,"PM domain %s will not be powered off\n" , + genpd->name);
BTW, I'm seeing messages like:
sh_cmt ffca0000.timer: PM domain always-on will not be powered off
and
sh_cmt e6138000.timer: PM domain c5 will not be powered off
on various Renesas SoCs, and wanted to know if there was more than one IRQ safe device preventing a PM domain power down (answer: there isn't). Note that the above are OK, as both "always-on" and "c5" are always-on PM domains.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Re: [PATCH v4 8/9] PM / Domains: Support IRQ safe PM domains
In reply to Lina IyerHi Lina,
Yes, this is a reply to an old patch.
But it seems there are no upstream users of GENPD_FLAG_IRQ_SAFE yet...
Quoted 51 lines · Lina Iyer
Generic Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
To achieve domain specific locking, set the domain's ->flag to
GENPD_FLAG_IRQ_SAFEwhile defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted throughgenpd_lock()andgenpd_unlock()functions that use the flag to determine the appropriate lock to be used for that domain.Domains that have lower latency to suspend and resume and can operate with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that non-IRQ safe domains may not have IRQ-safe subdomains, but IRQ safe domains may have IRQ safe and non-IRQ safe subdomains and devices.
Cc Ulf Hansson <[email protected]>Cc Kevin Hilman <[email protected]>Cc Rafael J. Wysocki <[email protected]>Signed-off-by Lina Iyer <[email protected]>Acked-by Ulf Hansson <[email protected]>--- drivers/base/power/domain.c | 111 ++++++++++++++++++++++++++++++++++++++++---- include/linux/pm_domain.h | 10 +++- 2 files changed, 110 insertions(+), 11 deletions(-)drivers/base/power/domain.c +7
-0@@ -74,11 +74,70 @@ static const struct genpd_lock_ops genpd_mtx_ops = { .unlock = genpd_unlock_mtx, }; +static void genpd_lock_spin (struct generic_pm_domain *genpd) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave (&genpd->slock, flags); + genpd->lock_flags = flags;
drivers/base/power/domain.c static const struct genpd_lock_ops genpd_mtx_ops = { · line 93
Quoted 10 lines
drivers/base/power/domain.c +10
-0+} + +static void genpd_lock_nested_spin (struct generic_pm_domain *genpd, +int depth) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave_nested (&genpd->slock, flags, depth); + genpd->lock_flags = flags;
I don't think this is correct: during the second or any subsequent call, the old genpd->lock_flags will be overwritten by the new one. As interrupts are already disabled when doing a second call, the new flags will reflect this, and calling genpd_unlock_spin() below will never re-enable interrupts again.
Note that changing the above to
if (!depth)
genpd->lock_flags = flags;won't help, as that means the first call to genpd_unlock_spin() will re-enable interrupts, which is too early.
Using a single genpd->lock_flags can work only in case interrupts were already disabled before the first call. But perhaps that's guaranteed?
Apart from that, given depth is used as a lockdep subclass, and the following comment in include/linux/lockdep.h:
/*
* For trivial one-depth nesting of a lock-class, the following
* global define can be used. (Subsystems with multiple levels
* of nesting should define their own lock-nesting subclasses.) */
#define SINGLE_DEPTH_NESTING 1
I'm wondering if depth > 1 actually works as expected.
Am I missing something?
Thanks!
Quoted 7 lines
drivers/base/power/domain.c +7
-0+} + +static int genpd_lock_interruptible_spin (struct generic_pm_domain *genpd) + __acquires(&genpd->slock) +{ +unsigned long flags; +
drivers/base/power/domain.c static const struct genpd_lock_ops genpd_mtx_ops = { · line 110
Quoted 10 lines
drivers/base/power/domain.c +10
-0+spin_lock_irqsave (&genpd->slock, flags); + genpd->lock_flags = flags; +return 0 ; +} + +static void genpd_unlock_spin (struct generic_pm_domain *genpd) + __releases(&genpd->slock) +{ +spin_unlock_irqrestore (&genpd->slock, genpd->lock_flags); +}
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Re: [PATCH v4 8/9] PM / Domains: Support IRQ safe PM domains
Quoted 5 lines · Geert Uytterhoeven
Hi Lina,
Yes, this is a reply to an old patch.
But it seems there are no upstream users ofGENPD_FLAG_IRQ_SAFEyet...
Correct. But I are working on adding the first ones, as you probably know.
Quoted 61 lines · Lina Iyer
Quoted 59 lines
Generic Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
To achieve domain specific locking, set the domain's ->flag to
GENPD_FLAG_IRQ_SAFEwhile defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted throughgenpd_lock()andgenpd_unlock()functions that use the flag to determine the appropriate lock to be used for that domain.Domains that have lower latency to suspend and resume and can operate with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that non-IRQ safe domains may not have IRQ-safe subdomains, but IRQ safe domains may have IRQ safe and non-IRQ safe subdomains and devices.
Cc Ulf Hansson <[email protected]>Cc Kevin Hilman <[email protected]>Cc Rafael J. Wysocki <[email protected]>Signed-off-by Lina Iyer <[email protected]>Acked-by Ulf Hansson <[email protected]>--- drivers/base/power/domain.c | 111 ++++++++++++++++++++++++++++++++++++++++---- include/linux/pm_domain.h | 10 +++- 2 files changed, 110 insertions(+), 11 deletions(-)drivers/base/power/domain.c +15
-0@@ -74,11 +74,70 @@ static const struct genpd_lock_ops genpd_mtx_ops = { .unlock = genpd_unlock_mtx, }; +static void genpd_lock_spin (struct generic_pm_domain *genpd) + __acquires(&genpd->slock) +{ +unsigned long flags; + +spin_lock_irqsave (&genpd->slock, flags); + genpd->lock_flags = flags; +} + +static void genpd_lock_nested_spin (struct generic_pm_domain *genpd, +int depth) + __acquires(&genpd->slock) +{ +unsigned long flags; +
Quoted 8 lines
drivers/base/power/domain.c static const struct genpd_lock_ops genpd_mtx_ops = { · line 93
Quoted 2 lines
drivers/base/power/domain.c +2
-0+spin_lock_irqsave_nested (&genpd->slock, flags, depth); + genpd->lock_flags = flags;I don't think this is correct: during the second or any subsequent call, the old
genpd->lock_flagswill be overwritten by the new one. As interrupts are already disabled when doing a second call, the new flags will reflect this, and callinggenpd_unlock_spin()below will never re-enable interrupts again.
That seems like a correct observation, at least as far as I can tell. Thanks for looking into this!
Quoted 11 lines
Note that changing the above to
if (!depth) genpd->lock_flags = flags;won't help, as that means the first call to
genpd_unlock_spin()will re-enable interrupts, which is too early.Using a single
genpd->lock_flagscan work only in case interrupts were already disabled before the first call. But perhaps that's guaranteed?
So far, the only user case I have been playing with, is for CPUs. In that case, this code in executed in the cpuidle path and thus IRQs are disabled, so yes then it works.
Quoted 11 lines
Apart from that, given depth is used as a lockdep subclass, and the following comment in
include/linux/lockdep.h:/*
* For trivial one-depth nesting of a lock-class, the following
* global define can be used. (Subsystems with multiple levels
* of nesting should define their own lock-nesting subclasses.) */
#defineSINGLE_DEPTH_NESTING1
Quoted 3 lines
I'm wondering if depth > 1 actually works as expected.
Am I missing something?
I don't think so, we need to fix this problem, in one way or the other.
Clearly this code hasn't been executed enough, probably only by me and Lina and for CPUs. Ideally, we should try to make it work for the generic case, as I think there are more potential users of it.
[...]
Kind regards
Uffe
PATCH v4 9/9 PM / doc: Update device documentation for devices in IRQ safe PM domains
In reply to Lina IyerUpdate documentation to reflect the changes made to support IRQ safe PM domains.
--- Documentation/power/devices.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
Documentation/power/devices.txt +13 -1
@@ -607,7 +607,9 @@ individually. Instead, a set of devices sharing a power resource can be put into a low-power state together at the same time by turning off the shared power resource. Of course, they also need to be put into the full-power state together, by turning the shared power resource on. A set of devices with this -property is often referred to as a power domain. +property is often referred to as a power domain. A power domain may also be +nested inside another power domain. The nested domain is referred to as the +sub-domain of the parent domain. Support for power domains is provided through the pm_domain field of struct device. This field is a pointer to an object of type struct dev_pm_domain,
@@ -629,6 +631,16 @@ support for power domains into subsystem-level callbacks, for example by modifying the platform bus type. Other platforms need not implement it or take it into account in any way. +Devices may be defined as IRQ-safe which indicates to the PM core that their +runtime PM callbacks may be invoked with disabled interrupts (see +Documentation/power/runtime_pm.txt for more information). If an IRQ-safe +device belongs to a PM domain, the runtime PM of the domain will be +disallowed, unless the domain itself is defined as IRQ-safe. However, it +makes sense to define a PM domain as IRQ-safe only if all the devices in it +are IRQ-safe. Moreover, if an IRQ-safe domain has a parent domain, the runtime +PM of the parent is only allowed if the parent itself is IRQ-safe too with the +additional restriction that all child domains of an IRQ-safe parent must also +be IRQ-safe. Device Low Power (suspend) States ---------------------------------
Signature · 1 line
2.7.4
v4 cover PM / Domains: DT support for domain idle states & atomic PM domains
In reply to Lina IyerQuoted 8 lines · Lina Iyer
Hi all,
Changes since v3 [5] -
- Added "domain-idle-state" compatible toarm/idle-states.txt
- Updated documentation for defining PM domain idle states
- Updated documentation for devices and PM domains.
- Updated code to look for "domain-idle-state" instead of "arm,idle-state" compatible.
- The series is available at [6].
Is this the series I've already queued up?
If so, can you please send a delta patch only with a proper changelog?
Thanks,
Rafael
v4 cover PM / Domains: DT support for domain idle states & atomic PM domains
Quoted 12 lines · Rafael J. Wysocki
Quoted 8 lines · Lina Iyer
Hi all,
Changes since v3 [5] -
- Added "domain-idle-state" compatible toarm/idle-states.txt
- Updated documentation for defining PM domain idle states
- Updated documentation for devices and PM domains.
- Updated code to look for "domain-idle-state" instead of "arm,idle-state" compatible.
- The series is available at [6].Is this the series I've already queued up?
Yes. It is the update to the series that you already queued up.
Quoted 2 lines
If so, can you please send a delta patch only with a proper changelog?
Sure. Will do that.
Thanks,
Lina