Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // Copyright (C) 2020 ROHM Semiconductors |
| 3 | |
| 4 | #include <linux/errno.h> |
| 5 | #include <linux/mfd/rohm-generic.h> |
| 6 | #include <linux/module.h> |
| 7 | #include <linux/of.h> |
| 8 | #include <linux/regmap.h> |
| 9 | #include <linux/regulator/driver.h> |
| 10 | |
| 11 | static int set_dvs_level(const struct regulator_desc *desc, |
| 12 | struct device_node *np, struct regmap *regmap, |
| 13 | char *prop, unsigned int reg, unsigned int mask, |
| 14 | unsigned int omask, unsigned int oreg) |
| 15 | { |
| 16 | int ret, i; |
| 17 | uint32_t uv; |
| 18 | |
| 19 | ret = of_property_read_u32(np, prop, &uv); |
| 20 | if (ret) { |
| 21 | if (ret != -EINVAL) |
| 22 | return ret; |
| 23 | return 0; |
| 24 | } |
| 25 | |
| 26 | if (uv == 0) { |
| 27 | if (omask) |
| 28 | return regmap_update_bits(regmap, oreg, omask, 0); |
| 29 | } |
| 30 | for (i = 0; i < desc->n_voltages; i++) { |
| 31 | ret = regulator_desc_list_voltage_linear_range(desc, i); |
| 32 | if (ret < 0) |
| 33 | continue; |
| 34 | if (ret == uv) { |
| 35 | i <<= ffs(desc->vsel_mask) - 1; |
| 36 | ret = regmap_update_bits(regmap, reg, mask, i); |
| 37 | if (omask && !ret) |
| 38 | ret = regmap_update_bits(regmap, oreg, omask, |
| 39 | omask); |
| 40 | break; |
| 41 | } |
| 42 | } |
| 43 | return ret; |
| 44 | } |
| 45 | |
| 46 | int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, |
| 47 | struct device_node *np, |
| 48 | const struct regulator_desc *desc, |
| 49 | struct regmap *regmap) |
| 50 | { |
| 51 | int i, ret = 0; |
| 52 | char *prop; |
| 53 | unsigned int reg, mask, omask, oreg = desc->enable_reg; |
| 54 | |
| 55 | for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) { |
| 56 | int bit; |
| 57 | |
| 58 | bit = BIT(i); |
| 59 | if (dvs->level_map & bit) { |
| 60 | switch (bit) { |
| 61 | case ROHM_DVS_LEVEL_RUN: |
| 62 | prop = "rohm,dvs-run-voltage"; |
| 63 | reg = dvs->run_reg; |
| 64 | mask = dvs->run_mask; |
| 65 | omask = dvs->run_on_mask; |
| 66 | break; |
| 67 | case ROHM_DVS_LEVEL_IDLE: |
| 68 | prop = "rohm,dvs-idle-voltage"; |
| 69 | reg = dvs->idle_reg; |
| 70 | mask = dvs->idle_mask; |
| 71 | omask = dvs->idle_on_mask; |
| 72 | break; |
| 73 | case ROHM_DVS_LEVEL_SUSPEND: |
| 74 | prop = "rohm,dvs-suspend-voltage"; |
| 75 | reg = dvs->suspend_reg; |
| 76 | mask = dvs->suspend_mask; |
| 77 | omask = dvs->suspend_on_mask; |
| 78 | break; |
| 79 | case ROHM_DVS_LEVEL_LPSR: |
| 80 | prop = "rohm,dvs-lpsr-voltage"; |
| 81 | reg = dvs->lpsr_reg; |
| 82 | mask = dvs->lpsr_mask; |
| 83 | omask = dvs->lpsr_on_mask; |
| 84 | break; |
| 85 | default: |
| 86 | return -EINVAL; |
| 87 | } |
| 88 | ret = set_dvs_level(desc, np, regmap, prop, reg, mask, |
| 89 | omask, oreg); |
| 90 | } |
| 91 | } |
| 92 | return ret; |
| 93 | } |
| 94 | EXPORT_SYMBOL(rohm_regulator_set_dvs_levels); |
| 95 | |
| 96 | MODULE_LICENSE("GPL v2"); |
| 97 | MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); |
| 98 | MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers"); |