162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Extreme Engineering Solutions.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitops.h>
962306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1062306a36Sopenharmony_ci#include <linux/ioport.h>
1162306a36Sopenharmony_ci#include <linux/mfd/lpc_ich.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define DRV_NAME "gpio_ich"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * GPIO register offsets in GPIO I/O space.
1962306a36Sopenharmony_ci * Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and
2062306a36Sopenharmony_ci * LVLx registers.  Logic in the read/write functions takes a register and
2162306a36Sopenharmony_ci * an absolute bit number and determines the proper register offset and bit
2262306a36Sopenharmony_ci * number in that register.  For example, to read the value of GPIO bit 50
2362306a36Sopenharmony_ci * the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)],
2462306a36Sopenharmony_ci * bit 18 (50%32).
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cienum GPIO_REG {
2762306a36Sopenharmony_ci	GPIO_USE_SEL = 0,
2862306a36Sopenharmony_ci	GPIO_IO_SEL,
2962306a36Sopenharmony_ci	GPIO_LVL,
3062306a36Sopenharmony_ci	GPO_BLINK
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const u8 ichx_regs[4][3] = {
3462306a36Sopenharmony_ci	{0x00, 0x30, 0x40},	/* USE_SEL[1-3] offsets */
3562306a36Sopenharmony_ci	{0x04, 0x34, 0x44},	/* IO_SEL[1-3] offsets */
3662306a36Sopenharmony_ci	{0x0c, 0x38, 0x48},	/* LVL[1-3] offsets */
3762306a36Sopenharmony_ci	{0x18, 0x18, 0x18},	/* BLINK offset */
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const u8 ichx_reglen[3] = {
4162306a36Sopenharmony_ci	0x30, 0x10, 0x10,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const u8 avoton_regs[4][3] = {
4562306a36Sopenharmony_ci	{0x00, 0x80, 0x00},
4662306a36Sopenharmony_ci	{0x04, 0x84, 0x00},
4762306a36Sopenharmony_ci	{0x08, 0x88, 0x00},
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const u8 avoton_reglen[3] = {
5162306a36Sopenharmony_ci	0x10, 0x10, 0x00,
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define ICHX_WRITE(val, reg, base_res)	outl(val, (reg) + (base_res)->start)
5562306a36Sopenharmony_ci#define ICHX_READ(reg, base_res)	inl((reg) + (base_res)->start)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct ichx_desc {
5862306a36Sopenharmony_ci	/* Max GPIO pins the chipset can have */
5962306a36Sopenharmony_ci	uint ngpio;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* chipset registers */
6262306a36Sopenharmony_ci	const u8 (*regs)[3];
6362306a36Sopenharmony_ci	const u8 *reglen;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* GPO_BLINK is available on this chipset */
6662306a36Sopenharmony_ci	bool have_blink;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Whether the chipset has GPIO in GPE0_STS in the PM IO region */
6962306a36Sopenharmony_ci	bool uses_gpe0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* USE_SEL is bogus on some chipsets, eg 3100 */
7262306a36Sopenharmony_ci	u32 use_sel_ignore[3];
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Some chipsets have quirks, let these use their own request/get */
7562306a36Sopenharmony_ci	int (*request)(struct gpio_chip *chip, unsigned int offset);
7662306a36Sopenharmony_ci	int (*get)(struct gpio_chip *chip, unsigned int offset);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * Some chipsets don't let reading output values on GPIO_LVL register
8062306a36Sopenharmony_ci	 * this option allows driver caching written output values
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	bool use_outlvl_cache;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct {
8662306a36Sopenharmony_ci	spinlock_t lock;
8762306a36Sopenharmony_ci	struct device *dev;
8862306a36Sopenharmony_ci	struct gpio_chip chip;
8962306a36Sopenharmony_ci	struct resource *gpio_base;	/* GPIO IO base */
9062306a36Sopenharmony_ci	struct resource *pm_base;	/* Power Management IO base */
9162306a36Sopenharmony_ci	struct ichx_desc *desc;	/* Pointer to chipset-specific description */
9262306a36Sopenharmony_ci	u32 orig_gpio_ctrl;	/* Orig CTRL value, used to restore on exit */
9362306a36Sopenharmony_ci	u8 use_gpio;		/* Which GPIO groups are usable */
9462306a36Sopenharmony_ci	int outlvl_cache[3];	/* cached output values */
9562306a36Sopenharmony_ci} ichx_priv;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int modparam_gpiobase = -1;	/* dynamic */
9862306a36Sopenharmony_cimodule_param_named(gpiobase, modparam_gpiobase, int, 0444);
9962306a36Sopenharmony_ciMODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int ichx_write_bit(int reg, unsigned int nr, int val, int verify)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	unsigned long flags;
10462306a36Sopenharmony_ci	u32 data, tmp;
10562306a36Sopenharmony_ci	int reg_nr = nr / 32;
10662306a36Sopenharmony_ci	int bit = nr & 0x1f;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	spin_lock_irqsave(&ichx_priv.lock, flags);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
11162306a36Sopenharmony_ci		data = ichx_priv.outlvl_cache[reg_nr];
11262306a36Sopenharmony_ci	else
11362306a36Sopenharmony_ci		data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
11462306a36Sopenharmony_ci				 ichx_priv.gpio_base);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (val)
11762306a36Sopenharmony_ci		data |= BIT(bit);
11862306a36Sopenharmony_ci	else
11962306a36Sopenharmony_ci		data &= ~BIT(bit);
12062306a36Sopenharmony_ci	ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr],
12162306a36Sopenharmony_ci			 ichx_priv.gpio_base);
12262306a36Sopenharmony_ci	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
12362306a36Sopenharmony_ci		ichx_priv.outlvl_cache[reg_nr] = data;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
12662306a36Sopenharmony_ci			ichx_priv.gpio_base);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ichx_priv.lock, flags);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return (verify && data != tmp) ? -EPERM : 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int ichx_read_bit(int reg, unsigned int nr)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	unsigned long flags;
13662306a36Sopenharmony_ci	u32 data;
13762306a36Sopenharmony_ci	int reg_nr = nr / 32;
13862306a36Sopenharmony_ci	int bit = nr & 0x1f;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&ichx_priv.lock, flags);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
14362306a36Sopenharmony_ci			 ichx_priv.gpio_base);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
14662306a36Sopenharmony_ci		data = ichx_priv.outlvl_cache[reg_nr] | data;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ichx_priv.lock, flags);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return !!(data & BIT(bit));
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned int nr)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return !!(ichx_priv.use_gpio & BIT(nr / 32));
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned int nr)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	if (ichx_read_bit(GPIO_IO_SEL, nr))
16162306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_IN;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_OUT;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Try setting pin as an input and verify it worked since many pins
17062306a36Sopenharmony_ci	 * are output-only.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr,
17662306a36Sopenharmony_ci					int val)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	/* Disable blink hardware which is available for GPIOs from 0 to 31. */
17962306a36Sopenharmony_ci	if (nr < 32 && ichx_priv.desc->have_blink)
18062306a36Sopenharmony_ci		ichx_write_bit(GPO_BLINK, nr, 0, 0);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Set GPIO output value. */
18362306a36Sopenharmony_ci	ichx_write_bit(GPIO_LVL, nr, val, 0);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Try setting pin as an output and verify it worked since many pins
18762306a36Sopenharmony_ci	 * are input-only.
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int ichx_gpio_get(struct gpio_chip *chip, unsigned int nr)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	return ichx_read_bit(GPIO_LVL, nr);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int ich6_gpio_get(struct gpio_chip *chip, unsigned int nr)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	unsigned long flags;
20062306a36Sopenharmony_ci	u32 data;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/*
20362306a36Sopenharmony_ci	 * GPI 0 - 15 need to be read from the power management registers on
20462306a36Sopenharmony_ci	 * a ICH6/3100 bridge.
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci	if (nr < 16) {
20762306a36Sopenharmony_ci		if (!ichx_priv.pm_base)
20862306a36Sopenharmony_ci			return -ENXIO;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		spin_lock_irqsave(&ichx_priv.lock, flags);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		/* GPI 0 - 15 are latched, write 1 to clear*/
21362306a36Sopenharmony_ci		ICHX_WRITE(BIT(16 + nr), 0, ichx_priv.pm_base);
21462306a36Sopenharmony_ci		data = ICHX_READ(0, ichx_priv.pm_base);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		spin_unlock_irqrestore(&ichx_priv.lock, flags);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		return !!((data >> 16) & BIT(nr));
21962306a36Sopenharmony_ci	} else {
22062306a36Sopenharmony_ci		return ichx_gpio_get(chip, nr);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int ichx_gpio_request(struct gpio_chip *chip, unsigned int nr)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (!ichx_gpio_check_available(chip, nr))
22762306a36Sopenharmony_ci		return -ENXIO;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Note we assume the BIOS properly set a bridge's USE value.  Some
23162306a36Sopenharmony_ci	 * chips (eg Intel 3100) have bogus USE values though, so first see if
23262306a36Sopenharmony_ci	 * the chipset's USE value can be trusted for this specific bit.
23362306a36Sopenharmony_ci	 * If it can't be trusted, assume that the pin can be used as a GPIO.
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	if (ichx_priv.desc->use_sel_ignore[nr / 32] & BIT(nr & 0x1f))
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	/*
24462306a36Sopenharmony_ci	 * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100
24562306a36Sopenharmony_ci	 * bridge as they are controlled by USE register bits 0 and 1.  See
24662306a36Sopenharmony_ci	 * "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for
24762306a36Sopenharmony_ci	 * additional info.
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	if (nr == 16 || nr == 17)
25062306a36Sopenharmony_ci		nr -= 16;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return ichx_gpio_request(chip, nr);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	ichx_write_bit(GPIO_LVL, nr, val, 0);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void ichx_gpiolib_setup(struct gpio_chip *chip)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	chip->owner = THIS_MODULE;
26362306a36Sopenharmony_ci	chip->label = DRV_NAME;
26462306a36Sopenharmony_ci	chip->parent = ichx_priv.dev;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Allow chip-specific overrides of request()/get() */
26762306a36Sopenharmony_ci	chip->request = ichx_priv.desc->request ?
26862306a36Sopenharmony_ci		ichx_priv.desc->request : ichx_gpio_request;
26962306a36Sopenharmony_ci	chip->get = ichx_priv.desc->get ?
27062306a36Sopenharmony_ci		ichx_priv.desc->get : ichx_gpio_get;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	chip->set = ichx_gpio_set;
27362306a36Sopenharmony_ci	chip->get_direction = ichx_gpio_get_direction;
27462306a36Sopenharmony_ci	chip->direction_input = ichx_gpio_direction_input;
27562306a36Sopenharmony_ci	chip->direction_output = ichx_gpio_direction_output;
27662306a36Sopenharmony_ci	chip->base = modparam_gpiobase;
27762306a36Sopenharmony_ci	chip->ngpio = ichx_priv.desc->ngpio;
27862306a36Sopenharmony_ci	chip->can_sleep = false;
27962306a36Sopenharmony_ci	chip->dbg_show = NULL;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/* ICH6-based, 631xesb-based */
28362306a36Sopenharmony_cistatic struct ichx_desc ich6_desc = {
28462306a36Sopenharmony_ci	/* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */
28562306a36Sopenharmony_ci	.request = ich6_gpio_request,
28662306a36Sopenharmony_ci	.get = ich6_gpio_get,
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* GPIO 0-15 are read in the GPE0_STS PM register */
28962306a36Sopenharmony_ci	.uses_gpe0 = true,
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	.ngpio = 50,
29262306a36Sopenharmony_ci	.have_blink = true,
29362306a36Sopenharmony_ci	.regs = ichx_regs,
29462306a36Sopenharmony_ci	.reglen = ichx_reglen,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* Intel 3100 */
29862306a36Sopenharmony_cistatic struct ichx_desc i3100_desc = {
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on
30162306a36Sopenharmony_ci	 * the Intel 3100.  See "Table 712. GPIO Summary Table" of 3100
30262306a36Sopenharmony_ci	 * Datasheet for more info.
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	.use_sel_ignore = {0x00130000, 0x00010000, 0x0},
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* The 3100 needs fixups for GPIO 0 - 17 */
30762306a36Sopenharmony_ci	.request = ich6_gpio_request,
30862306a36Sopenharmony_ci	.get = ich6_gpio_get,
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* GPIO 0-15 are read in the GPE0_STS PM register */
31162306a36Sopenharmony_ci	.uses_gpe0 = true,
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	.ngpio = 50,
31462306a36Sopenharmony_ci	.regs = ichx_regs,
31562306a36Sopenharmony_ci	.reglen = ichx_reglen,
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/* ICH7 and ICH8-based */
31962306a36Sopenharmony_cistatic struct ichx_desc ich7_desc = {
32062306a36Sopenharmony_ci	.ngpio = 50,
32162306a36Sopenharmony_ci	.have_blink = true,
32262306a36Sopenharmony_ci	.regs = ichx_regs,
32362306a36Sopenharmony_ci	.reglen = ichx_reglen,
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/* ICH9-based */
32762306a36Sopenharmony_cistatic struct ichx_desc ich9_desc = {
32862306a36Sopenharmony_ci	.ngpio = 61,
32962306a36Sopenharmony_ci	.have_blink = true,
33062306a36Sopenharmony_ci	.regs = ichx_regs,
33162306a36Sopenharmony_ci	.reglen = ichx_reglen,
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/* ICH10-based - Consumer/corporate versions have different amount of GPIO */
33562306a36Sopenharmony_cistatic struct ichx_desc ich10_cons_desc = {
33662306a36Sopenharmony_ci	.ngpio = 61,
33762306a36Sopenharmony_ci	.have_blink = true,
33862306a36Sopenharmony_ci	.regs = ichx_regs,
33962306a36Sopenharmony_ci	.reglen = ichx_reglen,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_cistatic struct ichx_desc ich10_corp_desc = {
34262306a36Sopenharmony_ci	.ngpio = 72,
34362306a36Sopenharmony_ci	.have_blink = true,
34462306a36Sopenharmony_ci	.regs = ichx_regs,
34562306a36Sopenharmony_ci	.reglen = ichx_reglen,
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/* Intel 5 series, 6 series, 3400 series, and C200 series */
34962306a36Sopenharmony_cistatic struct ichx_desc intel5_desc = {
35062306a36Sopenharmony_ci	.ngpio = 76,
35162306a36Sopenharmony_ci	.regs = ichx_regs,
35262306a36Sopenharmony_ci	.reglen = ichx_reglen,
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/* Avoton */
35662306a36Sopenharmony_cistatic struct ichx_desc avoton_desc = {
35762306a36Sopenharmony_ci	/* Avoton has only 59 GPIOs, but we assume the first set of register
35862306a36Sopenharmony_ci	 * (Core) has 32 instead of 31 to keep gpio-ich compliance
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	.ngpio = 60,
36162306a36Sopenharmony_ci	.regs = avoton_regs,
36262306a36Sopenharmony_ci	.reglen = avoton_reglen,
36362306a36Sopenharmony_ci	.use_outlvl_cache = true,
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int ichx_gpio_request_regions(struct device *dev,
36762306a36Sopenharmony_ci	struct resource *res_base, const char *name, u8 use_gpio)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	int i;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (!res_base || !res_base->start || !res_base->end)
37262306a36Sopenharmony_ci		return -ENODEV;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
37562306a36Sopenharmony_ci		if (!(use_gpio & BIT(i)))
37662306a36Sopenharmony_ci			continue;
37762306a36Sopenharmony_ci		if (!devm_request_region(dev,
37862306a36Sopenharmony_ci				res_base->start + ichx_priv.desc->regs[0][i],
37962306a36Sopenharmony_ci				ichx_priv.desc->reglen[i], name))
38062306a36Sopenharmony_ci			return -EBUSY;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int ichx_gpio_probe(struct platform_device *pdev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
38862306a36Sopenharmony_ci	struct lpc_ich_info *ich_info = dev_get_platdata(dev);
38962306a36Sopenharmony_ci	struct resource *res_base, *res_pm;
39062306a36Sopenharmony_ci	int err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (!ich_info)
39362306a36Sopenharmony_ci		return -ENODEV;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	switch (ich_info->gpio_version) {
39662306a36Sopenharmony_ci	case ICH_I3100_GPIO:
39762306a36Sopenharmony_ci		ichx_priv.desc = &i3100_desc;
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	case ICH_V5_GPIO:
40062306a36Sopenharmony_ci		ichx_priv.desc = &intel5_desc;
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	case ICH_V6_GPIO:
40362306a36Sopenharmony_ci		ichx_priv.desc = &ich6_desc;
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case ICH_V7_GPIO:
40662306a36Sopenharmony_ci		ichx_priv.desc = &ich7_desc;
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case ICH_V9_GPIO:
40962306a36Sopenharmony_ci		ichx_priv.desc = &ich9_desc;
41062306a36Sopenharmony_ci		break;
41162306a36Sopenharmony_ci	case ICH_V10CORP_GPIO:
41262306a36Sopenharmony_ci		ichx_priv.desc = &ich10_corp_desc;
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	case ICH_V10CONS_GPIO:
41562306a36Sopenharmony_ci		ichx_priv.desc = &ich10_cons_desc;
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci	case AVOTON_GPIO:
41862306a36Sopenharmony_ci		ichx_priv.desc = &avoton_desc;
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	default:
42162306a36Sopenharmony_ci		return -ENODEV;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ichx_priv.dev = dev;
42562306a36Sopenharmony_ci	spin_lock_init(&ichx_priv.lock);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
42862306a36Sopenharmony_ci	err = ichx_gpio_request_regions(dev, res_base, pdev->name,
42962306a36Sopenharmony_ci					ich_info->use_gpio);
43062306a36Sopenharmony_ci	if (err)
43162306a36Sopenharmony_ci		return err;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ichx_priv.gpio_base = res_base;
43462306a36Sopenharmony_ci	ichx_priv.use_gpio = ich_info->use_gpio;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/*
43762306a36Sopenharmony_ci	 * If necessary, determine the I/O address of ACPI/power management
43862306a36Sopenharmony_ci	 * registers which are needed to read the GPE0 register for GPI pins
43962306a36Sopenharmony_ci	 * 0 - 15 on some chipsets.
44062306a36Sopenharmony_ci	 */
44162306a36Sopenharmony_ci	if (!ichx_priv.desc->uses_gpe0)
44262306a36Sopenharmony_ci		goto init;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
44562306a36Sopenharmony_ci	if (!res_pm) {
44662306a36Sopenharmony_ci		dev_warn(dev, "ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
44762306a36Sopenharmony_ci		goto init;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (!devm_request_region(dev, res_pm->start, resource_size(res_pm),
45162306a36Sopenharmony_ci				 pdev->name)) {
45262306a36Sopenharmony_ci		dev_warn(dev, "ACPI BAR is busy, GPI 0 - 15 unavailable\n");
45362306a36Sopenharmony_ci		goto init;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ichx_priv.pm_base = res_pm;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ciinit:
45962306a36Sopenharmony_ci	ichx_gpiolib_setup(&ichx_priv.chip);
46062306a36Sopenharmony_ci	err = devm_gpiochip_add_data(dev, &ichx_priv.chip, NULL);
46162306a36Sopenharmony_ci	if (err) {
46262306a36Sopenharmony_ci		dev_err(dev, "Failed to register GPIOs\n");
46362306a36Sopenharmony_ci		return err;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	dev_info(dev, "GPIO from %d to %d\n", ichx_priv.chip.base,
46762306a36Sopenharmony_ci		 ichx_priv.chip.base + ichx_priv.chip.ngpio - 1);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct platform_driver ichx_gpio_driver = {
47362306a36Sopenharmony_ci	.driver		= {
47462306a36Sopenharmony_ci		.name	= DRV_NAME,
47562306a36Sopenharmony_ci	},
47662306a36Sopenharmony_ci	.probe		= ichx_gpio_probe,
47762306a36Sopenharmony_ci};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cimodule_platform_driver(ichx_gpio_driver);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciMODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>");
48262306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for Intel ICH series");
48362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
48462306a36Sopenharmony_ciMODULE_ALIAS("platform:"DRV_NAME);
485