diff --git a/commands/hwclock.c b/commands/hwclock.c index a1f5293..6a0fe03 100644 --- a/commands/hwclock.c +++ b/commands/hwclock.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -92,9 +93,11 @@ char *env_name = NULL; int opt; int set = 0; + int ret; + int ntp_to_hw = 0; + char *ntpserver = NULL; - while ((opt = getopt(argc, argv, "f:s:e:")) > 0) { - int ret; + while ((opt = getopt(argc, argv, "f:s:e:n:")) > 0) { switch (opt) { case 'f': @@ -116,6 +119,10 @@ case 'e': env_name = optarg; break; + case 'n': + ntp_to_hw = 1; + ntpserver = optarg; + break; } } @@ -124,11 +131,29 @@ return PTR_ERR(r); if (set) { - rtc_set_time(r, &stm); - return 0; + return rtc_set_time(r, &stm); } - rtc_read_time(r, &tm); + if (ntp_to_hw) { + s64 now; + + if (!IS_ENABLED(CONFIG_NET_SNTP)) { + printf("SNTP support is disabled\n"); + return 1; + } + + now = sntp(ntpserver); + if (now < 0) + return now; + + rtc_time_to_tm(now, &stm); + printf("%s\n", time_str(&stm)); + return rtc_set_time(r, &stm); + } + + ret = rtc_read_time(r, &tm); + if (ret < 0) + return ret; if (env_name) { unsigned long time; @@ -138,9 +163,7 @@ 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); + printf("%s\n", time_str(&tm)); } return 0; @@ -150,6 +173,7 @@ 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 ("-n NTPSERVER\t", "set RTC from NTP server") BAREBOX_CMD_HELP_OPT ("-s ccyymmddHHMM[.SS]\t", "set time") BAREBOX_CMD_HELP_END diff --git a/common/date.c b/common/date.c index 6b6b7ab..129192e 100644 --- a/common/date.c +++ b/common/date.c @@ -148,3 +148,21 @@ )*60 + min /* now have minutes */ )*60 + sec; /* finally seconds */ } + +const char *time_str(struct rtc_time *tm) +{ + const char *weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + static char buf[128]; + + sprintf(buf, "%s %02d %s %4d %02d:%02d:%02d", + weekdays[tm->tm_wday], + tm->tm_mday, + months[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + return buf; +} diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 191ad97..7d18194 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -33,6 +33,9 @@ registers may add features such as NVRAM, a trickle charger for the RTC/NVRAM backup power, and alarms. +config RTC_DRV_ABRACON + tristate "Abracon RTCs" + endif # I2C config RTC_DRV_JZ4740 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1cc9bb8..68741c2 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -7,5 +7,6 @@ # Keep the list ordered. +obj-$(CONFIG_RTC_DRV_ABRACON) += rtc-abracon.o 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 index 356707b..8b047a6 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -46,7 +46,16 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) { - return rtc->ops->set_time(rtc, tm); + struct rtc_time time; + unsigned long secs; + + if (rtc_valid_tm(tm)) + return -EINVAL; + + rtc_tm_to_time(tm, &secs); + rtc_time_to_tm(secs, &time); + + return rtc->ops->set_time(rtc, &time); } EXPORT_SYMBOL(rtc_set_time); diff --git a/drivers/rtc/rtc-abracon.c b/drivers/rtc/rtc-abracon.c new file mode 100644 index 0000000..b3af990 --- /dev/null +++ b/drivers/rtc/rtc-abracon.c @@ -0,0 +1,126 @@ +/* + * 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; version 2. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct abracon { + struct rtc_device rtc; + struct i2c_client *client; +}; + +static inline struct abracon *to_abracon_priv(struct rtc_device *rtcdev) +{ + return container_of(rtcdev, struct abracon, rtc); +} + +static int abracon_get_time(struct rtc_device *rtcdev, struct rtc_time *t) +{ + struct abracon *abracon = to_abracon_priv(rtcdev); + struct i2c_client *client = abracon->client; + u8 cp[7] = {}; + u8 reg = 8; + struct i2c_msg msg[2] = {}; + int ret; + + msg[0].addr = client->addr; + msg[0].buf = ® + msg[0].len = 1; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = cp; + msg[1].len = 7; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) + return -EIO; + + t->tm_sec = bcd2bin(cp[0]); + t->tm_min = bcd2bin(cp[1]); + t->tm_hour = bcd2bin(cp[2]); + t->tm_mday = bcd2bin(cp[3]); + t->tm_wday = bcd2bin(cp[4]); + t->tm_mon = bcd2bin(cp[5]); + t->tm_year = bcd2bin(cp[6]) + 100; + + return 0; +} + +static int abracon_set_time(struct rtc_device *rtcdev, struct rtc_time *t) +{ + struct abracon *abracon = to_abracon_priv(rtcdev); + struct i2c_client *client = abracon->client; + u8 cp[8] = {}; + int ret; + + cp[0] = 8; + cp[1] = bin2bcd(t->tm_sec); + cp[2] = bin2bcd(t->tm_min); + cp[3] = bin2bcd(t->tm_hour); + cp[4] = bin2bcd(t->tm_mday); + cp[5] = bin2bcd(t->tm_wday); + cp[6] = bin2bcd(t->tm_mon); + cp[7] = bin2bcd(t->tm_year - 100); + + ret = i2c_master_send(client, cp, 8); + if (ret != 8) + return -EIO; + + return 0; +} + +static const struct rtc_class_ops ds13xx_rtc_ops = { + .read_time = abracon_get_time, + .set_time = abracon_set_time, +}; + +static int abracon_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct abracon *abracon; + int ret; + + abracon = xzalloc(sizeof(*abracon)); + + abracon->client = client; + + abracon->rtc.ops = &ds13xx_rtc_ops; + abracon->rtc.dev = dev; + + ret = rtc_register(&abracon->rtc); + + return ret; +}; + +static struct platform_device_id abracon_id[] = { + { "ab-rtcmc-32.768khz-eoz9-s3", 0 }, + { } +}; + +static struct driver_d abracon_driver = { + .name = "rtc-abracon", + .probe = abracon_probe, + .id_table = abracon_id, +}; + +static int __init abracon_init(void) +{ + return i2c_driver_register(&abracon_driver); +} +device_initcall(abracon_init); \ No newline at end of file diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index d78faa8..e2d561b 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,9 @@ */ enum ds_type { ds_1307, + ds_1337, ds_1338, + ds_1341, last_ds_type /* always last */ }; @@ -62,6 +65,28 @@ # define DS1307_BIT_SQWE 0x10 # define DS1307_BIT_RS1 0x02 # define DS1307_BIT_RS0 0x01 +#define DS1337_REG_CONTROL 0x0e +# define DS1337_BIT_nEOSC 0x80 +# define DS1339_BIT_BBSQI 0x20 +# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */ +# define DS1337_BIT_RS2 0x10 +# define DS1337_BIT_RS1 0x08 +# define DS1337_BIT_INTCN 0x04 +# define DS1337_BIT_A2IE 0x02 +# define DS1337_BIT_A1IE 0x01 +#define DS1340_REG_CONTROL 0x07 +# define DS1340_BIT_OUT 0x80 +# define DS1340_BIT_FT 0x40 +# define DS1340_BIT_CALIB_SIGN 0x20 +# define DS1340_M_CALIBRATION 0x1f +#define DS1340_REG_FLAG 0x09 +# define DS1340_BIT_OSF 0x80 +#define DS1337_REG_STATUS 0x0f +# define DS1337_BIT_OSF 0x80 +# define DS1341_BIT_ECLK 0x04 +# define DS1337_BIT_A2I 0x02 +# define DS1337_BIT_A1I 0x01 + struct ds1307 { struct rtc_device rtc; @@ -78,7 +103,9 @@ static struct platform_device_id ds1307_id[] = { { "ds1307", ds_1307 }, + { "ds1337", ds_1337 }, { "ds1338", ds_1338 }, + { "ds1341", ds_1341 }, { "pt7c4338", ds_1307 }, { } }; @@ -224,6 +251,16 @@ tmp = t->tm_year - 100; buf[DS1307_REG_YEAR] = bin2bcd(tmp); + switch (ds1307->type) { + case ds_1337: + case ds_1341: + buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; + break; + default: + break; + } + + dev_dbg(dev, "%s: %7ph\n", "write", buf); result = ds1307->write_block_data(ds1307->client, @@ -263,6 +300,61 @@ ds1307->read_block_data = ds1307_read_block_data; ds1307->write_block_data = ds1307_write_block_data; + + switch (ds1307->type) { + case ds_1337: + case ds_1341: + /* get registers that the "rtc" read below won't read... */ + tmp = ds1307->read_block_data(ds1307->client, + DS1337_REG_CONTROL, 2, buf); + + if (tmp != 2) { + dev_dbg(&client->dev, "read error %d\n", tmp); + err = -EIO; + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (ds1307->regs[0] & DS1337_BIT_nEOSC) + ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + + + /* + Make sure no alarm interrupts or square wave signals + are produced by the chip while we are in + bootloader. We do this by configuring the RTC to + generate alarm interrupts (thus disabling square + wave generation), but disabling each individual + alarm interrupt source + */ + ds1307->regs[0] |= DS1337_BIT_INTCN; + ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); + + i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, + ds1307->regs[0]); + + /* + For the above to be true, DS1341 also has to have + ECLK bit set to 0 + */ + if (ds1307->type == ds_1341) { + ds1307->regs[1] &= DS1341_BIT_ECLK; + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, + ds1307->regs[1]); + } + + + /* oscillator fault? clear flag, and warn */ + if (ds1307->regs[1] & DS1337_BIT_OSF) { + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, + ds1307->regs[1] & ~DS1337_BIT_OSF); + dev_warn(&client->dev, "SET TIME!\n"); + } + + default: + break; + } + read_rtc: /* read RTC registers */ tmp = ds1307->read_block_data(client, ds1307->offset, 8, buf); @@ -331,6 +423,8 @@ err = rtc_register(&ds1307->rtc); exit: + if (err) + free(ds1307); return err; } diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 1b23458..83d8045 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -90,6 +90,8 @@ { if (tm->tm_year < 70 || ((unsigned)tm->tm_mon) >= 12 + || tm->tm_wday < 0 + || tm->tm_wday > 6 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) || ((unsigned)tm->tm_hour) >= 24 diff --git a/include/rtc.h b/include/rtc.h index e2414fb..600dc46 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -55,4 +55,6 @@ extern struct rtc_device *rtc_lookup(const char *name); +const char *time_str(struct rtc_time *tm); + #endif /* _RTC_H_ */ diff --git a/include/sntp.h b/include/sntp.h new file mode 100644 index 0000000..bfcfcfa --- /dev/null +++ b/include/sntp.h @@ -0,0 +1,8 @@ +#ifndef __SNTP_H +#define __SNTP_H + +#include + +s64 sntp(const char *server); + +#endif /* __SNTP_H */ diff --git a/net/Kconfig b/net/Kconfig index a890492..f6ef0ce 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -26,4 +26,8 @@ bool prompt "dhcp support" +config NET_SNTP + bool + prompt "sntp support" + endif diff --git a/net/Makefile b/net/Makefile index 8d564e7..eb8d439 100644 --- a/net/Makefile +++ b/net/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_NET) += net.o obj-$(CONFIG_NET_NFS) += nfs.o obj-$(CONFIG_NET_DHCP) += dhcp.o +obj-$(CONFIG_NET_SNTP) += sntp.o obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_NET_RESOLV)+= dns.o obj-$(CONFIG_NET_NETCONSOLE) += netconsole.o diff --git a/net/sntp.c b/net/sntp.c new file mode 100644 index 0000000..577c859 --- /dev/null +++ b/net/sntp.c @@ -0,0 +1,171 @@ +/* + * 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; version 2. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNTP_PORT 123 +#define TIMEOUT 1 + +#define VERSION 4 /* version number */ + +#define M_RSVD 0 /* reserved */ +#define M_SACT 1 /* symmetric active */ +#define M_PASV 2 /* symmetric passive */ +#define M_CLNT 3 /* client */ +#define M_SERV 4 /* server */ +#define M_BCST 5 /* broadcast server */ +#define M_BCLN 6 /* broadcast client */ + +typedef uint64_t tstamp; /* NTP timestamp format */ +typedef uint32_t tdist; /* NTP short format */ + +struct ntp_packet { +#ifdef __LITTLE_ENDIAN /* reversed */ + unsigned int mode:3; /* mode */ + unsigned int version:3; /* version number */ + unsigned int leap:2; /* leap indicator */ +#else /* forward */ + unsigned int leap:2; /* leap indicator */ + unsigned int version:3; /* version number */ + unsigned int mode:3; /* mode */ +#endif + uint8_t stratum; /* stratum */ + uint8_t poll; /* poll interval */ + int8_t precision; /* precision */ + tdist rootdelay; /* root delay */ + tdist rootdisp; /* root dispersion */ + uint32_t refid; /* reference ID */ + tstamp reftime; /* reference time */ + tstamp org; /* origin timestamp */ + tstamp rec; /* receive timestamp */ + tstamp xmt; /* transmit timestamp */ +}; + +static IPaddr_t net_sntp_ip; + +#define SNTP_STATE_INIT 0 +#define SNTP_STATE_SUCCESS 1 + +static int sntp_state; + +static struct net_connection *sntp_con; + +static s64 curr_timestamp; + +static int sntp_send(void) +{ + struct ntp_packet *ntp = net_udp_get_payload(sntp_con); + + memset(ntp, 0, sizeof(struct ntp_packet)); + + ntp->version = VERSION; + ntp->mode = M_CLNT; + + return net_udp_send(sntp_con, sizeof(struct ntp_packet)); +} + +static void sntp_handler(void *ctx, char *pkt, unsigned len) +{ + IPaddr_t ip_addr; + struct iphdr *ip = net_eth_to_iphdr(pkt); + struct ntp_packet *ntp = + (struct ntp_packet *)net_eth_to_udp_payload(pkt); + + ip_addr = net_read_ip((void *)&ip->saddr); + if (ip_addr != net_sntp_ip) + return; + + len = net_eth_to_udplen(pkt); + if (len < sizeof(struct ntp_packet)) + return; + + pr_debug("received SNTP response\n"); + + if (ntp->version != VERSION) + return; + + if (ntp->mode != M_SERV) + return; + + curr_timestamp = (get_unaligned_be64(&ntp->xmt) >> 32) - 2208988800UL; + + sntp_state = SNTP_STATE_SUCCESS; +} + +s64 sntp(const char *server) +{ + int ret, repeat = 5; + u64 sntp_start; + + if (!server) + server = getenv("global.dhcp.ntpserver"); + if (!server) + return -EINVAL; + + net_sntp_ip = resolv(server); + if (!net_sntp_ip) { + printf("unknown host %s\n", server); + return 1; + } + + sntp_con = net_udp_new(net_sntp_ip, SNTP_PORT, sntp_handler, NULL); + if (IS_ERR(sntp_con)) { + ret = PTR_ERR(sntp_con); + goto out; + } + + sntp_start = get_time_ns(); + ret = sntp_send(); + if (ret) + goto out_unreg; + + sntp_state = SNTP_STATE_INIT; + + while (sntp_state == SNTP_STATE_INIT) { + if (ctrlc()) { + ret = -EINTR; + break; + } + + net_poll(); + + if (is_timeout(sntp_start, 1 * SECOND)) { + sntp_start = get_time_ns(); + ret = sntp_send(); + if (ret) + goto out_unreg; + repeat--; + if (!repeat) { + ret = -ETIMEDOUT; + goto out_unreg; + } + } + } + + net_unregister(sntp_con); + + return curr_timestamp; + +out_unreg: + net_unregister(sntp_con); +out: + return ret; +}