162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci//		http://www.samsung.com/
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright 2008 Openmoko, Inc.
762306a36Sopenharmony_ci// Copyright 2008 Simtec Electronics
862306a36Sopenharmony_ci//      Ben Dooks <ben@simtec.co.uk>
962306a36Sopenharmony_ci//      http://armlinux.simtec.co.uk/
1062306a36Sopenharmony_ci//
1162306a36Sopenharmony_ci// Samsung - GPIOlib support
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/gpio.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/device.h>
2262306a36Sopenharmony_ci#include <linux/ioport.h>
2362306a36Sopenharmony_ci#include <linux/of.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/of_address.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <asm/irq.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "irqs.h"
3062306a36Sopenharmony_ci#include "map.h"
3162306a36Sopenharmony_ci#include "regs-gpio.h"
3262306a36Sopenharmony_ci#include "gpio-samsung.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "cpu.h"
3562306a36Sopenharmony_ci#include "gpio-core.h"
3662306a36Sopenharmony_ci#include "gpio-cfg.h"
3762306a36Sopenharmony_ci#include "gpio-cfg-helpers.h"
3862306a36Sopenharmony_ci#include "pm.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip,
4162306a36Sopenharmony_ci				unsigned int off, samsung_gpio_pull_t pull)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	void __iomem *reg = chip->base + 0x08;
4462306a36Sopenharmony_ci	int shift = off * 2;
4562306a36Sopenharmony_ci	u32 pup;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	pup = __raw_readl(reg);
4862306a36Sopenharmony_ci	pup &= ~(3 << shift);
4962306a36Sopenharmony_ci	pup |= pull << shift;
5062306a36Sopenharmony_ci	__raw_writel(pup, reg);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip,
5662306a36Sopenharmony_ci						unsigned int off)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	void __iomem *reg = chip->base + 0x08;
5962306a36Sopenharmony_ci	int shift = off * 2;
6062306a36Sopenharmony_ci	u32 pup = __raw_readl(reg);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	pup >>= shift;
6362306a36Sopenharmony_ci	pup &= 0x3;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return (__force samsung_gpio_pull_t)pup;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip,
6962306a36Sopenharmony_ci				    unsigned int off, unsigned int cfg)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	void __iomem *reg = chip->base;
7262306a36Sopenharmony_ci	unsigned int shift = off * 2;
7362306a36Sopenharmony_ci	u32 con;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (samsung_gpio_is_cfg_special(cfg)) {
7662306a36Sopenharmony_ci		cfg &= 0xf;
7762306a36Sopenharmony_ci		if (cfg > 3)
7862306a36Sopenharmony_ci			return -EINVAL;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		cfg <<= shift;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	con = __raw_readl(reg);
8462306a36Sopenharmony_ci	con &= ~(0x3 << shift);
8562306a36Sopenharmony_ci	con |= cfg;
8662306a36Sopenharmony_ci	__raw_writel(con, reg);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read.
9362306a36Sopenharmony_ci * @chip: The gpio chip that is being configured.
9462306a36Sopenharmony_ci * @off: The offset for the GPIO being configured.
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * The reverse of samsung_gpio_setcfg_2bit(). Will return a value which
9762306a36Sopenharmony_ci * could be directly passed back to samsung_gpio_setcfg_2bit(), from the
9862306a36Sopenharmony_ci * S3C_GPIO_SPECIAL() macro.
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip,
10262306a36Sopenharmony_ci					     unsigned int off)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u32 con;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	con = __raw_readl(chip->base);
10762306a36Sopenharmony_ci	con >>= off * 2;
10862306a36Sopenharmony_ci	con &= 3;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* this conversion works for IN and OUT as well as special mode */
11162306a36Sopenharmony_ci	return S3C_GPIO_SPECIAL(con);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*
11562306a36Sopenharmony_ci * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config.
11662306a36Sopenharmony_ci * @chip: The gpio chip that is being configured.
11762306a36Sopenharmony_ci * @off: The offset for the GPIO being configured.
11862306a36Sopenharmony_ci * @cfg: The configuration value to set.
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * This helper deal with the GPIO cases where the control register has 4 bits
12162306a36Sopenharmony_ci * of control per GPIO, generally in the form of:
12262306a36Sopenharmony_ci *	0000 = Input
12362306a36Sopenharmony_ci *	0001 = Output
12462306a36Sopenharmony_ci *	others = Special functions (dependent on bank)
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * Note, since the code to deal with the case where there are two control
12762306a36Sopenharmony_ci * registers instead of one, we do not have a separate set of functions for
12862306a36Sopenharmony_ci * each case.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip,
13262306a36Sopenharmony_ci				    unsigned int off, unsigned int cfg)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	void __iomem *reg = chip->base;
13562306a36Sopenharmony_ci	unsigned int shift = (off & 7) * 4;
13662306a36Sopenharmony_ci	u32 con;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (off < 8 && chip->chip.ngpio > 8)
13962306a36Sopenharmony_ci		reg -= 4;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (samsung_gpio_is_cfg_special(cfg)) {
14262306a36Sopenharmony_ci		cfg &= 0xf;
14362306a36Sopenharmony_ci		cfg <<= shift;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	con = __raw_readl(reg);
14762306a36Sopenharmony_ci	con &= ~(0xf << shift);
14862306a36Sopenharmony_ci	con |= cfg;
14962306a36Sopenharmony_ci	__raw_writel(con, reg);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read.
15662306a36Sopenharmony_ci * @chip: The gpio chip that is being configured.
15762306a36Sopenharmony_ci * @off: The offset for the GPIO being configured.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration
16062306a36Sopenharmony_ci * register setting into a value the software can use, such as could be passed
16162306a36Sopenharmony_ci * to samsung_gpio_setcfg_4bit().
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * @sa samsung_gpio_getcfg_2bit
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip,
16762306a36Sopenharmony_ci					 unsigned int off)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	void __iomem *reg = chip->base;
17062306a36Sopenharmony_ci	unsigned int shift = (off & 7) * 4;
17162306a36Sopenharmony_ci	u32 con;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (off < 8 && chip->chip.ngpio > 8)
17462306a36Sopenharmony_ci		reg -= 4;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	con = __raw_readl(reg);
17762306a36Sopenharmony_ci	con >>= shift;
17862306a36Sopenharmony_ci	con &= 0xf;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* this conversion works for IN and OUT as well as special mode */
18162306a36Sopenharmony_ci	return S3C_GPIO_SPECIAL(con);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg,
18562306a36Sopenharmony_ci					   int nr_chips)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	for (; nr_chips > 0; nr_chips--, chipcfg++) {
18862306a36Sopenharmony_ci		if (!chipcfg->set_config)
18962306a36Sopenharmony_ci			chipcfg->set_config = samsung_gpio_setcfg_4bit;
19062306a36Sopenharmony_ci		if (!chipcfg->get_config)
19162306a36Sopenharmony_ci			chipcfg->get_config = samsung_gpio_getcfg_4bit;
19262306a36Sopenharmony_ci		if (!chipcfg->set_pull)
19362306a36Sopenharmony_ci			chipcfg->set_pull = samsung_gpio_setpull_updown;
19462306a36Sopenharmony_ci		if (!chipcfg->get_pull)
19562306a36Sopenharmony_ci			chipcfg->get_pull = samsung_gpio_getpull_updown;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic struct samsung_gpio_cfg samsung_gpio_cfgs[] = {
20062306a36Sopenharmony_ci	[0] = {
20162306a36Sopenharmony_ci		.cfg_eint	= 0x0,
20262306a36Sopenharmony_ci	},
20362306a36Sopenharmony_ci	[1] = {
20462306a36Sopenharmony_ci		.cfg_eint	= 0x3,
20562306a36Sopenharmony_ci	},
20662306a36Sopenharmony_ci	[2] = {
20762306a36Sopenharmony_ci		.cfg_eint	= 0x7,
20862306a36Sopenharmony_ci	},
20962306a36Sopenharmony_ci	[3] = {
21062306a36Sopenharmony_ci		.cfg_eint	= 0xF,
21162306a36Sopenharmony_ci	},
21262306a36Sopenharmony_ci	[4] = {
21362306a36Sopenharmony_ci		.cfg_eint	= 0x0,
21462306a36Sopenharmony_ci		.set_config	= samsung_gpio_setcfg_2bit,
21562306a36Sopenharmony_ci		.get_config	= samsung_gpio_getcfg_2bit,
21662306a36Sopenharmony_ci	},
21762306a36Sopenharmony_ci	[5] = {
21862306a36Sopenharmony_ci		.cfg_eint	= 0x2,
21962306a36Sopenharmony_ci		.set_config	= samsung_gpio_setcfg_2bit,
22062306a36Sopenharmony_ci		.get_config	= samsung_gpio_getcfg_2bit,
22162306a36Sopenharmony_ci	},
22262306a36Sopenharmony_ci	[6] = {
22362306a36Sopenharmony_ci		.cfg_eint	= 0x3,
22462306a36Sopenharmony_ci		.set_config	= samsung_gpio_setcfg_2bit,
22562306a36Sopenharmony_ci		.get_config	= samsung_gpio_getcfg_2bit,
22662306a36Sopenharmony_ci	},
22762306a36Sopenharmony_ci	[7] = {
22862306a36Sopenharmony_ci		.set_config	= samsung_gpio_setcfg_2bit,
22962306a36Sopenharmony_ci		.get_config	= samsung_gpio_getcfg_2bit,
23062306a36Sopenharmony_ci	},
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * Default routines for controlling GPIO, based on the original S3C24XX
23562306a36Sopenharmony_ci * GPIO functions which deal with the case where each gpio bank of the
23662306a36Sopenharmony_ci * chip is as following:
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * base + 0x00: Control register, 2 bits per gpio
23962306a36Sopenharmony_ci *	        gpio n: 2 bits starting at (2*n)
24062306a36Sopenharmony_ci *		00 = input, 01 = output, others mean special-function
24162306a36Sopenharmony_ci * base + 0x04: Data register, 1 bit per gpio
24262306a36Sopenharmony_ci *		bit n: data bit n
24362306a36Sopenharmony_ci*/
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
24862306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
24962306a36Sopenharmony_ci	unsigned long flags;
25062306a36Sopenharmony_ci	unsigned long con;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	samsung_gpio_lock(ourchip, flags);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	con = __raw_readl(base + 0x00);
25562306a36Sopenharmony_ci	con &= ~(3 << (offset * 2));
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	__raw_writel(con, base + 0x00);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	samsung_gpio_unlock(ourchip, flags);
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int samsung_gpiolib_2bit_output(struct gpio_chip *chip,
26462306a36Sopenharmony_ci				       unsigned offset, int value)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
26762306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
26862306a36Sopenharmony_ci	unsigned long flags;
26962306a36Sopenharmony_ci	unsigned long dat;
27062306a36Sopenharmony_ci	unsigned long con;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	samsung_gpio_lock(ourchip, flags);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	dat = __raw_readl(base + 0x04);
27562306a36Sopenharmony_ci	dat &= ~(1 << offset);
27662306a36Sopenharmony_ci	if (value)
27762306a36Sopenharmony_ci		dat |= 1 << offset;
27862306a36Sopenharmony_ci	__raw_writel(dat, base + 0x04);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	con = __raw_readl(base + 0x00);
28162306a36Sopenharmony_ci	con &= ~(3 << (offset * 2));
28262306a36Sopenharmony_ci	con |= 1 << (offset * 2);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	__raw_writel(con, base + 0x00);
28562306a36Sopenharmony_ci	__raw_writel(dat, base + 0x04);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	samsung_gpio_unlock(ourchip, flags);
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * The samsung_gpiolib_4bit routines are to control the gpio banks where
29362306a36Sopenharmony_ci * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
29462306a36Sopenharmony_ci * following example:
29562306a36Sopenharmony_ci *
29662306a36Sopenharmony_ci * base + 0x00: Control register, 4 bits per gpio
29762306a36Sopenharmony_ci *		gpio n: 4 bits starting at (4*n)
29862306a36Sopenharmony_ci *		0000 = input, 0001 = output, others mean special-function
29962306a36Sopenharmony_ci * base + 0x04: Data register, 1 bit per gpio
30062306a36Sopenharmony_ci *		bit n: data bit n
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * Note, since the data register is one bit per gpio and is at base + 0x4
30362306a36Sopenharmony_ci * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the
30462306a36Sopenharmony_ci * state of the output.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
30862306a36Sopenharmony_ci				      unsigned int offset)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
31162306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
31262306a36Sopenharmony_ci	unsigned long con;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	con = __raw_readl(base + GPIOCON_OFF);
31562306a36Sopenharmony_ci	if (ourchip->bitmap_gpio_int & BIT(offset))
31662306a36Sopenharmony_ci		con |= 0xf << con_4bit_shift(offset);
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		con &= ~(0xf << con_4bit_shift(offset));
31962306a36Sopenharmony_ci	__raw_writel(con, base + GPIOCON_OFF);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	pr_debug("%s: %p: CON now %08lx\n", __func__, base, con);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int samsung_gpiolib_4bit_output(struct gpio_chip *chip,
32762306a36Sopenharmony_ci				       unsigned int offset, int value)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
33062306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
33162306a36Sopenharmony_ci	unsigned long con;
33262306a36Sopenharmony_ci	unsigned long dat;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	con = __raw_readl(base + GPIOCON_OFF);
33562306a36Sopenharmony_ci	con &= ~(0xf << con_4bit_shift(offset));
33662306a36Sopenharmony_ci	con |= 0x1 << con_4bit_shift(offset);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	dat = __raw_readl(base + GPIODAT_OFF);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (value)
34162306a36Sopenharmony_ci		dat |= 1 << offset;
34262306a36Sopenharmony_ci	else
34362306a36Sopenharmony_ci		dat &= ~(1 << offset);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	__raw_writel(dat, base + GPIODAT_OFF);
34662306a36Sopenharmony_ci	__raw_writel(con, base + GPIOCON_OFF);
34762306a36Sopenharmony_ci	__raw_writel(dat, base + GPIODAT_OFF);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci * The next set of routines are for the case where the GPIO configuration
35662306a36Sopenharmony_ci * registers are 4 bits per GPIO but there is more than one register (the
35762306a36Sopenharmony_ci * bank has more than 8 GPIOs.
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * This case is the similar to the 4 bit case, but the registers are as
36062306a36Sopenharmony_ci * follows:
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
36362306a36Sopenharmony_ci *		gpio n: 4 bits starting at (4*n)
36462306a36Sopenharmony_ci *		0000 = input, 0001 = output, others mean special-function
36562306a36Sopenharmony_ci * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
36662306a36Sopenharmony_ci *		gpio n: 4 bits starting at (4*n)
36762306a36Sopenharmony_ci *		0000 = input, 0001 = output, others mean special-function
36862306a36Sopenharmony_ci * base + 0x08: Data register, 1 bit per gpio
36962306a36Sopenharmony_ci *		bit n: data bit n
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set
37262306a36Sopenharmony_ci * routines we store the 'base + 0x4' address so that these routines see
37362306a36Sopenharmony_ci * the data register at ourchip->base + 0x04.
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int samsung_gpiolib_4bit2_input(struct gpio_chip *chip,
37762306a36Sopenharmony_ci				       unsigned int offset)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
38062306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
38162306a36Sopenharmony_ci	void __iomem *regcon = base;
38262306a36Sopenharmony_ci	unsigned long con;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (offset > 7)
38562306a36Sopenharmony_ci		offset -= 8;
38662306a36Sopenharmony_ci	else
38762306a36Sopenharmony_ci		regcon -= 4;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	con = __raw_readl(regcon);
39062306a36Sopenharmony_ci	con &= ~(0xf << con_4bit_shift(offset));
39162306a36Sopenharmony_ci	__raw_writel(con, regcon);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	pr_debug("%s: %p: CON %08lx\n", __func__, base, con);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int samsung_gpiolib_4bit2_output(struct gpio_chip *chip,
39962306a36Sopenharmony_ci					unsigned int offset, int value)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
40262306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
40362306a36Sopenharmony_ci	void __iomem *regcon = base;
40462306a36Sopenharmony_ci	unsigned long con;
40562306a36Sopenharmony_ci	unsigned long dat;
40662306a36Sopenharmony_ci	unsigned con_offset = offset;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (con_offset > 7)
40962306a36Sopenharmony_ci		con_offset -= 8;
41062306a36Sopenharmony_ci	else
41162306a36Sopenharmony_ci		regcon -= 4;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	con = __raw_readl(regcon);
41462306a36Sopenharmony_ci	con &= ~(0xf << con_4bit_shift(con_offset));
41562306a36Sopenharmony_ci	con |= 0x1 << con_4bit_shift(con_offset);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	dat = __raw_readl(base + GPIODAT_OFF);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (value)
42062306a36Sopenharmony_ci		dat |= 1 << offset;
42162306a36Sopenharmony_ci	else
42262306a36Sopenharmony_ci		dat &= ~(1 << offset);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	__raw_writel(dat, base + GPIODAT_OFF);
42562306a36Sopenharmony_ci	__raw_writel(con, regcon);
42662306a36Sopenharmony_ci	__raw_writel(dat, base + GPIODAT_OFF);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	pr_debug("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic void samsung_gpiolib_set(struct gpio_chip *chip,
43462306a36Sopenharmony_ci				unsigned offset, int value)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
43762306a36Sopenharmony_ci	void __iomem *base = ourchip->base;
43862306a36Sopenharmony_ci	unsigned long flags;
43962306a36Sopenharmony_ci	unsigned long dat;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	samsung_gpio_lock(ourchip, flags);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	dat = __raw_readl(base + 0x04);
44462306a36Sopenharmony_ci	dat &= ~(1 << offset);
44562306a36Sopenharmony_ci	if (value)
44662306a36Sopenharmony_ci		dat |= 1 << offset;
44762306a36Sopenharmony_ci	__raw_writel(dat, base + 0x04);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	samsung_gpio_unlock(ourchip, flags);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
45562306a36Sopenharmony_ci	unsigned long val;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	val = __raw_readl(ourchip->base + 0x04);
45862306a36Sopenharmony_ci	val >>= offset;
45962306a36Sopenharmony_ci	val &= 1;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return val;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/*
46562306a36Sopenharmony_ci * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
46662306a36Sopenharmony_ci * for use with the configuration calls, and other parts of the s3c gpiolib
46762306a36Sopenharmony_ci * support code.
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * Not all s3c support code will need this, as some configurations of cpu
47062306a36Sopenharmony_ci * may only support one or two different configuration options and have an
47162306a36Sopenharmony_ci * easy gpio to samsung_gpio_chip mapping function. If this is the case, then
47262306a36Sopenharmony_ci * the machine support file should provide its own samsung_gpiolib_getchip()
47362306a36Sopenharmony_ci * and any other necessary functions.
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci#ifdef CONFIG_S3C_GPIO_TRACK
47762306a36Sopenharmony_cistruct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END];
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	unsigned int gpn;
48262306a36Sopenharmony_ci	int i;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	gpn = chip->chip.base;
48562306a36Sopenharmony_ci	for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
48662306a36Sopenharmony_ci		BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
48762306a36Sopenharmony_ci		s3c_gpios[gpn] = chip;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci#endif /* CONFIG_S3C_GPIO_TRACK */
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/*
49362306a36Sopenharmony_ci * samsung_gpiolib_add() - add the Samsung gpio_chip.
49462306a36Sopenharmony_ci * @chip: The chip to register
49562306a36Sopenharmony_ci *
49662306a36Sopenharmony_ci * This is a wrapper to gpiochip_add() that takes our specific gpio chip
49762306a36Sopenharmony_ci * information and makes the necessary alterations for the platform and
49862306a36Sopenharmony_ci * notes the information for use with the configuration systems and any
49962306a36Sopenharmony_ci * other parts of the system.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct gpio_chip *gc = &chip->chip;
50562306a36Sopenharmony_ci	int ret;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	BUG_ON(!chip->base);
50862306a36Sopenharmony_ci	BUG_ON(!gc->label);
50962306a36Sopenharmony_ci	BUG_ON(!gc->ngpio);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (!gc->direction_input)
51462306a36Sopenharmony_ci		gc->direction_input = samsung_gpiolib_2bit_input;
51562306a36Sopenharmony_ci	if (!gc->direction_output)
51662306a36Sopenharmony_ci		gc->direction_output = samsung_gpiolib_2bit_output;
51762306a36Sopenharmony_ci	if (!gc->set)
51862306a36Sopenharmony_ci		gc->set = samsung_gpiolib_set;
51962306a36Sopenharmony_ci	if (!gc->get)
52062306a36Sopenharmony_ci		gc->get = samsung_gpiolib_get;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci#ifdef CONFIG_PM
52362306a36Sopenharmony_ci	if (chip->pm != NULL) {
52462306a36Sopenharmony_ci		if (!chip->pm->save || !chip->pm->resume)
52562306a36Sopenharmony_ci			pr_err("gpio: %s has missing PM functions\n",
52662306a36Sopenharmony_ci			       gc->label);
52762306a36Sopenharmony_ci	} else
52862306a36Sopenharmony_ci		pr_err("gpio: %s has no PM function\n", gc->label);
52962306a36Sopenharmony_ci#endif
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* gpiochip_add() prints own failure message on error. */
53262306a36Sopenharmony_ci	ret = gpiochip_add_data(gc, chip);
53362306a36Sopenharmony_ci	if (ret >= 0)
53462306a36Sopenharmony_ci		s3c_gpiolib_track(chip);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip,
53862306a36Sopenharmony_ci						  int nr_chips, void __iomem *base,
53962306a36Sopenharmony_ci						  unsigned int offset)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	int i;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	for (i = 0 ; i < nr_chips; i++, chip++) {
54462306a36Sopenharmony_ci		chip->chip.direction_input = samsung_gpiolib_2bit_input;
54562306a36Sopenharmony_ci		chip->chip.direction_output = samsung_gpiolib_2bit_output;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if (!chip->config)
54862306a36Sopenharmony_ci			chip->config = &samsung_gpio_cfgs[7];
54962306a36Sopenharmony_ci		if (!chip->pm)
55062306a36Sopenharmony_ci			chip->pm = __gpio_pm(&samsung_gpio_pm_2bit);
55162306a36Sopenharmony_ci		if ((base != NULL) && (chip->base == NULL))
55262306a36Sopenharmony_ci			chip->base = base + ((i) * offset);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		samsung_gpiolib_add(chip);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*
55962306a36Sopenharmony_ci * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config.
56062306a36Sopenharmony_ci * @chip: The gpio chip that is being configured.
56162306a36Sopenharmony_ci * @nr_chips: The no of chips (gpio ports) for the GPIO being configured.
56262306a36Sopenharmony_ci *
56362306a36Sopenharmony_ci * This helper deal with the GPIO cases where the control register has 4 bits
56462306a36Sopenharmony_ci * of control per GPIO, generally in the form of:
56562306a36Sopenharmony_ci * 0000 = Input
56662306a36Sopenharmony_ci * 0001 = Output
56762306a36Sopenharmony_ci * others = Special functions (dependent on bank)
56862306a36Sopenharmony_ci *
56962306a36Sopenharmony_ci * Note, since the code to deal with the case where there are two control
57062306a36Sopenharmony_ci * registers instead of one, we do not have a separate set of function
57162306a36Sopenharmony_ci * (samsung_gpiolib_add_4bit2_chips)for each case.
57262306a36Sopenharmony_ci */
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip,
57562306a36Sopenharmony_ci						  int nr_chips, void __iomem *base)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	int i;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	for (i = 0 ; i < nr_chips; i++, chip++) {
58062306a36Sopenharmony_ci		chip->chip.direction_input = samsung_gpiolib_4bit_input;
58162306a36Sopenharmony_ci		chip->chip.direction_output = samsung_gpiolib_4bit_output;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (!chip->config)
58462306a36Sopenharmony_ci			chip->config = &samsung_gpio_cfgs[2];
58562306a36Sopenharmony_ci		if (!chip->pm)
58662306a36Sopenharmony_ci			chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
58762306a36Sopenharmony_ci		if ((base != NULL) && (chip->base == NULL))
58862306a36Sopenharmony_ci			chip->base = base + ((i) * 0x20);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		chip->bitmap_gpio_int = 0;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		samsung_gpiolib_add(chip);
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip,
59762306a36Sopenharmony_ci						   int nr_chips)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	for (; nr_chips > 0; nr_chips--, chip++) {
60062306a36Sopenharmony_ci		chip->chip.direction_input = samsung_gpiolib_4bit2_input;
60162306a36Sopenharmony_ci		chip->chip.direction_output = samsung_gpiolib_4bit2_output;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		if (!chip->config)
60462306a36Sopenharmony_ci			chip->config = &samsung_gpio_cfgs[2];
60562306a36Sopenharmony_ci		if (!chip->pm)
60662306a36Sopenharmony_ci			chip->pm = __gpio_pm(&samsung_gpio_pm_4bit);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		samsung_gpiolib_add(chip);
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ciint samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct samsung_gpio_chip *samsung_chip = gpiochip_get_data(chip);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return samsung_chip->irq_base + offset;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/*
63062306a36Sopenharmony_ci * GPIO bank summary:
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * Bank	GPIOs	Style	SlpCon	ExtInt Group
63362306a36Sopenharmony_ci * A	8	4Bit	Yes	1
63462306a36Sopenharmony_ci * B	7	4Bit	Yes	1
63562306a36Sopenharmony_ci * C	8	4Bit	Yes	2
63662306a36Sopenharmony_ci * D	5	4Bit	Yes	3
63762306a36Sopenharmony_ci * E	5	4Bit	Yes	None
63862306a36Sopenharmony_ci * F	16	2Bit	Yes	4 [1]
63962306a36Sopenharmony_ci * G	7	4Bit	Yes	5
64062306a36Sopenharmony_ci * H	10	4Bit[2]	Yes	6
64162306a36Sopenharmony_ci * I	16	2Bit	Yes	None
64262306a36Sopenharmony_ci * J	12	2Bit	Yes	None
64362306a36Sopenharmony_ci * K	16	4Bit[2]	No	None
64462306a36Sopenharmony_ci * L	15	4Bit[2] No	None
64562306a36Sopenharmony_ci * M	6	4Bit	No	IRQ_EINT
64662306a36Sopenharmony_ci * N	16	2Bit	No	IRQ_EINT
64762306a36Sopenharmony_ci * O	16	2Bit	Yes	7
64862306a36Sopenharmony_ci * P	15	2Bit	Yes	8
64962306a36Sopenharmony_ci * Q	9	2Bit	Yes	9
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * [1] BANKF pins 14,15 do not form part of the external interrupt sources
65262306a36Sopenharmony_ci * [2] BANK has two control registers, GPxCON0 and GPxCON1
65362306a36Sopenharmony_ci */
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic struct samsung_gpio_chip s3c64xx_gpios_4bit[] = {
65662306a36Sopenharmony_ci	{
65762306a36Sopenharmony_ci		.chip	= {
65862306a36Sopenharmony_ci			.base	= S3C64XX_GPA(0),
65962306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_A_NR,
66062306a36Sopenharmony_ci			.label	= "GPA",
66162306a36Sopenharmony_ci		},
66262306a36Sopenharmony_ci	}, {
66362306a36Sopenharmony_ci		.chip	= {
66462306a36Sopenharmony_ci			.base	= S3C64XX_GPB(0),
66562306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_B_NR,
66662306a36Sopenharmony_ci			.label	= "GPB",
66762306a36Sopenharmony_ci		},
66862306a36Sopenharmony_ci	}, {
66962306a36Sopenharmony_ci		.chip	= {
67062306a36Sopenharmony_ci			.base	= S3C64XX_GPC(0),
67162306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_C_NR,
67262306a36Sopenharmony_ci			.label	= "GPC",
67362306a36Sopenharmony_ci		},
67462306a36Sopenharmony_ci	}, {
67562306a36Sopenharmony_ci		.chip	= {
67662306a36Sopenharmony_ci			.base	= S3C64XX_GPD(0),
67762306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_D_NR,
67862306a36Sopenharmony_ci			.label	= "GPD",
67962306a36Sopenharmony_ci		},
68062306a36Sopenharmony_ci	}, {
68162306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[0],
68262306a36Sopenharmony_ci		.chip	= {
68362306a36Sopenharmony_ci			.base	= S3C64XX_GPE(0),
68462306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_E_NR,
68562306a36Sopenharmony_ci			.label	= "GPE",
68662306a36Sopenharmony_ci		},
68762306a36Sopenharmony_ci	}, {
68862306a36Sopenharmony_ci		.base	= S3C64XX_GPG_BASE,
68962306a36Sopenharmony_ci		.chip	= {
69062306a36Sopenharmony_ci			.base	= S3C64XX_GPG(0),
69162306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_G_NR,
69262306a36Sopenharmony_ci			.label	= "GPG",
69362306a36Sopenharmony_ci		},
69462306a36Sopenharmony_ci	}, {
69562306a36Sopenharmony_ci		.base	= S3C64XX_GPM_BASE,
69662306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[1],
69762306a36Sopenharmony_ci		.chip	= {
69862306a36Sopenharmony_ci			.base	= S3C64XX_GPM(0),
69962306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_M_NR,
70062306a36Sopenharmony_ci			.label	= "GPM",
70162306a36Sopenharmony_ci			.to_irq = s3c64xx_gpiolib_mbank_to_irq,
70262306a36Sopenharmony_ci		},
70362306a36Sopenharmony_ci	},
70462306a36Sopenharmony_ci};
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = {
70762306a36Sopenharmony_ci	{
70862306a36Sopenharmony_ci		.base	= S3C64XX_GPH_BASE + 0x4,
70962306a36Sopenharmony_ci		.chip	= {
71062306a36Sopenharmony_ci			.base	= S3C64XX_GPH(0),
71162306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_H_NR,
71262306a36Sopenharmony_ci			.label	= "GPH",
71362306a36Sopenharmony_ci		},
71462306a36Sopenharmony_ci	}, {
71562306a36Sopenharmony_ci		.base	= S3C64XX_GPK_BASE + 0x4,
71662306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[0],
71762306a36Sopenharmony_ci		.chip	= {
71862306a36Sopenharmony_ci			.base	= S3C64XX_GPK(0),
71962306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_K_NR,
72062306a36Sopenharmony_ci			.label	= "GPK",
72162306a36Sopenharmony_ci		},
72262306a36Sopenharmony_ci	}, {
72362306a36Sopenharmony_ci		.base	= S3C64XX_GPL_BASE + 0x4,
72462306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[1],
72562306a36Sopenharmony_ci		.chip	= {
72662306a36Sopenharmony_ci			.base	= S3C64XX_GPL(0),
72762306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_L_NR,
72862306a36Sopenharmony_ci			.label	= "GPL",
72962306a36Sopenharmony_ci			.to_irq = s3c64xx_gpiolib_lbank_to_irq,
73062306a36Sopenharmony_ci		},
73162306a36Sopenharmony_ci	},
73262306a36Sopenharmony_ci};
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic struct samsung_gpio_chip s3c64xx_gpios_2bit[] = {
73562306a36Sopenharmony_ci	{
73662306a36Sopenharmony_ci		.base	= S3C64XX_GPF_BASE,
73762306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[6],
73862306a36Sopenharmony_ci		.chip	= {
73962306a36Sopenharmony_ci			.base	= S3C64XX_GPF(0),
74062306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_F_NR,
74162306a36Sopenharmony_ci			.label	= "GPF",
74262306a36Sopenharmony_ci		},
74362306a36Sopenharmony_ci	}, {
74462306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[7],
74562306a36Sopenharmony_ci		.chip	= {
74662306a36Sopenharmony_ci			.base	= S3C64XX_GPI(0),
74762306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_I_NR,
74862306a36Sopenharmony_ci			.label	= "GPI",
74962306a36Sopenharmony_ci		},
75062306a36Sopenharmony_ci	}, {
75162306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[7],
75262306a36Sopenharmony_ci		.chip	= {
75362306a36Sopenharmony_ci			.base	= S3C64XX_GPJ(0),
75462306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_J_NR,
75562306a36Sopenharmony_ci			.label	= "GPJ",
75662306a36Sopenharmony_ci		},
75762306a36Sopenharmony_ci	}, {
75862306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[6],
75962306a36Sopenharmony_ci		.chip	= {
76062306a36Sopenharmony_ci			.base	= S3C64XX_GPO(0),
76162306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_O_NR,
76262306a36Sopenharmony_ci			.label	= "GPO",
76362306a36Sopenharmony_ci		},
76462306a36Sopenharmony_ci	}, {
76562306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[6],
76662306a36Sopenharmony_ci		.chip	= {
76762306a36Sopenharmony_ci			.base	= S3C64XX_GPP(0),
76862306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_P_NR,
76962306a36Sopenharmony_ci			.label	= "GPP",
77062306a36Sopenharmony_ci		},
77162306a36Sopenharmony_ci	}, {
77262306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[6],
77362306a36Sopenharmony_ci		.chip	= {
77462306a36Sopenharmony_ci			.base	= S3C64XX_GPQ(0),
77562306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_Q_NR,
77662306a36Sopenharmony_ci			.label	= "GPQ",
77762306a36Sopenharmony_ci		},
77862306a36Sopenharmony_ci	}, {
77962306a36Sopenharmony_ci		.base	= S3C64XX_GPN_BASE,
78062306a36Sopenharmony_ci		.irq_base = IRQ_EINT(0),
78162306a36Sopenharmony_ci		.config	= &samsung_gpio_cfgs[5],
78262306a36Sopenharmony_ci		.chip	= {
78362306a36Sopenharmony_ci			.base	= S3C64XX_GPN(0),
78462306a36Sopenharmony_ci			.ngpio	= S3C64XX_GPIO_N_NR,
78562306a36Sopenharmony_ci			.label	= "GPN",
78662306a36Sopenharmony_ci			.to_irq = samsung_gpiolib_to_irq,
78762306a36Sopenharmony_ci		},
78862306a36Sopenharmony_ci	},
78962306a36Sopenharmony_ci};
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci/* TODO: cleanup soc_is_* */
79262306a36Sopenharmony_cistatic __init int samsung_gpiolib_init(void)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	/*
79562306a36Sopenharmony_ci	 * Currently there are two drivers that can provide GPIO support for
79662306a36Sopenharmony_ci	 * Samsung SoCs. For device tree enabled platforms, the new
79762306a36Sopenharmony_ci	 * pinctrl-samsung driver is used, providing both GPIO and pin control
79862306a36Sopenharmony_ci	 * interfaces. For legacy (non-DT) platforms this driver is used.
79962306a36Sopenharmony_ci	 */
80062306a36Sopenharmony_ci	if (of_have_populated_dt())
80162306a36Sopenharmony_ci		return 0;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (soc_is_s3c64xx()) {
80462306a36Sopenharmony_ci		samsung_gpiolib_set_cfg(samsung_gpio_cfgs,
80562306a36Sopenharmony_ci				ARRAY_SIZE(samsung_gpio_cfgs));
80662306a36Sopenharmony_ci		samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit,
80762306a36Sopenharmony_ci				ARRAY_SIZE(s3c64xx_gpios_2bit),
80862306a36Sopenharmony_ci				S3C64XX_VA_GPIO + 0xE0, 0x20);
80962306a36Sopenharmony_ci		samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit,
81062306a36Sopenharmony_ci				ARRAY_SIZE(s3c64xx_gpios_4bit),
81162306a36Sopenharmony_ci				S3C64XX_VA_GPIO);
81262306a36Sopenharmony_ci		samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2,
81362306a36Sopenharmony_ci				ARRAY_SIZE(s3c64xx_gpios_4bit2));
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return 0;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_cicore_initcall(samsung_gpiolib_init);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ciint s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
82362306a36Sopenharmony_ci	unsigned long flags;
82462306a36Sopenharmony_ci	int offset;
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (!chip)
82862306a36Sopenharmony_ci		return -EINVAL;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	offset = pin - chip->chip.base;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	samsung_gpio_lock(chip, flags);
83362306a36Sopenharmony_ci	ret = samsung_gpio_do_setcfg(chip, offset, config);
83462306a36Sopenharmony_ci	samsung_gpio_unlock(chip, flags);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return ret;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ciEXPORT_SYMBOL(s3c_gpio_cfgpin);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ciint s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr,
84162306a36Sopenharmony_ci			  unsigned int cfg)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	int ret;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	for (; nr > 0; nr--, start++) {
84662306a36Sopenharmony_ci		ret = s3c_gpio_cfgpin(start, cfg);
84762306a36Sopenharmony_ci		if (ret != 0)
84862306a36Sopenharmony_ci			return ret;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return 0;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ciint s3c_gpio_cfgall_range(unsigned int start, unsigned int nr,
85662306a36Sopenharmony_ci			  unsigned int cfg, samsung_gpio_pull_t pull)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	int ret;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	for (; nr > 0; nr--, start++) {
86162306a36Sopenharmony_ci		s3c_gpio_setpull(start, pull);
86262306a36Sopenharmony_ci		ret = s3c_gpio_cfgpin(start, cfg);
86362306a36Sopenharmony_ci		if (ret != 0)
86462306a36Sopenharmony_ci			return ret;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return 0;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ciint s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin);
87462306a36Sopenharmony_ci	unsigned long flags;
87562306a36Sopenharmony_ci	int offset, ret;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!chip)
87862306a36Sopenharmony_ci		return -EINVAL;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	offset = pin - chip->chip.base;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	samsung_gpio_lock(chip, flags);
88362306a36Sopenharmony_ci	ret = samsung_gpio_do_setpull(chip, offset, pull);
88462306a36Sopenharmony_ci	samsung_gpio_unlock(chip, flags);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return ret;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ciEXPORT_SYMBOL(s3c_gpio_setpull);
889