reset: max77620: add support to configure GPIO before power reset
Laxman Dewangan [Mon, 18 May 2015 15:56:08 +0000 (20:56 +0530)]
Add support on MAX77620 power off driver to configure
device's GPIOs to desired state just before system power
OFF or reset. The GPIOs and desired state will be provided
through DT. If desired property is not available then it
will not configure the GPIOs.

bug 1643199

Change-Id: Ibe5d7417e3f98a47f8e56c1ecb8805fc2e371e4d
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: http://git-master/r/744404
Reviewed-on: http://git-master/r/744892

Documentation/devicetree/bindings/mfd/max77620.txt
drivers/power/reset/max77620-poweroff.c

index 47b5018..40030f1 100644 (file)
@@ -13,6 +13,24 @@ Required properties:
   The second cell is the flags, encoded as the trigger masks from binding document
        interrupts.txt, using dt-bindings/irq.
 
+This device also support the power OFF and power reset of system.
+Following properties are used for this purpose:
+- maxim,system-pmic-power-off: Boolean, This device will be used for system
+       power OFF. Host will issue i2c command to PMIC to power OFF system.
+- maxim,system-pmic-power-reset: Boolean, This device will used for system
+       power reset. Host will issue i2c command to PMIC to reset system.
+- maxim,system-power-controller: Boolean, This device will be use as
+       system power controller and used for power OFF and power reset
+       of system. Host issue necessary command to PMIC.
+- maxim,power-reset-gpio-states: Two dimensional array type, This property
+       provides the option to set the PMIC GPIOs on desired state just
+       before power OFF or power reset of system. Values are provided as
+       follows:
+               maxim,power-reset-gpio-states = <6 0
+                                               7 1>;
+               Means GPIO6 will set as LOW and GPIO7 will set as HIGH
+               just before power OFF and reset of system.
+
 Optional properties:
 -------------------
 
index 5ddad03..ccd8c5b 100644 (file)
 
 #include <linux/errno.h>
 #include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/power/reset/system-pmic.h>
+#include <linux/reboot.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/mfd/max77620.h>
 #define MAX77620_PM_RTC_RB_UPDATE_MASK         (1 << 4)
 #define MAX77620_PM_RTC_INT1_MASK                      (1 << 1)
 
+#define GPIO_REG_ADDR(offset) (MAX77620_REG_GPIO0 + offset)
+#define MAX77620_MAX_GPIO      8
+#define GPIO_VAL_TO_STATE(g, v) ((g << 8) | v)
+#define GPIO_STATE_TO_GPIO(gv) (gv >> 8)
+#define GPIO_STATE_TO_VAL(gv) (gv & 0xFF)
+
 struct max77620_poweroff {
        struct device *dev;
        struct max77620_chip *max77620;
        struct system_pmic_dev *system_pmic_dev;
+       struct notifier_block reset_nb;
+       int gpio_state[MAX77620_MAX_GPIO];
+       int ngpio_states;
        bool use_power_off;
        bool use_power_reset;
        bool need_rtc_power_on;
 };
 
+static int max77620_turn_off_gpio(
+               struct max77620_poweroff *max77620_poweroff,
+               int gpio, int value)
+{
+       struct device *dev = max77620_poweroff->dev;
+       struct device *parent = max77620_poweroff->max77620->dev;
+       int val;
+       int ret;
+
+       dev_info(dev, "Turning off GPIO %d\n", gpio);
+
+       val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :
+                               MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+       ret = max77620_reg_update(parent, MAX77620_PWR_SLAVE,
+                       GPIO_REG_ADDR(gpio),
+                       MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+       if (ret < 0) {
+               dev_err(dev, "CNFG_GPIOx val update failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = max77620_reg_update(parent, MAX77620_PWR_SLAVE,
+               GPIO_REG_ADDR(gpio), MAX77620_CNFG_GPIO_DIR_MASK,
+                               MAX77620_CNFG_GPIO_DIR_OUTPUT);
+       if (ret < 0)
+               dev_err(dev, "CNFG_GPIOx dir update failed: %d\n", ret);
+       return ret;
+}
+
 static void max77620_auto_power_on(struct max77620_poweroff *max77620_poweroff)
 {
        int ret;
@@ -127,6 +168,32 @@ static int max77620_configure_power_on(void *drv_data,
        return 0;
 }
 
+static void _max77620_prepare_system_power_off(
+               struct max77620_poweroff *max77620_poweroff)
+{
+       int i;
+       int gpio;
+       int val;
+
+       if (!max77620_poweroff->ngpio_states)
+               return;
+
+       dev_info(max77620_poweroff->dev, "Preparing power-off from PMIC\n");
+
+       for (i = 0; i < max77620_poweroff->ngpio_states; ++i) {
+               gpio = GPIO_STATE_TO_GPIO(max77620_poweroff->gpio_state[i]);
+               val = GPIO_STATE_TO_VAL(max77620_poweroff->gpio_state[i]);
+               max77620_turn_off_gpio(max77620_poweroff, gpio, val);
+       }
+}
+
+static void max77620_prepare_system_power_off(
+               struct max77620_poweroff *max77620_poff)
+{
+       max77620_allow_atomic_xfer(max77620_poff->max77620);
+       _max77620_prepare_system_power_off(max77620_poff);
+}
+
 static void max77620_pm_power_off(void *drv_data)
 {
        struct max77620_poweroff *max77620_poweroff = drv_data;
@@ -164,6 +231,8 @@ static void max77620_pm_power_off(void *drv_data)
                        "Interrupt status reg 0x%x read failed: %d\n",
                        MAX77620_REG_IRQTOP, ret);
 
+       _max77620_prepare_system_power_off(max77620_poweroff);
+
        if (max77620_poweroff->need_rtc_power_on)
                max77620_auto_power_on(max77620_poweroff);
 
@@ -189,6 +258,8 @@ static void max77620_pm_power_reset(void *drv_data)
                MAX77620_PWR_SLAVE, MAX77620_REG_ONOFFCNFG2,
                MAX77620_ONOFFCNFG2_SFT_RST_WK, MAX77620_ONOFFCNFG2_SFT_RST_WK);
 
+       _max77620_prepare_system_power_off(max77620_poweroff);
+
        ret = max77620_reg_update(max77620_poweroff->max77620->dev,
                MAX77620_PWR_SLAVE, MAX77620_REG_ONOFFCNFG1,
                MAX77620_ONOFFCNFG1_SFT_RST, MAX77620_ONOFFCNFG1_SFT_RST);
@@ -197,6 +268,17 @@ static void max77620_pm_power_reset(void *drv_data)
                        "REG_ONOFFCNFG1 update failed, %d\n", ret);
 }
 
+static int max77620_restart_notify(struct notifier_block *nb,
+                           unsigned long action, void *data)
+{
+       struct max77620_poweroff *max77620_poweroff;
+
+       max77620_poweroff = container_of(nb, struct max77620_poweroff,
+                                       reset_nb);
+       max77620_prepare_system_power_off(max77620_poweroff);
+       return NOTIFY_OK;
+};
+
 static struct system_pmic_ops max77620_pm_ops = {
        .power_off = max77620_pm_power_off,
        .power_reset = max77620_pm_power_reset,
@@ -212,6 +294,7 @@ static int max77620_poweroff_probe(struct platform_device *pdev)
        bool use_power_off = false;
        bool use_power_reset = false;
        u8 poweroff_event_recorder;
+       int count;
        int ret;
 
        if (np) {
@@ -240,6 +323,29 @@ static int max77620_poweroff_probe(struct platform_device *pdev)
        if (!max77620_poweroff)
                return -ENOMEM;
 
+       if (!np)
+               goto gpio_done;
+
+       count = of_property_count_u32(np, "maxim,power-reset-gpio-states");
+       if (count % 2) {
+               dev_warn(&pdev->dev, "Not able to parse reset-gpio-states\n");
+               goto gpio_done;
+       }
+       max77620_poweroff->ngpio_states = count / 2;
+       for (count = 0; count < max77620_poweroff->ngpio_states; ++count) {
+               u32 gpio = 0;
+               u32 val = 0;
+               int index = count * 2;
+
+               of_property_read_u32_index(np, "maxim,power-reset-gpio-states",
+                               index, &gpio);
+               of_property_read_u32_index(np, "maxim,power-reset-gpio-states",
+                               index + 1, &val);
+               max77620_poweroff->gpio_state[count] =
+                                       GPIO_VAL_TO_STATE(gpio, val);
+       }
+
+gpio_done:
        max77620_poweroff->max77620 = max77620;
        max77620_poweroff->dev = &pdev->dev;
        max77620_poweroff->use_power_off = use_power_off;
@@ -269,6 +375,14 @@ static int max77620_poweroff_probe(struct platform_device *pdev)
                dev_info(&pdev->dev, "Event recorder REG_NVERC : 0x%x\n",
                                poweroff_event_recorder);
 
+       max77620_poweroff->reset_nb.notifier_call = max77620_restart_notify;
+       max77620_poweroff->reset_nb.priority = 200;
+       ret = register_restart_handler(&max77620_poweroff->reset_nb);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "restart handler registration failed\n");
+               return ret;
+       }
+
        return 0;
 }