commit 7f2268e6c9bf0a679afb08925facc27c8ecbcf57
Author: zhaoxc0502 <zhaoxc0502@thundersoft.com>
Date:   Thu Jun 16 17:19:42 2022 +0800

    linux_drivers_gpio
    
    Change-Id: I85874db4979e1d5dcfde13a3ecaeca8507d0ea43

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d1300fc00..e4ecf9d02 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -427,6 +427,12 @@ config GPIO_MXC
 	select GPIO_GENERIC
 	select GENERIC_IRQ_CHIP
 
+config GPIO_SCU
+	def_bool y
+	depends on IMX_SCU
+	help
+	  Say Y here to enable the imx8 gpio over SCFW MISC API
+
 config GPIO_MXS
 	bool "Freescale MXS GPIO support" if COMPILE_TEST
 	depends on ARCH_MXS || COMPILE_TEST
@@ -434,6 +440,13 @@ config GPIO_MXS
 	select GPIO_GENERIC
 	select GENERIC_IRQ_CHIP
 
+config GPIO_MXC_PAD_WAKEUP
+	def_bool y
+	depends on IMX_SCU
+	select GPIO_MXC
+	help
+	  Say Y here to enable the imx8 gpio pad wakeup
+
 config GPIO_OCTEON
 	tristate "Cavium OCTEON GPIO"
 	depends on CAVIUM_OCTEON_SOC
@@ -628,7 +641,7 @@ config GPIO_UNIPHIER
 
 config GPIO_VF610
 	def_bool y
-	depends on ARCH_MXC && SOC_VF610
+	depends on ARCH_MXC
 	select GPIOLIB_IRQCHIP
 	help
 	  Say yes here to support Vybrid vf610 GPIOs.
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 09dada80a..a8dd32998 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -129,6 +129,7 @@ obj-$(CONFIG_ARCH_SA1100)		+= gpio-sa1100.o
 obj-$(CONFIG_GPIO_SAMA5D2_PIOBU)	+= gpio-sama5d2-piobu.o
 obj-$(CONFIG_GPIO_SCH311X)		+= gpio-sch311x.o
 obj-$(CONFIG_GPIO_SCH)			+= gpio-sch.o
+obj-$(CONFIG_GPIO_SCU)			+= gpio-scu.o
 obj-$(CONFIG_GPIO_SIFIVE)		+= gpio-sifive.o
 obj-$(CONFIG_GPIO_SIOX)			+= gpio-siox.o
 obj-$(CONFIG_GPIO_SL28CPLD)		+= gpio-sl28cpld.o
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 238cbe926..4af7ea075 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -19,6 +19,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_data/max732x.h>
 #include <linux/of.h>
+#include <linux/reset.h>
 
 
 /*
@@ -76,6 +77,12 @@
 
 #define INT_CAPS(x)	(((uint64_t)(x)) << 32)
 
+enum {
+	OUTPUT_MASK,
+	OUTPUT_VAL,
+	OUTPUT_NUM,
+};
+
 enum {
 	MAX7319,
 	MAX7320,
@@ -622,6 +629,8 @@ static int max732x_probe(struct i2c_client *client,
 	struct i2c_client *c;
 	uint16_t addr_a, addr_b;
 	int ret, nr_port;
+	u16 out_set[OUTPUT_NUM];
+	unsigned long mask, val;
 
 	pdata = dev_get_platdata(&client->dev);
 	node = client->dev.of_node;
@@ -639,6 +648,10 @@ static int max732x_probe(struct i2c_client *client,
 		return -ENOMEM;
 	chip->client = client;
 
+	ret = device_reset(&client->dev);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
 	nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base);
 	chip->gpio_chip.parent = &client->dev;
 
@@ -711,6 +724,15 @@ static int max732x_probe(struct i2c_client *client,
 	}
 
 	i2c_set_clientdata(client, chip);
+
+	/* set the output IO default voltage */
+	if (!of_property_read_u16_array(node, "out-default", out_set,
+					ARRAY_SIZE(out_set))) {
+		mask = out_set[OUTPUT_MASK] & chip->dir_output;
+		val = out_set[OUTPUT_VAL];
+		max732x_gpio_set_multiple(&chip->gpio_chip, &mask, &val);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index d60d55207..4982e90d8 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -426,9 +426,20 @@ static int mpc8xxx_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static void mpc8xxx_shutdown(struct platform_device *pdev)
+{
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = platform_get_drvdata(pdev);
+
+	if (mpc8xxx_gc->irq) {
+		irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, NULL, NULL);
+		irq_domain_remove(mpc8xxx_gc->irq);
+	}
+}
+
 static struct platform_driver mpc8xxx_plat_driver = {
 	.probe		= mpc8xxx_probe,
 	.remove		= mpc8xxx_remove,
+	.shutdown	= mpc8xxx_shutdown,
 	.driver		= {
 		.name = "gpio-mpc8xxx",
 		.of_match_table	= mpc8xxx_gpio_ids,
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index ba6ed2a41..cd167ce3b 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -17,12 +17,21 @@
 #include <linux/irqchip/chained_irq.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/syscore_ops.h>
 #include <linux/gpio/driver.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/bug.h>
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+#include <linux/firmware/imx/sci.h>
+
+#define IMX_SC_PAD_FUNC_GET_WAKEUP	9
+#define IMX_SC_PAD_FUNC_SET_WAKEUP	4
+#define IMX_SC_PAD_WAKEUP_OFF		0
+#define IMX_SC_IRQ_PAD			(1 << 1)
+#endif
 
 enum mxc_gpio_hwtype {
 	IMX1_GPIO,	/* runs on i.mx1 */
@@ -31,6 +40,33 @@ enum mxc_gpio_hwtype {
 	IMX35_GPIO,	/* runs on all other i.mx */
 };
 
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+struct mxc_gpio_pad_wakeup {
+	u32 pin_id;
+	u32 type;
+	u32 line;
+};
+
+struct imx_sc_msg_gpio_get_pad_wakeup {
+	struct imx_sc_rpc_msg hdr;
+	union {
+		struct req_pad {
+			u16 pad;
+		} __packed req;
+		struct resp_wakeup {
+			u8 wakeup;
+		} resp;
+	} data;
+} __packed __aligned(4);
+
+struct imx_sc_msg_gpio_set_pad_wakeup {
+	struct imx_sc_rpc_msg hdr;
+	u16 pad;
+	u8 wakeup;
+} __packed __aligned(4);
+
+#endif
+
 /* device type dependent stuff */
 struct mxc_gpio_hwdata {
 	unsigned dr_reg;
@@ -68,8 +104,17 @@ struct mxc_gpio_port {
 	u32 both_edges;
 	struct mxc_gpio_reg_saved gpio_saved_reg;
 	bool power_off;
+	bool gpio_ranges;
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+	u32 pad_wakeup_num;
+	struct mxc_gpio_pad_wakeup pad_wakeup[32];
+#endif
 };
 
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+static struct imx_sc_ipc *gpio_ipc_handle;
+#endif
+
 static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
 	.dr_reg		= 0x1c,
 	.gdir_reg	= 0x00,
@@ -312,6 +357,85 @@ static void mx2_gpio_irq_handler(struct irq_desc *desc)
 	chained_irq_exit(chip, desc);
 }
 
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+static int mxc_gpio_get_pad_wakeup(struct mxc_gpio_port *port)
+{
+	struct imx_sc_msg_gpio_get_pad_wakeup msg;
+	struct imx_sc_rpc_msg *hdr = &msg.hdr;
+	u8 wakeup_type;
+	int ret;
+	int i;
+
+	hdr->ver = IMX_SC_RPC_VERSION;
+	hdr->svc = IMX_SC_RPC_SVC_PAD;
+	hdr->func = IMX_SC_PAD_FUNC_GET_WAKEUP;
+	hdr->size = 2;
+
+	for (i = 0; i < port->pad_wakeup_num; i++) {
+		/* get original pad type */
+		wakeup_type = port->pad_wakeup[i].type;
+		msg.data.req.pad = port->pad_wakeup[i].pin_id;
+		ret = imx_scu_call_rpc(gpio_ipc_handle, &msg, true);
+		if (ret) {
+			dev_err(port->gc.parent, "get pad wakeup failed, ret %d\n", ret);
+			return ret;
+		}
+		wakeup_type = msg.data.resp.wakeup;
+		/* return wakeup gpio pin's line */
+		if (wakeup_type != port->pad_wakeup[i].type)
+			return port->pad_wakeup[i].line;
+	}
+
+	return -EINVAL;
+}
+
+static void mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
+{
+	struct imx_sc_msg_gpio_set_pad_wakeup msg;
+	struct imx_sc_rpc_msg *hdr = &msg.hdr;
+	int ret;
+	int i;
+
+	hdr->ver = IMX_SC_RPC_VERSION;
+	hdr->svc = IMX_SC_RPC_SVC_PAD;
+	hdr->func = IMX_SC_PAD_FUNC_SET_WAKEUP;
+	hdr->size = 2;
+
+	for (i = 0; i < port->pad_wakeup_num; i++) {
+		msg.pad = port->pad_wakeup[i].pin_id;
+		msg.wakeup = enable ? port->pad_wakeup[i].type : IMX_SC_PAD_WAKEUP_OFF;
+		ret = imx_scu_call_rpc(gpio_ipc_handle, &msg, true);
+		if (ret) {
+			dev_err(port->gc.parent, "set pad wakeup failed, ret %d\n", ret);
+			return;
+		}
+	}
+}
+
+static void mxc_gpio_handle_pad_wakeup(struct mxc_gpio_port *port, int line)
+{
+	struct irq_desc *desc = irq_to_desc(port->irq);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	u32 irq_stat;
+
+	/* skip invalid line */
+	if (line > 31) {
+		dev_err(port->gc.parent, "invalid wakeup line %d\n", line);
+		return;
+	}
+
+	dev_info(port->gc.parent, "wakeup by pad, line %d\n", line);
+
+	chained_irq_enter(chip, desc);
+
+	irq_stat = (1 << line);
+
+	mxc_gpio_irq_handler(port, irq_stat);
+
+	chained_irq_exit(chip, desc);
+}
+#endif
+
 /*
  * Set interrupt number "irq" in the GPIO as a wake-up source.
  * While system is running, all registered GPIO interrupts need to have
@@ -343,7 +467,32 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
 	return ret;
 }
 
-static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
+static int mxc_gpio_irq_reqres(struct irq_data *d)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+	struct mxc_gpio_port *port = gc->private;
+
+	if (gpiochip_lock_as_irq(&port->gc, d->hwirq)) {
+		dev_err(port->gc.parent,
+			"unable to lock HW IRQ %lu for IRQ\n",
+			d->hwirq);
+		return -EINVAL;
+	}
+
+	return irq_chip_pm_get(d);
+}
+
+static void mxc_gpio_irq_relres(struct irq_data *d)
+{
+	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+	struct mxc_gpio_port *port = gc->private;
+
+	gpiochip_unlock_as_irq(&port->gc, d->hwirq);
+	irq_chip_pm_put(d);
+}
+
+static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base,
+			    struct device *dev)
 {
 	struct irq_chip_generic *gc;
 	struct irq_chip_type *ct;
@@ -356,11 +505,14 @@ static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
 	gc->private = port;
 
 	ct = gc->chip_types;
+	ct->chip.parent_device = dev;
 	ct->chip.irq_ack = irq_gc_ack_set_bit;
 	ct->chip.irq_mask = irq_gc_mask_clr_bit;
 	ct->chip.irq_unmask = irq_gc_mask_set_bit;
 	ct->chip.irq_set_type = gpio_set_irq_type;
 	ct->chip.irq_set_wake = gpio_set_wake_irq;
+	ct->chip.irq_request_resources = mxc_gpio_irq_reqres;
+	ct->chip.irq_release_resources = mxc_gpio_irq_relres,
 	ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
 	ct->regs.ack = GPIO_ISR;
 	ct->regs.mask = GPIO_IMR;
@@ -409,6 +561,30 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
 	return irq_find_mapping(port->domain, offset);
 }
 
+static int mxc_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct mxc_gpio_port *port = gpiochip_get_data(chip);
+	int ret;
+
+	if (port->gpio_ranges) {
+		ret = gpiochip_generic_request(chip, offset);
+		if (ret)
+			return ret;
+	}
+
+	ret = pm_runtime_get_sync(chip->parent);
+	return ret < 0 ? ret : 0;
+}
+
+static void mxc_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	struct mxc_gpio_port *port = gpiochip_get_data(chip);
+
+	if (port->gpio_ranges)
+		gpiochip_generic_free(chip, offset);
+	pm_runtime_put(chip->parent);
+}
+
 static int mxc_gpio_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -416,6 +592,9 @@ static int mxc_gpio_probe(struct platform_device *pdev)
 	int irq_count;
 	int irq_base;
 	int err;
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+	int i;
+#endif
 
 	mxc_gpio_get_hw(pdev);
 
@@ -434,7 +613,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
 		return irq_count;
 
 	if (irq_count > 1) {
-		port->irq_high = platform_get_irq(pdev, 1);
+		port->irq_high = platform_get_irq_optional(pdev, 1);
 		if (port->irq_high < 0)
 			port->irq_high = 0;
 	}
@@ -454,9 +633,44 @@ static int mxc_gpio_probe(struct platform_device *pdev)
 		return err;
 	}
 
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+	/*
+	 * parse pad wakeup info from dtb, each pad has to provide
+	 * <pin_id, type, line>, these info should be put in each
+	 * gpio node and with a "pad-wakeup-num" to indicate the
+	 * total lines are with pad wakeup enabled.
+	 */
+	if (!of_property_read_u32(np, "pad-wakeup-num", &port->pad_wakeup_num)) {
+		if (port->pad_wakeup_num != 0) {
+			if (!gpio_ipc_handle) {
+				err = imx_scu_get_handle(&gpio_ipc_handle);
+				if (err)
+					return err;
+			}
+			for (i = 0; i < port->pad_wakeup_num; i++) {
+				of_property_read_u32_index(np, "pad-wakeup",
+					i * 3 + 0, &port->pad_wakeup[i].pin_id);
+				of_property_read_u32_index(np, "pad-wakeup",
+					i * 3 + 1, &port->pad_wakeup[i].type);
+				of_property_read_u32_index(np, "pad-wakeup",
+					i * 3 + 2, &port->pad_wakeup[i].line);
+			}
+			err = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, IMX_SC_IRQ_PAD, true);
+			if (err)
+				dev_warn(&pdev->dev, "Enable irq failed, GPIO pad wakeup NOT supported\n");
+		}
+	}
+#endif
+
 	if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
 		port->power_off = true;
 
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	err = pm_runtime_get_sync(&pdev->dev);
+	if (err < 0)
+		goto out_pm_dis;
+
 	/* disable the interrupt and clear the status */
 	writel(0, port->base + GPIO_IMR);
 	writel(~0, port->base + GPIO_ISR);
@@ -487,8 +701,14 @@ static int mxc_gpio_probe(struct platform_device *pdev)
 	if (err)
 		goto out_bgio;
 
-	port->gc.request = gpiochip_generic_request;
-	port->gc.free = gpiochip_generic_free;
+	if (of_property_read_bool(np, "gpio_ranges"))
+		port->gpio_ranges = true;
+	else
+		port->gpio_ranges = false;
+
+	port->gc.request = mxc_gpio_request;
+	port->gc.free = mxc_gpio_free;
+	port->gc.parent = &pdev->dev;
 	port->gc.to_irq = mxc_gpio_to_irq;
 	port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
 					     pdev->id * 32;
@@ -511,16 +731,20 @@ static int mxc_gpio_probe(struct platform_device *pdev)
 	}
 
 	/* gpio-mxc can be a generic irq chip */
-	err = mxc_gpio_init_gc(port, irq_base);
+	err = mxc_gpio_init_gc(port, irq_base, &pdev->dev);
 	if (err < 0)
 		goto out_irqdomain_remove;
 
 	list_add_tail(&port->node, &mxc_gpio_ports);
 
 	platform_set_drvdata(pdev, port);
+	pm_runtime_put(&pdev->dev);
 
 	return 0;
 
+out_pm_dis:
+	pm_runtime_disable(&pdev->dev);
+	clk_disable_unprepare(port->clk);
 out_irqdomain_remove:
 	irq_domain_remove(port->domain);
 out_bgio:
@@ -555,12 +779,73 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
 	writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
 }
 
+static int __maybe_unused mxc_gpio_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+	mxc_gpio_save_regs(port);
+	clk_disable_unprepare(port->clk);
+
+	return 0;
+}
+
+static int __maybe_unused mxc_gpio_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = clk_prepare_enable(port->clk);
+	if (ret)
+		return ret;
+
+	mxc_gpio_restore_regs(port);
+
+	return 0;
+}
+
+static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
+{
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+	mxc_gpio_set_pad_wakeup(port, true);
+#endif
+	return 0;
+}
+
+static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
+{
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+	int wakeup_line = mxc_gpio_get_pad_wakeup(port);
+
+	mxc_gpio_set_pad_wakeup(port, false);
+
+	if (wakeup_line >= 0)
+		mxc_gpio_handle_pad_wakeup(port, wakeup_line);
+#endif
+	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)
+	SET_RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
+};
+
 static int mxc_gpio_syscore_suspend(void)
 {
 	struct mxc_gpio_port *port;
+	int ret;
 
 	/* walk through all ports */
 	list_for_each_entry(port, &mxc_gpio_ports, node) {
+		ret = clk_prepare_enable(port->clk);
+		if (ret)
+			return ret;
 		mxc_gpio_save_regs(port);
 		clk_disable_unprepare(port->clk);
 	}
@@ -581,6 +866,7 @@ static void mxc_gpio_syscore_resume(void)
 			return;
 		}
 		mxc_gpio_restore_regs(port);
+		clk_disable_unprepare(port->clk);
 	}
 }
 
@@ -594,6 +880,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,
 	.id_table	= mxc_gpio_devtype,
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index a78167b2c..33d3a0bfe 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -20,6 +20,7 @@
 #include <linux/platform_data/pca953x.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 
 #include <asm/unaligned.h>
@@ -981,14 +982,19 @@ static int pca953x_probe(struct i2c_client *client,
 
 	chip->client = client;
 
-	reg = devm_regulator_get(&client->dev, "vcc");
-	if (IS_ERR(reg))
-		return dev_err_probe(&client->dev, PTR_ERR(reg), "reg get err\n");
+	reg = devm_regulator_get_optional(&client->dev, "vcc");
+	if (IS_ERR(reg)) {
+		if (PTR_ERR(reg) != -ENODEV)
+			return dev_err_probe(&client->dev, PTR_ERR(reg), "reg get err\n");
+		reg = NULL;
+	}
 
-	ret = regulator_enable(reg);
-	if (ret) {
-		dev_err(&client->dev, "reg en err: %d\n", ret);
-		return ret;
+	if (reg) {
+		ret = regulator_enable(reg);
+		if (ret) {
+			dev_err(&client->dev, "reg en err: %d\n", ret);
+			return ret;
+		}
 	}
 	chip->regulator = reg;
 
@@ -1046,6 +1052,10 @@ static int pca953x_probe(struct i2c_client *client,
 	lockdep_set_subclass(&chip->i2c_lock,
 			     i2c_adapter_depth(client->adapter));
 
+	ret = device_reset(&client->dev);
+	if (ret == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
 	/* initialize cached registers from their original values.
 	 * we can't share this chip with another i2c master.
 	 */
@@ -1078,7 +1088,8 @@ static int pca953x_probe(struct i2c_client *client,
 	return 0;
 
 err_exit:
-	regulator_disable(chip->regulator);
+	if (chip->regulator)
+		regulator_disable(chip->regulator);
 	return ret;
 }
 
@@ -1097,7 +1108,8 @@ static int pca953x_remove(struct i2c_client *client)
 		ret = 0;
 	}
 
-	regulator_disable(chip->regulator);
+	if (chip->regulator)
+		regulator_disable(chip->regulator);
 
 	return ret;
 }
@@ -1158,7 +1170,8 @@ static int pca953x_suspend(struct device *dev)
 	if (atomic_read(&chip->wakeup_path))
 		device_set_wakeup_path(dev);
 	else
-		regulator_disable(chip->regulator);
+		if (chip->regulator)
+			regulator_disable(chip->regulator);
 
 	return 0;
 }
@@ -1168,15 +1181,20 @@ static int pca953x_resume(struct device *dev)
 	struct pca953x_chip *chip = dev_get_drvdata(dev);
 	int ret;
 
+	regcache_cache_only(chip->regmap, false);
+
 	if (!atomic_read(&chip->wakeup_path)) {
-		ret = regulator_enable(chip->regulator);
-		if (ret) {
-			dev_err(dev, "Failed to enable regulator: %d\n", ret);
+		if (chip->regulator) {
+			ret = regulator_enable(chip->regulator);
+			if (ret) {
+				dev_err(dev, "Failed to enable regulator: %d\n", ret);
+				return 0;
+			}
+		} else {
 			return 0;
 		}
 	}
 
-	regcache_cache_only(chip->regmap, false);
 	regcache_mark_dirty(chip->regmap);
 	ret = pca953x_regcache_sync(dev);
 	if (ret)
diff --git a/drivers/gpio/gpio-scu.c b/drivers/gpio/gpio-scu.c
new file mode 100644
index 000000000..5fb2d8b72
--- /dev/null
+++ b/drivers/gpio/gpio-scu.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021 NXP
+ *
+ * The driver exports a standard gpiochip interface
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/imx/svc/rm.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+#define PIN_NUMBER 8
+
+struct imxscfw {
+	struct mutex	lock;
+	struct imx_sc_ipc *handle;
+	struct gpio_chip chip;
+	struct device *dev;
+};
+
+static unsigned int sc_arr[] = {
+	IMX_SC_R_BOARD_R0,
+	IMX_SC_R_BOARD_R1,
+	IMX_SC_R_BOARD_R2,
+	IMX_SC_R_BOARD_R3,
+	IMX_SC_R_BOARD_R4,
+	IMX_SC_R_BOARD_R5,
+	IMX_SC_R_BOARD_R6,  //R6 is MII select
+	IMX_SC_R_BOARD_R7,
+};
+
+static int imxscfw_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct imxscfw *scu = gpiochip_get_data(chip);
+	int err = -EINVAL, level = 0;
+
+	if (offset >= sizeof(sc_arr)/sizeof(unsigned int))
+		return err;
+
+	mutex_lock(&scu->lock);
+
+	/* to read PIN state via scu api */
+	err = imx_sc_misc_get_control(scu->handle, sc_arr[offset],
+				      0, &level);
+	mutex_unlock(&scu->lock);
+
+	if (err) {
+		pr_err("%s: failed %d\n", __func__, err);
+		return -EINVAL;
+	}
+
+	return level;
+}
+
+static void imxscfw_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+	struct imxscfw *scu = gpiochip_get_data(chip);
+	int err;
+
+	if (offset >= sizeof(sc_arr)/sizeof(unsigned int))
+		return;
+
+	mutex_lock(&scu->lock);
+
+	/* to set PIN output level via scu api */
+	err = imx_sc_misc_set_control(scu->handle, sc_arr[offset], 0, value);
+
+	mutex_unlock(&scu->lock);
+
+	if (err)
+		pr_err("%s: failed %d\n", __func__, err);
+
+
+}
+
+static int imx_scu_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct imxscfw *port;
+	struct gpio_chip *gc;
+	int ret;
+
+	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	ret = imx_scu_get_handle(&port->handle);
+	if (ret)
+		return ret;
+
+	mutex_init(&port->lock);
+	gc = &port->chip;
+	gc->of_node = np;
+	gc->parent = dev;
+	gc->label = "imx-scu-gpio";
+	gc->ngpio = PIN_NUMBER;
+	gc->base = of_alias_get_id(np, "gpio") * 32;
+
+	gc->get = imxscfw_get;
+	gc->set = imxscfw_set;
+
+	platform_set_drvdata(pdev, port);
+
+	ret = devm_gpiochip_add_data(dev, gc, port);
+
+	return ret;
+}
+
+static const struct of_device_id imx_scu_gpio_dt_ids[] = {
+	{ .compatible = "fsl,imx-scu-gpio" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver imx_scu_gpio_driver = {
+	.driver	= {
+		.name = "gpio-imx-scu",
+		.of_match_table = imx_scu_gpio_dt_ids,
+	},
+	.probe = imx_scu_gpio_probe,
+};
+
+static int __init _imx_scu_gpio_init(void)
+{
+	return platform_driver_register(&imx_scu_gpio_driver);
+}
+
+subsys_initcall_sync(_imx_scu_gpio_init);
+
+MODULE_AUTHOR("Shenwei Wang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NXP GPIO over SCU-MISC API, i.MX8");
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index 58776f2d6..b99f4968b 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -300,7 +300,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
 	gc = &port->gc;
 	gc->of_node = np;
 	gc->parent = dev;
-	gc->label = "vf610-gpio";
+	gc->label = dev_name(dev);
 	gc->ngpio = VF610_GPIO_PER_PORT;
 	gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
 
