18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
48c2ecf20Sopenharmony_ci// Copyright 2008 Juergen Beisert, kernel@pengutronix.de
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Based on code from Freescale Semiconductor,
78c2ecf20Sopenharmony_ci// Authors: Daniel Mack, Juergen Beisert.
88c2ecf20Sopenharmony_ci// Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/irq.h>
168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
178c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
228c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
238c2ecf20Sopenharmony_ci#include <linux/of.h>
248c2ecf20Sopenharmony_ci#include <linux/of_device.h>
258c2ecf20Sopenharmony_ci#include <linux/bug.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cienum mxc_gpio_hwtype {
288c2ecf20Sopenharmony_ci	IMX1_GPIO,	/* runs on i.mx1 */
298c2ecf20Sopenharmony_ci	IMX21_GPIO,	/* runs on i.mx21 and i.mx27 */
308c2ecf20Sopenharmony_ci	IMX31_GPIO,	/* runs on i.mx31 */
318c2ecf20Sopenharmony_ci	IMX35_GPIO,	/* runs on all other i.mx */
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* device type dependent stuff */
358c2ecf20Sopenharmony_cistruct mxc_gpio_hwdata {
368c2ecf20Sopenharmony_ci	unsigned dr_reg;
378c2ecf20Sopenharmony_ci	unsigned gdir_reg;
388c2ecf20Sopenharmony_ci	unsigned psr_reg;
398c2ecf20Sopenharmony_ci	unsigned icr1_reg;
408c2ecf20Sopenharmony_ci	unsigned icr2_reg;
418c2ecf20Sopenharmony_ci	unsigned imr_reg;
428c2ecf20Sopenharmony_ci	unsigned isr_reg;
438c2ecf20Sopenharmony_ci	int edge_sel_reg;
448c2ecf20Sopenharmony_ci	unsigned low_level;
458c2ecf20Sopenharmony_ci	unsigned high_level;
468c2ecf20Sopenharmony_ci	unsigned rise_edge;
478c2ecf20Sopenharmony_ci	unsigned fall_edge;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct mxc_gpio_reg_saved {
518c2ecf20Sopenharmony_ci	u32 icr1;
528c2ecf20Sopenharmony_ci	u32 icr2;
538c2ecf20Sopenharmony_ci	u32 imr;
548c2ecf20Sopenharmony_ci	u32 gdir;
558c2ecf20Sopenharmony_ci	u32 edge_sel;
568c2ecf20Sopenharmony_ci	u32 dr;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct mxc_gpio_port {
608c2ecf20Sopenharmony_ci	struct list_head node;
618c2ecf20Sopenharmony_ci	void __iomem *base;
628c2ecf20Sopenharmony_ci	struct clk *clk;
638c2ecf20Sopenharmony_ci	int irq;
648c2ecf20Sopenharmony_ci	int irq_high;
658c2ecf20Sopenharmony_ci	struct irq_domain *domain;
668c2ecf20Sopenharmony_ci	struct gpio_chip gc;
678c2ecf20Sopenharmony_ci	struct device *dev;
688c2ecf20Sopenharmony_ci	u32 both_edges;
698c2ecf20Sopenharmony_ci	struct mxc_gpio_reg_saved gpio_saved_reg;
708c2ecf20Sopenharmony_ci	bool power_off;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
748c2ecf20Sopenharmony_ci	.dr_reg		= 0x1c,
758c2ecf20Sopenharmony_ci	.gdir_reg	= 0x00,
768c2ecf20Sopenharmony_ci	.psr_reg	= 0x24,
778c2ecf20Sopenharmony_ci	.icr1_reg	= 0x28,
788c2ecf20Sopenharmony_ci	.icr2_reg	= 0x2c,
798c2ecf20Sopenharmony_ci	.imr_reg	= 0x30,
808c2ecf20Sopenharmony_ci	.isr_reg	= 0x34,
818c2ecf20Sopenharmony_ci	.edge_sel_reg	= -EINVAL,
828c2ecf20Sopenharmony_ci	.low_level	= 0x03,
838c2ecf20Sopenharmony_ci	.high_level	= 0x02,
848c2ecf20Sopenharmony_ci	.rise_edge	= 0x00,
858c2ecf20Sopenharmony_ci	.fall_edge	= 0x01,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct mxc_gpio_hwdata imx31_gpio_hwdata = {
898c2ecf20Sopenharmony_ci	.dr_reg		= 0x00,
908c2ecf20Sopenharmony_ci	.gdir_reg	= 0x04,
918c2ecf20Sopenharmony_ci	.psr_reg	= 0x08,
928c2ecf20Sopenharmony_ci	.icr1_reg	= 0x0c,
938c2ecf20Sopenharmony_ci	.icr2_reg	= 0x10,
948c2ecf20Sopenharmony_ci	.imr_reg	= 0x14,
958c2ecf20Sopenharmony_ci	.isr_reg	= 0x18,
968c2ecf20Sopenharmony_ci	.edge_sel_reg	= -EINVAL,
978c2ecf20Sopenharmony_ci	.low_level	= 0x00,
988c2ecf20Sopenharmony_ci	.high_level	= 0x01,
998c2ecf20Sopenharmony_ci	.rise_edge	= 0x02,
1008c2ecf20Sopenharmony_ci	.fall_edge	= 0x03,
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct mxc_gpio_hwdata imx35_gpio_hwdata = {
1048c2ecf20Sopenharmony_ci	.dr_reg		= 0x00,
1058c2ecf20Sopenharmony_ci	.gdir_reg	= 0x04,
1068c2ecf20Sopenharmony_ci	.psr_reg	= 0x08,
1078c2ecf20Sopenharmony_ci	.icr1_reg	= 0x0c,
1088c2ecf20Sopenharmony_ci	.icr2_reg	= 0x10,
1098c2ecf20Sopenharmony_ci	.imr_reg	= 0x14,
1108c2ecf20Sopenharmony_ci	.isr_reg	= 0x18,
1118c2ecf20Sopenharmony_ci	.edge_sel_reg	= 0x1c,
1128c2ecf20Sopenharmony_ci	.low_level	= 0x00,
1138c2ecf20Sopenharmony_ci	.high_level	= 0x01,
1148c2ecf20Sopenharmony_ci	.rise_edge	= 0x02,
1158c2ecf20Sopenharmony_ci	.fall_edge	= 0x03,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic enum mxc_gpio_hwtype mxc_gpio_hwtype;
1198c2ecf20Sopenharmony_cistatic struct mxc_gpio_hwdata *mxc_gpio_hwdata;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define GPIO_DR			(mxc_gpio_hwdata->dr_reg)
1228c2ecf20Sopenharmony_ci#define GPIO_GDIR		(mxc_gpio_hwdata->gdir_reg)
1238c2ecf20Sopenharmony_ci#define GPIO_PSR		(mxc_gpio_hwdata->psr_reg)
1248c2ecf20Sopenharmony_ci#define GPIO_ICR1		(mxc_gpio_hwdata->icr1_reg)
1258c2ecf20Sopenharmony_ci#define GPIO_ICR2		(mxc_gpio_hwdata->icr2_reg)
1268c2ecf20Sopenharmony_ci#define GPIO_IMR		(mxc_gpio_hwdata->imr_reg)
1278c2ecf20Sopenharmony_ci#define GPIO_ISR		(mxc_gpio_hwdata->isr_reg)
1288c2ecf20Sopenharmony_ci#define GPIO_EDGE_SEL		(mxc_gpio_hwdata->edge_sel_reg)
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define GPIO_INT_LOW_LEV	(mxc_gpio_hwdata->low_level)
1318c2ecf20Sopenharmony_ci#define GPIO_INT_HIGH_LEV	(mxc_gpio_hwdata->high_level)
1328c2ecf20Sopenharmony_ci#define GPIO_INT_RISE_EDGE	(mxc_gpio_hwdata->rise_edge)
1338c2ecf20Sopenharmony_ci#define GPIO_INT_FALL_EDGE	(mxc_gpio_hwdata->fall_edge)
1348c2ecf20Sopenharmony_ci#define GPIO_INT_BOTH_EDGES	0x4
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const struct platform_device_id mxc_gpio_devtype[] = {
1378c2ecf20Sopenharmony_ci	{
1388c2ecf20Sopenharmony_ci		.name = "imx1-gpio",
1398c2ecf20Sopenharmony_ci		.driver_data = IMX1_GPIO,
1408c2ecf20Sopenharmony_ci	}, {
1418c2ecf20Sopenharmony_ci		.name = "imx21-gpio",
1428c2ecf20Sopenharmony_ci		.driver_data = IMX21_GPIO,
1438c2ecf20Sopenharmony_ci	}, {
1448c2ecf20Sopenharmony_ci		.name = "imx31-gpio",
1458c2ecf20Sopenharmony_ci		.driver_data = IMX31_GPIO,
1468c2ecf20Sopenharmony_ci	}, {
1478c2ecf20Sopenharmony_ci		.name = "imx35-gpio",
1488c2ecf20Sopenharmony_ci		.driver_data = IMX35_GPIO,
1498c2ecf20Sopenharmony_ci	}, {
1508c2ecf20Sopenharmony_ci		/* sentinel */
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic const struct of_device_id mxc_gpio_dt_ids[] = {
1558c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
1568c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
1578c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
1588c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
1598c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx7d-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
1608c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/*
1658c2ecf20Sopenharmony_ci * MX2 has one interrupt *for all* gpio ports. The list is used
1668c2ecf20Sopenharmony_ci * to save the references to all ports, so that mx2_gpio_irq_handler
1678c2ecf20Sopenharmony_ci * can walk through all interrupt status registers.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_cistatic LIST_HEAD(mxc_gpio_ports);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* Note: This driver assumes 32 GPIOs are handled in one register */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int gpio_set_irq_type(struct irq_data *d, u32 type)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
1768c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port = gc->private;
1778c2ecf20Sopenharmony_ci	u32 bit, val;
1788c2ecf20Sopenharmony_ci	u32 gpio_idx = d->hwirq;
1798c2ecf20Sopenharmony_ci	int edge;
1808c2ecf20Sopenharmony_ci	void __iomem *reg = port->base;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	port->both_edges &= ~(1 << gpio_idx);
1838c2ecf20Sopenharmony_ci	switch (type) {
1848c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
1858c2ecf20Sopenharmony_ci		edge = GPIO_INT_RISE_EDGE;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
1888c2ecf20Sopenharmony_ci		edge = GPIO_INT_FALL_EDGE;
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
1918c2ecf20Sopenharmony_ci		if (GPIO_EDGE_SEL >= 0) {
1928c2ecf20Sopenharmony_ci			edge = GPIO_INT_BOTH_EDGES;
1938c2ecf20Sopenharmony_ci		} else {
1948c2ecf20Sopenharmony_ci			val = port->gc.get(&port->gc, gpio_idx);
1958c2ecf20Sopenharmony_ci			if (val) {
1968c2ecf20Sopenharmony_ci				edge = GPIO_INT_LOW_LEV;
1978c2ecf20Sopenharmony_ci				pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx);
1988c2ecf20Sopenharmony_ci			} else {
1998c2ecf20Sopenharmony_ci				edge = GPIO_INT_HIGH_LEV;
2008c2ecf20Sopenharmony_ci				pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx);
2018c2ecf20Sopenharmony_ci			}
2028c2ecf20Sopenharmony_ci			port->both_edges |= 1 << gpio_idx;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci		break;
2058c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
2068c2ecf20Sopenharmony_ci		edge = GPIO_INT_LOW_LEV;
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
2098c2ecf20Sopenharmony_ci		edge = GPIO_INT_HIGH_LEV;
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci	default:
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (GPIO_EDGE_SEL >= 0) {
2168c2ecf20Sopenharmony_ci		val = readl(port->base + GPIO_EDGE_SEL);
2178c2ecf20Sopenharmony_ci		if (edge == GPIO_INT_BOTH_EDGES)
2188c2ecf20Sopenharmony_ci			writel(val | (1 << gpio_idx),
2198c2ecf20Sopenharmony_ci				port->base + GPIO_EDGE_SEL);
2208c2ecf20Sopenharmony_ci		else
2218c2ecf20Sopenharmony_ci			writel(val & ~(1 << gpio_idx),
2228c2ecf20Sopenharmony_ci				port->base + GPIO_EDGE_SEL);
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (edge != GPIO_INT_BOTH_EDGES) {
2268c2ecf20Sopenharmony_ci		reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
2278c2ecf20Sopenharmony_ci		bit = gpio_idx & 0xf;
2288c2ecf20Sopenharmony_ci		val = readl(reg) & ~(0x3 << (bit << 1));
2298c2ecf20Sopenharmony_ci		writel(val | (edge << (bit << 1)), reg);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	writel(1 << gpio_idx, port->base + GPIO_ISR);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return port->gc.direction_input(&port->gc, gpio_idx);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	void __iomem *reg = port->base;
2408c2ecf20Sopenharmony_ci	u32 bit, val;
2418c2ecf20Sopenharmony_ci	int edge;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
2448c2ecf20Sopenharmony_ci	bit = gpio & 0xf;
2458c2ecf20Sopenharmony_ci	val = readl(reg);
2468c2ecf20Sopenharmony_ci	edge = (val >> (bit << 1)) & 3;
2478c2ecf20Sopenharmony_ci	val &= ~(0x3 << (bit << 1));
2488c2ecf20Sopenharmony_ci	if (edge == GPIO_INT_HIGH_LEV) {
2498c2ecf20Sopenharmony_ci		edge = GPIO_INT_LOW_LEV;
2508c2ecf20Sopenharmony_ci		pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
2518c2ecf20Sopenharmony_ci	} else if (edge == GPIO_INT_LOW_LEV) {
2528c2ecf20Sopenharmony_ci		edge = GPIO_INT_HIGH_LEV;
2538c2ecf20Sopenharmony_ci		pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
2548c2ecf20Sopenharmony_ci	} else {
2558c2ecf20Sopenharmony_ci		pr_err("mxc: invalid configuration for GPIO %d: %x\n",
2568c2ecf20Sopenharmony_ci		       gpio, edge);
2578c2ecf20Sopenharmony_ci		return;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci	writel(val | (edge << (bit << 1)), reg);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/* handle 32 interrupts in one status register */
2638c2ecf20Sopenharmony_cistatic void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	while (irq_stat != 0) {
2668c2ecf20Sopenharmony_ci		int irqoffset = fls(irq_stat) - 1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		if (port->both_edges & (1 << irqoffset))
2698c2ecf20Sopenharmony_ci			mxc_flip_edge(port, irqoffset);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		irq_stat &= ~(1 << irqoffset);
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/* MX1 and MX3 has one interrupt *per* gpio port */
2788c2ecf20Sopenharmony_cistatic void mx3_gpio_irq_handler(struct irq_desc *desc)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	u32 irq_stat;
2818c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port = irq_desc_get_handler_data(desc);
2828c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	chained_irq_enter(chip, desc);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mxc_gpio_irq_handler(port, irq_stat);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	chained_irq_exit(chip, desc);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/* MX2 has one interrupt *for all* gpio ports */
2948c2ecf20Sopenharmony_cistatic void mx2_gpio_irq_handler(struct irq_desc *desc)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	u32 irq_msk, irq_stat;
2978c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port;
2988c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	chained_irq_enter(chip, desc);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* walk through all interrupt status registers */
3038c2ecf20Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
3048c2ecf20Sopenharmony_ci		irq_msk = readl(port->base + GPIO_IMR);
3058c2ecf20Sopenharmony_ci		if (!irq_msk)
3068c2ecf20Sopenharmony_ci			continue;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		irq_stat = readl(port->base + GPIO_ISR) & irq_msk;
3098c2ecf20Sopenharmony_ci		if (irq_stat)
3108c2ecf20Sopenharmony_ci			mxc_gpio_irq_handler(port, irq_stat);
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	chained_irq_exit(chip, desc);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/*
3168c2ecf20Sopenharmony_ci * Set interrupt number "irq" in the GPIO as a wake-up source.
3178c2ecf20Sopenharmony_ci * While system is running, all registered GPIO interrupts need to have
3188c2ecf20Sopenharmony_ci * wake-up enabled. When system is suspended, only selected GPIO interrupts
3198c2ecf20Sopenharmony_ci * need to have wake-up enabled.
3208c2ecf20Sopenharmony_ci * @param  irq          interrupt source number
3218c2ecf20Sopenharmony_ci * @param  enable       enable as wake-up if equal to non-zero
3228c2ecf20Sopenharmony_ci * @return       This function returns 0 on success.
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic int gpio_set_wake_irq(struct irq_data *d, u32 enable)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
3278c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port = gc->private;
3288c2ecf20Sopenharmony_ci	u32 gpio_idx = d->hwirq;
3298c2ecf20Sopenharmony_ci	int ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (enable) {
3328c2ecf20Sopenharmony_ci		if (port->irq_high && (gpio_idx >= 16))
3338c2ecf20Sopenharmony_ci			ret = enable_irq_wake(port->irq_high);
3348c2ecf20Sopenharmony_ci		else
3358c2ecf20Sopenharmony_ci			ret = enable_irq_wake(port->irq);
3368c2ecf20Sopenharmony_ci	} else {
3378c2ecf20Sopenharmony_ci		if (port->irq_high && (gpio_idx >= 16))
3388c2ecf20Sopenharmony_ci			ret = disable_irq_wake(port->irq_high);
3398c2ecf20Sopenharmony_ci		else
3408c2ecf20Sopenharmony_ci			ret = disable_irq_wake(port->irq);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return ret;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc;
3498c2ecf20Sopenharmony_ci	struct irq_chip_type *ct;
3508c2ecf20Sopenharmony_ci	int rv;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	gc = devm_irq_alloc_generic_chip(port->dev, "gpio-mxc", 1, irq_base,
3538c2ecf20Sopenharmony_ci					 port->base, handle_level_irq);
3548c2ecf20Sopenharmony_ci	if (!gc)
3558c2ecf20Sopenharmony_ci		return -ENOMEM;
3568c2ecf20Sopenharmony_ci	gc->private = port;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ct = gc->chip_types;
3598c2ecf20Sopenharmony_ci	ct->chip.irq_ack = irq_gc_ack_set_bit;
3608c2ecf20Sopenharmony_ci	ct->chip.irq_mask = irq_gc_mask_clr_bit;
3618c2ecf20Sopenharmony_ci	ct->chip.irq_unmask = irq_gc_mask_set_bit;
3628c2ecf20Sopenharmony_ci	ct->chip.irq_set_type = gpio_set_irq_type;
3638c2ecf20Sopenharmony_ci	ct->chip.irq_set_wake = gpio_set_wake_irq;
3648c2ecf20Sopenharmony_ci	ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
3658c2ecf20Sopenharmony_ci	ct->regs.ack = GPIO_ISR;
3668c2ecf20Sopenharmony_ci	ct->regs.mask = GPIO_IMR;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	rv = devm_irq_setup_generic_chip(port->dev, gc, IRQ_MSK(32),
3698c2ecf20Sopenharmony_ci					 IRQ_GC_INIT_NESTED_LOCK,
3708c2ecf20Sopenharmony_ci					 IRQ_NOREQUEST, 0);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return rv;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void mxc_gpio_get_hw(struct platform_device *pdev)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	const struct of_device_id *of_id =
3788c2ecf20Sopenharmony_ci			of_match_device(mxc_gpio_dt_ids, &pdev->dev);
3798c2ecf20Sopenharmony_ci	enum mxc_gpio_hwtype hwtype;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (of_id)
3828c2ecf20Sopenharmony_ci		pdev->id_entry = of_id->data;
3838c2ecf20Sopenharmony_ci	hwtype = pdev->id_entry->driver_data;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (mxc_gpio_hwtype) {
3868c2ecf20Sopenharmony_ci		/*
3878c2ecf20Sopenharmony_ci		 * The driver works with a reasonable presupposition,
3888c2ecf20Sopenharmony_ci		 * that is all gpio ports must be the same type when
3898c2ecf20Sopenharmony_ci		 * running on one soc.
3908c2ecf20Sopenharmony_ci		 */
3918c2ecf20Sopenharmony_ci		BUG_ON(mxc_gpio_hwtype != hwtype);
3928c2ecf20Sopenharmony_ci		return;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (hwtype == IMX35_GPIO)
3968c2ecf20Sopenharmony_ci		mxc_gpio_hwdata = &imx35_gpio_hwdata;
3978c2ecf20Sopenharmony_ci	else if (hwtype == IMX31_GPIO)
3988c2ecf20Sopenharmony_ci		mxc_gpio_hwdata = &imx31_gpio_hwdata;
3998c2ecf20Sopenharmony_ci	else
4008c2ecf20Sopenharmony_ci		mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	mxc_gpio_hwtype = hwtype;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port = gpiochip_get_data(gc);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return irq_find_mapping(port->domain, offset);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int mxc_gpio_probe(struct platform_device *pdev)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
4158c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port;
4168c2ecf20Sopenharmony_ci	int irq_count;
4178c2ecf20Sopenharmony_ci	int irq_base;
4188c2ecf20Sopenharmony_ci	int err;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	mxc_gpio_get_hw(pdev);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
4238c2ecf20Sopenharmony_ci	if (!port)
4248c2ecf20Sopenharmony_ci		return -ENOMEM;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	port->dev = &pdev->dev;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	port->base = devm_platform_ioremap_resource(pdev, 0);
4298c2ecf20Sopenharmony_ci	if (IS_ERR(port->base))
4308c2ecf20Sopenharmony_ci		return PTR_ERR(port->base);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	irq_count = platform_irq_count(pdev);
4338c2ecf20Sopenharmony_ci	if (irq_count < 0)
4348c2ecf20Sopenharmony_ci		return irq_count;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (irq_count > 1) {
4378c2ecf20Sopenharmony_ci		port->irq_high = platform_get_irq(pdev, 1);
4388c2ecf20Sopenharmony_ci		if (port->irq_high < 0)
4398c2ecf20Sopenharmony_ci			port->irq_high = 0;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	port->irq = platform_get_irq(pdev, 0);
4438c2ecf20Sopenharmony_ci	if (port->irq < 0)
4448c2ecf20Sopenharmony_ci		return port->irq;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* the controller clock is optional */
4478c2ecf20Sopenharmony_ci	port->clk = devm_clk_get_optional(&pdev->dev, NULL);
4488c2ecf20Sopenharmony_ci	if (IS_ERR(port->clk))
4498c2ecf20Sopenharmony_ci		return PTR_ERR(port->clk);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	err = clk_prepare_enable(port->clk);
4528c2ecf20Sopenharmony_ci	if (err) {
4538c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to enable clock.\n");
4548c2ecf20Sopenharmony_ci		return err;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
4588c2ecf20Sopenharmony_ci		port->power_off = true;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* disable the interrupt and clear the status */
4618c2ecf20Sopenharmony_ci	writel(0, port->base + GPIO_IMR);
4628c2ecf20Sopenharmony_ci	writel(~0, port->base + GPIO_ISR);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (mxc_gpio_hwtype == IMX21_GPIO) {
4658c2ecf20Sopenharmony_ci		/*
4668c2ecf20Sopenharmony_ci		 * Setup one handler for all GPIO interrupts. Actually setting
4678c2ecf20Sopenharmony_ci		 * the handler is needed only once, but doing it for every port
4688c2ecf20Sopenharmony_ci		 * is more robust and easier.
4698c2ecf20Sopenharmony_ci		 */
4708c2ecf20Sopenharmony_ci		irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
4718c2ecf20Sopenharmony_ci	} else {
4728c2ecf20Sopenharmony_ci		/* setup one handler for each entry */
4738c2ecf20Sopenharmony_ci		irq_set_chained_handler_and_data(port->irq,
4748c2ecf20Sopenharmony_ci						 mx3_gpio_irq_handler, port);
4758c2ecf20Sopenharmony_ci		if (port->irq_high > 0)
4768c2ecf20Sopenharmony_ci			/* setup handler for GPIO 16 to 31 */
4778c2ecf20Sopenharmony_ci			irq_set_chained_handler_and_data(port->irq_high,
4788c2ecf20Sopenharmony_ci							 mx3_gpio_irq_handler,
4798c2ecf20Sopenharmony_ci							 port);
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	err = bgpio_init(&port->gc, &pdev->dev, 4,
4838c2ecf20Sopenharmony_ci			 port->base + GPIO_PSR,
4848c2ecf20Sopenharmony_ci			 port->base + GPIO_DR, NULL,
4858c2ecf20Sopenharmony_ci			 port->base + GPIO_GDIR, NULL,
4868c2ecf20Sopenharmony_ci			 BGPIOF_READ_OUTPUT_REG_SET);
4878c2ecf20Sopenharmony_ci	if (err)
4888c2ecf20Sopenharmony_ci		goto out_bgio;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	port->gc.request = gpiochip_generic_request;
4918c2ecf20Sopenharmony_ci	port->gc.free = gpiochip_generic_free;
4928c2ecf20Sopenharmony_ci	port->gc.to_irq = mxc_gpio_to_irq;
4938c2ecf20Sopenharmony_ci	port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
4948c2ecf20Sopenharmony_ci					     pdev->id * 32;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port);
4978c2ecf20Sopenharmony_ci	if (err)
4988c2ecf20Sopenharmony_ci		goto out_bgio;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
5018c2ecf20Sopenharmony_ci	if (irq_base < 0) {
5028c2ecf20Sopenharmony_ci		err = irq_base;
5038c2ecf20Sopenharmony_ci		goto out_bgio;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
5078c2ecf20Sopenharmony_ci					     &irq_domain_simple_ops, NULL);
5088c2ecf20Sopenharmony_ci	if (!port->domain) {
5098c2ecf20Sopenharmony_ci		err = -ENODEV;
5108c2ecf20Sopenharmony_ci		goto out_bgio;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* gpio-mxc can be a generic irq chip */
5148c2ecf20Sopenharmony_ci	err = mxc_gpio_init_gc(port, irq_base);
5158c2ecf20Sopenharmony_ci	if (err < 0)
5168c2ecf20Sopenharmony_ci		goto out_irqdomain_remove;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	list_add_tail(&port->node, &mxc_gpio_ports);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, port);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ciout_irqdomain_remove:
5258c2ecf20Sopenharmony_ci	irq_domain_remove(port->domain);
5268c2ecf20Sopenharmony_ciout_bgio:
5278c2ecf20Sopenharmony_ci	clk_disable_unprepare(port->clk);
5288c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
5298c2ecf20Sopenharmony_ci	return err;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void mxc_gpio_save_regs(struct mxc_gpio_port *port)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	if (!port->power_off)
5358c2ecf20Sopenharmony_ci		return;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	port->gpio_saved_reg.icr1 = readl(port->base + GPIO_ICR1);
5388c2ecf20Sopenharmony_ci	port->gpio_saved_reg.icr2 = readl(port->base + GPIO_ICR2);
5398c2ecf20Sopenharmony_ci	port->gpio_saved_reg.imr = readl(port->base + GPIO_IMR);
5408c2ecf20Sopenharmony_ci	port->gpio_saved_reg.gdir = readl(port->base + GPIO_GDIR);
5418c2ecf20Sopenharmony_ci	port->gpio_saved_reg.edge_sel = readl(port->base + GPIO_EDGE_SEL);
5428c2ecf20Sopenharmony_ci	port->gpio_saved_reg.dr = readl(port->base + GPIO_DR);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	if (!port->power_off)
5488c2ecf20Sopenharmony_ci		return;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.icr1, port->base + GPIO_ICR1);
5518c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.icr2, port->base + GPIO_ICR2);
5528c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.imr, port->base + GPIO_IMR);
5538c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.gdir, port->base + GPIO_GDIR);
5548c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.edge_sel, port->base + GPIO_EDGE_SEL);
5558c2ecf20Sopenharmony_ci	writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic int mxc_gpio_syscore_suspend(void)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* walk through all ports */
5638c2ecf20Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
5648c2ecf20Sopenharmony_ci		mxc_gpio_save_regs(port);
5658c2ecf20Sopenharmony_ci		clk_disable_unprepare(port->clk);
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void mxc_gpio_syscore_resume(void)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct mxc_gpio_port *port;
5748c2ecf20Sopenharmony_ci	int ret;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* walk through all ports */
5778c2ecf20Sopenharmony_ci	list_for_each_entry(port, &mxc_gpio_ports, node) {
5788c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(port->clk);
5798c2ecf20Sopenharmony_ci		if (ret) {
5808c2ecf20Sopenharmony_ci			pr_err("mxc: failed to enable gpio clock %d\n", ret);
5818c2ecf20Sopenharmony_ci			return;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci		mxc_gpio_restore_regs(port);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic struct syscore_ops mxc_gpio_syscore_ops = {
5888c2ecf20Sopenharmony_ci	.suspend = mxc_gpio_syscore_suspend,
5898c2ecf20Sopenharmony_ci	.resume = mxc_gpio_syscore_resume,
5908c2ecf20Sopenharmony_ci};
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic struct platform_driver mxc_gpio_driver = {
5938c2ecf20Sopenharmony_ci	.driver		= {
5948c2ecf20Sopenharmony_ci		.name	= "gpio-mxc",
5958c2ecf20Sopenharmony_ci		.of_match_table = mxc_gpio_dt_ids,
5968c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
5978c2ecf20Sopenharmony_ci	},
5988c2ecf20Sopenharmony_ci	.probe		= mxc_gpio_probe,
5998c2ecf20Sopenharmony_ci	.id_table	= mxc_gpio_devtype,
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int __init gpio_mxc_init(void)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	register_syscore_ops(&mxc_gpio_syscore_ops);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return platform_driver_register(&mxc_gpio_driver);
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_cisubsys_initcall(gpio_mxc_init);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
6118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i.MX GPIO Driver");
6128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
613