diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 137570fe0da2..fac0814f60ae 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -45,16 +45,17 @@ struct pse_control {
/**
* struct pse_power_domain - a PSE power domain
* @id: ID of the power domain
- * @supply: Power supply the Power Domain
* @refcnt: Number of gets of this pse_power_domain
* @budget_eval_strategy: Current power budget evaluation strategy of the
* power domain
+ * @pis: PIs that use the power domain, under pse_pw_d_mutex. The supply
+ * handles of their regulators all point at the same supply.
*/
struct pse_power_domain {
int id;
- struct regulator *supply;
struct kref refcnt;
u32 budget_eval_strategy;
+ struct list_head pis;
};
static int of_load_single_pse_pi_pairset(struct device_node *node,@@ -442,7 +443,7 @@ static void pse_pi_deallocate_pw_budget(struct pse_pi *pi)
if (!pi->pw_d || !pi->pw_allocated_mW)
return;
- regulator_free_power_budget(pi->pw_d->supply, pi->pw_allocated_mW);
+ regulator_free_power_budget(pi->rdev->supply, pi->pw_allocated_mW);
pi->pw_allocated_mW = 0;
}
@@ -555,7 +556,8 @@ pse_pi_allocate_pw_budget_static_prio(struct pse_controller_dev *pcdev, int id,
int ret, _prio;
_prio = pcdev->nr_lines;
- while (regulator_request_power_budget(pi->pw_d->supply, pw_req) == -ERANGE) {
+ while (regulator_request_power_budget(pi->rdev->supply, pw_req) ==
+ -ERANGE) {
if (_prio <= pi->prio) {
NL_SET_ERR_MSG_FMT(extack,
"PI %d: not enough power budget available",@@ -890,24 +892,17 @@ devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
}
/* Release for a put made with pse_pw_d_mutex already held. */
-static void pse_pw_d_release_locked(struct kref *kref)
+static void pse_pw_d_release(struct kref *kref)
{
struct pse_power_domain *pw_d = container_of(kref,
struct pse_power_domain,
refcnt);
lockdep_assert_held(&pse_pw_d_mutex);
- regulator_put(pw_d->supply);
xa_erase(&pse_pw_d_map, pw_d->id);
kfree(pw_d);
}
-static void __pse_pw_d_release(struct kref *kref)
-{
- pse_pw_d_release_locked(kref);
- mutex_unlock(&pse_pw_d_mutex);
-}
-
/* Clear pi[id].pw_d before the domain's reference is dropped. The PI
* regulator ops and pse_isr() hold pcdev->lock while they use it.
*/@@ -924,6 +919,14 @@ pse_pi_take_pw_d(struct pse_controller_dev *pcdev, int id)
return pw_d;
}
+/* Drop @pi from @pw_d, with pse_pw_d_mutex held */
+static void pse_pw_d_leave(struct pse_power_domain *pw_d, struct pse_pi *pi)
+{
+ lockdep_assert_held(&pse_pw_d_mutex);
+ list_del(&pi->pw_d_node);
+ kref_put(&pw_d->refcnt, pse_pw_d_release);
+}
+
/**
* pse_flush_pw_ds - flush all PSE power domains of a PSE
* @pcdev: a pointer to the initialized PSE controller device@@ -933,18 +936,13 @@ static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)
struct pse_power_domain *pw_d;
int i;
+ mutex_lock(&pse_pw_d_mutex);
for (i = 0; i < pcdev->nr_lines; i++) {
pw_d = pse_pi_take_pw_d(pcdev, i);
- if (!pw_d)
- continue;
-
- pw_d = xa_load(&pse_pw_d_map, pw_d->id);
- if (!pw_d)
- continue;
-
- kref_put_mutex(&pw_d->refcnt, __pse_pw_d_release,
- &pse_pw_d_mutex);
+ if (pw_d)
+ pse_pw_d_leave(pw_d, &pcdev->pi[i]);
}
+ mutex_unlock(&pse_pw_d_mutex);
}
/**@@ -952,8 +950,8 @@ static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)
*
* A power domain is shared by every controller whose PIs sit on the same
* supply, so it must not be tied to the lifetime of the controller that
- * happened to create it. It is freed by pse_pw_d_release_locked() on the
- * last kref_put().
+ * happened to create it. It is freed by pse_pw_d_release() on the last
+ * kref_put().
*
* Return: Pointer to the newly allocated PSE power domain or error pointers
*/
@@ -992,7 +990,6 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
for (i = 0; i < pcdev->nr_lines; i++) {
struct regulator_dev *rdev = pcdev->pi[i].rdev;
struct pse_power_domain *pw_d;
- struct regulator *supply;
bool present = false;
unsigned long index;
@@ -1003,10 +1000,15 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
continue;
xa_for_each(&pse_pw_d_map, index, pw_d) {
+ struct pse_pi *pi = list_first_entry(&pw_d->pis,
+ struct pse_pi,
+ pw_d_node);
+
/* Power supply already registered as a PSE power
* domain.
*/
- if (regulator_is_equal(pw_d->supply, rdev->supply)) {
+ if (regulator_is_equal(pi->rdev->supply,
+ rdev->supply)) {
present = true;
pcdev->pi[i].pw_d = pw_d;
break;@@ -1014,6 +1016,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
}
if (present) {
kref_get(&pw_d->refcnt);
+ list_add_tail(&pcdev->pi[i].pw_d_node, &pw_d->pis);
continue;
}
@@ -1023,20 +1026,13 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
goto out;
}
- supply = regulator_get(&rdev->dev, rdev->supply_name);
- if (IS_ERR(supply)) {
- xa_erase(&pse_pw_d_map, pw_d->id);
- kfree(pw_d);
- ret = PTR_ERR(supply);
- goto out;
- }
-
- pw_d->supply = supply;
if (pcdev->supp_budget_eval_strategies)
pw_d->budget_eval_strategy = pcdev->supp_budget_eval_strategies;
else
pw_d->budget_eval_strategy = PSE_BUDGET_EVAL_STRAT_DISABLED;
kref_init(&pw_d->refcnt);
+ INIT_LIST_HEAD(&pw_d->pis);
+ list_add_tail(&pcdev->pi[i].pw_d_node, &pw_d->pis);
pcdev->pi[i].pw_d = pw_d;
}
@@ -1046,7 +1042,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)
struct pse_power_domain *pw_d = pse_pi_take_pw_d(pcdev, i);
if (pw_d)
- kref_put(&pw_d->refcnt, pse_pw_d_release_locked);
+ pse_pw_d_leave(pw_d, &pcdev->pi[i]);
}
mutex_unlock(&pse_pw_d_mutex);
return ret;
@@ -1940,7 +1936,7 @@ static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id,
if (!pw_diff) {
goto out;
} else if (pw_diff > 0) {
- ret = regulator_request_power_budget(pi->pw_d->supply, pw_diff);
+ ret = regulator_request_power_budget(pi->rdev->supply, pw_diff);
if (ret) {
NL_SET_ERR_MSG_FMT(extack,
"PI %d: not enough power budget available",@@ -1949,7 +1945,7 @@ static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id,
}
} else {
- regulator_free_power_budget(pi->pw_d->supply, -pw_diff);
+ regulator_free_power_budget(pi->rdev->supply, -pw_diff);
}
pi->pw_allocated_mW = pw_req;
*updated = true;diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index a917ac32dc0c..17738ffcd224 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -247,6 +247,7 @@ struct pse_pi_pairset {
* @rdev: regulator represented by the PSE PI
* @admin_state_enabled: PI enabled state
* @pw_d: Power domain of the PSE PI
+ * @pw_d_node: entry in the list of PIs that use @pw_d
* @prio: Priority of the PSE PI. Used in static budget evaluation strategy
* @isr_pd_detected: PSE PI detection status managed by the interruption
* handler. This variable is relevant when the power enabled@@ -261,6 +262,7 @@ struct pse_pi {
struct regulator_dev *rdev;
bool admin_state_enabled;
struct pse_power_domain *pw_d;
+ struct list_head pw_d_node;
int prio;
bool isr_pd_detected;
int pw_allocated_mW;--
2.43.0