diff options
author | Mulin Chao <mlchao@nuvoton.com> | 2021-03-22 17:09:02 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-03-24 07:03:54 +0000 |
commit | 57b25fbf444379f488a731fcd0b745ef1f42e87b (patch) | |
tree | 444360e53066c81b8995049ab0d41fe8df806961 /zephyr/drivers/cros_rtc/cros_rtc_npcx.c | |
parent | 87910263fc889211312efbce054c811b61af7a74 (diff) | |
download | chrome-ec-57b25fbf444379f488a731fcd0b745ef1f42e87b.tar.gz |
zephyr: add cros_rtc_npcx driver for npcx7 series
This implements cros_rtc_npcx driver for Zephyr OS-based EC. For the
NPCX7 chip, NPCX Monotonic Counter(MTC) provides a time-keeping
function with a resolution of one second. MTC is selected to implement
the cros_rtc_npcx driver.
BUG=b:178230662
BRANCH=None.
TEST=zmake testall
Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
Signed-off-by: Wealian Liao <whliao@nuvoton.corp-partner.google.com>
Change-Id: Ibd9e27efe4a8b84cdac6d61539742c4f3eb93fce
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2767056
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'zephyr/drivers/cros_rtc/cros_rtc_npcx.c')
-rw-r--r-- | zephyr/drivers/cros_rtc/cros_rtc_npcx.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/zephyr/drivers/cros_rtc/cros_rtc_npcx.c b/zephyr/drivers/cros_rtc/cros_rtc_npcx.c new file mode 100644 index 0000000000..11aa1994e5 --- /dev/null +++ b/zephyr/drivers/cros_rtc/cros_rtc_npcx.c @@ -0,0 +1,235 @@ +/* + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nuvoton_npcx_cros_mtc + +#include <assert.h> +#include <drivers/cros_rtc.h> +#include <drivers/gpio.h> +#include <kernel.h> +#include <soc.h> +#include <soc/nuvoton_npcx/reg_def_cros.h> + +#include "ec_tasks.h" +#include "soc_miwu.h" +#include "task.h" + +#include <logging/log.h> +LOG_MODULE_REGISTER(cros_rtc, LOG_LEVEL_ERR); + +#define NPCX_MTC_TTC_LOAD_DELAY_US 250 /* Delay after writing TTC */ +#define NPCX_MTC_ALARM_MASK GENMASK(24, 0) /* Valid field of alarm in WTC */ + +/* Driver config */ +struct cros_rtc_npcx_config { + /* Monotonic counter base address */ + uintptr_t base; + /* Monotonic counter wake-up input source configuration */ + const struct npcx_wui mtc_alarm; +}; + +/* Driver data */ +struct cros_rtc_npcx_data { + /* Monotonic counter wake-up callback object */ + struct miwu_dev_callback miwu_mtc_cb; + cros_rtc_alarm_callback_t alarm_callback; +}; + +/* Driver convenience defines */ +#define DRV_CONFIG(dev) ((const struct cros_rtc_npcx_config *)(dev)->config) + +#define DRV_DATA(dev) ((struct cros_rtc_npcx_data *)(dev)->data) + +#define HAL_INSTANCE(dev) (struct mtc_reg *)(DRV_CONFIG(dev)->base) + +/* Counter internal local functions */ +static uint32_t counter_npcx_get_val(const struct device *dev) +{ + struct mtc_reg *const inst = HAL_INSTANCE(dev); + + /* + * Get value of monotonic counter which keeps counting when VCC1 power + * domain exists (Unit:sec) + */ + return inst->TTC; +} + +static void counter_npcx_set_val(const struct device *dev, uint32_t val) +{ + struct mtc_reg *const inst = HAL_INSTANCE(dev); + + /* + * Set monotonic counter. Write it twice to ensure the value latch to + * TTC register. A delay (~250 us) is also needed before writing again. + */ + inst->TTC = val; + k_busy_wait(NPCX_MTC_TTC_LOAD_DELAY_US); + + inst->TTC = val; + k_busy_wait(NPCX_MTC_TTC_LOAD_DELAY_US); +} + +static uint32_t counter_npcx_get_alarm_val(const struct device *dev) +{ + struct mtc_reg *const inst = HAL_INSTANCE(dev); + + /* + * If alarm is not set or it is set and has already gone off, return + * zero directly. + */ + if (!IS_BIT_SET(inst->WTC, NPCX_WTC_WIE) || + IS_BIT_SET(inst->WTC, NPCX_WTC_PTO)) { + return 0; + } + + /* Return 25-bit alarm value */ + return inst->WTC & NPCX_MTC_ALARM_MASK; +} + +static void counter_npcx_set_alarm_val(const struct device *dev, uint32_t val) +{ + struct mtc_reg *const inst = HAL_INSTANCE(dev); + + /* Disable alarm interrupt */ + inst->WTC &= ~BIT(NPCX_WTC_WIE); + + /* Set new alarm value */ + inst->WTC = val & NPCX_MTC_ALARM_MASK; + + /* Enable alarm interrupt */ + inst->WTC |= BIT(NPCX_WTC_WIE); +} + +static void counter_npcx_reset_alarm(const struct device *dev) +{ + struct mtc_reg *const inst = HAL_INSTANCE(dev); + + /* Disable alarm interrupt first */ + if (IS_BIT_SET(inst->WTC, NPCX_WTC_WIE)) { + inst->WTC &= ~BIT(NPCX_WTC_WIE); + } + + /* Set alarm to maximum value and clear its pending bit */ + if (IS_BIT_SET(inst->WTC, NPCX_WTC_PTO)) { + inst->WTC = NPCX_MTC_ALARM_MASK; + inst->WTC |= BIT(NPCX_WTC_PTO); + } +} + +/* Counter local functions */ +static void counter_npcx_isr(const struct device *dev, struct npcx_wui *wui) +{ + struct cros_rtc_npcx_data *data = DRV_DATA(dev); + + LOG_DBG("%s", __func__); + + /* Alarm is one-shot, so reset alarm to default */ + counter_npcx_reset_alarm(dev); + + /* Call callback function */ + if (data->alarm_callback) { + data->alarm_callback(dev); + } +} + +/* cros ec RTC api functions */ +static int cros_rtc_npcx_configure(const struct device *dev, + cros_rtc_alarm_callback_t callback) +{ + struct cros_rtc_npcx_data *data = DRV_DATA(dev); + + if (callback == NULL) { + return -EINVAL; + } + + data->alarm_callback = callback; + return 0; +} + +static int cros_rtc_npcx_get_value(const struct device *dev, uint32_t *value) +{ + *value = counter_npcx_get_val(dev); + + return 0; +} + +static int cros_rtc_npcx_set_value(const struct device *dev, uint32_t value) +{ + counter_npcx_set_val(dev, value); + + return 0; +} +static int cros_rtc_npcx_get_alarm(const struct device *dev, uint32_t *seconds, + uint32_t *microseconds) +{ + *seconds = counter_npcx_get_alarm_val(dev); + *microseconds = 0; + + return 0; +} +static int cros_rtc_npcx_set_alarm(const struct device *dev, uint32_t seconds, + uint32_t microseconds) +{ + ARG_UNUSED(microseconds); + + /* Make sure alarm restore to default state */ + counter_npcx_reset_alarm(dev); + counter_npcx_set_alarm_val(dev, seconds); + + return 0; +} + +static int cros_rtc_npcx_reset_alarm(const struct device *dev) +{ + counter_npcx_reset_alarm(dev); + + return 0; +} + +/* cros ec RTC driver registration */ +static const struct cros_rtc_driver_api cros_rtc_npcx_driver_api = { + .configure = cros_rtc_npcx_configure, + .get_value = cros_rtc_npcx_get_value, + .set_value = cros_rtc_npcx_set_value, + .get_alarm = cros_rtc_npcx_get_alarm, + .set_alarm = cros_rtc_npcx_set_alarm, + .reset_alarm = cros_rtc_npcx_reset_alarm, +}; + +static int cros_rtc_npcx_init(const struct device *dev) +{ + const struct cros_rtc_npcx_config *config = DRV_CONFIG(dev); + struct cros_rtc_npcx_data *data = DRV_DATA(dev); + + /* Initialize the miwu input and its callback for monotonic counter */ + npcx_miwu_init_dev_callback(&data->miwu_mtc_cb, &config->mtc_alarm, + counter_npcx_isr, dev); + npcx_miwu_manage_dev_callback(&data->miwu_mtc_cb, true); + + /* + * Configure the monotonic counter wake-up event triggered from a rising + * edge on its signal. + */ + npcx_miwu_interrupt_configure(&config->mtc_alarm, NPCX_MIWU_MODE_EDGE, + NPCX_MIWU_TRIG_HIGH); + + /* Enable interrupt of the wake-up input source */ + npcx_miwu_irq_enable(&config->mtc_alarm); + + return 0; +} + +static const struct cros_rtc_npcx_config cros_rtc_npcx_cfg_0 = { + .base = DT_INST_REG_ADDR(0), + .mtc_alarm = NPCX_DT_WUI_ITEM_BY_NAME(0, mtc_alarm) +}; + +static struct cros_rtc_npcx_data cros_rtc_npcx_data_0; + +DEVICE_DT_INST_DEFINE(0, cros_rtc_npcx_init, device_pm_control_nop, + &cros_rtc_npcx_data_0, &cros_rtc_npcx_cfg_0, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &cros_rtc_npcx_driver_api); |