162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Support functions for OMAP GPIO
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003-2005 Nokia Corporation
662306a36Sopenharmony_ci * Written by Juha Yrjölä <juha.yrjola@nokia.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments
962306a36Sopenharmony_ci * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/seq_file.h>
1662306a36Sopenharmony_ci#include <linux/syscore_ops.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/cpu_pm.h>
2162306a36Sopenharmony_ci#include <linux/device.h>
2262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2362306a36Sopenharmony_ci#include <linux/pm.h>
2462306a36Sopenharmony_ci#include <linux/of.h>
2562306a36Sopenharmony_ci#include <linux/gpio/driver.h>
2662306a36Sopenharmony_ci#include <linux/bitops.h>
2762306a36Sopenharmony_ci#include <linux/platform_data/gpio-omap.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct gpio_regs {
3262306a36Sopenharmony_ci	u32 sysconfig;
3362306a36Sopenharmony_ci	u32 irqenable1;
3462306a36Sopenharmony_ci	u32 irqenable2;
3562306a36Sopenharmony_ci	u32 wake_en;
3662306a36Sopenharmony_ci	u32 ctrl;
3762306a36Sopenharmony_ci	u32 oe;
3862306a36Sopenharmony_ci	u32 leveldetect0;
3962306a36Sopenharmony_ci	u32 leveldetect1;
4062306a36Sopenharmony_ci	u32 risingdetect;
4162306a36Sopenharmony_ci	u32 fallingdetect;
4262306a36Sopenharmony_ci	u32 dataout;
4362306a36Sopenharmony_ci	u32 debounce;
4462306a36Sopenharmony_ci	u32 debounce_en;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct gpio_bank {
4862306a36Sopenharmony_ci	void __iomem *base;
4962306a36Sopenharmony_ci	const struct omap_gpio_reg_offs *regs;
5062306a36Sopenharmony_ci	struct device *dev;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	int irq;
5362306a36Sopenharmony_ci	u32 non_wakeup_gpios;
5462306a36Sopenharmony_ci	u32 enabled_non_wakeup_gpios;
5562306a36Sopenharmony_ci	struct gpio_regs context;
5662306a36Sopenharmony_ci	u32 saved_datain;
5762306a36Sopenharmony_ci	u32 level_mask;
5862306a36Sopenharmony_ci	u32 toggle_mask;
5962306a36Sopenharmony_ci	raw_spinlock_t lock;
6062306a36Sopenharmony_ci	raw_spinlock_t wa_lock;
6162306a36Sopenharmony_ci	struct gpio_chip chip;
6262306a36Sopenharmony_ci	struct clk *dbck;
6362306a36Sopenharmony_ci	struct notifier_block nb;
6462306a36Sopenharmony_ci	unsigned int is_suspended:1;
6562306a36Sopenharmony_ci	unsigned int needs_resume:1;
6662306a36Sopenharmony_ci	u32 mod_usage;
6762306a36Sopenharmony_ci	u32 irq_usage;
6862306a36Sopenharmony_ci	u32 dbck_enable_mask;
6962306a36Sopenharmony_ci	bool dbck_enabled;
7062306a36Sopenharmony_ci	bool is_mpuio;
7162306a36Sopenharmony_ci	bool dbck_flag;
7262306a36Sopenharmony_ci	bool loses_context;
7362306a36Sopenharmony_ci	bool context_valid;
7462306a36Sopenharmony_ci	int stride;
7562306a36Sopenharmony_ci	u32 width;
7662306a36Sopenharmony_ci	int context_loss_count;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
7962306a36Sopenharmony_ci	int (*get_context_loss_count)(struct device *dev);
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define GPIO_MOD_CTRL_BIT	BIT(0)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
8562306a36Sopenharmony_ci#define LINE_USED(line, offset) (line & (BIT(offset)))
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void omap_gpio_unmask_irq(struct irq_data *d);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic inline struct gpio_bank *omap_irq_data_get_bank(struct irq_data *d)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
9262306a36Sopenharmony_ci	return gpiochip_get_data(chip);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline u32 omap_gpio_rmw(void __iomem *reg, u32 mask, bool set)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	u32 val = readl_relaxed(reg);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (set)
10062306a36Sopenharmony_ci		val |= mask;
10162306a36Sopenharmony_ci	else
10262306a36Sopenharmony_ci		val &= ~mask;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	writel_relaxed(val, reg);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return val;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void omap_set_gpio_direction(struct gpio_bank *bank, int gpio,
11062306a36Sopenharmony_ci				    int is_input)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	bank->context.oe = omap_gpio_rmw(bank->base + bank->regs->direction,
11362306a36Sopenharmony_ci					 BIT(gpio), is_input);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* set data out value using dedicate set/clear register */
11862306a36Sopenharmony_cistatic void omap_set_gpio_dataout_reg(struct gpio_bank *bank, unsigned offset,
11962306a36Sopenharmony_ci				      int enable)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	void __iomem *reg = bank->base;
12262306a36Sopenharmony_ci	u32 l = BIT(offset);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (enable) {
12562306a36Sopenharmony_ci		reg += bank->regs->set_dataout;
12662306a36Sopenharmony_ci		bank->context.dataout |= l;
12762306a36Sopenharmony_ci	} else {
12862306a36Sopenharmony_ci		reg += bank->regs->clr_dataout;
12962306a36Sopenharmony_ci		bank->context.dataout &= ~l;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	writel_relaxed(l, reg);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* set data out value using mask register */
13662306a36Sopenharmony_cistatic void omap_set_gpio_dataout_mask(struct gpio_bank *bank, unsigned offset,
13762306a36Sopenharmony_ci				       int enable)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	bank->context.dataout = omap_gpio_rmw(bank->base + bank->regs->dataout,
14062306a36Sopenharmony_ci					      BIT(offset), enable);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic inline void omap_gpio_dbck_enable(struct gpio_bank *bank)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	if (bank->dbck_enable_mask && !bank->dbck_enabled) {
14662306a36Sopenharmony_ci		clk_enable(bank->dbck);
14762306a36Sopenharmony_ci		bank->dbck_enabled = true;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		writel_relaxed(bank->dbck_enable_mask,
15062306a36Sopenharmony_ci			     bank->base + bank->regs->debounce_en);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	if (bank->dbck_enable_mask && bank->dbck_enabled) {
15762306a36Sopenharmony_ci		/*
15862306a36Sopenharmony_ci		 * Disable debounce before cutting it's clock. If debounce is
15962306a36Sopenharmony_ci		 * enabled but the clock is not, GPIO module seems to be unable
16062306a36Sopenharmony_ci		 * to detect events and generate interrupts at least on OMAP3.
16162306a36Sopenharmony_ci		 */
16262306a36Sopenharmony_ci		writel_relaxed(0, bank->base + bank->regs->debounce_en);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		clk_disable(bank->dbck);
16562306a36Sopenharmony_ci		bank->dbck_enabled = false;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/**
17062306a36Sopenharmony_ci * omap2_set_gpio_debounce - low level gpio debounce time
17162306a36Sopenharmony_ci * @bank: the gpio bank we're acting upon
17262306a36Sopenharmony_ci * @offset: the gpio number on this @bank
17362306a36Sopenharmony_ci * @debounce: debounce time to use
17462306a36Sopenharmony_ci *
17562306a36Sopenharmony_ci * OMAP's debounce time is in 31us steps
17662306a36Sopenharmony_ci *   <debounce time> = (GPIO_DEBOUNCINGTIME[7:0].DEBOUNCETIME + 1) x 31
17762306a36Sopenharmony_ci * so we need to convert and round up to the closest unit.
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * Return: 0 on success, negative error otherwise.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
18262306a36Sopenharmony_ci				   unsigned debounce)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32			val;
18562306a36Sopenharmony_ci	u32			l;
18662306a36Sopenharmony_ci	bool			enable = !!debounce;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!bank->dbck_flag)
18962306a36Sopenharmony_ci		return -ENOTSUPP;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (enable) {
19262306a36Sopenharmony_ci		debounce = DIV_ROUND_UP(debounce, 31) - 1;
19362306a36Sopenharmony_ci		if ((debounce & OMAP4_GPIO_DEBOUNCINGTIME_MASK) != debounce)
19462306a36Sopenharmony_ci			return -EINVAL;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	l = BIT(offset);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	clk_enable(bank->dbck);
20062306a36Sopenharmony_ci	writel_relaxed(debounce, bank->base + bank->regs->debounce);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	val = omap_gpio_rmw(bank->base + bank->regs->debounce_en, l, enable);
20362306a36Sopenharmony_ci	bank->dbck_enable_mask = val;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	clk_disable(bank->dbck);
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * Enable debounce clock per module.
20862306a36Sopenharmony_ci	 * This call is mandatory because in omap_gpio_request() when
20962306a36Sopenharmony_ci	 * *_runtime_get_sync() is called,  _gpio_dbck_enable() within
21062306a36Sopenharmony_ci	 * runtime callbck fails to turn on dbck because dbck_enable_mask
21162306a36Sopenharmony_ci	 * used within _gpio_dbck_enable() is still not initialized at
21262306a36Sopenharmony_ci	 * that point. Therefore we have to enable dbck here.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	omap_gpio_dbck_enable(bank);
21562306a36Sopenharmony_ci	if (bank->dbck_enable_mask) {
21662306a36Sopenharmony_ci		bank->context.debounce = debounce;
21762306a36Sopenharmony_ci		bank->context.debounce_en = val;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/**
22462306a36Sopenharmony_ci * omap_clear_gpio_debounce - clear debounce settings for a gpio
22562306a36Sopenharmony_ci * @bank: the gpio bank we're acting upon
22662306a36Sopenharmony_ci * @offset: the gpio number on this @bank
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci * If a gpio is using debounce, then clear the debounce enable bit and if
22962306a36Sopenharmony_ci * this is the only gpio in this bank using debounce, then clear the debounce
23062306a36Sopenharmony_ci * time too. The debounce clock will also be disabled when calling this function
23162306a36Sopenharmony_ci * if this is the only gpio in the bank using debounce.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	u32 gpio_bit = BIT(offset);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!bank->dbck_flag)
23862306a36Sopenharmony_ci		return;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!(bank->dbck_enable_mask & gpio_bit))
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	bank->dbck_enable_mask &= ~gpio_bit;
24462306a36Sopenharmony_ci	bank->context.debounce_en &= ~gpio_bit;
24562306a36Sopenharmony_ci        writel_relaxed(bank->context.debounce_en,
24662306a36Sopenharmony_ci		     bank->base + bank->regs->debounce_en);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!bank->dbck_enable_mask) {
24962306a36Sopenharmony_ci		bank->context.debounce = 0;
25062306a36Sopenharmony_ci		writel_relaxed(bank->context.debounce, bank->base +
25162306a36Sopenharmony_ci			     bank->regs->debounce);
25262306a36Sopenharmony_ci		clk_disable(bank->dbck);
25362306a36Sopenharmony_ci		bank->dbck_enabled = false;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/*
25862306a36Sopenharmony_ci * Off mode wake-up capable GPIOs in bank(s) that are in the wakeup domain.
25962306a36Sopenharmony_ci * See TRM section for GPIO for "Wake-Up Generation" for the list of GPIOs
26062306a36Sopenharmony_ci * in wakeup domain. If bank->non_wakeup_gpios is not configured, assume none
26162306a36Sopenharmony_ci * are capable waking up the system from off mode.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic bool omap_gpio_is_off_wakeup_capable(struct gpio_bank *bank, u32 gpio_mask)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	u32 no_wake = bank->non_wakeup_gpios;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (no_wake)
26862306a36Sopenharmony_ci		return !!(~no_wake & gpio_mask);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return false;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
27462306a36Sopenharmony_ci						unsigned trigger)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	void __iomem *base = bank->base;
27762306a36Sopenharmony_ci	u32 gpio_bit = BIT(gpio);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->leveldetect0, gpio_bit,
28062306a36Sopenharmony_ci		      trigger & IRQ_TYPE_LEVEL_LOW);
28162306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->leveldetect1, gpio_bit,
28262306a36Sopenharmony_ci		      trigger & IRQ_TYPE_LEVEL_HIGH);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/*
28562306a36Sopenharmony_ci	 * We need the edge detection enabled for to allow the GPIO block
28662306a36Sopenharmony_ci	 * to be woken from idle state.  Set the appropriate edge detection
28762306a36Sopenharmony_ci	 * in addition to the level detection.
28862306a36Sopenharmony_ci	 */
28962306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->risingdetect, gpio_bit,
29062306a36Sopenharmony_ci		      trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH));
29162306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->fallingdetect, gpio_bit,
29262306a36Sopenharmony_ci		      trigger & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW));
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	bank->context.leveldetect0 =
29562306a36Sopenharmony_ci			readl_relaxed(bank->base + bank->regs->leveldetect0);
29662306a36Sopenharmony_ci	bank->context.leveldetect1 =
29762306a36Sopenharmony_ci			readl_relaxed(bank->base + bank->regs->leveldetect1);
29862306a36Sopenharmony_ci	bank->context.risingdetect =
29962306a36Sopenharmony_ci			readl_relaxed(bank->base + bank->regs->risingdetect);
30062306a36Sopenharmony_ci	bank->context.fallingdetect =
30162306a36Sopenharmony_ci			readl_relaxed(bank->base + bank->regs->fallingdetect);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	bank->level_mask = bank->context.leveldetect0 |
30462306a36Sopenharmony_ci			   bank->context.leveldetect1;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* This part needs to be executed always for OMAP{34xx, 44xx} */
30762306a36Sopenharmony_ci	if (!bank->regs->irqctrl && !omap_gpio_is_off_wakeup_capable(bank, gpio)) {
30862306a36Sopenharmony_ci		/*
30962306a36Sopenharmony_ci		 * Log the edge gpio and manually trigger the IRQ
31062306a36Sopenharmony_ci		 * after resume if the input level changes
31162306a36Sopenharmony_ci		 * to avoid irq lost during PER RET/OFF mode
31262306a36Sopenharmony_ci		 * Applies for omap2 non-wakeup gpio and all omap3 gpios
31362306a36Sopenharmony_ci		 */
31462306a36Sopenharmony_ci		if (trigger & IRQ_TYPE_EDGE_BOTH)
31562306a36Sopenharmony_ci			bank->enabled_non_wakeup_gpios |= gpio_bit;
31662306a36Sopenharmony_ci		else
31762306a36Sopenharmony_ci			bank->enabled_non_wakeup_gpios &= ~gpio_bit;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/*
32262306a36Sopenharmony_ci * This only applies to chips that can't do both rising and falling edge
32362306a36Sopenharmony_ci * detection at once.  For all other chips, this function is a noop.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic void omap_toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARCH_OMAP1) && bank->regs->irqctrl) {
32862306a36Sopenharmony_ci		void __iomem *reg = bank->base + bank->regs->irqctrl;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		writel_relaxed(readl_relaxed(reg) ^ BIT(gpio), reg);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int omap_set_gpio_triggering(struct gpio_bank *bank, int gpio,
33562306a36Sopenharmony_ci				    unsigned trigger)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	void __iomem *reg = bank->base;
33862306a36Sopenharmony_ci	u32 l = 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (bank->regs->leveldetect0 && bank->regs->wkup_en) {
34162306a36Sopenharmony_ci		omap_set_gpio_trigger(bank, gpio, trigger);
34262306a36Sopenharmony_ci	} else if (bank->regs->irqctrl) {
34362306a36Sopenharmony_ci		reg += bank->regs->irqctrl;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		l = readl_relaxed(reg);
34662306a36Sopenharmony_ci		if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
34762306a36Sopenharmony_ci			bank->toggle_mask |= BIT(gpio);
34862306a36Sopenharmony_ci		if (trigger & IRQ_TYPE_EDGE_RISING)
34962306a36Sopenharmony_ci			l |= BIT(gpio);
35062306a36Sopenharmony_ci		else if (trigger & IRQ_TYPE_EDGE_FALLING)
35162306a36Sopenharmony_ci			l &= ~(BIT(gpio));
35262306a36Sopenharmony_ci		else
35362306a36Sopenharmony_ci			return -EINVAL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		writel_relaxed(l, reg);
35662306a36Sopenharmony_ci	} else if (bank->regs->edgectrl1) {
35762306a36Sopenharmony_ci		if (gpio & 0x08)
35862306a36Sopenharmony_ci			reg += bank->regs->edgectrl2;
35962306a36Sopenharmony_ci		else
36062306a36Sopenharmony_ci			reg += bank->regs->edgectrl1;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		gpio &= 0x07;
36362306a36Sopenharmony_ci		l = readl_relaxed(reg);
36462306a36Sopenharmony_ci		l &= ~(3 << (gpio << 1));
36562306a36Sopenharmony_ci		if (trigger & IRQ_TYPE_EDGE_RISING)
36662306a36Sopenharmony_ci			l |= 2 << (gpio << 1);
36762306a36Sopenharmony_ci		if (trigger & IRQ_TYPE_EDGE_FALLING)
36862306a36Sopenharmony_ci			l |= BIT(gpio << 1);
36962306a36Sopenharmony_ci		writel_relaxed(l, reg);
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void omap_enable_gpio_module(struct gpio_bank *bank, unsigned offset)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	if (bank->regs->pinctrl) {
37762306a36Sopenharmony_ci		void __iomem *reg = bank->base + bank->regs->pinctrl;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		/* Claim the pin for MPU */
38062306a36Sopenharmony_ci		writel_relaxed(readl_relaxed(reg) | (BIT(offset)), reg);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (bank->regs->ctrl && !BANK_USED(bank)) {
38462306a36Sopenharmony_ci		void __iomem *reg = bank->base + bank->regs->ctrl;
38562306a36Sopenharmony_ci		u32 ctrl;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		ctrl = readl_relaxed(reg);
38862306a36Sopenharmony_ci		/* Module is enabled, clocks are not gated */
38962306a36Sopenharmony_ci		ctrl &= ~GPIO_MOD_CTRL_BIT;
39062306a36Sopenharmony_ci		writel_relaxed(ctrl, reg);
39162306a36Sopenharmony_ci		bank->context.ctrl = ctrl;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void omap_disable_gpio_module(struct gpio_bank *bank, unsigned offset)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	if (bank->regs->ctrl && !BANK_USED(bank)) {
39862306a36Sopenharmony_ci		void __iomem *reg = bank->base + bank->regs->ctrl;
39962306a36Sopenharmony_ci		u32 ctrl;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		ctrl = readl_relaxed(reg);
40262306a36Sopenharmony_ci		/* Module is disabled, clocks are gated */
40362306a36Sopenharmony_ci		ctrl |= GPIO_MOD_CTRL_BIT;
40462306a36Sopenharmony_ci		writel_relaxed(ctrl, reg);
40562306a36Sopenharmony_ci		bank->context.ctrl = ctrl;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int omap_gpio_is_input(struct gpio_bank *bank, unsigned offset)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	void __iomem *reg = bank->base + bank->regs->direction;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return readl_relaxed(reg) & BIT(offset);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic void omap_gpio_init_irq(struct gpio_bank *bank, unsigned offset)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	if (!LINE_USED(bank->mod_usage, offset)) {
41962306a36Sopenharmony_ci		omap_enable_gpio_module(bank, offset);
42062306a36Sopenharmony_ci		omap_set_gpio_direction(bank, offset, 1);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci	bank->irq_usage |= BIT(offset);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic int omap_gpio_irq_type(struct irq_data *d, unsigned type)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
42862306a36Sopenharmony_ci	int retval;
42962306a36Sopenharmony_ci	unsigned long flags;
43062306a36Sopenharmony_ci	unsigned offset = d->hwirq;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (type & ~IRQ_TYPE_SENSE_MASK)
43362306a36Sopenharmony_ci		return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!bank->regs->leveldetect0 &&
43662306a36Sopenharmony_ci		(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
43762306a36Sopenharmony_ci		return -EINVAL;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
44062306a36Sopenharmony_ci	retval = omap_set_gpio_triggering(bank, offset, type);
44162306a36Sopenharmony_ci	if (retval) {
44262306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&bank->lock, flags);
44362306a36Sopenharmony_ci		goto error;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci	omap_gpio_init_irq(bank, offset);
44662306a36Sopenharmony_ci	if (!omap_gpio_is_input(bank, offset)) {
44762306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&bank->lock, flags);
44862306a36Sopenharmony_ci		retval = -EINVAL;
44962306a36Sopenharmony_ci		goto error;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
45462306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
45562306a36Sopenharmony_ci	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
45662306a36Sopenharmony_ci		/*
45762306a36Sopenharmony_ci		 * Edge IRQs are already cleared/acked in irq_handler and
45862306a36Sopenharmony_ci		 * not need to be masked, as result handle_edge_irq()
45962306a36Sopenharmony_ci		 * logic is excessed here and may cause lose of interrupts.
46062306a36Sopenharmony_ci		 * So just use handle_simple_irq.
46162306a36Sopenharmony_ci		 */
46262306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_simple_irq);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cierror:
46762306a36Sopenharmony_ci	return retval;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void omap_clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	void __iomem *reg = bank->base;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	reg += bank->regs->irqstatus;
47562306a36Sopenharmony_ci	writel_relaxed(gpio_mask, reg);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Workaround for clearing DSP GPIO interrupts to allow retention */
47862306a36Sopenharmony_ci	if (bank->regs->irqstatus2) {
47962306a36Sopenharmony_ci		reg = bank->base + bank->regs->irqstatus2;
48062306a36Sopenharmony_ci		writel_relaxed(gpio_mask, reg);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Flush posted write for the irq status to avoid spurious interrupts */
48462306a36Sopenharmony_ci	readl_relaxed(reg);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic inline void omap_clear_gpio_irqstatus(struct gpio_bank *bank,
48862306a36Sopenharmony_ci					     unsigned offset)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	omap_clear_gpio_irqbank(bank, BIT(offset));
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic u32 omap_get_gpio_irqbank_mask(struct gpio_bank *bank)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	void __iomem *reg = bank->base;
49662306a36Sopenharmony_ci	u32 l;
49762306a36Sopenharmony_ci	u32 mask = (BIT(bank->width)) - 1;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	reg += bank->regs->irqenable;
50062306a36Sopenharmony_ci	l = readl_relaxed(reg);
50162306a36Sopenharmony_ci	if (bank->regs->irqenable_inv)
50262306a36Sopenharmony_ci		l = ~l;
50362306a36Sopenharmony_ci	l &= mask;
50462306a36Sopenharmony_ci	return l;
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic inline void omap_set_gpio_irqenable(struct gpio_bank *bank,
50862306a36Sopenharmony_ci					   unsigned offset, int enable)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	void __iomem *reg = bank->base;
51162306a36Sopenharmony_ci	u32 gpio_mask = BIT(offset);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (bank->regs->set_irqenable && bank->regs->clr_irqenable) {
51462306a36Sopenharmony_ci		if (enable) {
51562306a36Sopenharmony_ci			reg += bank->regs->set_irqenable;
51662306a36Sopenharmony_ci			bank->context.irqenable1 |= gpio_mask;
51762306a36Sopenharmony_ci		} else {
51862306a36Sopenharmony_ci			reg += bank->regs->clr_irqenable;
51962306a36Sopenharmony_ci			bank->context.irqenable1 &= ~gpio_mask;
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci		writel_relaxed(gpio_mask, reg);
52262306a36Sopenharmony_ci	} else {
52362306a36Sopenharmony_ci		bank->context.irqenable1 =
52462306a36Sopenharmony_ci			omap_gpio_rmw(reg + bank->regs->irqenable, gpio_mask,
52562306a36Sopenharmony_ci				      enable ^ bank->regs->irqenable_inv);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/*
52962306a36Sopenharmony_ci	 * Program GPIO wakeup along with IRQ enable to satisfy OMAP4430 TRM
53062306a36Sopenharmony_ci	 * note requiring correlation between the IRQ enable registers and
53162306a36Sopenharmony_ci	 * the wakeup registers.  In any case, we want wakeup from idle
53262306a36Sopenharmony_ci	 * enabled for the GPIOs which support this feature.
53362306a36Sopenharmony_ci	 */
53462306a36Sopenharmony_ci	if (bank->regs->wkup_en &&
53562306a36Sopenharmony_ci	    (bank->regs->edgectrl1 || !(bank->non_wakeup_gpios & gpio_mask))) {
53662306a36Sopenharmony_ci		bank->context.wake_en =
53762306a36Sopenharmony_ci			omap_gpio_rmw(bank->base + bank->regs->wkup_en,
53862306a36Sopenharmony_ci				      gpio_mask, enable);
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
54362306a36Sopenharmony_cistatic int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return irq_set_irq_wake(bank->irq, enable);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci * We need to unmask the GPIO bank interrupt as soon as possible to
55262306a36Sopenharmony_ci * avoid missing GPIO interrupts for other lines in the bank.
55362306a36Sopenharmony_ci * Then we need to mask-read-clear-unmask the triggered GPIO lines
55462306a36Sopenharmony_ci * in the bank to avoid missing nested interrupts for a GPIO line.
55562306a36Sopenharmony_ci * If we wait to unmask individual GPIO lines in the bank after the
55662306a36Sopenharmony_ci * line's interrupt handler has been run, we may miss some nested
55762306a36Sopenharmony_ci * interrupts.
55862306a36Sopenharmony_ci */
55962306a36Sopenharmony_cistatic irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	void __iomem *isr_reg = NULL;
56262306a36Sopenharmony_ci	u32 enabled, isr, edge;
56362306a36Sopenharmony_ci	unsigned int bit;
56462306a36Sopenharmony_ci	struct gpio_bank *bank = gpiobank;
56562306a36Sopenharmony_ci	unsigned long wa_lock_flags;
56662306a36Sopenharmony_ci	unsigned long lock_flags;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	isr_reg = bank->base + bank->regs->irqstatus;
56962306a36Sopenharmony_ci	if (WARN_ON(!isr_reg))
57062306a36Sopenharmony_ci		goto exit;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (WARN_ONCE(!pm_runtime_active(bank->chip.parent),
57362306a36Sopenharmony_ci		      "gpio irq%i while runtime suspended?\n", irq))
57462306a36Sopenharmony_ci		return IRQ_NONE;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	while (1) {
57762306a36Sopenharmony_ci		raw_spin_lock_irqsave(&bank->lock, lock_flags);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		enabled = omap_get_gpio_irqbank_mask(bank);
58062306a36Sopenharmony_ci		isr = readl_relaxed(isr_reg) & enabled;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		/*
58362306a36Sopenharmony_ci		 * Clear edge sensitive interrupts before calling handler(s)
58462306a36Sopenharmony_ci		 * so subsequent edge transitions are not missed while the
58562306a36Sopenharmony_ci		 * handlers are running.
58662306a36Sopenharmony_ci		 */
58762306a36Sopenharmony_ci		edge = isr & ~bank->level_mask;
58862306a36Sopenharmony_ci		if (edge)
58962306a36Sopenharmony_ci			omap_clear_gpio_irqbank(bank, edge);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		if (!isr)
59462306a36Sopenharmony_ci			break;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		while (isr) {
59762306a36Sopenharmony_ci			bit = __ffs(isr);
59862306a36Sopenharmony_ci			isr &= ~(BIT(bit));
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			raw_spin_lock_irqsave(&bank->lock, lock_flags);
60162306a36Sopenharmony_ci			/*
60262306a36Sopenharmony_ci			 * Some chips can't respond to both rising and falling
60362306a36Sopenharmony_ci			 * at the same time.  If this irq was requested with
60462306a36Sopenharmony_ci			 * both flags, we need to flip the ICR data for the IRQ
60562306a36Sopenharmony_ci			 * to respond to the IRQ for the opposite direction.
60662306a36Sopenharmony_ci			 * This will be indicated in the bank toggle_mask.
60762306a36Sopenharmony_ci			 */
60862306a36Sopenharmony_ci			if (bank->toggle_mask & (BIT(bit)))
60962306a36Sopenharmony_ci				omap_toggle_gpio_edge_triggering(bank, bit);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci			raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci			raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci			generic_handle_domain_irq(bank->chip.irq.domain, bit);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci			raw_spin_unlock_irqrestore(&bank->wa_lock,
61862306a36Sopenharmony_ci						   wa_lock_flags);
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ciexit:
62262306a36Sopenharmony_ci	return IRQ_HANDLED;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic unsigned int omap_gpio_irq_startup(struct irq_data *d)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
62862306a36Sopenharmony_ci	unsigned long flags;
62962306a36Sopenharmony_ci	unsigned offset = d->hwirq;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!LINE_USED(bank->mod_usage, offset))
63462306a36Sopenharmony_ci		omap_set_gpio_direction(bank, offset, 1);
63562306a36Sopenharmony_ci	omap_enable_gpio_module(bank, offset);
63662306a36Sopenharmony_ci	bank->irq_usage |= BIT(offset);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
63962306a36Sopenharmony_ci	omap_gpio_unmask_irq(d);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic void omap_gpio_irq_shutdown(struct irq_data *d)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
64762306a36Sopenharmony_ci	unsigned long flags;
64862306a36Sopenharmony_ci	unsigned offset = d->hwirq;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
65162306a36Sopenharmony_ci	bank->irq_usage &= ~(BIT(offset));
65262306a36Sopenharmony_ci	omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
65362306a36Sopenharmony_ci	omap_clear_gpio_irqstatus(bank, offset);
65462306a36Sopenharmony_ci	omap_set_gpio_irqenable(bank, offset, 0);
65562306a36Sopenharmony_ci	if (!LINE_USED(bank->mod_usage, offset))
65662306a36Sopenharmony_ci		omap_clear_gpio_debounce(bank, offset);
65762306a36Sopenharmony_ci	omap_disable_gpio_module(bank, offset);
65862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic void omap_gpio_irq_bus_lock(struct irq_data *data)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(data);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	pm_runtime_get_sync(bank->chip.parent);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic void gpio_irq_bus_sync_unlock(struct irq_data *data)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(data);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	pm_runtime_put(bank->chip.parent);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void omap_gpio_mask_irq(struct irq_data *d)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
67862306a36Sopenharmony_ci	unsigned offset = d->hwirq;
67962306a36Sopenharmony_ci	unsigned long flags;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
68262306a36Sopenharmony_ci	omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
68362306a36Sopenharmony_ci	omap_set_gpio_irqenable(bank, offset, 0);
68462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
68562306a36Sopenharmony_ci	gpiochip_disable_irq(&bank->chip, offset);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void omap_gpio_unmask_irq(struct irq_data *d)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
69162306a36Sopenharmony_ci	unsigned offset = d->hwirq;
69262306a36Sopenharmony_ci	u32 trigger = irqd_get_trigger_type(d);
69362306a36Sopenharmony_ci	unsigned long flags;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	gpiochip_enable_irq(&bank->chip, offset);
69662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
69762306a36Sopenharmony_ci	omap_set_gpio_irqenable(bank, offset, 1);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/*
70062306a36Sopenharmony_ci	 * For level-triggered GPIOs, clearing must be done after the source
70162306a36Sopenharmony_ci	 * is cleared, thus after the handler has run. OMAP4 needs this done
70262306a36Sopenharmony_ci	 * after enabing the interrupt to clear the wakeup status.
70362306a36Sopenharmony_ci	 */
70462306a36Sopenharmony_ci	if (bank->regs->leveldetect0 && bank->regs->wkup_en &&
70562306a36Sopenharmony_ci	    trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
70662306a36Sopenharmony_ci		omap_clear_gpio_irqstatus(bank, offset);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (trigger)
70962306a36Sopenharmony_ci		omap_set_gpio_triggering(bank, offset, trigger);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic void omap_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct gpio_bank *bank = omap_irq_data_get_bank(d);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	seq_printf(p, dev_name(bank->dev));
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic const struct irq_chip omap_gpio_irq_chip = {
72262306a36Sopenharmony_ci	.irq_startup = omap_gpio_irq_startup,
72362306a36Sopenharmony_ci	.irq_shutdown = omap_gpio_irq_shutdown,
72462306a36Sopenharmony_ci	.irq_mask = omap_gpio_mask_irq,
72562306a36Sopenharmony_ci	.irq_unmask = omap_gpio_unmask_irq,
72662306a36Sopenharmony_ci	.irq_set_type = omap_gpio_irq_type,
72762306a36Sopenharmony_ci	.irq_set_wake = omap_gpio_wake_enable,
72862306a36Sopenharmony_ci	.irq_bus_lock = omap_gpio_irq_bus_lock,
72962306a36Sopenharmony_ci	.irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
73062306a36Sopenharmony_ci	.irq_print_chip = omap_gpio_irq_print_chip,
73162306a36Sopenharmony_ci	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
73262306a36Sopenharmony_ci	 GPIOCHIP_IRQ_RESOURCE_HELPERS,
73362306a36Sopenharmony_ci};
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic const struct irq_chip omap_gpio_irq_chip_nowake = {
73662306a36Sopenharmony_ci	.irq_startup = omap_gpio_irq_startup,
73762306a36Sopenharmony_ci	.irq_shutdown = omap_gpio_irq_shutdown,
73862306a36Sopenharmony_ci	.irq_mask = omap_gpio_mask_irq,
73962306a36Sopenharmony_ci	.irq_unmask = omap_gpio_unmask_irq,
74062306a36Sopenharmony_ci	.irq_set_type = omap_gpio_irq_type,
74162306a36Sopenharmony_ci	.irq_bus_lock = omap_gpio_irq_bus_lock,
74262306a36Sopenharmony_ci	.irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
74362306a36Sopenharmony_ci	.irq_print_chip = omap_gpio_irq_print_chip,
74462306a36Sopenharmony_ci	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
74562306a36Sopenharmony_ci	 GPIOCHIP_IRQ_RESOURCE_HELPERS,
74662306a36Sopenharmony_ci};
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/*---------------------------------------------------------------------*/
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic int omap_mpuio_suspend_noirq(struct device *dev)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct gpio_bank	*bank = dev_get_drvdata(dev);
75362306a36Sopenharmony_ci	void __iomem		*mask_reg = bank->base +
75462306a36Sopenharmony_ci					OMAP_MPUIO_GPIO_MASKIT / bank->stride;
75562306a36Sopenharmony_ci	unsigned long		flags;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
75862306a36Sopenharmony_ci	writel_relaxed(0xffff & ~bank->context.wake_en, mask_reg);
75962306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return 0;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic int omap_mpuio_resume_noirq(struct device *dev)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct gpio_bank	*bank = dev_get_drvdata(dev);
76762306a36Sopenharmony_ci	void __iomem		*mask_reg = bank->base +
76862306a36Sopenharmony_ci					OMAP_MPUIO_GPIO_MASKIT / bank->stride;
76962306a36Sopenharmony_ci	unsigned long		flags;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
77262306a36Sopenharmony_ci	writel_relaxed(bank->context.wake_en, mask_reg);
77362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return 0;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic const struct dev_pm_ops omap_mpuio_dev_pm_ops = {
77962306a36Sopenharmony_ci	.suspend_noirq = omap_mpuio_suspend_noirq,
78062306a36Sopenharmony_ci	.resume_noirq = omap_mpuio_resume_noirq,
78162306a36Sopenharmony_ci};
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci/* use platform_driver for this. */
78462306a36Sopenharmony_cistatic struct platform_driver omap_mpuio_driver = {
78562306a36Sopenharmony_ci	.driver		= {
78662306a36Sopenharmony_ci		.name	= "mpuio",
78762306a36Sopenharmony_ci		.pm	= &omap_mpuio_dev_pm_ops,
78862306a36Sopenharmony_ci	},
78962306a36Sopenharmony_ci};
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic struct platform_device omap_mpuio_device = {
79262306a36Sopenharmony_ci	.name		= "mpuio",
79362306a36Sopenharmony_ci	.id		= -1,
79462306a36Sopenharmony_ci	.dev = {
79562306a36Sopenharmony_ci		.driver = &omap_mpuio_driver.driver,
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci	/* could list the /proc/iomem resources */
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic inline void omap_mpuio_init(struct gpio_bank *bank)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	platform_set_drvdata(&omap_mpuio_device, bank);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (platform_driver_register(&omap_mpuio_driver) == 0)
80562306a36Sopenharmony_ci		(void) platform_device_register(&omap_mpuio_device);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/*---------------------------------------------------------------------*/
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
81362306a36Sopenharmony_ci	unsigned long flags;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	pm_runtime_get_sync(chip->parent);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
81862306a36Sopenharmony_ci	omap_enable_gpio_module(bank, offset);
81962306a36Sopenharmony_ci	bank->mod_usage |= BIT(offset);
82062306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
82862306a36Sopenharmony_ci	unsigned long flags;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
83162306a36Sopenharmony_ci	bank->mod_usage &= ~(BIT(offset));
83262306a36Sopenharmony_ci	if (!LINE_USED(bank->irq_usage, offset)) {
83362306a36Sopenharmony_ci		omap_set_gpio_direction(bank, offset, 1);
83462306a36Sopenharmony_ci		omap_clear_gpio_debounce(bank, offset);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci	omap_disable_gpio_module(bank, offset);
83762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	pm_runtime_put(chip->parent);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic int omap_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (readl_relaxed(bank->base + bank->regs->direction) & BIT(offset))
84762306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_IN;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_OUT;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int omap_gpio_input(struct gpio_chip *chip, unsigned offset)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct gpio_bank *bank;
85562306a36Sopenharmony_ci	unsigned long flags;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	bank = gpiochip_get_data(chip);
85862306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
85962306a36Sopenharmony_ci	omap_set_gpio_direction(bank, offset, 1);
86062306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
86162306a36Sopenharmony_ci	return 0;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int omap_gpio_get(struct gpio_chip *chip, unsigned offset)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
86762306a36Sopenharmony_ci	void __iomem *reg;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (omap_gpio_is_input(bank, offset))
87062306a36Sopenharmony_ci		reg = bank->base + bank->regs->datain;
87162306a36Sopenharmony_ci	else
87262306a36Sopenharmony_ci		reg = bank->base + bank->regs->dataout;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	return (readl_relaxed(reg) & BIT(offset)) != 0;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic int omap_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct gpio_bank *bank;
88062306a36Sopenharmony_ci	unsigned long flags;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	bank = gpiochip_get_data(chip);
88362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
88462306a36Sopenharmony_ci	bank->set_dataout(bank, offset, value);
88562306a36Sopenharmony_ci	omap_set_gpio_direction(bank, offset, 0);
88662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
88762306a36Sopenharmony_ci	return 0;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int omap_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
89162306a36Sopenharmony_ci				  unsigned long *bits)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
89462306a36Sopenharmony_ci	void __iomem *base = bank->base;
89562306a36Sopenharmony_ci	u32 direction, m, val = 0;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	direction = readl_relaxed(base + bank->regs->direction);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	m = direction & *mask;
90062306a36Sopenharmony_ci	if (m)
90162306a36Sopenharmony_ci		val |= readl_relaxed(base + bank->regs->datain) & m;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	m = ~direction & *mask;
90462306a36Sopenharmony_ci	if (m)
90562306a36Sopenharmony_ci		val |= readl_relaxed(base + bank->regs->dataout) & m;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	*bits = val;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	return 0;
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
91362306a36Sopenharmony_ci			      unsigned debounce)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct gpio_bank *bank;
91662306a36Sopenharmony_ci	unsigned long flags;
91762306a36Sopenharmony_ci	int ret;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	bank = gpiochip_get_data(chip);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
92262306a36Sopenharmony_ci	ret = omap2_set_gpio_debounce(bank, offset, debounce);
92362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (ret)
92662306a36Sopenharmony_ci		dev_info(chip->parent,
92762306a36Sopenharmony_ci			 "Could not set line %u debounce to %u microseconds (%d)",
92862306a36Sopenharmony_ci			 offset, debounce, ret);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return ret;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset,
93462306a36Sopenharmony_ci				unsigned long config)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	u32 debounce;
93762306a36Sopenharmony_ci	int ret = -ENOTSUPP;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	switch (pinconf_to_config_param(config)) {
94062306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
94162306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
94262306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
94362306a36Sopenharmony_ci		ret = gpiochip_generic_config(chip, offset, config);
94462306a36Sopenharmony_ci		break;
94562306a36Sopenharmony_ci	case PIN_CONFIG_INPUT_DEBOUNCE:
94662306a36Sopenharmony_ci		debounce = pinconf_to_config_argument(config);
94762306a36Sopenharmony_ci		ret = omap_gpio_debounce(chip, offset, debounce);
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	default:
95062306a36Sopenharmony_ci		break;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	return ret;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct gpio_bank *bank;
95962306a36Sopenharmony_ci	unsigned long flags;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	bank = gpiochip_get_data(chip);
96262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
96362306a36Sopenharmony_ci	bank->set_dataout(bank, offset, value);
96462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
96862306a36Sopenharmony_ci				   unsigned long *bits)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct gpio_bank *bank = gpiochip_get_data(chip);
97162306a36Sopenharmony_ci	void __iomem *reg = bank->base + bank->regs->dataout;
97262306a36Sopenharmony_ci	unsigned long flags;
97362306a36Sopenharmony_ci	u32 l;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
97662306a36Sopenharmony_ci	l = (readl_relaxed(reg) & ~*mask) | (*bits & *mask);
97762306a36Sopenharmony_ci	writel_relaxed(l, reg);
97862306a36Sopenharmony_ci	bank->context.dataout = l;
97962306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci/*---------------------------------------------------------------------*/
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic void omap_gpio_show_rev(struct gpio_bank *bank)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	static bool called;
98762306a36Sopenharmony_ci	u32 rev;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (called || bank->regs->revision == USHRT_MAX)
99062306a36Sopenharmony_ci		return;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	rev = readw_relaxed(bank->base + bank->regs->revision);
99362306a36Sopenharmony_ci	pr_info("OMAP GPIO hardware version %d.%d\n",
99462306a36Sopenharmony_ci		(rev >> 4) & 0x0f, rev & 0x0f);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	called = true;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic void omap_gpio_mod_init(struct gpio_bank *bank)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	void __iomem *base = bank->base;
100262306a36Sopenharmony_ci	u32 l = 0xffffffff;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (bank->width == 16)
100562306a36Sopenharmony_ci		l = 0xffff;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (bank->is_mpuio) {
100862306a36Sopenharmony_ci		writel_relaxed(l, bank->base + bank->regs->irqenable);
100962306a36Sopenharmony_ci		return;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->irqenable, l,
101362306a36Sopenharmony_ci		      bank->regs->irqenable_inv);
101462306a36Sopenharmony_ci	omap_gpio_rmw(base + bank->regs->irqstatus, l,
101562306a36Sopenharmony_ci		      !bank->regs->irqenable_inv);
101662306a36Sopenharmony_ci	if (bank->regs->debounce_en)
101762306a36Sopenharmony_ci		writel_relaxed(0, base + bank->regs->debounce_en);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* Save OE default value (0xffffffff) in the context */
102062306a36Sopenharmony_ci	bank->context.oe = readl_relaxed(bank->base + bank->regs->direction);
102162306a36Sopenharmony_ci	 /* Initialize interface clk ungated, module enabled */
102262306a36Sopenharmony_ci	if (bank->regs->ctrl)
102362306a36Sopenharmony_ci		writel_relaxed(0, base + bank->regs->ctrl);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct gpio_irq_chip *irq;
102962306a36Sopenharmony_ci	static int gpio;
103062306a36Sopenharmony_ci	const char *label;
103162306a36Sopenharmony_ci	int ret;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/*
103462306a36Sopenharmony_ci	 * REVISIT eventually switch from OMAP-specific gpio structs
103562306a36Sopenharmony_ci	 * over to the generic ones
103662306a36Sopenharmony_ci	 */
103762306a36Sopenharmony_ci	bank->chip.request = omap_gpio_request;
103862306a36Sopenharmony_ci	bank->chip.free = omap_gpio_free;
103962306a36Sopenharmony_ci	bank->chip.get_direction = omap_gpio_get_direction;
104062306a36Sopenharmony_ci	bank->chip.direction_input = omap_gpio_input;
104162306a36Sopenharmony_ci	bank->chip.get = omap_gpio_get;
104262306a36Sopenharmony_ci	bank->chip.get_multiple = omap_gpio_get_multiple;
104362306a36Sopenharmony_ci	bank->chip.direction_output = omap_gpio_output;
104462306a36Sopenharmony_ci	bank->chip.set_config = omap_gpio_set_config;
104562306a36Sopenharmony_ci	bank->chip.set = omap_gpio_set;
104662306a36Sopenharmony_ci	bank->chip.set_multiple = omap_gpio_set_multiple;
104762306a36Sopenharmony_ci	if (bank->is_mpuio) {
104862306a36Sopenharmony_ci		bank->chip.label = "mpuio";
104962306a36Sopenharmony_ci		if (bank->regs->wkup_en)
105062306a36Sopenharmony_ci			bank->chip.parent = &omap_mpuio_device.dev;
105162306a36Sopenharmony_ci		bank->chip.base = OMAP_MPUIO(0);
105262306a36Sopenharmony_ci	} else {
105362306a36Sopenharmony_ci		label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d",
105462306a36Sopenharmony_ci				       gpio, gpio + bank->width - 1);
105562306a36Sopenharmony_ci		if (!label)
105662306a36Sopenharmony_ci			return -ENOMEM;
105762306a36Sopenharmony_ci		bank->chip.label = label;
105862306a36Sopenharmony_ci		bank->chip.base = -1;
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci	bank->chip.ngpio = bank->width;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	irq = &bank->chip.irq;
106362306a36Sopenharmony_ci	/* MPUIO is a bit different, reading IRQ status clears it */
106462306a36Sopenharmony_ci	if (bank->is_mpuio && !bank->regs->wkup_en)
106562306a36Sopenharmony_ci		gpio_irq_chip_set_chip(irq, &omap_gpio_irq_chip_nowake);
106662306a36Sopenharmony_ci	else
106762306a36Sopenharmony_ci		gpio_irq_chip_set_chip(irq, &omap_gpio_irq_chip);
106862306a36Sopenharmony_ci	irq->handler = handle_bad_irq;
106962306a36Sopenharmony_ci	irq->default_type = IRQ_TYPE_NONE;
107062306a36Sopenharmony_ci	irq->num_parents = 1;
107162306a36Sopenharmony_ci	irq->parents = &bank->irq;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	ret = gpiochip_add_data(&bank->chip, bank);
107462306a36Sopenharmony_ci	if (ret)
107562306a36Sopenharmony_ci		return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n");
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	irq_domain_set_pm_device(bank->chip.irq.domain, pm_dev);
107862306a36Sopenharmony_ci	ret = devm_request_irq(bank->chip.parent, bank->irq,
107962306a36Sopenharmony_ci			       omap_gpio_irq_handler,
108062306a36Sopenharmony_ci			       0, dev_name(bank->chip.parent), bank);
108162306a36Sopenharmony_ci	if (ret)
108262306a36Sopenharmony_ci		gpiochip_remove(&bank->chip);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (!bank->is_mpuio)
108562306a36Sopenharmony_ci		gpio += bank->width;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return ret;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic void omap_gpio_init_context(struct gpio_bank *p)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	const struct omap_gpio_reg_offs *regs = p->regs;
109362306a36Sopenharmony_ci	void __iomem *base = p->base;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	p->context.sysconfig	= readl_relaxed(base + regs->sysconfig);
109662306a36Sopenharmony_ci	p->context.ctrl		= readl_relaxed(base + regs->ctrl);
109762306a36Sopenharmony_ci	p->context.oe		= readl_relaxed(base + regs->direction);
109862306a36Sopenharmony_ci	p->context.wake_en	= readl_relaxed(base + regs->wkup_en);
109962306a36Sopenharmony_ci	p->context.leveldetect0	= readl_relaxed(base + regs->leveldetect0);
110062306a36Sopenharmony_ci	p->context.leveldetect1	= readl_relaxed(base + regs->leveldetect1);
110162306a36Sopenharmony_ci	p->context.risingdetect	= readl_relaxed(base + regs->risingdetect);
110262306a36Sopenharmony_ci	p->context.fallingdetect = readl_relaxed(base + regs->fallingdetect);
110362306a36Sopenharmony_ci	p->context.irqenable1	= readl_relaxed(base + regs->irqenable);
110462306a36Sopenharmony_ci	p->context.irqenable2	= readl_relaxed(base + regs->irqenable2);
110562306a36Sopenharmony_ci	p->context.dataout	= readl_relaxed(base + regs->dataout);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	p->context_valid = true;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic void omap_gpio_restore_context(struct gpio_bank *bank)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	const struct omap_gpio_reg_offs *regs = bank->regs;
111362306a36Sopenharmony_ci	void __iomem *base = bank->base;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	writel_relaxed(bank->context.sysconfig, base + regs->sysconfig);
111662306a36Sopenharmony_ci	writel_relaxed(bank->context.wake_en, base + regs->wkup_en);
111762306a36Sopenharmony_ci	writel_relaxed(bank->context.ctrl, base + regs->ctrl);
111862306a36Sopenharmony_ci	writel_relaxed(bank->context.leveldetect0, base + regs->leveldetect0);
111962306a36Sopenharmony_ci	writel_relaxed(bank->context.leveldetect1, base + regs->leveldetect1);
112062306a36Sopenharmony_ci	writel_relaxed(bank->context.risingdetect, base + regs->risingdetect);
112162306a36Sopenharmony_ci	writel_relaxed(bank->context.fallingdetect, base + regs->fallingdetect);
112262306a36Sopenharmony_ci	writel_relaxed(bank->context.dataout, base + regs->dataout);
112362306a36Sopenharmony_ci	writel_relaxed(bank->context.oe, base + regs->direction);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (bank->dbck_enable_mask) {
112662306a36Sopenharmony_ci		writel_relaxed(bank->context.debounce, base + regs->debounce);
112762306a36Sopenharmony_ci		writel_relaxed(bank->context.debounce_en,
112862306a36Sopenharmony_ci			       base + regs->debounce_en);
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	writel_relaxed(bank->context.irqenable1, base + regs->irqenable);
113262306a36Sopenharmony_ci	writel_relaxed(bank->context.irqenable2, base + regs->irqenable2);
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	struct device *dev = bank->chip.parent;
113862306a36Sopenharmony_ci	void __iomem *base = bank->base;
113962306a36Sopenharmony_ci	u32 mask, nowake;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	bank->saved_datain = readl_relaxed(base + bank->regs->datain);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* Save syconfig, it's runtime value can be different from init value */
114462306a36Sopenharmony_ci	if (bank->loses_context)
114562306a36Sopenharmony_ci		bank->context.sysconfig = readl_relaxed(base + bank->regs->sysconfig);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (!bank->enabled_non_wakeup_gpios)
114862306a36Sopenharmony_ci		goto update_gpio_context_count;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* Check for pending EDGE_FALLING, ignore EDGE_BOTH */
115162306a36Sopenharmony_ci	mask = bank->enabled_non_wakeup_gpios & bank->context.fallingdetect;
115262306a36Sopenharmony_ci	mask &= ~bank->context.risingdetect;
115362306a36Sopenharmony_ci	bank->saved_datain |= mask;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	/* Check for pending EDGE_RISING, ignore EDGE_BOTH */
115662306a36Sopenharmony_ci	mask = bank->enabled_non_wakeup_gpios & bank->context.risingdetect;
115762306a36Sopenharmony_ci	mask &= ~bank->context.fallingdetect;
115862306a36Sopenharmony_ci	bank->saved_datain &= ~mask;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	if (!may_lose_context)
116162306a36Sopenharmony_ci		goto update_gpio_context_count;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/*
116462306a36Sopenharmony_ci	 * If going to OFF, remove triggering for all wkup domain
116562306a36Sopenharmony_ci	 * non-wakeup GPIOs.  Otherwise spurious IRQs will be
116662306a36Sopenharmony_ci	 * generated.  See OMAP2420 Errata item 1.101.
116762306a36Sopenharmony_ci	 */
116862306a36Sopenharmony_ci	if (!bank->loses_context && bank->enabled_non_wakeup_gpios) {
116962306a36Sopenharmony_ci		nowake = bank->enabled_non_wakeup_gpios;
117062306a36Sopenharmony_ci		omap_gpio_rmw(base + bank->regs->fallingdetect, nowake, ~nowake);
117162306a36Sopenharmony_ci		omap_gpio_rmw(base + bank->regs->risingdetect, nowake, ~nowake);
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ciupdate_gpio_context_count:
117562306a36Sopenharmony_ci	if (bank->get_context_loss_count)
117662306a36Sopenharmony_ci		bank->context_loss_count =
117762306a36Sopenharmony_ci				bank->get_context_loss_count(dev);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	omap_gpio_dbck_disable(bank);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void omap_gpio_unidle(struct gpio_bank *bank)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct device *dev = bank->chip.parent;
118562306a36Sopenharmony_ci	u32 l = 0, gen, gen0, gen1;
118662306a36Sopenharmony_ci	int c;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * On the first resume during the probe, the context has not
119062306a36Sopenharmony_ci	 * been initialised and so initialise it now. Also initialise
119162306a36Sopenharmony_ci	 * the context loss count.
119262306a36Sopenharmony_ci	 */
119362306a36Sopenharmony_ci	if (bank->loses_context && !bank->context_valid) {
119462306a36Sopenharmony_ci		omap_gpio_init_context(bank);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		if (bank->get_context_loss_count)
119762306a36Sopenharmony_ci			bank->context_loss_count =
119862306a36Sopenharmony_ci				bank->get_context_loss_count(dev);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	omap_gpio_dbck_enable(bank);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (bank->loses_context) {
120462306a36Sopenharmony_ci		if (!bank->get_context_loss_count) {
120562306a36Sopenharmony_ci			omap_gpio_restore_context(bank);
120662306a36Sopenharmony_ci		} else {
120762306a36Sopenharmony_ci			c = bank->get_context_loss_count(dev);
120862306a36Sopenharmony_ci			if (c != bank->context_loss_count) {
120962306a36Sopenharmony_ci				omap_gpio_restore_context(bank);
121062306a36Sopenharmony_ci			} else {
121162306a36Sopenharmony_ci				return;
121262306a36Sopenharmony_ci			}
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci	} else {
121562306a36Sopenharmony_ci		/* Restore changes done for OMAP2420 errata 1.101 */
121662306a36Sopenharmony_ci		writel_relaxed(bank->context.fallingdetect,
121762306a36Sopenharmony_ci			       bank->base + bank->regs->fallingdetect);
121862306a36Sopenharmony_ci		writel_relaxed(bank->context.risingdetect,
121962306a36Sopenharmony_ci			       bank->base + bank->regs->risingdetect);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	l = readl_relaxed(bank->base + bank->regs->datain);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/*
122562306a36Sopenharmony_ci	 * Check if any of the non-wakeup interrupt GPIOs have changed
122662306a36Sopenharmony_ci	 * state.  If so, generate an IRQ by software.  This is
122762306a36Sopenharmony_ci	 * horribly racy, but it's the best we can do to work around
122862306a36Sopenharmony_ci	 * this silicon bug.
122962306a36Sopenharmony_ci	 */
123062306a36Sopenharmony_ci	l ^= bank->saved_datain;
123162306a36Sopenharmony_ci	l &= bank->enabled_non_wakeup_gpios;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	/*
123462306a36Sopenharmony_ci	 * No need to generate IRQs for the rising edge for gpio IRQs
123562306a36Sopenharmony_ci	 * configured with falling edge only; and vice versa.
123662306a36Sopenharmony_ci	 */
123762306a36Sopenharmony_ci	gen0 = l & bank->context.fallingdetect;
123862306a36Sopenharmony_ci	gen0 &= bank->saved_datain;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	gen1 = l & bank->context.risingdetect;
124162306a36Sopenharmony_ci	gen1 &= ~(bank->saved_datain);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* FIXME: Consider GPIO IRQs with level detections properly! */
124462306a36Sopenharmony_ci	gen = l & (~(bank->context.fallingdetect) &
124562306a36Sopenharmony_ci					 ~(bank->context.risingdetect));
124662306a36Sopenharmony_ci	/* Consider all GPIO IRQs needed to be updated */
124762306a36Sopenharmony_ci	gen |= gen0 | gen1;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (gen) {
125062306a36Sopenharmony_ci		u32 old0, old1;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		old0 = readl_relaxed(bank->base + bank->regs->leveldetect0);
125362306a36Sopenharmony_ci		old1 = readl_relaxed(bank->base + bank->regs->leveldetect1);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		if (!bank->regs->irqstatus_raw0) {
125662306a36Sopenharmony_ci			writel_relaxed(old0 | gen, bank->base +
125762306a36Sopenharmony_ci						bank->regs->leveldetect0);
125862306a36Sopenharmony_ci			writel_relaxed(old1 | gen, bank->base +
125962306a36Sopenharmony_ci						bank->regs->leveldetect1);
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		if (bank->regs->irqstatus_raw0) {
126362306a36Sopenharmony_ci			writel_relaxed(old0 | l, bank->base +
126462306a36Sopenharmony_ci						bank->regs->leveldetect0);
126562306a36Sopenharmony_ci			writel_relaxed(old1 | l, bank->base +
126662306a36Sopenharmony_ci						bank->regs->leveldetect1);
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci		writel_relaxed(old0, bank->base + bank->regs->leveldetect0);
126962306a36Sopenharmony_ci		writel_relaxed(old1, bank->base + bank->regs->leveldetect1);
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic int gpio_omap_cpu_notifier(struct notifier_block *nb,
127462306a36Sopenharmony_ci				  unsigned long cmd, void *v)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct gpio_bank *bank;
127762306a36Sopenharmony_ci	unsigned long flags;
127862306a36Sopenharmony_ci	int ret = NOTIFY_OK;
127962306a36Sopenharmony_ci	u32 isr, mask;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	bank = container_of(nb, struct gpio_bank, nb);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
128462306a36Sopenharmony_ci	if (bank->is_suspended)
128562306a36Sopenharmony_ci		goto out_unlock;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	switch (cmd) {
128862306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
128962306a36Sopenharmony_ci		mask = omap_get_gpio_irqbank_mask(bank);
129062306a36Sopenharmony_ci		isr = readl_relaxed(bank->base + bank->regs->irqstatus) & mask;
129162306a36Sopenharmony_ci		if (isr) {
129262306a36Sopenharmony_ci			ret = NOTIFY_BAD;
129362306a36Sopenharmony_ci			break;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci		omap_gpio_idle(bank, true);
129662306a36Sopenharmony_ci		break;
129762306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER_FAILED:
129862306a36Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
129962306a36Sopenharmony_ci		omap_gpio_unidle(bank);
130062306a36Sopenharmony_ci		break;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ciout_unlock:
130462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return ret;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic const struct omap_gpio_reg_offs omap2_gpio_regs = {
131062306a36Sopenharmony_ci	.revision =		OMAP24XX_GPIO_REVISION,
131162306a36Sopenharmony_ci	.sysconfig =		OMAP24XX_GPIO_SYSCONFIG,
131262306a36Sopenharmony_ci	.direction =		OMAP24XX_GPIO_OE,
131362306a36Sopenharmony_ci	.datain =		OMAP24XX_GPIO_DATAIN,
131462306a36Sopenharmony_ci	.dataout =		OMAP24XX_GPIO_DATAOUT,
131562306a36Sopenharmony_ci	.set_dataout =		OMAP24XX_GPIO_SETDATAOUT,
131662306a36Sopenharmony_ci	.clr_dataout =		OMAP24XX_GPIO_CLEARDATAOUT,
131762306a36Sopenharmony_ci	.irqstatus =		OMAP24XX_GPIO_IRQSTATUS1,
131862306a36Sopenharmony_ci	.irqstatus2 =		OMAP24XX_GPIO_IRQSTATUS2,
131962306a36Sopenharmony_ci	.irqenable =		OMAP24XX_GPIO_IRQENABLE1,
132062306a36Sopenharmony_ci	.irqenable2 =		OMAP24XX_GPIO_IRQENABLE2,
132162306a36Sopenharmony_ci	.set_irqenable =	OMAP24XX_GPIO_SETIRQENABLE1,
132262306a36Sopenharmony_ci	.clr_irqenable =	OMAP24XX_GPIO_CLEARIRQENABLE1,
132362306a36Sopenharmony_ci	.debounce =		OMAP24XX_GPIO_DEBOUNCE_VAL,
132462306a36Sopenharmony_ci	.debounce_en =		OMAP24XX_GPIO_DEBOUNCE_EN,
132562306a36Sopenharmony_ci	.ctrl =			OMAP24XX_GPIO_CTRL,
132662306a36Sopenharmony_ci	.wkup_en =		OMAP24XX_GPIO_WAKE_EN,
132762306a36Sopenharmony_ci	.leveldetect0 =		OMAP24XX_GPIO_LEVELDETECT0,
132862306a36Sopenharmony_ci	.leveldetect1 =		OMAP24XX_GPIO_LEVELDETECT1,
132962306a36Sopenharmony_ci	.risingdetect =		OMAP24XX_GPIO_RISINGDETECT,
133062306a36Sopenharmony_ci	.fallingdetect =	OMAP24XX_GPIO_FALLINGDETECT,
133162306a36Sopenharmony_ci};
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic const struct omap_gpio_reg_offs omap4_gpio_regs = {
133462306a36Sopenharmony_ci	.revision =		OMAP4_GPIO_REVISION,
133562306a36Sopenharmony_ci	.sysconfig =		OMAP4_GPIO_SYSCONFIG,
133662306a36Sopenharmony_ci	.direction =		OMAP4_GPIO_OE,
133762306a36Sopenharmony_ci	.datain =		OMAP4_GPIO_DATAIN,
133862306a36Sopenharmony_ci	.dataout =		OMAP4_GPIO_DATAOUT,
133962306a36Sopenharmony_ci	.set_dataout =		OMAP4_GPIO_SETDATAOUT,
134062306a36Sopenharmony_ci	.clr_dataout =		OMAP4_GPIO_CLEARDATAOUT,
134162306a36Sopenharmony_ci	.irqstatus =		OMAP4_GPIO_IRQSTATUS0,
134262306a36Sopenharmony_ci	.irqstatus2 =		OMAP4_GPIO_IRQSTATUS1,
134362306a36Sopenharmony_ci	.irqstatus_raw0 =	OMAP4_GPIO_IRQSTATUSRAW0,
134462306a36Sopenharmony_ci	.irqstatus_raw1 =	OMAP4_GPIO_IRQSTATUSRAW1,
134562306a36Sopenharmony_ci	.irqenable =		OMAP4_GPIO_IRQSTATUSSET0,
134662306a36Sopenharmony_ci	.irqenable2 =		OMAP4_GPIO_IRQSTATUSSET1,
134762306a36Sopenharmony_ci	.set_irqenable =	OMAP4_GPIO_IRQSTATUSSET0,
134862306a36Sopenharmony_ci	.clr_irqenable =	OMAP4_GPIO_IRQSTATUSCLR0,
134962306a36Sopenharmony_ci	.debounce =		OMAP4_GPIO_DEBOUNCINGTIME,
135062306a36Sopenharmony_ci	.debounce_en =		OMAP4_GPIO_DEBOUNCENABLE,
135162306a36Sopenharmony_ci	.ctrl =			OMAP4_GPIO_CTRL,
135262306a36Sopenharmony_ci	.wkup_en =		OMAP4_GPIO_IRQWAKEN0,
135362306a36Sopenharmony_ci	.leveldetect0 =		OMAP4_GPIO_LEVELDETECT0,
135462306a36Sopenharmony_ci	.leveldetect1 =		OMAP4_GPIO_LEVELDETECT1,
135562306a36Sopenharmony_ci	.risingdetect =		OMAP4_GPIO_RISINGDETECT,
135662306a36Sopenharmony_ci	.fallingdetect =	OMAP4_GPIO_FALLINGDETECT,
135762306a36Sopenharmony_ci};
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic const struct omap_gpio_platform_data omap2_pdata = {
136062306a36Sopenharmony_ci	.regs = &omap2_gpio_regs,
136162306a36Sopenharmony_ci	.bank_width = 32,
136262306a36Sopenharmony_ci	.dbck_flag = false,
136362306a36Sopenharmony_ci};
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic const struct omap_gpio_platform_data omap3_pdata = {
136662306a36Sopenharmony_ci	.regs = &omap2_gpio_regs,
136762306a36Sopenharmony_ci	.bank_width = 32,
136862306a36Sopenharmony_ci	.dbck_flag = true,
136962306a36Sopenharmony_ci};
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_cistatic const struct omap_gpio_platform_data omap4_pdata = {
137262306a36Sopenharmony_ci	.regs = &omap4_gpio_regs,
137362306a36Sopenharmony_ci	.bank_width = 32,
137462306a36Sopenharmony_ci	.dbck_flag = true,
137562306a36Sopenharmony_ci};
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_cistatic const struct of_device_id omap_gpio_match[] = {
137862306a36Sopenharmony_ci	{
137962306a36Sopenharmony_ci		.compatible = "ti,omap4-gpio",
138062306a36Sopenharmony_ci		.data = &omap4_pdata,
138162306a36Sopenharmony_ci	},
138262306a36Sopenharmony_ci	{
138362306a36Sopenharmony_ci		.compatible = "ti,omap3-gpio",
138462306a36Sopenharmony_ci		.data = &omap3_pdata,
138562306a36Sopenharmony_ci	},
138662306a36Sopenharmony_ci	{
138762306a36Sopenharmony_ci		.compatible = "ti,omap2-gpio",
138862306a36Sopenharmony_ci		.data = &omap2_pdata,
138962306a36Sopenharmony_ci	},
139062306a36Sopenharmony_ci	{ },
139162306a36Sopenharmony_ci};
139262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_gpio_match);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic int omap_gpio_probe(struct platform_device *pdev)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
139762306a36Sopenharmony_ci	struct device_node *node = dev->of_node;
139862306a36Sopenharmony_ci	const struct omap_gpio_platform_data *pdata;
139962306a36Sopenharmony_ci	struct gpio_bank *bank;
140062306a36Sopenharmony_ci	int ret;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	pdata = device_get_match_data(dev);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	pdata = pdata ?: dev_get_platdata(dev);
140562306a36Sopenharmony_ci	if (!pdata)
140662306a36Sopenharmony_ci		return -EINVAL;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
140962306a36Sopenharmony_ci	if (!bank)
141062306a36Sopenharmony_ci		return -ENOMEM;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	bank->dev = dev;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	bank->irq = platform_get_irq(pdev, 0);
141562306a36Sopenharmony_ci	if (bank->irq < 0)
141662306a36Sopenharmony_ci		return bank->irq;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	bank->chip.parent = dev;
141962306a36Sopenharmony_ci	bank->chip.owner = THIS_MODULE;
142062306a36Sopenharmony_ci	bank->dbck_flag = pdata->dbck_flag;
142162306a36Sopenharmony_ci	bank->stride = pdata->bank_stride;
142262306a36Sopenharmony_ci	bank->width = pdata->bank_width;
142362306a36Sopenharmony_ci	bank->is_mpuio = pdata->is_mpuio;
142462306a36Sopenharmony_ci	bank->non_wakeup_gpios = pdata->non_wakeup_gpios;
142562306a36Sopenharmony_ci	bank->regs = pdata->regs;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (node) {
142862306a36Sopenharmony_ci		if (!of_property_read_bool(node, "ti,gpio-always-on"))
142962306a36Sopenharmony_ci			bank->loses_context = true;
143062306a36Sopenharmony_ci	} else {
143162306a36Sopenharmony_ci		bank->loses_context = pdata->loses_context;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci		if (bank->loses_context)
143462306a36Sopenharmony_ci			bank->get_context_loss_count =
143562306a36Sopenharmony_ci				pdata->get_context_loss_count;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (bank->regs->set_dataout && bank->regs->clr_dataout)
143962306a36Sopenharmony_ci		bank->set_dataout = omap_set_gpio_dataout_reg;
144062306a36Sopenharmony_ci	else
144162306a36Sopenharmony_ci		bank->set_dataout = omap_set_gpio_dataout_mask;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	raw_spin_lock_init(&bank->lock);
144462306a36Sopenharmony_ci	raw_spin_lock_init(&bank->wa_lock);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* Static mapping, never released */
144762306a36Sopenharmony_ci	bank->base = devm_platform_ioremap_resource(pdev, 0);
144862306a36Sopenharmony_ci	if (IS_ERR(bank->base)) {
144962306a36Sopenharmony_ci		return PTR_ERR(bank->base);
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (bank->dbck_flag) {
145362306a36Sopenharmony_ci		bank->dbck = devm_clk_get(dev, "dbclk");
145462306a36Sopenharmony_ci		if (IS_ERR(bank->dbck)) {
145562306a36Sopenharmony_ci			dev_err(dev,
145662306a36Sopenharmony_ci				"Could not get gpio dbck. Disable debounce\n");
145762306a36Sopenharmony_ci			bank->dbck_flag = false;
145862306a36Sopenharmony_ci		} else {
145962306a36Sopenharmony_ci			clk_prepare(bank->dbck);
146062306a36Sopenharmony_ci		}
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	platform_set_drvdata(pdev, bank);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	pm_runtime_enable(dev);
146662306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (bank->is_mpuio)
146962306a36Sopenharmony_ci		omap_mpuio_init(bank);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	omap_gpio_mod_init(bank);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	ret = omap_gpio_chip_init(bank, dev);
147462306a36Sopenharmony_ci	if (ret) {
147562306a36Sopenharmony_ci		pm_runtime_put_sync(dev);
147662306a36Sopenharmony_ci		pm_runtime_disable(dev);
147762306a36Sopenharmony_ci		if (bank->dbck_flag)
147862306a36Sopenharmony_ci			clk_unprepare(bank->dbck);
147962306a36Sopenharmony_ci		return ret;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	omap_gpio_show_rev(bank);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	bank->nb.notifier_call = gpio_omap_cpu_notifier;
148562306a36Sopenharmony_ci	cpu_pm_register_notifier(&bank->nb);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	pm_runtime_put(dev);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return 0;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic int omap_gpio_remove(struct platform_device *pdev)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct gpio_bank *bank = platform_get_drvdata(pdev);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	cpu_pm_unregister_notifier(&bank->nb);
149762306a36Sopenharmony_ci	gpiochip_remove(&bank->chip);
149862306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
149962306a36Sopenharmony_ci	if (bank->dbck_flag)
150062306a36Sopenharmony_ci		clk_unprepare(bank->dbck);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	return 0;
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistatic int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct gpio_bank *bank = dev_get_drvdata(dev);
150862306a36Sopenharmony_ci	unsigned long flags;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
151162306a36Sopenharmony_ci	omap_gpio_idle(bank, true);
151262306a36Sopenharmony_ci	bank->is_suspended = true;
151362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return 0;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cistatic int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	struct gpio_bank *bank = dev_get_drvdata(dev);
152162306a36Sopenharmony_ci	unsigned long flags;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&bank->lock, flags);
152462306a36Sopenharmony_ci	omap_gpio_unidle(bank);
152562306a36Sopenharmony_ci	bank->is_suspended = false;
152662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&bank->lock, flags);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	return 0;
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic int __maybe_unused omap_gpio_suspend(struct device *dev)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct gpio_bank *bank = dev_get_drvdata(dev);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	if (bank->is_suspended)
153662306a36Sopenharmony_ci		return 0;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	bank->needs_resume = 1;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return omap_gpio_runtime_suspend(dev);
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic int __maybe_unused omap_gpio_resume(struct device *dev)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	struct gpio_bank *bank = dev_get_drvdata(dev);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	if (!bank->needs_resume)
154862306a36Sopenharmony_ci		return 0;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	bank->needs_resume = 0;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	return omap_gpio_runtime_resume(dev);
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic const struct dev_pm_ops gpio_pm_ops = {
155662306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
155762306a36Sopenharmony_ci									NULL)
155862306a36Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
155962306a36Sopenharmony_ci};
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic struct platform_driver omap_gpio_driver = {
156262306a36Sopenharmony_ci	.probe		= omap_gpio_probe,
156362306a36Sopenharmony_ci	.remove		= omap_gpio_remove,
156462306a36Sopenharmony_ci	.driver		= {
156562306a36Sopenharmony_ci		.name	= "omap_gpio",
156662306a36Sopenharmony_ci		.pm	= &gpio_pm_ops,
156762306a36Sopenharmony_ci		.of_match_table = omap_gpio_match,
156862306a36Sopenharmony_ci	},
156962306a36Sopenharmony_ci};
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci/*
157262306a36Sopenharmony_ci * gpio driver register needs to be done before
157362306a36Sopenharmony_ci * machine_init functions access gpio APIs.
157462306a36Sopenharmony_ci * Hence omap_gpio_drv_reg() is a postcore_initcall.
157562306a36Sopenharmony_ci */
157662306a36Sopenharmony_cistatic int __init omap_gpio_drv_reg(void)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	return platform_driver_register(&omap_gpio_driver);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_cipostcore_initcall(omap_gpio_drv_reg);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_cistatic void __exit omap_gpio_exit(void)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	platform_driver_unregister(&omap_gpio_driver);
158562306a36Sopenharmony_ci}
158662306a36Sopenharmony_cimodule_exit(omap_gpio_exit);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ciMODULE_DESCRIPTION("omap gpio driver");
158962306a36Sopenharmony_ciMODULE_ALIAS("platform:gpio-omap");
159062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1591