intc: miwu: npcx: improve interrupt latency of miwu input events

To reduce the interrupt latency of MIWU events, the driver prepares a
dedicated callback function item list for each MIWU group in this PR. We
needn't check the MIWU table and group of the event in ISR. And the
maximum item number of each list is also limited to 8. After applying
this PR, the interrupt latency reduces to ~10us consistently.

Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
This commit is contained in:
Mulin Chao 2023-04-25 00:31:56 -07:00 committed by Carles Cufí
commit 9da9c90639
7 changed files with 118 additions and 146 deletions

View file

@ -176,8 +176,8 @@ static const struct npcx_vw_out_config vw_out_gpio_tbl1[] = {
};
/* Callbacks for eSPI bus reset and Virtual Wire signals. */
static struct miwu_dev_callback espi_rst_callback;
static struct miwu_dev_callback vw_in_callback[ARRAY_SIZE(vw_in_tbl)];
static struct miwu_callback espi_rst_callback;
static struct miwu_callback vw_in_callback[ARRAY_SIZE(vw_in_tbl)];
/* eSPI VW service function forward declarations */
static int espi_npcx_receive_vwire(const struct device *dev,
@ -188,7 +188,7 @@ static void espi_vw_send_bootload_done(const struct device *dev);
/* eSPI local initialization functions */
static void espi_init_wui_callback(const struct device *dev,
struct miwu_dev_callback *callback, const struct npcx_wui *wui,
struct miwu_callback *callback, const struct npcx_wui *wui,
miwu_dev_callback_handler_t handler)
{
/* VW signal which has no wake-up input source */
@ -197,7 +197,7 @@ static void espi_init_wui_callback(const struct device *dev,
/* Install callback function */
npcx_miwu_init_dev_callback(callback, wui, handler, dev);
npcx_miwu_manage_dev_callback(callback, 1);
npcx_miwu_manage_callback(callback, 1);
/* Configure MIWU setting and enable its interrupt */
npcx_miwu_interrupt_configure(wui, NPCX_MIWU_MODE_EDGE,

View file

@ -356,7 +356,7 @@ static int gpio_npcx_manage_callback(const struct device *dev,
struct gpio_callback *callback, bool set)
{
const struct gpio_npcx_config *const config = dev->config;
struct miwu_io_callback *miwu_cb = (struct miwu_io_callback *)callback;
struct miwu_callback *miwu_cb = (struct miwu_callback *)callback;
int pin = find_lsb_set(callback->pin_mask) - 1;
/* pin_mask should not be zero */
@ -376,7 +376,7 @@ static int gpio_npcx_manage_callback(const struct device *dev,
config->port);
/* Insert or remove a IO callback which being called in MIWU ISRs */
return npcx_miwu_manage_gpio_callback(miwu_cb, set);
return npcx_miwu_manage_callback(miwu_cb, set);
}
/* GPIO driver registration */

View file

@ -78,68 +78,53 @@ struct intc_miwu_config {
uint8_t index;
};
/* Callback functions list for GPIO wake-up inputs */
sys_slist_t cb_list_gpio;
/*
* Callback functions list for the generic hardware modules wake-up inputs
* such as timer, uart, i2c, host interface and so on.
*/
sys_slist_t cb_list_generic;
BUILD_ASSERT(sizeof(struct miwu_io_callback) == sizeof(struct gpio_callback),
"Size of struct miwu_io_callback must equal to struct gpio_callback");
/* Driver data */
struct intc_miwu_data {
/* Callback functions list for each MIWU group */
sys_slist_t cb_list_grp[8];
};
BUILD_ASSERT(sizeof(struct miwu_io_params) == sizeof(gpio_port_pins_t),
"Size of struct miwu_io_params must equal to struct gpio_port_pins_t");
BUILD_ASSERT(offsetof(struct miwu_callback, io_cb.params) +
sizeof(struct miwu_io_params) == sizeof(struct gpio_callback),
"Failed in size check of miwu_callback and gpio_callback structures!");
BUILD_ASSERT(offsetof(struct miwu_callback, io_cb.params.cb_type) ==
offsetof(struct miwu_callback, dev_cb.params.cb_type),
"Failed in offset check of cb_type field of miwu_callback structure");
/* MIWU local functions */
static void intc_miwu_dispatch_gpio_isr(uint8_t wui_table,
uint8_t wui_group, uint8_t wui_bit)
static void intc_miwu_dispatch_isr(sys_slist_t *cb_list, uint8_t mask)
{
struct miwu_io_callback *cb, *tmp;
struct miwu_callback *cb, *tmp;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_gpio, cb, tmp, node) {
/* Pending bit, group and table match the wui item in list */
if (cb->params.wui.table == wui_table
&& cb->params.wui.group == wui_group
&& cb->params.wui.bit == wui_bit) {
__ASSERT(cb->handler, "No GPIO callback handler!");
/*
* Execute GPIO callback and the other callback might
* match the same wui item.
*/
cb->handler(npcx_get_gpio_dev(cb->params.gpio_port),
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(cb_list, cb, tmp, node) {
if (cb->io_cb.params.cb_type == NPCX_MIWU_CALLBACK_GPIO) {
if (BIT(cb->io_cb.params.wui.bit) & mask) {
__ASSERT(cb->io_cb.handler, "No GPIO callback handler!");
cb->io_cb.handler(
npcx_get_gpio_dev(cb->io_cb.params.gpio_port),
(struct gpio_callback *)cb,
cb->params.pin_mask);
}
}
}
cb->io_cb.params.pin_mask);
}
} else {
if (BIT(cb->dev_cb.params.wui.bit) & mask) {
__ASSERT(cb->dev_cb.handler, "No device callback handler!");
static void intc_miwu_dispatch_generic_isr(uint8_t wui_table,
uint8_t wui_group, uint8_t wui_bit)
{
struct miwu_dev_callback *cb, *tmp;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_generic, cb, tmp, node) {
/* Pending bit, group and table match the wui item in list */
if (cb->wui.table == wui_table
&& cb->wui.group == wui_group
&& cb->wui.bit == wui_bit) {
__ASSERT(cb->handler, "No Generic callback handler!");
/*
* Execute generic callback and the other callback might
* match the same wui item.
*/
cb->handler(cb->source, &cb->wui);
cb->dev_cb.handler(cb->dev_cb.params.source,
&cb->dev_cb.params.wui);
}
}
}
}
static void intc_miwu_isr_pri(int wui_table, int wui_group)
{
int wui_bit;
const struct intc_miwu_config *config = miwu_devs[wui_table]->config;
struct intc_miwu_data *data = miwu_devs[wui_table]->data;
const uint32_t base = config->base;
uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
@ -148,17 +133,8 @@ static void intc_miwu_isr_pri(int wui_table, int wui_group)
NPCX_WKPCL(base, wui_group) = mask;
}
for (wui_bit = 0; wui_bit < 8; wui_bit++) {
if (mask & BIT(wui_bit)) {
LOG_DBG("miwu_isr %d %d %d!\n", wui_table,
wui_group, wui_bit);
/* Dispatch registered gpio and generic isrs */
intc_miwu_dispatch_gpio_isr(wui_table,
wui_group, wui_bit);
intc_miwu_dispatch_generic_isr(wui_table,
wui_group, wui_bit);
}
}
/* Dispatch registered gpio isrs */
intc_miwu_dispatch_isr(&data->cb_list_grp[wui_group], mask);
}
/* Platform specific MIWU functions */
@ -278,33 +254,47 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
return 0;
}
void npcx_miwu_init_gpio_callback(struct miwu_io_callback *callback,
void npcx_miwu_init_gpio_callback(struct miwu_callback *callback,
const struct npcx_wui *io_wui, int port)
{
/* Initialize WUI and GPIO settings in unused bits field */
callback->params.wui.table = io_wui->table;
callback->params.wui.group = io_wui->group;
callback->params.wui.bit = io_wui->bit;
callback->params.gpio_port = port;
callback->io_cb.params.wui.table = io_wui->table;
callback->io_cb.params.wui.bit = io_wui->bit;
callback->io_cb.params.gpio_port = port;
callback->io_cb.params.cb_type = NPCX_MIWU_CALLBACK_GPIO;
callback->io_cb.params.wui.group = io_wui->group;
}
void npcx_miwu_init_dev_callback(struct miwu_dev_callback *callback,
void npcx_miwu_init_dev_callback(struct miwu_callback *callback,
const struct npcx_wui *dev_wui,
miwu_dev_callback_handler_t handler,
const struct device *source)
{
/* Initialize WUI and input device settings */
callback->wui.table = dev_wui->table;
callback->wui.group = dev_wui->group;
callback->wui.bit = dev_wui->bit;
callback->handler = handler;
callback->source = source;
callback->dev_cb.params.wui.table = dev_wui->table;
callback->dev_cb.params.wui.group = dev_wui->group;
callback->dev_cb.params.wui.bit = dev_wui->bit;
callback->dev_cb.params.source = source;
callback->dev_cb.params.cb_type = NPCX_MIWU_CALLBACK_DEV;
callback->dev_cb.handler = handler;
}
int npcx_miwu_manage_gpio_callback(struct miwu_io_callback *cb, bool set)
int npcx_miwu_manage_callback(struct miwu_callback *cb, bool set)
{
if (!sys_slist_is_empty(&cb_list_gpio)) {
if (!sys_slist_find_and_remove(&cb_list_gpio, &cb->node)) {
struct npcx_wui *wui;
struct intc_miwu_data *data;
sys_slist_t *cb_list;
if (cb->io_cb.params.cb_type == NPCX_MIWU_CALLBACK_GPIO) {
wui = &cb->io_cb.params.wui;
} else {
wui = &cb->dev_cb.params.wui;
}
data = miwu_devs[wui->table]->data;
cb_list = &data->cb_list_grp[wui->group];
if (!sys_slist_is_empty(cb_list)) {
if (!sys_slist_find_and_remove(cb_list, &cb->node)) {
if (!set) {
return -EINVAL;
}
@ -312,24 +302,7 @@ int npcx_miwu_manage_gpio_callback(struct miwu_io_callback *cb, bool set)
}
if (set) {
sys_slist_prepend(&cb_list_gpio, &cb->node);
}
return 0;
}
int npcx_miwu_manage_dev_callback(struct miwu_dev_callback *cb, bool set)
{
if (!sys_slist_is_empty(&cb_list_generic)) {
if (!sys_slist_find_and_remove(&cb_list_generic, &cb->node)) {
if (!set) {
return -EINVAL;
}
}
}
if (set) {
sys_slist_prepend(&cb_list_generic, &cb->node);
sys_slist_prepend(cb_list, &cb->node);
}
return 0;
@ -385,11 +358,12 @@ int npcx_miwu_manage_dev_callback(struct miwu_dev_callback *cb, bool set)
.base = DT_REG_ADDR(DT_NODELABEL(miwu##inst)), \
.index = DT_PROP(DT_NODELABEL(miwu##inst), index), \
}; \
struct intc_miwu_data miwu_data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, \
NPCX_MIWU_INIT_FUNC(inst), \
NULL, \
NULL, &miwu_config_##inst, \
&miwu_data_##inst, &miwu_config_##inst, \
PRE_KERNEL_1, \
CONFIG_INTC_INIT_PRIORITY, NULL); \
\

View file

@ -59,7 +59,7 @@ struct kscan_npcx_data {
uint8_t matrix_new_state[KSCAN_COL_SIZE];
/* Index in to the scan_clock_cycle to indicate start of debouncing */
uint8_t scan_cycle_idx[KSCAN_COL_SIZE * KSCAN_ROW_SIZE];
struct miwu_dev_callback ksi_callback[KSCAN_ROW_SIZE];
struct miwu_callback ksi_callback[KSCAN_ROW_SIZE];
/* Track previous "elapsed clock cycles" per matrix scan. This
* is used to calculate the debouncing time for every key
*/
@ -393,7 +393,7 @@ static void kscan_matrix_polling_thread(const struct device *dev, void *dummy2,
}
static void kscan_npcx_init_ksi_wui_callback(const struct device *dev,
struct miwu_dev_callback *callback,
struct miwu_callback *callback,
const struct npcx_wui *wui,
miwu_dev_callback_handler_t handler)
{
@ -404,7 +404,7 @@ static void kscan_npcx_init_ksi_wui_callback(const struct device *dev,
/* Install callback function */
npcx_miwu_init_dev_callback(callback, wui, handler, dev);
npcx_miwu_manage_dev_callback(callback, 1);
npcx_miwu_manage_callback(callback, 1);
/* Configure MIWU setting and enable its interrupt */
npcx_miwu_interrupt_configure(wui, NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_BOTH);

View file

@ -47,7 +47,7 @@ enum uart_pm_policy_state_flag {
struct uart_npcx_data {
/* Baud rate */
uint32_t baud_rate;
struct miwu_dev_callback uart_rx_cb;
struct miwu_callback uart_rx_cb;
struct k_spinlock lock;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t user_cb;
@ -519,7 +519,7 @@ static int uart_npcx_init(const struct device *dev)
/* Initialize a miwu device input and its callback function */
npcx_miwu_init_dev_callback(&data->uart_rx_cb, &config->uart_rx_wui,
uart_npcx_rx_wk_isr, dev);
npcx_miwu_manage_dev_callback(&data->uart_rx_cb, true);
npcx_miwu_manage_callback(&data->uart_rx_cb, true);
/*
* Configure the UART wake-up event triggered from a falling
* edge on CR_SIN pin. No need for callback function.

View file

@ -82,7 +82,7 @@ struct wdt_npcx_data {
bool timeout_installed;
};
struct miwu_dev_callback miwu_cb;
struct miwu_callback miwu_cb;
/* Driver convenience defines */
#define HAL_INSTANCE(dev) ((struct twd_reg *)((const struct wdt_npcx_config *)(dev)->config)->base)
@ -153,7 +153,7 @@ static void wdt_config_t0out_interrupt(const struct device *dev)
/* Initialize a miwu device input and its callback function */
npcx_miwu_init_dev_callback(&miwu_cb, &config->t0out, wdt_t0out_isr,
dev);
npcx_miwu_manage_dev_callback(&miwu_cb, true);
npcx_miwu_manage_callback(&miwu_cb, true);
/*
* Configure the T0 wake-up event triggered from a rising edge

View file

@ -50,6 +50,12 @@ enum miwu_int_trig {
NPCX_MIWU_TRIG_BOTH, /** Both edge rising and failing detection */
};
/* NPCX miwu driver callback type */
enum {
NPCX_MIWU_CALLBACK_GPIO,
NPCX_MIWU_CALLBACK_DEV,
};
/**
* @brief NPCX wake-up input source structure
*
@ -79,46 +85,50 @@ typedef void (*miwu_dev_callback_handler_t)(const struct device *source,
*/
struct miwu_io_params {
uint8_t pin_mask; /** A mask of pins the callback is interested in. */
uint8_t reserved;
uint8_t gpio_port; /** GPIO device index */
uint8_t cb_type; /** Callback type */
struct npcx_wui wui; /** Wake-up input source of GPIO */
};
/**
* @brief MIWU callback structure for a GPIO input
* @brief MIWU/generic device information structure
*
* Used to register a GPIO callback in the driver instance callback list.
* Beware such structure should not be allocated on stack and its size must
* equal struct gpio_callback.
*
* Note: To help setting it, see npcx_miwu_init_gpio_callback() below
* It contains the information used for MIWU generic device event. Please notice
* the offset of cb_type must be the same as cb_type in struct miwu_io_params.
*/
struct miwu_io_callback {
/** Node of single-linked list */
sys_snode_t node;
/** Callback function being called when GPIO event occurred */
gpio_callback_handler_t handler;
/** GPIO callback parameters used in MIWU ISR */
struct miwu_io_params params;
struct miwu_dev_params {
uint8_t reserve1;
uint8_t reserve2;
uint8_t cb_type; /** Callback type */
struct npcx_wui wui; /** Device instance register callback function */
const struct device *source; /** Wake-up input source */
};
/**
* @brief MIWU callback structure for a device input
* @brief MIWU callback structure for a gpio or device input
*
* Used to register a generic hardware device callback in the driver instance
* Used to register a generic gpio/device callback in the driver instance
* callback list. Beware such structure should not be allocated on stack.
*
* Note: To help setting it, see npcx_miwu_init_dev_callback() below
* Note: To help setting it, see npcx_miwu_init_dev_callback() and
* npcx_miwu_manage_callback() below
*/
struct miwu_dev_callback {
struct miwu_callback {
/** Node of single-linked list */
sys_snode_t node;
/** Callback function being called when device event occurred */
miwu_dev_callback_handler_t handler;
/** Device instance register callback function */
const struct device *source;
/* Wake-up input source */
struct npcx_wui wui;
union {
struct {
/** Callback function being called when GPIO event occurred */
gpio_callback_handler_t handler;
struct miwu_io_params params;
} io_cb;
struct {
/** Callback function being called when device event occurred */
miwu_dev_callback_handler_t handler;
struct miwu_dev_params params;
} dev_cb;
};
};
/**
@ -181,50 +191,38 @@ int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
enum miwu_int_mode mode, enum miwu_int_trig trig);
/**
* @brief Function to initialize a struct miwu_io_callback properly
* @brief Function to initialize a struct miwu_callback with gpio properly
*
* @param callback Pointer to io callback structure for initialization
* @param io_wui Pointer to wake-up input IO source
* @param port GPIO port issued a callback function
*/
void npcx_miwu_init_gpio_callback(struct miwu_io_callback *callback,
void npcx_miwu_init_gpio_callback(struct miwu_callback *callback,
const struct npcx_wui *io_wui, int port);
/**
* @brief Function to initialize a struct miwu_dev_callback properly
* @brief Function to initialize a struct miwu_callback with device properly
*
* @param callback Pointer to device callback structure for initialization
* @param dev_wui Pointer to wake-up input device source
* @param handler A function called when its device input event issued
* @param source Pointer to device instance issued a callback function
*/
void npcx_miwu_init_dev_callback(struct miwu_dev_callback *callback,
void npcx_miwu_init_dev_callback(struct miwu_callback *callback,
const struct npcx_wui *dev_wui,
miwu_dev_callback_handler_t handler,
const struct device *source);
/**
* @brief Function to insert or remove a IO callback from a callback list
* @brief Function to insert or remove a miwu callback from a callback list
*
* @param callback Pointer to io callback structure
* @param callback Pointer to miwu callback structure
* @param set A boolean indicating insertion or removal of the callback
*
* @retval 0 If successful.
* @retval -EINVAL Invalid parameters
*/
int npcx_miwu_manage_gpio_callback(struct miwu_io_callback *callback, bool set);
/**
* @brief Function to insert or remove a device callback from a callback list
*
* @param callback Pointer to device callback structure
* @param set A boolean indicating insertion or removal of the callback
*
* @retval 0 If successful.
* @retval -EINVAL Invalid parameters
*/
int npcx_miwu_manage_dev_callback(struct miwu_dev_callback *cb, bool set);
int npcx_miwu_manage_callback(struct miwu_callback *cb, bool set);
#ifdef __cplusplus
}