On Tue, Oct 25, 2022 at 01:32:44PM -0500, Shenwei Wang wrote: >On i.MX8QM/QXP/DXL SoCs, even a GPIO is selected as the wakeup source, >the GPIO block will be powered off when system enters into suspend >state. This can greatly reduce the power consumption of suspend state >because the whole partition can be shutdown. This is called PAD wakeup >feature on i.MX8x platform. > >This patch adds the noirq suspend/resume hooks and uses the pad wakeup >feature as the default wakeup method for GPIO modules on >i.MX8QM/QXP/DXL platforms. > >Signed-off-by: Shenwei Wang <shenwei.wang@xxxxxxx> >--- > drivers/gpio/gpio-mxc.c | 91 ++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 90 insertions(+), 1 deletion(-) > >diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c >index c871602fc5ba..a6fe5ed3d320 100644 >--- a/drivers/gpio/gpio-mxc.c >+++ b/drivers/gpio/gpio-mxc.c >@@ -24,6 +24,12 @@ > #include <linux/of_device.h> > #include <linux/bug.h> > >+#define IMX_SCU_WAKEUP_OFF 0 >+#define IMX_SCU_WAKEUP_LOW_LVL 4 >+#define IMX_SCU_WAKEUP_FALL_EDGE 5 >+#define IMX_SCU_WAKEUP_RISE_EDGE 6 >+#define IMX_SCU_WAKEUP_HIGH_LVL 7 >+ > /* device type dependent stuff */ > struct mxc_gpio_hwdata { > unsigned dr_reg; >@@ -61,6 +67,9 @@ struct mxc_gpio_port { > u32 both_edges; > struct mxc_gpio_reg_saved gpio_saved_reg; > bool power_off; >+ u32 wakeup_pads; >+ bool is_pad_wakeup; >+ u32 pad_type[32]; > const struct mxc_gpio_hwdata *hwdata; > }; > >@@ -130,6 +139,9 @@ static const struct of_device_id mxc_gpio_dt_ids[] = { > { .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata }, > { .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata }, > { .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata }, >+ { .compatible = "fsl,imx8dxl-gpio", .data = &imx35_gpio_hwdata }, >+ { .compatible = "fsl,imx8qm-gpio", .data = &imx35_gpio_hwdata }, >+ { .compatible = "fsl,imx8qxp-gpio", .data = &imx35_gpio_hwdata }, > { /* sentinel */ } > }; > MODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids); >@@ -203,6 +215,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) > } > > writel(1 << gpio_idx, port->base + GPIO_ISR); >+ port->pad_type[gpio_idx] = type; > > return 0; > } >@@ -254,6 +267,9 @@ static void mx3_gpio_irq_handler(struct irq_desc *desc) > struct mxc_gpio_port *port = irq_desc_get_handler_data(desc); > struct irq_chip *chip = irq_desc_get_chip(desc); > >+ if (port->is_pad_wakeup) >+ return; >+ > chained_irq_enter(chip, desc); > > irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); >@@ -306,11 +322,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) > ret = enable_irq_wake(port->irq_high); > else > ret = enable_irq_wake(port->irq); >+ port->wakeup_pads |= (1<<gpio_idx); "1 << gpio_idx" > } else { > if (port->irq_high && (gpio_idx >= 16)) > ret = disable_irq_wake(port->irq_high); > else > ret = disable_irq_wake(port->irq); >+ port->wakeup_pads &= ~(1<<gpio_idx); "1 << gpio_idx" > } > > return ret; >@@ -365,7 +383,6 @@ static int mxc_gpio_probe(struct platform_device *pdev) > return -ENOMEM; > > port->dev = &pdev->dev; >- > port->hwdata = device_get_match_data(&pdev->dev); > > port->base = devm_platform_ioremap_resource(pdev, 0); >@@ -498,6 +515,77 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port) > writel(port->gpio_saved_reg.dr, port->base + GPIO_DR); > } > >+static int mxc_gpio_generic_config(struct mxc_gpio_port *port, >+ unsigned int offset, unsigned long config) >+{ >+ struct device_node *np = port->dev->of_node; >+ >+ if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") || >+ of_device_is_compatible(np, "fsl,imx8qxp-gpio") || >+ of_device_is_compatible(np, "fsl,imx8qm-gpio")) >+ return gpiochip_generic_config(&port->gc, offset, config); checkpatch should report warning. >+ >+ return 0; >+} >+ >+static void mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable) >+{ >+ unsigned long config; >+ int i, type; >+ >+ static const u32 pad_type_map[] = { >+ IMX_SCU_WAKEUP_OFF, /* 0 */ >+ IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */ >+ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */ >+ IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */ >+ IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */ >+ IMX_SCU_WAKEUP_OFF, /* 5 */ >+ IMX_SCU_WAKEUP_OFF, /* 6 */ >+ IMX_SCU_WAKEUP_OFF, /* 7 */ >+ IMX_SCU_WAKEUP_LOW_LVL, /* IRQ_TYPE_LEVEL_LOW */ >+ }; >+ >+ for (i = 0; i < 32; i++) { >+ if ((port->wakeup_pads & (1<<i))) { >+ type = port->pad_type[i]; >+ if (enable) >+ config = pad_type_map[type]; >+ else >+ config = IMX_SCU_WAKEUP_OFF; >+ mxc_gpio_generic_config(port, i, config); >+ } >+ } >+} >+ >+static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct mxc_gpio_port *port = platform_get_drvdata(pdev); >+ >+ if (port->wakeup_pads > 0) { >+ mxc_gpio_set_pad_wakeup(port, true); >+ port->is_pad_wakeup = true; >+ } >+ >+ return 0; >+} >+ >+static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct mxc_gpio_port *port = platform_get_drvdata(pdev); >+ >+ if (port->wakeup_pads > 0) >+ mxc_gpio_set_pad_wakeup(port, false); >+ port->is_pad_wakeup = false; >+ >+ return 0; >+} >+ >+static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { >+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) >+}; >+ > static int mxc_gpio_syscore_suspend(void) > { > struct mxc_gpio_port *port; >@@ -537,6 +625,7 @@ static struct platform_driver mxc_gpio_driver = { > .name = "gpio-mxc", > .of_match_table = mxc_gpio_dt_ids, > .suppress_bind_attrs = true, >+ .pm = &mxc_gpio_dev_pm_ops, > }, > .probe = mxc_gpio_probe, > }; Except the format issue, patch looks good to me. Regards, Peng. >-- >2.34.1 > --