/*
 * Copyright (c) 2018-2019 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT intel_gpio

/**
 * @file
 * @brief Intel GPIO Controller Driver
 *
 * The GPIO controller on Intel SoC serves
 * both GPIOs and Pinmuxing function. This driver provides
 * the GPIO function.
 *
 * Due to GPIO callback only allowing 32 pins (as a 32-bit mask) at once,
 * each set is further sub-divided into multiple devices, so
 * we export GPIO_INTEL_NR_SUBDEVS devices to the kernel.
 */

#include <errno.h>
#include <zephyr/drivers/gpio.h>
#include <soc.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/slist.h>
#include <zephyr/sys/speculation.h>
#include <zephyr/irq.h>
#include <zephyr/dt-bindings/interrupt-controller/intel-ioapic.h>
#include <zephyr/acpi/acpi.h>
#include <zephyr/drivers/gpio/gpio_utils.h>

#define REG_MISCCFG			0x0010
#define MISCCFG_IRQ_ROUTE_POS		3

#define PAD_OWN_MASK			0x03
#define PAD_OWN_HOST			0
#define PAD_OWN_CSME			1
#define PAD_OWN_ISH			2
#define PAD_OWN_IE			3

#define PAD_HOST_SW_OWN_GPIO		1
#define PAD_HOST_SW_OWN_ACPI		0


#define PAD_CFG0_RXPADSTSEL		BIT(29)
#define PAD_CFG0_RXRAW1			BIT(28)


#define PAD_CFG0_RXEVCFG_POS		25
#define PAD_CFG0_RXEVCFG_MASK		(0x03 << PAD_CFG0_RXEVCFG_POS)
#define PAD_CFG0_RXEVCFG_LEVEL		(0 << PAD_CFG0_RXEVCFG_POS)
#define PAD_CFG0_RXEVCFG_EDGE		(1 << PAD_CFG0_RXEVCFG_POS)
#define PAD_CFG0_RXEVCFG_DRIVE0		(2 << PAD_CFG0_RXEVCFG_POS)

#define PAD_CFG0_PREGFRXSEL		BIT(24)
#define PAD_CFG0_RXINV			BIT(23)

#define PAD_CFG0_RXDIS			BIT(9)
#define PAD_CFG0_TXDIS			BIT(8)
#define PAD_CFG0_RXSTATE		BIT(1)
#define PAD_CFG0_RXSTATE_POS		1
#define PAD_CFG0_TXSTATE		BIT(0)
#define PAD_CFG0_TXSTATE_POS		0

#define PAD_CFG1_IOSTERM_POS		8
#define PAD_CFG1_IOSTERM_MASK		(0x03 << PAD_CFG1_IOSTERM_POS)
#define PAD_CFG1_IOSTERM_FUNC		(0 << PAD_CFG1_IOSTERM_POS)
#define PAD_CFG1_IOSTERM_DISPUD		(1 << PAD_CFG1_IOSTERM_POS)
#define PAD_CFG1_IOSTERM_PU		(2 << PAD_CFG1_IOSTERM_POS)
#define PAD_CFG1_IOSTERM_PD		(3 << PAD_CFG1_IOSTERM_POS)

#define PAD_CFG1_TERM_POS		10
#define PAD_CFG1_TERM_MASK		(0x0F << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_NONE		(0x00 << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PD_5K		(0x02 << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PD_20K		(0x04 << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_NONE2		(0x08 << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PU_1K		(0x09 << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PU_5K		(0x0A << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PU_2K		(0x0B << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PU_20K		(0x0C << PAD_CFG1_TERM_POS)
#define PAD_CFG1_TERM_PU_1K_2K		(0x0D << PAD_CFG1_TERM_POS)

#define PAD_CFG1_IOSSTATE_POS		14
#define PAD_CFG1_IOSSTATE_MASK		(0x0F << PAD_CFG1_IOSSTATE_POS)
#define PAD_CFG1_IOSSTATE_IGNORE	(0x0F << PAD_CFG1_IOSSTATE_POS)

/* Required by DEVICE_MMIO_NAMED_* macros */
#define DEV_CFG(_dev) \
	((const struct gpio_intel_config *)(_dev)->config)
#define DEV_DATA(_dev) ((struct gpio_intel_data *)(_dev)->data)

struct gpio_intel_config {
	/* gpio_driver_config needs to be first */
	struct gpio_driver_config common;

	DEVICE_MMIO_NAMED_ROM(reg_base);

#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid)
	uint8_t pin_offset;
	uint8_t group_index;
	uint8_t num_pins;
#endif
};

struct gpio_intel_data {
	/* gpio_driver_data needs to be first */
	struct gpio_driver_data common;

	DEVICE_MMIO_NAMED_RAM(reg_base);

	/* Pad base address */
	uint32_t pad_base;

	sys_slist_t cb;
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid)
	uint32_t num_pins;
	uint32_t pad_owner_reg;
	uint32_t host_owner_reg;
	uint32_t intr_stat_reg;
	uint32_t intr_en_reg;
	uint32_t base_num;
#endif
};

#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid)
#define GPIO_PAD_OWNERSHIP_SHIFT (0x04) /* Shift between Ownership regs */

#define GPIO_PAD_PINS_PER_REG (8) /*Pins for each Register */

#define GPIO_REG_BASE_GET(dev) DEVICE_MMIO_NAMED_GET(dev, reg_base)

#define REG_GPI_INT_STS_BASE_GET(data) (data)->intr_stat_reg

#define PIN_OFFSET_GET(dev) (0)

#define REG_PAD_HOST_SW_OWNER_GET(data) (data)->host_owner_reg

#define GPIO_BASE_GET(cdf) (0)

#define GPIO_INTERRUPT_BASE_GET(cfg) (0)

#define GPIO_GET_PIN_MAX(dev) ((struct gpio_intel_data *)(dev)->data)->num_pins

#define REG_GPI_INT_EN_BASE_GET(data) (data)->intr_en_reg

#if DT_ANY_INST_HAS_BOOL_STATUS_OKAY(acpi_ginf)
#define GPIO_PAD_OWNERSHIP_GET(data, pin, offset)\
	(data)->pad_owner_reg + (pin * GPIO_PAD_OWNERSHIP_SHIFT)

#else
#define GPIO_PAD_OWNERSHIP_GET(data, pin, offset) (data)->pad_owner_reg +\
	(((pin) / GPIO_PAD_PINS_PER_REG) * GPIO_PAD_OWNERSHIP_SHIFT)

#endif

#else /* Non-ACPI */
#define GPIO_REG_BASE_GET(dev) GPIO_REG_BASE(DEVICE_MMIO_NAMED_GET(dev, reg_base))

#define REG_GPI_INT_STS_BASE_GET(data) REG_GPI_INT_STS_BASE

#define REG_GPI_INT_EN_BASE_GET(data) REG_GPI_INT_EN_BASE

#define PIN_OFFSET_GET(dev) ((const struct gpio_intel_config *)(dev)->config)->pin_offset

#define GPIO_PAD_OWNERSHIP_GET(data, pin, offset) GPIO_PAD_OWNERSHIP(pin, offset)

#define REG_PAD_HOST_SW_OWNER_GET(data) REG_PAD_HOST_SW_OWNER

#define GPIO_BASE_GET(cdf) GPIO_BASE(((const struct gpio_intel_config *)(dev)->config))

#define GPIO_INTERRUPT_BASE_GET(cfg) GPIO_INTERRUPT_BASE(cfg)

#define GPIO_GET_PIN_MAX(dev) ((const struct gpio_intel_config *)(dev)->config)->num_pins
#endif

static inline mm_reg_t regs(const struct device *dev)
{
	return GPIO_REG_BASE_GET(dev);
}

#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid)
static inline mm_reg_t pad_base(const struct device *dev)
{
	return GPIO_PAD_BASE(DEVICE_MMIO_NAMED_GET(dev, reg_base));
}
#endif

#ifdef CONFIG_GPIO_INTEL_CHECK_PERMS
/**
 * @brief Check if host has permission to alter this GPIO pin.
 *
 * @param "struct device *dev" Device struct
 * @param "uint32_t raw_pin" Raw GPIO pin
 *
 * @return true if host owns the GPIO pin, false otherwise
 */
static bool check_perm(const struct device *dev, uint32_t raw_pin)
{
	struct gpio_intel_data *data = dev->data;
	uint32_t offset, val, pin_offset;

	pin_offset = PIN_OFFSET_GET(dev);
	/* First is to establish that host software owns the pin */

	/* read the Pad Ownership register related to the pin */
	offset = GPIO_PAD_OWNERSHIP_GET(data, raw_pin, pin_offset);

	val = sys_read32(regs(dev) + offset);

	/* get the bits about ownership */
	offset = GPIO_OWNERSHIP_BIT(raw_pin);
	val = (val >> offset) & PAD_OWN_MASK;
	if (val) {
		/* PAD_OWN_HOST == 0, so !0 => false*/
		return false;
	}

	/* Also need to make sure the function of pad is GPIO */
	offset = data->pad_base + (raw_pin << 4);
	val = sys_read32(regs(dev) + offset);
	if (val & PAD_CFG0_PMODE_MASK) {
		/* mode is not zero => not functioning as GPIO */
		return false;
	}

	return true;
}
#else
#define check_perm(...) (1)
#endif

/*
 * as the kernel initializes the subdevices, we add them
 * to the list of devices to check at ISR time.
 */

static bool first_inst = true;

static void gpio_intel_isr(const struct device *dev)
{
	const struct gpio_intel_config *cfg;
	struct gpio_intel_data *data;
	struct gpio_callback *cb, *tmp;
	uint32_t reg, int_sts, cur_mask, acc_mask;

	cfg = dev->config;
	data = dev->data;

	reg = regs(dev) + REG_GPI_INT_STS_BASE_GET(data) + GPIO_INTERRUPT_BASE_GET(cfg);
	int_sts = sys_read32(reg);
	acc_mask = 0U;

	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&data->cb, cb, tmp, node) {
		cur_mask = int_sts & cb->pin_mask;
		acc_mask |= cur_mask;
		if (cur_mask) {
			__ASSERT(cb->handler, "No callback handler!");
			cb->handler(dev, cb, cur_mask);
		}
	}

	/* clear handled interrupt bits */
	sys_write32(acc_mask, reg);
}

static int gpio_intel_config(const struct device *dev,
			     gpio_pin_t pin, gpio_flags_t flags)
{
	struct gpio_intel_data *data = dev->data;
	uint32_t raw_pin, reg, cfg0, cfg1;

	/* Only support push-pull mode */
	if ((flags & GPIO_SINGLE_ENDED) != 0U) {
		return -ENOTSUP;
	}

	pin = k_array_index_sanitize(pin, GPIO_GET_PIN_MAX(dev) + 1);

	raw_pin = GPIO_RAW_PIN(pin, PIN_OFFSET_GET(dev));

	if (!check_perm(dev, raw_pin)) {
		return -EINVAL;
	}

	/* read in pad configuration register */
	reg = regs(dev) + data->pad_base + (raw_pin * PIN_OFFSET);
	cfg0 = sys_read32(reg);
	cfg1 = sys_read32(reg + 4);

	/* don't override RX to 1 */
	cfg0 &= ~(PAD_CFG0_RXRAW1);

	/* set input/output */
	if ((flags & GPIO_INPUT) != 0U) {
		/* clear RX disable bit */
		cfg0 &= ~PAD_CFG0_RXDIS;
	} else {
		/* set RX disable bit */
		cfg0 |= PAD_CFG0_RXDIS;
	}

	if ((flags & GPIO_OUTPUT) != 0U) {
		/* pin to output */

		/* set pin output if desired */
		if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
			cfg0 |= PAD_CFG0_TXSTATE;
		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
			cfg0 &= ~PAD_CFG0_TXSTATE;
		}

		/* clear TX disable bit */
		cfg0 &= ~PAD_CFG0_TXDIS;
	} else {
		/* set TX disable bit */
		cfg0 |= PAD_CFG0_TXDIS;
	}

	/* pull-up or pull-down */
	cfg1 &= ~(PAD_CFG1_TERM_MASK | PAD_CFG1_IOSTERM_MASK);
	if ((flags & GPIO_PULL_UP) != 0U) {
		cfg1 |= (PAD_CFG1_TERM_PU_20K | PAD_CFG1_IOSTERM_PU);
	} else if ((flags & GPIO_PULL_DOWN) != 0U) {
		cfg1 |= (PAD_CFG1_TERM_PD_20K | PAD_CFG1_IOSTERM_PD);
	} else {
		cfg1 |= (PAD_CFG1_TERM_NONE | PAD_CFG1_IOSTERM_FUNC);
	}

	/* IO Standby state to TX,RX enabled */
	cfg1 &= ~PAD_CFG1_IOSSTATE_MASK;

	/* write back pad configuration register after all changes */
	sys_write32(cfg0, reg);
	sys_write32(cfg1, reg + 4);

	return 0;
}

static int gpio_intel_pin_interrupt_configure(const struct device *dev,
					      gpio_pin_t pin,
					      enum gpio_int_mode mode,
					      enum gpio_int_trig trig)
{
	struct gpio_intel_data *data = dev->data;
	uint32_t raw_pin, cfg0, cfg1;
	uint32_t reg, reg_en, reg_sts;

	/* no double-edge triggering according to data sheet */
	if (trig == GPIO_INT_TRIG_BOTH) {
		return -ENOTSUP;
	}

	pin = k_array_index_sanitize(pin, GPIO_GET_PIN_MAX(dev) + 1);

	raw_pin = GPIO_RAW_PIN(pin, PIN_OFFSET_GET(dev));

	if (!check_perm(dev, raw_pin)) {
		return -EINVAL;
	}

	/* set owner to GPIO driver mode for legacy interrupt mode */
	reg = regs(dev) + REG_PAD_HOST_SW_OWNER_GET(data) + GPIO_BASE_GET(dev);

	sys_bitfield_set_bit(reg, raw_pin);

	/* read in pad configuration register */
	reg = regs(dev) + data->pad_base + (raw_pin * PIN_OFFSET);

	cfg0 = sys_read32(reg);
	cfg1 = sys_read32(reg + 4);

	reg_en = regs(dev) + REG_GPI_INT_EN_BASE_GET(data) + GPIO_BASE_GET(dev);

	/* disable interrupt bit first before setup */
	sys_bitfield_clear_bit(reg_en, raw_pin);

	/* clear (by setting) interrupt status bit */
	reg_sts = regs(dev) + REG_GPI_INT_STS_BASE_GET(data) + GPIO_BASE_GET(dev);
	sys_bitfield_set_bit(reg_sts, raw_pin);

	/* clear level/edge configuration bits */
	cfg0 &= ~PAD_CFG0_RXEVCFG_MASK;

	if (mode == GPIO_INT_MODE_DISABLED) {
		/* set RX conf to drive 0 */
		cfg0 |= PAD_CFG0_RXEVCFG_DRIVE0;
	} else {
		/* cannot enable interrupt without pin as input */
		if ((cfg0 & PAD_CFG0_RXDIS) != 0U) {
			return -ENOTSUP;
		}

		/*
		 * Do not enable interrupt with pin as output.
		 * Hardware does not seem to support triggering
		 * interrupt by setting line as both input/output
		 * and then setting output to desired level.
		 * So just say not supported.
		 */
		if ((cfg0 & PAD_CFG0_TXDIS) == 0U) {
			return -ENOTSUP;
		}

		if (mode == GPIO_INT_MODE_LEVEL) {
			/* level trigger */
			cfg0 |= PAD_CFG0_RXEVCFG_LEVEL;
		} else {
			/* edge trigger */
			cfg0 |= PAD_CFG0_RXEVCFG_EDGE;
		}

		/* invert pin for active low triggering */
		if (trig == GPIO_INT_TRIG_LOW) {
			cfg0 |= PAD_CFG0_RXINV;
		} else {
			cfg0 &= ~PAD_CFG0_RXINV;
		}
	}

	/* write back pad configuration register after all changes */
	sys_write32(cfg0, reg);
	sys_write32(cfg1, reg + 4);

	if (mode != GPIO_INT_MODE_DISABLED) {
		/* enable interrupt bit */
		sys_bitfield_set_bit(reg_en, raw_pin);
	}

	return 0;
}

static int gpio_intel_manage_callback(const struct device *dev,
				      struct gpio_callback *callback,
				      bool set)
{
	struct gpio_intel_data *data = dev->data;

	return gpio_manage_callback(&data->cb, callback, set);
}

static int port_get_raw(const struct device *dev, uint32_t mask,
			uint32_t *value,
			bool read_tx)
{
	struct gpio_intel_data *data = dev->data;
	uint32_t pin, raw_pin, reg_addr, reg_val, cmp;

	if (read_tx) {
		cmp = PAD_CFG0_TXSTATE;
	} else {
		cmp = PAD_CFG0_RXSTATE;
	}

	*value = 0;
	while (mask != 0U) {
		pin = find_lsb_set(mask) - 1;

		if (pin >= GPIO_GET_PIN_MAX(dev)) {
			break;
		}

		mask &= ~BIT(pin);

		raw_pin = GPIO_RAW_PIN(pin, PIN_OFFSET_GET(dev));

		if (!check_perm(dev, raw_pin)) {
			continue;
		}

		reg_addr = regs(dev) + data->pad_base + (raw_pin * PIN_OFFSET);
		reg_val = sys_read32(reg_addr);

		if ((reg_val & cmp) != 0U) {
			*value |= BIT(pin);
		}
	}

	return 0;
}

static int port_set_raw(const struct device *dev, uint32_t mask,
			uint32_t value)
{
	struct gpio_intel_data *data = dev->data;
	uint32_t pin, raw_pin, reg_addr, reg_val;

	while (mask != 0) {
		pin = find_lsb_set(mask) - 1;

		if (pin >= GPIO_GET_PIN_MAX(dev)) {
			break;
		}

		mask &= ~BIT(pin);

		raw_pin = GPIO_RAW_PIN(pin, PIN_OFFSET_GET(dev));

		if (!check_perm(dev, raw_pin)) {
			continue;
		}

		reg_addr = regs(dev) + data->pad_base + (raw_pin * PIN_OFFSET);
		reg_val = sys_read32(reg_addr);

		if ((value & BIT(pin)) != 0) {
			reg_val |= PAD_CFG0_TXSTATE;
		} else {
			reg_val &= ~PAD_CFG0_TXSTATE;
		}

		sys_write32(reg_val, reg_addr);
	}

	return 0;
}

static int gpio_intel_port_set_masked_raw(const struct device *dev,
					  uint32_t mask,
					  uint32_t value)
{
	uint32_t port_val;

	port_get_raw(dev, mask, &port_val, true);

	port_val = (port_val & ~mask) | (mask & value);

	port_set_raw(dev, mask, port_val);

	return 0;
}

static int gpio_intel_port_set_bits_raw(const struct device *dev,
					uint32_t mask)
{
	return gpio_intel_port_set_masked_raw(dev, mask, mask);
}

static int gpio_intel_port_clear_bits_raw(const struct device *dev,
					  uint32_t mask)
{
	return gpio_intel_port_set_masked_raw(dev, mask, 0);
}

static int gpio_intel_port_toggle_bits(const struct device *dev,
				       uint32_t mask)
{
	uint32_t port_val;

	port_get_raw(dev, mask, &port_val, true);

	port_val ^= mask;

	port_set_raw(dev, mask, port_val);

	return 0;
}

static int gpio_intel_port_get_raw(const struct device *dev,
				   uint32_t *value)
{
	return port_get_raw(dev, 0xFFFFFFFF, value, false);
}

static DEVICE_API(gpio, gpio_intel_api) = {
	.pin_configure = gpio_intel_config,
	.manage_callback = gpio_intel_manage_callback,
	.port_get_raw = gpio_intel_port_get_raw,
	.port_set_masked_raw = gpio_intel_port_set_masked_raw,
	.port_set_bits_raw = gpio_intel_port_set_bits_raw,
	.port_clear_bits_raw = gpio_intel_port_clear_bits_raw,
	.port_toggle_bits = gpio_intel_port_toggle_bits,
	.pin_interrupt_configure = gpio_intel_pin_interrupt_configure,
};

/* We need support either DTS or ACPI base resource enumeration at time.*/
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid)

static int gpio_intel_acpi_enum(const struct device *dev, int bank_idx, char *hid, char *uid,
				bool ginf)
{
	int ret;
	struct gpio_acpi_res res;
	struct gpio_intel_data *data = dev->data;

	ret = soc_acpi_gpio_resource_get(bank_idx, hid, uid, &res, ginf);
	if (ret) {
		return ret;
	}

	device_map(&data->reg_base, res.reg_base, res.len, K_MEM_CACHE_NONE);

	data->num_pins = res.num_pins;
	data->pad_owner_reg = res.pad_owner_reg;
	data->host_owner_reg = res.host_owner_reg;
	data->intr_stat_reg = res.gp_evt_stat_reg - DT_INST_PROP(0, int_stat_offset);
	data->intr_en_reg = res.gp_evt_stat_reg - DT_INST_PROP(0, int_en_offset);
	data->base_num = res.base_num;
	data->pad_base = res.pad_base;

	/* Note that all controllers are using the same IRQ line.
	 * So we can just use the values from the first instance.
	 */
	if (first_inst) {
		irq_connect_dynamic(res.irq, DT_INST_IRQ(0, priority),
				    (void (*)(const void *))gpio_intel_isr, dev, res.irq_flags);
		irq_enable(res.irq);
		first_inst = false;
	}

	if (IS_ENABLED(CONFIG_SOC_APOLLO_LAKE)) {
		/* route to IRQ 14 */
		sys_bitfield_clear_bit(regs(dev) + REG_MISCCFG, MISCCFG_IRQ_ROUTE_POS);
	}

	return 0;
}

#define GPIO_INIT_FN_DEFINE(n)                                                                     \
	static int gpio_intel_init##n(const struct device *dev)                                    \
	{                                                                                          \
		return gpio_intel_acpi_enum(dev, DT_INST_PROP(n, group_index),			   \
			ACPI_DT_HID(DT_DRV_INST(n)),  ACPI_DT_UID(DT_DRV_INST(n)),		   \
			DT_INST_PROP(n, acpi_ginf_3_param));					   \
	}

#define GPIO_MMIO_ROM_INIT(n)

#define GPIO_INIT_CONFIG(n)                                                                        \
	static const struct gpio_intel_config gpio_intel_cfg_##n = {                               \
		.common =                                                                          \
			{                                                                          \
				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),               \
			},                                                                         \
	}
#else

static int gpio_intel_dts_init(const struct device *dev)
{
	struct gpio_intel_data *data = dev->data;

#ifdef CONFIG_SOC_APOLLO_LAKE
	/*
	 * On Apollo Lake, each GPIO controller has more than 32 pins.
	 * But Zephyr API can only manipulate 32 pins per controller.
	 * So the workaround is to divide each hardware GPIO controller
	 * into 32-pin blocks so each block has a GPIO driver instance.
	 * Compounding to the issue is that there cannot be two device
	 * tree nodes with same register address. So another workaround
	 * is to increment the register addresses by 1 for each block.
	 * So when mapping the address, the lowest 8-bit needs to be
	 * masked to get the actual hardware address. Hence the weird
	 * code below.
	 */

	const struct gpio_intel_config *cfg = dev->config;

	device_map(&data->reg_base,
		   cfg->reg_base.phys_addr & ~0xFFU,
		   cfg->reg_base.size,
		   K_MEM_CACHE_NONE);
#else
	DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE);
#endif
	data->pad_base = pad_base(dev);

	if (first_inst) {
		/* Note that all controllers are using the same IRQ line.
		 * So we can just use the values from the first instance.
		 */
		IRQ_CONNECT(DT_INST_IRQN(0),
			    DT_INST_IRQ(0, priority),
			    gpio_intel_isr, dev,
			    DT_INST_IRQ(0, sense));

		irq_enable(DT_INST_IRQN(0));

		first_inst = false;
	}

	if (IS_ENABLED(CONFIG_SOC_APOLLO_LAKE)) {
		/* route to IRQ 14 */
		sys_bitfield_clear_bit(regs(dev) + REG_MISCCFG,
				       MISCCFG_IRQ_ROUTE_POS);
	}
	return 0;
}

#define GPIO_INIT_FN_DEFINE(n)                                                                     \
	static int gpio_intel_init##n(const struct device *dev)                                    \
	{                                                                                          \
		return gpio_intel_dts_init(dev);                                                   \
	}

#define GPIO_MMIO_ROM_INIT(n) DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)),

#define GPIO_INIT_CONFIG(n)                                                                        \
	static const struct gpio_intel_config gpio_intel_cfg_##n = {                               \
		.common =                                                                          \
			{                                                                          \
				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),               \
			},                                                                         \
		GPIO_MMIO_ROM_INIT(n).pin_offset = DT_INST_PROP(n, pin_offset),                    \
		.group_index = DT_INST_PROP_OR(n, group_index, 0),                                 \
		.num_pins = DT_INST_PROP(n, ngpios),                                               \
	}

#endif

#define GPIO_INTEL_DEV_CFG_DATA(n)                                                                 \
	GPIO_INIT_FN_DEFINE(n)                                                                     \
	GPIO_INIT_CONFIG(n);                                                                       \
	static struct gpio_intel_data gpio_intel_data_##n;                                         \
                                                                                                   \
	DEVICE_DT_INST_DEFINE(n, gpio_intel_init##n, NULL, &gpio_intel_data_##n,                   \
			      &gpio_intel_cfg_##n, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY,         \
			      &gpio_intel_api);

/* "sub" devices.  no more than GPIO_INTEL_NR_SUBDEVS of these! */
DT_INST_FOREACH_STATUS_OKAY(GPIO_INTEL_DEV_CFG_DATA)
