]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
pinctrl: realtek: Support system suspend and resume
authorTzuyi Chang <tychang@realtek.com>
Fri, 6 Mar 2026 07:52:36 +0000 (15:52 +0800)
committerLinus Walleij <linusw@kernel.org>
Tue, 10 Mar 2026 09:26:37 +0000 (10:26 +0100)
Add system suspend and resume capabilities to the common Realtek pinctrl
library. This enables saving and restoring pin configurations during the
noirq phase for SoCs that define register ranges.

Signed-off-by: Tzuyi Chang <tychang@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
drivers/pinctrl/realtek/pinctrl-rtd.c
drivers/pinctrl/realtek/pinctrl-rtd.h

index 5888a36babba40eb013619fe27c8dff32bd81a53..60dfb39bc986dd0c58cfa725dfe3df525e8b098a 100644 (file)
@@ -30,6 +30,7 @@ struct rtd_pinctrl {
        struct pinctrl_desc desc;
        const struct rtd_pinctrl_desc *info;
        struct regmap *regmap_pinctrl;
+       u32 **saved_regs;
 };
 
 /* custom pinconf parameters */
@@ -540,6 +541,30 @@ static const struct regmap_config rtd_pinctrl_regmap_config = {
        .use_relaxed_mmio = true,
 };
 
+static int rtd_pinctrl_init_pm(struct rtd_pinctrl *data)
+{
+       const struct rtd_pin_range *pin_range = data->info->pin_range;
+       struct device *dev = data->pcdev->dev;
+       const struct rtd_reg_range *range;
+       size_t num_entries;
+       int i;
+
+       data->saved_regs = devm_kcalloc(dev, pin_range->num_ranges, sizeof(u32 *), GFP_KERNEL);
+       if (!data->saved_regs)
+               return -ENOMEM;
+
+       for (i = 0; i < pin_range->num_ranges; i++) {
+               range = &pin_range->ranges[i];
+               num_entries = range->len / 4;
+
+               data->saved_regs[i] = devm_kzalloc(dev, num_entries * sizeof(u32), GFP_KERNEL);
+               if (!data->saved_regs[i])
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
 int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc)
 {
        struct rtd_pinctrl *data;
@@ -579,9 +604,82 @@ int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_des
 
        dev_dbg(&pdev->dev, "probed\n");
 
+       if (data->info->pin_range) {
+               if (rtd_pinctrl_init_pm(data))
+                       return -ENOMEM;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(rtd_pinctrl_probe);
 
+static int realtek_pinctrl_suspend(struct device *dev)
+{
+       struct rtd_pinctrl *data = dev_get_drvdata(dev);
+       const struct rtd_pin_range *pin_range = data->info->pin_range;
+       const struct rtd_reg_range *range;
+       u32 *range_regs;
+       int count;
+       int i, j;
+       int ret;
+
+       if (!data->saved_regs)
+               return 0;
+
+       for (i = 0; i < pin_range->num_ranges; i++) {
+               range = &pin_range->ranges[i];
+               range_regs = data->saved_regs[i];
+               count = range->len / 4;
+
+               for (j = 0; j < count; j++) {
+                       ret = regmap_read(data->regmap_pinctrl, range->offset + (j * 4),
+                                         &range_regs[j]);
+                       if (ret) {
+                               dev_err(dev, "failed to store register 0x%x: %d\n",
+                                       range->offset + (j * 4), ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int realtek_pinctrl_resume(struct device *dev)
+{
+       struct rtd_pinctrl *data = dev_get_drvdata(dev);
+       const struct rtd_pin_range *pin_range = data->info->pin_range;
+       const struct rtd_reg_range *range;
+       u32 *range_regs;
+       int count;
+       int i, j;
+       int ret;
+
+       if (!data->saved_regs)
+               return 0;
+
+       for (i = 0; i < pin_range->num_ranges; i++) {
+               range = &pin_range->ranges[i];
+               range_regs = data->saved_regs[i];
+               count = range->len / 4;
+
+               for (j = 0; j < count; j++) {
+                       ret = regmap_write(data->regmap_pinctrl, range->offset + (j * 4),
+                                          range_regs[j]);
+                       if (ret) {
+                               dev_err(dev, "failed to restore register 0x%x: %d\n",
+                                       range->offset + (j * 4), ret);
+                               return ret;
+                       }
+               }
+       }
+       return 0;
+}
+
+const struct dev_pm_ops realtek_pinctrl_pm_ops = {
+       NOIRQ_SYSTEM_SLEEP_PM_OPS(realtek_pinctrl_suspend, realtek_pinctrl_resume)
+};
+EXPORT_SYMBOL_GPL(realtek_pinctrl_pm_ops);
+
 MODULE_DESCRIPTION("Realtek DHC SoC pinctrl driver");
 MODULE_LICENSE("GPL");
index e15130896abc0f79377a51d8a7b8d66afc285185..7fb0955ce7491077de735eb2dc461f744b67e574 100644 (file)
@@ -47,6 +47,16 @@ struct rtd_pin_sconfig_desc {
        unsigned int pdrive_maskbits;
 };
 
+struct rtd_reg_range {
+       unsigned int offset;
+       size_t len;
+};
+
+struct rtd_pin_range {
+       const struct rtd_reg_range *ranges;
+       const int num_ranges;
+};
+
 struct rtd_pin_desc {
        const char *name;
        unsigned int mux_offset;
@@ -119,6 +129,9 @@ struct rtd_pinctrl_desc {
        unsigned int num_sconfigs;
        struct rtd_pin_reg_list *lists;
        unsigned int num_regs;
+       const struct rtd_pin_range *pin_range;
 };
 
 int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc);
+
+extern const struct dev_pm_ops realtek_pinctrl_pm_ops;