From patchwork Tue Nov 27 21:53:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Tomsich X-Patchwork-Id: 1004058 X-Patchwork-Delegate: philipp.tomsich@theobroma-systems.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=theobroma-systems.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 434HcP10mxz9s3Z for ; Wed, 28 Nov 2018 08:54:13 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id AC59DC22271; Tue, 27 Nov 2018 21:54:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id E1930C2215B; Tue, 27 Nov 2018 21:54:07 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id D70AEC2215B; Tue, 27 Nov 2018 21:54:06 +0000 (UTC) Received: from mail.theobroma-systems.com (vegas.theobroma-systems.com [144.76.126.164]) by lists.denx.de (Postfix) with ESMTPS id BC7F7C220DB for ; Tue, 27 Nov 2018 21:54:05 +0000 (UTC) Received: from [86.59.122.178] (port=57649 helo=android.lan) by mail.theobroma-systems.com with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA256:128) (Exim 4.80) (envelope-from ) id 1gRlIx-0000AZ-Pa; Tue, 27 Nov 2018 22:54:03 +0100 From: Philipp Tomsich To: u-boot@lists.denx.de Date: Tue, 27 Nov 2018 22:53:57 +0100 Message-Id: <1543355638-35440-1-git-send-email-philipp.tomsich@theobroma-systems.com> X-Mailer: git-send-email 2.1.4 Cc: Heinrich Schuchardt , Nandor Han , Sebastian Reichel , Chris Packham Subject: [U-Boot] [PATCH 1/2] rtc: rv3029: add to Kconfig X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The MicroCrystal RV3029 driver didn't have a Kconfig entry and was not used anywhere. Add it to Kconfig to make it selectable. Signed-off-by: Philipp Tomsich Tested-by: Klaus Goger --- drivers/rtc/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index bcc01b1..6038b43 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -60,6 +60,16 @@ config RTC_ISL1208 This driver supports reading and writing the RTC/calendar and detects total power failures. +config RTC_RV3029 + bool "Enable RV3029 driver" + depends on DM_RTC + help + The MicroCrystal RV3029 is a I2C Real Time Clock (RTC) with 8-byte + battery-backed SRAM. + + This driver supports reading and writing the RTC/calendar and the + battery-baced SRAM section. + config RTC_RX8010SJ bool "Enable RX8010SJ driver" depends on DM_RTC From patchwork Tue Nov 27 21:53:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Tomsich X-Patchwork-Id: 1004059 X-Patchwork-Delegate: philipp.tomsich@theobroma-systems.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=theobroma-systems.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 434HdM6XBdz9s3C for ; Wed, 28 Nov 2018 08:55:03 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id A643CC22279; Tue, 27 Nov 2018 21:54:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id A389DC2220F; Tue, 27 Nov 2018 21:54:26 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 58E2AC2224B; Tue, 27 Nov 2018 21:54:12 +0000 (UTC) Received: from mail.theobroma-systems.com (vegas.theobroma-systems.com [144.76.126.164]) by lists.denx.de (Postfix) with ESMTPS id D8527C2225D for ; Tue, 27 Nov 2018 21:54:08 +0000 (UTC) Received: from [86.59.122.178] (port=57649 helo=android.lan) by mail.theobroma-systems.com with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA256:128) (Exim 4.80) (envelope-from ) id 1gRlJ0-0000AZ-FX; Tue, 27 Nov 2018 22:54:06 +0100 From: Philipp Tomsich To: u-boot@lists.denx.de Date: Tue, 27 Nov 2018 22:53:58 +0100 Message-Id: <1543355638-35440-2-git-send-email-philipp.tomsich@theobroma-systems.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1543355638-35440-1-git-send-email-philipp.tomsich@theobroma-systems.com> References: <1543355638-35440-1-git-send-email-philipp.tomsich@theobroma-systems.com> Cc: Stephen Warren , Marek Vasut Subject: [U-Boot] [PATCH 2/2] rtc: rv3029: update to support DM and sync with Linux 4.17 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The "Flamingo" carrier-board for the RK3399-Q7 has a RV3029 populated and the application will use the off-module RV3029 RTC including the battery backed SRAM. To support this use case, this commit includes the following changes: * updates the rv3029 driver to use DM * implements the read8/write8 operations This syncs the implementation with the Linux code (based on 4.17), porting the trickle-charger support from there (with improvements to avoid unnecessary EEPROM updates) and adheres to the Linux DTS binding. Signed-off-by: Philipp Tomsich Tested-by: Klaus Goger --- drivers/rtc/rv3029.c | 580 +++++++++++++++++++++++++++++++++---------- scripts/config_whitelist.txt | 1 - 2 files changed, 443 insertions(+), 138 deletions(-) diff --git a/drivers/rtc/rv3029.c b/drivers/rtc/rv3029.c index dd4d060..38acb9c 100644 --- a/drivers/rtc/rv3029.c +++ b/drivers/rtc/rv3029.c @@ -1,189 +1,495 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * (C) Copyright 2010 - * Heiko Schocher, DENX Software Engineering, hs@denx.de + * (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH + * + * Based on a the Linux rtc-rv3029c2.c driver written by: + * Gregory Hermant + * Michael Buesch */ + #include #include +#include #include #include -#define RTC_RV3029_CTRL1 0x00 -#define RTC_RV3029_CTRL1_EERE (1 << 3) - -#define RTC_RV3029_CTRL_STATUS 0x03 -#define RTC_RV3029_CTRLS_EEBUSY (1 << 7) +#define RTC_RV3029_PAGE_LEN 7 -#define RTC_RV3029_CTRL_RESET 0x04 -#define RTC_RV3029_CTRL_SYS_R (1 << 4) +/* control section */ +#define RV3029_ONOFF_CTRL 0x00 +#define RV3029_ONOFF_CTRL_WE BIT(0) +#define RV3029_ONOFF_CTRL_TE BIT(1) +#define RV3029_ONOFF_CTRL_TAR BIT(2) +#define RV3029_ONOFF_CTRL_EERE BIT(3) +#define RV3029_ONOFF_CTRL_SRON BIT(4) +#define RV3029_ONOFF_CTRL_TD0 BIT(5) +#define RV3029_ONOFF_CTRL_TD1 BIT(6) +#define RV3029_ONOFF_CTRL_CLKINT BIT(7) +#define RV3029_IRQ_CTRL 0x01 +#define RV3029_IRQ_CTRL_AIE BIT(0) +#define RV3029_IRQ_CTRL_TIE BIT(1) +#define RV3029_IRQ_CTRL_V1IE BIT(2) +#define RV3029_IRQ_CTRL_V2IE BIT(3) +#define RV3029_IRQ_CTRL_SRIE BIT(4) +#define RV3029_IRQ_FLAGS 0x02 +#define RV3029_IRQ_FLAGS_AF BIT(0) +#define RV3029_IRQ_FLAGS_TF BIT(1) +#define RV3029_IRQ_FLAGS_V1IF BIT(2) +#define RV3029_IRQ_FLAGS_V2IF BIT(3) +#define RV3029_IRQ_FLAGS_SRF BIT(4) +#define RV3029_STATUS 0x03 +#define RV3029_STATUS_VLOW1 BIT(2) +#define RV3029_STATUS_VLOW2 BIT(3) +#define RV3029_STATUS_SR BIT(4) +#define RV3029_STATUS_PON BIT(5) +#define RV3029_STATUS_EEBUSY BIT(7) +#define RV3029_RST_CTRL 0x04 +#define RV3029_RST_CTRL_SYSR BIT(4) +#define RV3029_CONTROL_SECTION_LEN 0x05 -#define RTC_RV3029_CLOCK_PAGE 0x08 -#define RTC_RV3029_PAGE_LEN 7 +/* watch section */ +#define RV3029_W_SEC 0x08 +#define RV3029_W_MINUTES 0x09 +#define RV3029_W_HOURS 0x0A +#define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */ +#define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */ +#define RV3029_W_DATE 0x0B +#define RV3029_W_DAYS 0x0C +#define RV3029_W_MONTHS 0x0D +#define RV3029_W_YEARS 0x0E -#define RV3029C2_W_SECONDS 0x00 -#define RV3029C2_W_MINUTES 0x01 -#define RV3029C2_W_HOURS 0x02 -#define RV3029C2_W_DATE 0x03 -#define RV3029C2_W_DAYS 0x04 -#define RV3029C2_W_MONTHS 0x05 -#define RV3029C2_W_YEARS 0x06 +/* eeprom control section */ +#define RV3029_CONTROL_E2P_EECTRL 0x30 +#define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */ +#define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */ +#define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */ +#define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */ +#define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\ + RV3029_TRICKLE_5K |\ + RV3029_TRICKLE_20K |\ + RV3029_TRICKLE_80K) +#define RV3029_TRICKLE_SHIFT 4 -#define RV3029C2_REG_HR_12_24 (1 << 6) /* 24h/12h mode */ -#define RV3029C2_REG_HR_PM (1 << 5) /* PM/AM bit in 12h mode */ -#define RTC_RV3029_EEPROM_CTRL 0x30 -#define RTC_RV3029_TRICKLE_1K (1 << 4) -#define RTC_RV3029_TRICKLE_5K (1 << 5) -#define RTC_RV3029_TRICKLE_20K (1 << 6) -#define RTC_RV3029_TRICKLE_80K (1 << 7) - -int rtc_get( struct rtc_time *tmp ) +static int rv3029_rtc_get(struct udevice *dev, struct rtc_time *tm) { - int ret; - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 regs[RTC_RV3029_PAGE_LEN]; + int ret; - ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, buf, \ - RTC_RV3029_PAGE_LEN); - if (ret) { + ret = dm_i2c_read(dev, RV3029_W_SEC, regs, sizeof(regs)); + if (ret < 0) { printf("%s: error reading RTC: %x\n", __func__, ret); - return -1; + return -EIO; + } + + tm->tm_sec = bcd2bin(regs[RV3029_W_SEC - RV3029_W_SEC]); + tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES - RV3029_W_SEC]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[RV3029_W_HOURS - RV3029_W_SEC]; + + if (_hr & RV3029_REG_HR_12_24) { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & RV3029_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } else { + /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + } } - tmp->tm_sec = bcd2bin( buf[RV3029C2_W_SECONDS] & 0x7f); - tmp->tm_min = bcd2bin( buf[RV3029C2_W_MINUTES] & 0x7f); - if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_12_24) { - /* 12h format */ - tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x1f); - if (buf[RV3029C2_W_HOURS] & RV3029C2_REG_HR_PM) - /* PM flag set */ - tmp->tm_hour += 12; - } else - tmp->tm_hour = bcd2bin(buf[RV3029C2_W_HOURS] & 0x3f); - - tmp->tm_mday = bcd2bin( buf[RV3029C2_W_DATE] & 0x3F ); - tmp->tm_mon = bcd2bin( buf[RV3029C2_W_MONTHS] & 0x1F ); - tmp->tm_wday = bcd2bin( buf[RV3029C2_W_DAYS] & 0x07 ); + + tm->tm_mday = bcd2bin(regs[RV3029_W_DATE - RV3029_W_SEC]); + tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS - RV3029_W_SEC]) - 1; /* RTC supports only years > 1999 */ - tmp->tm_year = bcd2bin( buf[RV3029C2_W_YEARS]) + 2000; - tmp->tm_yday = 0; - tmp->tm_isdst = 0; + tm->tm_year = bcd2bin(regs[RV3029_W_YEARS - RV3029_W_SEC]) + 2000; + tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS - RV3029_W_SEC]) - 1; + + tm->tm_yday = 0; + tm->tm_isdst = 0; - debug( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); + 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; } -int rtc_set( struct rtc_time *tmp ) +static int rv3029_rtc_set(struct udevice *dev, const struct rtc_time *tm) { - int ret; - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 regs[RTC_RV3029_PAGE_LEN]; + + 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); - debug( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", - tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - if (tmp->tm_year < 2000) { - printf("RTC: year %d < 2000 not possible\n", tmp->tm_year); - return -1; + if (tm->tm_year < 2000) { + printf("%s: year %d (before 2000) not supported\n", + __func__, tm->tm_year); + return -EINVAL; } - buf[RV3029C2_W_SECONDS] = bin2bcd(tmp->tm_sec); - buf[RV3029C2_W_MINUTES] = bin2bcd(tmp->tm_min); - buf[RV3029C2_W_HOURS] = bin2bcd(tmp->tm_hour); - /* set 24h format */ - buf[RV3029C2_W_HOURS] &= ~RV3029C2_REG_HR_12_24; - buf[RV3029C2_W_DATE] = bin2bcd(tmp->tm_mday); - buf[RV3029C2_W_DAYS] = bin2bcd(tmp->tm_wday); - buf[RV3029C2_W_MONTHS] = bin2bcd(tmp->tm_mon); - tmp->tm_year -= 2000; - buf[RV3029C2_W_YEARS] = bin2bcd(tmp->tm_year); - ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CLOCK_PAGE, 1, - buf, RTC_RV3029_PAGE_LEN); - - /* give the RTC some time to update */ - udelay(1000); - return ret; + + regs[RV3029_W_SEC - RV3029_W_SEC] = bin2bcd(tm->tm_sec); + regs[RV3029_W_MINUTES - RV3029_W_SEC] = bin2bcd(tm->tm_min); + regs[RV3029_W_HOURS - RV3029_W_SEC] = bin2bcd(tm->tm_hour); + regs[RV3029_W_DATE - RV3029_W_SEC] = bin2bcd(tm->tm_mday); + regs[RV3029_W_MONTHS - RV3029_W_SEC] = bin2bcd(tm->tm_mon + 1); + regs[RV3029_W_DAYS - RV3029_W_SEC] = bin2bcd(tm->tm_wday + 1) & 0x7; + regs[RV3029_W_YEARS - RV3029_W_SEC] = bin2bcd(tm->tm_year - 2000); + + return dm_i2c_write(dev, RV3029_W_SEC, regs, sizeof(regs)); } -/* sets EERE-Bit (automatic EEPROM refresh) */ -static void set_eere_bit(int state) +static int rv3029_rtc_reset(struct udevice *dev) { - unsigned char reg_ctrl1; + u8 ctrl = RV3029_RST_CTRL_SYSR; + unsigned long start; + const unsigned long timeout_ms = 10000; + int ret; - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, - ®_ctrl1, 1); + /* trigger the system-reset */ + ret = dm_i2c_write(dev, RV3029_RST_CTRL, &ctrl, 1); + if (ret < 0) + return -EIO; - if (state) - reg_ctrl1 |= RTC_RV3029_CTRL1_EERE; - else - reg_ctrl1 &= (~RTC_RV3029_CTRL1_EERE); + /* wait for the system-reset to complete */ + start = get_timer(0); + do { + if (get_timer(start) > timeout_ms) + return -ETIMEDOUT; - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, - ®_ctrl1, 1); + ret = dm_i2c_read(dev, RV3029_RST_CTRL, &ctrl, 1); + if (ret < 0) + return -EIO; + } while (ctrl & RV3029_RST_CTRL_SYSR); + + return 0; } -/* waits until EEPROM page is no longer busy (times out after 10ms*loops) */ -static int wait_eebusy(int loops) +static int rv3029_rtc_read8(struct udevice *dev, unsigned int reg) { - int i; - unsigned char ctrl_status; + u8 data; + int ret; + + ret = dm_i2c_read(dev, reg, &data, sizeof(data)); + return ret < 0 ? ret : data; +} - for (i = 0; i < loops; i++) { - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS, - 1, &ctrl_status, 1); +static int rv3029_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 data = val; + + return dm_i2c_write(dev, reg, &data, 1); +} + +#if defined(OF_CONTROL) +static int rv3029_get_sr(struct udevice *dev, u8 *buf) +{ + int ret = dm_i2c_read(dev, RV3029_STATUS, buf, 1); + + if (ret < 0) + return -EIO; + + dev_dbg(dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + return 0; +} - if ((ctrl_status & RTC_RV3029_CTRLS_EEBUSY) == 0) +static int rv3029_set_sr(struct udevice *dev, u8 val) +{ + int ret; + + ret = dm_i2c_read(dev, RV3029_STATUS, &val, 1); + if (ret < 0) + return -EIO; + + dev_dbg(dev, "status = 0x%.2x (%d)\n", val, val); + return 0; +} + +static int rv3029_eeprom_busywait(struct udevice *dev) +{ + int i, ret; + u8 sr; + + for (i = 100; i > 0; i--) { + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + break; + if (!(sr & RV3029_STATUS_EEBUSY)) break; udelay(10000); } - return i; + if (i <= 0) { + dev_err(dev, "EEPROM busy wait timeout.\n"); + return -ETIMEDOUT; + } + + return ret; } -void rtc_reset (void) +static int rv3029_update_bits(struct udevice *dev, u8 reg, u8 mask, u8 set) { - unsigned char buf[RTC_RV3029_PAGE_LEN]; + u8 buf; + int ret; - buf[0] = RTC_RV3029_CTRL_SYS_R; - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1, - buf, 1); + ret = dm_i2c_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; -#if defined(CONFIG_SYS_RV3029_TCR) - /* - * because EEPROM_CTRL register is in EEPROM page it is necessary to - * disable automatic EEPROM refresh and check if EEPROM is busy - * before EEPORM_CTRL register may be accessed - */ - set_eere_bit(0); - wait_eebusy(100); - /* read current trickle charger setting */ - (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, - 1, buf, 1); - /* enable automatic EEPROM refresh again */ - set_eere_bit(1); + if ((buf & mask) == (set && mask)) + return 0; - /* - * to minimize EEPROM access write trickle charger setting only if it - * differs from current value - */ - if ((buf[0] & 0xF0) != CONFIG_SYS_RV3029_TCR) { - buf[0] = (buf[0] & 0x0F) | CONFIG_SYS_RV3029_TCR; - /* - * write trickle charger setting (disable autom. EEPROM - * refresh and wait until EEPROM is idle) - */ - set_eere_bit(0); - wait_eebusy(100); - (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, - RTC_RV3029_EEPROM_CTRL, 1, buf, 1); - /* - * it is necessary to wait 10ms before EEBUSY-Bit may be read - * (this is not documented in the data sheet yet, but the - * manufacturer recommends it) + buf = (buf & ~mask) | (set & mask); + ret = dm_i2c_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int rv3029_eeprom_exit(struct udevice *dev) +{ + /* Re-enable eeprom refresh */ + return rv3029_update_bits(dev, RV3029_ONOFF_CTRL, + RV3029_ONOFF_CTRL_EERE, + RV3029_ONOFF_CTRL_EERE); +} + +static int rv3029_eeprom_enter(struct udevice *dev) +{ + int ret; + u8 sr; + + /* Check whether we are in the allowed voltage range. */ + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + /* We clear the bits and retry once just in case + * we had a brown out in early startup. */ + sr &= ~RV3029_STATUS_VLOW1; + sr &= ~RV3029_STATUS_VLOW2; + ret = rv3029_set_sr(dev, sr); + if (ret < 0) + return ret; udelay(10000); - /* wait until EEPROM write access is finished */ - wait_eebusy(100); - set_eere_bit(1); + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + dev_err(dev, "Supply voltage is too low to safely access the EEPROM.\n"); + return -ENODEV; + } + } + + /* Disable eeprom refresh. */ + ret = rv3029_update_bits(dev, + RV3029_ONOFF_CTRL, RV3029_ONOFF_CTRL_EERE, 0); + if (ret < 0) + return ret; + + /* Wait for any previous eeprom accesses to finish. */ + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + rv3029_eeprom_exit(dev); + + return ret; +} + +static int rv3029_eeprom_read(struct udevice *dev, u8 reg, + u8 buf[], size_t len) +{ + int ret, err; + + err = rv3029_eeprom_enter(dev); + if (err < 0) + return err; + + ret = dm_i2c_read(dev, reg, buf, len); + + err = rv3029_eeprom_exit(dev); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_write(struct udevice *dev, u8 reg, + u8 const buf[], size_t len) +{ + int ret; + size_t i; + u8 tmp; + + ret = rv3029_eeprom_enter(dev); + if (ret < 0) + return ret; + + for (i = 0; i < len; i++, reg++) { + ret = dm_i2c_read(dev, reg, &tmp, 1); + if (ret < 0) + break; + if (tmp != buf[i]) { + ret = dm_i2c_write(dev, reg, &buf[i], 1); + if (ret < 0) + break; + } + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + break; } + + ret = rv3029_eeprom_exit(dev); + if (ret < 0) + return ret; + + return 0; +} + +static int rv3029_eeprom_update_bits(struct udevice *dev, + u8 reg, u8 mask, u8 set) +{ + u8 buf; + int ret; + + ret = rv3029_eeprom_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + /* + * If the EEPROM already reads the correct bitpattern, we don't need + * to update it. + */ + if ((buf & mask) == (set & mask)) + return 0; + + buf = (buf & ~mask) | (set & mask); + ret = rv3029_eeprom_write(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void rv3029_trickle_config(struct udevice *dev) +{ + static const struct rv3029_trickle_tab_elem { + u32 r; /* resistance in ohms */ + u8 conf; /* trickle config bits */ + } rv3029_trickle_tab[] = { + { + .r = 1076, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 1091, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K, + }, { + .r = 1137, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_80K, + }, { + .r = 1154, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K, + }, { + .r = 1371, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 1395, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K, + }, { + .r = 1472, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K, + }, { + .r = 1500, + .conf = RV3029_TRICKLE_1K, + }, { + .r = 3810, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 4000, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K, + }, { + .r = 4706, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K, + }, { + .r = 5000, + .conf = RV3029_TRICKLE_5K, + }, { + .r = 16000, + .conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 20000, + .conf = RV3029_TRICKLE_20K, + }, { + .r = 80000, + .conf = RV3029_TRICKLE_80K, + }, + }; + int err; + u32 ohms; + u8 trickle_set_bits = 0; + + /* Configure the trickle charger. */ + err = dev_read_u32(dev, "trickle-resistor-ohms", &ohms); + + if (!err) { + /* Find trickle-charger config */ + for (int i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++) + if (rv3029_trickle_tab[i].r >= ohms) { + dev_dbg(dev, "trickle charger at %d ohms\n", + rv3029_trickle_tab[i].r); + trickle_set_bits = rv3029_trickle_tab[i].conf; + break; + } + } + + dev_dbg(dev, "trickle charger config 0x%x\n", trickle_set_bits); + err = rv3029_eeprom_update_bits(dev, RV3029_CONTROL_E2P_EECTRL, + RV3029_TRICKLE_MASK, + trickle_set_bits); + if (err < 0) + dev_dbg(dev, "failed to update trickle charger\n"); +} +#else +static inline void rv3029_trickle_config(struct udevice *dev) +{ +} #endif + +static int rv3029_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); + + rv3029_trickle_config(dev); + return 0; } + +static const struct rtc_ops rv3029_rtc_ops = { + .get = rv3029_rtc_get, + .set = rv3029_rtc_set, + .read8 = rv3029_rtc_read8, + .write8 = rv3029_rtc_write8, + .reset = rv3029_rtc_reset, +}; + +static const struct udevice_id rv3029_rtc_ids[] = { + { .compatible = "mc,rv3029" }, + { .compatible = "mc,rv3029c2" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3029) = { + .name = "rtc-rv3029", + .id = UCLASS_RTC, + .probe = rv3029_probe, + .of_match = rv3029_rtc_ids, + .ops = &rv3029_rtc_ops, +}; diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt index 89fed73..2151d65 100644 --- a/scripts/config_whitelist.txt +++ b/scripts/config_whitelist.txt @@ -4053,7 +4053,6 @@ CONFIG_SYS_RTC_CNT CONFIG_SYS_RTC_OSCILLATOR CONFIG_SYS_RTC_REG_BASE_ADDR CONFIG_SYS_RTC_SETUP -CONFIG_SYS_RV3029_TCR CONFIG_SYS_RX_ETH_BUFFER CONFIG_SYS_SATA CONFIG_SYS_SATA1