1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Poweroff & reset driver for Actions Semi ATC260x PMICs 4 * 5 * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> 6 */ 7 8 #include <linux/delay.h> 9 #include <linux/mfd/atc260x/core.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/power_supply.h> 13 #include <linux/reboot.h> 14 #include <linux/regmap.h> 15 16 struct atc260x_pwrc { 17 struct device *dev; 18 struct regmap *regmap; 19 struct notifier_block restart_nb; 20 int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart); 21 }; 22 23 /* Global variable needed only for pm_power_off */ 24 static struct atc260x_pwrc *atc260x_pwrc_data; 25 26 static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart) 27 { 28 int ret, deep_sleep = 0; 29 uint reg_mask, reg_val; 30 31 /* S4-Deep Sleep Mode is NOT available for WALL/USB power */ 32 if (!restart && !power_supply_is_system_supplied()) { 33 deep_sleep = 1; 34 dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode"); 35 } 36 37 /* Update wakeup sources */ 38 reg_val = ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN | 39 (restart ? ATC2603C_PMU_SYS_CTL0_RESET_WK_EN 40 : ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN); 41 42 ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0, 43 ATC2603C_PMU_SYS_CTL0_WK_ALL, reg_val); 44 if (ret) 45 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret); 46 47 /* Update power mode */ 48 reg_mask = ATC2603C_PMU_SYS_CTL3_EN_S2 | ATC2603C_PMU_SYS_CTL3_EN_S3; 49 50 ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3, reg_mask, 51 deep_sleep ? 0 : ATC2603C_PMU_SYS_CTL3_EN_S3); 52 if (ret) { 53 dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret); 54 return ret; 55 } 56 57 /* Trigger poweroff / restart sequence */ 58 reg_mask = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN 59 : ATC2603C_PMU_SYS_CTL1_EN_S1; 60 reg_val = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN : 0; 61 62 ret = regmap_update_bits(pwrc->regmap, 63 restart ? ATC2603C_PMU_SYS_CTL0 : ATC2603C_PMU_SYS_CTL1, 64 reg_mask, reg_val); 65 if (ret) { 66 dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n", 67 restart ? 0 : 1, ret); 68 return ret; 69 } 70 71 /* Wait for trigger completion */ 72 mdelay(200); 73 74 return 0; 75 } 76 77 static int atc2609a_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart) 78 { 79 int ret, deep_sleep = 0; 80 uint reg_mask, reg_val; 81 82 /* S4-Deep Sleep Mode is NOT available for WALL/USB power */ 83 if (!restart && !power_supply_is_system_supplied()) { 84 deep_sleep = 1; 85 dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode"); 86 } 87 88 /* Update wakeup sources */ 89 reg_val = ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN | 90 (restart ? ATC2609A_PMU_SYS_CTL0_RESET_WK_EN 91 : ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN); 92 93 ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0, 94 ATC2609A_PMU_SYS_CTL0_WK_ALL, reg_val); 95 if (ret) 96 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret); 97 98 /* Update power mode */ 99 reg_mask = ATC2609A_PMU_SYS_CTL3_EN_S2 | ATC2609A_PMU_SYS_CTL3_EN_S3; 100 101 ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL3, reg_mask, 102 deep_sleep ? 0 : ATC2609A_PMU_SYS_CTL3_EN_S3); 103 if (ret) { 104 dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret); 105 return ret; 106 } 107 108 /* Trigger poweroff / restart sequence */ 109 reg_mask = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN 110 : ATC2609A_PMU_SYS_CTL1_EN_S1; 111 reg_val = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN : 0; 112 113 ret = regmap_update_bits(pwrc->regmap, 114 restart ? ATC2609A_PMU_SYS_CTL0 : ATC2609A_PMU_SYS_CTL1, 115 reg_mask, reg_val); 116 if (ret) { 117 dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n", 118 restart ? 0 : 1, ret); 119 return ret; 120 } 121 122 /* Wait for trigger completion */ 123 mdelay(200); 124 125 return 0; 126 } 127 128 static int atc2603c_init(const struct atc260x_pwrc *pwrc) 129 { 130 int ret; 131 132 /* 133 * Delay transition from S2/S3 to S1 in order to avoid 134 * DDR init failure in Bootloader. 135 */ 136 ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3, 137 ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN, 138 ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN); 139 if (ret) 140 dev_warn(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret); 141 142 /* Set wakeup sources */ 143 ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0, 144 ATC2603C_PMU_SYS_CTL0_WK_ALL, 145 ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN | 146 ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN); 147 if (ret) 148 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret); 149 150 return ret; 151 } 152 153 static int atc2609a_init(const struct atc260x_pwrc *pwrc) 154 { 155 int ret; 156 157 /* Set wakeup sources */ 158 ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0, 159 ATC2609A_PMU_SYS_CTL0_WK_ALL, 160 ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN | 161 ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN); 162 if (ret) 163 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret); 164 165 return ret; 166 } 167 168 static void atc260x_pwrc_pm_handler(void) 169 { 170 atc260x_pwrc_data->do_poweroff(atc260x_pwrc_data, false); 171 172 WARN_ONCE(1, "Unable to power off system\n"); 173 } 174 175 static int atc260x_pwrc_restart_handler(struct notifier_block *nb, 176 unsigned long mode, void *cmd) 177 { 178 struct atc260x_pwrc *pwrc = container_of(nb, struct atc260x_pwrc, 179 restart_nb); 180 pwrc->do_poweroff(pwrc, true); 181 182 return NOTIFY_DONE; 183 } 184 185 static int atc260x_pwrc_probe(struct platform_device *pdev) 186 { 187 struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); 188 struct atc260x_pwrc *priv; 189 int ret; 190 191 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 192 if (!priv) 193 return -ENOMEM; 194 195 priv->dev = &pdev->dev; 196 priv->regmap = atc260x->regmap; 197 priv->restart_nb.notifier_call = atc260x_pwrc_restart_handler; 198 priv->restart_nb.priority = 192; 199 200 switch (atc260x->ic_type) { 201 case ATC2603C: 202 priv->do_poweroff = atc2603c_do_poweroff; 203 ret = atc2603c_init(priv); 204 break; 205 case ATC2609A: 206 priv->do_poweroff = atc2609a_do_poweroff; 207 ret = atc2609a_init(priv); 208 break; 209 default: 210 dev_err(priv->dev, 211 "Poweroff not supported for ATC260x PMIC type: %u\n", 212 atc260x->ic_type); 213 return -EINVAL; 214 } 215 216 if (ret) 217 return ret; 218 219 platform_set_drvdata(pdev, priv); 220 221 if (!pm_power_off) { 222 atc260x_pwrc_data = priv; 223 pm_power_off = atc260x_pwrc_pm_handler; 224 } else { 225 dev_warn(priv->dev, "Poweroff callback already assigned\n"); 226 } 227 228 ret = register_restart_handler(&priv->restart_nb); 229 if (ret) 230 dev_err(priv->dev, "failed to register restart handler: %d\n", 231 ret); 232 233 return ret; 234 } 235 236 static int atc260x_pwrc_remove(struct platform_device *pdev) 237 { 238 struct atc260x_pwrc *priv = platform_get_drvdata(pdev); 239 240 if (atc260x_pwrc_data == priv) { 241 pm_power_off = NULL; 242 atc260x_pwrc_data = NULL; 243 } 244 245 unregister_restart_handler(&priv->restart_nb); 246 247 return 0; 248 } 249 250 static struct platform_driver atc260x_pwrc_driver = { 251 .probe = atc260x_pwrc_probe, 252 .remove = atc260x_pwrc_remove, 253 .driver = { 254 .name = "atc260x-pwrc", 255 }, 256 }; 257 258 module_platform_driver(atc260x_pwrc_driver); 259 260 MODULE_DESCRIPTION("Poweroff & reset driver for ATC260x PMICs"); 261 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>"); 262 MODULE_LICENSE("GPL"); 263