123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /*
- * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdint.h>
- #include <string.h>
- #include <common/debug.h>
- #include <drivers/delay_timer.h>
- #include <drivers/st/regulator.h>
- #include <libfdt.h>
- #define MAX_PROPERTY_LEN 64
- CASSERT(PLAT_NB_RDEVS >= 1U, plat_nb_rdevs_must_be_higher);
- static struct rdev rdev_array[PLAT_NB_RDEVS];
- #define for_each_rdev(rdev) \
- for ((rdev) = rdev_array; (rdev) <= &rdev_array[PLAT_NB_RDEVS - 1U]; (rdev)++)
- #define for_each_registered_rdev(rdev) \
- for ((rdev) = rdev_array; \
- ((rdev) <= &rdev_array[PLAT_NB_RDEVS - 1U]) && ((rdev)->desc != NULL); (rdev)++)
- static void lock_driver(const struct rdev *rdev)
- {
- if (rdev->desc->ops->lock != NULL) {
- rdev->desc->ops->lock(rdev->desc);
- }
- }
- static void unlock_driver(const struct rdev *rdev)
- {
- if (rdev->desc->ops->unlock != NULL) {
- rdev->desc->ops->unlock(rdev->desc);
- }
- }
- static struct rdev *regulator_get_by_phandle(int32_t phandle)
- {
- struct rdev *rdev;
- for_each_registered_rdev(rdev) {
- if (rdev->phandle == phandle) {
- return rdev;
- }
- }
- WARN("%s: phandle %d not found\n", __func__, phandle);
- return NULL;
- }
- /*
- * Get a regulator from its node name
- *
- * @fdt - pointer to device tree memory
- * @node_name - name of the node "ldo1"
- * Return pointer to rdev if succeed, NULL else.
- */
- struct rdev *regulator_get_by_name(const char *node_name)
- {
- struct rdev *rdev;
- assert(node_name != NULL);
- VERBOSE("get %s\n", node_name);
- for_each_registered_rdev(rdev) {
- if (strcmp(rdev->desc->node_name, node_name) == 0) {
- return rdev;
- }
- }
- WARN("%s: %s not found\n", __func__, node_name);
- return NULL;
- }
- static int32_t get_supply_phandle(const void *fdt, int node, const char *name)
- {
- const fdt32_t *cuint;
- int len __unused;
- int supply_phandle = -FDT_ERR_NOTFOUND;
- char prop_name[MAX_PROPERTY_LEN];
- len = snprintf(prop_name, MAX_PROPERTY_LEN - 1, "%s-supply", name);
- assert((len >= 0) && (len < (MAX_PROPERTY_LEN - 1)));
- cuint = fdt_getprop(fdt, node, prop_name, NULL);
- if (cuint != NULL) {
- supply_phandle = fdt32_to_cpu(*cuint);
- VERBOSE("%s: supplied by %d\n", name, supply_phandle);
- }
- return supply_phandle;
- }
- /*
- * Get a regulator from a supply name
- *
- * @fdt - pointer to device tree memory
- * @node - offset of the node that contains the supply description
- * @name - name of the supply "vdd" for "vdd-supply'
- * Return pointer to rdev if succeed, NULL else.
- */
- struct rdev *regulator_get_by_supply_name(const void *fdt, int node, const char *name)
- {
- const int p = get_supply_phandle(fdt, node, name);
- if (p < 0) {
- return NULL;
- }
- return regulator_get_by_phandle(p);
- }
- static int __regulator_set_state(struct rdev *rdev, bool state)
- {
- if (rdev->desc->ops->set_state == NULL) {
- return -ENODEV;
- }
- return rdev->desc->ops->set_state(rdev->desc, state);
- }
- /*
- * Enable regulator
- *
- * @rdev - pointer to rdev struct
- * Return 0 if succeed, non 0 else.
- */
- int regulator_enable(struct rdev *rdev)
- {
- int ret;
- assert(rdev != NULL);
- ret = __regulator_set_state(rdev, STATE_ENABLE);
- udelay(rdev->enable_ramp_delay);
- return ret;
- }
- /*
- * Disable regulator
- *
- * @rdev - pointer to rdev struct
- * Return 0 if succeed, non 0 else.
- */
- int regulator_disable(struct rdev *rdev)
- {
- int ret;
- assert(rdev != NULL);
- if ((rdev->flags & REGUL_ALWAYS_ON) != 0U) {
- return 0;
- }
- ret = __regulator_set_state(rdev, STATE_DISABLE);
- udelay(rdev->enable_ramp_delay);
- return ret;
- }
- /*
- * Regulator enabled query
- *
- * @rdev - pointer to rdev struct
- * Return 0 if disabled, 1 if enabled, <0 else.
- */
- int regulator_is_enabled(const struct rdev *rdev)
- {
- int ret;
- assert(rdev != NULL);
- VERBOSE("%s: is en\n", rdev->desc->node_name);
- if (rdev->desc->ops->get_state == NULL) {
- return -ENODEV;
- }
- lock_driver(rdev);
- ret = rdev->desc->ops->get_state(rdev->desc);
- if (ret < 0) {
- ERROR("regul %s get state failed: err:%d\n",
- rdev->desc->node_name, ret);
- }
- unlock_driver(rdev);
- return ret;
- }
- /*
- * Set regulator voltage
- *
- * @rdev - pointer to rdev struct
- * @mvolt - Target voltage level in millivolt
- * Return 0 if succeed, non 0 else.
- */
- int regulator_set_voltage(struct rdev *rdev, uint16_t mvolt)
- {
- int ret;
- assert(rdev != NULL);
- VERBOSE("%s: set mvolt\n", rdev->desc->node_name);
- if ((mvolt < rdev->min_mv) || (mvolt > rdev->max_mv)) {
- return -EPERM;
- }
- if (regulator_get_voltage(rdev) == mvolt) {
- return 0U;
- }
- if (rdev->desc->ops->set_voltage == NULL) {
- return -ENODEV;
- }
- lock_driver(rdev);
- ret = rdev->desc->ops->set_voltage(rdev->desc, mvolt);
- if (ret < 0) {
- ERROR("regul %s set volt failed: err:%d\n",
- rdev->desc->node_name, ret);
- }
- unlock_driver(rdev);
- return ret;
- }
- /*
- * Set regulator min voltage
- *
- * @rdev - pointer to rdev struct
- * Return 0 if succeed, non 0 else.
- */
- int regulator_set_min_voltage(struct rdev *rdev)
- {
- return regulator_set_voltage(rdev, rdev->min_mv);
- }
- /*
- * Get regulator voltage
- *
- * @rdev - pointer to rdev struct
- * Return milli volts if succeed, <0 else.
- */
- int regulator_get_voltage(const struct rdev *rdev)
- {
- int ret;
- assert(rdev != NULL);
- VERBOSE("%s: get volt\n", rdev->desc->node_name);
- if (rdev->desc->ops->get_voltage == NULL) {
- return rdev->min_mv;
- }
- lock_driver(rdev);
- ret = rdev->desc->ops->get_voltage(rdev->desc);
- if (ret < 0) {
- ERROR("regul %s get voltage failed: err:%d\n",
- rdev->desc->node_name, ret);
- }
- unlock_driver(rdev);
- return ret;
- }
- /*
- * List regulator voltages
- *
- * @rdev - pointer to rdev struct
- * @levels - out: array of supported millitvolt levels from min to max value
- * @count - out: number of possible millivolt values
- * Return 0 if succeed, non 0 else.
- */
- int regulator_list_voltages(const struct rdev *rdev, const uint16_t **levels, size_t *count)
- {
- int ret;
- size_t n;
- assert(rdev != NULL);
- assert(levels != NULL);
- assert(count != NULL);
- VERBOSE("%s: list volt\n", rdev->desc->node_name);
- if (rdev->desc->ops->list_voltages == NULL) {
- return -ENODEV;
- }
- lock_driver(rdev);
- ret = rdev->desc->ops->list_voltages(rdev->desc, levels, count);
- unlock_driver(rdev);
- if (ret < 0) {
- ERROR("regul %s list_voltages failed: err: %d\n",
- rdev->desc->node_name, ret);
- return ret;
- }
- /*
- * Reduce the possible values depending on min and max from device-tree
- */
- n = *count;
- while ((n > 1U) && ((*levels)[n - 1U] > rdev->max_mv)) {
- n--;
- }
- /* Verify that max val is a valid value */
- if (rdev->max_mv != (*levels)[n - 1]) {
- ERROR("regul %s: max value %u is invalid\n",
- rdev->desc->node_name, rdev->max_mv);
- return -EINVAL;
- }
- while ((n > 1U) && ((*levels[0U]) < rdev->min_mv)) {
- (*levels)++;
- n--;
- }
- /* Verify that min is not too high */
- if (n == 0U) {
- ERROR("regul %s set min voltage is too high\n",
- rdev->desc->node_name);
- return -EINVAL;
- }
- /* Verify that min val is a valid vlue */
- if (rdev->min_mv != (*levels)[0U]) {
- ERROR("regul %s: min value %u is invalid\n",
- rdev->desc->node_name, rdev->min_mv);
- return -EINVAL;
- }
- *count = n;
- VERBOSE("rdev->min_mv=%u rdev->max_mv=%u\n", rdev->min_mv, rdev->max_mv);
- return 0;
- }
- /*
- * Get regulator voltages range
- *
- * @rdev - pointer to rdev struct
- * @min_mv - out: min possible millivolt value
- * @max_mv - out: max possible millivolt value
- * Return 0 if succeed, non 0 else.
- */
- void regulator_get_range(const struct rdev *rdev, uint16_t *min_mv, uint16_t *max_mv)
- {
- assert(rdev != NULL);
- if (min_mv != NULL) {
- *min_mv = rdev->min_mv;
- }
- if (max_mv != NULL) {
- *max_mv = rdev->max_mv;
- }
- }
- /*
- * Set regulator flag
- *
- * @rdev - pointer to rdev struct
- * @flag - flag value to set (eg: REGUL_OCP)
- * Return 0 if succeed, non 0 else.
- */
- int regulator_set_flag(struct rdev *rdev, uint16_t flag)
- {
- int ret;
- /* check that only one bit is set on flag */
- if (__builtin_popcount(flag) != 1) {
- return -EINVAL;
- }
- /* REGUL_ALWAYS_ON and REGUL_BOOT_ON are internal properties of the core */
- if ((flag == REGUL_ALWAYS_ON) || (flag == REGUL_BOOT_ON)) {
- rdev->flags |= flag;
- return 0;
- }
- if (rdev->desc->ops->set_flag == NULL) {
- ERROR("%s can not set any flag\n", rdev->desc->node_name);
- return -ENODEV;
- }
- lock_driver(rdev);
- ret = rdev->desc->ops->set_flag(rdev->desc, flag);
- unlock_driver(rdev);
- if (ret != 0) {
- ERROR("%s: could not set flag %d ret=%d\n",
- rdev->desc->node_name, flag, ret);
- return ret;
- }
- rdev->flags |= flag;
- return 0;
- }
- static int parse_properties(const void *fdt, struct rdev *rdev, int node)
- {
- const fdt32_t *cuint;
- int ret;
- if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) {
- VERBOSE("%s: set regulator-always-on\n", rdev->desc->node_name);
- ret = regulator_set_flag(rdev, REGUL_ALWAYS_ON);
- if (ret != 0) {
- return ret;
- }
- }
- cuint = fdt_getprop(fdt, node, "regulator-enable-ramp-delay", NULL);
- if (cuint != NULL) {
- rdev->enable_ramp_delay = fdt32_to_cpu(*cuint);
- VERBOSE("%s: enable_ramp_delay=%u\n", rdev->desc->node_name,
- rdev->enable_ramp_delay);
- }
- return 0;
- }
- /*
- * Parse the device-tree for a regulator
- *
- * Read min/max voltage from dt and check its validity
- * Read the properties, and call the driver to set flags
- * Read power supply phandle
- * Read and store low power mode states
- *
- * @rdev - pointer to rdev struct
- * @node - device-tree node offset of the regulator
- * Return 0 if disabled, 1 if enabled, <0 else.
- */
- static int parse_dt(struct rdev *rdev, int node)
- {
- void *fdt;
- const fdt32_t *cuint;
- const uint16_t *levels;
- size_t size;
- int ret;
- VERBOSE("%s: parse dt\n", rdev->desc->node_name);
- if (fdt_get_address(&fdt) == 0) {
- return -ENOENT;
- }
- rdev->phandle = fdt_get_phandle(fdt, node);
- cuint = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
- if (cuint != NULL) {
- uint16_t min_mv;
- min_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U);
- VERBOSE("%s: min_mv=%d\n", rdev->desc->node_name, (int)min_mv);
- if (min_mv <= rdev->max_mv) {
- rdev->min_mv = min_mv;
- } else {
- ERROR("%s: min_mv=%d is too high\n",
- rdev->desc->node_name, (int)min_mv);
- return -EINVAL;
- }
- }
- cuint = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
- if (cuint != NULL) {
- uint16_t max_mv;
- max_mv = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U);
- VERBOSE("%s: max_mv=%d\n", rdev->desc->node_name, (int)max_mv);
- if (max_mv >= rdev->min_mv) {
- rdev->max_mv = max_mv;
- } else {
- ERROR("%s: max_mv=%d is too low\n",
- rdev->desc->node_name, (int)max_mv);
- return -EINVAL;
- }
- }
- /* validate that min and max values can be used */
- ret = regulator_list_voltages(rdev, &levels, &size);
- if ((ret != 0) && (ret != -ENODEV)) {
- return ret;
- }
- ret = parse_properties(fdt, rdev, node);
- if (ret != 0) {
- return ret;
- }
- return 0;
- }
- /*
- * Register a regulator driver in regulator framework.
- * Initialize voltage range from driver description
- *
- * @desc - pointer to the regulator description
- * @node - device-tree node offset of the regulator
- * Return 0 if succeed, non 0 else.
- */
- int regulator_register(const struct regul_description *desc, int node)
- {
- struct rdev *rdev;
- assert(desc != NULL);
- VERBOSE("register %s\n", desc->node_name);
- for_each_rdev(rdev) {
- if (rdev->desc == NULL) {
- break;
- }
- }
- if (rdev > &rdev_array[PLAT_NB_RDEVS - 1U]) {
- WARN("Not enough place for regulators, PLAT_NB_RDEVS should be increased.\n");
- return -ENOMEM;
- }
- rdev->desc = desc;
- rdev->enable_ramp_delay = rdev->desc->enable_ramp_delay;
- if (rdev->desc->ops->list_voltages != NULL) {
- int ret;
- const uint16_t *levels;
- size_t count;
- lock_driver(rdev);
- ret = rdev->desc->ops->list_voltages(rdev->desc, &levels, &count);
- unlock_driver(rdev);
- if (ret < 0) {
- ERROR("regul %s set state failed: err:%d\n",
- rdev->desc->node_name, ret);
- return ret;
- }
- rdev->min_mv = levels[0];
- rdev->max_mv = levels[count - 1U];
- } else {
- rdev->max_mv = UINT16_MAX;
- }
- return parse_dt(rdev, node);
- }
|