Merge tag 'ib-mfd-i2c-reboot-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorWolfram Sang <wsa@kernel.org>
Tue, 19 Sep 2023 14:56:46 +0000 (16:56 +0200)
committerWolfram Sang <wsa@kernel.org>
Tue, 19 Sep 2023 14:56:46 +0000 (16:56 +0200)
Immutable branch between MFD, I2C and Reboot due for the v6.7 merge window

drivers/i2c/i2c-core.h
drivers/mfd/tps6586x.c
include/linux/reboot.h
kernel/reboot.c

index 1247e6e6e97517afdd045c939cb7eee9bd0fd2bc..05b8b8dfa9bdd295a4415bc49d91b8bbf1f36dc6 100644 (file)
@@ -29,7 +29,7 @@ int i2c_dev_irq_from_resources(const struct resource *resources,
  */
 static inline bool i2c_in_atomic_xfer_mode(void)
 {
-       return system_state > SYSTEM_RUNNING && irqs_disabled();
+       return system_state > SYSTEM_RUNNING && !preemptible();
 }
 
 static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
index 55675ceedcd3b315598d5db29a6abd2046228c85..7e8160b947848db87a99fd4fa20ef8e2e22cb7d2 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/of.h>
 
@@ -29,6 +30,7 @@
 #include <linux/mfd/tps6586x.h>
 
 #define TPS6586X_SUPPLYENE     0x14
+#define SOFT_RST_BIT           BIT(0)
 #define EXITSLREQ_BIT          BIT(1)
 #define SLEEP_MODE_BIT         BIT(3)
 
@@ -457,13 +459,34 @@ static const struct regmap_config tps6586x_regmap_config = {
        .cache_type = REGCACHE_RBTREE,
 };
 
-static struct device *tps6586x_dev;
-static void tps6586x_power_off(void)
+static int tps6586x_power_off_handler(struct sys_off_data *data)
 {
-       if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT))
-               return;
+       int ret;
+
+       /* Put the PMIC into sleep state. This takes at least 20ms. */
+       ret = tps6586x_clr_bits(data->dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT);
+       if (ret)
+               return notifier_from_errno(ret);
+
+       ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
+       if (ret)
+               return notifier_from_errno(ret);
+
+       mdelay(50);
+       return notifier_from_errno(-ETIME);
+}
+
+static int tps6586x_restart_handler(struct sys_off_data *data)
+{
+       int ret;
+
+       /* Put the PMIC into hard reboot state. This takes at least 20ms. */
+       ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SOFT_RST_BIT);
+       if (ret)
+               return notifier_from_errno(ret);
 
-       tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
+       mdelay(50);
+       return notifier_from_errno(-ETIME);
 }
 
 static void tps6586x_print_version(struct i2c_client *client, int version)
@@ -559,9 +582,20 @@ static int tps6586x_i2c_probe(struct i2c_client *client)
                goto err_add_devs;
        }
 
-       if (pdata->pm_off && !pm_power_off) {
-               tps6586x_dev = &client->dev;
-               pm_power_off = tps6586x_power_off;
+       if (pdata->pm_off) {
+               ret = devm_register_power_off_handler(&client->dev, &tps6586x_power_off_handler,
+                                                     NULL);
+               if (ret) {
+                       dev_err(&client->dev, "register power off handler failed: %d\n", ret);
+                       goto err_add_devs;
+               }
+
+               ret = devm_register_restart_handler(&client->dev, &tps6586x_restart_handler,
+                                                   NULL);
+               if (ret) {
+                       dev_err(&client->dev, "register restart handler failed: %d\n", ret);
+                       goto err_add_devs;
+               }
        }
 
        return 0;
index 2b6bb593be5b655b3251506841b227813525454c..c4cc3b89ced1fbed2cfc903cde2ceb2a5e5ff9cb 100644 (file)
@@ -129,11 +129,14 @@ enum sys_off_mode {
  * @cb_data: User's callback data.
  * @cmd: Command string. Currently used only by the sys-off restart mode,
  *       NULL otherwise.
+ * @dev: Device of the sys-off handler. Only if known (devm_register_*),
+ *       NULL otherwise.
  */
 struct sys_off_data {
        int mode;
        void *cb_data;
        const char *cmd;
+       struct device *dev;
 };
 
 struct sys_off_handler *
index 3bba88c7ffc6be6b86ee69d1b73f64d7beb70542..395a0ea3c7a8a647d5cebb56dc56701c185b768e 100644 (file)
@@ -55,6 +55,7 @@ struct sys_off_handler {
        enum sys_off_mode mode;
        bool blocking;
        void *list;
+       struct device *dev;
 };
 
 /*
@@ -74,6 +75,7 @@ void __weak (*pm_power_off)(void);
 void emergency_restart(void)
 {
        kmsg_dump(KMSG_DUMP_EMERG);
+       system_state = SYSTEM_RESTART;
        machine_emergency_restart();
 }
 EXPORT_SYMBOL_GPL(emergency_restart);
@@ -323,6 +325,7 @@ static int sys_off_notify(struct notifier_block *nb,
        data.cb_data = handler->cb_data;
        data.mode = mode;
        data.cmd = cmd;
+       data.dev = handler->dev;
 
        return handler->sys_off_cb(&data);
 }
@@ -510,6 +513,7 @@ int devm_register_sys_off_handler(struct device *dev,
        handler = register_sys_off_handler(mode, priority, callback, cb_data);
        if (IS_ERR(handler))
                return PTR_ERR(handler);
+       handler->dev = dev;
 
        return devm_add_action_or_reset(dev, devm_unregister_sys_off_handler,
                                        handler);