Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regulator: act8945a-regulator: Implement PM functionalities

The regulator supports a dedicated suspend mode.
Implement the appropriate ->set_suspend_xx() hooks, add support for
->set_mode(), and provide basic PM ops functionalities to setup the
regulator in a suspend state when the system is entering suspend.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
[claudiu.beznea@microchip.com: remove shutdown function, use dev_pm_ops,
fix checkpatch warning, adapt commit message, add LDO modes support,
move modes constants to active-semi,8945a-regulator.h, remove rdevs from
struct act8945a_pmic, add op_mode to act8945a_pmic]
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Boris Brezillon and committed by
Mark Brown
7482d6ec 90bc8ac2

+211 -5
+181 -5
drivers/regulator/act8945a-regulator.c
··· 18 18 #include <linux/regmap.h> 19 19 #include <linux/regulator/driver.h> 20 20 #include <linux/regulator/machine.h> 21 + #include <dt-bindings/regulator/active-semi,8945a-regulator.h> 21 22 22 23 /** 23 24 * ACT8945A Global Register Map. ··· 29 28 #define ACT8945A_DCDC1_VSET1 0x20 30 29 #define ACT8945A_DCDC1_VSET2 0x21 31 30 #define ACT8945A_DCDC1_CTRL 0x22 31 + #define ACT8945A_DCDC1_SUS 0x24 32 32 #define ACT8945A_DCDC2_VSET1 0x30 33 33 #define ACT8945A_DCDC2_VSET2 0x31 34 34 #define ACT8945A_DCDC2_CTRL 0x32 35 + #define ACT8945A_DCDC2_SUS 0x34 35 36 #define ACT8945A_DCDC3_VSET1 0x40 36 37 #define ACT8945A_DCDC3_VSET2 0x41 37 38 #define ACT8945A_DCDC3_CTRL 0x42 39 + #define ACT8945A_DCDC3_SUS 0x44 38 40 #define ACT8945A_LDO1_VSET 0x50 39 41 #define ACT8945A_LDO1_CTRL 0x51 42 + #define ACT8945A_LDO1_SUS 0x52 40 43 #define ACT8945A_LDO2_VSET 0x54 41 44 #define ACT8945A_LDO2_CTRL 0x55 45 + #define ACT8945A_LDO2_SUS 0x56 42 46 #define ACT8945A_LDO3_VSET 0x60 43 47 #define ACT8945A_LDO3_CTRL 0x61 48 + #define ACT8945A_LDO3_SUS 0x62 44 49 #define ACT8945A_LDO4_VSET 0x64 45 50 #define ACT8945A_LDO4_CTRL 0x65 51 + #define ACT8945A_LDO4_SUS 0x66 46 52 47 53 /** 48 54 * Field Definitions. ··· 70 62 ACT8945A_ID_LDO2, 71 63 ACT8945A_ID_LDO3, 72 64 ACT8945A_ID_LDO4, 73 - ACT8945A_REG_NUM, 65 + ACT8945A_ID_MAX, 66 + }; 67 + 68 + struct act8945a_pmic { 69 + struct regmap *regmap; 70 + u32 op_mode[ACT8945A_ID_MAX]; 74 71 }; 75 72 76 73 static const struct regulator_linear_range act8945a_voltage_ranges[] = { ··· 84 71 REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000), 85 72 }; 86 73 74 + static int act8945a_set_suspend_state(struct regulator_dev *rdev, bool enable) 75 + { 76 + struct regmap *regmap = rdev->regmap; 77 + int id = rdev->desc->id, reg, val; 78 + 79 + switch (id) { 80 + case ACT8945A_ID_DCDC1: 81 + reg = ACT8945A_DCDC1_SUS; 82 + val = 0xa8; 83 + break; 84 + case ACT8945A_ID_DCDC2: 85 + reg = ACT8945A_DCDC2_SUS; 86 + val = 0xa8; 87 + break; 88 + case ACT8945A_ID_DCDC3: 89 + reg = ACT8945A_DCDC3_SUS; 90 + val = 0xa8; 91 + break; 92 + case ACT8945A_ID_LDO1: 93 + reg = ACT8945A_LDO1_SUS; 94 + val = 0xe8; 95 + break; 96 + case ACT8945A_ID_LDO2: 97 + reg = ACT8945A_LDO2_SUS; 98 + val = 0xe8; 99 + break; 100 + case ACT8945A_ID_LDO3: 101 + reg = ACT8945A_LDO3_SUS; 102 + val = 0xe8; 103 + break; 104 + case ACT8945A_ID_LDO4: 105 + reg = ACT8945A_LDO4_SUS; 106 + val = 0xe8; 107 + break; 108 + default: 109 + return -EINVAL; 110 + } 111 + 112 + if (enable) 113 + val |= BIT(4); 114 + 115 + /* 116 + * Ask the PMIC to enable/disable this output when entering hibernate 117 + * mode. 118 + */ 119 + return regmap_write(regmap, reg, val); 120 + } 121 + 122 + static int act8945a_set_suspend_enable(struct regulator_dev *rdev) 123 + { 124 + return act8945a_set_suspend_state(rdev, true); 125 + } 126 + 127 + static int act8945a_set_suspend_disable(struct regulator_dev *rdev) 128 + { 129 + return act8945a_set_suspend_state(rdev, false); 130 + } 131 + 132 + static unsigned int act8945a_of_map_mode(unsigned int mode) 133 + { 134 + switch (mode) { 135 + case ACT8945A_REGULATOR_MODE_FIXED: 136 + case ACT8945A_REGULATOR_MODE_NORMAL: 137 + return REGULATOR_MODE_NORMAL; 138 + case ACT8945A_REGULATOR_MODE_LOWPOWER: 139 + return REGULATOR_MODE_STANDBY; 140 + default: 141 + return REGULATOR_MODE_INVALID; 142 + } 143 + } 144 + 145 + static int act8945a_set_mode(struct regulator_dev *rdev, unsigned int mode) 146 + { 147 + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); 148 + struct regmap *regmap = rdev->regmap; 149 + int id = rdev->desc->id; 150 + int reg, ret, val = 0; 151 + 152 + switch (id) { 153 + case ACT8945A_ID_DCDC1: 154 + reg = ACT8945A_DCDC1_CTRL; 155 + break; 156 + case ACT8945A_ID_DCDC2: 157 + reg = ACT8945A_DCDC2_CTRL; 158 + break; 159 + case ACT8945A_ID_DCDC3: 160 + reg = ACT8945A_DCDC3_CTRL; 161 + break; 162 + case ACT8945A_ID_LDO1: 163 + reg = ACT8945A_LDO1_SUS; 164 + break; 165 + case ACT8945A_ID_LDO2: 166 + reg = ACT8945A_LDO2_SUS; 167 + break; 168 + case ACT8945A_ID_LDO3: 169 + reg = ACT8945A_LDO3_SUS; 170 + break; 171 + case ACT8945A_ID_LDO4: 172 + reg = ACT8945A_LDO4_SUS; 173 + break; 174 + default: 175 + return -EINVAL; 176 + } 177 + 178 + switch (mode) { 179 + case REGULATOR_MODE_STANDBY: 180 + if (rdev->desc->id > ACT8945A_ID_DCDC3) 181 + val = BIT(5); 182 + break; 183 + case REGULATOR_MODE_NORMAL: 184 + if (rdev->desc->id <= ACT8945A_ID_DCDC3) 185 + val = BIT(5); 186 + break; 187 + default: 188 + return -EINVAL; 189 + } 190 + 191 + ret = regmap_update_bits(regmap, reg, BIT(5), val); 192 + if (ret) 193 + return ret; 194 + 195 + act8945a->op_mode[id] = mode; 196 + 197 + return 0; 198 + } 199 + 200 + static unsigned int act8945a_get_mode(struct regulator_dev *rdev) 201 + { 202 + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); 203 + int id = rdev->desc->id; 204 + 205 + if (id < ACT8945A_ID_DCDC1 || id >= ACT8945A_ID_MAX) 206 + return -EINVAL; 207 + 208 + return act8945a->op_mode[id]; 209 + } 210 + 87 211 static const struct regulator_ops act8945a_ops = { 88 212 .list_voltage = regulator_list_voltage_linear_range, 89 213 .map_voltage = regulator_map_voltage_linear_range, ··· 228 78 .set_voltage_sel = regulator_set_voltage_sel_regmap, 229 79 .enable = regulator_enable_regmap, 230 80 .disable = regulator_disable_regmap, 81 + .set_mode = act8945a_set_mode, 82 + .get_mode = act8945a_get_mode, 231 83 .is_enabled = regulator_is_enabled_regmap, 84 + .set_suspend_enable = act8945a_set_suspend_enable, 85 + .set_suspend_disable = act8945a_set_suspend_disable, 232 86 }; 233 87 234 88 #define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply) \ ··· 240 86 .name = _name, \ 241 87 .supply_name = _supply, \ 242 88 .of_match = of_match_ptr("REG_"#_id), \ 89 + .of_map_mode = act8945a_of_map_mode, \ 243 90 .regulators_node = of_match_ptr("regulators"), \ 244 91 .id = _family##_ID_##_id, \ 245 92 .type = REGULATOR_VOLTAGE, \ ··· 279 124 { 280 125 struct regulator_config config = { }; 281 126 const struct regulator_desc *regulators; 127 + struct act8945a_pmic *act8945a; 282 128 struct regulator_dev *rdev; 283 - struct regmap *regmap; 284 129 int i, num_regulators; 285 130 bool voltage_select; 286 131 287 - regmap = dev_get_regmap(pdev->dev.parent, NULL); 288 - if (!regmap) { 132 + act8945a = devm_kzalloc(&pdev->dev, sizeof(*act8945a), GFP_KERNEL); 133 + if (!act8945a) 134 + return -ENOMEM; 135 + 136 + act8945a->regmap = dev_get_regmap(pdev->dev.parent, NULL); 137 + if (!act8945a->regmap) { 289 138 dev_err(&pdev->dev, 290 139 "could not retrieve regmap from parent device\n"); 291 140 return -EINVAL; ··· 308 149 309 150 config.dev = &pdev->dev; 310 151 config.dev->of_node = pdev->dev.parent->of_node; 152 + config.driver_data = act8945a; 311 153 for (i = 0; i < num_regulators; i++) { 312 154 rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config); 313 155 if (IS_ERR(rdev)) { ··· 319 159 } 320 160 } 321 161 162 + platform_set_drvdata(pdev, act8945a); 163 + 322 164 /* Unlock expert registers. */ 323 - return regmap_write(regmap, ACT8945A_SYS_UNLK_REGS, 0xef); 165 + return regmap_write(act8945a->regmap, ACT8945A_SYS_UNLK_REGS, 0xef); 324 166 } 167 + 168 + static int act8945a_suspend(struct device *pdev) 169 + { 170 + struct act8945a_pmic *act8945a = dev_get_drvdata(pdev); 171 + 172 + /* 173 + * Ask the PMIC to enter the suspend mode on the next PWRHLD 174 + * transition. 175 + */ 176 + return regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x42); 177 + } 178 + 179 + SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); 325 180 326 181 static struct platform_driver act8945a_pmic_driver = { 327 182 .driver = { 328 183 .name = "act8945a-regulator", 184 + .pm = &act8945a_pm, 329 185 }, 330 186 .probe = act8945a_pmic_probe, 331 187 };
+30
include/dt-bindings/regulator/active-semi,8945a-regulator.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (c) 2018 Microchip Technology, Inc. All rights reserved. 4 + * 5 + * Device Tree binding constants for the ACT8945A PMIC regulators 6 + */ 7 + 8 + #ifndef _DT_BINDINGS_REGULATOR_ACT8945A_H 9 + #define _DT_BINDINGS_REGULATOR_ACT8945A_H 10 + 11 + /* 12 + * These constants should be used to specify regulator modes in device tree for 13 + * ACT8945A regulators as follows: 14 + * ACT8945A_REGULATOR_MODE_FIXED: It is specific to DCDC regulators and it 15 + * specifies the usage of fixed-frequency 16 + * PWM. 17 + * 18 + * ACT8945A_REGULATOR_MODE_NORMAL: It is specific to LDO regulators and it 19 + * specifies the usage of normal mode. 20 + * 21 + * ACT8945A_REGULATOR_MODE_LOWPOWER: For DCDC and LDO regulators; it specify 22 + * the usage of proprietary power-saving 23 + * mode. 24 + */ 25 + 26 + #define ACT8945A_REGULATOR_MODE_FIXED 1 27 + #define ACT8945A_REGULATOR_MODE_NORMAL 2 28 + #define ACT8945A_REGULATOR_MODE_LOWPOWER 3 29 + 30 + #endif