[PATCH 1/5] drivers: rtc: add rv3032 driver
Alexandre Belloni
alexandre.belloni at bootlin.com
Wed Aug 30 21:43:51 CEST 2023
Hello,
On 30/08/2023 16:03:30+0200, Steffen Dirkwinkel wrote:
> From: Steffen Dirkwinkel <s.dirkwinkel at beckhoff.com>
>
> Based on linux driver, with these differences:
> - no support for trickle charger
> - no support for hwmon
> - no support for battery backed memory
> - dm_i2c instead of regmap
> - different tm_year and tm_mon
>
> read/write access the user eeprom. The read and write functions access
> the user eeprom so it can be used for nvmem-cells. (like in
> arch/sandbox/dts/test.dts). This is currently different from linux as
> you'd get nvram using nvmem-cells. I'm hoping to switch the order there
I'm not sure I get this as both nvram and eeprom are exposed through
nvmem. The solution is not to reorder but instead to fix the nvmem core
so you can expose both using nvmem-cells as there are users that access
the nvram to handle A/B updates (bootcount, rescue mode,...)
> too (there are currently no users) or to make a more specific binding.
> Currently this would also just work as is if used for mac addresses, as
> u-boot will put these into fdt before booting linux and linux will then
> prefer the u-boot provided mac.
>
> Signed-off-by: Steffen Dirkwinkel <s.dirkwinkel at beckhoff.com>
>
> ---
>
> drivers/rtc/Kconfig | 10 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rv3032.c | 334 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 345 insertions(+)
> create mode 100644 drivers/rtc/rv3032.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 23173139e0..a41ec9b6cc 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -172,6 +172,16 @@ config RTC_RV3029
> This driver supports reading and writing the RTC/calendar and the
> battery-baced SRAM section.
>
> +config RTC_RV3032
> + bool "Enable RV3032 driver"
> + depends on DM_RTC
> + help
> + The MicroCrystal RV3032 is a I2C Real Time Clock (RTC) with a 16-byte
> + battery-backed SRAM and a 32-byte user eeprom.
> +
> + This driver supports reading and writing the RTC/calendar and the
> + user eeprom.
> +
> config RTC_RV8803
> bool "Enable RV8803 driver"
> depends on DM_RTC
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 308fab8da9..9c2d8c7aa9 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_RTC_PL031) += pl031.o
> obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o
> obj-$(CONFIG_RTC_RV3028) += rv3028.o
> obj-$(CONFIG_RTC_RV3029) += rv3029.o
> +obj-$(CONFIG_RTC_RV3032) += rv3032.o
> obj-$(CONFIG_RTC_RV8803) += rv8803.o
> obj-$(CONFIG_RTC_RX8025) += rx8025.o
> obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o
> diff --git a/drivers/rtc/rv3032.c b/drivers/rtc/rv3032.c
> new file mode 100644
> index 0000000000..8d5d860c0a
> --- /dev/null
> +++ b/drivers/rtc/rv3032.c
> @@ -0,0 +1,334 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RTC driver for the Micro Crystal RV3032
> + *
> + * based on linux driver from
> + * Copyright (C) 2020 Micro Crystal SA
> + *
> + * Alexandre Belloni <alexandre.belloni at bootlin.com>
> + *
> + */
> +
> +#include <dm.h>
> +#include <i2c.h>
> +#include <linux/delay.h>
> +#include <regmap.h>
> +#include <rtc.h>
> +#include <time.h>
> +
> +#define RV3032_SEC 0x01
> +#define RV3032_MIN 0x02
> +#define RV3032_HOUR 0x03
> +#define RV3032_WDAY 0x04
> +#define RV3032_DAY 0x05
> +#define RV3032_MONTH 0x06
> +#define RV3032_YEAR 0x07
> +#define RV3032_ALARM_MIN 0x08
> +#define RV3032_ALARM_HOUR 0x09
> +#define RV3032_ALARM_DAY 0x0A
> +#define RV3032_STATUS 0x0D
> +#define RV3032_TLSB 0x0E
> +#define RV3032_TMSB 0x0F
> +#define RV3032_CTRL1 0x10
> +#define RV3032_CTRL2 0x11
> +#define RV3032_CTRL3 0x12
> +#define RV3032_TS_CTRL 0x13
> +#define RV3032_CLK_IRQ 0x14
> +#define RV3032_EEPROM_ADDR 0x3D
> +#define RV3032_EEPROM_DATA 0x3E
> +#define RV3032_EEPROM_CMD 0x3F
> +#define RV3032_RAM1 0x40
> +#define RV3032_PMU 0xC0
> +#define RV3032_OFFSET 0xC1
> +#define RV3032_CLKOUT1 0xC2
> +#define RV3032_CLKOUT2 0xC3
> +#define RV3032_TREF0 0xC4
> +#define RV3032_TREF1 0xC5
> +
> +#define RV3032_STATUS_VLF BIT(0)
> +#define RV3032_STATUS_PORF BIT(1)
> +#define RV3032_STATUS_EVF BIT(2)
> +#define RV3032_STATUS_AF BIT(3)
> +#define RV3032_STATUS_TF BIT(4)
> +#define RV3032_STATUS_UF BIT(5)
> +#define RV3032_STATUS_TLF BIT(6)
> +#define RV3032_STATUS_THF BIT(7)
> +
> +#define RV3032_TLSB_CLKF BIT(1)
> +#define RV3032_TLSB_EEBUSY BIT(2)
> +#define RV3032_TLSB_TEMP GENMASK(7, 4)
> +
> +#define RV3032_CLKOUT2_HFD_MSK GENMASK(4, 0)
> +#define RV3032_CLKOUT2_FD_MSK GENMASK(6, 5)
> +#define RV3032_CLKOUT2_OS BIT(7)
> +
> +#define RV3032_CTRL1_EERD BIT(3)
> +#define RV3032_CTRL1_WADA BIT(5)
> +
> +#define RV3032_CTRL2_STOP BIT(0)
> +#define RV3032_CTRL2_EIE BIT(2)
> +#define RV3032_CTRL2_AIE BIT(3)
> +#define RV3032_CTRL2_TIE BIT(4)
> +#define RV3032_CTRL2_UIE BIT(5)
> +#define RV3032_CTRL2_CLKIE BIT(6)
> +#define RV3032_CTRL2_TSE BIT(7)
> +
> +#define RV3032_PMU_TCM GENMASK(1, 0)
> +#define RV3032_PMU_TCR GENMASK(3, 2)
> +#define RV3032_PMU_BSM GENMASK(5, 4)
> +#define RV3032_PMU_NCLKE BIT(6)
> +
> +#define RV3032_PMU_BSM_DSM 1
> +#define RV3032_PMU_BSM_LSM 2
> +
> +#define RV3032_OFFSET_MSK GENMASK(5, 0)
> +
> +#define RV3032_EVT_CTRL_TSR BIT(2)
> +
> +#define RV3032_EEPROM_CMD_UPDATE 0x11
> +#define RV3032_EEPROM_CMD_WRITE 0x21
> +#define RV3032_EEPROM_CMD_READ 0x22
> +
> +#define RV3032_EEPROM_USER 0xCB
> +
> +#define RV3032_EEBUSY_POLL 10000
> +#define RV3032_EEBUSY_TIMEOUT 100000
> +
> +#define OFFSET_STEP_PPT 238419
> +
> +static int rv3032_update_bits(struct udevice *dev, u8 reg, u8 mask, u8 set)
> +{
> + u8 buf;
> + int ret;
> +
> + ret = dm_i2c_read(dev, reg, &buf, 1);
> + if (ret < 0)
> + return ret;
> +
> + if ((buf & mask) == (set && mask))
> + return 0;
> +
> + buf = (buf & ~mask) | (set & mask);
> + ret = dm_i2c_read(dev, reg, &buf, 1);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rv3032_exit_eerd(struct udevice *dev, u32 eerd)
> +{
> + if (eerd)
> + return 0;
> +
> + return rv3032_update_bits(dev, RV3032_CTRL1, RV3032_CTRL1_EERD, 0);
> +}
> +
> +static int rv3032_eeprom_busywait(struct udevice *dev)
> +{
> + int ret;
> + u8 status;
> + unsigned long start = get_timer(0);
> +
> + for (;;) {
> + ret = dm_i2c_read(dev, RV3032_TLSB, &status, 1);
> + if (ret < 0)
> + break;
> + if (!(status & RV3032_TLSB_EEBUSY))
> + break;
> + if (get_timer(start) > RV3032_EEBUSY_TIMEOUT)
> + return -ETIMEDOUT;
> + udelay(RV3032_EEBUSY_POLL);
> + }
> +
> + return ret;
> +}
> +
> +static int rv3032_enter_eerd(struct udevice *dev, u32 *eerd)
> +{
> + u8 ctrl1;
> + int ret;
> +
> + ret = dm_i2c_read(dev, RV3032_CTRL1, &ctrl1, sizeof(ctrl1));
> + if (ret)
> + return ret;
> +
> + *eerd = ctrl1 & RV3032_CTRL1_EERD;
> + if (*eerd)
> + return 0;
> +
> + ret = rv3032_update_bits(dev, RV3032_CTRL1,
> + RV3032_CTRL1_EERD, RV3032_CTRL1_EERD);
> + if (ret)
> + return ret;
> +
> + ret = rv3032_eeprom_busywait(dev);
> + if (ret) {
> + rv3032_exit_eerd(dev, *eerd);
> +
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rv3032_get_time(struct udevice *dev, struct rtc_time *tm)
> +{
> + u8 date[7];
> + int ret;
> + u8 status;
> +
> + ret = dm_i2c_read(dev, RV3032_STATUS, &status, 1);
> + if (ret < 0)
> + return ret;
> +
> + if (status & (RV3032_STATUS_PORF | RV3032_STATUS_VLF))
> + return -EINVAL;
> +
> + ret = dm_i2c_read(dev, RV3032_SEC, date, sizeof(date));
> + if (ret)
> + return ret;
> +
> + tm->tm_sec = bcd2bin(date[0] & 0x7f);
> + tm->tm_min = bcd2bin(date[1] & 0x7f);
> + tm->tm_hour = bcd2bin(date[2] & 0x3f);
> + tm->tm_wday = date[3] & 0x7;
> + tm->tm_mday = bcd2bin(date[4] & 0x3f);
> + tm->tm_mon = bcd2bin(date[5] & 0x1f);
> + tm->tm_year = bcd2bin(date[6]) + 2000;
> +
> + return 0;
> +}
> +
> +static int rv3032_set_time(struct udevice *dev, const struct rtc_time *tm)
> +{
> + u8 date[7];
> + int ret;
> +
> + date[0] = bin2bcd(tm->tm_sec);
> + date[1] = bin2bcd(tm->tm_min);
> + date[2] = bin2bcd(tm->tm_hour);
> + date[3] = tm->tm_wday;
> + date[4] = bin2bcd(tm->tm_mday);
> + date[5] = bin2bcd(tm->tm_mon);
> + date[6] = bin2bcd(tm->tm_year - 2000);
> +
> + ret = dm_i2c_write(dev, RV3032_SEC, date,
> + sizeof(date));
> + if (ret)
> + return ret;
> +
> + ret = rv3032_update_bits(dev, RV3032_STATUS,
> + RV3032_STATUS_PORF | RV3032_STATUS_VLF, 0);
> +
> + return ret;
> +}
> +
> +static int rv3032_eeprom_write(struct udevice *dev, unsigned int reg,
> + const u8 *buf, unsigned int len)
> +{
> + u32 eerd;
> + int i, ret;
> + u8 cmd;
> +
> + ret = rv3032_enter_eerd(dev, &eerd);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < len; i++) {
> + cmd = RV3032_EEPROM_USER + reg + i;
> + ret = dm_i2c_write(dev, RV3032_EEPROM_ADDR,
> + &cmd, 1);
> + if (ret)
> + goto exit_eerd;
> +
> + ret = dm_i2c_write(dev, RV3032_EEPROM_DATA, &buf[i], 1);
> + if (ret)
> + goto exit_eerd;
> +
> + cmd = RV3032_EEPROM_CMD_WRITE;
> + ret = dm_i2c_write(dev, RV3032_EEPROM_CMD,
> + &cmd, 1);
> + if (ret)
> + goto exit_eerd;
> +
> + udelay(RV3032_EEBUSY_POLL);
> +
> + ret = rv3032_eeprom_busywait(dev);
> + if (ret)
> + goto exit_eerd;
> + }
> +
> +exit_eerd:
> + rv3032_exit_eerd(dev, eerd);
> +
> + return ret;
> +}
> +
> +static int rv3032_eeprom_read(struct udevice *dev, unsigned int reg,
> + u8 *buf, unsigned int len)
> +{
> + u32 eerd;
> + int i, ret;
> + u8 cmd, data;
> +
> + ret = rv3032_enter_eerd(dev, &eerd);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < len; i++) {
> + cmd = RV3032_EEPROM_USER + reg + i;
> + ret = dm_i2c_write(dev, RV3032_EEPROM_ADDR,
> + &cmd, 1);
> + if (ret)
> + goto exit_eerd;
> +
> + cmd = RV3032_EEPROM_CMD_READ;
> + ret = dm_i2c_write(dev, RV3032_EEPROM_CMD,
> + &cmd, 1);
> + if (ret)
> + goto exit_eerd;
> +
> + ret = rv3032_eeprom_busywait(dev);
> + if (ret)
> + goto exit_eerd;
> +
> + ret = dm_i2c_read(dev, RV3032_EEPROM_DATA, &data, sizeof(data));
> + if (ret)
> + goto exit_eerd;
> + buf[i] = data;
> + }
> +
> +exit_eerd:
> + rv3032_exit_eerd(dev, eerd);
> +
> + return ret;
> +}
> +
> +static int rv3032_probe(struct udevice *dev)
> +{
> + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS |
> + DM_I2C_CHIP_WR_ADDRESS);
> +
> + return 0;
> +}
> +
> +static const struct rtc_ops rv3032_rtc_ops = {
> + .get = rv3032_get_time,
> + .set = rv3032_set_time,
> + .read = rv3032_eeprom_read,
> + .write = rv3032_eeprom_write,
> +};
> +
> +static const struct udevice_id rv3032_rtc_ids[] = {
> + { .compatible = "microcrystal,rv3032", },
> + { }
> +};
> +
> +U_BOOT_DRIVER(rtc_rv3032) = {
> + .name = "rtc-rv3028",
> + .id = UCLASS_RTC,
> + .probe = rv3032_probe,
> + .of_match = rv3032_rtc_ids,
> + .ops = &rv3032_rtc_ops,
> +};
> --
> 2.42.0
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
More information about the U-Boot
mailing list