[PATCH] rtc: add support for rv3028 rtc

Heiko Schocher hs at denx.de
Wed Mar 10 05:20:18 CET 2021


Hi stefan,

On 09.03.21 17:10, Stefan Roese wrote:
> Hi Heiko,
> 
> On 09.03.21 14:45, Heiko Schocher wrote:
>> add support for rtc3028 rtc from microcrystal.
>> based on linux dirver:
>> commit a38fd8748464: ("Linux 5.12-rc2")
> 
> Nitpicking: You might want to start a sentence in upper-case? ;)

Yup.

> 
> Another minor comment below...
> 
>> Signed-off-by: Heiko Schocher <hs at denx.de>
>>
>> ---
>> driver is based on code in linux, but with already
>> corrected weekday usage. linux codes the weekday
>> bitwise, while the weekday register has only 3 valid
>> bits as the app manual [1] says:
>>
>> This register holds the current day of the week. Each value represents
>> one weekday that is assigned by the user. Values will range from 0 to 6
>> The weekday counter is simply a 3-bit counter which counts up to 6
>> and then resets to 0.
>>
>> [1] https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-3028-C7_App-Manual.pdf
>>
>> This is not a big problem, as userspace never use weekday.
>> Nevertheless, I will also send an update for the linux driver.
>>
>> Also the nvram can be used for bootcounter purposes.
>>
>> Tested this driver on "PHYTEC phyBOARD-Pollux i.MX8MP" board.
>>
>> azure build:
>> https://dev.azure.com/hs0298/hs/_build/results?buildId=63&view=results
>>
>>   drivers/rtc/Kconfig  |   6 ++
>>   drivers/rtc/Makefile |   1 +
>>   drivers/rtc/rv3028.c | 215 +++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 222 insertions(+)
>>   create mode 100644 drivers/rtc/rv3028.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index aa6d90158c..e451308b40 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -95,6 +95,12 @@ config RTC_PCF8563
>>         If you say yes here you get support for the Philips PCF8563 RTC
>>         and compatible chips.
>>   +config RTC_RV3028
>> +    bool "Enable RV3028 driver"
>> +    depends on DM_RTC
>> +    help
>> +      The MicroCrystal RV3028 is a I2C Real Time Clock (RTC)
>> +
>>   config RTC_RV3029
>>       bool "Enable RV3029 driver"
>>       depends on DM_RTC
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 6a45a9c874..e7acd76266 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -46,6 +46,7 @@ obj-$(CONFIG_RTC_PCF2127) += pcf2127.o
>>   obj-$(CONFIG_RTC_PL031) += pl031.o
>>   obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o
>>   obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o
>> +obj-$(CONFIG_RTC_RV3028) += rv3028.o
>>   obj-$(CONFIG_RTC_RV3029) += rv3029.o
>>   obj-$(CONFIG_RTC_RV8803) += rv8803.o
>>   obj-$(CONFIG_RTC_RX8025) += rx8025.o
>> diff --git a/drivers/rtc/rv3028.c b/drivers/rtc/rv3028.c
>> new file mode 100644
>> index 0000000000..8d8336c5f1
>> --- /dev/null
>> +++ b/drivers/rtc/rv3028.c
>> @@ -0,0 +1,215 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * RTC driver for the Micro Crystal RV3028
>> + *
>> + * based on linux driver from
>> + * Copyright (C) 2019 Micro Crystal SA
>> + *
>> + * Alexandre Belloni <alexandre.belloni at bootlin.com>
>> + *
>> + */
>> +
>> +#include <common.h>
> 
> Please don't include "common.h" any more. It's deprecated.
> 
>> +#include <command.h>
> 
> Do you really need "command.h" ...
> 
>> +#include <dm.h>
>> +#include <eeprom.h>
> 
> ... and this one? Please check and only inlude the necessary headers.

Of course, will check them, thanks!

> Other that that:
> 
> Reviewed-by: Stefan Roese <sr at denx.de>

bye,
Heiko
> 
> Thanks,
> Stefan
> 
>> +#include <i2c.h>
>> +#include <log.h>
>> +#include <rtc.h>
>> +#include <dm/device_compat.h>
>> +#include <linux/bitops.h>
>> +#include <linux/delay.h>
>> +
>> +#define RV3028_SEC            0x00
>> +#define RV3028_MIN            0x01
>> +#define RV3028_HOUR            0x02
>> +#define RV3028_WDAY            0x03
>> +#define RV3028_DAY            0x04
>> +#define RV3028_MONTH            0x05
>> +#define RV3028_YEAR            0x06
>> +#define RV3028_ALARM_MIN        0x07
>> +#define RV3028_ALARM_HOUR        0x08
>> +#define RV3028_ALARM_DAY        0x09
>> +#define RV3028_STATUS            0x0E
>> +#define RV3028_CTRL1            0x0F
>> +#define RV3028_CTRL2            0x10
>> +#define RV3028_EVT_CTRL            0x13
>> +#define RV3028_TS_COUNT            0x14
>> +#define RV3028_TS_SEC            0x15
>> +#define RV3028_RAM1            0x1F
>> +#define RV3028_EEPROM_ADDR        0x25
>> +#define RV3028_EEPROM_DATA        0x26
>> +#define RV3028_EEPROM_CMD        0x27
>> +#define RV3028_CLKOUT            0x35
>> +#define RV3028_OFFSET            0x36
>> +#define RV3028_BACKUP            0x37
>> +
>> +#define RV3028_STATUS_PORF        BIT(0)
>> +#define RV3028_STATUS_EVF        BIT(1)
>> +#define RV3028_STATUS_AF        BIT(2)
>> +#define RV3028_STATUS_TF        BIT(3)
>> +#define RV3028_STATUS_UF        BIT(4)
>> +#define RV3028_STATUS_BSF        BIT(5)
>> +#define RV3028_STATUS_CLKF        BIT(6)
>> +#define RV3028_STATUS_EEBUSY        BIT(7)
>> +
>> +#define RV3028_CLKOUT_FD_MASK        GENMASK(2, 0)
>> +#define RV3028_CLKOUT_PORIE        BIT(3)
>> +#define RV3028_CLKOUT_CLKSY        BIT(6)
>> +#define RV3028_CLKOUT_CLKOE        BIT(7)
>> +
>> +#define RV3028_CTRL1_EERD        BIT(3)
>> +#define RV3028_CTRL1_WADA        BIT(5)
>> +
>> +#define RV3028_CTRL2_RESET        BIT(0)
>> +#define RV3028_CTRL2_12_24        BIT(1)
>> +#define RV3028_CTRL2_EIE        BIT(2)
>> +#define RV3028_CTRL2_AIE        BIT(3)
>> +#define RV3028_CTRL2_TIE        BIT(4)
>> +#define RV3028_CTRL2_UIE        BIT(5)
>> +#define RV3028_CTRL2_TSE        BIT(7)
>> +
>> +#define RV3028_EVT_CTRL_TSR        BIT(2)
>> +
>> +#define RV3028_EEPROM_CMD_UPDATE    0x11
>> +#define RV3028_EEPROM_CMD_WRITE        0x21
>> +#define RV3028_EEPROM_CMD_READ        0x22
>> +
>> +#define RV3028_EEBUSY_POLL        10000
>> +#define RV3028_EEBUSY_TIMEOUT        100000
>> +
>> +#define RV3028_BACKUP_TCE        BIT(5)
>> +#define RV3028_BACKUP_TCR_MASK        GENMASK(1, 0)
>> +
>> +#define OFFSET_STEP_PPT            953674
>> +
>> +#define RTC_RV3028_LEN            7
>> +
>> +static int rv3028_rtc_get(struct udevice *dev, struct rtc_time *tm)
>> +{
>> +    u8 regs[RTC_RV3028_LEN];
>> +    u8 status;
>> +    int ret;
>> +
>> +    ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1);
>> +    if (ret < 0) {
>> +        printf("%s: error reading RTC status: %x\n", __func__, ret);
>> +        return -EIO;
>> +    }
>> +
>> +    if (status & RV3028_STATUS_PORF) {
>> +        printf("Voltage low, data is invalid.\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    ret = dm_i2c_read(dev, RV3028_SEC, regs, sizeof(regs));
>> +    if (ret < 0) {
>> +        printf("%s: error reading RTC: %x\n", __func__, ret);
>> +        return -EIO;
>> +    }
>> +
>> +    tm->tm_sec = bcd2bin(regs[RV3028_SEC] & 0x7f);
>> +    tm->tm_min = bcd2bin(regs[RV3028_MIN] & 0x7f);
>> +    tm->tm_hour = bcd2bin(regs[RV3028_HOUR] & 0x3f);
>> +    tm->tm_wday = regs[RV3028_WDAY] & 0x7;
>> +    tm->tm_mday = bcd2bin(regs[RV3028_DAY] & 0x3f);
>> +    tm->tm_mon  = bcd2bin(regs[RV3028_MONTH] & 0x1f);
>> +    tm->tm_year = bcd2bin(regs[RV3028_YEAR]) + 2000;
>> +    tm->tm_yday = 0;
>> +    tm->tm_isdst = 0;
>> +
>> +    debug("%s: %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n",
>> +          __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
>> +          tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
>> +
>> +    return 0;
>> +}
>> +
>> +static int rv3028_rtc_set(struct udevice *dev, const struct rtc_time *tm)
>> +{
>> +    u8 regs[RTC_RV3028_LEN];
>> +    u8 status;
>> +    int ret;
>> +
>> +    debug("%s: %4d-%02d-%02d (wday=%d( %2d:%02d:%02d\n",
>> +          __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
>> +          tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
>> +
>> +    if (tm->tm_year < 2000) {
>> +        printf("%s: year %d (before 2000) not supported\n",
>> +               __func__, tm->tm_year);
>> +        return -EINVAL;
>> +    }
>> +
>> +    regs[RV3028_SEC] = bin2bcd(tm->tm_sec);
>> +    regs[RV3028_MIN] = bin2bcd(tm->tm_min);
>> +    regs[RV3028_HOUR] = bin2bcd(tm->tm_hour);
>> +    regs[RV3028_WDAY]  = tm->tm_wday;
>> +    regs[RV3028_DAY]   = bin2bcd(tm->tm_mday);
>> +    regs[RV3028_MONTH] = bin2bcd(tm->tm_mon);
>> +    regs[RV3028_YEAR]  = bin2bcd(tm->tm_year - 2000);
>> +
>> +    ret = dm_i2c_write(dev, RV3028_SEC, regs, sizeof(regs));
>> +    if (ret) {
>> +        printf("%s: set rtc error: %d\n", __func__, ret);
>> +        return ret;
>> +    }
>> +
>> +    ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1);
>> +    if (ret < 0) {
>> +        printf("%s: error reading RTC status: %x\n", __func__, ret);
>> +        return -EIO;
>> +    }
>> +    status |= RV3028_STATUS_PORF;
>> +    return dm_i2c_write(dev, RV3028_STATUS, &status, 1);
>> +}
>> +
>> +static int rv3028_rtc_reset(struct udevice *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +static int rv3028_rtc_read8(struct udevice *dev, unsigned int reg)
>> +{
>> +    u8 data;
>> +    int ret;
>> +
>> +    ret = dm_i2c_read(dev, reg, &data, sizeof(data));
>> +    return ret < 0 ? ret : data;
>> +}
>> +
>> +static int rv3028_rtc_write8(struct udevice *dev, unsigned int reg, int val)
>> +{
>> +    u8 data = val;
>> +
>> +    return dm_i2c_write(dev, reg, &data, 1);
>> +}
>> +
>> +static int rv3028_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 rv3028_rtc_ops = {
>> +    .get = rv3028_rtc_get,
>> +    .set = rv3028_rtc_set,
>> +    .read8 = rv3028_rtc_read8,
>> +    .write8 = rv3028_rtc_write8,
>> +    .reset = rv3028_rtc_reset,
>> +};
>> +
>> +static const struct udevice_id rv3028_rtc_ids[] = {
>> +    { .compatible = "microcrystal,rv3028" },
>> +    { }
>> +};
>> +
>> +U_BOOT_DRIVER(rtc_rv3028) = {
>> +    .name    = "rtc-rv3028",
>> +    .id    = UCLASS_RTC,
>> +    .probe    = rv3028_probe,
>> +    .of_match = rv3028_rtc_ids,
>> +    .ops    = &rv3028_rtc_ops,
>> +};
>>
> 
> 
> Viele Grüße,
> Stefan
> 

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-52   Fax: +49-8142-66989-80   Email: hs at denx.de


More information about the U-Boot mailing list