diff options
author | Sam Hurst <shurst@google.com> | 2021-09-21 08:44:33 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-09-23 06:14:02 +0000 |
commit | 3151e266808c9cbff6a5274ad8ab993e5b7d09f0 (patch) | |
tree | adf1d3b81b1ae07eed0639f056808e7fcaed4b37 | |
parent | 5155d9c7bfc42aa3f53572e0389a8180d7f3a366 (diff) | |
download | chrome-ec-3151e266808c9cbff6a5274ad8ab993e5b7d09f0.tar.gz |
RTC: Zephyr IDT1337AG RTC driver
Zephyr IDT1337AG RTC driver
BRANCH=none
BUG=b:197743934
TEST=ectool rtcset 30000
ectool rtcget
Current time: 0x00007535 (30005)
ectool rtcsetalarm 10
after ~10 (measured with a timer) printed on
ec console: [160.248900 event set 0x0000000002000000]
I tested the alarm up to one hour.
Hibernation test:
rtc_alarm 10
hibernate
[339.906200 system hibernating]
*** Booting Zephyr OS build zephyr-v2.6.0-110-g862339fbeda5 ***
[Image: RO, herobrine_npcx9_v2.6.73690-ec:c87457
[Reset cause: power-on hibernate]
Signed-off-by: Sam Hurst <shurst@google.com>
Change-Id: If5b936af2555915582ca38fee751f57bb2b8d10f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3173093
Reviewed-by: Wai-Hong Tam <waihong@google.com>
Reviewed-by: Keith Short <keithshort@chromium.org>
-rw-r--r-- | zephyr/drivers/cros_rtc/CMakeLists.txt | 2 | ||||
-rw-r--r-- | zephyr/drivers/cros_rtc/Kconfig | 7 | ||||
-rw-r--r-- | zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.c | 480 | ||||
-rw-r--r-- | zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.h | 60 | ||||
-rw-r--r-- | zephyr/dts/bindings/cros_rtc/renesas,rtc-idt1337ag.yaml | 17 |
5 files changed, 566 insertions, 0 deletions
diff --git a/zephyr/drivers/cros_rtc/CMakeLists.txt b/zephyr/drivers/cros_rtc/CMakeLists.txt index 24de568406..f6ce9b6890 100644 --- a/zephyr/drivers/cros_rtc/CMakeLists.txt +++ b/zephyr/drivers/cros_rtc/CMakeLists.txt @@ -5,3 +5,5 @@ zephyr_library_sources_ifdef(CONFIG_CROS_RTC_NPCX cros_rtc_npcx.c) zephyr_library_sources_ifdef(CONFIG_CROS_RTC_NXP_PCF85063A nxp_rtc_pcf85063a.c) +zephyr_library_sources_ifdef(CONFIG_CROS_RTC_RENESAS_IDT1337AG + renesas_rtc_idt1337ag.c) diff --git a/zephyr/drivers/cros_rtc/Kconfig b/zephyr/drivers/cros_rtc/Kconfig index 05aefe384c..9abe8e27e0 100644 --- a/zephyr/drivers/cros_rtc/Kconfig +++ b/zephyr/drivers/cros_rtc/Kconfig @@ -24,6 +24,13 @@ config CROS_RTC_NXP_PCF85063A This option enables a driver for providing the support of NXP Real-Time Clock (RTC) on the the I2C bus. +config CROS_RTC_RENESAS_IDT1337AG + bool "RENESAS IDT1337AG Real-Time Clock (RTC) driver for the Zephyr shim" + depends on PLATFORM_EC_I2C + help + This option enables a driver for providing the support of RENESAS + Real-Time Clock (RTC) on the the I2C bus. + endchoice # RTC Select endif # PLATFORM_EC_RTC diff --git a/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.c b/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.c new file mode 100644 index 0000000000..bbe76f5286 --- /dev/null +++ b/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.c @@ -0,0 +1,480 @@ +/* Copyright 2021 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#define DT_DRV_COMPAT renesas_rtc_idt1337ag + +#include <assert.h> +#include <device.h> +#include <drivers/cros_rtc.h> +#include <drivers/gpio.h> +#include <drivers/i2c.h> +#include <kernel.h> +#include <rtc.h> +#include <soc.h> + +#include "renesas_rtc_idt1337ag.h" + +#include <logging/log.h> +LOG_MODULE_REGISTER(cros_rtc, LOG_LEVEL_ERR); + +/* Driver config */ +struct renesas_rtc_idt1337ag_config { + const struct device *bus; + const uint16_t i2c_addr_flags; + const struct gpio_dt_spec gpio_alert; +}; + +/* Driver data */ +struct renesas_rtc_idt1337ag_data { + const struct device *dev; + struct gpio_callback gpio_cb; + cros_rtc_alarm_callback_t alarm_callback; +}; + +enum timer_type { + RTC_TIMER, + ALARM_TIMER, +}; + +/* + * type == ALARM_TIMER: Reads alarm registers SECONDS, MINUTES, HOURS, and DAYS + * type == RTC_TIMER: Reads time registers SECONDS, MINUTES, HOURS, DAYS, and + * MONTHS, YEARS + */ +static int idt1337ag_read_time_regs(const struct device *dev, + uint8_t *time_reg, enum timer_type type) +{ + const struct renesas_rtc_idt1337ag_config *const config = dev->config; + uint8_t start_reg; + uint8_t num_reg; + + if (type == ALARM_TIMER) { + start_reg = REG_SECOND_ALARM1; + num_reg = NUM_ALARM_REGS; + } else { + start_reg = REG_SECONDS; + num_reg = NUM_TIMER_REGS; + } + + return i2c_burst_read(config->bus, + config->i2c_addr_flags, start_reg, time_reg, num_reg); +} + +static int idt1337ag_read_reg(const struct device *dev, + uint8_t reg, uint8_t *val) +{ + const struct renesas_rtc_idt1337ag_config *const config = dev->config; + + return i2c_reg_read_byte(config->bus, config->i2c_addr_flags, reg, val); +} + +/* + * type == ALARM_TIMER: Writes alarm registers SECONDS, MINUTES, HOURS, and DAYS + * type == RTC_TIMER: Writes time registers SECONDS, MINUTES, HOURS, DAYS, and + * MONTHS, YEARS + */ +static int idt1337ag_write_time_regs(const struct device *dev, + uint8_t *time_reg, enum timer_type type) +{ + const struct renesas_rtc_idt1337ag_config *const config = dev->config; + uint8_t start_reg; + uint8_t num_reg; + + if (type == ALARM_TIMER) { + /* + * Register 0x0A bit 6 determines if the DAY(1b) or DATE(0b) + * alarm is selected. + * Select the DAY alarm + */ + time_reg[DAYS] |= SELECT_DAYS_ALARM; + + start_reg = REG_SECOND_ALARM1; + num_reg = NUM_ALARM_REGS; + } else { + start_reg = REG_SECONDS; + num_reg = NUM_TIMER_REGS; + } + + return i2c_burst_write(config->bus, + config->i2c_addr_flags, start_reg, time_reg, num_reg); +} + +static int idt1337ag_write_reg(const struct device *dev, + uint8_t reg, uint8_t val) +{ + const struct renesas_rtc_idt1337ag_config *const config = dev->config; + uint8_t tx_buf[2]; + + tx_buf[0] = reg; + tx_buf[1] = val; + + return i2c_write(config->bus, + tx_buf, sizeof(tx_buf), config->i2c_addr_flags); +} + +/* + * val bits 7 to 4 - tens place + * val bits 3 to 0 - ones place + */ +static int bcd_to_dec(uint8_t val, enum bcd_mask mask) +{ + int tens = ((val & mask) >> 4) * 10; + int ones = (val & 0xf); + + return tens + ones; +} + +/* + * val bits 7 to 4 - tens place + * val bits 3 to 0 - ones place + */ +static uint8_t dec_to_bcd(uint32_t val, enum bcd_mask mask) +{ + int tens = val / 10; + int ones = val - (tens * 10); + + return ((tens << 4) & mask) | ones; +} + +static int renesas_rtc_idt1337ag_read_seconds(const struct device *dev, + uint32_t *value, enum timer_type type) +{ + uint8_t time_reg[NUM_TIMER_REGS]; + struct calendar_date time; + int ret; + + ret = idt1337ag_read_time_regs(dev, time_reg, type); + + if (ret < 0) { + return ret; + } + + if (type == ALARM_TIMER) { + *value = (bcd_to_dec(time_reg[DAYS], DAYS_MASK) * + SECS_PER_DAY) + + (bcd_to_dec(time_reg[HOURS], HOURS24_MASK) * + SECS_PER_HOUR) + + (bcd_to_dec(time_reg[MINUTES], MINUTES_MASK) * + SECS_PER_MINUTE) + + bcd_to_dec(time_reg[SECONDS], SECONDS_MASK); + } else { + time.year = bcd_to_dec(time_reg[YEARS], YEARS_MASK); + time.month = bcd_to_dec(time_reg[MONTHS], MONTHS_MASK); + time.day = bcd_to_dec(time_reg[DAYS], DAYS_MASK); + + *value = date_to_sec(time) - SECS_TILL_YEAR_2K + + (bcd_to_dec(time_reg[HOURS], HOURS24_MASK) * + SECS_PER_HOUR) + + (bcd_to_dec(time_reg[MINUTES], MINUTES_MASK) * + SECS_PER_MINUTE) + + bcd_to_dec(time_reg[SECONDS], SECONDS_MASK); + } + + return ret; +} + +static int renesas_rtc_idt1337ag_write_seconds(const struct device *dev, + uint32_t value, enum timer_type type) +{ + uint8_t time_reg[NUM_TIMER_REGS]; + struct calendar_date time; + uint32_t tmp_sec; + + time = sec_to_date(value + SECS_TILL_YEAR_2K); + + if (type == RTC_TIMER) { + time_reg[YEARS] = dec_to_bcd(time.year, YEARS_MASK); + time_reg[MONTHS] = dec_to_bcd(time.month, MONTHS_MASK); + } + + time_reg[DAYS] = dec_to_bcd(time.day, DAYS_MASK); + + value %= SECS_PER_DAY; + tmp_sec = value / SECS_PER_HOUR; + time_reg[HOURS] = dec_to_bcd(tmp_sec, HOURS24_MASK); + + value -= (tmp_sec * SECS_PER_HOUR); + tmp_sec = value / SECS_PER_MINUTE; + time_reg[MINUTES] = dec_to_bcd(tmp_sec, MINUTES_MASK); + + value -= (tmp_sec * SECS_PER_MINUTE); + time_reg[SECONDS] = dec_to_bcd(value, SECONDS_MASK); + + return idt1337ag_write_time_regs(dev, time_reg, type); +} + +static int renesas_rtc_idt1337ag_configure(const struct device *dev, + cros_rtc_alarm_callback_t callback) +{ + struct renesas_rtc_idt1337ag_data *data = dev->data; + + if (callback == NULL) { + return -EINVAL; + } + + data->alarm_callback = callback; + + return 0; +} + +static int renesas_rtc_idt1337ag_get_value(const struct device *dev, + uint32_t *value) +{ + return renesas_rtc_idt1337ag_read_seconds(dev, value, RTC_TIMER); +} + +static int renesas_rtc_idt1337ag_set_value(const struct device *dev, + uint32_t value) +{ + return renesas_rtc_idt1337ag_write_seconds(dev, value, RTC_TIMER); +} + +static int renesas_rtc_idt1337ag_get_alarm(const struct device *dev, + uint32_t *seconds, uint32_t *microseconds) +{ + *microseconds = 0; + return renesas_rtc_idt1337ag_read_seconds(dev, seconds, ALARM_TIMER); +} + +static int renesas_rtc_idt1337ag_reset_alarm(const struct device *dev) +{ + uint8_t time_reg[NUM_ALARM_REGS]; + int ret; + uint8_t val; + + ret = idt1337ag_read_reg(dev, REG_CONTROL, &val); + + if (ret < 0) { + return ret; + } + + /* Disable alarm interrupt and clear pending alarm flag */ + val &= ~CONTROL_A1IE; + ret = idt1337ag_write_reg(dev, REG_CONTROL, val); + + if (ret < 0) { + return ret; + } + + /* Clear alarm1 flag if set */ + ret = idt1337ag_read_reg(dev, REG_STATUS, &val); + + if (ret < 0) { + return ret; + } + + /* Clear the alarm1 and alarm2 flag */ + val &= ~(STATUS_A1F | STATUS_A2F); + ret = idt1337ag_write_reg(dev, REG_STATUS, val); + + if (ret < 0) { + return ret; + } + + /* Clear and disable the alarm registers */ + time_reg[SECONDS] = DISABLE_ALARM; + time_reg[MINUTES] = DISABLE_ALARM; + time_reg[HOURS] = DISABLE_ALARM; + time_reg[DAYS] = DISABLE_ALARM; + + return idt1337ag_write_time_regs(dev, time_reg, ALARM_TIMER); +} + +static int renesas_rtc_idt1337ag_set_alarm(const struct device *dev, + uint32_t seconds, uint32_t microseconds) +{ + int ret; + uint8_t val; + + ARG_UNUSED(microseconds); + + ret = renesas_rtc_idt1337ag_reset_alarm(dev); + if (ret < 0) { + return ret; + } + + ret = renesas_rtc_idt1337ag_write_seconds(dev, seconds, ALARM_TIMER); + if (ret < 0) { + return ret; + } + + ret = idt1337ag_read_reg(dev, REG_CONTROL, &val); + if (ret < 0) { + return ret; + } + + val |= CONTROL_A1IE; + idt1337ag_write_reg(dev, REG_CONTROL, val); + + return 0; +} + +static void renesas_rtc_idt1337ag_isr(const struct device *port, + struct gpio_callback *cb, uint32_t pin) +{ + struct renesas_rtc_idt1337ag_data *data = + CONTAINER_OF(cb, struct renesas_rtc_idt1337ag_data, gpio_cb); + const struct device *dev = data->dev; + + ARG_UNUSED(port); + ARG_UNUSED(pin); + ARG_UNUSED(cb); + + LOG_DBG("%s", __func__); + + /* Call callback function */ + if (data->alarm_callback) { + data->alarm_callback(dev); + } +} + +static const struct cros_rtc_driver_api renesas_rtc_idt1337ag_driver_api = { + .configure = renesas_rtc_idt1337ag_configure, + .get_value = renesas_rtc_idt1337ag_get_value, + .set_value = renesas_rtc_idt1337ag_set_value, + .get_alarm = renesas_rtc_idt1337ag_get_alarm, + .set_alarm = renesas_rtc_idt1337ag_set_alarm, + .reset_alarm = renesas_rtc_idt1337ag_reset_alarm, +}; + +static int renesas_rtc_idt1337ag_init(const struct device *dev) +{ + const struct renesas_rtc_idt1337ag_config *const config = dev->config; + struct renesas_rtc_idt1337ag_data *data = dev->data; + uint8_t val; + int ret; + + if (!device_is_ready(config->bus)) { + LOG_ERR("Device %s is not ready", config->bus->name); + return -ENODEV; + } + + /* + * Read Control register. For normal operation, + * the values should be as follows: + * Bit 7 (enable oscillator) : (0) normal mode + * Bit 6 (unused) : (0) + * Bit 5 (unused) : (0) + * BIT 4 (RS2) : (0) Not used when INTCN == 1 + * BIT 3 (RS1) : (0) Not used when INTCN == 1 + * BIT 2 (INTCN) : (1) a match between the timekeeping + * registers and the alarm 1 + * registers activate the INTA pin + * BIT 1 (A2IE) : (0) Alarm 2 is not used + * BIT 0 (A1IE) : (1) Enables Alarm 1 + */ + ret = idt1337ag_read_reg(dev, REG_CONTROL, &val); + + if (ret < 0) { + return ret; + } + + /* Enable IDT1337AG oscillator */ + val &= ~CONTROL_EOSC; + + /* Disable Alarm 2 */ + val &= ~CONTROL_A2IE; + + /* Alarm 1 assert INTA pin */ + val |= CONTROL_INTCN; + + ret = idt1337ag_write_reg(dev, REG_CONTROL, val); + + if (ret < 0) { + return ret; + } + + /* Date register isn't used. Set it to zero */ + ret = idt1337ag_write_reg(dev, REG_DATE, 0); + + /* Make sure the oscillator is running */ + ret = idt1337ag_read_reg(dev, REG_STATUS, &val); + + if (ret < 0) { + return ret; + } + + /* Clear IDT1337AG oscillator not running flag */ + val &= ~STATUS_OSF; + + /* Clear Alarm 2 flag */ + val &= ~STATUS_A2F; + + ret = idt1337ag_write_reg(dev, REG_STATUS, val); + + if (ret < 0) { + return ret; + } + + renesas_rtc_idt1337ag_reset_alarm(dev); + + /* Disable Alarm2 */ + idt1337ag_write_reg(dev, REG_MINUTE_ALARM2, DISABLE_ALARM); + idt1337ag_write_reg(dev, REG_HOUR_ALARM2, DISABLE_ALARM); + idt1337ag_write_reg(dev, REG_DAY_ALARM2, DISABLE_ALARM); + + /* Configure GPIO interrupt pin for IDT1337AG alarm pin */ + + if (!device_is_ready(config->gpio_alert.port)) { + LOG_ERR("Alert GPIO device not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&config->gpio_alert, GPIO_INPUT); + + if (ret < 0) { + LOG_ERR("Could not configure RTC alert pin"); + return ret; + } + + gpio_init_callback(&data->gpio_cb, + renesas_rtc_idt1337ag_isr, BIT(config->gpio_alert.pin)); + + ret = gpio_add_callback(config->gpio_alert.port, &data->gpio_cb); + + if (ret < 0) { + LOG_ERR("Could not set RTC alert pin callback"); + return ret; + } + + data->dev = dev; + + return gpio_pin_interrupt_configure_dt(&config->gpio_alert, + GPIO_INT_EDGE_FALLING); +} + +#define IDT1337AG_INT_GPIOS \ + DT_PHANDLE_BY_IDX(DT_NODELABEL(idt1337ag), int_gpios, 0) + +/* + * dt_flags is a uint8_t type. However, for platform/ec + * the GPIO flags in the devicetree are expanded past 8 bits + * to support the INPUT/OUTPUT and PULLUP/PULLDOWN properties. + * Cast back to a gpio_dt_flags to compile, discarding the bits + * that are not supported by the Zephyr GPIO API. + */ +#define CROS_EC_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \ + { \ + .port = \ + DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \ + .pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \ + .dt_flags = \ + (gpio_dt_flags_t)DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \ + } + +static const struct renesas_rtc_idt1337ag_config renesas_rtc_idt1337ag_cfg_0 = { + .bus = DEVICE_DT_GET(DT_INST_BUS(0)), + .i2c_addr_flags = DT_INST_REG_ADDR(0), + .gpio_alert = + CROS_EC_GPIO_DT_SPEC_GET_BY_IDX(IDT1337AG_INT_GPIOS, gpios, 0) +}; + +static struct renesas_rtc_idt1337ag_data renesas_rtc_idt1337ag_data_0; + +DEVICE_DT_INST_DEFINE(0, renesas_rtc_idt1337ag_init, /* pm_control_fn= */ NULL, + &renesas_rtc_idt1337ag_data_0, + &renesas_rtc_idt1337ag_cfg_0, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &renesas_rtc_idt1337ag_driver_api); diff --git a/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.h b/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.h new file mode 100644 index 0000000000..3296f80992 --- /dev/null +++ b/zephyr/drivers/cros_rtc/renesas_rtc_idt1337ag.h @@ -0,0 +1,60 @@ +/* Copyright 2021 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __CROS_EC_RTC_IDT1337AG_H +#define __CROS_EC_RTC_IDT1337AG_H + +/* Setting bit 6 of register 0Ah selects the DAY as alarm source */ +#define SELECT_DAYS_ALARM 0x40 +#define DISABLE_ALARM 0x80 + +#define CONTROL_A1IE BIT(0) +#define CONTROL_A2IE BIT(1) +#define CONTROL_INTCN BIT(2) +#define CONTROL_EOSC BIT(7) + +#define STATUS_A1F BIT(0) +#define STATUS_A2F BIT(1) +#define STATUS_OSF BIT(7) + +#define NUM_TIMER_REGS 7 +#define NUM_ALARM_REGS 4 + +#define REG_SECONDS 0x00 +#define REG_MINUTES 0x01 +#define REG_HOURS 0x02 +#define REG_DAYS 0x03 +#define REG_DATE 0x04 +#define REG_MONTHS 0x05 +#define REG_YEARS 0x06 +#define REG_SECOND_ALARM1 0x07 +#define REG_MINUTE_ALARM1 0x08 +#define REG_HOUR_ALARM1 0x09 +#define REG_DAY_ALARM1 0x0a +#define REG_MINUTE_ALARM2 0x0b +#define REG_HOUR_ALARM2 0x0c +#define REG_DAY_ALARM2 0x0d +#define REG_CONTROL 0x0e +#define REG_STATUS 0x0f + +/* Macros for indexing time_reg buffer */ +#define SECONDS 0 +#define MINUTES 1 +#define HOURS 2 +#define DAYS 3 +#define DATE 4 +#define MONTHS 5 +#define YEARS 6 + +enum bcd_mask { + SECONDS_MASK = 0x70, + MINUTES_MASK = 0x70, + HOURS24_MASK = 0x30, + DAYS_MASK = 0x00, + MONTHS_MASK = 0x10, + YEARS_MASK = 0xf0 +}; + +#endif /* __CROS_EC_RTC_IDT1337AG_H */ diff --git a/zephyr/dts/bindings/cros_rtc/renesas,rtc-idt1337ag.yaml b/zephyr/dts/bindings/cros_rtc/renesas,rtc-idt1337ag.yaml new file mode 100644 index 0000000000..a741a98aef --- /dev/null +++ b/zephyr/dts/bindings/cros_rtc/renesas,rtc-idt1337ag.yaml @@ -0,0 +1,17 @@ +# Copyright 2021 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: RENESAS, Real-Time Clock + +compatible: "renesas,rtc-idt1337ag" + +include: [base.yaml, i2c-device.yaml] + +properties: + label: + required: true + + int-gpios: + type: phandle-array + required: true + description: Interrupt from RTC |