162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2013 MundoReader S.L.
462306a36Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_address.h>
2062306a36Sopenharmony_ci#include <linux/of_irq.h>
2162306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
2262306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
2362306a36Sopenharmony_ci#include <linux/platform_device.h>
2462306a36Sopenharmony_ci#include <linux/regmap.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "../pinctrl/core.h"
2762306a36Sopenharmony_ci#include "../pinctrl/pinctrl-rockchip.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define GPIO_TYPE_V1		(0)           /* GPIO Version ID reserved */
3062306a36Sopenharmony_ci#define GPIO_TYPE_V2		(0x01000C2B)  /* GPIO Version ID 0x01000C2B */
3162306a36Sopenharmony_ci#define GPIO_TYPE_V2_1		(0x0101157C)  /* GPIO Version ID 0x0101157C */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v1 = {
3462306a36Sopenharmony_ci	.port_dr = 0x00,
3562306a36Sopenharmony_ci	.port_ddr = 0x04,
3662306a36Sopenharmony_ci	.int_en = 0x30,
3762306a36Sopenharmony_ci	.int_mask = 0x34,
3862306a36Sopenharmony_ci	.int_type = 0x38,
3962306a36Sopenharmony_ci	.int_polarity = 0x3c,
4062306a36Sopenharmony_ci	.int_status = 0x40,
4162306a36Sopenharmony_ci	.int_rawstatus = 0x44,
4262306a36Sopenharmony_ci	.debounce = 0x48,
4362306a36Sopenharmony_ci	.port_eoi = 0x4c,
4462306a36Sopenharmony_ci	.ext_port = 0x50,
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct rockchip_gpio_regs gpio_regs_v2 = {
4862306a36Sopenharmony_ci	.port_dr = 0x00,
4962306a36Sopenharmony_ci	.port_ddr = 0x08,
5062306a36Sopenharmony_ci	.int_en = 0x10,
5162306a36Sopenharmony_ci	.int_mask = 0x18,
5262306a36Sopenharmony_ci	.int_type = 0x20,
5362306a36Sopenharmony_ci	.int_polarity = 0x28,
5462306a36Sopenharmony_ci	.int_bothedge = 0x30,
5562306a36Sopenharmony_ci	.int_status = 0x50,
5662306a36Sopenharmony_ci	.int_rawstatus = 0x58,
5762306a36Sopenharmony_ci	.debounce = 0x38,
5862306a36Sopenharmony_ci	.dbclk_div_en = 0x40,
5962306a36Sopenharmony_ci	.dbclk_div_con = 0x48,
6062306a36Sopenharmony_ci	.port_eoi = 0x60,
6162306a36Sopenharmony_ci	.ext_port = 0x70,
6262306a36Sopenharmony_ci	.version_id = 0x78,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic inline void gpio_writel_v2(u32 val, void __iomem *reg)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	writel((val & 0xffff) | 0xffff0000, reg);
6862306a36Sopenharmony_ci	writel((val >> 16) | 0xffff0000, reg + 0x4);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline u32 gpio_readl_v2(void __iomem *reg)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return readl(reg + 0x4) << 16 | readl(reg);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank,
7762306a36Sopenharmony_ci					u32 value, unsigned int offset)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	void __iomem *reg = bank->reg_base + offset;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2)
8262306a36Sopenharmony_ci		gpio_writel_v2(value, reg);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		writel(value, reg);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank,
8862306a36Sopenharmony_ci				      unsigned int offset)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	void __iomem *reg = bank->reg_base + offset;
9162306a36Sopenharmony_ci	u32 value;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2)
9462306a36Sopenharmony_ci		value = gpio_readl_v2(reg);
9562306a36Sopenharmony_ci	else
9662306a36Sopenharmony_ci		value = readl(reg);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return value;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank,
10262306a36Sopenharmony_ci					    u32 bit, u32 value,
10362306a36Sopenharmony_ci					    unsigned int offset)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	void __iomem *reg = bank->reg_base + offset;
10662306a36Sopenharmony_ci	u32 data;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2) {
10962306a36Sopenharmony_ci		if (value)
11062306a36Sopenharmony_ci			data = BIT(bit % 16) | BIT(bit % 16 + 16);
11162306a36Sopenharmony_ci		else
11262306a36Sopenharmony_ci			data = BIT(bit % 16 + 16);
11362306a36Sopenharmony_ci		writel(data, bit >= 16 ? reg + 0x4 : reg);
11462306a36Sopenharmony_ci	} else {
11562306a36Sopenharmony_ci		data = readl(reg);
11662306a36Sopenharmony_ci		data &= ~BIT(bit);
11762306a36Sopenharmony_ci		if (value)
11862306a36Sopenharmony_ci			data |= BIT(bit);
11962306a36Sopenharmony_ci		writel(data, reg);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank,
12462306a36Sopenharmony_ci					  u32 bit, unsigned int offset)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	void __iomem *reg = bank->reg_base + offset;
12762306a36Sopenharmony_ci	u32 data;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2) {
13062306a36Sopenharmony_ci		data = readl(bit >= 16 ? reg + 0x4 : reg);
13162306a36Sopenharmony_ci		data >>= bit % 16;
13262306a36Sopenharmony_ci	} else {
13362306a36Sopenharmony_ci		data = readl(reg);
13462306a36Sopenharmony_ci		data >>= bit;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return data & (0x1);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int rockchip_gpio_get_direction(struct gpio_chip *chip,
14162306a36Sopenharmony_ci				       unsigned int offset)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
14462306a36Sopenharmony_ci	u32 data;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
14762306a36Sopenharmony_ci	if (data)
14862306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int rockchip_gpio_set_direction(struct gpio_chip *chip,
15462306a36Sopenharmony_ci				       unsigned int offset, bool input)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
15762306a36Sopenharmony_ci	unsigned long flags;
15862306a36Sopenharmony_ci	u32 data = input ? 0 : 1;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (input)
16262306a36Sopenharmony_ci		pinctrl_gpio_direction_input(bank->pin_base + offset);
16362306a36Sopenharmony_ci	else
16462306a36Sopenharmony_ci		pinctrl_gpio_direction_output(bank->pin_base + offset);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->slock, flags);
16762306a36Sopenharmony_ci	rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
16862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->slock, flags);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
17462306a36Sopenharmony_ci			      int value)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
17762306a36Sopenharmony_ci	unsigned long flags;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->slock, flags);
18062306a36Sopenharmony_ci	rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);
18162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->slock, flags);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
18762306a36Sopenharmony_ci	u32 data;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	data = readl(bank->reg_base + bank->gpio_regs->ext_port);
19062306a36Sopenharmony_ci	data >>= offset;
19162306a36Sopenharmony_ci	data &= 1;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return data;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int rockchip_gpio_set_debounce(struct gpio_chip *gc,
19762306a36Sopenharmony_ci				      unsigned int offset,
19862306a36Sopenharmony_ci				      unsigned int debounce)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
20162306a36Sopenharmony_ci	const struct rockchip_gpio_regs	*reg = bank->gpio_regs;
20262306a36Sopenharmony_ci	unsigned long flags, div_reg, freq, max_debounce;
20362306a36Sopenharmony_ci	bool div_debounce_support;
20462306a36Sopenharmony_ci	unsigned int cur_div_reg;
20562306a36Sopenharmony_ci	u64 div;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2 && !IS_ERR(bank->db_clk)) {
20862306a36Sopenharmony_ci		div_debounce_support = true;
20962306a36Sopenharmony_ci		freq = clk_get_rate(bank->db_clk);
21062306a36Sopenharmony_ci		max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq;
21162306a36Sopenharmony_ci		if (debounce > max_debounce)
21262306a36Sopenharmony_ci			return -EINVAL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		div = debounce * freq;
21562306a36Sopenharmony_ci		div_reg = DIV_ROUND_CLOSEST_ULL(div, 2 * USEC_PER_SEC) - 1;
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		div_debounce_support = false;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->slock, flags);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Only the v1 needs to configure div_en and div_con for dbclk */
22362306a36Sopenharmony_ci	if (debounce) {
22462306a36Sopenharmony_ci		if (div_debounce_support) {
22562306a36Sopenharmony_ci			/* Configure the max debounce from consumers */
22662306a36Sopenharmony_ci			cur_div_reg = readl(bank->reg_base +
22762306a36Sopenharmony_ci					    reg->dbclk_div_con);
22862306a36Sopenharmony_ci			if (cur_div_reg < div_reg)
22962306a36Sopenharmony_ci				writel(div_reg, bank->reg_base +
23062306a36Sopenharmony_ci				       reg->dbclk_div_con);
23162306a36Sopenharmony_ci			rockchip_gpio_writel_bit(bank, offset, 1,
23262306a36Sopenharmony_ci						 reg->dbclk_div_en);
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		rockchip_gpio_writel_bit(bank, offset, 1, reg->debounce);
23662306a36Sopenharmony_ci	} else {
23762306a36Sopenharmony_ci		if (div_debounce_support)
23862306a36Sopenharmony_ci			rockchip_gpio_writel_bit(bank, offset, 0,
23962306a36Sopenharmony_ci						 reg->dbclk_div_en);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		rockchip_gpio_writel_bit(bank, offset, 0, reg->debounce);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->slock, flags);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Enable or disable dbclk at last */
24762306a36Sopenharmony_ci	if (div_debounce_support) {
24862306a36Sopenharmony_ci		if (debounce)
24962306a36Sopenharmony_ci			clk_prepare_enable(bank->db_clk);
25062306a36Sopenharmony_ci		else
25162306a36Sopenharmony_ci			clk_disable_unprepare(bank->db_clk);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int rockchip_gpio_direction_input(struct gpio_chip *gc,
25862306a36Sopenharmony_ci					 unsigned int offset)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	return rockchip_gpio_set_direction(gc, offset, true);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int rockchip_gpio_direction_output(struct gpio_chip *gc,
26462306a36Sopenharmony_ci					  unsigned int offset, int value)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	rockchip_gpio_set(gc, offset, value);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return rockchip_gpio_set_direction(gc, offset, false);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*
27262306a36Sopenharmony_ci * gpiolib set_config callback function. The setting of the pin
27362306a36Sopenharmony_ci * mux function as 'gpio output' will be handled by the pinctrl subsystem
27462306a36Sopenharmony_ci * interface.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
27762306a36Sopenharmony_ci				  unsigned long config)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	enum pin_config_param param = pinconf_to_config_param(config);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	switch (param) {
28262306a36Sopenharmony_ci	case PIN_CONFIG_INPUT_DEBOUNCE:
28362306a36Sopenharmony_ci		rockchip_gpio_set_debounce(gc, offset, true);
28462306a36Sopenharmony_ci		/*
28562306a36Sopenharmony_ci		 * Rockchip's gpio could only support up to one period
28662306a36Sopenharmony_ci		 * of the debounce clock(pclk), which is far away from
28762306a36Sopenharmony_ci		 * satisftying the requirement, as pclk is usually near
28862306a36Sopenharmony_ci		 * 100MHz shared by all peripherals. So the fact is it
28962306a36Sopenharmony_ci		 * has crippled debounce capability could only be useful
29062306a36Sopenharmony_ci		 * to prevent any spurious glitches from waking up the system
29162306a36Sopenharmony_ci		 * if the gpio is conguired as wakeup interrupt source. Let's
29262306a36Sopenharmony_ci		 * still return -ENOTSUPP as before, to make sure the caller
29362306a36Sopenharmony_ci		 * of gpiod_set_debounce won't change its behaviour.
29462306a36Sopenharmony_ci		 */
29562306a36Sopenharmony_ci		return -ENOTSUPP;
29662306a36Sopenharmony_ci	default:
29762306a36Sopenharmony_ci		return -ENOTSUPP;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/*
30262306a36Sopenharmony_ci * gpiod_to_irq() callback function. Creates a mapping between a GPIO pin
30362306a36Sopenharmony_ci * and a virtual IRQ, if not already present.
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_cistatic int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
30862306a36Sopenharmony_ci	unsigned int virq;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!bank->domain)
31162306a36Sopenharmony_ci		return -ENXIO;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	virq = irq_create_mapping(bank->domain, offset);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return (virq) ? : -ENXIO;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic const struct gpio_chip rockchip_gpiolib_chip = {
31962306a36Sopenharmony_ci	.request = gpiochip_generic_request,
32062306a36Sopenharmony_ci	.free = gpiochip_generic_free,
32162306a36Sopenharmony_ci	.set = rockchip_gpio_set,
32262306a36Sopenharmony_ci	.get = rockchip_gpio_get,
32362306a36Sopenharmony_ci	.get_direction	= rockchip_gpio_get_direction,
32462306a36Sopenharmony_ci	.direction_input = rockchip_gpio_direction_input,
32562306a36Sopenharmony_ci	.direction_output = rockchip_gpio_direction_output,
32662306a36Sopenharmony_ci	.set_config = rockchip_gpio_set_config,
32762306a36Sopenharmony_ci	.to_irq = rockchip_gpio_to_irq,
32862306a36Sopenharmony_ci	.owner = THIS_MODULE,
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void rockchip_irq_demux(struct irq_desc *desc)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
33462306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
33562306a36Sopenharmony_ci	unsigned long pending;
33662306a36Sopenharmony_ci	unsigned int irq;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	dev_dbg(bank->dev, "got irq for bank %s\n", bank->name);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	pending = readl_relaxed(bank->reg_base + bank->gpio_regs->int_status);
34362306a36Sopenharmony_ci	for_each_set_bit(irq, &pending, 32) {
34462306a36Sopenharmony_ci		dev_dbg(bank->dev, "handling irq %d\n", irq);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		/*
34762306a36Sopenharmony_ci		 * Triggering IRQ on both rising and falling edge
34862306a36Sopenharmony_ci		 * needs manual intervention.
34962306a36Sopenharmony_ci		 */
35062306a36Sopenharmony_ci		if (bank->toggle_edge_mode & BIT(irq)) {
35162306a36Sopenharmony_ci			u32 data, data_old, polarity;
35262306a36Sopenharmony_ci			unsigned long flags;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci			data = readl_relaxed(bank->reg_base +
35562306a36Sopenharmony_ci					     bank->gpio_regs->ext_port);
35662306a36Sopenharmony_ci			do {
35762306a36Sopenharmony_ci				raw_spin_lock_irqsave(&bank->slock, flags);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci				polarity = readl_relaxed(bank->reg_base +
36062306a36Sopenharmony_ci							 bank->gpio_regs->int_polarity);
36162306a36Sopenharmony_ci				if (data & BIT(irq))
36262306a36Sopenharmony_ci					polarity &= ~BIT(irq);
36362306a36Sopenharmony_ci				else
36462306a36Sopenharmony_ci					polarity |= BIT(irq);
36562306a36Sopenharmony_ci				writel(polarity,
36662306a36Sopenharmony_ci				       bank->reg_base +
36762306a36Sopenharmony_ci				       bank->gpio_regs->int_polarity);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci				raw_spin_unlock_irqrestore(&bank->slock, flags);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci				data_old = data;
37262306a36Sopenharmony_ci				data = readl_relaxed(bank->reg_base +
37362306a36Sopenharmony_ci						     bank->gpio_regs->ext_port);
37462306a36Sopenharmony_ci			} while ((data & BIT(irq)) != (data_old & BIT(irq)));
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		generic_handle_domain_irq(bank->domain, irq);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
38662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gc->private;
38762306a36Sopenharmony_ci	u32 mask = BIT(d->hwirq);
38862306a36Sopenharmony_ci	u32 polarity;
38962306a36Sopenharmony_ci	u32 level;
39062306a36Sopenharmony_ci	u32 data;
39162306a36Sopenharmony_ci	unsigned long flags;
39262306a36Sopenharmony_ci	int ret = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->slock, flags);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	rockchip_gpio_writel_bit(bank, d->hwirq, 0,
39762306a36Sopenharmony_ci				 bank->gpio_regs->port_ddr);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->slock, flags);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_BOTH)
40262306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
40362306a36Sopenharmony_ci	else
40462306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->slock, flags);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type);
40962306a36Sopenharmony_ci	polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (type == IRQ_TYPE_EDGE_BOTH) {
41262306a36Sopenharmony_ci		if (bank->gpio_type == GPIO_TYPE_V2) {
41362306a36Sopenharmony_ci			rockchip_gpio_writel_bit(bank, d->hwirq, 1,
41462306a36Sopenharmony_ci						 bank->gpio_regs->int_bothedge);
41562306a36Sopenharmony_ci			goto out;
41662306a36Sopenharmony_ci		} else {
41762306a36Sopenharmony_ci			bank->toggle_edge_mode |= mask;
41862306a36Sopenharmony_ci			level &= ~mask;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci			/*
42162306a36Sopenharmony_ci			 * Determine gpio state. If 1 next interrupt should be
42262306a36Sopenharmony_ci			 * low otherwise high.
42362306a36Sopenharmony_ci			 */
42462306a36Sopenharmony_ci			data = readl(bank->reg_base + bank->gpio_regs->ext_port);
42562306a36Sopenharmony_ci			if (data & mask)
42662306a36Sopenharmony_ci				polarity &= ~mask;
42762306a36Sopenharmony_ci			else
42862306a36Sopenharmony_ci				polarity |= mask;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci	} else {
43162306a36Sopenharmony_ci		if (bank->gpio_type == GPIO_TYPE_V2) {
43262306a36Sopenharmony_ci			rockchip_gpio_writel_bit(bank, d->hwirq, 0,
43362306a36Sopenharmony_ci						 bank->gpio_regs->int_bothedge);
43462306a36Sopenharmony_ci		} else {
43562306a36Sopenharmony_ci			bank->toggle_edge_mode &= ~mask;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci		switch (type) {
43862306a36Sopenharmony_ci		case IRQ_TYPE_EDGE_RISING:
43962306a36Sopenharmony_ci			level |= mask;
44062306a36Sopenharmony_ci			polarity |= mask;
44162306a36Sopenharmony_ci			break;
44262306a36Sopenharmony_ci		case IRQ_TYPE_EDGE_FALLING:
44362306a36Sopenharmony_ci			level |= mask;
44462306a36Sopenharmony_ci			polarity &= ~mask;
44562306a36Sopenharmony_ci			break;
44662306a36Sopenharmony_ci		case IRQ_TYPE_LEVEL_HIGH:
44762306a36Sopenharmony_ci			level &= ~mask;
44862306a36Sopenharmony_ci			polarity |= mask;
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		case IRQ_TYPE_LEVEL_LOW:
45162306a36Sopenharmony_ci			level &= ~mask;
45262306a36Sopenharmony_ci			polarity &= ~mask;
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		default:
45562306a36Sopenharmony_ci			ret = -EINVAL;
45662306a36Sopenharmony_ci			goto out;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type);
46162306a36Sopenharmony_ci	rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity);
46262306a36Sopenharmony_ciout:
46362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->slock, flags);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return ret;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic int rockchip_irq_reqres(struct irq_data *d)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
47162306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gc->private;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return gpiochip_reqres_irq(&bank->gpio_chip, d->hwirq);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic void rockchip_irq_relres(struct irq_data *d)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
47962306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gc->private;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	gpiochip_relres_irq(&bank->gpio_chip, d->hwirq);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic void rockchip_irq_suspend(struct irq_data *d)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
48762306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gc->private;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask);
49062306a36Sopenharmony_ci	irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void rockchip_irq_resume(struct irq_data *d)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
49662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = gc->private;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask);
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic void rockchip_irq_enable(struct irq_data *d)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	irq_gc_mask_clr_bit(d);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic void rockchip_irq_disable(struct irq_data *d)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	irq_gc_mask_set_bit(d);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
51462306a36Sopenharmony_ci	struct irq_chip_generic *gc;
51562306a36Sopenharmony_ci	int ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	bank->domain = irq_domain_add_linear(bank->of_node, 32,
51862306a36Sopenharmony_ci					&irq_generic_chip_ops, NULL);
51962306a36Sopenharmony_ci	if (!bank->domain) {
52062306a36Sopenharmony_ci		dev_warn(bank->dev, "could not init irq domain for bank %s\n",
52162306a36Sopenharmony_ci			 bank->name);
52262306a36Sopenharmony_ci		return -EINVAL;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
52662306a36Sopenharmony_ci					     "rockchip_gpio_irq",
52762306a36Sopenharmony_ci					     handle_level_irq,
52862306a36Sopenharmony_ci					     clr, 0, 0);
52962306a36Sopenharmony_ci	if (ret) {
53062306a36Sopenharmony_ci		dev_err(bank->dev, "could not alloc generic chips for bank %s\n",
53162306a36Sopenharmony_ci			bank->name);
53262306a36Sopenharmony_ci		irq_domain_remove(bank->domain);
53362306a36Sopenharmony_ci		return -EINVAL;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	gc = irq_get_domain_generic_chip(bank->domain, 0);
53762306a36Sopenharmony_ci	if (bank->gpio_type == GPIO_TYPE_V2) {
53862306a36Sopenharmony_ci		gc->reg_writel = gpio_writel_v2;
53962306a36Sopenharmony_ci		gc->reg_readl = gpio_readl_v2;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	gc->reg_base = bank->reg_base;
54362306a36Sopenharmony_ci	gc->private = bank;
54462306a36Sopenharmony_ci	gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask;
54562306a36Sopenharmony_ci	gc->chip_types[0].regs.ack = bank->gpio_regs->port_eoi;
54662306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
54762306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
54862306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
54962306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
55062306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
55162306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
55262306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
55362306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
55462306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
55562306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_request_resources = rockchip_irq_reqres;
55662306a36Sopenharmony_ci	gc->chip_types[0].chip.irq_release_resources = rockchip_irq_relres;
55762306a36Sopenharmony_ci	gc->wake_enabled = IRQ_MSK(bank->nr_pins);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/*
56062306a36Sopenharmony_ci	 * Linux assumes that all interrupts start out disabled/masked.
56162306a36Sopenharmony_ci	 * Our driver only uses the concept of masked and always keeps
56262306a36Sopenharmony_ci	 * things enabled, so for us that's all masked and all enabled.
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_mask);
56562306a36Sopenharmony_ci	rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->port_eoi);
56662306a36Sopenharmony_ci	rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_en);
56762306a36Sopenharmony_ci	gc->mask_cache = 0xffffffff;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	irq_set_chained_handler_and_data(bank->irq,
57062306a36Sopenharmony_ci					 rockchip_irq_demux, bank);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct gpio_chip *gc;
57862306a36Sopenharmony_ci	int ret;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	bank->gpio_chip = rockchip_gpiolib_chip;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	gc = &bank->gpio_chip;
58362306a36Sopenharmony_ci	gc->base = bank->pin_base;
58462306a36Sopenharmony_ci	gc->ngpio = bank->nr_pins;
58562306a36Sopenharmony_ci	gc->label = bank->name;
58662306a36Sopenharmony_ci	gc->parent = bank->dev;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ret = gpiochip_add_data(gc, bank);
58962306a36Sopenharmony_ci	if (ret) {
59062306a36Sopenharmony_ci		dev_err(bank->dev, "failed to add gpiochip %s, %d\n",
59162306a36Sopenharmony_ci			gc->label, ret);
59262306a36Sopenharmony_ci		return ret;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/*
59662306a36Sopenharmony_ci	 * For DeviceTree-supported systems, the gpio core checks the
59762306a36Sopenharmony_ci	 * pinctrl's device node for the "gpio-ranges" property.
59862306a36Sopenharmony_ci	 * If it is present, it takes care of adding the pin ranges
59962306a36Sopenharmony_ci	 * for the driver. In this case the driver can skip ahead.
60062306a36Sopenharmony_ci	 *
60162306a36Sopenharmony_ci	 * In order to remain compatible with older, existing DeviceTree
60262306a36Sopenharmony_ci	 * files which don't set the "gpio-ranges" property or systems that
60362306a36Sopenharmony_ci	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
60462306a36Sopenharmony_ci	 */
60562306a36Sopenharmony_ci	if (!of_property_read_bool(bank->of_node, "gpio-ranges")) {
60662306a36Sopenharmony_ci		struct device_node *pctlnp = of_get_parent(bank->of_node);
60762306a36Sopenharmony_ci		struct pinctrl_dev *pctldev = NULL;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (!pctlnp)
61062306a36Sopenharmony_ci			return -ENODATA;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		pctldev = of_pinctrl_get(pctlnp);
61362306a36Sopenharmony_ci		of_node_put(pctlnp);
61462306a36Sopenharmony_ci		if (!pctldev)
61562306a36Sopenharmony_ci			return -ENODEV;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0,
61862306a36Sopenharmony_ci					     gc->base, gc->ngpio);
61962306a36Sopenharmony_ci		if (ret) {
62062306a36Sopenharmony_ci			dev_err(bank->dev, "Failed to add pin range\n");
62162306a36Sopenharmony_ci			goto fail;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	ret = rockchip_interrupts_register(bank);
62662306a36Sopenharmony_ci	if (ret) {
62762306a36Sopenharmony_ci		dev_err(bank->dev, "failed to register interrupt, %d\n", ret);
62862306a36Sopenharmony_ci		goto fail;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cifail:
63462306a36Sopenharmony_ci	gpiochip_remove(&bank->gpio_chip);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return ret;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct resource res;
64262306a36Sopenharmony_ci	int id = 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (of_address_to_resource(bank->of_node, 0, &res)) {
64562306a36Sopenharmony_ci		dev_err(bank->dev, "cannot find IO resource for bank\n");
64662306a36Sopenharmony_ci		return -ENOENT;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	bank->reg_base = devm_ioremap_resource(bank->dev, &res);
65062306a36Sopenharmony_ci	if (IS_ERR(bank->reg_base))
65162306a36Sopenharmony_ci		return PTR_ERR(bank->reg_base);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	bank->irq = irq_of_parse_and_map(bank->of_node, 0);
65462306a36Sopenharmony_ci	if (!bank->irq)
65562306a36Sopenharmony_ci		return -EINVAL;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	bank->clk = of_clk_get(bank->of_node, 0);
65862306a36Sopenharmony_ci	if (IS_ERR(bank->clk))
65962306a36Sopenharmony_ci		return PTR_ERR(bank->clk);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	clk_prepare_enable(bank->clk);
66262306a36Sopenharmony_ci	id = readl(bank->reg_base + gpio_regs_v2.version_id);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* If not gpio v2, that is default to v1. */
66562306a36Sopenharmony_ci	if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) {
66662306a36Sopenharmony_ci		bank->gpio_regs = &gpio_regs_v2;
66762306a36Sopenharmony_ci		bank->gpio_type = GPIO_TYPE_V2;
66862306a36Sopenharmony_ci		bank->db_clk = of_clk_get(bank->of_node, 1);
66962306a36Sopenharmony_ci		if (IS_ERR(bank->db_clk)) {
67062306a36Sopenharmony_ci			dev_err(bank->dev, "cannot find debounce clk\n");
67162306a36Sopenharmony_ci			clk_disable_unprepare(bank->clk);
67262306a36Sopenharmony_ci			return -EINVAL;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci	} else {
67562306a36Sopenharmony_ci		bank->gpio_regs = &gpio_regs_v1;
67662306a36Sopenharmony_ci		bank->gpio_type = GPIO_TYPE_V1;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic struct rockchip_pin_bank *
68362306a36Sopenharmony_cirockchip_gpio_find_bank(struct pinctrl_dev *pctldev, int id)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct rockchip_pinctrl *info;
68662306a36Sopenharmony_ci	struct rockchip_pin_bank *bank;
68762306a36Sopenharmony_ci	int i, found = 0;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	info = pinctrl_dev_get_drvdata(pctldev);
69062306a36Sopenharmony_ci	bank = info->ctrl->pin_banks;
69162306a36Sopenharmony_ci	for (i = 0; i < info->ctrl->nr_banks; i++, bank++) {
69262306a36Sopenharmony_ci		if (bank->bank_num == id) {
69362306a36Sopenharmony_ci			found = 1;
69462306a36Sopenharmony_ci			break;
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return found ? bank : NULL;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic int rockchip_gpio_probe(struct platform_device *pdev)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
70462306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
70562306a36Sopenharmony_ci	struct device_node *pctlnp = of_get_parent(np);
70662306a36Sopenharmony_ci	struct pinctrl_dev *pctldev = NULL;
70762306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = NULL;
70862306a36Sopenharmony_ci	struct rockchip_pin_deferred *cfg;
70962306a36Sopenharmony_ci	static int gpio;
71062306a36Sopenharmony_ci	int id, ret;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (!np || !pctlnp)
71362306a36Sopenharmony_ci		return -ENODEV;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	pctldev = of_pinctrl_get(pctlnp);
71662306a36Sopenharmony_ci	if (!pctldev)
71762306a36Sopenharmony_ci		return -EPROBE_DEFER;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	id = of_alias_get_id(np, "gpio");
72062306a36Sopenharmony_ci	if (id < 0)
72162306a36Sopenharmony_ci		id = gpio++;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	bank = rockchip_gpio_find_bank(pctldev, id);
72462306a36Sopenharmony_ci	if (!bank)
72562306a36Sopenharmony_ci		return -EINVAL;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	bank->dev = dev;
72862306a36Sopenharmony_ci	bank->of_node = np;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	raw_spin_lock_init(&bank->slock);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	ret = rockchip_get_bank_data(bank);
73362306a36Sopenharmony_ci	if (ret)
73462306a36Sopenharmony_ci		return ret;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/*
73762306a36Sopenharmony_ci	 * Prevent clashes with a deferred output setting
73862306a36Sopenharmony_ci	 * being added right at this moment.
73962306a36Sopenharmony_ci	 */
74062306a36Sopenharmony_ci	mutex_lock(&bank->deferred_lock);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	ret = rockchip_gpiolib_register(bank);
74362306a36Sopenharmony_ci	if (ret) {
74462306a36Sopenharmony_ci		clk_disable_unprepare(bank->clk);
74562306a36Sopenharmony_ci		mutex_unlock(&bank->deferred_lock);
74662306a36Sopenharmony_ci		return ret;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	while (!list_empty(&bank->deferred_pins)) {
75062306a36Sopenharmony_ci		cfg = list_first_entry(&bank->deferred_pins,
75162306a36Sopenharmony_ci				       struct rockchip_pin_deferred, head);
75262306a36Sopenharmony_ci		list_del(&cfg->head);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		switch (cfg->param) {
75562306a36Sopenharmony_ci		case PIN_CONFIG_OUTPUT:
75662306a36Sopenharmony_ci			ret = rockchip_gpio_direction_output(&bank->gpio_chip, cfg->pin, cfg->arg);
75762306a36Sopenharmony_ci			if (ret)
75862306a36Sopenharmony_ci				dev_warn(dev, "setting output pin %u to %u failed\n", cfg->pin,
75962306a36Sopenharmony_ci					 cfg->arg);
76062306a36Sopenharmony_ci			break;
76162306a36Sopenharmony_ci		case PIN_CONFIG_INPUT_ENABLE:
76262306a36Sopenharmony_ci			ret = rockchip_gpio_direction_input(&bank->gpio_chip, cfg->pin);
76362306a36Sopenharmony_ci			if (ret)
76462306a36Sopenharmony_ci				dev_warn(dev, "setting input pin %u failed\n", cfg->pin);
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci		default:
76762306a36Sopenharmony_ci			dev_warn(dev, "unknown deferred config param %d\n", cfg->param);
76862306a36Sopenharmony_ci			break;
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci		kfree(cfg);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	mutex_unlock(&bank->deferred_lock);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	platform_set_drvdata(pdev, bank);
77662306a36Sopenharmony_ci	dev_info(dev, "probed %pOF\n", np);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic int rockchip_gpio_remove(struct platform_device *pdev)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct rockchip_pin_bank *bank = platform_get_drvdata(pdev);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	clk_disable_unprepare(bank->clk);
78662306a36Sopenharmony_ci	gpiochip_remove(&bank->gpio_chip);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	return 0;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic const struct of_device_id rockchip_gpio_match[] = {
79262306a36Sopenharmony_ci	{ .compatible = "rockchip,gpio-bank", },
79362306a36Sopenharmony_ci	{ .compatible = "rockchip,rk3188-gpio-bank0" },
79462306a36Sopenharmony_ci	{ },
79562306a36Sopenharmony_ci};
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic struct platform_driver rockchip_gpio_driver = {
79862306a36Sopenharmony_ci	.probe		= rockchip_gpio_probe,
79962306a36Sopenharmony_ci	.remove		= rockchip_gpio_remove,
80062306a36Sopenharmony_ci	.driver		= {
80162306a36Sopenharmony_ci		.name	= "rockchip-gpio",
80262306a36Sopenharmony_ci		.of_match_table = rockchip_gpio_match,
80362306a36Sopenharmony_ci	},
80462306a36Sopenharmony_ci};
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int __init rockchip_gpio_init(void)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	return platform_driver_register(&rockchip_gpio_driver);
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_cipostcore_initcall(rockchip_gpio_init);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic void __exit rockchip_gpio_exit(void)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	platform_driver_unregister(&rockchip_gpio_driver);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_cimodule_exit(rockchip_gpio_exit);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip gpio driver");
81962306a36Sopenharmony_ciMODULE_ALIAS("platform:rockchip-gpio");
82062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
82162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_gpio_match);
822