| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978 |
- // SPDX-License-Identifier: GPL-2.0-only
- //
- // Framework for Ethernet Power Sourcing Equipment
- //
- // Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
- //
- #include <linux/device.h>
- #include <linux/ethtool.h>
- #include <linux/ethtool_netlink.h>
- #include <linux/of.h>
- #include <linux/phy.h>
- #include <linux/pse-pd/pse.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- #include <linux/rtnetlink.h>
- #include <net/net_trackers.h>
- #define PSE_PW_D_LIMIT INT_MAX
- static DEFINE_MUTEX(pse_list_mutex);
- static LIST_HEAD(pse_controller_list);
- static DEFINE_XARRAY_ALLOC(pse_pw_d_map);
- static DEFINE_MUTEX(pse_pw_d_mutex);
- /**
- * struct pse_control - a PSE control
- * @pcdev: a pointer to the PSE controller device
- * this PSE control belongs to
- * @ps: PSE PI supply of the PSE control
- * @list: list entry for the pcdev's PSE controller list
- * @id: ID of the PSE line in the PSE controller device
- * @refcnt: Number of gets of this pse_control
- * @attached_phydev: PHY device pointer attached by the PSE control
- */
- struct pse_control {
- struct pse_controller_dev *pcdev;
- struct regulator *ps;
- struct list_head list;
- unsigned int id;
- struct kref refcnt;
- struct phy_device *attached_phydev;
- };
- /**
- * 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
- */
- struct pse_power_domain {
- int id;
- struct regulator *supply;
- struct kref refcnt;
- u32 budget_eval_strategy;
- };
- static int of_load_single_pse_pi_pairset(struct device_node *node,
- struct pse_pi *pi,
- int pairset_num)
- {
- struct device_node *pairset_np;
- const char *name;
- int ret;
- ret = of_property_read_string_index(node, "pairset-names",
- pairset_num, &name);
- if (ret)
- return ret;
- if (!strcmp(name, "alternative-a")) {
- pi->pairset[pairset_num].pinout = ALTERNATIVE_A;
- } else if (!strcmp(name, "alternative-b")) {
- pi->pairset[pairset_num].pinout = ALTERNATIVE_B;
- } else {
- pr_err("pse: wrong pairset-names value %s (%pOF)\n",
- name, node);
- return -EINVAL;
- }
- pairset_np = of_parse_phandle(node, "pairsets", pairset_num);
- if (!pairset_np)
- return -ENODEV;
- pi->pairset[pairset_num].np = pairset_np;
- return 0;
- }
- /**
- * of_load_pse_pi_pairsets - load PSE PI pairsets pinout and polarity
- * @node: a pointer of the device node
- * @pi: a pointer of the PSE PI to fill
- * @npairsets: the number of pairsets (1 or 2) used by the PI
- *
- * Return: 0 on success and failure value on error
- */
- static int 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;
- }
- static void 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);
- }
- /**
- * of_load_pse_pis - load all the PSE PIs
- * @pcdev: a pointer to the PSE controller device
- *
- * Return: 0 on success and failure value on error
- */
- static int of_load_pse_pis(struct pse_controller_dev *pcdev)
- {
- struct device_node *np = pcdev->dev->of_node;
- struct device_node *node, *pis;
- int ret;
- if (!np)
- return -ENODEV;
- pcdev->pi = kzalloc_objs(*pcdev->pi, pcdev->nr_lines);
- 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;
- return 0;
- }
- 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;
- } else if (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;
- }
- of_node_get(node);
- pi.np = node;
- memcpy(&pcdev->pi[id], &pi, sizeof(pi));
- }
- of_node_put(pis);
- return 0;
- out:
- pse_release_pis(pcdev);
- of_node_put(node);
- of_node_put(pis);
- return ret;
- }
- /**
- * pse_control_find_net_by_id - Find net attached to the pse control id
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- *
- * Return: pse_control pointer or NULL. The device returned has had a
- * reference added and the pointer is safe until the user calls
- * pse_control_put() to indicate they have finished with it.
- */
- static struct pse_control *
- pse_control_find_by_id(struct pse_controller_dev *pcdev, int id)
- {
- struct pse_control *psec;
- mutex_lock(&pse_list_mutex);
- list_for_each_entry(psec, &pcdev->pse_control_head, list) {
- if (psec->id == id) {
- kref_get(&psec->refcnt);
- mutex_unlock(&pse_list_mutex);
- return psec;
- }
- }
- mutex_unlock(&pse_list_mutex);
- return NULL;
- }
- /**
- * pse_control_get_netdev - Return netdev associated to a PSE control
- * @psec: PSE control pointer
- *
- * Return: netdev pointer or NULL
- */
- static struct net_device *pse_control_get_netdev(struct pse_control *psec)
- {
- ASSERT_RTNL();
- if (!psec || !psec->attached_phydev)
- return NULL;
- return psec->attached_phydev->attached_dev;
- }
- /**
- * pse_pi_is_hw_enabled - Is PI enabled at the hardware level
- * @pcdev: a pointer to the PSE controller device
- * @id: Index of the PI
- *
- * Return: 1 if the PI is enabled at the hardware level, 0 if not, and
- * a failure value on error
- */
- static int pse_pi_is_hw_enabled(struct pse_controller_dev *pcdev, int id)
- {
- struct pse_admin_state admin_state = {0};
- int ret;
- 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)
- return 1;
- return 0;
- }
- /**
- * pse_pi_is_admin_enable_pending - Check if PI is in admin enable pending state
- * which mean the power is not yet being
- * delivered
- * @pcdev: a pointer to the PSE controller device
- * @id: Index of the PI
- *
- * Detects if a PI is enabled in software with a PD detected, but the hardware
- * admin state hasn't been applied yet.
- *
- * This function is used in the power delivery and retry mechanisms to determine
- * which PIs need to have power delivery attempted again.
- *
- * Return: true if the PI has admin enable flag set in software but not yet
- * reflected in the hardware admin state, false otherwise.
- */
- static bool
- pse_pi_is_admin_enable_pending(struct pse_controller_dev *pcdev, int id)
- {
- int ret;
- /* PI not enabled or nothing is plugged */
- if (!pcdev->pi[id].admin_state_enabled ||
- !pcdev->pi[id].isr_pd_detected)
- return false;
- ret = pse_pi_is_hw_enabled(pcdev, id);
- /* PSE PI is already enabled at hardware level */
- if (ret == 1)
- return false;
- return true;
- }
- static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev,
- int id,
- struct netlink_ext_ack *extack);
- /**
- * pse_pw_d_retry_power_delivery - Retry power delivery for pending ports in a
- * PSE power domain
- * @pcdev: a pointer to the PSE controller device
- * @pw_d: a pointer to the PSE power domain
- *
- * Scans all ports in the specified power domain and attempts to enable power
- * delivery to any ports that have admin enable state set but don't yet have
- * hardware power enabled. Used when there are changes in connection status,
- * admin state, or priority that might allow previously unpowered ports to
- * receive power, especially in over-budget conditions.
- */
- static void pse_pw_d_retry_power_delivery(struct pse_controller_dev *pcdev,
- struct pse_power_domain *pw_d)
- {
- int i, ret = 0;
- for (i = 0; i < pcdev->nr_lines; i++) {
- int prio_max = pcdev->nr_lines;
- struct netlink_ext_ack extack;
- if (pcdev->pi[i].pw_d != pw_d)
- continue;
- if (!pse_pi_is_admin_enable_pending(pcdev, i))
- continue;
- /* Do not try to enable PI with a lower prio (higher value)
- * than one which already can't be enabled.
- */
- if (pcdev->pi[i].prio > prio_max)
- continue;
- ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, i, &extack);
- if (ret == -ERANGE)
- prio_max = pcdev->pi[i].prio;
- }
- }
- /**
- * pse_pw_d_is_sw_pw_control - Determine if power control is software managed
- * @pcdev: a pointer to the PSE controller device
- * @pw_d: a pointer to the PSE power domain
- *
- * This function determines whether the power control for a specific power
- * domain is managed by software in the interrupt handler rather than directly
- * by hardware.
- *
- * Software power control is active in the following cases:
- * - When the budget evaluation strategy is set to static
- * - When the budget evaluation strategy is disabled but the PSE controller
- * has an interrupt handler that can report if a Powered Device is connected
- *
- * Return: true if the power control of the power domain is managed by software,
- * false otherwise
- */
- static bool pse_pw_d_is_sw_pw_control(struct pse_controller_dev *pcdev,
- struct pse_power_domain *pw_d)
- {
- if (!pw_d)
- return false;
- if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC)
- return true;
- if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_DISABLED &&
- pcdev->ops->pi_enable && pcdev->irq)
- return true;
- return false;
- }
- static int pse_pi_is_enabled(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- const struct pse_controller_ops *ops;
- int id, ret;
- 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;
- }
- ret = pse_pi_is_hw_enabled(pcdev, id);
- out:
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- /**
- * pse_pi_deallocate_pw_budget - Deallocate power budget of the PI
- * @pi: a pointer to the PSE PI
- */
- 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);
- pi->pw_allocated_mW = 0;
- }
- /**
- * _pse_pi_disable - Call disable operation. Assumes the PSE lock has been
- * acquired.
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- *
- * Return: 0 on success and failure value on error
- */
- static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id)
- {
- const struct pse_controller_ops *ops = pcdev->ops;
- int ret;
- if (!ops->pi_disable)
- return -EOPNOTSUPP;
- ret = ops->pi_disable(pcdev, id);
- if (ret)
- return ret;
- pse_pi_deallocate_pw_budget(&pcdev->pi[id]);
- if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d))
- pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[id].pw_d);
- return 0;
- }
- /**
- * pse_disable_pi_pol - Disable a PI on a power budget policy
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE PI
- *
- * Return: 0 on success and failure value on error
- */
- static int pse_disable_pi_pol(struct pse_controller_dev *pcdev, int id)
- {
- unsigned long notifs = ETHTOOL_PSE_EVENT_OVER_BUDGET;
- struct pse_ntf ntf = {};
- int ret;
- dev_dbg(pcdev->dev, "Disabling PI %d to free power budget\n", id);
- ret = _pse_pi_disable(pcdev, id);
- if (ret)
- notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
- ntf.notifs = notifs;
- ntf.id = id;
- kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, &pcdev->ntf_fifo_lock);
- schedule_work(&pcdev->ntf_work);
- return ret;
- }
- /**
- * pse_disable_pi_prio - Disable all PIs of a given priority inside a PSE
- * power domain
- * @pcdev: a pointer to the PSE
- * @pw_d: a pointer to the PSE power domain
- * @prio: priority
- *
- * Return: 0 on success and failure value on error
- */
- static int pse_disable_pi_prio(struct pse_controller_dev *pcdev,
- struct pse_power_domain *pw_d,
- int prio)
- {
- int i;
- for (i = 0; i < pcdev->nr_lines; i++) {
- int ret;
- if (pcdev->pi[i].prio != prio ||
- pcdev->pi[i].pw_d != pw_d ||
- pse_pi_is_hw_enabled(pcdev, i) <= 0)
- continue;
- ret = pse_disable_pi_pol(pcdev, i);
- if (ret)
- return ret;
- }
- return 0;
- }
- /**
- * pse_pi_allocate_pw_budget_static_prio - Allocate power budget for the PI
- * when the budget eval strategy is
- * static
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- * @pw_req: power requested in mW
- * @extack: extack for error reporting
- *
- * Allocates power using static budget evaluation strategy, where allocation
- * is based on PD classification. When insufficient budget is available,
- * lower-priority ports (higher priority numbers) are turned off first.
- *
- * Return: 0 on success and failure value on error
- */
- static int
- pse_pi_allocate_pw_budget_static_prio(struct pse_controller_dev *pcdev, int id,
- int pw_req, struct netlink_ext_ack *extack)
- {
- struct pse_pi *pi = &pcdev->pi[id];
- int ret, _prio;
- _prio = pcdev->nr_lines;
- while (regulator_request_power_budget(pi->pw_d->supply, pw_req) == -ERANGE) {
- if (_prio <= pi->prio) {
- NL_SET_ERR_MSG_FMT(extack,
- "PI %d: not enough power budget available",
- id);
- return -ERANGE;
- }
- ret = pse_disable_pi_prio(pcdev, pi->pw_d, _prio);
- if (ret < 0)
- return ret;
- _prio--;
- }
- pi->pw_allocated_mW = pw_req;
- return 0;
- }
- /**
- * pse_pi_allocate_pw_budget - Allocate power budget for the PI
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- * @pw_req: power requested in mW
- * @extack: extack for error reporting
- *
- * Return: 0 on success and failure value on error
- */
- static int pse_pi_allocate_pw_budget(struct pse_controller_dev *pcdev, int id,
- int pw_req, struct netlink_ext_ack *extack)
- {
- struct pse_pi *pi = &pcdev->pi[id];
- if (!pi->pw_d)
- return 0;
- /* PSE_BUDGET_EVAL_STRAT_STATIC */
- if (pi->pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC)
- return pse_pi_allocate_pw_budget_static_prio(pcdev, id, pw_req,
- extack);
- return 0;
- }
- /**
- * _pse_pi_delivery_power_sw_pw_ctrl - Enable PSE PI in case of software power
- * control. Assumes the PSE lock has been
- * acquired.
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- * @extack: extack for error reporting
- *
- * Return: 0 on success and failure value on error
- */
- static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev,
- int id,
- struct netlink_ext_ack *extack)
- {
- const struct pse_controller_ops *ops = pcdev->ops;
- struct pse_pi *pi = &pcdev->pi[id];
- int ret, pw_req;
- 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;
- }
- return 0;
- }
- static int pse_pi_enable(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- const struct 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.
- * Real enable process will happen if a port is connected.
- */
- 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);
- return ret;
- }
- static int pse_pi_disable(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- struct pse_pi *pi;
- int id, ret;
- id = rdev_get_id(rdev);
- pi = &pcdev->pi[id];
- mutex_lock(&pcdev->lock);
- ret = _pse_pi_disable(pcdev, id);
- if (!ret)
- pi->admin_state_enabled = 0;
- mutex_unlock(&pcdev->lock);
- return 0;
- }
- static int _pse_pi_get_voltage(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- const struct pse_controller_ops *ops;
- int id;
- ops = pcdev->ops;
- if (!ops->pi_get_voltage)
- return -EOPNOTSUPP;
- id = rdev_get_id(rdev);
- return ops->pi_get_voltage(pcdev, id);
- }
- static int pse_pi_get_voltage(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- int ret;
- mutex_lock(&pcdev->lock);
- ret = _pse_pi_get_voltage(rdev);
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- static int pse_pi_get_current_limit(struct regulator_dev *rdev)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- const struct pse_controller_ops *ops;
- int id, uV, mW, ret;
- s64 tmp_64;
- 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;
- }
- static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA,
- int max_uA)
- {
- struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
- const struct 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;
- tmp_64 = ret;
- tmp_64 *= max_uA;
- /* mW = uA * uV / 1000000000 */
- mW = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
- ret = ops->pi_set_pw_limit(pcdev, id, mW);
- out:
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- static const struct regulator_ops pse_pi_ops = {
- .is_enabled = pse_pi_is_enabled,
- .enable = pse_pi_enable,
- .disable = pse_pi_disable,
- .get_voltage = pse_pi_get_voltage,
- .get_current_limit = pse_pi_get_current_limit,
- .set_current_limit = pse_pi_set_current_limit,
- };
- static int
- devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
- char *name, int id)
- {
- struct regulator_init_data *rinit_data;
- struct regulator_config rconfig = {0};
- struct regulator_desc *rdesc;
- struct regulator_dev *rdev;
- 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
- * PSE PI id for the well functioning of the PSE controls.
- */
- rdesc->id = id;
- rdesc->name = name;
- rdesc->type = REGULATOR_VOLTAGE;
- rdesc->ops = &pse_pi_ops;
- rdesc->owner = pcdev->owner;
- rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
- if (pcdev->ops->pi_set_pw_limit)
- rinit_data->constraints.valid_ops_mask |=
- REGULATOR_CHANGE_CURRENT;
- rinit_data->supply_regulator = "vpwr";
- rconfig.dev = pcdev->dev;
- rconfig.driver_data = pcdev;
- rconfig.init_data = rinit_data;
- rconfig.of_node = pcdev->pi[id].np;
- rdev = devm_regulator_register(pcdev->dev, rdesc, &rconfig);
- if (IS_ERR(rdev)) {
- dev_err_probe(pcdev->dev, PTR_ERR(rdev),
- "Failed to register regulator\n");
- return PTR_ERR(rdev);
- }
- pcdev->pi[id].rdev = rdev;
- return 0;
- }
- static void __pse_pw_d_release(struct kref *kref)
- {
- struct pse_power_domain *pw_d = container_of(kref,
- struct pse_power_domain,
- refcnt);
- regulator_put(pw_d->supply);
- xa_erase(&pse_pw_d_map, pw_d->id);
- mutex_unlock(&pse_pw_d_mutex);
- }
- /**
- * pse_flush_pw_ds - flush all PSE power domains of a PSE
- * @pcdev: a pointer to the initialized PSE controller device
- */
- static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)
- {
- struct pse_power_domain *pw_d;
- int i;
- for (i = 0; i < pcdev->nr_lines; i++) {
- if (!pcdev->pi[i].pw_d)
- continue;
- pw_d = xa_load(&pse_pw_d_map, pcdev->pi[i].pw_d->id);
- if (!pw_d)
- continue;
- kref_put_mutex(&pw_d->refcnt, __pse_pw_d_release,
- &pse_pw_d_mutex);
- }
- }
- /**
- * devm_pse_alloc_pw_d - allocate a new PSE power domain for a device
- * @dev: device that is registering this PSE power domain
- *
- * Return: Pointer to the newly allocated PSE power domain or error pointers
- */
- static struct pse_power_domain *devm_pse_alloc_pw_d(struct device *dev)
- {
- struct pse_power_domain *pw_d;
- int index, ret;
- pw_d = devm_kzalloc(dev, sizeof(*pw_d), GFP_KERNEL);
- if (!pw_d)
- return ERR_PTR(-ENOMEM);
- ret = xa_alloc(&pse_pw_d_map, &index, pw_d, XA_LIMIT(1, PSE_PW_D_LIMIT),
- GFP_KERNEL);
- if (ret)
- return ERR_PTR(ret);
- kref_init(&pw_d->refcnt);
- pw_d->id = index;
- return pw_d;
- }
- /**
- * pse_register_pw_ds - register the PSE power domains for a PSE
- * @pcdev: a pointer to the PSE controller device
- *
- * Return: 0 on success and failure value on error
- */
- static int 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;
- unsigned long index;
- /* No regulator or regulator parent supply registered.
- * We need a regulator parent to register a PSE power domain
- */
- 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;
- }
- 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);
- pcdev->pi[i].pw_d = pw_d;
- }
- out:
- mutex_unlock(&pse_pw_d_mutex);
- return ret;
- }
- /**
- * pse_send_ntf_worker - Worker to send PSE notifications
- * @work: work object
- *
- * Manage and send PSE netlink notifications using a workqueue to avoid
- * deadlock between pcdev_lock and pse_list_mutex.
- */
- static void pse_send_ntf_worker(struct work_struct *work)
- {
- struct pse_controller_dev *pcdev;
- struct pse_ntf ntf;
- pcdev = container_of(work, struct pse_controller_dev, ntf_work);
- while (kfifo_out(&pcdev->ntf_fifo, &ntf, 1)) {
- struct net_device *netdev;
- struct pse_control *psec;
- psec = pse_control_find_by_id(pcdev, ntf.id);
- rtnl_lock();
- netdev = pse_control_get_netdev(psec);
- if (netdev)
- ethnl_pse_send_ntf(netdev, ntf.notifs);
- rtnl_unlock();
- pse_control_put(psec);
- }
- }
- /**
- * pse_controller_register - register a PSE controller device
- * @pcdev: a pointer to the initialized PSE controller device
- *
- * Return: 0 on success and failure value on error
- */
- 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 +
- * int max digit number (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;
- snprintf(reg_name, reg_name_len, "pse-%s_pi%d",
- dev_name(pcdev->dev), i);
- ret = devm_pse_pi_regulator_register(pcdev, reg_name, i);
- if (ret)
- return ret;
- }
- ret = pse_register_pw_ds(pcdev);
- if (ret)
- return ret;
- mutex_lock(&pse_list_mutex);
- list_add(&pcdev->list, &pse_controller_list);
- mutex_unlock(&pse_list_mutex);
- return 0;
- }
- EXPORT_SYMBOL_GPL(pse_controller_register);
- /**
- * pse_controller_unregister - unregister a PSE controller device
- * @pcdev: a pointer to the PSE controller device
- */
- void pse_controller_unregister(struct pse_controller_dev *pcdev)
- {
- pse_flush_pw_ds(pcdev);
- pse_release_pis(pcdev);
- if (pcdev->irq)
- disable_irq(pcdev->irq);
- cancel_work_sync(&pcdev->ntf_work);
- kfifo_free(&pcdev->ntf_fifo);
- mutex_lock(&pse_list_mutex);
- list_del(&pcdev->list);
- mutex_unlock(&pse_list_mutex);
- }
- EXPORT_SYMBOL_GPL(pse_controller_unregister);
- static void devm_pse_controller_release(struct device *dev, void *res)
- {
- pse_controller_unregister(*(struct pse_controller_dev **)res);
- }
- /**
- * devm_pse_controller_register - resource managed pse_controller_register()
- * @dev: device that is registering this PSE controller
- * @pcdev: a pointer to the initialized PSE controller device
- *
- * Managed pse_controller_register(). For PSE controllers registered by
- * this function, pse_controller_unregister() is automatically called on
- * driver detach. See pse_controller_register() for more information.
- *
- * Return: 0 on success and failure value on error
- */
- int devm_pse_controller_register(struct device *dev,
- struct pse_controller_dev *pcdev)
- {
- struct pse_controller_dev **pcdevp;
- int ret;
- pcdevp = devres_alloc(devm_pse_controller_release, sizeof(*pcdevp),
- GFP_KERNEL);
- if (!pcdevp)
- return -ENOMEM;
- ret = pse_controller_register(pcdev);
- if (ret) {
- devres_free(pcdevp);
- return ret;
- }
- *pcdevp = pcdev;
- devres_add(dev, pcdevp);
- return 0;
- }
- EXPORT_SYMBOL_GPL(devm_pse_controller_register);
- struct pse_irq {
- struct pse_controller_dev *pcdev;
- struct pse_irq_desc desc;
- unsigned long *notifs;
- };
- /**
- * pse_to_regulator_notifs - Convert PSE notifications to Regulator
- * notifications
- * @notifs: PSE notifications
- *
- * Return: Regulator notifications
- */
- static unsigned long pse_to_regulator_notifs(unsigned long notifs)
- {
- unsigned long rnotifs = 0;
- if (notifs & ETHTOOL_PSE_EVENT_OVER_CURRENT)
- rnotifs |= REGULATOR_EVENT_OVER_CURRENT;
- if (notifs & ETHTOOL_PSE_EVENT_OVER_TEMP)
- rnotifs |= REGULATOR_EVENT_OVER_TEMP;
- return rnotifs;
- }
- /**
- * pse_set_config_isr - Set PSE control config according to the PSE
- * notifications
- * @pcdev: a pointer to the PSE
- * @id: index of the PSE control
- * @notifs: PSE event notifications
- *
- * Return: 0 on success and failure value on error
- */
- static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id,
- unsigned long notifs)
- {
- int ret = 0;
- if (notifs & PSE_BUDGET_EVAL_STRAT_DYNAMIC)
- return 0;
- if ((notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) &&
- ((notifs & ETHTOOL_C33_PSE_EVENT_DETECTION) ||
- (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION))) {
- dev_dbg(pcdev->dev,
- "PI %d: error, connection and disconnection reported simultaneously",
- id);
- return -EINVAL;
- }
- if (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION) {
- struct netlink_ext_ack extack;
- pcdev->pi[id].isr_pd_detected = true;
- if (pcdev->pi[id].admin_state_enabled) {
- ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id,
- &extack);
- if (ret == -ERANGE)
- ret = 0;
- }
- } else if (notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) {
- if (pcdev->pi[id].admin_state_enabled &&
- pcdev->pi[id].isr_pd_detected)
- ret = _pse_pi_disable(pcdev, id);
- pcdev->pi[id].isr_pd_detected = false;
- }
- return ret;
- }
- /**
- * pse_isr - IRQ handler for PSE
- * @irq: irq number
- * @data: pointer to user interrupt structure
- *
- * Return: irqreturn_t - status of IRQ
- */
- static irqreturn_t pse_isr(int irq, void *data)
- {
- struct pse_controller_dev *pcdev;
- unsigned long notifs_mask = 0;
- struct pse_irq_desc *desc;
- struct pse_irq *h = data;
- int ret, i;
- desc = &h->desc;
- pcdev = h->pcdev;
- /* Clear notifs mask */
- memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
- mutex_lock(&pcdev->lock);
- ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask);
- if (ret || !notifs_mask) {
- mutex_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
- for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) {
- unsigned long notifs, rnotifs;
- struct pse_ntf ntf = {};
- /* 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);
- ntf.notifs = notifs;
- ntf.id = i;
- kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1,
- &pcdev->ntf_fifo_lock);
- schedule_work(&pcdev->ntf_work);
- rnotifs = pse_to_regulator_notifs(notifs);
- regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
- NULL);
- }
- mutex_unlock(&pcdev->lock);
- return IRQ_HANDLED;
- }
- /**
- * devm_pse_irq_helper - Register IRQ based PSE event notifier
- * @pcdev: a pointer to the PSE
- * @irq: the irq value to be passed to request_irq
- * @irq_flags: the flags to be passed to request_irq
- * @d: PSE interrupt description
- *
- * Return: 0 on success and errno on failure
- */
- int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
- int irq_flags, const struct pse_irq_desc *d)
- {
- struct device *dev = pcdev->dev;
- size_t irq_name_len;
- struct pse_irq *h;
- char *irq_name;
- int ret;
- if (!d || !d->map_event || !d->name)
- return -EINVAL;
- h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
- if (!h)
- return -ENOMEM;
- h->pcdev = pcdev;
- h->desc = *d;
- /* IRQ name len is pcdev dev name + 5 char + irq desc name + 1 */
- irq_name_len = strlen(dev_name(pcdev->dev)) + 5 + strlen(d->name) + 1;
- irq_name = devm_kzalloc(dev, irq_name_len, GFP_KERNEL);
- if (!irq_name)
- return -ENOMEM;
- snprintf(irq_name, irq_name_len, "pse-%s:%s", dev_name(pcdev->dev),
- d->name);
- h->notifs = devm_kcalloc(dev, pcdev->nr_lines,
- sizeof(*h->notifs), GFP_KERNEL);
- if (!h->notifs)
- return -ENOMEM;
- ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr,
- IRQF_ONESHOT | irq_flags,
- irq_name, h);
- if (ret)
- dev_err(pcdev->dev, "Failed to request IRQ %d\n", irq);
- pcdev->irq = irq;
- return ret;
- }
- EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
- /* PSE control section */
- static void __pse_control_release(struct kref *kref)
- {
- struct pse_control *psec = container_of(kref, struct pse_control,
- refcnt);
- lockdep_assert_held(&pse_list_mutex);
- if (psec->pcdev->pi[psec->id].admin_state_enabled)
- regulator_disable(psec->ps);
- devm_regulator_put(psec->ps);
- module_put(psec->pcdev->owner);
- list_del(&psec->list);
- kfree(psec);
- }
- static void __pse_control_put_internal(struct pse_control *psec)
- {
- lockdep_assert_held(&pse_list_mutex);
- kref_put(&psec->refcnt, __pse_control_release);
- }
- /**
- * pse_control_put - free the PSE control
- * @psec: PSE control pointer
- */
- void pse_control_put(struct pse_control *psec)
- {
- if (IS_ERR_OR_NULL(psec))
- return;
- mutex_lock(&pse_list_mutex);
- __pse_control_put_internal(psec);
- mutex_unlock(&pse_list_mutex);
- }
- EXPORT_SYMBOL_GPL(pse_control_put);
- static struct pse_control *
- pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,
- struct phy_device *phydev)
- {
- struct pse_control *psec;
- int ret;
- lockdep_assert_held(&pse_list_mutex);
- list_for_each_entry(psec, &pcdev->pse_control_head, list) {
- if (psec->id == index) {
- kref_get(&psec->refcnt);
- return psec;
- }
- }
- psec = kzalloc_obj(*psec);
- 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
- * aims to have the right value reported in the first is_enabled
- * call in case of control managed by software.
- */
- 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;
- }
- psec->pcdev = pcdev;
- list_add(&psec->list, &pcdev->pse_control_head);
- psec->id = index;
- psec->attached_phydev = phydev;
- kref_init(&psec->refcnt);
- return psec;
- put_module:
- module_put(pcdev->owner);
- free_psec:
- kfree(psec);
- return ERR_PTR(ret);
- }
- /**
- * of_pse_match_pi - Find the PSE PI id matching the device node phandle
- * @pcdev: a pointer to the PSE controller device
- * @np: a pointer to the device node
- *
- * Return: id of the PSE PI, -EINVAL if not found
- */
- static int of_pse_match_pi(struct pse_controller_dev *pcdev,
- struct device_node *np)
- {
- int i;
- for (i = 0; i < pcdev->nr_lines; i++) {
- if (pcdev->pi[i].np == np)
- return i;
- }
- return -EINVAL;
- }
- /**
- * psec_id_xlate - translate pse_spec to the PSE line number according
- * to the number of pse-cells in case of no pse_pi node
- * @pcdev: a pointer to the PSE controller device
- * @pse_spec: PSE line specifier as found in the device tree
- *
- * Return: 0 if #pse-cells = <0>. Return PSE line number otherwise.
- */
- static int psec_id_xlate(struct pse_controller_dev *pcdev,
- const struct of_phandle_args *pse_spec)
- {
- if (!pcdev->of_pse_n_cells)
- return 0;
- if (pcdev->of_pse_n_cells > 1 ||
- pse_spec->args[0] >= pcdev->nr_lines)
- return -EINVAL;
- return pse_spec->args[0];
- }
- struct pse_control *of_pse_control_get(struct device_node *node,
- struct phy_device *phydev)
- {
- struct pse_controller_dev *r, *pcdev;
- struct of_phandle_args args;
- struct pse_control *psec;
- int psec_id;
- int ret;
- if (!node)
- return ERR_PTR(-EINVAL);
- ret = of_parse_phandle_with_args(node, "pses", "#pse-cells", 0, &args);
- if (ret)
- return ERR_PTR(ret);
- mutex_lock(&pse_list_mutex);
- pcdev = NULL;
- list_for_each_entry(r, &pse_controller_list, list) {
- if (!r->no_of_pse_pi) {
- ret = of_pse_match_pi(r, args.np);
- if (ret >= 0) {
- pcdev = r;
- psec_id = ret;
- break;
- }
- } else if (args.np == r->dev->of_node) {
- pcdev = r;
- break;
- }
- }
- if (!pcdev) {
- psec = ERR_PTR(-EPROBE_DEFER);
- goto out;
- }
- if (WARN_ON(args.args_count != pcdev->of_pse_n_cells)) {
- psec = ERR_PTR(-EINVAL);
- goto out;
- }
- if (pcdev->no_of_pse_pi) {
- psec_id = psec_id_xlate(pcdev, &args);
- if (psec_id < 0) {
- psec = ERR_PTR(psec_id);
- goto out;
- }
- }
- /* pse_list_mutex also protects the pcdev's pse_control list */
- psec = pse_control_get_internal(pcdev, psec_id, phydev);
- out:
- mutex_unlock(&pse_list_mutex);
- of_node_put(args.np);
- return psec;
- }
- EXPORT_SYMBOL_GPL(of_pse_control_get);
- /**
- * pse_get_sw_admin_state - Convert the software admin state to c33 or podl
- * admin state value used in the standard
- * @psec: PSE control pointer
- * @admin_state: a pointer to the admin_state structure
- */
- static void pse_get_sw_admin_state(struct pse_control *psec,
- struct pse_admin_state *admin_state)
- {
- struct pse_pi *pi = &psec->pcdev->pi[psec->id];
- if (pse_has_podl(psec)) {
- if (pi->admin_state_enabled)
- admin_state->podl_admin_state =
- ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED;
- else
- admin_state->podl_admin_state =
- ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED;
- }
- if (pse_has_c33(psec)) {
- if (pi->admin_state_enabled)
- admin_state->c33_admin_state =
- ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
- else
- admin_state->c33_admin_state =
- ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
- }
- }
- /**
- * pse_ethtool_get_status - get status of PSE control
- * @psec: PSE control pointer
- * @extack: extack for reporting useful error messages
- * @status: struct to store PSE status
- *
- * Return: 0 on success and failure value on error
- */
- int pse_ethtool_get_status(struct pse_control *psec,
- struct netlink_ext_ack *extack,
- struct ethtool_pse_control_status *status)
- {
- struct pse_admin_state admin_state = {0};
- struct pse_pw_status pw_status = {0};
- const struct pse_controller_ops *ops;
- struct pse_controller_dev *pcdev;
- struct pse_pi *pi;
- int ret;
- pcdev = psec->pcdev;
- ops = pcdev->ops;
- pi = &pcdev->pi[psec->id];
- mutex_lock(&pcdev->lock);
- if (pi->pw_d) {
- status->pw_d_id = pi->pw_d->id;
- if (pse_pw_d_is_sw_pw_control(pcdev, pi->pw_d)) {
- pse_get_sw_admin_state(psec, &admin_state);
- } else {
- ret = ops->pi_get_admin_state(pcdev, psec->id,
- &admin_state);
- if (ret)
- goto out;
- }
- status->podl_admin_state = admin_state.podl_admin_state;
- status->c33_admin_state = admin_state.c33_admin_state;
- switch (pi->pw_d->budget_eval_strategy) {
- case PSE_BUDGET_EVAL_STRAT_STATIC:
- status->prio_max = pcdev->nr_lines - 1;
- status->prio = pi->prio;
- break;
- case PSE_BUDGET_EVAL_STRAT_DYNAMIC:
- status->prio_max = pcdev->pis_prio_max;
- if (ops->pi_get_prio) {
- ret = ops->pi_get_prio(pcdev, psec->id);
- if (ret < 0)
- goto out;
- status->prio = ret;
- }
- break;
- default:
- break;
- }
- }
- ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status);
- if (ret)
- goto out;
- status->podl_pw_status = pw_status.podl_pw_status;
- status->c33_pw_status = pw_status.c33_pw_status;
- if (ops->pi_get_ext_state) {
- struct pse_ext_state_info ext_state_info = {0};
- ret = ops->pi_get_ext_state(pcdev, psec->id,
- &ext_state_info);
- if (ret)
- goto out;
- memcpy(&status->c33_ext_state_info,
- &ext_state_info.c33_ext_state_info,
- sizeof(status->c33_ext_state_info));
- }
- if (ops->pi_get_pw_class) {
- ret = ops->pi_get_pw_class(pcdev, psec->id);
- if (ret < 0)
- goto out;
- status->c33_pw_class = ret;
- }
- if (ops->pi_get_actual_pw) {
- ret = ops->pi_get_actual_pw(pcdev, psec->id);
- if (ret < 0)
- goto out;
- status->c33_actual_pw = ret;
- }
- if (ops->pi_get_pw_limit) {
- ret = ops->pi_get_pw_limit(pcdev, psec->id);
- if (ret < 0)
- goto out;
- status->c33_avail_pw_limit = ret;
- }
- if (ops->pi_get_pw_limit_ranges) {
- struct pse_pw_limit_ranges pw_limit_ranges = {0};
- ret = ops->pi_get_pw_limit_ranges(pcdev, psec->id,
- &pw_limit_ranges);
- if (ret < 0)
- goto out;
- status->c33_pw_limit_ranges =
- pw_limit_ranges.c33_pw_limit_ranges;
- status->c33_pw_limit_nb_ranges = ret;
- }
- out:
- mutex_unlock(&psec->pcdev->lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(pse_ethtool_get_status);
- static int pse_ethtool_c33_set_config(struct pse_control *psec,
- const struct pse_control_config *config)
- {
- int err = 0;
- /* Look at admin_state_enabled status to not call regulator_enable
- * or regulator_disable twice creating a regulator counter mismatch
- */
- switch (config->c33_admin_control) {
- case ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED:
- /* We could have mismatch between admin_state_enabled and
- * state reported by regulator_is_enabled. This can occur when
- * the PI is forcibly turn off by the controller. Call
- * regulator_disable on that case to fix the counters state.
- */
- 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;
- }
- return err;
- }
- static int pse_ethtool_podl_set_config(struct pse_control *psec,
- const struct pse_control_config *config)
- {
- int err = 0;
- /* Look at admin_state_enabled status to not call regulator_enable
- * or regulator_disable twice creating a regulator counter mismatch
- */
- 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;
- }
- return err;
- }
- /**
- * pse_ethtool_set_config - set PSE control configuration
- * @psec: PSE control pointer
- * @extack: extack for reporting useful error messages
- * @config: Configuration of the test to run
- *
- * Return: 0 on success and failure value on error
- */
- int pse_ethtool_set_config(struct pse_control *psec,
- struct netlink_ext_ack *extack,
- const struct pse_control_config *config)
- {
- int err = 0;
- if (pse_has_c33(psec) && config->c33_admin_control) {
- err = pse_ethtool_c33_set_config(psec, config);
- if (err)
- return err;
- }
- if (pse_has_podl(psec) && config->podl_admin_control)
- err = pse_ethtool_podl_set_config(psec, config);
- return err;
- }
- EXPORT_SYMBOL_GPL(pse_ethtool_set_config);
- /**
- * pse_pi_update_pw_budget - Update PSE power budget allocated with new
- * power in mW
- * @pcdev: a pointer to the PSE controller device
- * @id: index of the PSE PI
- * @pw_req: power requested
- * @extack: extack for reporting useful error messages
- *
- * Return: Previous power allocated on success and failure value on error
- */
- static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id,
- const unsigned int 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
- * power budget update
- */
- mutex_lock(&pcdev->lock);
- previous_pw_allocated = pi->pw_allocated_mW;
- pw_diff = pw_req - previous_pw_allocated;
- if (!pw_diff) {
- goto out;
- } else if (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;
- }
- } else {
- regulator_free_power_budget(pi->pw_d->supply, -pw_diff);
- }
- pi->pw_allocated_mW = pw_req;
- ret = previous_pw_allocated;
- out:
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- /**
- * pse_ethtool_set_pw_limit - set PSE control power limit
- * @psec: PSE control pointer
- * @extack: extack for reporting useful error messages
- * @pw_limit: power limit value in mW
- *
- * Return: 0 on success and failure value on error
- */
- int pse_ethtool_set_pw_limit(struct pse_control *psec,
- struct netlink_ext_ack *extack,
- const unsigned int pw_limit)
- {
- int uV, uA, ret, previous_pw_allocated = 0;
- s64 tmp_64;
- if (pw_limit > MAX_PI_PW)
- return -ERANGE;
- 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
- * if a Power Device is powered.
- */
- 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);
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit);
- /**
- * pse_ethtool_set_prio - Set PSE PI priority according to the budget
- * evaluation strategy
- * @psec: PSE control pointer
- * @extack: extack for reporting useful error messages
- * @prio: priovity value
- *
- * Return: 0 on success and failure value on error
- */
- int pse_ethtool_set_prio(struct pse_control *psec,
- struct netlink_ext_ack *extack,
- unsigned int prio)
- {
- struct pse_controller_dev *pcdev = psec->pcdev;
- const struct pse_controller_ops *ops;
- int ret = 0;
- 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/disable call or a priority mode change
- */
- 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;
- }
- pcdev->pi[psec->id].prio = prio;
- pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[psec->id].pw_d);
- break;
- 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;
- default:
- ret = -EOPNOTSUPP;
- }
- out:
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(pse_ethtool_set_prio);
- bool pse_has_podl(struct pse_control *psec)
- {
- return psec->pcdev->types & ETHTOOL_PSE_PODL;
- }
- EXPORT_SYMBOL_GPL(pse_has_podl);
- bool pse_has_c33(struct pse_control *psec)
- {
- return psec->pcdev->types & ETHTOOL_PSE_C33;
- }
- EXPORT_SYMBOL_GPL(pse_has_c33);
|