pinctrl: samsung: Add infrastructure for pin-bank retention control
authorMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 26 Jan 2017 09:29:24 +0000 (10:29 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Thu, 26 Jan 2017 15:35:50 +0000 (16:35 +0100)
Pad retention control after suspend/resume cycle should be done from pin
controller driver instead of PMU (power management unit) driver to avoid
possible ordering and logical dependencies. Till now it worked fine only
because PMU driver registered its sys_ops after pin controller.

This patch adds infrastructure to handle pad retention during pin control
driver resume.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Tomasz Figa <tomasz.figa@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/samsung/pinctrl-samsung.c
drivers/pinctrl/samsung/pinctrl-samsung.h

index 59f99ea7e65bb117ea9746bba82032e65b2fc3eb..021abd7221f8bc20ca917949e3eb28966349e48f 100644 (file)
@@ -1060,6 +1060,13 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
        if (res)
                drvdata->irq = res->start;
 
+       if (ctrl->retention_data) {
+               drvdata->retention_ctrl = ctrl->retention_data->init(drvdata,
+                                                         ctrl->retention_data);
+               if (IS_ERR(drvdata->retention_ctrl))
+                       return PTR_ERR(drvdata->retention_ctrl);
+       }
+
        ret = samsung_gpiolib_register(pdev, drvdata);
        if (ret)
                return ret;
@@ -1126,6 +1133,8 @@ static void samsung_pinctrl_suspend_dev(
 
        if (drvdata->suspend)
                drvdata->suspend(drvdata);
+       if (drvdata->retention_ctrl && drvdata->retention_ctrl->enable)
+               drvdata->retention_ctrl->enable(drvdata);
 }
 
 /**
@@ -1173,6 +1182,9 @@ static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
                        if (widths[type])
                                writel(bank->pm_save[type], reg + offs[type]);
        }
+
+       if (drvdata->retention_ctrl && drvdata->retention_ctrl->disable)
+               drvdata->retention_ctrl->disable(drvdata);
 }
 
 /**
index 6f7ce7539a002765fb2a39f5dc979a9f5c5d9721..515a61035e54a5415b6a3d142319b59377b9bd88 100644 (file)
@@ -184,11 +184,49 @@ struct samsung_pin_bank {
        u32 pm_save[PINCFG_TYPE_NUM + 1]; /* +1 to handle double CON registers*/
 };
 
+/**
+ * struct samsung_retention_data: runtime pin-bank retention control data.
+ * @regs: array of PMU registers to control pad retention.
+ * @nr_regs: number of registers in @regs array.
+ * @value: value to store to registers to turn off retention.
+ * @refcnt: atomic counter if retention control affects more than one bank.
+ * @priv: retention control code private data
+ * @enable: platform specific callback to enter retention mode.
+ * @disable: platform specific callback to exit retention mode.
+ **/
+struct samsung_retention_ctrl {
+       const u32       *regs;
+       int             nr_regs;
+       u32             value;
+       atomic_t        *refcnt;
+       void            *priv;
+       void            (*enable)(struct samsung_pinctrl_drv_data *);
+       void            (*disable)(struct samsung_pinctrl_drv_data *);
+};
+
+/**
+ * struct samsung_retention_data: represent a pin-bank retention control data.
+ * @regs: array of PMU registers to control pad retention.
+ * @nr_regs: number of registers in @regs array.
+ * @value: value to store to registers to turn off retention.
+ * @refcnt: atomic counter if retention control affects more than one bank.
+ * @init: platform specific callback to initialize retention control.
+ **/
+struct samsung_retention_data {
+       const u32       *regs;
+       int             nr_regs;
+       u32             value;
+       atomic_t        *refcnt;
+       struct samsung_retention_ctrl *(*init)(struct samsung_pinctrl_drv_data *,
+                                       const struct samsung_retention_data *);
+};
+
 /**
  * struct samsung_pin_ctrl: represent a pin controller.
  * @pin_banks: list of pin banks included in this controller.
  * @nr_banks: number of pin banks.
  * @nr_ext_resources: number of the extra base address for pin banks.
+ * @retention_data: configuration data for retention control.
  * @eint_gpio_init: platform specific callback to setup the external gpio
  *     interrupts for the controller.
  * @eint_wkup_init: platform specific callback to setup the external wakeup
@@ -198,6 +236,7 @@ struct samsung_pin_ctrl {
        const struct samsung_pin_bank_data *pin_banks;
        u32             nr_banks;
        int             nr_ext_resources;
+       const struct samsung_retention_data *retention_data;
 
        int             (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
        int             (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
@@ -219,6 +258,7 @@ struct samsung_pin_ctrl {
  * @nr_function: number of such pin functions.
  * @pin_base: starting system wide pin number.
  * @nr_pins: number of pins supported by the controller.
+ * @retention_ctrl: retention control runtime data.
  */
 struct samsung_pinctrl_drv_data {
        struct list_head                node;
@@ -238,6 +278,8 @@ struct samsung_pinctrl_drv_data {
        unsigned int                    pin_base;
        unsigned int                    nr_pins;
 
+       struct samsung_retention_ctrl   *retention_ctrl;
+
        void (*suspend)(struct samsung_pinctrl_drv_data *);
        void (*resume)(struct samsung_pinctrl_drv_data *);
 };