summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/bootcount/Kconfig58
-rw-r--r--drivers/bootcount/Makefile1
-rw-r--r--drivers/bootcount/bootcount_ext.c62
-rw-r--r--drivers/clk/rockchip/clk_rk3399.c8
-rw-r--r--drivers/i2c/mxc_i2c.c25
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx6.c7
-rw-r--r--drivers/pinctrl/rockchip/pinctrl_rk3399.c19
-rw-r--r--drivers/pwm/pwm-imx-util.c2
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/s35392a.c365
12 files changed, 539 insertions, 17 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 613e60235d..c2e813f5ad 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -10,6 +10,8 @@ source "drivers/ata/Kconfig"
source "drivers/block/Kconfig"
+source "drivers/bootcount/Kconfig"
+
source "drivers/clk/Kconfig"
source "drivers/cpu/Kconfig"
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
new file mode 100644
index 0000000000..c9d627cce2
--- /dev/null
+++ b/drivers/bootcount/Kconfig
@@ -0,0 +1,58 @@
+#
+# Boot count configuration
+#
+
+menu "Boot count support"
+
+config BOOTCOUNT
+ bool "Enable Boot count support"
+ help
+ Enable boot count support, which provides the ability to store the
+ number of times the board has booted on a number of different
+ persistent storage mediums.
+
+if BOOTCOUNT
+
+config BOOTCOUNT_EXT
+ bool "Boot counter on EXT filesystem"
+ help
+ Add support for maintaining boot count in a file on an EXT
+ filesystem.
+
+if BOOTCOUNT_EXT
+
+config SYS_BOOTCOUNT_EXT_INTERFACE
+ string "Interface on which to find boot counter EXT filesystem"
+ default "mmc"
+ depends on BOOTCOUNT_EXT
+ help
+ Set the interface to use when locating the filesystem to use for the
+ boot counter.
+
+config SYS_BOOTCOUNT_EXT_DEVPART
+ string "Partition of the boot counter EXT filesystem"
+ default "0:1"
+ depends on BOOTCOUNT_EXT
+ help
+ Set the partition to use when locating the filesystem to use for the
+ boot counter.
+
+config SYS_BOOTCOUNT_EXT_NAME
+ string "Path and filename of the EXT filesystem based boot counter"
+ default "/boot/failures"
+ depends on BOOTCOUNT_EXT
+ help
+ Set the filename and path of the file used to store the boot counter.
+
+config SYS_BOOTCOUNT_ADDR
+ hex "RAM address used for reading and writing the boot counter"
+ default 0x7000A000
+ depends on BOOTCOUNT_EXT
+ help
+ Set the address used for reading and writing the boot counter.
+
+endif
+
+endif
+
+endmenu
diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile
index ed9659ad97..45445d24bf 100644
--- a/drivers/bootcount/Makefile
+++ b/drivers/bootcount/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o
obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o
obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o
obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o
+obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o
diff --git a/drivers/bootcount/bootcount_ext.c b/drivers/bootcount/bootcount_ext.c
new file mode 100644
index 0000000000..e0dd21ba6a
--- /dev/null
+++ b/drivers/bootcount/bootcount_ext.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 General Electric Company. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <bootcount.h>
+#include <fs.h>
+#include <mapmem.h>
+
+#define BC_MAGIC 0xbc
+
+void bootcount_store(ulong a)
+{
+ u8 *buf;
+ loff_t len;
+ int ret;
+
+ if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_EXT_INTERFACE,
+ CONFIG_SYS_BOOTCOUNT_EXT_DEVPART, FS_TYPE_EXT)) {
+ puts("Error selecting device\n");
+ return;
+ }
+
+ buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
+ buf[0] = BC_MAGIC;
+ buf[1] = (a & 0xff);
+ unmap_sysmem(buf);
+
+ ret = fs_write(CONFIG_SYS_BOOTCOUNT_EXT_NAME,
+ CONFIG_SYS_BOOTCOUNT_ADDR, 0, 2, &len);
+ if (ret != 0)
+ puts("Error storing bootcount\n");
+}
+
+ulong bootcount_load(void)
+{
+ u8 *buf;
+ loff_t len_read;
+ int ret;
+
+ if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_EXT_INTERFACE,
+ CONFIG_SYS_BOOTCOUNT_EXT_DEVPART, FS_TYPE_EXT)) {
+ puts("Error selecting device\n");
+ return 0;
+ }
+
+ ret = fs_read(CONFIG_SYS_BOOTCOUNT_EXT_NAME, CONFIG_SYS_BOOTCOUNT_ADDR,
+ 0, 2, &len_read);
+ if (ret != 0 || len_read != 2) {
+ puts("Error loading bootcount\n");
+ return 0;
+ }
+
+ buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
+ if (buf[0] == BC_MAGIC)
+ ret = buf[1];
+
+ unmap_sysmem(buf);
+
+ return ret;
+}
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index e21d056efb..2e85ac7df2 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -569,11 +569,6 @@ static const struct spi_clkreg spi_clkregs[] = {
.sel_shift = CLK_SPI5_PLL_SEL_SHIFT, },
};
-static inline u32 extract_bits(u32 val, unsigned width, unsigned shift)
-{
- return (val >> shift) & ((1 << width) - 1);
-}
-
static ulong rk3399_spi_get_clk(struct rk3399_cru *cru, ulong clk_id)
{
const struct spi_clkreg *spiclk = NULL;
@@ -590,7 +585,8 @@ static ulong rk3399_spi_get_clk(struct rk3399_cru *cru, ulong clk_id)
}
val = readl(&cru->clksel_con[spiclk->reg]);
- div = extract_bits(val, CLK_SPI_PLL_DIV_CON_WIDTH, spiclk->div_shift);
+ div = bitfield_extract(val, spiclk->div_shift,
+ CLK_SPI_PLL_DIV_CON_WIDTH);
return DIV_TO_RATE(GPLL_HZ, div);
}
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c
index abf1da2ae3..205274e947 100644
--- a/drivers/i2c/mxc_i2c.c
+++ b/drivers/i2c/mxc_i2c.c
@@ -317,16 +317,19 @@ static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip,
temp |= I2CR_MTX | I2CR_TX_NO_AK;
writeb(temp, base + (I2CR << reg_shift));
- /* write slave address */
- ret = tx_byte(i2c_bus, chip << 1);
- if (ret < 0)
- return ret;
-
- while (alen--) {
- ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff);
+ if (alen >= 0) {
+ /* write slave address */
+ ret = tx_byte(i2c_bus, chip << 1);
if (ret < 0)
return ret;
+
+ while (alen--) {
+ ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff);
+ if (ret < 0)
+ return ret;
+ }
}
+
return 0;
}
@@ -537,9 +540,11 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
if (ret < 0)
return ret;
- temp = readb(base + (I2CR << reg_shift));
- temp |= I2CR_RSTA;
- writeb(temp, base + (I2CR << reg_shift));
+ if (alen >= 0) {
+ temp = readb(base + (I2CR << reg_shift));
+ temp |= I2CR_RSTA;
+ writeb(temp, base + (I2CR << reg_shift));
+ }
ret = tx_byte(i2c_bus, (chip << 1) | 1);
if (ret < 0) {
diff --git a/drivers/pinctrl/nxp/pinctrl-imx6.c b/drivers/pinctrl/nxp/pinctrl-imx6.c
index 0f767d9079..cac577b40b 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx6.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx6.c
@@ -13,6 +13,11 @@
static struct imx_pinctrl_soc_info imx6_pinctrl_soc_info;
+/* FIXME Before reloaction, BSS is overlapped with DT area */
+static struct imx_pinctrl_soc_info imx6ul_pinctrl_soc_info = {
+ .flags = ZERO_OFFSET_VALID,
+};
+
static struct imx_pinctrl_soc_info imx6_snvs_pinctrl_soc_info = {
.flags = ZERO_OFFSET_VALID,
};
@@ -32,7 +37,7 @@ static const struct udevice_id imx6_pinctrl_match[] = {
{ .compatible = "fsl,imx6sll-iomuxc-snvs", .data = (ulong)&imx6_snvs_pinctrl_soc_info },
{ .compatible = "fsl,imx6sll-iomuxc", .data = (ulong)&imx6_pinctrl_soc_info },
{ .compatible = "fsl,imx6sx-iomuxc", .data = (ulong)&imx6_pinctrl_soc_info },
- { .compatible = "fsl,imx6ul-iomuxc", .data = (ulong)&imx6_pinctrl_soc_info },
+ { .compatible = "fsl,imx6ul-iomuxc", .data = (ulong)&imx6ul_pinctrl_soc_info },
{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = (ulong)&imx6_snvs_pinctrl_soc_info },
{ /* sentinel */ }
};
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3399.c b/drivers/pinctrl/rockchip/pinctrl_rk3399.c
index cab268c7d6..19a7415522 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3399.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3399.c
@@ -70,11 +70,21 @@ static void pinctrl_rk3399_i2c_config(struct rk3399_grf_regs *grf,
PMUGRF_GPIO1C0_SEL_MASK,
PMUGRF_I2C0PMU_SCL << PMUGRF_GPIO1C0_SEL_SHIFT);
break;
+ case PERIPH_ID_I2C8:
+ rk_clrsetreg(&pmugrf->gpio1c_iomux,
+ PMUGRF_GPIO1C4_SEL_MASK,
+ PMUGRF_I2C8PMU_SDA << PMUGRF_GPIO1C4_SEL_SHIFT);
+ rk_clrsetreg(&pmugrf->gpio1c_iomux,
+ PMUGRF_GPIO1C5_SEL_MASK,
+ PMUGRF_I2C8PMU_SCL << PMUGRF_GPIO1C5_SEL_SHIFT);
+ break;
case PERIPH_ID_I2C1:
case PERIPH_ID_I2C2:
case PERIPH_ID_I2C3:
case PERIPH_ID_I2C4:
case PERIPH_ID_I2C5:
+ case PERIPH_ID_I2C6:
+ case PERIPH_ID_I2C7:
default:
debug("i2c id = %d iomux error!\n", i2c_id);
break;
@@ -301,6 +311,9 @@ static int rk3399_pinctrl_request(struct udevice *dev, int func, int flags)
case PERIPH_ID_I2C3:
case PERIPH_ID_I2C4:
case PERIPH_ID_I2C5:
+ case PERIPH_ID_I2C6:
+ case PERIPH_ID_I2C7:
+ case PERIPH_ID_I2C8:
pinctrl_rk3399_i2c_config(priv->grf, priv->pmugrf, func);
break;
case PERIPH_ID_SPI0:
@@ -375,6 +388,12 @@ static int rk3399_pinctrl_get_periph_id(struct udevice *dev,
return PERIPH_ID_I2C4;
case 38:
return PERIPH_ID_I2C5;
+ case 37:
+ return PERIPH_ID_I2C6;
+ case 36:
+ return PERIPH_ID_I2C7;
+ case 58:
+ return PERIPH_ID_I2C8;
case 65:
return PERIPH_ID_SDMMC1;
#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
diff --git a/drivers/pwm/pwm-imx-util.c b/drivers/pwm/pwm-imx-util.c
index 534dd8eece..97ac0c8fc0 100644
--- a/drivers/pwm/pwm-imx-util.c
+++ b/drivers/pwm/pwm-imx-util.c
@@ -23,10 +23,12 @@ struct pwm_regs *pwm_id_to_reg(int pwm_id)
return (struct pwm_regs *)PWM1_BASE_ADDR;
case 1:
return (struct pwm_regs *)PWM2_BASE_ADDR;
+#ifdef CONFIG_MX6
case 2:
return (struct pwm_regs *)PWM3_BASE_ADDR;
case 3:
return (struct pwm_regs *)PWM4_BASE_ADDR;
+#endif
#ifdef CONFIG_MX6SX
case 4:
return (struct pwm_regs *)PWM5_BASE_ADDR;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index d06130c7a2..2964bb2211 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -30,4 +30,10 @@ config RTC_DS1307
Support for Dallas Semiconductor (now Maxim) DS1307 and DS1338/9 and
compatible Real Time Clock devices.
+config RTC_S35392A
+ bool "Enable S35392A driver"
+ select BITREVERSE
+ help
+ Enable s35392a driver which provides rtc get and set function.
+
endmenu
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 003e31aeba..7a8f97a05f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -49,5 +49,6 @@ obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o
obj-$(CONFIG_RTC_RV3029) += rv3029.o
obj-$(CONFIG_RTC_RX8025) += rx8025.o
obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o
+obj-$(CONFIG_RTC_S35392A) += s35392a.o
obj-$(CONFIG_SANDBOX) += sandbox_rtc.o
obj-$(CONFIG_RTC_X1205) += x1205.o
diff --git a/drivers/rtc/s35392a.c b/drivers/rtc/s35392a.c
new file mode 100644
index 0000000000..9adcefc98d
--- /dev/null
+++ b/drivers/rtc/s35392a.c
@@ -0,0 +1,365 @@
+/*
+ * SII Semiconductor Corporation S35392A RTC driver.
+ *
+ * Copyright (c) 2017, General Electric Company
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <command.h>
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <linux/bitrev.h>
+#include <rtc.h>
+
+#define S35390A_CMD_STATUS1 0x30
+#define S35390A_CMD_STATUS2 0x31
+#define S35390A_CMD_TIME1 0x32
+#define S35390A_CMD_TIME2 0x33
+#define S35390A_CMD_INT2_REG1 0x35
+
+#define S35390A_BYTE_YEAR 0
+#define S35390A_BYTE_MONTH 1
+#define S35390A_BYTE_DAY 2
+#define S35390A_BYTE_WDAY 3
+#define S35390A_BYTE_HOURS 4
+#define S35390A_BYTE_MINS 5
+#define S35390A_BYTE_SECS 6
+
+/* flags for STATUS1 */
+#define S35390A_FLAG_POC 0x01
+#define S35390A_FLAG_BLD 0x02
+#define S35390A_FLAG_INT2 0x04
+#define S35390A_FLAG_24H 0x40
+#define S35390A_FLAG_RESET 0x80
+
+/*
+ * If either BLD or POC is set, then the chip has lost power long enough for
+ * the time value to become invalid.
+ */
+#define S35390A_LOW_VOLTAGE (S35390A_FLAG_POC | S35390A_FLAG_BLD)
+
+/*---------------------------------------------------------------------*/
+#undef DEBUG_RTC
+
+#ifdef DEBUG_RTC
+#define DEBUGR(fmt, args...) printf(fmt, ##args)
+#else
+#define DEBUGR(fmt, args...)
+#endif
+/*---------------------------------------------------------------------*/
+
+#ifdef CONFIG_DM_RTC
+#define DEV_TYPE struct udevice
+#else
+/* Local udevice */
+struct ludevice {
+ u8 chip;
+};
+
+#define DEV_TYPE struct ludevice
+struct ludevice dev;
+
+#endif
+
+#define msleep(a) udelay(a * 1000)
+
+int lowvoltage;
+
+static int s35392a_rtc_reset(DEV_TYPE *dev);
+
+static int s35392a_rtc_read(DEV_TYPE *dev, u8 reg, u8 *buf, int len)
+{
+ int ret;
+
+#ifdef CONFIG_DM_RTC
+ /* TODO: we need to tweak the chip address to reg */
+ ret = dm_i2c_read(dev, 0, buf, len);
+#else
+ (void)dev;
+ ret = i2c_read(reg, 0, -1, buf, len);
+#endif
+
+ return ret;
+}
+
+static int s35392a_rtc_write(DEV_TYPE *dev, u8 reg, u8 *buf, int len)
+{
+ int ret;
+
+#ifdef CONFIG_DM_RTC
+ /* TODO: we need to tweak the chip address to reg */
+ ret = dm_i2c_write(dev, 0, buf, 1);
+#else
+ (void)dev;
+ ret = i2c_write(reg, 0, 0, buf, len);
+#endif
+
+ return ret;
+}
+
+static int s35392a_rtc_read8(DEV_TYPE *dev, unsigned int reg)
+{
+ u8 val;
+ int ret;
+
+ ret = s35392a_rtc_read(dev, reg, &val, sizeof(val));
+ return ret < 0 ? ret : val;
+}
+
+static int s35392a_rtc_write8(DEV_TYPE *dev, unsigned int reg, int val)
+{
+ int ret;
+ u8 lval = val;
+
+ ret = s35392a_rtc_write(dev, reg, &lval, sizeof(lval));
+ return ret < 0 ? ret : 0;
+}
+
+static int validate_time(const struct rtc_time *tm)
+{
+ if ((tm->tm_year < 2000) || (tm->tm_year > 2099))
+ return -EINVAL;
+
+ if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+ return -EINVAL;
+
+ if ((tm->tm_mday < 1) || (tm->tm_mday > 31))
+ return -EINVAL;
+
+ if ((tm->tm_wday < 0) || (tm->tm_wday > 6))
+ return -EINVAL;
+
+ if ((tm->tm_hour < 0) || (tm->tm_hour > 23))
+ return -EINVAL;
+
+ if ((tm->tm_min < 0) || (tm->tm_min > 59))
+ return -EINVAL;
+
+ if ((tm->tm_sec < 0) || (tm->tm_sec > 59))
+ return -EINVAL;
+
+ return 0;
+}
+
+void s35392a_rtc_init(DEV_TYPE *dev)
+{
+ int status;
+
+ status = s35392a_rtc_read8(dev, S35390A_CMD_STATUS1);
+ if (status < 0)
+ goto error;
+
+ DEBUGR("init: S35390A_CMD_STATUS1: 0x%x\n", status);
+
+ lowvoltage = status & S35390A_LOW_VOLTAGE ? 1 : 0;
+
+ if (status & S35390A_FLAG_POC)
+ /*
+ * Do not communicate for 0.5 seconds since the power-on
+ * detection circuit is in operation.
+ */
+ msleep(500);
+
+ else if (!lowvoltage)
+ /*
+ * If both POC and BLD are unset everything is fine.
+ */
+ return;
+
+ if (lowvoltage)
+ printf("RTC low voltage detected\n");
+
+ if (!s35392a_rtc_reset(dev))
+ return;
+
+error:
+ printf("Error RTC init.\n");
+}
+
+/* Get the current time from the RTC */
+static int s35392a_rtc_get(DEV_TYPE *dev, struct rtc_time *tm)
+{
+ u8 date[7];
+ int ret, i;
+
+ if (lowvoltage) {
+ DEBUGR("RTC low voltage detected\n");
+ return -EINVAL;
+ }
+
+ ret = s35392a_rtc_read(dev, S35390A_CMD_TIME1, date, sizeof(date));
+ if (ret < 0) {
+ DEBUGR("Error reading date from RTC\n");
+ return -EIO;
+ }
+
+ /* This chip returns the bits of each byte in reverse order */
+ for (i = 0; i < 7; ++i)
+ date[i] = bitrev8(date[i]);
+
+ tm->tm_sec = bcd2bin(date[S35390A_BYTE_SECS]);
+ tm->tm_min = bcd2bin(date[S35390A_BYTE_MINS]);
+ tm->tm_hour = bcd2bin(date[S35390A_BYTE_HOURS] & ~S35390A_FLAG_24H);
+ tm->tm_wday = bcd2bin(date[S35390A_BYTE_WDAY]);
+ tm->tm_mday = bcd2bin(date[S35390A_BYTE_DAY]);
+ tm->tm_mon = bcd2bin(date[S35390A_BYTE_MONTH]);
+ tm->tm_year = bcd2bin(date[S35390A_BYTE_YEAR]) + 2000;
+
+ DEBUGR("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+/* Set the RTC */
+static int s35392a_rtc_set(DEV_TYPE *dev, const struct rtc_time *tm)
+{
+ int i, ret;
+ int status;
+ u8 date[7];
+
+ DEBUGR("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ ret = validate_time(tm);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* We support only 24h mode */
+ ret = s35392a_rtc_read8(dev, S35390A_CMD_STATUS1);
+ if (ret < 0)
+ return -EIO;
+ status = ret;
+
+ ret = s35392a_rtc_write8(dev, S35390A_CMD_STATUS1,
+ status | S35390A_FLAG_24H);
+ if (ret < 0)
+ return -EIO;
+
+ date[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 2000);
+ date[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon);
+ date[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday);
+ date[S35390A_BYTE_WDAY] = bin2bcd(tm->tm_wday);
+ date[S35390A_BYTE_HOURS] = bin2bcd(tm->tm_hour);
+ date[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min);
+ date[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec);
+
+ /* This chip expects the bits of each byte to be in reverse order */
+ for (i = 0; i < 7; ++i)
+ date[i] = bitrev8(date[i]);
+
+ ret = s35392a_rtc_write(dev, S35390A_CMD_TIME1, date, sizeof(date));
+ if (ret < 0) {
+ DEBUGR("Error writing date to RTC\n");
+ return -EIO;
+ }
+
+ /* Now we have time. Reset the low voltage status */
+ lowvoltage = 0;
+
+ return 0;
+}
+
+/* Reset the RTC. */
+static int s35392a_rtc_reset(DEV_TYPE *dev)
+{
+ int buf;
+ int ret;
+ unsigned int initcount = 0;
+
+ buf = S35390A_FLAG_RESET;
+
+initialize:
+ ret = s35392a_rtc_write8(dev, S35390A_CMD_STATUS1, buf);
+ if (ret < 0)
+ return -EIO;
+
+ ret = s35392a_rtc_read8(dev, S35390A_CMD_STATUS1);
+ if (ret < 0)
+ return -EIO;
+ buf = ret;
+
+ if (!lowvoltage)
+ lowvoltage = buf & S35390A_LOW_VOLTAGE ? 1 : 0;
+
+ if (buf & S35390A_LOW_VOLTAGE) {
+ /* Try up to five times to reset the chip */
+ if (initcount < 5) {
+ ++initcount;
+ goto initialize;
+ } else {
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+#ifndef CONFIG_DM_RTC
+
+int rtc_get(struct rtc_time *tm)
+{
+ return s35392a_rtc_get(&dev, tm);
+}
+
+int rtc_set(struct rtc_time *tm)
+{
+ return s35392a_rtc_set(&dev, tm);
+}
+
+void rtc_reset(void)
+{
+ s35392a_rtc_reset(&dev);
+}
+
+void rtc_init(void)
+{
+ s35392a_rtc_init(&dev);
+}
+
+#else
+
+static int s35392a_probe(struct udevice *dev)
+{
+ s35392a_rtc_init(dev);
+ return 0;
+}
+
+static const struct rtc_ops s35392a_rtc_ops = {
+ .get = s35392a_rtc_get,
+ .set = s35392a_rtc_set,
+ .read8 = s35392a_rtc_read8,
+ .write8 = s35392a_rtc_write8,
+ .reset = s35392a_rtc_reset,
+};
+
+static const struct udevice_id s35392a_rtc_ids[] = {
+ { .compatible = "sii,s35392a-rtc" },
+ { }
+};
+
+U_BOOT_DRIVER(s35392a_rtc) = {
+ .name = "s35392a_rtc",
+ .id = UCLASS_RTC,
+ .probe = s35392a_probe,
+ .of_match = s35392a_rtc_ids,
+ .ops = &s35392a_rtc_ops,
+};
+
+#endif