drivers: gpio: npcx: workaround both trigger issue for npcx9m7fb

Apply the workaround for the issue "MIWU Any Edge Trigger Condition" in
the NPCX99nFB_Errata.

Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
This commit is contained in:
Jun Lin 2024-01-30 16:33:32 +08:00 committed by Fabio Baltieri
parent c2179bcef0
commit 0bf4ec6d7b
3 changed files with 105 additions and 3 deletions

View file

@ -11,3 +11,10 @@ config NPCX_MIWU
This option enables the Multi-Input Wake-Up Unit (MIWU) driver
for NPCX family of processors.
This is required for GPIO, RTC, LPC/eSPI interrupt support.
config NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
bool
default y if SOC_NPCX9M7FB
help
Workaround the issue "MIWU Any Edge Trigger Condition"
in the npcx9m7fb SoC errata.

View file

@ -82,6 +82,10 @@ struct intc_miwu_config {
struct intc_miwu_data {
/* Callback functions list for each MIWU group */
sys_slist_t cb_list_grp[8];
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
uint8_t both_edge_pins[8];
struct k_spinlock lock;
#endif
};
BUILD_ASSERT(sizeof(struct miwu_io_params) == sizeof(gpio_port_pins_t),
@ -121,6 +125,23 @@ static void intc_miwu_dispatch_isr(sys_slist_t *cb_list, uint8_t mask)
}
}
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
static void npcx_miwu_set_pseudo_both_edge(uint8_t table, uint8_t group, uint8_t bit)
{
const struct intc_miwu_config *config = miwu_devs[table]->config;
const uint32_t base = config->base;
uint8_t pmask = BIT(bit);
if (IS_BIT_SET(NPCX_WKST(base, group), bit)) {
/* Current signal level is high, set falling edge triger. */
NPCX_WKEDG(base, group) |= pmask;
} else {
/* Current signal level is low, set rising edge triger. */
NPCX_WKEDG(base, group) &= ~pmask;
}
}
#endif
static void intc_miwu_isr_pri(int wui_table, int wui_group)
{
const struct intc_miwu_config *config = miwu_devs[wui_table]->config;
@ -128,10 +149,26 @@ static void intc_miwu_isr_pri(int wui_table, int wui_group)
const uint32_t base = config->base;
uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
uint8_t new_mask = mask;
while (new_mask != 0) {
uint8_t pending_bit = find_lsb_set(new_mask) - 1;
uint8_t pending_mask = BIT(pending_bit);
NPCX_WKPCL(base, wui_group) = pending_mask;
if ((data->both_edge_pins[wui_group] & pending_mask) != 0) {
npcx_miwu_set_pseudo_both_edge(wui_table, wui_group, pending_bit);
}
new_mask &= ~pending_mask;
};
#else
/* Clear pending bits before dispatch ISR */
if (mask) {
NPCX_WKPCL(base, wui_group) = mask;
}
#endif
/* Dispatch registered gpio isrs */
intc_miwu_dispatch_isr(&data->cb_list_grp[wui_group], mask);
@ -143,7 +180,21 @@ void npcx_miwu_irq_enable(const struct npcx_wui *wui)
const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
const uint32_t base = config->base;
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
k_spinlock_key_t key;
struct intc_miwu_data *data = miwu_devs[wui->table]->data;
key = k_spin_lock(&data->lock);
#endif
NPCX_WKEN(base, wui->group) |= BIT(wui->bit);
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
if ((data->both_edge_pins[wui->group] & BIT(wui->bit)) != 0) {
npcx_miwu_set_pseudo_both_edge(wui->table, wui->group, wui->bit);
}
k_spin_unlock(&data->lock, key);
#endif
}
void npcx_miwu_irq_disable(const struct npcx_wui *wui)
@ -182,10 +233,26 @@ bool npcx_miwu_irq_get_and_clear_pending(const struct npcx_wui *wui)
{
const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
const uint32_t base = config->base;
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
k_spinlock_key_t key;
struct intc_miwu_data *data = miwu_devs[wui->table]->data;
#endif
bool pending = IS_BIT_SET(NPCX_WKPND(base, wui->group), wui->bit);
if (pending) {
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
key = k_spin_lock(&data->lock);
NPCX_WKPCL(base, wui->group) = BIT(wui->bit);
if ((data->both_edge_pins[wui->group] & BIT(wui->bit)) != 0) {
npcx_miwu_set_pseudo_both_edge(wui->table, wui->group, wui->bit);
}
k_spin_unlock(&data->lock, key);
#else
NPCX_WKPCL(base, wui->group) = BIT(wui->bit);
#endif
}
return pending;
@ -197,10 +264,19 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
const uint32_t base = config->base;
uint8_t pmask = BIT(wui->bit);
int ret = 0;
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
struct intc_miwu_data *data = miwu_devs[wui->table]->data;
k_spinlock_key_t key;
#endif
/* Disable interrupt of wake-up input source before configuring it */
npcx_miwu_irq_disable(wui);
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
key = k_spin_lock(&data->lock);
data->both_edge_pins[wui->group] &= ~BIT(wui->bit);
#endif
/* Handle interrupt for level trigger */
if (mode == NPCX_MIWU_MODE_LEVEL) {
/* Set detection mode to level */
@ -215,7 +291,8 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
NPCX_WKEDG(base, wui->group) |= pmask;
break;
default:
return -EINVAL;
ret = -EINVAL;
goto early_exit;
}
/* Handle interrupt for edge trigger */
} else {
@ -234,11 +311,17 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
break;
/* Handle interrupting on both edges */
case NPCX_MIWU_TRIG_BOTH:
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
NPCX_WKAEDG(base, wui->group) &= ~pmask;
data->both_edge_pins[wui->group] |= BIT(wui->bit);
#else
/* Enable any edge */
NPCX_WKAEDG(base, wui->group) |= pmask;
#endif
break;
default:
return -EINVAL;
ret = -EINVAL;
goto early_exit;
}
}
@ -251,7 +334,17 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
*/
NPCX_WKPCL(base, wui->group) |= pmask;
return 0;
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
if ((data->both_edge_pins[wui->group] & BIT(wui->bit)) != 0) {
npcx_miwu_set_pseudo_both_edge(wui->table, wui->group, wui->bit);
}
#endif
early_exit:
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
k_spin_unlock(&data->lock, key);
#endif
return ret;
}
void npcx_miwu_init_gpio_callback(struct miwu_callback *callback,

View file

@ -396,6 +396,8 @@ struct uart_reg {
(*(volatile uint8_t *)(base + NPCX_WKINEN_OFFSET(group)))
#define NPCX_WKMOD(base, group) \
(*(volatile uint8_t *)(base + NPCX_WKMOD_OFFSET(group)))
#define NPCX_WKST(base, group) \
(*(volatile uint8_t *)(base + NPCX_WKST_OFFSET(group)))
/*
* General-Purpose I/O (GPIO) device registers