summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/versatilepb_defconfig2
-rw-r--r--arch/mips/configs/ritmix-rzx50_defconfig3
-rw-r--r--arch/mips/dts/jz4755.dtsi5
-rw-r--r--commands/Kconfig8
-rw-r--r--commands/Makefile1
-rw-r--r--commands/hwclock.c161
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/rtc/Kconfig44
-rw-r--r--drivers/rtc/Makefile11
-rw-r--r--drivers/rtc/class.c70
-rw-r--r--drivers/rtc/rtc-ds1307.c347
-rw-r--r--drivers/rtc/rtc-jz4740.c165
-rw-r--r--drivers/rtc/rtc-lib.c113
-rw-r--r--include/linux/bcd.h22
-rw-r--r--include/linux/rtc.h47
-rw-r--r--include/rtc.h2
-rw-r--r--lib/Makefile1
-rw-r--r--lib/bcd.c14
19 files changed, 1018 insertions, 0 deletions
diff --git a/arch/arm/configs/versatilepb_defconfig b/arch/arm/configs/versatilepb_defconfig
index 58dfb8b555..a447f93292 100644
--- a/arch/arm/configs/versatilepb_defconfig
+++ b/arch/arm/configs/versatilepb_defconfig
@@ -51,6 +51,8 @@ CONFIG_DRIVER_NET_SMC91111=y
CONFIG_I2C=y
CONFIG_I2C_VERSATILE=y
CONFIG_GPIO_PL061=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1307=y
CONFIG_FS_CRAMFS=y
CONFIG_FS_TFTP=y
CONFIG_SHA1=y
diff --git a/arch/mips/configs/ritmix-rzx50_defconfig b/arch/mips/configs/ritmix-rzx50_defconfig
index 232faa2659..fb67014532 100644
--- a/arch/mips/configs/ritmix-rzx50_defconfig
+++ b/arch/mips/configs/ritmix-rzx50_defconfig
@@ -10,6 +10,7 @@ CONFIG_CMDLINE_EDITING=y
CONFIG_AUTO_COMPLETE=y
# CONFIG_DEFAULT_ENVIRONMENT is not set
CONFIG_DEBUG_LL=y
+CONFIG_LONGHELP=y
CONFIG_CMD_IOMEM=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_BOOTM_SHOW_TYPE=y
@@ -42,6 +43,8 @@ CONFIG_LED_GPIO=y
CONFIG_LED_GPIO_OF=y
CONFIG_LED_TRIGGERS=y
CONFIG_GPIO_JZ4740=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_JZ4740=y
CONFIG_SHA1=y
CONFIG_SHA224=y
CONFIG_SHA256=y
diff --git a/arch/mips/dts/jz4755.dtsi b/arch/mips/dts/jz4755.dtsi
index 718463548b..b83d884a24 100644
--- a/arch/mips/dts/jz4755.dtsi
+++ b/arch/mips/dts/jz4755.dtsi
@@ -13,6 +13,11 @@
reg = <0xb0002000 0x10>;
};
+ rtc: rtc@10003000 {
+ compatible = "ingenic,jz4740-rtc";
+ reg = <0xb0003000 0x38>;
+ };
+
serial0: serial@b0030000 {
compatible = "ingenic,jz4740-uart";
reg = <0xb0030000 0x20>;
diff --git a/commands/Kconfig b/commands/Kconfig
index 71cd1ee99e..8ee0805407 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1700,6 +1700,14 @@ config CMD_GPIO
Usage: gpio_set_value GPIO VALUE
+config CMD_HWCLOCK
+ bool
+ depends on RTC_CLASS
+ prompt "hwclock command"
+ default y
+ help
+ The hwclock command allows to query or set the hardware clock (RTC).
+
config CMD_I2C
bool
depends on I2C
diff --git a/commands/Makefile b/commands/Makefile
index 44741360e2..ca0289782e 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -101,3 +101,4 @@ obj-$(CONFIG_CMD_2048) += 2048.o
obj-$(CONFIG_CMD_REGULATOR) += regulator.o
obj-$(CONFIG_CMD_LSPCI) += lspci.o
obj-$(CONFIG_CMD_IMD) += imd.o
+obj-$(CONFIG_CMD_HWCLOCK) += hwclock.o
diff --git a/commands/hwclock.c b/commands/hwclock.c
new file mode 100644
index 0000000000..a1f5293122
--- /dev/null
+++ b/commands/hwclock.c
@@ -0,0 +1,161 @@
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <rtc.h>
+#include <linux/rtc.h>
+#include <string.h>
+#include <environment.h>
+
+static char *strchrnul(const char *s, int c)
+{
+ while (*s != '\0' && *s != c)
+ s++;
+
+ return (char *)s;
+}
+
+static int sscanf_two_digits(char *s, int *res)
+{
+ char buf[3];
+ unsigned long t;
+
+ if (!isdigit(s[0]) || !isdigit(s[1])) {
+ return -EINVAL;
+ }
+
+ buf[0] = s[0];
+ buf[1] = s[1];
+ buf[2] = '\0';
+
+ t = simple_strtoul(buf, NULL, 10);
+ *res = t;
+
+ return 0;
+}
+
+static int parse_datestr(char *date_str, struct rtc_time *ptm)
+{
+ char end = '\0';
+ int len = strchrnul(date_str, '.') - date_str;
+ int year;
+
+ /* ccyymmddHHMM[.SS] */
+ if (len != 12) {
+ return -EINVAL;
+ }
+
+ if (sscanf_two_digits(date_str, &year) ||
+ sscanf_two_digits(&date_str[2], &ptm->tm_year)) {
+ return -EINVAL;
+ }
+
+ ptm->tm_year = year * 100 + ptm->tm_year;
+
+ /* Adjust years */
+ ptm->tm_year -= 1900;
+
+ if (sscanf_two_digits(&date_str[4], &ptm->tm_mon) ||
+ sscanf_two_digits(&date_str[6], &ptm->tm_mday) ||
+ sscanf_two_digits(&date_str[8], &ptm->tm_hour) ||
+ sscanf_two_digits(&date_str[10], &ptm->tm_min)) {
+ return -EINVAL;
+ }
+
+ /* Adjust month from 1-12 to 0-11 */
+ ptm->tm_mon -= 1;
+
+ end = date_str[12];
+
+ if (end == '.') {
+ /* xxx.SS */
+ if (!sscanf_two_digits(&date_str[13], &ptm->tm_sec)) {
+ end = '\0';
+ }
+ /* else end != NUL and we error out */
+ }
+
+ if (end != '\0') {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int do_hwclock(int argc, char *argv[])
+{
+ struct rtc_device *r;
+ struct rtc_time tm;
+ struct rtc_time stm;
+ char rtc_name[16] = "rtc0";
+ char *env_name = NULL;
+ int opt;
+ int set = 0;
+
+ while ((opt = getopt(argc, argv, "f:s:e:")) > 0) {
+ int ret;
+
+ switch (opt) {
+ case 'f':
+ strncpy(rtc_name, optarg, 16);
+ break;
+ case 's':
+ memset(&stm, 0, sizeof(stm));
+
+ ret = parse_datestr(optarg, &stm);
+ if (ret)
+ return ret;
+
+ ret = rtc_valid_tm(&stm);
+ if (ret)
+ return ret;
+
+ set = 1;
+ break;
+ case 'e':
+ env_name = optarg;
+ break;
+ }
+ }
+
+ r = rtc_lookup(rtc_name);
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+
+ if (set) {
+ rtc_set_time(r, &stm);
+ return 0;
+ }
+
+ rtc_read_time(r, &tm);
+
+ if (env_name) {
+ unsigned long time;
+ char t[12];
+
+ rtc_tm_to_time(&tm, &time);
+ snprintf(t, 12, "%lu", time);
+ setenv(env_name, t);
+ } else {
+ printf("%02d:%02d:%02d %02d-%02d-%04d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
+ }
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(hwclock)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-f NAME\t\t\t", "RTC device name (default rtc0)")
+BAREBOX_CMD_HELP_OPT ("-e VARNAME\t\t", "store RTC readout into variable VARNAME")
+BAREBOX_CMD_HELP_OPT ("-s ccyymmddHHMM[.SS]\t", "set time")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(hwclock)
+ .cmd = do_hwclock,
+ BAREBOX_CMD_DESC("query or set the hardware clock (RTC)")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_hwclock_help)
+BAREBOX_CMD_END
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 12a9d8c7d8..d38032c0e6 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -28,5 +28,6 @@ source "drivers/bus/Kconfig"
source "drivers/regulator/Kconfig"
source "drivers/reset/Kconfig"
source "drivers/pci/Kconfig"
+source "drivers/rtc/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 1990e86bd9..4591f9a408 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -27,3 +27,4 @@ obj-y += bus/
obj-$(CONFIG_REGULATOR) += regulator/
obj-$(CONFIG_RESET_CONTROLLER) += reset/
obj-$(CONFIG_PCI) += pci/
+obj-y += rtc/
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
new file mode 100644
index 0000000000..957fee73f8
--- /dev/null
+++ b/drivers/rtc/Kconfig
@@ -0,0 +1,44 @@
+#
+# RTC class/drivers configuration
+#
+
+config RTC_LIB
+ bool
+
+menuconfig RTC_CLASS
+ bool "Real Time Clock"
+ default n
+ select RTC_LIB
+ help
+ Generic RTC class support. If you say yes here, you will
+ be allowed to plug one or more RTCs to your system. You will
+ probably want to enable one or more of the interfaces below.
+
+if RTC_CLASS
+
+comment "I2C RTC drivers"
+ depends on I2C
+
+if I2C
+
+config RTC_DRV_DS1307
+ tristate "Dallas/Maxim DS1307/38"
+ help
+ If you say yes here you get support for various compatible RTC
+ chips (often with battery backup) connected with I2C. This driver
+ should handle DS1307, DS1338 and probably other chips.
+
+ The first seven registers on these chips hold an RTC, and other
+ registers may add features such as NVRAM, a trickle charger for
+ the RTC/NVRAM backup power, and alarms.
+
+endif # I2C
+
+config RTC_DRV_JZ4740
+ tristate "Ingenic JZ4740 RTC"
+ depends on MACH_MIPS_XBURST
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC RTC
+ controller.
+
+endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
new file mode 100644
index 0000000000..1cc9bb8b4b
--- /dev/null
+++ b/drivers/rtc/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for RTC class/drivers.
+#
+
+obj-$(CONFIG_RTC_LIB) += rtc-lib.o
+obj-$(CONFIG_RTC_CLASS) += class.o
+
+# Keep the list ordered.
+
+obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
+obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
new file mode 100644
index 0000000000..356707be2f
--- /dev/null
+++ b/drivers/rtc/class.c
@@ -0,0 +1,70 @@
+/*
+ * RTC barebox subsystem, base class
+ *
+ * Copyright (C) 2014 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <rtc.h>
+#include <linux/rtc.h>
+
+LIST_HEAD(rtc_list);
+EXPORT_SYMBOL(rtc_list);
+
+struct rtc_device *rtc_lookup(const char *name)
+{
+ struct rtc_device *r;
+
+ if (!name)
+ return ERR_PTR(-ENODEV);
+
+ list_for_each_entry(r, &rtc_list, list) {
+ if (!strcmp(dev_name(&r->class_dev), name))
+ return r;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(rtc_lookup);
+
+int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ return rtc->ops->read_time(rtc, tm);
+}
+EXPORT_SYMBOL(rtc_read_time);
+
+int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ return rtc->ops->set_time(rtc, tm);
+}
+EXPORT_SYMBOL(rtc_set_time);
+
+int rtc_register(struct rtc_device *rtcdev)
+{
+ struct device_d *dev = &rtcdev->class_dev;
+
+ if (!rtcdev->ops)
+ return -EINVAL;
+
+ dev->id = DEVICE_ID_DYNAMIC;
+ strcpy(dev->name, "rtc");
+ if (rtcdev->dev)
+ dev->parent = rtcdev->dev;
+ platform_device_register(dev);
+
+ list_add_tail(&rtcdev->list, &rtc_list);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtc_register);
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
new file mode 100644
index 0000000000..725f15ab57
--- /dev/null
+++ b/drivers/rtc/rtc-ds1307.c
@@ -0,0 +1,347 @@
+/*
+ * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
+ *
+ * This code was ported from linux-3.15 kernel by Antony Pavlov.
+ *
+ * Copyright (C) 2005 James Chapman (ds1337 core)
+ * Copyright (C) 2006 David Brownell
+ * Copyright (C) 2009 Matthias Fuchs (rx8025 support)
+ * Copyright (C) 2012 Bertrand Achard (nvram access fixes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+#include <rtc.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+/*
+ * We can't determine type by probing, but if we expect pre-Linux code
+ * to have set the chip up as a clock (turning on the oscillator and
+ * setting the date and time), Linux can ignore the non-clock features.
+ * That's a natural job for a factory or repair bench.
+ */
+enum ds_type {
+ ds_1307,
+ ds_1338,
+ last_ds_type /* always last */
+};
+
+/* RTC registers don't differ much, except for the century flag */
+#define DS1307_REG_SECS 0x00 /* 00-59 */
+# define DS1307_BIT_CH 0x80
+# define DS1340_BIT_nEOSC 0x80
+#define DS1307_REG_MIN 0x01 /* 00-59 */
+#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
+# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */
+# define DS1307_BIT_PM 0x20 /* in REG_HOUR */
+# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
+# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
+#define DS1307_REG_WDAY 0x03 /* 01-07 */
+#define DS1307_REG_MDAY 0x04 /* 01-31 */
+#define DS1307_REG_MONTH 0x05 /* 01-12 */
+# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
+#define DS1307_REG_YEAR 0x06 /* 00-99 */
+
+/*
+ * Other registers (control, status, alarms, trickle charge, NVRAM, etc)
+ * start at 7, and they differ a LOT. Only control and status matter for
+ * basic RTC date and time functionality; be careful using them.
+ */
+#define DS1307_REG_CONTROL 0x07 /* or ds1338 */
+# define DS1307_BIT_OUT 0x80
+# define DS1338_BIT_OSF 0x20
+# define DS1307_BIT_SQWE 0x10
+# define DS1307_BIT_RS1 0x02
+# define DS1307_BIT_RS0 0x01
+
+struct ds1307 {
+ struct rtc_device rtc;
+ u8 offset; /* register's offset */
+ u8 regs[11];
+ enum ds_type type;
+ unsigned long flags;
+ struct i2c_client *client;
+ s32 (*read_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values);
+ s32 (*write_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values);
+};
+
+static struct platform_device_id ds1307_id[] = {
+ { "ds1307", ds_1307 },
+ { "ds1338", ds_1338 },
+ { "pt7c4338", ds_1307 },
+ { }
+};
+
+/*----------------------------------------------------------------------*/
+
+#define BLOCK_DATA_MAX_TRIES 10
+
+static s32 ds1307_read_block_data_once(const struct i2c_client *client,
+ u8 command, u8 length, u8 *values)
+{
+ s32 i, data;
+
+ for (i = 0; i < length; i++) {
+ data = i2c_smbus_read_byte_data(client, command + i);
+ if (data < 0)
+ return data;
+ values[i] = data;
+ }
+ return i;
+}
+
+static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ u8 oldvalues[255];
+ s32 ret;
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length);
+ ret = ds1307_read_block_data_once(client, command, length, values);
+ if (ret < 0)
+ return ret;
+ do {
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_read_block_data failed\n");
+ return -EIO;
+ }
+ memcpy(oldvalues, values, length);
+ ret = ds1307_read_block_data_once(client, command, length,
+ values);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(oldvalues, values, length));
+ return length;
+}
+
+static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values)
+{
+ u8 currvalues[255];
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length);
+ do {
+ s32 i, ret;
+
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_write_block_data failed\n");
+ return -EIO;
+ }
+ for (i = 0; i < length; i++) {
+ ret = i2c_smbus_write_byte_data(client, command + i,
+ values[i]);
+ if (ret < 0)
+ return ret;
+ }
+ ret = ds1307_read_block_data_once(client, command, length,
+ currvalues);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(currvalues, values, length));
+ return length;
+}
+
+static inline struct ds1307 *to_ds1307_priv(struct rtc_device *rtcdev)
+{
+ return container_of(rtcdev, struct ds1307, rtc);
+}
+
+static int ds1307_get_time(struct rtc_device *rtcdev, struct rtc_time *t)
+{
+ struct device_d *dev = rtcdev->dev;
+ struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
+ int tmp;
+
+ /* read the RTC date and time registers all at once */
+ tmp = ds1307->read_block_data(ds1307->client,
+ ds1307->offset, 7, ds1307->regs);
+ if (tmp != 7) {
+ dev_err(dev, "%s error %d\n", "read", tmp);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
+
+ t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);
+ t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f);
+ tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
+ t->tm_hour = bcd2bin(tmp);
+ t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;
+ t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
+ tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
+ t->tm_mon = bcd2bin(tmp) - 1;
+
+ /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
+ t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(t);
+}
+
+static int ds1307_set_time(struct rtc_device *rtcdev, struct rtc_time *t)
+{
+ struct device_d *dev = rtcdev->dev;
+ struct ds1307 *ds1307 = to_ds1307_priv(rtcdev);
+ int result;
+ int tmp;
+ u8 *buf = ds1307->regs;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
+ buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
+ buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
+ buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
+ buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
+ buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
+
+ /* assume 20YY not 19YY */
+ tmp = t->tm_year - 100;
+ buf[DS1307_REG_YEAR] = bin2bcd(tmp);
+
+ dev_dbg(dev, "%s: %7ph\n", "write", buf);
+
+ result = ds1307->write_block_data(ds1307->client,
+ ds1307->offset, 7, buf);
+ if (result < 0) {
+ dev_err(dev, "%s error %d\n", "write", result);
+ return result;
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops ds13xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+};
+
+static int ds1307_probe(struct device_d *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307;
+ int err = -ENODEV;
+ int tmp;
+ unsigned char *buf;
+ unsigned long driver_data;
+
+ ds1307 = xzalloc(sizeof(struct ds1307));
+
+ err = dev_get_drvdata(dev, &driver_data);
+ if (err)
+ goto exit;
+
+ ds1307->client = client;
+ ds1307->type = driver_data;
+
+ buf = ds1307->regs;
+
+ ds1307->read_block_data = ds1307_read_block_data;
+ ds1307->write_block_data = ds1307_write_block_data;
+
+read_rtc:
+ /* read RTC registers */
+ tmp = ds1307->read_block_data(client, ds1307->offset, 8, buf);
+ if (tmp != 8) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /*
+ * minimal sanity checking; some chips (like DS1340) don't
+ * specify the extra bits as must-be-zero, but there are
+ * still a few values that are clearly out-of-range.
+ */
+ tmp = ds1307->regs[DS1307_REG_SECS];
+ switch (ds1307->type) {
+ case ds_1307:
+ /* clock halted? turn it on, so clock can tick. */
+ if (tmp & DS1307_BIT_CH) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+ break;
+ case ds_1338:
+ /* clock halted? turn it on, so clock can tick. */
+ if (tmp & DS1307_BIT_CH)
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+
+ /* oscillator fault? clear flag, and warn */
+ if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL,
+ ds1307->regs[DS1307_REG_CONTROL]
+ & ~DS1338_BIT_OSF);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+ break;
+ default:
+ break;
+ }
+
+ tmp = ds1307->regs[DS1307_REG_HOUR];
+ switch (ds1307->type) {
+ default:
+ if (!(tmp & DS1307_BIT_12HR))
+ break;
+
+ /*
+ * Be sure we're in 24 hour mode. Multi-master systems
+ * take note...
+ */
+ tmp = bcd2bin(tmp & 0x1f);
+ if (tmp == 12)
+ tmp = 0;
+ if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
+ tmp += 12;
+ i2c_smbus_write_byte_data(client,
+ ds1307->offset + DS1307_REG_HOUR,
+ bin2bcd(tmp));
+ }
+
+ ds1307->rtc.ops = &ds13xx_rtc_ops;
+ ds1307->rtc.dev = dev;
+
+ err = rtc_register(&ds1307->rtc);
+
+exit:
+ return err;
+}
+
+static struct driver_d ds1307_driver = {
+ .name = "rtc-ds1307",
+ .probe = ds1307_probe,
+ .id_table = ds1307_id,
+};
+
+static int __init ds1307_init(void)
+{
+ return i2c_driver_register(&ds1307_driver);
+}
+device_initcall(ds1307_init);
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
new file mode 100644
index 0000000000..8194050e7f
--- /dev/null
+++ b/drivers/rtc/rtc-jz4740.c
@@ -0,0 +1,165 @@
+/*
+ * JZ4740 SoC RTC driver
+ *
+ * This code was ported from linux-3.15 kernel by Antony Pavlov.
+ *
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <io.h>
+#include <rtc.h>
+#include <linux/rtc.h>
+
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_SEC 0x04
+#define JZ_REG_RTC_SEC_ALARM 0x08
+#define JZ_REG_RTC_REGULATOR 0x0C
+#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_SCRATCHPAD 0x34
+
+#define JZ_RTC_CTRL_WRDY BIT(7)
+
+struct jz4740_rtc {
+ struct rtc_device rtc;
+
+ void __iomem *base;
+};
+
+static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
+{
+ return readl(rtc->base + reg);
+}
+
+static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
+{
+ uint32_t ctrl;
+ int timeout = 1000;
+
+ do {
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+ } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout);
+
+ return timeout ? 0 : -EIO;
+}
+
+static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
+ uint32_t val)
+{
+ int ret;
+ ret = jz4740_rtc_wait_write_ready(rtc);
+ if (ret == 0)
+ writel(val, rtc->base + reg);
+
+ return ret;
+}
+
+static inline struct jz4740_rtc *to_jz4740_rtc_priv(struct rtc_device *rtcdev)
+{
+ return container_of(rtcdev, struct jz4740_rtc, rtc);
+}
+
+static int jz4740_rtc_read_time(struct rtc_device *rtcdev, struct rtc_time *time)
+{
+ struct jz4740_rtc *rtc = to_jz4740_rtc_priv(rtcdev);
+ uint32_t secs, secs2;
+ int timeout = 5;
+
+ /* If the seconds register is read while it is updated, it can contain a
+ * bogus value. This can be avoided by making sure that two consecutive
+ * reads have the same value.
+ */
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+
+ while (secs != secs2 && --timeout) {
+ secs = secs2;
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ }
+
+ if (timeout == 0)
+ return -EIO;
+
+ rtc_time_to_tm(secs, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int jz4740_rtc_set_time(struct rtc_device *rtcdev, struct rtc_time *t)
+{
+ struct jz4740_rtc *rtc = to_jz4740_rtc_priv(rtcdev);
+ unsigned long secs;
+
+ rtc_tm_to_time(t, &secs);
+
+ return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
+}
+
+static struct rtc_class_ops jz4740_rtc_ops = {
+ .read_time = jz4740_rtc_read_time,
+ .set_time = jz4740_rtc_set_time,
+};
+
+static int jz4740_rtc_probe(struct device_d *dev)
+{
+ int ret;
+ struct jz4740_rtc *rtc;
+ uint32_t scratchpad;
+ void __iomem *base;
+
+ base = dev_request_mem_region(dev, 0);
+ if (!base) {
+ dev_err(dev, "could not get memory region\n");
+ return -ENODEV;
+ }
+
+ rtc = xzalloc(sizeof(*rtc));
+
+ rtc->base = base;
+ rtc->rtc.ops = &jz4740_rtc_ops;
+ rtc->rtc.dev = dev;
+
+ ret = rtc_register(&rtc->rtc);
+ if (ret) {
+ dev_err(dev, "Failed to register rtc device: %d\n", ret);
+ return ret;
+ }
+
+ scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
+ if (scratchpad != 0x12345678) {
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
+ if (ret) {
+ dev_err(dev, "Could not write write to RTC registers\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id jz4740_rtc_dt_ids[] = {
+ {
+ .compatible = "ingenic,jz4740-rtc",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d jz4740_rtc_driver = {
+ .name = "jz4740-rtc",
+ .probe = jz4740_rtc_probe,
+ .of_compatible = DRV_OF_COMPAT(jz4740_rtc_dt_ids),
+};
+device_platform_driver(jz4740_rtc_driver);
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
new file mode 100644
index 0000000000..1b23458a57
--- /dev/null
+++ b/drivers/rtc/rtc-lib.c
@@ -0,0 +1,113 @@
+/*
+ * rtc and date/time utility functions
+ *
+ * This code was ported from linux-3.15 kernel by Antony Pavlov.
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c and other bits
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <rtc.h>
+#include <linux/rtc.h>
+
+static const unsigned char rtc_days_in_month[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const unsigned short rtc_ydays[2][13] = {
+ /* Normal years */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+
+/*
+ * The number of days in the month.
+ */
+int rtc_month_days(unsigned int month, unsigned int year)
+{
+ return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
+}
+EXPORT_SYMBOL(rtc_month_days);
+
+/*
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+ unsigned int month, year;
+ int days;
+
+ days = time / 86400;
+ time -= (unsigned int) days * 86400;
+
+ /* day of the week, 1970-01-01 was a Thursday */
+ tm->tm_wday = (days + 4) % 7;
+
+ year = 1970 + days / 365;
+ days -= (year - 1970) * 365
+ + LEAPS_THRU_END_OF(year - 1)
+ - LEAPS_THRU_END_OF(1970 - 1);
+ if (days < 0) {
+ year -= 1;
+ days += 365 + is_leap_year(year);
+ }
+ tm->tm_year = year - 1900;
+ tm->tm_yday = days + 1;
+
+ for (month = 0; month < 11; month++) {
+ int newdays;
+
+ newdays = days - rtc_month_days(month, year);
+ if (newdays < 0)
+ break;
+ days = newdays;
+ }
+ tm->tm_mon = month;
+ tm->tm_mday = days + 1;
+
+ tm->tm_hour = time / 3600;
+ time -= tm->tm_hour * 3600;
+ tm->tm_min = time / 60;
+ tm->tm_sec = time - tm->tm_min * 60;
+
+ tm->tm_isdst = 0;
+}
+EXPORT_SYMBOL(rtc_time_to_tm);
+
+/*
+ * Does the rtc_time represent a valid date/time?
+ */
+int rtc_valid_tm(struct rtc_time *tm)
+{
+ if (tm->tm_year < 70
+ || ((unsigned)tm->tm_mon) >= 12
+ || tm->tm_mday < 1
+ || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
+ || ((unsigned)tm->tm_hour) >= 24
+ || ((unsigned)tm->tm_min) >= 60
+ || ((unsigned)tm->tm_sec) >= 60)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtc_valid_tm);
+
+/*
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+{
+ *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return 0;
+}
+EXPORT_SYMBOL(rtc_tm_to_time);
diff --git a/include/linux/bcd.h b/include/linux/bcd.h
new file mode 100644
index 0000000000..18fff11fb3
--- /dev/null
+++ b/include/linux/bcd.h
@@ -0,0 +1,22 @@
+#ifndef _BCD_H
+#define _BCD_H
+
+#include <linux/compiler.h>
+
+#define bcd2bin(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bcd2bin(x) : \
+ _bcd2bin(x))
+
+#define bin2bcd(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bin2bcd(x) : \
+ _bin2bcd(x))
+
+#define const_bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10)
+#define const_bin2bcd(x) ((((x) / 10) << 4) + (x) % 10)
+
+unsigned _bcd2bin(unsigned char val) __attribute_const__;
+unsigned char _bin2bcd(unsigned val) __attribute_const__;
+
+#endif /* _BCD_H */
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
new file mode 100644
index 0000000000..2dacb14239
--- /dev/null
+++ b/include/linux/rtc.h
@@ -0,0 +1,47 @@
+/*
+ * Generic RTC interface.
+ * This version contains the part of the user interface to the Real Time Clock
+ * service. It is used with both the legacy mc146818 and also EFI
+ * Struct rtc_time and first 12 ioctl by Paul Gortmaker, 1996 - separated out
+ * from <linux/mc146818rtc.h> to this file for 2.4 kernels.
+ *
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ */
+#ifndef _LINUX_RTC_H_
+#define _LINUX_RTC_H_
+
+#include <common.h>
+#include <linux/types.h>
+
+extern int rtc_month_days(unsigned int month, unsigned int year);
+extern int rtc_valid_tm(struct rtc_time *tm);
+extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time);
+extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm);
+
+struct rtc_class_ops;
+
+struct rtc_device {
+ struct device_d *dev;
+ struct device_d class_dev;
+ struct list_head list;
+
+ const struct rtc_class_ops *ops;
+};
+
+struct rtc_class_ops {
+ int (*read_time)(struct rtc_device *, struct rtc_time *);
+ int (*set_time)(struct rtc_device *, struct rtc_time *);
+};
+
+extern int rtc_register(struct rtc_device *rtcdev);
+
+extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
+extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
+
+static inline bool is_leap_year(unsigned int year)
+{
+ return (!(year % 4) && (year % 100)) || !(year % 400);
+}
+
+#endif /* _LINUX_RTC_H_ */
diff --git a/include/rtc.h b/include/rtc.h
index c2d8bcefed..e2414fb7b1 100644
--- a/include/rtc.h
+++ b/include/rtc.h
@@ -53,4 +53,6 @@ void to_tm (int, struct rtc_time *);
unsigned long mktime (unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int);
+extern struct rtc_device *rtc_lookup(const char *name);
+
#endif /* _RTC_H_ */
diff --git a/lib/Makefile b/lib/Makefile
index 77207dc773..1a345442aa 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,3 +1,4 @@
+obj-y += bcd.o
obj-$(CONFIG_BOOTSTRAP) += bootstrap/
obj-y += ctype.o
obj-y += rbtree.o
diff --git a/lib/bcd.c b/lib/bcd.c
new file mode 100644
index 0000000000..b072d50a90
--- /dev/null
+++ b/lib/bcd.c
@@ -0,0 +1,14 @@
+#include <linux/bcd.h>
+#include <module.h>
+
+unsigned _bcd2bin(unsigned char val)
+{
+ return (val & 0x0f) + (val >> 4) * 10;
+}
+EXPORT_SYMBOL(_bcd2bin);
+
+unsigned char _bin2bcd(unsigned val)
+{
+ return ((val / 10) << 4) + val % 10;
+}
+EXPORT_SYMBOL(_bin2bcd);