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:
parent
c2179bcef0
commit
0bf4ec6d7b
3 changed files with 105 additions and 3 deletions
|
@ -11,3 +11,10 @@ config NPCX_MIWU
|
||||||
This option enables the Multi-Input Wake-Up Unit (MIWU) driver
|
This option enables the Multi-Input Wake-Up Unit (MIWU) driver
|
||||||
for NPCX family of processors.
|
for NPCX family of processors.
|
||||||
This is required for GPIO, RTC, LPC/eSPI interrupt support.
|
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.
|
||||||
|
|
|
@ -82,6 +82,10 @@ struct intc_miwu_config {
|
||||||
struct intc_miwu_data {
|
struct intc_miwu_data {
|
||||||
/* Callback functions list for each MIWU group */
|
/* Callback functions list for each MIWU group */
|
||||||
sys_slist_t cb_list_grp[8];
|
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),
|
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)
|
static void intc_miwu_isr_pri(int wui_table, int wui_group)
|
||||||
{
|
{
|
||||||
const struct intc_miwu_config *config = miwu_devs[wui_table]->config;
|
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;
|
const uint32_t base = config->base;
|
||||||
uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
|
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 */
|
/* Clear pending bits before dispatch ISR */
|
||||||
if (mask) {
|
if (mask) {
|
||||||
NPCX_WKPCL(base, wui_group) = mask;
|
NPCX_WKPCL(base, wui_group) = mask;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Dispatch registered gpio isrs */
|
/* Dispatch registered gpio isrs */
|
||||||
intc_miwu_dispatch_isr(&data->cb_list_grp[wui_group], mask);
|
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 struct intc_miwu_config *config = miwu_devs[wui->table]->config;
|
||||||
const uint32_t base = config->base;
|
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);
|
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)
|
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 struct intc_miwu_config *config = miwu_devs[wui->table]->config;
|
||||||
const uint32_t base = config->base;
|
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);
|
bool pending = IS_BIT_SET(NPCX_WKPND(base, wui->group), wui->bit);
|
||||||
|
|
||||||
if (pending) {
|
if (pending) {
|
||||||
|
#ifdef CONFIG_NPCX_MIWU_BOTH_EDGE_TRIG_WORKAROUND
|
||||||
|
key = k_spin_lock(&data->lock);
|
||||||
|
|
||||||
NPCX_WKPCL(base, wui->group) = BIT(wui->bit);
|
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;
|
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 struct intc_miwu_config *config = miwu_devs[wui->table]->config;
|
||||||
const uint32_t base = config->base;
|
const uint32_t base = config->base;
|
||||||
uint8_t pmask = BIT(wui->bit);
|
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 */
|
/* Disable interrupt of wake-up input source before configuring it */
|
||||||
npcx_miwu_irq_disable(wui);
|
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 */
|
/* Handle interrupt for level trigger */
|
||||||
if (mode == NPCX_MIWU_MODE_LEVEL) {
|
if (mode == NPCX_MIWU_MODE_LEVEL) {
|
||||||
/* Set detection mode to 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;
|
NPCX_WKEDG(base, wui->group) |= pmask;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto early_exit;
|
||||||
}
|
}
|
||||||
/* Handle interrupt for edge trigger */
|
/* Handle interrupt for edge trigger */
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,11 +311,17 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
|
||||||
break;
|
break;
|
||||||
/* Handle interrupting on both edges */
|
/* Handle interrupting on both edges */
|
||||||
case NPCX_MIWU_TRIG_BOTH:
|
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 */
|
/* Enable any edge */
|
||||||
NPCX_WKAEDG(base, wui->group) |= pmask;
|
NPCX_WKAEDG(base, wui->group) |= pmask;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
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,
|
void npcx_miwu_init_gpio_callback(struct miwu_callback *callback,
|
||||||
|
|
|
@ -396,6 +396,8 @@ struct uart_reg {
|
||||||
(*(volatile uint8_t *)(base + NPCX_WKINEN_OFFSET(group)))
|
(*(volatile uint8_t *)(base + NPCX_WKINEN_OFFSET(group)))
|
||||||
#define NPCX_WKMOD(base, group) \
|
#define NPCX_WKMOD(base, group) \
|
||||||
(*(volatile uint8_t *)(base + NPCX_WKMOD_OFFSET(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
|
* General-Purpose I/O (GPIO) device registers
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue