Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Poweroff & reset driver for Actions Semi ATC260x PMICs
0004  *
0005  * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/mfd/atc260x/core.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/power_supply.h>
0013 #include <linux/reboot.h>
0014 #include <linux/regmap.h>
0015 
0016 struct atc260x_pwrc {
0017     struct device *dev;
0018     struct regmap *regmap;
0019     struct notifier_block restart_nb;
0020     int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart);
0021 };
0022 
0023 /* Global variable needed only for pm_power_off */
0024 static struct atc260x_pwrc *atc260x_pwrc_data;
0025 
0026 static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
0027 {
0028     int ret, deep_sleep = 0;
0029     uint reg_mask, reg_val;
0030 
0031     /* S4-Deep Sleep Mode is NOT available for WALL/USB power */
0032     if (!restart && !power_supply_is_system_supplied()) {
0033         deep_sleep = 1;
0034         dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
0035     }
0036 
0037     /* Update wakeup sources */
0038     reg_val = ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
0039           (restart ? ATC2603C_PMU_SYS_CTL0_RESET_WK_EN
0040                : ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
0041 
0042     ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
0043                  ATC2603C_PMU_SYS_CTL0_WK_ALL, reg_val);
0044     if (ret)
0045         dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
0046 
0047     /* Update power mode */
0048     reg_mask = ATC2603C_PMU_SYS_CTL3_EN_S2 | ATC2603C_PMU_SYS_CTL3_EN_S3;
0049 
0050     ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3, reg_mask,
0051                  deep_sleep ? 0 : ATC2603C_PMU_SYS_CTL3_EN_S3);
0052     if (ret) {
0053         dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
0054         return ret;
0055     }
0056 
0057     /* Trigger poweroff / restart sequence */
0058     reg_mask = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN
0059                : ATC2603C_PMU_SYS_CTL1_EN_S1;
0060     reg_val = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN : 0;
0061 
0062     ret = regmap_update_bits(pwrc->regmap,
0063                  restart ? ATC2603C_PMU_SYS_CTL0 : ATC2603C_PMU_SYS_CTL1,
0064                  reg_mask, reg_val);
0065     if (ret) {
0066         dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
0067             restart ? 0 : 1, ret);
0068         return ret;
0069     }
0070 
0071     /* Wait for trigger completion */
0072     mdelay(200);
0073 
0074     return 0;
0075 }
0076 
0077 static int atc2609a_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
0078 {
0079     int ret, deep_sleep = 0;
0080     uint reg_mask, reg_val;
0081 
0082     /* S4-Deep Sleep Mode is NOT available for WALL/USB power */
0083     if (!restart && !power_supply_is_system_supplied()) {
0084         deep_sleep = 1;
0085         dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
0086     }
0087 
0088     /* Update wakeup sources */
0089     reg_val = ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
0090           (restart ? ATC2609A_PMU_SYS_CTL0_RESET_WK_EN
0091                : ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
0092 
0093     ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
0094                  ATC2609A_PMU_SYS_CTL0_WK_ALL, reg_val);
0095     if (ret)
0096         dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
0097 
0098     /* Update power mode */
0099     reg_mask = ATC2609A_PMU_SYS_CTL3_EN_S2 | ATC2609A_PMU_SYS_CTL3_EN_S3;
0100 
0101     ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL3, reg_mask,
0102                  deep_sleep ? 0 : ATC2609A_PMU_SYS_CTL3_EN_S3);
0103     if (ret) {
0104         dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
0105         return ret;
0106     }
0107 
0108     /* Trigger poweroff / restart sequence */
0109     reg_mask = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN
0110                : ATC2609A_PMU_SYS_CTL1_EN_S1;
0111     reg_val = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN : 0;
0112 
0113     ret = regmap_update_bits(pwrc->regmap,
0114                  restart ? ATC2609A_PMU_SYS_CTL0 : ATC2609A_PMU_SYS_CTL1,
0115                  reg_mask, reg_val);
0116     if (ret) {
0117         dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
0118             restart ? 0 : 1, ret);
0119         return ret;
0120     }
0121 
0122     /* Wait for trigger completion */
0123     mdelay(200);
0124 
0125     return 0;
0126 }
0127 
0128 static int atc2603c_init(const struct atc260x_pwrc *pwrc)
0129 {
0130     int ret;
0131 
0132     /*
0133      * Delay transition from S2/S3 to S1 in order to avoid
0134      * DDR init failure in Bootloader.
0135      */
0136     ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3,
0137                  ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN,
0138                  ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN);
0139     if (ret)
0140         dev_warn(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
0141 
0142     /* Set wakeup sources */
0143     ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
0144                  ATC2603C_PMU_SYS_CTL0_WK_ALL,
0145                  ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN |
0146                  ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
0147     if (ret)
0148         dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
0149 
0150     return ret;
0151 }
0152 
0153 static int atc2609a_init(const struct atc260x_pwrc *pwrc)
0154 {
0155     int ret;
0156 
0157     /* Set wakeup sources */
0158     ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
0159                  ATC2609A_PMU_SYS_CTL0_WK_ALL,
0160                  ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN |
0161                  ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
0162     if (ret)
0163         dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
0164 
0165     return ret;
0166 }
0167 
0168 static void atc260x_pwrc_pm_handler(void)
0169 {
0170     atc260x_pwrc_data->do_poweroff(atc260x_pwrc_data, false);
0171 
0172     WARN_ONCE(1, "Unable to power off system\n");
0173 }
0174 
0175 static int atc260x_pwrc_restart_handler(struct notifier_block *nb,
0176                     unsigned long mode, void *cmd)
0177 {
0178     struct atc260x_pwrc *pwrc = container_of(nb, struct atc260x_pwrc,
0179                          restart_nb);
0180     pwrc->do_poweroff(pwrc, true);
0181 
0182     return NOTIFY_DONE;
0183 }
0184 
0185 static int atc260x_pwrc_probe(struct platform_device *pdev)
0186 {
0187     struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
0188     struct atc260x_pwrc *priv;
0189     int ret;
0190 
0191     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0192     if (!priv)
0193         return -ENOMEM;
0194 
0195     priv->dev = &pdev->dev;
0196     priv->regmap = atc260x->regmap;
0197     priv->restart_nb.notifier_call = atc260x_pwrc_restart_handler;
0198     priv->restart_nb.priority = 192;
0199 
0200     switch (atc260x->ic_type) {
0201     case ATC2603C:
0202         priv->do_poweroff = atc2603c_do_poweroff;
0203         ret = atc2603c_init(priv);
0204         break;
0205     case ATC2609A:
0206         priv->do_poweroff = atc2609a_do_poweroff;
0207         ret = atc2609a_init(priv);
0208         break;
0209     default:
0210         dev_err(priv->dev,
0211             "Poweroff not supported for ATC260x PMIC type: %u\n",
0212             atc260x->ic_type);
0213         return -EINVAL;
0214     }
0215 
0216     if (ret)
0217         return ret;
0218 
0219     platform_set_drvdata(pdev, priv);
0220 
0221     if (!pm_power_off) {
0222         atc260x_pwrc_data = priv;
0223         pm_power_off = atc260x_pwrc_pm_handler;
0224     } else {
0225         dev_warn(priv->dev, "Poweroff callback already assigned\n");
0226     }
0227 
0228     ret = register_restart_handler(&priv->restart_nb);
0229     if (ret)
0230         dev_err(priv->dev, "failed to register restart handler: %d\n",
0231             ret);
0232 
0233     return ret;
0234 }
0235 
0236 static int atc260x_pwrc_remove(struct platform_device *pdev)
0237 {
0238     struct atc260x_pwrc *priv = platform_get_drvdata(pdev);
0239 
0240     if (atc260x_pwrc_data == priv) {
0241         pm_power_off = NULL;
0242         atc260x_pwrc_data = NULL;
0243     }
0244 
0245     unregister_restart_handler(&priv->restart_nb);
0246 
0247     return 0;
0248 }
0249 
0250 static struct platform_driver atc260x_pwrc_driver = {
0251     .probe = atc260x_pwrc_probe,
0252     .remove = atc260x_pwrc_remove,
0253     .driver = {
0254         .name = "atc260x-pwrc",
0255     },
0256 };
0257 
0258 module_platform_driver(atc260x_pwrc_driver);
0259 
0260 MODULE_DESCRIPTION("Poweroff & reset driver for ATC260x PMICs");
0261 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
0262 MODULE_LICENSE("GPL");