| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903 |
- // 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/of.h>
- #include <linux/pse-pd/pse.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- static DEFINE_MUTEX(pse_list_mutex);
- static LIST_HEAD(pse_controller_list);
- /**
- * 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
- */
- struct pse_control {
- struct pse_controller_dev *pcdev;
- struct regulator *ps;
- struct list_head list;
- unsigned int id;
- struct kref refcnt;
- };
- 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 = 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;
- 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;
- }
- 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_is_enabled)
- return -EOPNOTSUPP;
- id = rdev_get_id(rdev);
- mutex_lock(&pcdev->lock);
- ret = ops->pi_is_enabled(pcdev, id);
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- 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;
- ops = pcdev->ops;
- if (!ops->pi_enable)
- return -EOPNOTSUPP;
- id = rdev_get_id(rdev);
- mutex_lock(&pcdev->lock);
- 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);
- const struct pse_controller_ops *ops;
- int id, ret;
- ops = pcdev->ops;
- if (!ops->pi_disable)
- return -EOPNOTSUPP;
- id = rdev_get_id(rdev);
- mutex_lock(&pcdev->lock);
- ret = ops->pi_disable(pcdev, id);
- if (!ret)
- pcdev->pi[id].admin_state_enabled = 0;
- mutex_unlock(&pcdev->lock);
- return ret;
- }
- 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;
- 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;
- }
- /**
- * 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);
- if (!pcdev->nr_lines)
- pcdev->nr_lines = 1;
- 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;
- }
- 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_release_pis(pcdev);
- 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);
- /* 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 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(sizeof(*psec), GFP_KERNEL);
- if (!psec)
- return ERR_PTR(-ENOMEM);
- if (!try_module_get(pcdev->owner)) {
- ret = -ENODEV;
- goto free_psec;
- }
- 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;
- }
- ret = regulator_is_enabled(psec->ps);
- if (ret < 0)
- goto regulator_put;
- pcdev->pi[index].admin_state_enabled = ret;
- psec->pcdev = pcdev;
- list_add(&psec->list, &pcdev->pse_control_head);
- psec->id = index;
- kref_init(&psec->refcnt);
- return psec;
- regulator_put:
- devm_regulator_put(psec->ps);
- 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 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);
- out:
- mutex_unlock(&pse_list_mutex);
- of_node_put(args.np);
- return psec;
- }
- EXPORT_SYMBOL_GPL(of_pse_control_get);
- /**
- * 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 pse_control_status *status)
- {
- const struct pse_controller_ops *ops;
- struct pse_controller_dev *pcdev;
- int err;
- pcdev = psec->pcdev;
- ops = pcdev->ops;
- if (!ops->ethtool_get_status) {
- NL_SET_ERR_MSG(extack,
- "PSE driver does not support status report");
- return -EOPNOTSUPP;
- }
- mutex_lock(&pcdev->lock);
- err = ops->ethtool_get_status(pcdev, psec->id, extack, status);
- mutex_unlock(&pcdev->lock);
- return err;
- }
- 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_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;
- s64 tmp_64;
- 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);
- return regulator_set_current_limit(psec->ps, 0, uA);
- }
- EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit);
- 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);
|