summaryrefslogtreecommitdiff
path: root/zephyr/drivers/cros_rtc/cros_rtc_npcx.c
diff options
context:
space:
mode:
authorMulin Chao <mlchao@nuvoton.com>2021-03-22 17:09:02 +0800
committerCommit Bot <commit-bot@chromium.org>2021-03-24 07:03:54 +0000
commit57b25fbf444379f488a731fcd0b745ef1f42e87b (patch)
tree444360e53066c81b8995049ab0d41fe8df806961 /zephyr/drivers/cros_rtc/cros_rtc_npcx.c
parent87910263fc889211312efbce054c811b61af7a74 (diff)
downloadchrome-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.c235
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);