Commit 51750fb1 authored by Hien Dang's avatar Hien Dang Committed by Linus Walleij
Browse files

gpio: gpio-rcar: Support S2RAM



This patch adds an implementation that saves and restores the state of
GPIO configuration on suspend and resume.

Signed-off-by: default avatarHien Dang <hien.dang.eb@renesas.com>
Signed-off-by: default avatarTakeshi Kihara <takeshi.kihara.df@renesas.com>
[Modify structure of the bank info to simplify a saving registers]
[Remove DEV_PM_OPS macro]
Signed-off-by: default avatarYoshihiro Kaneko <ykaneko0929@gmail.com>
Tested-by: default avatarNguyen Viet Dung <dung.nguyen.aj@renesas.com>
Reviewed-by: default avatarSimon Horman <horms+renesas@verge.net.au>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 62c16234
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -31,6 +31,16 @@
#include <linux/spinlock.h>
#include <linux/slab.h>

struct gpio_rcar_bank_info {
	u32 iointsel;
	u32 inoutsel;
	u32 outdt;
	u32 posneg;
	u32 edglevel;
	u32 bothedge;
	u32 intmsk;
};

struct gpio_rcar_priv {
	void __iomem *base;
	spinlock_t lock;
@@ -41,6 +51,7 @@ struct gpio_rcar_priv {
	unsigned int irq_parent;
	bool has_both_edge_trigger;
	bool needs_clk;
	struct gpio_rcar_bank_info bank_info;
};

#define IOINTSEL 0x00	/* General IO/Interrupt Switching Register */
@@ -531,11 +542,66 @@ static int gpio_rcar_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int gpio_rcar_suspend(struct device *dev)
{
	struct gpio_rcar_priv *p = dev_get_drvdata(dev);

	p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL);
	p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL);
	p->bank_info.outdt = gpio_rcar_read(p, OUTDT);
	p->bank_info.intmsk = gpio_rcar_read(p, INTMSK);
	p->bank_info.posneg = gpio_rcar_read(p, POSNEG);
	p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL);
	if (p->has_both_edge_trigger)
		p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE);

	return 0;
}

static int gpio_rcar_resume(struct device *dev)
{
	struct gpio_rcar_priv *p = dev_get_drvdata(dev);
	unsigned int offset;
	u32 mask;

	for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
		mask = BIT(offset);
		/* I/O pin */
		if (!(p->bank_info.iointsel & mask)) {
			if (p->bank_info.inoutsel & mask)
				gpio_rcar_direction_output(
					&p->gpio_chip, offset,
					!!(p->bank_info.outdt & mask));
			else
				gpio_rcar_direction_input(&p->gpio_chip,
							  offset);
		} else {
			/* Interrupt pin */
			gpio_rcar_config_interrupt_input_mode(
				p,
				offset,
				!(p->bank_info.posneg & mask),
				!(p->bank_info.edglevel & mask),
				!!(p->bank_info.bothedge & mask));

			if (p->bank_info.intmsk & mask)
				gpio_rcar_write(p, MSKCLR, mask);
		}
	}

	return 0;
}
#endif /* CONFIG_PM_SLEEP*/

static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume);

static struct platform_driver gpio_rcar_device_driver = {
	.probe		= gpio_rcar_probe,
	.remove		= gpio_rcar_remove,
	.driver		= {
		.name	= "gpio_rcar",
		.pm     = &gpio_rcar_pm_ops,
		.of_match_table = of_match_ptr(gpio_rcar_of_table),
	}
};