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

Input: pmic8xxx-pwrkey - support shutdown

On pm8xxx PMICs, shutdown and restart are signaled to the PMIC via a pin
called PS_HOLD. When this pin goes low, the PMIC performs a configurable
power sequence. Add a .shutdown hook so that we can properly configure this
power sequence for shutdown or restart depending on the system state.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Stephen Boyd and committed by
Dmitry Torokhov
02b0b79c c57d5621

+266 -2
+266 -2
drivers/input/misc/pmic8xxx-pwrkey.c
··· 20 20 #include <linux/regmap.h> 21 21 #include <linux/log2.h> 22 22 #include <linux/of.h> 23 + #include <linux/of_device.h> 23 24 24 25 #define PON_CNTL_1 0x1C 25 26 #define PON_CNTL_PULL_UP BIT(7) 26 27 #define PON_CNTL_TRIG_DELAY_MASK (0x7) 28 + #define PON_CNTL_1_PULL_UP_EN 0xe0 29 + #define PON_CNTL_1_USB_PWR_EN 0x10 30 + #define PON_CNTL_1_WD_EN_RESET 0x08 31 + 32 + #define PM8058_SLEEP_CTRL 0x02b 33 + #define PM8921_SLEEP_CTRL 0x10a 34 + 35 + #define SLEEP_CTRL_SMPL_EN_RESET 0x04 36 + 37 + /* Regulator master enable addresses */ 38 + #define REG_PM8058_VREG_EN_MSM 0x018 39 + #define REG_PM8058_VREG_EN_GRP_5_4 0x1c8 40 + 41 + /* Regulator control registers for shutdown/reset */ 42 + #define PM8058_S0_CTRL 0x004 43 + #define PM8058_S1_CTRL 0x005 44 + #define PM8058_S3_CTRL 0x111 45 + #define PM8058_L21_CTRL 0x120 46 + #define PM8058_L22_CTRL 0x121 47 + 48 + #define PM8058_REGULATOR_ENABLE_MASK 0x80 49 + #define PM8058_REGULATOR_ENABLE 0x80 50 + #define PM8058_REGULATOR_DISABLE 0x00 51 + #define PM8058_REGULATOR_PULL_DOWN_MASK 0x40 52 + #define PM8058_REGULATOR_PULL_DOWN_EN 0x40 53 + 54 + /* Buck CTRL register */ 55 + #define PM8058_SMPS_LEGACY_VREF_SEL 0x20 56 + #define PM8058_SMPS_LEGACY_VPROG_MASK 0x1f 57 + #define PM8058_SMPS_ADVANCED_BAND_MASK 0xC0 58 + #define PM8058_SMPS_ADVANCED_BAND_SHIFT 6 59 + #define PM8058_SMPS_ADVANCED_VPROG_MASK 0x3f 60 + 61 + /* Buck TEST2 registers for shutdown/reset */ 62 + #define PM8058_S0_TEST2 0x084 63 + #define PM8058_S1_TEST2 0x085 64 + #define PM8058_S3_TEST2 0x11a 65 + 66 + #define PM8058_REGULATOR_BANK_WRITE 0x80 67 + #define PM8058_REGULATOR_BANK_MASK 0x70 68 + #define PM8058_REGULATOR_BANK_SHIFT 4 69 + #define PM8058_REGULATOR_BANK_SEL(n) ((n) << PM8058_REGULATOR_BANK_SHIFT) 70 + 71 + /* Buck TEST2 register bank 1 */ 72 + #define PM8058_SMPS_LEGACY_VLOW_SEL 0x01 73 + 74 + /* Buck TEST2 register bank 7 */ 75 + #define PM8058_SMPS_ADVANCED_MODE_MASK 0x02 76 + #define PM8058_SMPS_ADVANCED_MODE 0x02 77 + #define PM8058_SMPS_LEGACY_MODE 0x00 27 78 28 79 /** 29 80 * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information 30 81 * @key_press_irq: key press irq number 82 + * @regmap: device regmap 83 + * @shutdown_fn: shutdown configuration function 31 84 */ 32 85 struct pmic8xxx_pwrkey { 33 86 int key_press_irq; 87 + struct regmap *regmap; 88 + int (*shutdown_fn)(struct pmic8xxx_pwrkey *, bool); 34 89 }; 35 90 36 91 static irqreturn_t pwrkey_press_irq(int irq, void *_pwr) ··· 131 76 static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, 132 77 pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); 133 78 79 + static void pmic8xxx_pwrkey_shutdown(struct platform_device *pdev) 80 + { 81 + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); 82 + int error; 83 + u8 mask, val; 84 + bool reset = system_state == SYSTEM_RESTART; 85 + 86 + if (pwrkey->shutdown_fn) { 87 + error = pwrkey->shutdown_fn(pwrkey, reset); 88 + if (error) 89 + return; 90 + } 91 + 92 + /* 93 + * Select action to perform (reset or shutdown) when PS_HOLD goes low. 94 + * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that 95 + * USB charging is enabled. 96 + */ 97 + mask = PON_CNTL_1_PULL_UP_EN | PON_CNTL_1_USB_PWR_EN; 98 + mask |= PON_CNTL_1_WD_EN_RESET; 99 + val = mask; 100 + if (!reset) 101 + val &= ~PON_CNTL_1_WD_EN_RESET; 102 + 103 + regmap_update_bits(pwrkey->regmap, PON_CNTL_1, mask, val); 104 + } 105 + 106 + /* 107 + * Set an SMPS regulator to be disabled in its CTRL register, but enabled 108 + * in the master enable register. Also set it's pull down enable bit. 109 + * Take care to make sure that the output voltage doesn't change if switching 110 + * from advanced mode to legacy mode. 111 + */ 112 + static int pm8058_disable_smps_locally_set_pull_down(struct regmap *regmap, 113 + u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr, 114 + u8 master_enable_bit) 115 + { 116 + int error; 117 + u8 vref_sel, vlow_sel, band, vprog, bank; 118 + unsigned int reg; 119 + 120 + bank = PM8058_REGULATOR_BANK_SEL(7); 121 + error = regmap_write(regmap, test2_addr, bank); 122 + if (error) 123 + return error; 124 + 125 + error = regmap_read(regmap, test2_addr, &reg); 126 + if (error) 127 + return error; 128 + 129 + reg &= PM8058_SMPS_ADVANCED_MODE_MASK; 130 + /* Check if in advanced mode. */ 131 + if (reg == PM8058_SMPS_ADVANCED_MODE) { 132 + /* Determine current output voltage. */ 133 + error = regmap_read(regmap, ctrl_addr, &reg); 134 + if (error) 135 + return error; 136 + 137 + band = reg & PM8058_SMPS_ADVANCED_BAND_MASK; 138 + band >>= PM8058_SMPS_ADVANCED_BAND_SHIFT; 139 + switch (band) { 140 + case 3: 141 + vref_sel = 0; 142 + vlow_sel = 0; 143 + break; 144 + case 2: 145 + vref_sel = PM8058_SMPS_LEGACY_VREF_SEL; 146 + vlow_sel = 0; 147 + break; 148 + case 1: 149 + vref_sel = PM8058_SMPS_LEGACY_VREF_SEL; 150 + vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL; 151 + break; 152 + default: 153 + pr_err("%s: regulator already disabled\n", __func__); 154 + return -EPERM; 155 + } 156 + vprog = reg & PM8058_SMPS_ADVANCED_VPROG_MASK; 157 + /* Round up if fine step is in use. */ 158 + vprog = (vprog + 1) >> 1; 159 + if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK) 160 + vprog = PM8058_SMPS_LEGACY_VPROG_MASK; 161 + 162 + /* Set VLOW_SEL bit. */ 163 + bank = PM8058_REGULATOR_BANK_SEL(1); 164 + error = regmap_write(regmap, test2_addr, bank); 165 + if (error) 166 + return error; 167 + 168 + error = regmap_update_bits(regmap, test2_addr, 169 + PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK 170 + | PM8058_SMPS_LEGACY_VLOW_SEL, 171 + PM8058_REGULATOR_BANK_WRITE | 172 + PM8058_REGULATOR_BANK_SEL(1) | vlow_sel); 173 + if (error) 174 + return error; 175 + 176 + /* Switch to legacy mode */ 177 + bank = PM8058_REGULATOR_BANK_SEL(7); 178 + error = regmap_write(regmap, test2_addr, bank); 179 + if (error) 180 + return error; 181 + 182 + error = regmap_update_bits(regmap, test2_addr, 183 + PM8058_REGULATOR_BANK_WRITE | 184 + PM8058_REGULATOR_BANK_MASK | 185 + PM8058_SMPS_ADVANCED_MODE_MASK, 186 + PM8058_REGULATOR_BANK_WRITE | 187 + PM8058_REGULATOR_BANK_SEL(7) | 188 + PM8058_SMPS_LEGACY_MODE); 189 + if (error) 190 + return error; 191 + 192 + /* Enable locally, enable pull down, keep voltage the same. */ 193 + error = regmap_update_bits(regmap, ctrl_addr, 194 + PM8058_REGULATOR_ENABLE_MASK | 195 + PM8058_REGULATOR_PULL_DOWN_MASK | 196 + PM8058_SMPS_LEGACY_VREF_SEL | 197 + PM8058_SMPS_LEGACY_VPROG_MASK, 198 + PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN 199 + | vref_sel | vprog); 200 + if (error) 201 + return error; 202 + } 203 + 204 + /* Enable in master control register. */ 205 + error = regmap_update_bits(regmap, master_enable_addr, 206 + master_enable_bit, master_enable_bit); 207 + if (error) 208 + return error; 209 + 210 + /* Disable locally and enable pull down. */ 211 + return regmap_update_bits(regmap, ctrl_addr, 212 + PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK, 213 + PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN); 214 + } 215 + 216 + static int pm8058_disable_ldo_locally_set_pull_down(struct regmap *regmap, 217 + u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit) 218 + { 219 + int error; 220 + 221 + /* Enable LDO in master control register. */ 222 + error = regmap_update_bits(regmap, master_enable_addr, 223 + master_enable_bit, master_enable_bit); 224 + if (error) 225 + return error; 226 + 227 + /* Disable LDO in CTRL register and set pull down */ 228 + return regmap_update_bits(regmap, ctrl_addr, 229 + PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK, 230 + PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN); 231 + } 232 + 233 + static int pm8058_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset) 234 + { 235 + int error; 236 + struct regmap *regmap = pwrkey->regmap; 237 + u8 mask, val; 238 + 239 + /* When shutting down, enable active pulldowns on important rails. */ 240 + if (!reset) { 241 + /* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */ 242 + pm8058_disable_smps_locally_set_pull_down(regmap, 243 + PM8058_S0_CTRL, PM8058_S0_TEST2, 244 + REG_PM8058_VREG_EN_MSM, BIT(7)); 245 + pm8058_disable_smps_locally_set_pull_down(regmap, 246 + PM8058_S1_CTRL, PM8058_S1_TEST2, 247 + REG_PM8058_VREG_EN_MSM, BIT(6)); 248 + pm8058_disable_smps_locally_set_pull_down(regmap, 249 + PM8058_S3_CTRL, PM8058_S3_TEST2, 250 + REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4)); 251 + /* Disable LDO 21 locally and set pulldown enable bit. */ 252 + pm8058_disable_ldo_locally_set_pull_down(regmap, 253 + PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4, 254 + BIT(1)); 255 + } 256 + 257 + /* 258 + * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its 259 + * pull-down state intact. This ensures a safe shutdown. 260 + */ 261 + error = regmap_update_bits(regmap, PM8058_L22_CTRL, 0xbf, 0x93); 262 + if (error) 263 + return error; 264 + 265 + /* Enable SMPL if resetting is desired */ 266 + mask = SLEEP_CTRL_SMPL_EN_RESET; 267 + val = 0; 268 + if (reset) 269 + val = mask; 270 + return regmap_update_bits(regmap, PM8058_SLEEP_CTRL, mask, val); 271 + } 272 + 273 + static int pm8921_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset) 274 + { 275 + struct regmap *regmap = pwrkey->regmap; 276 + u8 mask = SLEEP_CTRL_SMPL_EN_RESET; 277 + u8 val = 0; 278 + 279 + /* Enable SMPL if resetting is desired */ 280 + if (reset) 281 + val = mask; 282 + return regmap_update_bits(regmap, PM8921_SLEEP_CTRL, mask, val); 283 + } 284 + 134 285 static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) 135 286 { 136 287 struct input_dev *pwr; ··· 370 109 if (!pwrkey) 371 110 return -ENOMEM; 372 111 112 + pwrkey->shutdown_fn = of_device_get_match_data(&pdev->dev); 113 + pwrkey->regmap = regmap; 373 114 pwrkey->key_press_irq = key_press_irq; 374 115 375 116 pwr = devm_input_allocate_device(&pdev->dev); ··· 445 182 } 446 183 447 184 static const struct of_device_id pm8xxx_pwr_key_id_table[] = { 448 - { .compatible = "qcom,pm8058-pwrkey" }, 449 - { .compatible = "qcom,pm8921-pwrkey" }, 185 + { .compatible = "qcom,pm8058-pwrkey", .data = &pm8058_pwrkey_shutdown }, 186 + { .compatible = "qcom,pm8921-pwrkey", .data = &pm8921_pwrkey_shutdown }, 450 187 { } 451 188 }; 452 189 MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); ··· 454 191 static struct platform_driver pmic8xxx_pwrkey_driver = { 455 192 .probe = pmic8xxx_pwrkey_probe, 456 193 .remove = pmic8xxx_pwrkey_remove, 194 + .shutdown = pmic8xxx_pwrkey_shutdown, 457 195 .driver = { 458 196 .name = "pm8xxx-pwrkey", 459 197 .pm = &pm8xxx_pwr_key_pm_ops,