You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
705 lines
20 KiB
705 lines
20 KiB
/* |
|
* 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 base_num; |
|
#endif |
|
}; |
|
|
|
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(acpi_hid) |
|
#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 REG_GPI_INT_EN_BASE_GET(data) (data)->intr_stat_reg + 0x20 |
|
|
|
#define PIN_OFFSET_GET(dev) (0) |
|
|
|
#define GPIO_PAD_OWNERSHIP_GET(data, pin, offset) (data)->pad_owner_reg + (((pin) / 8) * 0x4) |
|
|
|
#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 |
|
#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) |
|
{ |
|
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); |
|
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.intr_stat_reg; |
|
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))); \ |
|
} |
|
|
|
#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)
|
|
|