pairset_np = of_parse_phandle(node, "pairsets", pairset_num); if (!pairset_np) return -ENODEV;
pi->pairset[pairset_num].np = pairset_np;
return0;
}
/** *of_load_pse_pi_pairsets-loadPSEPIpairsetspinoutandpolarity *@node:apointerofthedevicenode *@pi:apointerofthePSEPItofill *@npairsets:thenumberofpairsets(1or2)usedbythePI * *Return:0onsuccessandfailurevalueonerror
*/ staticint of_load_pse_pi_pairsets(struct device_node *node, struct pse_pi *pi, int npairsets)
{ int i, ret;
ret = of_property_count_strings(node, "pairset-names"); if (ret != npairsets) {
pr_err("pse: amount of pairsets and pairset-names is not equal %d != %d (%pOF)\n",
npairsets, ret, node); return -EINVAL;
}
for (i = 0; i < npairsets; i++) {
ret = of_load_single_pse_pi_pairset(node, pi, i); if (ret) goto out;
}
if (npairsets == 2 &&
pi->pairset[0].pinout == pi->pairset[1].pinout) {
pr_err("pse: two PI pairsets can not have identical pinout (%pOF)",
node);
ret = -EINVAL;
}
out: /* If an error appears, release all the pairset device node kref */ if (ret) {
of_node_put(pi->pairset[0].np);
pi->pairset[0].np = NULL;
of_node_put(pi->pairset[1].np);
pi->pairset[1].np = NULL;
}
return ret;
}
staticvoid pse_release_pis(struct pse_controller_dev *pcdev)
{ int i;
for (i = 0; i < pcdev->nr_lines; i++) {
of_node_put(pcdev->pi[i].pairset[0].np);
of_node_put(pcdev->pi[i].pairset[1].np);
of_node_put(pcdev->pi[i].np);
}
kfree(pcdev->pi);
}
pcdev->pi = kcalloc(pcdev->nr_lines, sizeof(*pcdev->pi), GFP_KERNEL); if (!pcdev->pi) return -ENOMEM;
pis = of_get_child_by_name(np, "pse-pis"); if (!pis) { /* no description of PSE PIs */
pcdev->no_of_pse_pi = true; return0;
}
for_each_child_of_node(pis, node) { struct pse_pi pi = {0};
u32 id;
if (!of_node_name_eq(node, "pse-pi")) continue;
ret = of_property_read_u32(node, "reg", &id); if (ret) {
dev_err(pcdev->dev, "can't get reg property for node '%pOF'",
node); goto out;
}
if (id >= pcdev->nr_lines) {
dev_err(pcdev->dev, "reg value (%u) is out of range (%u) (%pOF)\n",
id, pcdev->nr_lines, node);
ret = -EINVAL; goto out;
}
if (pcdev->pi[id].np) {
dev_err(pcdev->dev, "other node with same reg value was already registered. %pOF : %pOF\n",
pcdev->pi[id].np, node);
ret = -EINVAL; goto out;
}
ret = of_count_phandle_with_args(node, "pairsets", NULL); /* npairsets is limited to value one or two */ if (ret == 1 || ret == 2) {
ret = of_load_pse_pi_pairsets(node, &pi, ret); if (ret) goto out;
} elseif (ret != ENOENT) {
dev_err(pcdev->dev, "error: wrong number of pairsets. Should be 1 or 2, got %d (%pOF)\n",
ret, node);
ret = -EINVAL; goto out;
}
ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state); if (ret < 0) return ret;
/* PI is well enabled at the hardware level */ if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED ||
admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) return1;
ops = pcdev->ops; if (!ops->pi_get_admin_state) return -EOPNOTSUPP;
id = rdev_get_id(rdev);
mutex_lock(&pcdev->lock); if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
ret = pcdev->pi[id].admin_state_enabled; goto out;
}
if (!ops->pi_get_pw_req) { /* No power allocation management */
ret = ops->pi_enable(pcdev, id); if (ret)
NL_SET_ERR_MSG_FMT(extack, "PI %d: enable error %d",
id, ret); return ret;
}
ret = ops->pi_get_pw_req(pcdev, id); if (ret < 0) return ret;
pw_req = ret;
/* Compare requested power with port power limit and use the lowest *one.
*/ if (ops->pi_get_pw_limit) {
ret = ops->pi_get_pw_limit(pcdev, id); if (ret < 0) return ret;
if (ret < pw_req)
pw_req = ret;
}
ret = pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack); if (ret) return ret;
ret = ops->pi_enable(pcdev, id); if (ret) {
pse_pi_deallocate_pw_budget(pi);
NL_SET_ERR_MSG_FMT(extack, "PI %d: enable error %d",
id, ret); return ret;
}
return0;
}
staticint pse_pi_enable(struct regulator_dev *rdev)
{ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); conststruct pse_controller_ops *ops; int id, ret = 0;
ops = pcdev->ops; if (!ops->pi_enable) return -EOPNOTSUPP;
id = rdev_get_id(rdev);
mutex_lock(&pcdev->lock); if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) { /* Manage enabled status by software. *Realenableprocesswillhappenifaportisconnected.
*/ if (pcdev->pi[id].isr_pd_detected) { struct netlink_ext_ack extack;
ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id, &extack);
} if (!ret || ret == -ERANGE) {
pcdev->pi[id].admin_state_enabled = 1;
ret = 0;
}
mutex_unlock(&pcdev->lock); return ret;
}
ret = ops->pi_enable(pcdev, id); if (!ret)
pcdev->pi[id].admin_state_enabled = 1;
mutex_unlock(&pcdev->lock);
ops = pcdev->ops;
id = rdev_get_id(rdev); if (!ops->pi_get_pw_limit || !ops->pi_get_voltage) return -EOPNOTSUPP;
mutex_lock(&pcdev->lock);
ret = ops->pi_get_pw_limit(pcdev, id); if (ret < 0) goto out;
mW = ret;
ret = _pse_pi_get_voltage(rdev); if (!ret) {
dev_err(pcdev->dev, "Voltage null\n");
ret = -ERANGE; goto out;
} if (ret < 0) goto out;
uV = ret;
tmp_64 = mW;
tmp_64 *= 1000000000ull; /* uA = mW * 1000000000 / uV */
ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
out:
mutex_unlock(&pcdev->lock); return ret;
}
staticint pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
{ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); conststruct pse_controller_ops *ops; int id, mW, ret;
s64 tmp_64;
ops = pcdev->ops; if (!ops->pi_set_pw_limit || !ops->pi_get_voltage) return -EOPNOTSUPP;
if (max_uA > MAX_PI_CURRENT) return -ERANGE;
id = rdev_get_id(rdev);
mutex_lock(&pcdev->lock);
ret = _pse_pi_get_voltage(rdev); if (!ret) {
dev_err(pcdev->dev, "Voltage null\n");
ret = -ERANGE; goto out;
} if (ret < 0) goto out;
rinit_data = devm_kzalloc(pcdev->dev, sizeof(*rinit_data),
GFP_KERNEL); if (!rinit_data) return -ENOMEM;
rdesc = devm_kzalloc(pcdev->dev, sizeof(*rdesc), GFP_KERNEL); if (!rdesc) return -ENOMEM;
/* Regulator descriptor id have to be the same as its associated *PSEPIidforthewellfunctioningofthePSEcontrols.
*/
rdesc->id = id;
rdesc->name = name;
rdesc->type = REGULATOR_VOLTAGE;
rdesc->ops = &pse_pi_ops;
rdesc->owner = pcdev->owner;
/** *pse_register_pw_ds-registerthePSEpowerdomainsforaPSE *@pcdev:apointertothePSEcontrollerdevice * *Return:0onsuccessandfailurevalueonerror
*/ staticint pse_register_pw_ds(struct pse_controller_dev *pcdev)
{ int i, ret = 0;
mutex_lock(&pse_pw_d_mutex); 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; unsignedlong index;
/* No regulator or regulator parent supply registered. *WeneedaregulatorparenttoregisteraPSEpowerdomain
*/ if (!rdev || !rdev->supply) continue;
xa_for_each(&pse_pw_d_map, index, pw_d) { /* Power supply already registered as a PSE power *domain.
*/ if (regulator_is_equal(pw_d->supply, rdev->supply)) {
present = true;
pcdev->pi[i].pw_d = pw_d; break;
}
} if (present) {
kref_get(&pw_d->refcnt); continue;
}
pw_d = devm_pse_alloc_pw_d(pcdev->dev); if (IS_ERR(pw_d)) {
ret = PTR_ERR(pw_d); goto out;
}
supply = regulator_get(&rdev->dev, rdev->supply_name); if (IS_ERR(supply)) {
xa_erase(&pse_pw_d_map, pw_d->id);
ret = PTR_ERR(supply); goto out;
}
/** *pse_controller_register-registeraPSEcontrollerdevice *@pcdev:apointertotheinitializedPSEcontrollerdevice * *Return:0onsuccessandfailurevalueonerror
*/ int pse_controller_register(struct pse_controller_dev *pcdev)
{
size_t reg_name_len; int ret, i;
mutex_init(&pcdev->lock);
INIT_LIST_HEAD(&pcdev->pse_control_head);
spin_lock_init(&pcdev->ntf_fifo_lock);
ret = kfifo_alloc(&pcdev->ntf_fifo, pcdev->nr_lines, GFP_KERNEL); if (ret) {
dev_err(pcdev->dev, "failed to allocate kfifo notifications\n"); return ret;
}
INIT_WORK(&pcdev->ntf_work, pse_send_ntf_worker);
if (!pcdev->nr_lines)
pcdev->nr_lines = 1;
if (!pcdev->ops->pi_get_admin_state ||
!pcdev->ops->pi_get_pw_status) {
dev_err(pcdev->dev, "Mandatory status report callbacks are missing"); return -EINVAL;
}
ret = of_load_pse_pis(pcdev); if (ret) return ret;
if (pcdev->ops->setup_pi_matrix) {
ret = pcdev->ops->setup_pi_matrix(pcdev); if (ret) return ret;
}
/* Each regulator name len is pcdev dev name + 7 char + *intmaxdigitnumber(10)+1
*/
reg_name_len = strlen(dev_name(pcdev->dev)) + 18;
/* Register PI regulators */ for (i = 0; i < pcdev->nr_lines; i++) { char *reg_name;
/* Do not register regulator for PIs not described */ if (!pcdev->no_of_pse_pi && !pcdev->pi[i].np) continue;
reg_name = devm_kzalloc(pcdev->dev, reg_name_len, GFP_KERNEL); if (!reg_name) return -ENOMEM;
/* Do nothing PI not described */ if (!pcdev->pi[i].rdev) continue;
notifs = h->notifs[i]; if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) {
ret = pse_set_config_isr(pcdev, i, notifs); if (ret)
notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
}
dev_dbg(h->pcdev->dev, "Sending PSE notification EVT 0x%lx\n", notifs);
psec = kzalloc(sizeof(*psec), GFP_KERNEL); if (!psec) return ERR_PTR(-ENOMEM);
if (!try_module_get(pcdev->owner)) {
ret = -ENODEV; goto free_psec;
}
if (!pcdev->ops->pi_get_admin_state) {
ret = -EOPNOTSUPP; goto free_psec;
}
/* Initialize admin_state_enabled before the regulator_get. This *aimstohavetherightvaluereportedinthefirstis_enabled *callincaseofcontrolmanagedbysoftware.
*/
ret = pse_pi_is_hw_enabled(pcdev, index); if (ret < 0) goto free_psec;
pcdev->pi[index].admin_state_enabled = ret;
psec->ps = devm_regulator_get_exclusive(pcdev->dev,
rdev_get_name(pcdev->pi[index].rdev)); if (IS_ERR(psec->ps)) {
ret = PTR_ERR(psec->ps); goto put_module;
}
/* Look at admin_state_enabled status to not call regulator_enable *orregulator_disabletwicecreatingaregulatorcountermismatch
*/ switch (config->c33_admin_control) { case ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED: /* We could have mismatch between admin_state_enabled and *statereportedbyregulator_is_enabled.Thiscanoccurwhen *thePIisforciblyturnoffbythecontroller.Call *regulator_disableonthatcasetofixthecountersstate.
*/ if (psec->pcdev->pi[psec->id].admin_state_enabled &&
!regulator_is_enabled(psec->ps)) {
err = regulator_disable(psec->ps); if (err) break;
} if (!psec->pcdev->pi[psec->id].admin_state_enabled)
err = regulator_enable(psec->ps); break; case ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED: if (psec->pcdev->pi[psec->id].admin_state_enabled)
err = regulator_disable(psec->ps); break; default:
err = -EOPNOTSUPP;
}
/* Look at admin_state_enabled status to not call regulator_enable *orregulator_disabletwicecreatingaregulatorcountermismatch
*/ switch (config->podl_admin_control) { case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: if (!psec->pcdev->pi[psec->id].admin_state_enabled)
err = regulator_enable(psec->ps); break; case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: if (psec->pcdev->pi[psec->id].admin_state_enabled)
err = regulator_disable(psec->ps); break; default:
err = -EOPNOTSUPP;
}
/** *pse_pi_update_pw_budget-UpdatePSEpowerbudgetallocatedwithnew *powerinmW *@pcdev:apointertothePSEcontrollerdevice *@id:indexofthePSEPI *@pw_req:powerrequested *@extack:extackforreportingusefulerrormessages * *Return:Previouspowerallocatedonsuccessandfailurevalueonerror
*/ staticint pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id, constunsignedint pw_req, struct netlink_ext_ack *extack)
{ struct pse_pi *pi = &pcdev->pi[id]; int previous_pw_allocated; int pw_diff, ret = 0;
/* We don't want pw_allocated_mW value change in the middle of an *powerbudgetupdate
*/
mutex_lock(&pcdev->lock);
previous_pw_allocated = pi->pw_allocated_mW;
pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) { goto out;
} elseif (pw_diff > 0) {
ret = regulator_request_power_budget(pi->pw_d->supply, pw_diff); if (ret) {
NL_SET_ERR_MSG_FMT(extack, "PI %d: not enough power budget available",
id); goto out;
}
ret = regulator_get_voltage(psec->ps); if (!ret) {
NL_SET_ERR_MSG(extack, "Can't calculate the current, PSE voltage read is 0"); return -ERANGE;
} if (ret < 0) {
NL_SET_ERR_MSG(extack, "Error reading PSE voltage"); return ret;
}
uV = ret;
tmp_64 = pw_limit;
tmp_64 *= 1000000000ull; /* uA = mW * 1000000000 / uV */
uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
/* Update power budget only in software power control case and *ifaPowerDeviceispowered.
*/ if (pse_pw_d_is_sw_pw_control(psec->pcdev,
psec->pcdev->pi[psec->id].pw_d) &&
psec->pcdev->pi[psec->id].admin_state_enabled &&
psec->pcdev->pi[psec->id].isr_pd_detected) {
ret = pse_pi_update_pw_budget(psec->pcdev, psec->id,
pw_limit, extack); if (ret < 0) return ret;
previous_pw_allocated = ret;
}
ret = regulator_set_current_limit(psec->ps, 0, uA); if (ret < 0 && previous_pw_allocated) {
pse_pi_update_pw_budget(psec->pcdev, psec->id,
previous_pw_allocated, extack);
}
if (!pcdev->pi[psec->id].pw_d) {
NL_SET_ERR_MSG(extack, "no power domain attached"); return -EOPNOTSUPP;
}
/* We don't want priority change in the middle of an *enable/disablecalloraprioritymodechange
*/
mutex_lock(&pcdev->lock); switch (pcdev->pi[psec->id].pw_d->budget_eval_strategy) { case PSE_BUDGET_EVAL_STRAT_STATIC: if (prio >= pcdev->nr_lines) {
NL_SET_ERR_MSG_FMT(extack, "priority %d exceed priority max %d",
prio, pcdev->nr_lines);
ret = -ERANGE; goto out;
}
case PSE_BUDGET_EVAL_STRAT_DYNAMIC:
ops = psec->pcdev->ops; if (!ops->pi_set_prio) {
NL_SET_ERR_MSG(extack, "pse driver does not support setting port priority");
ret = -EOPNOTSUPP; goto out;
}
if (prio > pcdev->pis_prio_max) {
NL_SET_ERR_MSG_FMT(extack, "priority %d exceed priority max %d",
prio, pcdev->pis_prio_max);
ret = -ERANGE; goto out;
}
ret = ops->pi_set_prio(pcdev, psec->id, prio); break;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.115Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.