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 /* If voltage is set to 0 => disable */ 26 if (uv == 0) { 27 if (omask) 28 return regmap_update_bits(regmap, oreg, omask, 0); 29 } 30 /* Some setups don't allow setting own voltage but do allow enabling */ 31 if (!mask) { 32 if (omask) 33 return regmap_update_bits(regmap, oreg, omask, omask); 34 35 return -EINVAL; 36 } 37 for (i = 0; i < desc->n_voltages; i++) { 38 /* NOTE to next hacker - Does not support pickable ranges */ 39 if (desc->linear_range_selectors) 40 return -EINVAL; 41 if (desc->n_linear_ranges) 42 ret = regulator_desc_list_voltage_linear_range(desc, i); 43 else 44 ret = regulator_desc_list_voltage_linear(desc, i); 45 if (ret < 0) 46 continue; 47 if (ret == uv) { 48 i <<= ffs(desc->vsel_mask) - 1; 49 ret = regmap_update_bits(regmap, reg, mask, i); 50 if (omask && !ret) 51 ret = regmap_update_bits(regmap, oreg, omask, 52 omask); 53 break; 54 } 55 } 56 return ret; 57 } 58 59 int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, 60 struct device_node *np, 61 const struct regulator_desc *desc, 62 struct regmap *regmap) 63 { 64 int i, ret = 0; 65 char *prop; 66 unsigned int reg, mask, omask, oreg = desc->enable_reg; 67 68 for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) { 69 int bit; 70 71 bit = BIT(i); 72 if (dvs->level_map & bit) { 73 switch (bit) { 74 case ROHM_DVS_LEVEL_RUN: 75 prop = "rohm,dvs-run-voltage"; 76 reg = dvs->run_reg; 77 mask = dvs->run_mask; 78 omask = dvs->run_on_mask; 79 break; 80 case ROHM_DVS_LEVEL_IDLE: 81 prop = "rohm,dvs-idle-voltage"; 82 reg = dvs->idle_reg; 83 mask = dvs->idle_mask; 84 omask = dvs->idle_on_mask; 85 break; 86 case ROHM_DVS_LEVEL_SUSPEND: 87 prop = "rohm,dvs-suspend-voltage"; 88 reg = dvs->suspend_reg; 89 mask = dvs->suspend_mask; 90 omask = dvs->suspend_on_mask; 91 break; 92 case ROHM_DVS_LEVEL_LPSR: 93 prop = "rohm,dvs-lpsr-voltage"; 94 reg = dvs->lpsr_reg; 95 mask = dvs->lpsr_mask; 96 omask = dvs->lpsr_on_mask; 97 break; 98 case ROHM_DVS_LEVEL_SNVS: 99 prop = "rohm,dvs-snvs-voltage"; 100 reg = dvs->snvs_reg; 101 mask = dvs->snvs_mask; 102 omask = dvs->snvs_on_mask; 103 break; 104 default: 105 return -EINVAL; 106 } 107 ret = set_dvs_level(desc, np, regmap, prop, reg, mask, 108 omask, oreg); 109 } 110 } 111 return ret; 112 } 113 EXPORT_SYMBOL(rohm_regulator_set_dvs_levels); 114 115 MODULE_LICENSE("GPL v2"); 116 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 117 MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers"); 118