18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Renesas R-Car GPIO Support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2014 Renesas Electronics Corporation
68c2ecf20Sopenharmony_ci *  Copyright (C) 2013 Magnus Damm
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/ioport.h>
158c2ecf20Sopenharmony_ci#include <linux/irq.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
228c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct gpio_rcar_bank_info {
268c2ecf20Sopenharmony_ci	u32 iointsel;
278c2ecf20Sopenharmony_ci	u32 inoutsel;
288c2ecf20Sopenharmony_ci	u32 outdt;
298c2ecf20Sopenharmony_ci	u32 posneg;
308c2ecf20Sopenharmony_ci	u32 edglevel;
318c2ecf20Sopenharmony_ci	u32 bothedge;
328c2ecf20Sopenharmony_ci	u32 intmsk;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct gpio_rcar_priv {
368c2ecf20Sopenharmony_ci	void __iomem *base;
378c2ecf20Sopenharmony_ci	spinlock_t lock;
388c2ecf20Sopenharmony_ci	struct device *dev;
398c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
408c2ecf20Sopenharmony_ci	struct irq_chip irq_chip;
418c2ecf20Sopenharmony_ci	unsigned int irq_parent;
428c2ecf20Sopenharmony_ci	atomic_t wakeup_path;
438c2ecf20Sopenharmony_ci	bool has_outdtsel;
448c2ecf20Sopenharmony_ci	bool has_both_edge_trigger;
458c2ecf20Sopenharmony_ci	struct gpio_rcar_bank_info bank_info;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define IOINTSEL 0x00	/* General IO/Interrupt Switching Register */
498c2ecf20Sopenharmony_ci#define INOUTSEL 0x04	/* General Input/Output Switching Register */
508c2ecf20Sopenharmony_ci#define OUTDT 0x08	/* General Output Register */
518c2ecf20Sopenharmony_ci#define INDT 0x0c	/* General Input Register */
528c2ecf20Sopenharmony_ci#define INTDT 0x10	/* Interrupt Display Register */
538c2ecf20Sopenharmony_ci#define INTCLR 0x14	/* Interrupt Clear Register */
548c2ecf20Sopenharmony_ci#define INTMSK 0x18	/* Interrupt Mask Register */
558c2ecf20Sopenharmony_ci#define MSKCLR 0x1c	/* Interrupt Mask Clear Register */
568c2ecf20Sopenharmony_ci#define POSNEG 0x20	/* Positive/Negative Logic Select Register */
578c2ecf20Sopenharmony_ci#define EDGLEVEL 0x24	/* Edge/level Select Register */
588c2ecf20Sopenharmony_ci#define FILONOFF 0x28	/* Chattering Prevention On/Off Register */
598c2ecf20Sopenharmony_ci#define OUTDTSEL 0x40	/* Output Data Select Register */
608c2ecf20Sopenharmony_ci#define BOTHEDGE 0x4c	/* One Edge/Both Edge Select Register */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define RCAR_MAX_GPIO_PER_BANK		32
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return ioread32(p->base + offs);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs,
708c2ecf20Sopenharmony_ci				   u32 value)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	iowrite32(value, p->base + offs);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs,
768c2ecf20Sopenharmony_ci				 int bit, bool value)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	u32 tmp = gpio_rcar_read(p, offs);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (value)
818c2ecf20Sopenharmony_ci		tmp |= BIT(bit);
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		tmp &= ~BIT(bit);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	gpio_rcar_write(p, offs, tmp);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void gpio_rcar_irq_disable(struct irq_data *d)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
918c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(gc);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d)));
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void gpio_rcar_irq_enable(struct irq_data *d)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
998c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(gc);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d)));
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
1058c2ecf20Sopenharmony_ci						  unsigned int hwirq,
1068c2ecf20Sopenharmony_ci						  bool active_high_rising_edge,
1078c2ecf20Sopenharmony_ci						  bool level_trigger,
1088c2ecf20Sopenharmony_ci						  bool both)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	unsigned long flags;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* follow steps in the GPIO documentation for
1138c2ecf20Sopenharmony_ci	 * "Setting Edge-Sensitive Interrupt Input Mode" and
1148c2ecf20Sopenharmony_ci	 * "Setting Level-Sensitive Interrupt Input Mode"
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Configure positive or negative logic in POSNEG */
1208c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Configure edge or level trigger in EDGLEVEL */
1238c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Select one edge or both edges in BOTHEDGE */
1268c2ecf20Sopenharmony_ci	if (p->has_both_edge_trigger)
1278c2ecf20Sopenharmony_ci		gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Select "Interrupt Input Mode" in IOINTSEL */
1308c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* Write INTCLR in case of edge trigger */
1338c2ecf20Sopenharmony_ci	if (!level_trigger)
1348c2ecf20Sopenharmony_ci		gpio_rcar_write(p, INTCLR, BIT(hwirq));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
1428c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(gc);
1438c2ecf20Sopenharmony_ci	unsigned int hwirq = irqd_to_hwirq(d);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	switch (type & IRQ_TYPE_SENSE_MASK) {
1488c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
1498c2ecf20Sopenharmony_ci		gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true,
1508c2ecf20Sopenharmony_ci						      false);
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
1538c2ecf20Sopenharmony_ci		gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true,
1548c2ecf20Sopenharmony_ci						      false);
1558c2ecf20Sopenharmony_ci		break;
1568c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
1578c2ecf20Sopenharmony_ci		gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false,
1588c2ecf20Sopenharmony_ci						      false);
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
1618c2ecf20Sopenharmony_ci		gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false,
1628c2ecf20Sopenharmony_ci						      false);
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
1658c2ecf20Sopenharmony_ci		if (!p->has_both_edge_trigger)
1668c2ecf20Sopenharmony_ci			return -EINVAL;
1678c2ecf20Sopenharmony_ci		gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false,
1688c2ecf20Sopenharmony_ci						      true);
1698c2ecf20Sopenharmony_ci		break;
1708c2ecf20Sopenharmony_ci	default:
1718c2ecf20Sopenharmony_ci		return -EINVAL;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
1798c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(gc);
1808c2ecf20Sopenharmony_ci	int error;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (p->irq_parent) {
1838c2ecf20Sopenharmony_ci		error = irq_set_irq_wake(p->irq_parent, on);
1848c2ecf20Sopenharmony_ci		if (error) {
1858c2ecf20Sopenharmony_ci			dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n",
1868c2ecf20Sopenharmony_ci				p->irq_parent);
1878c2ecf20Sopenharmony_ci			p->irq_parent = 0;
1888c2ecf20Sopenharmony_ci		}
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (on)
1928c2ecf20Sopenharmony_ci		atomic_inc(&p->wakeup_path);
1938c2ecf20Sopenharmony_ci	else
1948c2ecf20Sopenharmony_ci		atomic_dec(&p->wakeup_path);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return 0;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = dev_id;
2028c2ecf20Sopenharmony_ci	u32 pending;
2038c2ecf20Sopenharmony_ci	unsigned int offset, irqs_handled = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	while ((pending = gpio_rcar_read(p, INTDT) &
2068c2ecf20Sopenharmony_ci			  gpio_rcar_read(p, INTMSK))) {
2078c2ecf20Sopenharmony_ci		offset = __ffs(pending);
2088c2ecf20Sopenharmony_ci		gpio_rcar_write(p, INTCLR, BIT(offset));
2098c2ecf20Sopenharmony_ci		generic_handle_irq(irq_find_mapping(p->gpio_chip.irq.domain,
2108c2ecf20Sopenharmony_ci						    offset));
2118c2ecf20Sopenharmony_ci		irqs_handled++;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
2188c2ecf20Sopenharmony_ci						       unsigned int gpio,
2198c2ecf20Sopenharmony_ci						       bool output)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
2228c2ecf20Sopenharmony_ci	unsigned long flags;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* follow steps in the GPIO documentation for
2258c2ecf20Sopenharmony_ci	 * "Setting General Output Mode" and
2268c2ecf20Sopenharmony_ci	 * "Setting General Input Mode"
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* Configure positive logic in POSNEG */
2328c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, POSNEG, gpio, false);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Select "General Input/Output Mode" in IOINTSEL */
2358c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, IOINTSEL, gpio, false);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Select Input Mode or Output Mode in INOUTSEL */
2388c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, INOUTSEL, gpio, output);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Select General Output Register to output data in OUTDTSEL */
2418c2ecf20Sopenharmony_ci	if (p->has_outdtsel && output)
2428c2ecf20Sopenharmony_ci		gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
2508c2ecf20Sopenharmony_ci	int error;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	error = pm_runtime_get_sync(p->dev);
2538c2ecf20Sopenharmony_ci	if (error < 0) {
2548c2ecf20Sopenharmony_ci		pm_runtime_put(p->dev);
2558c2ecf20Sopenharmony_ci		return error;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	error = pinctrl_gpio_request(chip->base + offset);
2598c2ecf20Sopenharmony_ci	if (error)
2608c2ecf20Sopenharmony_ci		pm_runtime_put(p->dev);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return error;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	pinctrl_gpio_free(chip->base + offset);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * Set the GPIO as an input to ensure that the next GPIO request won't
2738c2ecf20Sopenharmony_ci	 * drive the GPIO pin as an output.
2748c2ecf20Sopenharmony_ci	 */
2758c2ecf20Sopenharmony_ci	gpio_rcar_config_general_input_output_mode(chip, offset, false);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	pm_runtime_put(p->dev);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (gpio_rcar_read(p, INOUTSEL) & BIT(offset))
2858c2ecf20Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	gpio_rcar_config_general_input_output_mode(chip, offset, false);
2938c2ecf20Sopenharmony_ci	return 0;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	u32 bit = BIT(offset);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* testing on r8a7790 shows that INDT does not show correct pin state
3018c2ecf20Sopenharmony_ci	 * when configured as output, so use OUTDT in case of output pins */
3028c2ecf20Sopenharmony_ci	if (gpio_rcar_read(gpiochip_get_data(chip), INOUTSEL) & bit)
3038c2ecf20Sopenharmony_ci		return !!(gpio_rcar_read(gpiochip_get_data(chip), OUTDT) & bit);
3048c2ecf20Sopenharmony_ci	else
3058c2ecf20Sopenharmony_ci		return !!(gpio_rcar_read(gpiochip_get_data(chip), INDT) & bit);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
3118c2ecf20Sopenharmony_ci	unsigned long flags;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
3148c2ecf20Sopenharmony_ci	gpio_rcar_modify_bit(p, OUTDT, offset, value);
3158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask,
3198c2ecf20Sopenharmony_ci				   unsigned long *bits)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = gpiochip_get_data(chip);
3228c2ecf20Sopenharmony_ci	unsigned long flags;
3238c2ecf20Sopenharmony_ci	u32 val, bankmask;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);
3268c2ecf20Sopenharmony_ci	if (chip->valid_mask)
3278c2ecf20Sopenharmony_ci		bankmask &= chip->valid_mask[0];
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!bankmask)
3308c2ecf20Sopenharmony_ci		return;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
3338c2ecf20Sopenharmony_ci	val = gpio_rcar_read(p, OUTDT);
3348c2ecf20Sopenharmony_ci	val &= ~bankmask;
3358c2ecf20Sopenharmony_ci	val |= (bankmask & bits[0]);
3368c2ecf20Sopenharmony_ci	gpio_rcar_write(p, OUTDT, val);
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
3418c2ecf20Sopenharmony_ci				      int value)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	/* write GPIO value to output before selecting output mode of pin */
3448c2ecf20Sopenharmony_ci	gpio_rcar_set(chip, offset, value);
3458c2ecf20Sopenharmony_ci	gpio_rcar_config_general_input_output_mode(chip, offset, true);
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistruct gpio_rcar_info {
3508c2ecf20Sopenharmony_ci	bool has_outdtsel;
3518c2ecf20Sopenharmony_ci	bool has_both_edge_trigger;
3528c2ecf20Sopenharmony_ci};
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic const struct gpio_rcar_info gpio_rcar_info_gen1 = {
3558c2ecf20Sopenharmony_ci	.has_outdtsel = false,
3568c2ecf20Sopenharmony_ci	.has_both_edge_trigger = false,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic const struct gpio_rcar_info gpio_rcar_info_gen2 = {
3608c2ecf20Sopenharmony_ci	.has_outdtsel = true,
3618c2ecf20Sopenharmony_ci	.has_both_edge_trigger = true,
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic const struct of_device_id gpio_rcar_of_table[] = {
3658c2ecf20Sopenharmony_ci	{
3668c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7743",
3678c2ecf20Sopenharmony_ci		/* RZ/G1 GPIO is identical to R-Car Gen2. */
3688c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3698c2ecf20Sopenharmony_ci	}, {
3708c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7790",
3718c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3728c2ecf20Sopenharmony_ci	}, {
3738c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7791",
3748c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3758c2ecf20Sopenharmony_ci	}, {
3768c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7792",
3778c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3788c2ecf20Sopenharmony_ci	}, {
3798c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7793",
3808c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3818c2ecf20Sopenharmony_ci	}, {
3828c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7794",
3838c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3848c2ecf20Sopenharmony_ci	}, {
3858c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7795",
3868c2ecf20Sopenharmony_ci		/* Gen3 GPIO is identical to Gen2. */
3878c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3888c2ecf20Sopenharmony_ci	}, {
3898c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-r8a7796",
3908c2ecf20Sopenharmony_ci		/* Gen3 GPIO is identical to Gen2. */
3918c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3928c2ecf20Sopenharmony_ci	}, {
3938c2ecf20Sopenharmony_ci		.compatible = "renesas,rcar-gen1-gpio",
3948c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen1,
3958c2ecf20Sopenharmony_ci	}, {
3968c2ecf20Sopenharmony_ci		.compatible = "renesas,rcar-gen2-gpio",
3978c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
3988c2ecf20Sopenharmony_ci	}, {
3998c2ecf20Sopenharmony_ci		.compatible = "renesas,rcar-gen3-gpio",
4008c2ecf20Sopenharmony_ci		/* Gen3 GPIO is identical to Gen2. */
4018c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen2,
4028c2ecf20Sopenharmony_ci	}, {
4038c2ecf20Sopenharmony_ci		.compatible = "renesas,gpio-rcar",
4048c2ecf20Sopenharmony_ci		.data = &gpio_rcar_info_gen1,
4058c2ecf20Sopenharmony_ci	}, {
4068c2ecf20Sopenharmony_ci		/* Terminator */
4078c2ecf20Sopenharmony_ci	},
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_rcar_of_table);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct device_node *np = p->dev->of_node;
4158c2ecf20Sopenharmony_ci	const struct gpio_rcar_info *info;
4168c2ecf20Sopenharmony_ci	struct of_phandle_args args;
4178c2ecf20Sopenharmony_ci	int ret;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	info = of_device_get_match_data(p->dev);
4208c2ecf20Sopenharmony_ci	p->has_outdtsel = info->has_outdtsel;
4218c2ecf20Sopenharmony_ci	p->has_both_edge_trigger = info->has_both_edge_trigger;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
4248c2ecf20Sopenharmony_ci	*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
4278c2ecf20Sopenharmony_ci		dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
4288c2ecf20Sopenharmony_ci			 *npins, RCAR_MAX_GPIO_PER_BANK);
4298c2ecf20Sopenharmony_ci		*npins = RCAR_MAX_GPIO_PER_BANK;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int gpio_rcar_probe(struct platform_device *pdev)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p;
4388c2ecf20Sopenharmony_ci	struct resource *irq;
4398c2ecf20Sopenharmony_ci	struct gpio_chip *gpio_chip;
4408c2ecf20Sopenharmony_ci	struct irq_chip *irq_chip;
4418c2ecf20Sopenharmony_ci	struct gpio_irq_chip *girq;
4428c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4438c2ecf20Sopenharmony_ci	const char *name = dev_name(dev);
4448c2ecf20Sopenharmony_ci	unsigned int npins;
4458c2ecf20Sopenharmony_ci	int ret;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
4488c2ecf20Sopenharmony_ci	if (!p)
4498c2ecf20Sopenharmony_ci		return -ENOMEM;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	p->dev = dev;
4528c2ecf20Sopenharmony_ci	spin_lock_init(&p->lock);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Get device configuration from DT node */
4558c2ecf20Sopenharmony_ci	ret = gpio_rcar_parse_dt(p, &npins);
4568c2ecf20Sopenharmony_ci	if (ret < 0)
4578c2ecf20Sopenharmony_ci		return ret;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, p);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
4648c2ecf20Sopenharmony_ci	if (!irq) {
4658c2ecf20Sopenharmony_ci		dev_err(dev, "missing IRQ\n");
4668c2ecf20Sopenharmony_ci		ret = -EINVAL;
4678c2ecf20Sopenharmony_ci		goto err0;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	p->base = devm_platform_ioremap_resource(pdev, 0);
4718c2ecf20Sopenharmony_ci	if (IS_ERR(p->base)) {
4728c2ecf20Sopenharmony_ci		ret = PTR_ERR(p->base);
4738c2ecf20Sopenharmony_ci		goto err0;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	gpio_chip = &p->gpio_chip;
4778c2ecf20Sopenharmony_ci	gpio_chip->request = gpio_rcar_request;
4788c2ecf20Sopenharmony_ci	gpio_chip->free = gpio_rcar_free;
4798c2ecf20Sopenharmony_ci	gpio_chip->get_direction = gpio_rcar_get_direction;
4808c2ecf20Sopenharmony_ci	gpio_chip->direction_input = gpio_rcar_direction_input;
4818c2ecf20Sopenharmony_ci	gpio_chip->get = gpio_rcar_get;
4828c2ecf20Sopenharmony_ci	gpio_chip->direction_output = gpio_rcar_direction_output;
4838c2ecf20Sopenharmony_ci	gpio_chip->set = gpio_rcar_set;
4848c2ecf20Sopenharmony_ci	gpio_chip->set_multiple = gpio_rcar_set_multiple;
4858c2ecf20Sopenharmony_ci	gpio_chip->label = name;
4868c2ecf20Sopenharmony_ci	gpio_chip->parent = dev;
4878c2ecf20Sopenharmony_ci	gpio_chip->owner = THIS_MODULE;
4888c2ecf20Sopenharmony_ci	gpio_chip->base = -1;
4898c2ecf20Sopenharmony_ci	gpio_chip->ngpio = npins;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	irq_chip = &p->irq_chip;
4928c2ecf20Sopenharmony_ci	irq_chip->name = "gpio-rcar";
4938c2ecf20Sopenharmony_ci	irq_chip->parent_device = dev;
4948c2ecf20Sopenharmony_ci	irq_chip->irq_mask = gpio_rcar_irq_disable;
4958c2ecf20Sopenharmony_ci	irq_chip->irq_unmask = gpio_rcar_irq_enable;
4968c2ecf20Sopenharmony_ci	irq_chip->irq_set_type = gpio_rcar_irq_set_type;
4978c2ecf20Sopenharmony_ci	irq_chip->irq_set_wake = gpio_rcar_irq_set_wake;
4988c2ecf20Sopenharmony_ci	irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	girq = &gpio_chip->irq;
5018c2ecf20Sopenharmony_ci	girq->chip = irq_chip;
5028c2ecf20Sopenharmony_ci	/* This will let us handle the parent IRQ in the driver */
5038c2ecf20Sopenharmony_ci	girq->parent_handler = NULL;
5048c2ecf20Sopenharmony_ci	girq->num_parents = 0;
5058c2ecf20Sopenharmony_ci	girq->parents = NULL;
5068c2ecf20Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
5078c2ecf20Sopenharmony_ci	girq->handler = handle_level_irq;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	ret = gpiochip_add_data(gpio_chip, p);
5108c2ecf20Sopenharmony_ci	if (ret) {
5118c2ecf20Sopenharmony_ci		dev_err(dev, "failed to add GPIO controller\n");
5128c2ecf20Sopenharmony_ci		goto err0;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	p->irq_parent = irq->start;
5168c2ecf20Sopenharmony_ci	if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler,
5178c2ecf20Sopenharmony_ci			     IRQF_SHARED, name, p)) {
5188c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request IRQ\n");
5198c2ecf20Sopenharmony_ci		ret = -ENOENT;
5208c2ecf20Sopenharmony_ci		goto err1;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	dev_info(dev, "driving %d GPIOs\n", npins);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cierr1:
5288c2ecf20Sopenharmony_ci	gpiochip_remove(gpio_chip);
5298c2ecf20Sopenharmony_cierr0:
5308c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
5318c2ecf20Sopenharmony_ci	return ret;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int gpio_rcar_remove(struct platform_device *pdev)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = platform_get_drvdata(pdev);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	gpiochip_remove(&p->gpio_chip);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
5418c2ecf20Sopenharmony_ci	return 0;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5458c2ecf20Sopenharmony_cistatic int gpio_rcar_suspend(struct device *dev)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = dev_get_drvdata(dev);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL);
5508c2ecf20Sopenharmony_ci	p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL);
5518c2ecf20Sopenharmony_ci	p->bank_info.outdt = gpio_rcar_read(p, OUTDT);
5528c2ecf20Sopenharmony_ci	p->bank_info.intmsk = gpio_rcar_read(p, INTMSK);
5538c2ecf20Sopenharmony_ci	p->bank_info.posneg = gpio_rcar_read(p, POSNEG);
5548c2ecf20Sopenharmony_ci	p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL);
5558c2ecf20Sopenharmony_ci	if (p->has_both_edge_trigger)
5568c2ecf20Sopenharmony_ci		p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (atomic_read(&p->wakeup_path))
5598c2ecf20Sopenharmony_ci		device_set_wakeup_path(dev);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return 0;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int gpio_rcar_resume(struct device *dev)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct gpio_rcar_priv *p = dev_get_drvdata(dev);
5678c2ecf20Sopenharmony_ci	unsigned int offset;
5688c2ecf20Sopenharmony_ci	u32 mask;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
5718c2ecf20Sopenharmony_ci		if (!gpiochip_line_is_valid(&p->gpio_chip, offset))
5728c2ecf20Sopenharmony_ci			continue;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		mask = BIT(offset);
5758c2ecf20Sopenharmony_ci		/* I/O pin */
5768c2ecf20Sopenharmony_ci		if (!(p->bank_info.iointsel & mask)) {
5778c2ecf20Sopenharmony_ci			if (p->bank_info.inoutsel & mask)
5788c2ecf20Sopenharmony_ci				gpio_rcar_direction_output(
5798c2ecf20Sopenharmony_ci					&p->gpio_chip, offset,
5808c2ecf20Sopenharmony_ci					!!(p->bank_info.outdt & mask));
5818c2ecf20Sopenharmony_ci			else
5828c2ecf20Sopenharmony_ci				gpio_rcar_direction_input(&p->gpio_chip,
5838c2ecf20Sopenharmony_ci							  offset);
5848c2ecf20Sopenharmony_ci		} else {
5858c2ecf20Sopenharmony_ci			/* Interrupt pin */
5868c2ecf20Sopenharmony_ci			gpio_rcar_config_interrupt_input_mode(
5878c2ecf20Sopenharmony_ci				p,
5888c2ecf20Sopenharmony_ci				offset,
5898c2ecf20Sopenharmony_ci				!(p->bank_info.posneg & mask),
5908c2ecf20Sopenharmony_ci				!(p->bank_info.edglevel & mask),
5918c2ecf20Sopenharmony_ci				!!(p->bank_info.bothedge & mask));
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci			if (p->bank_info.intmsk & mask)
5948c2ecf20Sopenharmony_ci				gpio_rcar_write(p, MSKCLR, mask);
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return 0;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP*/
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic struct platform_driver gpio_rcar_device_driver = {
6058c2ecf20Sopenharmony_ci	.probe		= gpio_rcar_probe,
6068c2ecf20Sopenharmony_ci	.remove		= gpio_rcar_remove,
6078c2ecf20Sopenharmony_ci	.driver		= {
6088c2ecf20Sopenharmony_ci		.name	= "gpio_rcar",
6098c2ecf20Sopenharmony_ci		.pm     = &gpio_rcar_pm_ops,
6108c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(gpio_rcar_of_table),
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci};
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cimodule_platform_driver(gpio_rcar_device_driver);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm");
6178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas R-Car GPIO Driver");
6188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
619