rt4831-regulator.c (5766B)
1// SPDX-License-Identifier: GPL-2.0-only 2 3#include <linux/bitops.h> 4#include <linux/kernel.h> 5#include <linux/module.h> 6#include <linux/of.h> 7#include <linux/platform_device.h> 8#include <linux/regmap.h> 9#include <linux/regulator/consumer.h> 10#include <linux/regulator/driver.h> 11 12enum { 13 DSV_OUT_VLCM = 0, 14 DSV_OUT_VPOS, 15 DSV_OUT_VNEG, 16 DSV_OUT_MAX 17}; 18 19#define RT4831_REG_DSVEN 0x09 20#define RT4831_REG_VLCM 0x0c 21#define RT4831_REG_VPOS 0x0d 22#define RT4831_REG_VNEG 0x0e 23#define RT4831_REG_FLAGS 0x0f 24 25#define RT4831_VOLT_MASK GENMASK(5, 0) 26#define RT4831_DSVMODE_SHIFT 5 27#define RT4831_DSVMODE_MASK GENMASK(7, 5) 28#define RT4831_POSADEN_MASK BIT(4) 29#define RT4831_NEGADEN_MASK BIT(3) 30#define RT4831_POSEN_MASK BIT(2) 31#define RT4831_NEGEN_MASK BIT(1) 32 33#define RT4831_OTP_MASK BIT(6) 34#define RT4831_LCMOVP_MASK BIT(5) 35#define RT4831_VPOSSCP_MASK BIT(3) 36#define RT4831_VNEGSCP_MASK BIT(2) 37 38#define DSV_MODE_NORMAL (0x4 << RT4831_DSVMODE_SHIFT) 39#define DSV_MODE_BYPASS (0x6 << RT4831_DSVMODE_SHIFT) 40#define STEP_UV 50000 41#define VLCM_MIN_UV 4000000 42#define VLCM_MAX_UV 7150000 43#define VLCM_N_VOLTAGES ((VLCM_MAX_UV - VLCM_MIN_UV) / STEP_UV + 1) 44#define VPN_MIN_UV 4000000 45#define VPN_MAX_UV 6500000 46#define VPN_N_VOLTAGES ((VPN_MAX_UV - VPN_MIN_UV) / STEP_UV + 1) 47 48static int rt4831_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) 49{ 50 struct regmap *regmap = rdev_get_regmap(rdev); 51 int rid = rdev_get_id(rdev); 52 unsigned int val, events = 0; 53 int ret; 54 55 ret = regmap_read(regmap, RT4831_REG_FLAGS, &val); 56 if (ret) 57 return ret; 58 59 if (val & RT4831_OTP_MASK) 60 events |= REGULATOR_ERROR_OVER_TEMP; 61 62 if (rid == DSV_OUT_VLCM && (val & RT4831_LCMOVP_MASK)) 63 events |= REGULATOR_ERROR_OVER_CURRENT; 64 65 if (rid == DSV_OUT_VPOS && (val & RT4831_VPOSSCP_MASK)) 66 events |= REGULATOR_ERROR_OVER_CURRENT; 67 68 if (rid == DSV_OUT_VNEG && (val & RT4831_VNEGSCP_MASK)) 69 events |= REGULATOR_ERROR_OVER_CURRENT; 70 71 *flags = events; 72 return 0; 73} 74 75static const struct regulator_ops rt4831_dsvlcm_ops = { 76 .list_voltage = regulator_list_voltage_linear, 77 .set_voltage_sel = regulator_set_voltage_sel_regmap, 78 .get_voltage_sel = regulator_get_voltage_sel_regmap, 79 .set_bypass = regulator_set_bypass_regmap, 80 .get_bypass = regulator_get_bypass_regmap, 81 .get_error_flags = rt4831_get_error_flags, 82}; 83 84static const struct regulator_ops rt4831_dsvpn_ops = { 85 .list_voltage = regulator_list_voltage_linear, 86 .set_voltage_sel = regulator_set_voltage_sel_regmap, 87 .get_voltage_sel = regulator_get_voltage_sel_regmap, 88 .enable = regulator_enable_regmap, 89 .disable = regulator_disable_regmap, 90 .is_enabled = regulator_is_enabled_regmap, 91 .set_active_discharge = regulator_set_active_discharge_regmap, 92 .get_error_flags = rt4831_get_error_flags, 93}; 94 95static const struct regulator_desc rt4831_regulator_descs[] = { 96 { 97 .name = "DSVLCM", 98 .ops = &rt4831_dsvlcm_ops, 99 .of_match = of_match_ptr("DSVLCM"), 100 .regulators_node = of_match_ptr("regulators"), 101 .type = REGULATOR_VOLTAGE, 102 .id = DSV_OUT_VLCM, 103 .n_voltages = VLCM_N_VOLTAGES, 104 .min_uV = VLCM_MIN_UV, 105 .uV_step = STEP_UV, 106 .vsel_reg = RT4831_REG_VLCM, 107 .vsel_mask = RT4831_VOLT_MASK, 108 .bypass_reg = RT4831_REG_DSVEN, 109 .bypass_mask = RT4831_DSVMODE_MASK, 110 .bypass_val_on = DSV_MODE_BYPASS, 111 .bypass_val_off = DSV_MODE_NORMAL, 112 .owner = THIS_MODULE, 113 }, 114 { 115 .name = "DSVP", 116 .ops = &rt4831_dsvpn_ops, 117 .of_match = of_match_ptr("DSVP"), 118 .regulators_node = of_match_ptr("regulators"), 119 .type = REGULATOR_VOLTAGE, 120 .id = DSV_OUT_VPOS, 121 .n_voltages = VPN_N_VOLTAGES, 122 .min_uV = VPN_MIN_UV, 123 .uV_step = STEP_UV, 124 .vsel_reg = RT4831_REG_VPOS, 125 .vsel_mask = RT4831_VOLT_MASK, 126 .enable_reg = RT4831_REG_DSVEN, 127 .enable_mask = RT4831_POSEN_MASK, 128 .active_discharge_reg = RT4831_REG_DSVEN, 129 .active_discharge_mask = RT4831_POSADEN_MASK, 130 .active_discharge_on = RT4831_POSADEN_MASK, 131 .owner = THIS_MODULE, 132 }, 133 { 134 .name = "DSVN", 135 .ops = &rt4831_dsvpn_ops, 136 .of_match = of_match_ptr("DSVN"), 137 .regulators_node = of_match_ptr("regulators"), 138 .type = REGULATOR_VOLTAGE, 139 .id = DSV_OUT_VNEG, 140 .n_voltages = VPN_N_VOLTAGES, 141 .min_uV = VPN_MIN_UV, 142 .uV_step = STEP_UV, 143 .vsel_reg = RT4831_REG_VNEG, 144 .vsel_mask = RT4831_VOLT_MASK, 145 .enable_reg = RT4831_REG_DSVEN, 146 .enable_mask = RT4831_NEGEN_MASK, 147 .active_discharge_reg = RT4831_REG_DSVEN, 148 .active_discharge_mask = RT4831_NEGADEN_MASK, 149 .active_discharge_on = RT4831_NEGADEN_MASK, 150 .owner = THIS_MODULE, 151 } 152}; 153 154static int rt4831_regulator_probe(struct platform_device *pdev) 155{ 156 struct regmap *regmap; 157 struct regulator_dev *rdev; 158 struct regulator_config config = {}; 159 int i, ret; 160 161 regmap = dev_get_regmap(pdev->dev.parent, NULL); 162 if (!regmap) { 163 dev_err(&pdev->dev, "Failed to init regmap\n"); 164 return -ENODEV; 165 } 166 167 /* Configure DSV mode to normal by default */ 168 ret = regmap_update_bits(regmap, RT4831_REG_DSVEN, RT4831_DSVMODE_MASK, DSV_MODE_NORMAL); 169 if (ret) { 170 dev_err(&pdev->dev, "Failed to configure dsv mode to normal\n"); 171 return ret; 172 } 173 174 config.dev = pdev->dev.parent; 175 config.regmap = regmap; 176 177 for (i = 0; i < DSV_OUT_MAX; i++) { 178 rdev = devm_regulator_register(&pdev->dev, rt4831_regulator_descs + i, &config); 179 if (IS_ERR(rdev)) { 180 dev_err(&pdev->dev, "Failed to register %d regulator\n", i); 181 return PTR_ERR(rdev); 182 } 183 } 184 185 return 0; 186} 187 188static const struct platform_device_id rt4831_regulator_match[] = { 189 { "rt4831-regulator", 0 }, 190 {} 191}; 192MODULE_DEVICE_TABLE(platform, rt4831_regulator_match); 193 194static struct platform_driver rt4831_regulator_driver = { 195 .driver = { 196 .name = "rt4831-regulator", 197 }, 198 .id_table = rt4831_regulator_match, 199 .probe = rt4831_regulator_probe, 200}; 201module_platform_driver(rt4831_regulator_driver); 202 203MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); 204MODULE_LICENSE("GPL v2");