162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
462306a36Sopenharmony_ci// Copyright 2008 Juergen Beisert, kernel@pengutronix.de
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Based on code from Freescale Semiconductor,
762306a36Sopenharmony_ci// Authors: Daniel Mack, Juergen Beisert.
862306a36Sopenharmony_ci// Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/irqdomain.h>
1762306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/spinlock.h>
2362306a36Sopenharmony_ci#include <linux/syscore_ops.h>
2462306a36Sopenharmony_ci#include <linux/gpio/driver.h>
2562306a36Sopenharmony_ci#include <linux/of.h>
2662306a36Sopenharmony_ci#include <linux/bug.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define IMX_SCU_WAKEUP_OFF		0
2962306a36Sopenharmony_ci#define IMX_SCU_WAKEUP_LOW_LVL		4
3062306a36Sopenharmony_ci#define IMX_SCU_WAKEUP_FALL_EDGE	5
3162306a36Sopenharmony_ci#define IMX_SCU_WAKEUP_RISE_EDGE	6
3262306a36Sopenharmony_ci#define IMX_SCU_WAKEUP_HIGH_LVL		7
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* device type dependent stuff */
3562306a36Sopenharmony_cistruct mxc_gpio_hwdata {
3662306a36Sopenharmony_ci	unsigned dr_reg;
3762306a36Sopenharmony_ci	unsigned gdir_reg;
3862306a36Sopenharmony_ci	unsigned psr_reg;
3962306a36Sopenharmony_ci	unsigned icr1_reg;
4062306a36Sopenharmony_ci	unsigned icr2_reg;
4162306a36Sopenharmony_ci	unsigned imr_reg;
4262306a36Sopenharmony_ci	unsigned isr_reg;
4362306a36Sopenharmony_ci	int edge_sel_reg;
4462306a36Sopenharmony_ci	unsigned low_level;
4562306a36Sopenharmony_ci	unsigned high_level;
4662306a36Sopenharmony_ci	unsigned rise_edge;
4762306a36Sopenharmony_ci	unsigned fall_edge;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct mxc_gpio_reg_saved {
5162306a36Sopenharmony_ci	u32 icr1;
5262306a36Sopenharmony_ci	u32 icr2;
5362306a36Sopenharmony_ci	u32 imr;
5462306a36Sopenharmony_ci	u32 gdir;
5562306a36Sopenharmony_ci	u32 edge_sel;
5662306a36Sopenharmony_ci	u32 dr;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct mxc_gpio_port {
6062306a36Sopenharmony_ci	struct list_head node;
6162306a36Sopenharmony_ci	void __iomem *base;
6262306a36Sopenharmony_ci	struct clk *clk;
6362306a36Sopenharmony_ci	int irq;
6462306a36Sopenharmony_ci	int irq_high;
6562306a36Sopenharmony_ci	void (*mx_irq_handler)(struct irq_desc *desc);
6662306a36Sopenharmony_ci	struct irq_domain *domain;
6762306a36Sopenharmony_ci	struct gpio_chip gc;
6862306a36Sopenharmony_ci	struct device *dev;
6962306a36Sopenharmony_ci	u32 both_edges;
7062306a36Sopenharmony_ci	struct mxc_gpio_reg_saved gpio_saved_reg;
7162306a36Sopenharmony_ci	bool power_off;
7262306a36Sopenharmony_ci	u32 wakeup_pads;
7362306a36Sopenharmony_ci	bool is_pad_wakeup;
7462306a36Sopenharmony_ci	u32 pad_type[32];
7562306a36Sopenharmony_ci	const struct mxc_gpio_hwdata *hwdata;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
7962306a36Sopenharmony_ci	.dr_reg		= 0x1c,
8062306a36Sopenharmony_ci	.gdir_reg	= 0x00,
8162306a36Sopenharmony_ci	.psr_reg	= 0x24,
8262306a36Sopenharmony_ci	.icr1_reg	= 0x28,
8362306a36Sopenharmony_ci	.icr2_reg	= 0x2c,
8462306a36Sopenharmony_ci	.imr_reg	= 0x30,
8562306a36Sopenharmony_ci	.isr_reg	= 0x34,
8662306a36Sopenharmony_ci	.edge_sel_reg	= -EINVAL,
8762306a36Sopenharmony_ci	.low_level	= 0x03,
8862306a36Sopenharmony_ci	.high_level	= 0x02,
8962306a36Sopenharmony_ci	.rise_edge	= 0x00,
9062306a36Sopenharmony_ci	.fall_edge	= 0x01,
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct mxc_gpio_hwdata imx31_gpio_hwdata = {
9462306a36Sopenharmony_ci	.dr_reg		= 0x00,
9562306a36Sopenharmony_ci	.gdir_reg	= 0x04,
9662306a36Sopenharmony_ci	.psr_reg	= 0x08,
9762306a36Sopenharmony_ci	.icr1_reg	= 0x0c,
9862306a36Sopenharmony_ci	.icr2_reg	= 0x10,
9962306a36Sopenharmony_ci	.imr_reg	= 0x14,
10062306a36Sopenharmony_ci	.isr_reg	= 0x18,
10162306a36Sopenharmony_ci	.edge_sel_reg	= -EINVAL,
10262306a36Sopenharmony_ci	.low_level	= 0x00,
10362306a36Sopenharmony_ci	.high_level	= 0x01,
10462306a36Sopenharmony_ci	.rise_edge	= 0x02,
10562306a36Sopenharmony_ci	.fall_edge	= 0x03,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct mxc_gpio_hwdata imx35_gpio_hwdata = {
10962306a36Sopenharmony_ci	.dr_reg		= 0x00,
11062306a36Sopenharmony_ci	.gdir_reg	= 0x04,
11162306a36Sopenharmony_ci	.psr_reg	= 0x08,
11262306a36Sopenharmony_ci	.icr1_reg	= 0x0c,
11362306a36Sopenharmony_ci	.icr2_reg	= 0x10,
11462306a36Sopenharmony_ci	.imr_reg	= 0x14,
11562306a36Sopenharmony_ci	.isr_reg	= 0x18,
11662306a36Sopenharmony_ci	.edge_sel_reg	= 0x1c,
11762306a36Sopenharmony_ci	.low_level	= 0x00,
11862306a36Sopenharmony_ci	.high_level	= 0x01,
11962306a36Sopenharmony_ci	.rise_edge	= 0x02,
12062306a36Sopenharmony_ci	.fall_edge	= 0x03,
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define GPIO_DR			(port->hwdata->dr_reg)
12462306a36Sopenharmony_ci#define GPIO_GDIR		(port->hwdata->gdir_reg)
12562306a36Sopenharmony_ci#define GPIO_PSR		(port->hwdata->psr_reg)
12662306a36Sopenharmony_ci#define GPIO_ICR1		(port->hwdata->icr1_reg)
12762306a36Sopenharmony_ci#define GPIO_ICR2		(port->hwdata->icr2_reg)
12862306a36Sopenharmony_ci#define GPIO_IMR		(port->hwdata->imr_reg)
12962306a36Sopenharmony_ci#define GPIO_ISR		(port->hwdata->isr_reg)
13062306a36Sopenharmony_ci#define GPIO_EDGE_SEL		(port->hwdata->edge_sel_reg)
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define GPIO_INT_LOW_LEV	(port->hwdata->low_level)
13362306a36Sopenharmony_ci#define GPIO_INT_HIGH_LEV	(port->hwdata->high_level)
13462306a36Sopenharmony_ci#define GPIO_INT_RISE_EDGE	(port->hwdata->rise_edge)
13562306a36Sopenharmony_ci#define GPIO_INT_FALL_EDGE	(port->hwdata->fall_edge)
13662306a36Sopenharmony_ci#define GPIO_INT_BOTH_EDGES	0x4
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct of_device_id mxc_gpio_dt_ids[] = {
13962306a36Sopenharmony_ci	{ .compatible = "fsl,imx1-gpio", .data =  &imx1_imx21_gpio_hwdata },
14062306a36Sopenharmony_ci	{ .compatible = "fsl,imx21-gpio", .data = &imx1_imx21_gpio_hwdata },
14162306a36Sopenharmony_ci	{ .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata },
14262306a36Sopenharmony_ci	{ .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata },
14362306a36Sopenharmony_ci	{ .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata },
14462306a36Sopenharmony_ci	{ .compatible = "fsl,imx8dxl-gpio", .data = &imx35_gpio_hwdata },
14562306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qm-gpio", .data = &imx35_gpio_hwdata },
14662306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qxp-gpio", .data = &imx35_gpio_hwdata },
14762306a36Sopenharmony_ci	{ /* sentinel */ }
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * MX2 has one interrupt *for all* gpio ports. The list is used
15362306a36Sopenharmony_ci * to save the references to all ports, so that mx2_gpio_irq_handler
15462306a36Sopenharmony_ci * can walk through all interrupt status registers.
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistatic LIST_HEAD(mxc_gpio_ports);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* Note: This driver assumes 32 GPIOs are handled in one register */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int gpio_set_irq_type(struct irq_data *d, u32 type)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
16362306a36Sopenharmony_ci	struct mxc_gpio_port *port = gc->private;
16462306a36Sopenharmony_ci	unsigned long flags;
16562306a36Sopenharmony_ci	u32 bit, val;
16662306a36Sopenharmony_ci	u32 gpio_idx = d->hwirq;
16762306a36Sopenharmony_ci	int edge;
16862306a36Sopenharmony_ci	void __iomem *reg = port->base;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	port->both_edges &= ~(1 << gpio_idx);
17162306a36Sopenharmony_ci	switch (type) {
17262306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
17362306a36Sopenharmony_ci		edge = GPIO_INT_RISE_EDGE;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
17662306a36Sopenharmony_ci		edge = GPIO_INT_FALL_EDGE;
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
17962306a36Sopenharmony_ci		if (GPIO_EDGE_SEL >= 0) {
18062306a36Sopenharmony_ci			edge = GPIO_INT_BOTH_EDGES;
18162306a36Sopenharmony_ci		} else {
18262306a36Sopenharmony_ci			val = port->gc.get(&port->gc, gpio_idx);
18362306a36Sopenharmony_ci			if (val) {
18462306a36Sopenharmony_ci				edge = GPIO_INT_LOW_LEV;
18562306a36Sopenharmony_ci				pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx);
18662306a36Sopenharmony_ci			} else {
18762306a36Sopenharmony_ci				edge = GPIO_INT_HIGH_LEV;
18862306a36Sopenharmony_ci				pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx);
18962306a36Sopenharmony_ci			}
19062306a36Sopenharmony_ci			port->both_edges |= 1 << gpio_idx;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
19462306a36Sopenharmony_ci		edge = GPIO_INT_LOW_LEV;
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
19762306a36Sopenharmony_ci		edge = GPIO_INT_HIGH_LEV;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	default:
20062306a36Sopenharmony_ci		return -EINVAL;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (GPIO_EDGE_SEL >= 0) {
20662306a36Sopenharmony_ci		val = readl(port->base + GPIO_EDGE_SEL);
20762306a36Sopenharmony_ci		if (edge == GPIO_INT_BOTH_EDGES)
20862306a36Sopenharmony_ci			writel(val | (1 << gpio_idx),
20962306a36Sopenharmony_ci				port->base + GPIO_EDGE_SEL);
21062306a36Sopenharmony_ci		else
21162306a36Sopenharmony_ci			writel(val & ~(1 << gpio_idx),
21262306a36Sopenharmony_ci				port->base + GPIO_EDGE_SEL);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (edge != GPIO_INT_BOTH_EDGES) {
21662306a36Sopenharmony_ci		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
21762306a36Sopenharmony_ci		bit = gpio_idx & 0xf;
21862306a36Sopenharmony_ci		val = readl(reg) & ~(0x3 << (bit << 1));
21962306a36Sopenharmony_ci		writel(val | (edge << (bit << 1)), reg);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	writel(1 << gpio_idx, port->base + GPIO_ISR);
22362306a36Sopenharmony_ci	port->pad_type[gpio_idx] = type;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return port->gc.direction_input(&port->gc, gpio_idx);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	void __iomem *reg = port->base;
23362306a36Sopenharmony_ci	unsigned long flags;
23462306a36Sopenharmony_ci	u32 bit, val;
23562306a36Sopenharmony_ci	int edge;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
24062306a36Sopenharmony_ci	bit = gpio & 0xf;
24162306a36Sopenharmony_ci	val = readl(reg);
24262306a36Sopenharmony_ci	edge = (val >> (bit << 1)) & 3;
24362306a36Sopenharmony_ci	val &= ~(0x3 << (bit << 1));
24462306a36Sopenharmony_ci	if (edge == GPIO_INT_HIGH_LEV) {
24562306a36Sopenharmony_ci		edge = GPIO_INT_LOW_LEV;
24662306a36Sopenharmony_ci		pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
24762306a36Sopenharmony_ci	} else if (edge == GPIO_INT_LOW_LEV) {
24862306a36Sopenharmony_ci		edge = GPIO_INT_HIGH_LEV;
24962306a36Sopenharmony_ci		pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
25262306a36Sopenharmony_ci		       gpio, edge);
25362306a36Sopenharmony_ci		goto unlock;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	writel(val | (edge << (bit << 1)), reg);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciunlock:
25862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/* handle 32 interrupts in one status register */
26262306a36Sopenharmony_cistatic void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	while (irq_stat != 0) {
26562306a36Sopenharmony_ci		int irqoffset = fls(irq_stat) - 1;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		if (port->both_edges & (1 << irqoffset))
26862306a36Sopenharmony_ci			mxc_flip_edge(port, irqoffset);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		generic_handle_domain_irq(port->domain, irqoffset);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		irq_stat &= ~(1 << irqoffset);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/* MX1 and MX3 has one interrupt *per* gpio port */
27762306a36Sopenharmony_cistatic void mx3_gpio_irq_handler(struct irq_desc *desc)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	u32 irq_stat;
28062306a36Sopenharmony_ci	struct mxc_gpio_port *port = irq_desc_get_handler_data(desc);
28162306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (port->is_pad_wakeup)
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mxc_gpio_irq_handler(port, irq_stat);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/* MX2 has one interrupt *for all* gpio ports */
29662306a36Sopenharmony_cistatic void mx2_gpio_irq_handler(struct irq_desc *desc)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	u32 irq_msk, irq_stat;
29962306a36Sopenharmony_ci	struct mxc_gpio_port *port;
30062306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* walk through all interrupt status registers */
30562306a36Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
30662306a36Sopenharmony_ci		irq_msk = readl(port->base + GPIO_IMR);
30762306a36Sopenharmony_ci		if (!irq_msk)
30862306a36Sopenharmony_ci			continue;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
31162306a36Sopenharmony_ci		if (irq_stat)
31262306a36Sopenharmony_ci			mxc_gpio_irq_handler(port, irq_stat);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/*
31862306a36Sopenharmony_ci * Set interrupt number "irq" in the GPIO as a wake-up source.
31962306a36Sopenharmony_ci * While system is running, all registered GPIO interrupts need to have
32062306a36Sopenharmony_ci * wake-up enabled. When system is suspended, only selected GPIO interrupts
32162306a36Sopenharmony_ci * need to have wake-up enabled.
32262306a36Sopenharmony_ci * @param  irq          interrupt source number
32362306a36Sopenharmony_ci * @param  enable       enable as wake-up if equal to non-zero
32462306a36Sopenharmony_ci * @return       This function returns 0 on success.
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistatic int gpio_set_wake_irq(struct irq_data *d, u32 enable)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
32962306a36Sopenharmony_ci	struct mxc_gpio_port *port = gc->private;
33062306a36Sopenharmony_ci	u32 gpio_idx = d->hwirq;
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (enable) {
33462306a36Sopenharmony_ci		if (port->irq_high && (gpio_idx >= 16))
33562306a36Sopenharmony_ci			ret = enable_irq_wake(port->irq_high);
33662306a36Sopenharmony_ci		else
33762306a36Sopenharmony_ci			ret = enable_irq_wake(port->irq);
33862306a36Sopenharmony_ci		port->wakeup_pads |= (1 << gpio_idx);
33962306a36Sopenharmony_ci	} else {
34062306a36Sopenharmony_ci		if (port->irq_high && (gpio_idx >= 16))
34162306a36Sopenharmony_ci			ret = disable_irq_wake(port->irq_high);
34262306a36Sopenharmony_ci		else
34362306a36Sopenharmony_ci			ret = disable_irq_wake(port->irq);
34462306a36Sopenharmony_ci		port->wakeup_pads &= ~(1 << gpio_idx);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct irq_chip_generic *gc;
35362306a36Sopenharmony_ci	struct irq_chip_type *ct;
35462306a36Sopenharmony_ci	int rv;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	gc = devm_irq_alloc_generic_chip(port->dev, "gpio-mxc", 1, irq_base,
35762306a36Sopenharmony_ci					 port->base, handle_level_irq);
35862306a36Sopenharmony_ci	if (!gc)
35962306a36Sopenharmony_ci		return -ENOMEM;
36062306a36Sopenharmony_ci	gc->private = port;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ct = gc->chip_types;
36362306a36Sopenharmony_ci	ct->chip.irq_ack = irq_gc_ack_set_bit;
36462306a36Sopenharmony_ci	ct->chip.irq_mask = irq_gc_mask_clr_bit;
36562306a36Sopenharmony_ci	ct->chip.irq_unmask = irq_gc_mask_set_bit;
36662306a36Sopenharmony_ci	ct->chip.irq_set_type = gpio_set_irq_type;
36762306a36Sopenharmony_ci	ct->chip.irq_set_wake = gpio_set_wake_irq;
36862306a36Sopenharmony_ci	ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
36962306a36Sopenharmony_ci	ct->regs.ack = GPIO_ISR;
37062306a36Sopenharmony_ci	ct->regs.mask = GPIO_IMR;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	rv = devm_irq_setup_generic_chip(port->dev, gc, IRQ_MSK(32),
37362306a36Sopenharmony_ci					 IRQ_GC_INIT_NESTED_LOCK,
37462306a36Sopenharmony_ci					 IRQ_NOREQUEST, 0);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return rv;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct mxc_gpio_port *port = gpiochip_get_data(gc);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return irq_find_mapping(port->domain, offset);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int mxc_gpio_request(struct gpio_chip *chip, unsigned int offset)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	int ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = gpiochip_generic_request(chip, offset);
39162306a36Sopenharmony_ci	if (ret)
39262306a36Sopenharmony_ci		return ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return pm_runtime_resume_and_get(chip->parent);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	gpiochip_generic_free(chip, offset);
40062306a36Sopenharmony_ci	pm_runtime_put(chip->parent);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enable)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	if (enable)
40662306a36Sopenharmony_ci		irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler, port);
40762306a36Sopenharmony_ci	else
40862306a36Sopenharmony_ci		irq_set_chained_handler_and_data(port->irq, NULL, NULL);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* setup handler for GPIO 16 to 31 */
41162306a36Sopenharmony_ci	if (port->irq_high > 0) {
41262306a36Sopenharmony_ci		if (enable)
41362306a36Sopenharmony_ci			irq_set_chained_handler_and_data(port->irq_high,
41462306a36Sopenharmony_ci							 port->mx_irq_handler,
41562306a36Sopenharmony_ci							 port);
41662306a36Sopenharmony_ci		else
41762306a36Sopenharmony_ci			irq_set_chained_handler_and_data(port->irq_high, NULL, NULL);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int mxc_gpio_probe(struct platform_device *pdev)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
42462306a36Sopenharmony_ci	struct mxc_gpio_port *port;
42562306a36Sopenharmony_ci	int irq_count;
42662306a36Sopenharmony_ci	int irq_base;
42762306a36Sopenharmony_ci	int err;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
43062306a36Sopenharmony_ci	if (!port)
43162306a36Sopenharmony_ci		return -ENOMEM;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	port->dev = &pdev->dev;
43462306a36Sopenharmony_ci	port->hwdata = device_get_match_data(&pdev->dev);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	port->base = devm_platform_ioremap_resource(pdev, 0);
43762306a36Sopenharmony_ci	if (IS_ERR(port->base))
43862306a36Sopenharmony_ci		return PTR_ERR(port->base);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	irq_count = platform_irq_count(pdev);
44162306a36Sopenharmony_ci	if (irq_count < 0)
44262306a36Sopenharmony_ci		return irq_count;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (irq_count > 1) {
44562306a36Sopenharmony_ci		port->irq_high = platform_get_irq(pdev, 1);
44662306a36Sopenharmony_ci		if (port->irq_high < 0)
44762306a36Sopenharmony_ci			port->irq_high = 0;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	port->irq = platform_get_irq(pdev, 0);
45162306a36Sopenharmony_ci	if (port->irq < 0)
45262306a36Sopenharmony_ci		return port->irq;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* the controller clock is optional */
45562306a36Sopenharmony_ci	port->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
45662306a36Sopenharmony_ci	if (IS_ERR(port->clk))
45762306a36Sopenharmony_ci		return PTR_ERR(port->clk);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
46062306a36Sopenharmony_ci		port->power_off = true;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	pm_runtime_get_noresume(&pdev->dev);
46362306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
46462306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* disable the interrupt and clear the status */
46762306a36Sopenharmony_ci	writel(0, port->base + GPIO_IMR);
46862306a36Sopenharmony_ci	writel(~0, port->base + GPIO_ISR);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx21-gpio")) {
47162306a36Sopenharmony_ci		/*
47262306a36Sopenharmony_ci		 * Setup one handler for all GPIO interrupts. Actually setting
47362306a36Sopenharmony_ci		 * the handler is needed only once, but doing it for every port
47462306a36Sopenharmony_ci		 * is more robust and easier.
47562306a36Sopenharmony_ci		 */
47662306a36Sopenharmony_ci		port->irq_high = -1;
47762306a36Sopenharmony_ci		port->mx_irq_handler = mx2_gpio_irq_handler;
47862306a36Sopenharmony_ci	} else
47962306a36Sopenharmony_ci		port->mx_irq_handler = mx3_gpio_irq_handler;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	mxc_update_irq_chained_handler(port, true);
48262306a36Sopenharmony_ci	err = bgpio_init(&port->gc, &pdev->dev, 4,
48362306a36Sopenharmony_ci			 port->base + GPIO_PSR,
48462306a36Sopenharmony_ci			 port->base + GPIO_DR, NULL,
48562306a36Sopenharmony_ci			 port->base + GPIO_GDIR, NULL,
48662306a36Sopenharmony_ci			 BGPIOF_READ_OUTPUT_REG_SET);
48762306a36Sopenharmony_ci	if (err)
48862306a36Sopenharmony_ci		goto out_bgio;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	port->gc.request = mxc_gpio_request;
49162306a36Sopenharmony_ci	port->gc.free = mxc_gpio_free;
49262306a36Sopenharmony_ci	port->gc.to_irq = mxc_gpio_to_irq;
49362306a36Sopenharmony_ci	port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
49462306a36Sopenharmony_ci					     pdev->id * 32;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port);
49762306a36Sopenharmony_ci	if (err)
49862306a36Sopenharmony_ci		goto out_bgio;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
50162306a36Sopenharmony_ci	if (irq_base < 0) {
50262306a36Sopenharmony_ci		err = irq_base;
50362306a36Sopenharmony_ci		goto out_bgio;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
50762306a36Sopenharmony_ci					     &irq_domain_simple_ops, NULL);
50862306a36Sopenharmony_ci	if (!port->domain) {
50962306a36Sopenharmony_ci		err = -ENODEV;
51062306a36Sopenharmony_ci		goto out_bgio;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	irq_domain_set_pm_device(port->domain, &pdev->dev);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* gpio-mxc can be a generic irq chip */
51662306a36Sopenharmony_ci	err = mxc_gpio_init_gc(port, irq_base);
51762306a36Sopenharmony_ci	if (err < 0)
51862306a36Sopenharmony_ci		goto out_irqdomain_remove;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	list_add_tail(&port->node, &mxc_gpio_ports);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	platform_set_drvdata(pdev, port);
52362306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ciout_irqdomain_remove:
52862306a36Sopenharmony_ci	irq_domain_remove(port->domain);
52962306a36Sopenharmony_ciout_bgio:
53062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
53162306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
53262306a36Sopenharmony_ci	dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
53362306a36Sopenharmony_ci	return err;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void mxc_gpio_save_regs(struct mxc_gpio_port *port)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	if (!port->power_off)
53962306a36Sopenharmony_ci		return;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	port->gpio_saved_reg.icr1 = readl(port->base + GPIO_ICR1);
54262306a36Sopenharmony_ci	port->gpio_saved_reg.icr2 = readl(port->base + GPIO_ICR2);
54362306a36Sopenharmony_ci	port->gpio_saved_reg.imr = readl(port->base + GPIO_IMR);
54462306a36Sopenharmony_ci	port->gpio_saved_reg.gdir = readl(port->base + GPIO_GDIR);
54562306a36Sopenharmony_ci	port->gpio_saved_reg.edge_sel = readl(port->base + GPIO_EDGE_SEL);
54662306a36Sopenharmony_ci	port->gpio_saved_reg.dr = readl(port->base + GPIO_DR);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	if (!port->power_off)
55262306a36Sopenharmony_ci		return;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	writel(port->gpio_saved_reg.icr1, port->base + GPIO_ICR1);
55562306a36Sopenharmony_ci	writel(port->gpio_saved_reg.icr2, port->base + GPIO_ICR2);
55662306a36Sopenharmony_ci	writel(port->gpio_saved_reg.imr, port->base + GPIO_IMR);
55762306a36Sopenharmony_ci	writel(port->gpio_saved_reg.gdir, port->base + GPIO_GDIR);
55862306a36Sopenharmony_ci	writel(port->gpio_saved_reg.edge_sel, port->base + GPIO_EDGE_SEL);
55962306a36Sopenharmony_ci	writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic bool mxc_gpio_generic_config(struct mxc_gpio_port *port,
56362306a36Sopenharmony_ci		unsigned int offset, unsigned long conf)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct device_node *np = port->dev->of_node;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") ||
56862306a36Sopenharmony_ci	    of_device_is_compatible(np, "fsl,imx8qxp-gpio") ||
56962306a36Sopenharmony_ci	    of_device_is_compatible(np, "fsl,imx8qm-gpio"))
57062306a36Sopenharmony_ci		return (gpiochip_generic_config(&port->gc, offset, conf) == 0);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return false;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	unsigned long config;
57862306a36Sopenharmony_ci	bool ret = false;
57962306a36Sopenharmony_ci	int i, type;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	static const u32 pad_type_map[] = {
58262306a36Sopenharmony_ci		IMX_SCU_WAKEUP_OFF,		/* 0 */
58362306a36Sopenharmony_ci		IMX_SCU_WAKEUP_RISE_EDGE,	/* IRQ_TYPE_EDGE_RISING */
58462306a36Sopenharmony_ci		IMX_SCU_WAKEUP_FALL_EDGE,	/* IRQ_TYPE_EDGE_FALLING */
58562306a36Sopenharmony_ci		IMX_SCU_WAKEUP_FALL_EDGE,	/* IRQ_TYPE_EDGE_BOTH */
58662306a36Sopenharmony_ci		IMX_SCU_WAKEUP_HIGH_LVL,	/* IRQ_TYPE_LEVEL_HIGH */
58762306a36Sopenharmony_ci		IMX_SCU_WAKEUP_OFF,		/* 5 */
58862306a36Sopenharmony_ci		IMX_SCU_WAKEUP_OFF,		/* 6 */
58962306a36Sopenharmony_ci		IMX_SCU_WAKEUP_OFF,		/* 7 */
59062306a36Sopenharmony_ci		IMX_SCU_WAKEUP_LOW_LVL,		/* IRQ_TYPE_LEVEL_LOW */
59162306a36Sopenharmony_ci	};
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
59462306a36Sopenharmony_ci		if ((port->wakeup_pads & (1 << i))) {
59562306a36Sopenharmony_ci			type = port->pad_type[i];
59662306a36Sopenharmony_ci			if (enable)
59762306a36Sopenharmony_ci				config = pad_type_map[type];
59862306a36Sopenharmony_ci			else
59962306a36Sopenharmony_ci				config = IMX_SCU_WAKEUP_OFF;
60062306a36Sopenharmony_ci			ret |= mxc_gpio_generic_config(port, i, config);
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return ret;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int mxc_gpio_runtime_suspend(struct device *dev)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct mxc_gpio_port *port = dev_get_drvdata(dev);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	mxc_gpio_save_regs(port);
61262306a36Sopenharmony_ci	clk_disable_unprepare(port->clk);
61362306a36Sopenharmony_ci	mxc_update_irq_chained_handler(port, false);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return 0;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int mxc_gpio_runtime_resume(struct device *dev)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct mxc_gpio_port *port = dev_get_drvdata(dev);
62162306a36Sopenharmony_ci	int ret;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	mxc_update_irq_chained_handler(port, true);
62462306a36Sopenharmony_ci	ret = clk_prepare_enable(port->clk);
62562306a36Sopenharmony_ci	if (ret) {
62662306a36Sopenharmony_ci		mxc_update_irq_chained_handler(port, false);
62762306a36Sopenharmony_ci		return ret;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	mxc_gpio_restore_regs(port);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int mxc_gpio_noirq_suspend(struct device *dev)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
63862306a36Sopenharmony_ci	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (port->wakeup_pads > 0)
64162306a36Sopenharmony_ci		port->is_pad_wakeup = mxc_gpio_set_pad_wakeup(port, true);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int mxc_gpio_noirq_resume(struct device *dev)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
64962306a36Sopenharmony_ci	struct mxc_gpio_port *port = platform_get_drvdata(pdev);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (port->wakeup_pads > 0)
65262306a36Sopenharmony_ci		mxc_gpio_set_pad_wakeup(port, false);
65362306a36Sopenharmony_ci	port->is_pad_wakeup = false;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
65962306a36Sopenharmony_ci	NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
66062306a36Sopenharmony_ci	RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic int mxc_gpio_syscore_suspend(void)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct mxc_gpio_port *port;
66662306a36Sopenharmony_ci	int ret;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* walk through all ports */
66962306a36Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
67062306a36Sopenharmony_ci		ret = clk_prepare_enable(port->clk);
67162306a36Sopenharmony_ci		if (ret)
67262306a36Sopenharmony_ci			return ret;
67362306a36Sopenharmony_ci		mxc_gpio_save_regs(port);
67462306a36Sopenharmony_ci		clk_disable_unprepare(port->clk);
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic void mxc_gpio_syscore_resume(void)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	struct mxc_gpio_port *port;
68362306a36Sopenharmony_ci	int ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* walk through all ports */
68662306a36Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
68762306a36Sopenharmony_ci		ret = clk_prepare_enable(port->clk);
68862306a36Sopenharmony_ci		if (ret) {
68962306a36Sopenharmony_ci			pr_err("mxc: failed to enable gpio clock %d\n", ret);
69062306a36Sopenharmony_ci			return;
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci		mxc_gpio_restore_regs(port);
69362306a36Sopenharmony_ci		clk_disable_unprepare(port->clk);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic struct syscore_ops mxc_gpio_syscore_ops = {
69862306a36Sopenharmony_ci	.suspend = mxc_gpio_syscore_suspend,
69962306a36Sopenharmony_ci	.resume = mxc_gpio_syscore_resume,
70062306a36Sopenharmony_ci};
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic struct platform_driver mxc_gpio_driver = {
70362306a36Sopenharmony_ci	.driver		= {
70462306a36Sopenharmony_ci		.name	= "gpio-mxc",
70562306a36Sopenharmony_ci		.of_match_table = mxc_gpio_dt_ids,
70662306a36Sopenharmony_ci		.suppress_bind_attrs = true,
70762306a36Sopenharmony_ci		.pm = pm_ptr(&mxc_gpio_dev_pm_ops),
70862306a36Sopenharmony_ci	},
70962306a36Sopenharmony_ci	.probe		= mxc_gpio_probe,
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int __init gpio_mxc_init(void)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	register_syscore_ops(&mxc_gpio_syscore_ops);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return platform_driver_register(&mxc_gpio_driver);
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_cisubsys_initcall(gpio_mxc_init);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciMODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
72162306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX GPIO Driver");
72262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
723