18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * SPEAr platform PLGPIO driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics
58c2ecf20Sopenharmony_ci * Viresh Kumar <viresh.kumar@linaro.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
88c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
198c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/pm.h>
228c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MAX_GPIO_PER_REG		32
258c2ecf20Sopenharmony_ci#define PIN_OFFSET(pin)			(pin % MAX_GPIO_PER_REG)
268c2ecf20Sopenharmony_ci#define REG_OFFSET(base, reg, pin)	(base + reg + (pin / MAX_GPIO_PER_REG) \
278c2ecf20Sopenharmony_ci							* sizeof(int *))
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * plgpio pins in all machines are not one to one mapped, bitwise with registers
318c2ecf20Sopenharmony_ci * bits. These set of macros define register masks for which below functions
328c2ecf20Sopenharmony_ci * (pin_to_offset and offset_to_pin) are required to be called.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#define PTO_ENB_REG		0x001
358c2ecf20Sopenharmony_ci#define PTO_WDATA_REG		0x002
368c2ecf20Sopenharmony_ci#define PTO_DIR_REG		0x004
378c2ecf20Sopenharmony_ci#define PTO_IE_REG		0x008
388c2ecf20Sopenharmony_ci#define PTO_RDATA_REG		0x010
398c2ecf20Sopenharmony_ci#define PTO_MIS_REG		0x020
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct plgpio_regs {
428c2ecf20Sopenharmony_ci	u32 enb;		/* enable register */
438c2ecf20Sopenharmony_ci	u32 wdata;		/* write data register */
448c2ecf20Sopenharmony_ci	u32 dir;		/* direction set register */
458c2ecf20Sopenharmony_ci	u32 rdata;		/* read data register */
468c2ecf20Sopenharmony_ci	u32 ie;			/* interrupt enable register */
478c2ecf20Sopenharmony_ci	u32 mis;		/* mask interrupt status register */
488c2ecf20Sopenharmony_ci	u32 eit;		/* edge interrupt type */
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * struct plgpio: plgpio driver specific structure
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * lock: lock for guarding gpio registers
558c2ecf20Sopenharmony_ci * base: base address of plgpio block
568c2ecf20Sopenharmony_ci * chip: gpio framework specific chip information structure
578c2ecf20Sopenharmony_ci * p2o: function ptr for pin to offset conversion. This is required only for
588c2ecf20Sopenharmony_ci *	machines where mapping b/w pin and offset is not 1-to-1.
598c2ecf20Sopenharmony_ci * o2p: function ptr for offset to pin conversion. This is required only for
608c2ecf20Sopenharmony_ci *	machines where mapping b/w pin and offset is not 1-to-1.
618c2ecf20Sopenharmony_ci * p2o_regs: mask of registers for which p2o and o2p are applicable
628c2ecf20Sopenharmony_ci * regs: register offsets
638c2ecf20Sopenharmony_ci * csave_regs: context save registers for standby/sleep/hibernate cases
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistruct plgpio {
668c2ecf20Sopenharmony_ci	spinlock_t		lock;
678c2ecf20Sopenharmony_ci	void __iomem		*base;
688c2ecf20Sopenharmony_ci	struct clk		*clk;
698c2ecf20Sopenharmony_ci	struct gpio_chip	chip;
708c2ecf20Sopenharmony_ci	int			(*p2o)(int pin);	/* pin_to_offset */
718c2ecf20Sopenharmony_ci	int			(*o2p)(int offset);	/* offset_to_pin */
728c2ecf20Sopenharmony_ci	u32			p2o_regs;
738c2ecf20Sopenharmony_ci	struct plgpio_regs	regs;
748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
758c2ecf20Sopenharmony_ci	struct plgpio_regs	*csave_regs;
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* register manipulation inline functions */
808c2ecf20Sopenharmony_cistatic inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	u32 offset = PIN_OFFSET(pin);
838c2ecf20Sopenharmony_ci	void __iomem *reg_off = REG_OFFSET(base, reg, pin);
848c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(reg_off);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return !!(val & (1 << offset));
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	u32 offset = PIN_OFFSET(pin);
928c2ecf20Sopenharmony_ci	void __iomem *reg_off = REG_OFFSET(base, reg, pin);
938c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(reg_off);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	writel_relaxed(val | (1 << offset), reg_off);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	u32 offset = PIN_OFFSET(pin);
1018c2ecf20Sopenharmony_ci	void __iomem *reg_off = REG_OFFSET(base, reg, pin);
1028c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(reg_off);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	writel_relaxed(val & ~(1 << offset), reg_off);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* gpio framework specific routines */
1088c2ecf20Sopenharmony_cistatic int plgpio_direction_input(struct gpio_chip *chip, unsigned offset)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
1118c2ecf20Sopenharmony_ci	unsigned long flags;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
1148c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) {
1158c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
1168c2ecf20Sopenharmony_ci		if (offset == -1)
1178c2ecf20Sopenharmony_ci			return -EINVAL;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
1218c2ecf20Sopenharmony_ci	plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir);
1228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int plgpio_direction_output(struct gpio_chip *chip, unsigned offset,
1288c2ecf20Sopenharmony_ci		int value)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
1318c2ecf20Sopenharmony_ci	unsigned long flags;
1328c2ecf20Sopenharmony_ci	unsigned dir_offset = offset, wdata_offset = offset, tmp;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
1358c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) {
1368c2ecf20Sopenharmony_ci		tmp = plgpio->p2o(offset);
1378c2ecf20Sopenharmony_ci		if (tmp == -1)
1388c2ecf20Sopenharmony_ci			return -EINVAL;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		if (plgpio->p2o_regs & PTO_DIR_REG)
1418c2ecf20Sopenharmony_ci			dir_offset = tmp;
1428c2ecf20Sopenharmony_ci		if (plgpio->p2o_regs & PTO_WDATA_REG)
1438c2ecf20Sopenharmony_ci			wdata_offset = tmp;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
1478c2ecf20Sopenharmony_ci	if (value)
1488c2ecf20Sopenharmony_ci		plgpio_reg_set(plgpio->base, wdata_offset,
1498c2ecf20Sopenharmony_ci				plgpio->regs.wdata);
1508c2ecf20Sopenharmony_ci	else
1518c2ecf20Sopenharmony_ci		plgpio_reg_reset(plgpio->base, wdata_offset,
1528c2ecf20Sopenharmony_ci				plgpio->regs.wdata);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir);
1558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int plgpio_get_value(struct gpio_chip *chip, unsigned offset)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (offset >= chip->ngpio)
1658c2ecf20Sopenharmony_ci		return -EINVAL;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
1688c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) {
1698c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
1708c2ecf20Sopenharmony_ci		if (offset == -1)
1718c2ecf20Sopenharmony_ci			return -EINVAL;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (offset >= chip->ngpio)
1828c2ecf20Sopenharmony_ci		return;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
1858c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) {
1868c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
1878c2ecf20Sopenharmony_ci		if (offset == -1)
1888c2ecf20Sopenharmony_ci			return;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (value)
1928c2ecf20Sopenharmony_ci		plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata);
1938c2ecf20Sopenharmony_ci	else
1948c2ecf20Sopenharmony_ci		plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int plgpio_request(struct gpio_chip *chip, unsigned offset)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
2008c2ecf20Sopenharmony_ci	int gpio = chip->base + offset;
2018c2ecf20Sopenharmony_ci	unsigned long flags;
2028c2ecf20Sopenharmony_ci	int ret = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (offset >= chip->ngpio)
2058c2ecf20Sopenharmony_ci		return -EINVAL;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = pinctrl_gpio_request(gpio);
2088c2ecf20Sopenharmony_ci	if (ret)
2098c2ecf20Sopenharmony_ci		return ret;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (!IS_ERR(plgpio->clk)) {
2128c2ecf20Sopenharmony_ci		ret = clk_enable(plgpio->clk);
2138c2ecf20Sopenharmony_ci		if (ret)
2148c2ecf20Sopenharmony_ci			goto err0;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (plgpio->regs.enb == -1)
2188c2ecf20Sopenharmony_ci		return 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * put gpio in IN mode before enabling it. This make enabling gpio safe
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	ret = plgpio_direction_input(chip, offset);
2248c2ecf20Sopenharmony_ci	if (ret)
2258c2ecf20Sopenharmony_ci		goto err1;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
2288c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
2298c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
2308c2ecf20Sopenharmony_ci		if (offset == -1) {
2318c2ecf20Sopenharmony_ci			ret = -EINVAL;
2328c2ecf20Sopenharmony_ci			goto err1;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
2378c2ecf20Sopenharmony_ci	plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb);
2388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cierr1:
2428c2ecf20Sopenharmony_ci	if (!IS_ERR(plgpio->clk))
2438c2ecf20Sopenharmony_ci		clk_disable(plgpio->clk);
2448c2ecf20Sopenharmony_cierr0:
2458c2ecf20Sopenharmony_ci	pinctrl_gpio_free(gpio);
2468c2ecf20Sopenharmony_ci	return ret;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void plgpio_free(struct gpio_chip *chip, unsigned offset)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(chip);
2528c2ecf20Sopenharmony_ci	int gpio = chip->base + offset;
2538c2ecf20Sopenharmony_ci	unsigned long flags;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (offset >= chip->ngpio)
2568c2ecf20Sopenharmony_ci		return;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (plgpio->regs.enb == -1)
2598c2ecf20Sopenharmony_ci		goto disable_clk;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
2628c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) {
2638c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
2648c2ecf20Sopenharmony_ci		if (offset == -1)
2658c2ecf20Sopenharmony_ci			return;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
2698c2ecf20Sopenharmony_ci	plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb);
2708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cidisable_clk:
2738c2ecf20Sopenharmony_ci	if (!IS_ERR(plgpio->clk))
2748c2ecf20Sopenharmony_ci		clk_disable(plgpio->clk);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	pinctrl_gpio_free(gpio);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/* PLGPIO IRQ */
2808c2ecf20Sopenharmony_cistatic void plgpio_irq_disable(struct irq_data *d)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
2838c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(gc);
2848c2ecf20Sopenharmony_ci	int offset = d->hwirq;
2858c2ecf20Sopenharmony_ci	unsigned long flags;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
2888c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
2898c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
2908c2ecf20Sopenharmony_ci		if (offset == -1)
2918c2ecf20Sopenharmony_ci			return;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
2958c2ecf20Sopenharmony_ci	plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie);
2968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void plgpio_irq_enable(struct irq_data *d)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
3028c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(gc);
3038c2ecf20Sopenharmony_ci	int offset = d->hwirq;
3048c2ecf20Sopenharmony_ci	unsigned long flags;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* get correct offset for "offset" pin */
3078c2ecf20Sopenharmony_ci	if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) {
3088c2ecf20Sopenharmony_ci		offset = plgpio->p2o(offset);
3098c2ecf20Sopenharmony_ci		if (offset == -1)
3108c2ecf20Sopenharmony_ci			return;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&plgpio->lock, flags);
3148c2ecf20Sopenharmony_ci	plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie);
3158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&plgpio->lock, flags);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int plgpio_irq_set_type(struct irq_data *d, unsigned trigger)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
3218c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(gc);
3228c2ecf20Sopenharmony_ci	int offset = d->hwirq;
3238c2ecf20Sopenharmony_ci	void __iomem *reg_off;
3248c2ecf20Sopenharmony_ci	unsigned int supported_type = 0, val;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (offset >= plgpio->chip.ngpio)
3278c2ecf20Sopenharmony_ci		return -EINVAL;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (plgpio->regs.eit == -1)
3308c2ecf20Sopenharmony_ci		supported_type = IRQ_TYPE_LEVEL_HIGH;
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		supported_type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!(trigger & supported_type))
3358c2ecf20Sopenharmony_ci		return -EINVAL;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (plgpio->regs.eit == -1)
3388c2ecf20Sopenharmony_ci		return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	reg_off = REG_OFFSET(plgpio->base, plgpio->regs.eit, offset);
3418c2ecf20Sopenharmony_ci	val = readl_relaxed(reg_off);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	offset = PIN_OFFSET(offset);
3448c2ecf20Sopenharmony_ci	if (trigger & IRQ_TYPE_EDGE_RISING)
3458c2ecf20Sopenharmony_ci		writel_relaxed(val | (1 << offset), reg_off);
3468c2ecf20Sopenharmony_ci	else
3478c2ecf20Sopenharmony_ci		writel_relaxed(val & ~(1 << offset), reg_off);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic struct irq_chip plgpio_irqchip = {
3538c2ecf20Sopenharmony_ci	.name		= "PLGPIO",
3548c2ecf20Sopenharmony_ci	.irq_enable	= plgpio_irq_enable,
3558c2ecf20Sopenharmony_ci	.irq_disable	= plgpio_irq_disable,
3568c2ecf20Sopenharmony_ci	.irq_set_type	= plgpio_irq_set_type,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void plgpio_irq_handler(struct irq_desc *desc)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
3628c2ecf20Sopenharmony_ci	struct plgpio *plgpio = gpiochip_get_data(gc);
3638c2ecf20Sopenharmony_ci	struct irq_chip *irqchip = irq_desc_get_chip(desc);
3648c2ecf20Sopenharmony_ci	int regs_count, count, pin, offset, i = 0;
3658c2ecf20Sopenharmony_ci	unsigned long pending;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	count = plgpio->chip.ngpio;
3688c2ecf20Sopenharmony_ci	regs_count = DIV_ROUND_UP(count, MAX_GPIO_PER_REG);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	chained_irq_enter(irqchip, desc);
3718c2ecf20Sopenharmony_ci	/* check all plgpio MIS registers for a possible interrupt */
3728c2ecf20Sopenharmony_ci	for (; i < regs_count; i++) {
3738c2ecf20Sopenharmony_ci		pending = readl_relaxed(plgpio->base + plgpio->regs.mis +
3748c2ecf20Sopenharmony_ci				i * sizeof(int *));
3758c2ecf20Sopenharmony_ci		if (!pending)
3768c2ecf20Sopenharmony_ci			continue;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		/* clear interrupts */
3798c2ecf20Sopenharmony_ci		writel_relaxed(~pending, plgpio->base + plgpio->regs.mis +
3808c2ecf20Sopenharmony_ci				i * sizeof(int *));
3818c2ecf20Sopenharmony_ci		/*
3828c2ecf20Sopenharmony_ci		 * clear extra bits in last register having gpios < MAX/REG
3838c2ecf20Sopenharmony_ci		 * ex: Suppose there are max 102 plgpios. then last register
3848c2ecf20Sopenharmony_ci		 * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits
3858c2ecf20Sopenharmony_ci		 * so, we must not take other 28 bits into consideration for
3868c2ecf20Sopenharmony_ci		 * checking interrupt. so clear those bits.
3878c2ecf20Sopenharmony_ci		 */
3888c2ecf20Sopenharmony_ci		count = count - i * MAX_GPIO_PER_REG;
3898c2ecf20Sopenharmony_ci		if (count < MAX_GPIO_PER_REG)
3908c2ecf20Sopenharmony_ci			pending &= (1 << count) - 1;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) {
3938c2ecf20Sopenharmony_ci			/* get correct pin for "offset" */
3948c2ecf20Sopenharmony_ci			if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) {
3958c2ecf20Sopenharmony_ci				pin = plgpio->o2p(offset);
3968c2ecf20Sopenharmony_ci				if (pin == -1)
3978c2ecf20Sopenharmony_ci					continue;
3988c2ecf20Sopenharmony_ci			} else
3998c2ecf20Sopenharmony_ci				pin = offset;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci			/* get correct irq line number */
4028c2ecf20Sopenharmony_ci			pin = i * MAX_GPIO_PER_REG + pin;
4038c2ecf20Sopenharmony_ci			generic_handle_irq(
4048c2ecf20Sopenharmony_ci				irq_find_mapping(gc->irq.domain, pin));
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	chained_irq_exit(irqchip, desc);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/*
4118c2ecf20Sopenharmony_ci * pin to offset and offset to pin converter functions
4128c2ecf20Sopenharmony_ci *
4138c2ecf20Sopenharmony_ci * In spear310 there is inconsistency among bit positions in plgpio regiseters,
4148c2ecf20Sopenharmony_ci * for different plgpio pins. For example: for pin 27, bit offset is 23, pin
4158c2ecf20Sopenharmony_ci * 28-33 are not supported, pin 95 has offset bit 95, bit 100 has offset bit 1
4168c2ecf20Sopenharmony_ci */
4178c2ecf20Sopenharmony_cistatic int spear310_p2o(int pin)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	int offset = pin;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (pin <= 27)
4228c2ecf20Sopenharmony_ci		offset += 4;
4238c2ecf20Sopenharmony_ci	else if (pin <= 33)
4248c2ecf20Sopenharmony_ci		offset = -1;
4258c2ecf20Sopenharmony_ci	else if (pin <= 97)
4268c2ecf20Sopenharmony_ci		offset -= 2;
4278c2ecf20Sopenharmony_ci	else if (pin <= 101)
4288c2ecf20Sopenharmony_ci		offset = 101 - pin;
4298c2ecf20Sopenharmony_ci	else
4308c2ecf20Sopenharmony_ci		offset = -1;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return offset;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int spear310_o2p(int offset)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	if (offset <= 3)
4388c2ecf20Sopenharmony_ci		return 101 - offset;
4398c2ecf20Sopenharmony_ci	else if (offset <= 31)
4408c2ecf20Sopenharmony_ci		return offset - 4;
4418c2ecf20Sopenharmony_ci	else
4428c2ecf20Sopenharmony_ci		return offset + 2;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic int plgpio_probe_dt(struct platform_device *pdev, struct plgpio *plgpio)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
4488c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4498c2ecf20Sopenharmony_ci	u32 val;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("st,spear310")) {
4528c2ecf20Sopenharmony_ci		plgpio->p2o = spear310_p2o;
4538c2ecf20Sopenharmony_ci		plgpio->o2p = spear310_o2p;
4548c2ecf20Sopenharmony_ci		plgpio->p2o_regs = PTO_WDATA_REG | PTO_DIR_REG | PTO_IE_REG |
4558c2ecf20Sopenharmony_ci			PTO_RDATA_REG | PTO_MIS_REG;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,ngpio", &val)) {
4598c2ecf20Sopenharmony_ci		plgpio->chip.ngpio = val;
4608c2ecf20Sopenharmony_ci	} else {
4618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid ngpio field\n");
4628c2ecf20Sopenharmony_ci		goto end;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,enb-reg", &val))
4668c2ecf20Sopenharmony_ci		plgpio->regs.enb = val;
4678c2ecf20Sopenharmony_ci	else
4688c2ecf20Sopenharmony_ci		plgpio->regs.enb = -1;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,wdata-reg", &val)) {
4718c2ecf20Sopenharmony_ci		plgpio->regs.wdata = val;
4728c2ecf20Sopenharmony_ci	} else {
4738c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid wdata reg\n");
4748c2ecf20Sopenharmony_ci		goto end;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,dir-reg", &val)) {
4788c2ecf20Sopenharmony_ci		plgpio->regs.dir = val;
4798c2ecf20Sopenharmony_ci	} else {
4808c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid dir reg\n");
4818c2ecf20Sopenharmony_ci		goto end;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,ie-reg", &val)) {
4858c2ecf20Sopenharmony_ci		plgpio->regs.ie = val;
4868c2ecf20Sopenharmony_ci	} else {
4878c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid ie reg\n");
4888c2ecf20Sopenharmony_ci		goto end;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,rdata-reg", &val)) {
4928c2ecf20Sopenharmony_ci		plgpio->regs.rdata = val;
4938c2ecf20Sopenharmony_ci	} else {
4948c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid rdata reg\n");
4958c2ecf20Sopenharmony_ci		goto end;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,mis-reg", &val)) {
4998c2ecf20Sopenharmony_ci		plgpio->regs.mis = val;
5008c2ecf20Sopenharmony_ci	} else {
5018c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT: Invalid mis reg\n");
5028c2ecf20Sopenharmony_ci		goto end;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "st-plgpio,eit-reg", &val))
5068c2ecf20Sopenharmony_ci		plgpio->regs.eit = val;
5078c2ecf20Sopenharmony_ci	else
5088c2ecf20Sopenharmony_ci		plgpio->regs.eit = -1;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	return 0;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ciend:
5138c2ecf20Sopenharmony_ci	return ret;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_cistatic int plgpio_probe(struct platform_device *pdev)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct plgpio *plgpio;
5188c2ecf20Sopenharmony_ci	int ret, irq;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	plgpio = devm_kzalloc(&pdev->dev, sizeof(*plgpio), GFP_KERNEL);
5218c2ecf20Sopenharmony_ci	if (!plgpio)
5228c2ecf20Sopenharmony_ci		return -ENOMEM;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	plgpio->base = devm_platform_ioremap_resource(pdev, 0);
5258c2ecf20Sopenharmony_ci	if (IS_ERR(plgpio->base))
5268c2ecf20Sopenharmony_ci		return PTR_ERR(plgpio->base);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	ret = plgpio_probe_dt(pdev, plgpio);
5298c2ecf20Sopenharmony_ci	if (ret) {
5308c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "DT probe failed\n");
5318c2ecf20Sopenharmony_ci		return ret;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	plgpio->clk = devm_clk_get(&pdev->dev, NULL);
5358c2ecf20Sopenharmony_ci	if (IS_ERR(plgpio->clk))
5368c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "clk_get() failed, work without it\n");
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5398c2ecf20Sopenharmony_ci	plgpio->csave_regs = devm_kcalloc(&pdev->dev,
5408c2ecf20Sopenharmony_ci			DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG),
5418c2ecf20Sopenharmony_ci			sizeof(*plgpio->csave_regs),
5428c2ecf20Sopenharmony_ci			GFP_KERNEL);
5438c2ecf20Sopenharmony_ci	if (!plgpio->csave_regs)
5448c2ecf20Sopenharmony_ci		return -ENOMEM;
5458c2ecf20Sopenharmony_ci#endif
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, plgpio);
5488c2ecf20Sopenharmony_ci	spin_lock_init(&plgpio->lock);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	plgpio->chip.base = -1;
5518c2ecf20Sopenharmony_ci	plgpio->chip.request = plgpio_request;
5528c2ecf20Sopenharmony_ci	plgpio->chip.free = plgpio_free;
5538c2ecf20Sopenharmony_ci	plgpio->chip.direction_input = plgpio_direction_input;
5548c2ecf20Sopenharmony_ci	plgpio->chip.direction_output = plgpio_direction_output;
5558c2ecf20Sopenharmony_ci	plgpio->chip.get = plgpio_get_value;
5568c2ecf20Sopenharmony_ci	plgpio->chip.set = plgpio_set_value;
5578c2ecf20Sopenharmony_ci	plgpio->chip.label = dev_name(&pdev->dev);
5588c2ecf20Sopenharmony_ci	plgpio->chip.parent = &pdev->dev;
5598c2ecf20Sopenharmony_ci	plgpio->chip.owner = THIS_MODULE;
5608c2ecf20Sopenharmony_ci	plgpio->chip.of_node = pdev->dev.of_node;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (!IS_ERR(plgpio->clk)) {
5638c2ecf20Sopenharmony_ci		ret = clk_prepare(plgpio->clk);
5648c2ecf20Sopenharmony_ci		if (ret) {
5658c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "clk prepare failed\n");
5668c2ecf20Sopenharmony_ci			return ret;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
5718c2ecf20Sopenharmony_ci	if (irq > 0) {
5728c2ecf20Sopenharmony_ci		struct gpio_irq_chip *girq;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		girq = &plgpio->chip.irq;
5758c2ecf20Sopenharmony_ci		girq->chip = &plgpio_irqchip;
5768c2ecf20Sopenharmony_ci		girq->parent_handler = plgpio_irq_handler;
5778c2ecf20Sopenharmony_ci		girq->num_parents = 1;
5788c2ecf20Sopenharmony_ci		girq->parents = devm_kcalloc(&pdev->dev, 1,
5798c2ecf20Sopenharmony_ci					     sizeof(*girq->parents),
5808c2ecf20Sopenharmony_ci					     GFP_KERNEL);
5818c2ecf20Sopenharmony_ci		if (!girq->parents)
5828c2ecf20Sopenharmony_ci			return -ENOMEM;
5838c2ecf20Sopenharmony_ci		girq->parents[0] = irq;
5848c2ecf20Sopenharmony_ci		girq->default_type = IRQ_TYPE_NONE;
5858c2ecf20Sopenharmony_ci		girq->handler = handle_simple_irq;
5868c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "PLGPIO registering with IRQs\n");
5878c2ecf20Sopenharmony_ci	} else {
5888c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "PLGPIO registering without IRQs\n");
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	ret = gpiochip_add_data(&plgpio->chip, plgpio);
5928c2ecf20Sopenharmony_ci	if (ret) {
5938c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to add gpio chip\n");
5948c2ecf20Sopenharmony_ci		goto unprepare_clk;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return 0;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciunprepare_clk:
6008c2ecf20Sopenharmony_ci	if (!IS_ERR(plgpio->clk))
6018c2ecf20Sopenharmony_ci		clk_unprepare(plgpio->clk);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return ret;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6078c2ecf20Sopenharmony_cistatic int plgpio_suspend(struct device *dev)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct plgpio *plgpio = dev_get_drvdata(dev);
6108c2ecf20Sopenharmony_ci	int i, reg_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG);
6118c2ecf20Sopenharmony_ci	void __iomem *off;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	for (i = 0; i < reg_count; i++) {
6148c2ecf20Sopenharmony_ci		off = plgpio->base + i * sizeof(int *);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		if (plgpio->regs.enb != -1)
6178c2ecf20Sopenharmony_ci			plgpio->csave_regs[i].enb =
6188c2ecf20Sopenharmony_ci				readl_relaxed(plgpio->regs.enb + off);
6198c2ecf20Sopenharmony_ci		if (plgpio->regs.eit != -1)
6208c2ecf20Sopenharmony_ci			plgpio->csave_regs[i].eit =
6218c2ecf20Sopenharmony_ci				readl_relaxed(plgpio->regs.eit + off);
6228c2ecf20Sopenharmony_ci		plgpio->csave_regs[i].wdata = readl_relaxed(plgpio->regs.wdata +
6238c2ecf20Sopenharmony_ci				off);
6248c2ecf20Sopenharmony_ci		plgpio->csave_regs[i].dir = readl_relaxed(plgpio->regs.dir +
6258c2ecf20Sopenharmony_ci				off);
6268c2ecf20Sopenharmony_ci		plgpio->csave_regs[i].ie = readl_relaxed(plgpio->regs.ie + off);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/*
6338c2ecf20Sopenharmony_ci * This is used to correct the values in end registers. End registers contain
6348c2ecf20Sopenharmony_ci * extra bits that might be used for other purpose in platform. So, we shouldn't
6358c2ecf20Sopenharmony_ci * overwrite these bits. This macro, reads given register again, preserves other
6368c2ecf20Sopenharmony_ci * bit values (non-plgpio bits), and retain captured value (plgpio bits).
6378c2ecf20Sopenharmony_ci */
6388c2ecf20Sopenharmony_ci#define plgpio_prepare_reg(__reg, _off, _mask, _tmp)		\
6398c2ecf20Sopenharmony_ci{								\
6408c2ecf20Sopenharmony_ci	_tmp = readl_relaxed(plgpio->regs.__reg + _off);		\
6418c2ecf20Sopenharmony_ci	_tmp &= ~_mask;						\
6428c2ecf20Sopenharmony_ci	plgpio->csave_regs[i].__reg =				\
6438c2ecf20Sopenharmony_ci		_tmp | (plgpio->csave_regs[i].__reg & _mask);	\
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int plgpio_resume(struct device *dev)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct plgpio *plgpio = dev_get_drvdata(dev);
6498c2ecf20Sopenharmony_ci	int i, reg_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG);
6508c2ecf20Sopenharmony_ci	void __iomem *off;
6518c2ecf20Sopenharmony_ci	u32 mask, tmp;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	for (i = 0; i < reg_count; i++) {
6548c2ecf20Sopenharmony_ci		off = plgpio->base + i * sizeof(int *);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		if (i == reg_count - 1) {
6578c2ecf20Sopenharmony_ci			mask = (1 << (plgpio->chip.ngpio - i *
6588c2ecf20Sopenharmony_ci						MAX_GPIO_PER_REG)) - 1;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci			if (plgpio->regs.enb != -1)
6618c2ecf20Sopenharmony_ci				plgpio_prepare_reg(enb, off, mask, tmp);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci			if (plgpio->regs.eit != -1)
6648c2ecf20Sopenharmony_ci				plgpio_prepare_reg(eit, off, mask, tmp);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci			plgpio_prepare_reg(wdata, off, mask, tmp);
6678c2ecf20Sopenharmony_ci			plgpio_prepare_reg(dir, off, mask, tmp);
6688c2ecf20Sopenharmony_ci			plgpio_prepare_reg(ie, off, mask, tmp);
6698c2ecf20Sopenharmony_ci		}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		writel_relaxed(plgpio->csave_regs[i].wdata, plgpio->regs.wdata +
6728c2ecf20Sopenharmony_ci				off);
6738c2ecf20Sopenharmony_ci		writel_relaxed(plgpio->csave_regs[i].dir, plgpio->regs.dir +
6748c2ecf20Sopenharmony_ci				off);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		if (plgpio->regs.eit != -1)
6778c2ecf20Sopenharmony_ci			writel_relaxed(plgpio->csave_regs[i].eit,
6788c2ecf20Sopenharmony_ci					plgpio->regs.eit + off);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		writel_relaxed(plgpio->csave_regs[i].ie, plgpio->regs.ie + off);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		if (plgpio->regs.enb != -1)
6838c2ecf20Sopenharmony_ci			writel_relaxed(plgpio->csave_regs[i].enb,
6848c2ecf20Sopenharmony_ci					plgpio->regs.enb + off);
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	return 0;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci#endif
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(plgpio_dev_pm_ops, plgpio_suspend, plgpio_resume);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic const struct of_device_id plgpio_of_match[] = {
6948c2ecf20Sopenharmony_ci	{ .compatible = "st,spear-plgpio" },
6958c2ecf20Sopenharmony_ci	{}
6968c2ecf20Sopenharmony_ci};
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic struct platform_driver plgpio_driver = {
6998c2ecf20Sopenharmony_ci	.probe = plgpio_probe,
7008c2ecf20Sopenharmony_ci	.driver = {
7018c2ecf20Sopenharmony_ci		.name = "spear-plgpio",
7028c2ecf20Sopenharmony_ci		.pm = &plgpio_dev_pm_ops,
7038c2ecf20Sopenharmony_ci		.of_match_table = plgpio_of_match,
7048c2ecf20Sopenharmony_ci	},
7058c2ecf20Sopenharmony_ci};
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic int __init plgpio_init(void)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	return platform_driver_register(&plgpio_driver);
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_cisubsys_initcall(plgpio_init);
712