GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,141 @@
#
# RTC drivers configuration
#
menu "Real Time Clock"
config DM_RTC
bool "Enable Driver Model for RTC drivers"
depends on DM
select LIB_DATE
help
Enable drver model for real-time-clock drivers. The RTC uclass
then provides the rtc_get()/rtc_set() interface, delegating to
drivers to perform the actual functions. See rtc.h for a
description of the API.
config SPL_DM_RTC
bool "Enable Driver Model for RTC drivers in SPL"
depends on SPL_DM
help
Enable drver model for real-time-clock drivers. The RTC uclass
then provides the rtc_get()/rtc_set() interface, delegating to
drivers to perform the actual functions. See rtc.h for a
description of the API.
config TPL_DM_RTC
bool "Enable Driver Model for RTC drivers in TPL"
depends on TPL_DM
help
Enable drver model for real-time-clock drivers. The RTC uclass
then provides the rtc_get()/rtc_set() interface, delegating to
drivers to perform the actual functions. See rtc.h for a
description of the API.
config RTC_ENABLE_32KHZ_OUTPUT
bool "Enable RTC 32Khz output"
help
Some real-time clocks support the output of 32kHz square waves (such as ds3231),
the config symbol choose Real Time Clock device 32Khz output feature.
config RTC_PCF2127
bool "Enable PCF2127 driver"
depends on DM_RTC
help
The PCF2127 is a CMOS Real Time Clock (RTC) and calendar with an integrated
Temperature Compensated Crystal (Xtal) Oscillator (TCXO) and a 32.768 kHz quartz
crystal optimized for very high accuracy and very low power consumption. The PCF2127
has a selectable I2C-bus or SPI-bus, a backup battery switch-over circuit, a
programmable watchdog function, a timestamp function, and many other features.
config RTC_DS1307
bool "Enable DS1307 driver"
depends on DM_RTC
help
Support for Dallas Semiconductor (now Maxim) DS1307 and DS1338/9 and
compatible Real Time Clock devices.
config RTC_ISL1208
bool "Enable ISL1208 driver"
depends on DM_RTC
help
The Renesas (formerly Intersil) ISL1208 is a I2C Real Time Clock (RTC) and
calendar with automatic leap year correction, 2-byte battery backed SRAM,
automatic power switch-over, alarm function and 15 selectable frequency
outputs.
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_RV8803
bool "Enable RV8803 driver"
depends on DM_RTC
help
The Micro Crystal RV8803 is a high accuracy, ultra-low power I2C
Real Time Clock (RTC) with temperature compensation.
This driver supports reading and writing the RTC/calendar and
detects total power failures.
config RTC_RX8010SJ
bool "Enable RX8010SJ driver"
depends on DM_RTC
help
Support for Epson RX8010SJ Real Time Clock devices.
config RTC_RX8025
bool "Enable RX8025 driver"
help
Support for Epson RX8025 Real Time Clock devices.
config RTC_PL031
bool "Enable ARM AMBA PL031 RTC driver"
help
The ARM PrimeCell Real Time Clock (PL031) is an optional SoC
peripheral based on the Advanced Microcontroller Bus Architecture
(AMBA). It is emulated in QEMU virtual ARM machines.
config RTC_MV
bool "Enable Marvell RTC driver"
depends on DM_RTC
help
Enable Marvell RTC driver. This driver supports the rtc that is present
on some Marvell SoCs.
config RTC_S35392A
bool "Enable S35392A driver"
select BITREVERSE
help
Enable s35392a driver which provides rtc get and set function.
config RTC_MC146818
bool "Enable MC146818 driver"
help
This is a widely used real-time clock chip originally by Motorola
and now available from NXP. It includes a battery-backed real-time
clock with a wide array of features and 50 bytes of general-purpose,
battery-backed RAM. The driver supports access to the clock and RAM.
config RTC_M41T62
bool "Enable M41T62 driver"
help
Enable driver for ST's M41T62 compatible RTC devices (like RV-4162).
It is a serial (I2C) real-time clock (RTC) with alarm.
config RTC_STM32
bool "Enable STM32 RTC driver"
depends on DM_RTC
help
Enable STM32 RTC driver. This driver supports the rtc that is present
on some STM32 SoCs.
endmenu
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2001-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#ccflags-y += -DDEBUG
obj-$(CONFIG_$(SPL_TPL_)DM_RTC) += rtc-uclass.o
obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o
obj-y += rtc-lib.o
obj-$(CONFIG_RTC_DAVINCI) += davinci.o
obj-$(CONFIG_RTC_DS1302) += ds1302.o
obj-$(CONFIG_RTC_DS1306) += ds1306.o
obj-$(CONFIG_RTC_DS1307) += ds1307.o
obj-$(CONFIG_RTC_DS1338) += ds1307.o
obj-$(CONFIG_RTC_DS1339) += ds1307.o
obj-$(CONFIG_RTC_DS1337) += ds1337.o
obj-$(CONFIG_RTC_DS1374) += ds1374.o
obj-$(CONFIG_RTC_DS1388) += ds1337.o
obj-$(CONFIG_RTC_DS1556) += ds1556.o
obj-$(CONFIG_RTC_DS164x) += ds164x.o
obj-$(CONFIG_RTC_DS174x) += ds174x.o
obj-$(CONFIG_RTC_DS3231) += ds3231.o
obj-$(CONFIG_RTC_FTRTC010) += ftrtc010.o
obj-$(CONFIG_SANDBOX) += i2c_rtc_emul.o
obj-$(CONFIG_RTC_IMXDI) += imxdi.o
obj-$(CONFIG_RTC_ISL1208) += isl1208.o
obj-$(CONFIG_RTC_M41T11) += m41t11.o
obj-$(CONFIG_RTC_M41T60) += m41t60.o
obj-$(CONFIG_RTC_M41T62) += m41t62.o
obj-$(CONFIG_RTC_M41T94) += m41t94.o
obj-$(CONFIG_RTC_M48T35A) += m48t35ax.o
obj-$(CONFIG_RTC_MAX6900) += max6900.o
obj-$(CONFIG_RTC_MC13XXX) += mc13xxx-rtc.o
obj-$(CONFIG_RTC_MC146818) += mc146818.o
obj-$(CONFIG_RTC_MCP79411) += ds1307.o
obj-$(CONFIG_MCFRTC) += mcfrtc.o
obj-$(CONFIG_RTC_MK48T59) += mk48t59.o
obj-$(CONFIG_RTC_MV) += mvrtc.o
obj-$(CONFIG_RTC_MX27) += mx27rtc.o
obj-$(CONFIG_RTC_MXS) += mxsrtc.o
obj-$(CONFIG_RTC_PCF8563) += pcf8563.o
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_RV3029) += rv3029.o
obj-$(CONFIG_RTC_RV8803) += rv8803.o
obj-$(CONFIG_RTC_RX8025) += rx8025.o
obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o
obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o
obj-$(CONFIG_RTC_S35392A) += s35392a.o
obj-$(CONFIG_RTC_STM32) += stm32_rtc.o
obj-$(CONFIG_SANDBOX) += sandbox_rtc.o
obj-$(CONFIG_RTC_X1205) += x1205.o
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2010
* Reinhard Meyer, reinhard.meyer@emk-elektronik.de
*/
/*
* Date & Time support for the internal Real-time Timer
* of AT91SAM9260 and compatibles.
* Compatible with the LinuX rtc driver workaround:
* The RTT cannot be written to, but only reset.
* The actual time is the sum of RTT and one of
* the four GPBR registers.
*
* The at91sam9260 has 4 GPBR (0-3).
* For their typical use see at91_gpbr.h !
*
* make sure u-boot and kernel use the same GPBR !
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_rtt.h>
#include <asm/arch/at91_gpbr.h>
int rtc_get (struct rtc_time *tmp)
{
at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT;
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
ulong tim;
ulong tim2;
ulong off;
do {
tim = readl(&rtt->vr);
tim2 = readl(&rtt->vr);
} while (tim!=tim2);
off = readl(&gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]);
/* off==0 means time is invalid, but we ignore that */
rtc_to_tm(tim+off, tmp);
return 0;
}
int rtc_set (struct rtc_time *tmp)
{
at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT;
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
ulong tim;
tim = rtc_mktime(tmp);
/* clear alarm, set prescaler to 32768, clear counter */
writel(32768+AT91_RTT_RTTRST, &rtt->mr);
writel(~0, &rtt->ar);
writel(tim, &gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]);
/* wait for counter clear to happen, takes less than a 1/32768th second */
while (readl(&rtt->vr) != 0)
;
return 0;
}
void rtc_reset (void)
{
at91_rtt_t *rtt = (at91_rtt_t *) ATMEL_BASE_RTT;
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
/* clear alarm, set prescaler to 32768, clear counter */
writel(32768+AT91_RTT_RTTRST, &rtt->mr);
writel(~0, &rtt->ar);
writel(0, &gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]);
/* wait for counter clear to happen, takes less than a 1/32768th second */
while (readl(&rtt->vr) != 0)
;
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2011 DENX Software Engineering GmbH
* Heiko Schocher <hs@denx.de>
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/davinci_rtc.h>
int rtc_get(struct rtc_time *tmp)
{
struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE;
unsigned long sec, min, hour, mday, wday, mon_cent, year;
unsigned long status;
status = readl(&rtc->status);
if ((status & RTC_STATE_RUN) != RTC_STATE_RUN) {
printf("RTC doesn't run\n");
return -1;
}
if ((status & RTC_STATE_BUSY) == RTC_STATE_BUSY)
udelay(20);
sec = readl(&rtc->second);
min = readl(&rtc->minutes);
hour = readl(&rtc->hours);
mday = readl(&rtc->day);
wday = readl(&rtc->dotw);
mon_cent = readl(&rtc->month);
year = readl(&rtc->year);
debug("Get RTC year: %02lx mon/cent: %02lx mday: %02lx wday: %02lx "
"hr: %02lx min: %02lx sec: %02lx\n",
year, mon_cent, mday, wday,
hour, min, sec);
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
tmp->tm_hour = bcd2bin(hour & 0x3F);
tmp->tm_mday = bcd2bin(mday & 0x3F);
tmp->tm_mon = bcd2bin(mon_cent & 0x1F);
tmp->tm_year = bcd2bin(year) + 2000;
tmp->tm_wday = bcd2bin(wday & 0x07);
tmp->tm_yday = 0;
tmp->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);
return 0;
}
int rtc_set(struct rtc_time *tmp)
{
struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE;
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);
writel(bin2bcd(tmp->tm_year % 100), &rtc->year);
writel(bin2bcd(tmp->tm_mon), &rtc->month);
writel(bin2bcd(tmp->tm_wday), &rtc->dotw);
writel(bin2bcd(tmp->tm_mday), &rtc->day);
writel(bin2bcd(tmp->tm_hour), &rtc->hours);
writel(bin2bcd(tmp->tm_min), &rtc->minutes);
writel(bin2bcd(tmp->tm_sec), &rtc->second);
return 0;
}
void rtc_reset(void)
{
struct davinci_rtc *rtc = (struct davinci_rtc *)DAVINCI_RTC_BASE;
/* run RTC counter */
writel(0x01, &rtc->ctrl);
}
@@ -0,0 +1,328 @@
/*
* ds1302.c - Support for the Dallas Semiconductor DS1302 Timekeeping Chip
*
* Rex G. Feany <rfeany@zumanetworks.com>
*
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
/* GPP Pins */
#define DATA 0x200
#define SCLK 0x400
#define RST 0x800
/* Happy Fun Defines(tm) */
#define RESET rtc_go_low(RST), rtc_go_low(SCLK)
#define N_RESET rtc_go_high(RST), rtc_go_low(SCLK)
#define CLOCK_HIGH rtc_go_high(SCLK)
#define CLOCK_LOW rtc_go_low(SCLK)
#define DATA_HIGH rtc_go_high(DATA)
#define DATA_LOW rtc_go_low(DATA)
#define DATA_READ (GTREGREAD(GPP_VALUE) & DATA)
#undef RTC_DEBUG
#ifdef RTC_DEBUG
# define DPRINTF(x,args...) printf("ds1302: " x , ##args)
static inline void DUMP(const char *ptr, int num)
{
while (num--) printf("%x ", *ptr++);
printf("]\n");
}
#else
# define DPRINTF(x,args...)
# define DUMP(ptr, num)
#endif
/* time data format for DS1302 */
struct ds1302_st
{
unsigned char CH:1; /* clock halt 1=stop 0=start */
unsigned char sec10:3;
unsigned char sec:4;
unsigned char zero0:1;
unsigned char min10:3;
unsigned char min:4;
unsigned char fmt:1; /* 1=12 hour 0=24 hour */
unsigned char zero1:1;
unsigned char hr10:2; /* 10 (0-2) or am/pm (am/pm, 0-1) */
unsigned char hr:4;
unsigned char zero2:2;
unsigned char date10:2;
unsigned char date:4;
unsigned char zero3:3;
unsigned char month10:1;
unsigned char month:4;
unsigned char zero4:5;
unsigned char day:3; /* day of week */
unsigned char year10:4;
unsigned char year:4;
unsigned char WP:1; /* write protect 1=protect 0=unprot */
unsigned char zero5:7;
};
static int ds1302_initted=0;
/* Pin control */
static inline void
rtc_go_high(unsigned int mask)
{
unsigned int f = GTREGREAD(GPP_VALUE) | mask;
GT_REG_WRITE(GPP_VALUE, f);
}
static inline void
rtc_go_low(unsigned int mask)
{
unsigned int f = GTREGREAD(GPP_VALUE) & ~mask;
GT_REG_WRITE(GPP_VALUE, f);
}
static inline void
rtc_go_input(unsigned int mask)
{
unsigned int f = GTREGREAD(GPP_IO_CONTROL) & ~mask;
GT_REG_WRITE(GPP_IO_CONTROL, f);
}
static inline void
rtc_go_output(unsigned int mask)
{
unsigned int f = GTREGREAD(GPP_IO_CONTROL) | mask;
GT_REG_WRITE(GPP_IO_CONTROL, f);
}
/* Access data in RTC */
static void
write_byte(unsigned char b)
{
int i;
unsigned char mask=1;
for(i=0;i<8;i++) {
CLOCK_LOW; /* Lower clock */
(b&mask)?DATA_HIGH:DATA_LOW; /* set data */
udelay(1);
CLOCK_HIGH; /* latch data with rising clock */
udelay(1);
mask=mask<<1;
}
}
static unsigned char
read_byte(void)
{
int i;
unsigned char mask=1;
unsigned char b=0;
for(i=0;i<8;i++) {
CLOCK_LOW;
udelay(1);
if (DATA_READ) b|=mask; /* if this bit is high, set in b */
CLOCK_HIGH; /* clock out next bit */
udelay(1);
mask=mask<<1;
}
return b;
}
static void
read_ser_drv(unsigned char addr, unsigned char *buf, int count)
{
int i;
#ifdef RTC_DEBUG
char *foo = buf;
#endif
DPRINTF("READ 0x%x bytes @ 0x%x [ ", count, addr);
addr|=1; /* READ */
N_RESET;
udelay(4);
write_byte(addr);
rtc_go_input(DATA); /* Put gpp pin into input mode */
udelay(1);
for(i=0;i<count;i++) *(buf++)=read_byte();
RESET;
rtc_go_output(DATA);/* Reset gpp for output */
udelay(4);
DUMP(foo, count);
}
static void
write_ser_drv(unsigned char addr, unsigned char *buf, int count)
{
int i;
DPRINTF("WRITE 0x%x bytes @ 0x%x [ ", count, addr);
DUMP(buf, count);
addr&=~1; /* WRITE */
N_RESET;
udelay(4);
write_byte(addr);
for(i=0;i<count;i++) write_byte(*(buf++));
RESET;
udelay(4);
}
void
rtc_init(void)
{
struct ds1302_st bbclk;
unsigned char b;
int mod;
DPRINTF("init\n");
rtc_go_output(DATA|SCLK|RST);
/* disable write protect */
b = 0;
write_ser_drv(0x8e,&b,1);
/* enable trickle */
b = 0xa5; /* 1010.0101 */
write_ser_drv(0x90,&b,1);
/* read burst */
read_ser_drv(0xbe, (unsigned char *)&bbclk, 8);
/* Sanity checks */
mod = 0;
if (bbclk.CH) {
printf("ds1302: Clock was halted, starting clock\n");
bbclk.CH=0;
mod=1;
}
if (bbclk.fmt) {
printf("ds1302: Clock was in 12 hour mode, fixing\n");
bbclk.fmt=0;
mod=1;
}
if (bbclk.year>9) {
printf("ds1302: Year was corrupted, fixing\n");
bbclk.year10=100/10; /* 2000 - why not? ;) */
bbclk.year=0;
mod=1;
}
/* Write out the changes if needed */
if (mod) {
/* enable write protect */
bbclk.WP = 1;
write_ser_drv(0xbe,(unsigned char *)&bbclk,8);
} else {
/* Else just turn write protect on */
b = 0x80;
write_ser_drv(0x8e,&b,1);
}
DPRINTF("init done\n");
ds1302_initted=1;
}
void
rtc_reset(void)
{
if(!ds1302_initted) rtc_init();
/* TODO */
}
int
rtc_get(struct rtc_time *tmp)
{
int rel = 0;
struct ds1302_st bbclk;
if(!ds1302_initted) rtc_init();
read_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* read burst */
if (bbclk.CH) {
printf("ds1302: rtc_get: Clock was halted, clock probably "
"corrupt\n");
rel = -1;
}
tmp->tm_sec=10*bbclk.sec10+bbclk.sec;
tmp->tm_min=10*bbclk.min10+bbclk.min;
tmp->tm_hour=10*bbclk.hr10+bbclk.hr;
tmp->tm_wday=bbclk.day;
tmp->tm_mday=10*bbclk.date10+bbclk.date;
tmp->tm_mon=10*bbclk.month10+bbclk.month;
tmp->tm_year=10*bbclk.year10+bbclk.year + 1900;
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
DPRINTF("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 );
return rel;
}
int rtc_set(struct rtc_time *tmp)
{
struct ds1302_st bbclk;
unsigned char b=0;
if(!ds1302_initted) rtc_init();
DPRINTF("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);
memset(&bbclk,0,sizeof(bbclk));
bbclk.CH=0; /* dont halt */
bbclk.WP=1; /* write protect when we're done */
bbclk.sec10=tmp->tm_sec/10;
bbclk.sec=tmp->tm_sec%10;
bbclk.min10=tmp->tm_min/10;
bbclk.min=tmp->tm_min%10;
bbclk.hr10=tmp->tm_hour/10;
bbclk.hr=tmp->tm_hour%10;
bbclk.day=tmp->tm_wday;
bbclk.date10=tmp->tm_mday/10;
bbclk.date=tmp->tm_mday%10;
bbclk.month10=tmp->tm_mon/10;
bbclk.month=tmp->tm_mon%10;
tmp->tm_year -= 1900;
bbclk.year10=tmp->tm_year/10;
bbclk.year=tmp->tm_year%10;
write_ser_drv(0x8e,&b,1); /* disable write protect */
write_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* write burst */
return 0;
}
@@ -0,0 +1,437 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002 SIXNET, dge@sixnetio.com.
*
* (C) Copyright 2004, Li-Pro.Net <www.li-pro.net>
* Stephan Linz <linz@li-pro.net>
*/
/*
* Date & Time support for DS1306 RTC using SPI:
*
* - SXNI855T: it uses its own soft SPI here in this file
* - all other: use the external spi_xfer() function
* (see include/spi.h)
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <spi.h>
#define RTC_SECONDS 0x00
#define RTC_MINUTES 0x01
#define RTC_HOURS 0x02
#define RTC_DAY_OF_WEEK 0x03
#define RTC_DATE_OF_MONTH 0x04
#define RTC_MONTH 0x05
#define RTC_YEAR 0x06
#define RTC_SECONDS_ALARM0 0x07
#define RTC_MINUTES_ALARM0 0x08
#define RTC_HOURS_ALARM0 0x09
#define RTC_DAY_OF_WEEK_ALARM0 0x0a
#define RTC_SECONDS_ALARM1 0x0b
#define RTC_MINUTES_ALARM1 0x0c
#define RTC_HOURS_ALARM1 0x0d
#define RTC_DAY_OF_WEEK_ALARM1 0x0e
#define RTC_CONTROL 0x0f
#define RTC_STATUS 0x10
#define RTC_TRICKLE_CHARGER 0x11
#define RTC_USER_RAM_BASE 0x20
/* ************************************************************************* */
#ifdef CONFIG_SXNI855T /* !!! SHOULD BE CHANGED TO NEW CODE !!! */
static void soft_spi_send (unsigned char n);
static unsigned char soft_spi_read (void);
static void init_spi (void);
/*-----------------------------------------------------------------------
* Definitions
*/
#define PB_SPISCK 0x00000002 /* PB 30 */
#define PB_SPIMOSI 0x00000004 /* PB 29 */
#define PB_SPIMISO 0x00000008 /* PB 28 */
#define PB_SPI_CE 0x00010000 /* PB 15 */
/* ------------------------------------------------------------------------- */
/* read clock time from DS1306 and return it in *tmp */
int rtc_get (struct rtc_time *tmp)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
unsigned char spi_byte; /* Data Byte */
init_spi (); /* set port B for software SPI */
/* Now we can enable the DS1306 RTC */
immap->im_cpm.cp_pbdat |= PB_SPI_CE;
udelay (10);
/* Shift out the address (0) of the time in the Clock Chip */
soft_spi_send (0);
/* Put the clock readings into the rtc_time structure */
tmp->tm_sec = bcd2bin (soft_spi_read ()); /* Read seconds */
tmp->tm_min = bcd2bin (soft_spi_read ()); /* Read minutes */
/* Hours are trickier */
spi_byte = soft_spi_read (); /* Read Hours into temporary value */
if (spi_byte & 0x40) {
/* 12 hour mode bit is set (time is in 1-12 format) */
if (spi_byte & 0x20) {
/* since PM we add 11 to get 0-23 for hours */
tmp->tm_hour = (bcd2bin (spi_byte & 0x1F)) + 11;
} else {
/* since AM we subtract 1 to get 0-23 for hours */
tmp->tm_hour = (bcd2bin (spi_byte & 0x1F)) - 1;
}
} else {
/* Otherwise, 0-23 hour format */
tmp->tm_hour = (bcd2bin (spi_byte & 0x3F));
}
soft_spi_read (); /* Read and discard Day of week */
tmp->tm_mday = bcd2bin (soft_spi_read ()); /* Read Day of the Month */
tmp->tm_mon = bcd2bin (soft_spi_read ()); /* Read Month */
/* Read Year and convert to this century */
tmp->tm_year = bcd2bin (soft_spi_read ()) + 2000;
/* Now we can disable the DS1306 RTC */
immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */
udelay (10);
rtc_calc_weekday(tmp); /* Determine the day of week */
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);
return 0;
}
/* ------------------------------------------------------------------------- */
/* set clock time in DS1306 RTC and in MPC8xx RTC */
int rtc_set (struct rtc_time *tmp)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
init_spi (); /* set port B for software SPI */
/* Now we can enable the DS1306 RTC */
immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */
udelay (10);
/* First disable write protect in the clock chip control register */
soft_spi_send (0x8F); /* send address of the control register */
soft_spi_send (0x00); /* send control register contents */
/* Now disable the DS1306 to terminate the write */
immap->im_cpm.cp_pbdat &= ~PB_SPI_CE;
udelay (10);
/* Now enable the DS1306 to initiate a new write */
immap->im_cpm.cp_pbdat |= PB_SPI_CE;
udelay (10);
/* Next, send the address of the clock time write registers */
soft_spi_send (0x80); /* send address of the first time register */
/* Use Burst Mode to send all of the time data to the clock */
bin2bcd (tmp->tm_sec);
soft_spi_send (bin2bcd (tmp->tm_sec)); /* Send Seconds */
soft_spi_send (bin2bcd (tmp->tm_min)); /* Send Minutes */
soft_spi_send (bin2bcd (tmp->tm_hour)); /* Send Hour */
soft_spi_send (bin2bcd (tmp->tm_wday)); /* Send Day of the Week */
soft_spi_send (bin2bcd (tmp->tm_mday)); /* Send Day of Month */
soft_spi_send (bin2bcd (tmp->tm_mon)); /* Send Month */
soft_spi_send (bin2bcd (tmp->tm_year - 2000)); /* Send Year */
/* Now we can disable the Clock chip to terminate the burst write */
immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */
udelay (10);
/* Now we can enable the Clock chip to initiate a new write */
immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */
udelay (10);
/* First we Enable write protect in the clock chip control register */
soft_spi_send (0x8F); /* send address of the control register */
soft_spi_send (0x40); /* send out Control Register contents */
/* Now disable the DS1306 */
immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */
udelay (10);
/* Set standard MPC8xx clock to the same time so Linux will
* see the time even if it doesn't have a DS1306 clock driver.
* This helps with experimenting with standard kernels.
*/
{
ulong tim;
tim = rtc_mktime(tmp);
immap->im_sitk.sitk_rtck = KAPWR_KEY;
immap->im_sit.sit_rtc = tim;
}
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);
return 0;
}
/* ------------------------------------------------------------------------- */
/* Initialize Port B for software SPI */
static void init_spi (void)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
/* Force output pins to begin at logic 0 */
immap->im_cpm.cp_pbdat &= ~(PB_SPI_CE | PB_SPIMOSI | PB_SPISCK);
/* Set these 3 signals as outputs */
immap->im_cpm.cp_pbdir |= (PB_SPIMOSI | PB_SPI_CE | PB_SPISCK);
immap->im_cpm.cp_pbdir &= ~PB_SPIMISO; /* Make MISO pin an input */
udelay (10);
}
/* ------------------------------------------------------------------------- */
/* NOTE: soft_spi_send() assumes that the I/O lines are configured already */
static void soft_spi_send (unsigned char n)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
unsigned char bitpos; /* bit position to receive */
unsigned char i; /* Loop Control */
/* bit position to send, start with most significant bit */
bitpos = 0x80;
/* Send 8 bits to software SPI */
for (i = 0; i < 8; i++) { /* Loop for 8 bits */
immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */
if (n & bitpos)
immap->im_cpm.cp_pbdat |= PB_SPIMOSI; /* Set MOSI to 1 */
else
immap->im_cpm.cp_pbdat &= ~PB_SPIMOSI; /* Set MOSI to 0 */
udelay (10);
immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */
udelay (10);
bitpos >>= 1; /* Shift for next bit position */
}
}
/* ------------------------------------------------------------------------- */
/* NOTE: soft_spi_read() assumes that the I/O lines are configured already */
static unsigned char soft_spi_read (void)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
unsigned char spi_byte = 0; /* Return value, assume success */
unsigned char bitpos; /* bit position to receive */
unsigned char i; /* Loop Control */
/* bit position to receive, start with most significant bit */
bitpos = 0x80;
/* Read 8 bits here */
for (i = 0; i < 8; i++) { /* Do 8 bits in loop */
immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */
udelay (10);
if (immap->im_cpm.cp_pbdat & PB_SPIMISO) /* Get a bit of data */
spi_byte |= bitpos; /* Set data accordingly */
immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */
udelay (10);
bitpos >>= 1; /* Shift for next bit position */
}
return spi_byte; /* Return the byte read */
}
/* ------------------------------------------------------------------------- */
void rtc_reset (void)
{
return; /* nothing to do */
}
#else /* not CONFIG_SXNI855T */
/* ************************************************************************* */
static unsigned char rtc_read (unsigned char reg);
static void rtc_write (unsigned char reg, unsigned char val);
static struct spi_slave *slave;
/* read clock time from DS1306 and return it in *tmp */
int rtc_get (struct rtc_time *tmp)
{
unsigned char sec, min, hour, mday, wday, mon, year;
/*
* Assuming Vcc = 2.0V (lowest speed)
*
* REVISIT: If we add an rtc_init() function we can do this
* step just once.
*/
if (!slave) {
slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000,
SPI_MODE_3 | SPI_CS_HIGH);
if (!slave)
return;
}
if (spi_claim_bus(slave))
return;
sec = rtc_read (RTC_SECONDS);
min = rtc_read (RTC_MINUTES);
hour = rtc_read (RTC_HOURS);
mday = rtc_read (RTC_DATE_OF_MONTH);
wday = rtc_read (RTC_DAY_OF_WEEK);
mon = rtc_read (RTC_MONTH);
year = rtc_read (RTC_YEAR);
spi_release_bus(slave);
debug ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
debug ("Alarms[0]: wday: %02x hour: %02x min: %02x sec: %02x\n",
rtc_read (RTC_DAY_OF_WEEK_ALARM0),
rtc_read (RTC_HOURS_ALARM0),
rtc_read (RTC_MINUTES_ALARM0), rtc_read (RTC_SECONDS_ALARM0));
debug ("Alarms[1]: wday: %02x hour: %02x min: %02x sec: %02x\n",
rtc_read (RTC_DAY_OF_WEEK_ALARM1),
rtc_read (RTC_HOURS_ALARM1),
rtc_read (RTC_MINUTES_ALARM1), rtc_read (RTC_SECONDS_ALARM1));
tmp->tm_sec = bcd2bin (sec & 0x7F); /* convert Seconds */
tmp->tm_min = bcd2bin (min & 0x7F); /* convert Minutes */
/* convert Hours */
tmp->tm_hour = (hour & 0x40)
? ((hour & 0x20) /* 12 hour mode */
? bcd2bin (hour & 0x1F) + 11 /* PM */
: bcd2bin (hour & 0x1F) - 1 /* AM */
)
: bcd2bin (hour & 0x3F); /* 24 hour mode */
tmp->tm_mday = bcd2bin (mday & 0x3F); /* convert Day of the Month */
tmp->tm_mon = bcd2bin (mon & 0x1F); /* convert Month */
tmp->tm_year = bcd2bin (year) + 2000; /* convert Year */
tmp->tm_wday = bcd2bin (wday & 0x07) - 1; /* convert Day of the Week */
tmp->tm_yday = 0;
tmp->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);
return 0;
}
/* ------------------------------------------------------------------------- */
/* set clock time from *tmp in DS1306 RTC */
int rtc_set (struct rtc_time *tmp)
{
/* Assuming Vcc = 2.0V (lowest speed) */
if (!slave) {
slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000,
SPI_MODE_3 | SPI_CS_HIGH);
if (!slave)
return;
}
if (spi_claim_bus(slave))
return;
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);
rtc_write (RTC_SECONDS, bin2bcd (tmp->tm_sec));
rtc_write (RTC_MINUTES, bin2bcd (tmp->tm_min));
rtc_write (RTC_HOURS, bin2bcd (tmp->tm_hour));
rtc_write (RTC_DAY_OF_WEEK, bin2bcd (tmp->tm_wday + 1));
rtc_write (RTC_DATE_OF_MONTH, bin2bcd (tmp->tm_mday));
rtc_write (RTC_MONTH, bin2bcd (tmp->tm_mon));
rtc_write (RTC_YEAR, bin2bcd (tmp->tm_year - 2000));
spi_release_bus(slave);
}
/* ------------------------------------------------------------------------- */
/* reset the DS1306 */
void rtc_reset (void)
{
/* Assuming Vcc = 2.0V (lowest speed) */
if (!slave) {
slave = spi_setup_slave(0, CONFIG_SYS_SPI_RTC_DEVID, 600000,
SPI_MODE_3 | SPI_CS_HIGH);
if (!slave)
return;
}
if (spi_claim_bus(slave))
return;
/* clear the control register */
rtc_write (RTC_CONTROL, 0x00); /* 1st step: reset WP */
rtc_write (RTC_CONTROL, 0x00); /* 2nd step: reset 1Hz, AIE1, AIE0 */
/* reset all alarms */
rtc_write (RTC_SECONDS_ALARM0, 0x00);
rtc_write (RTC_SECONDS_ALARM1, 0x00);
rtc_write (RTC_MINUTES_ALARM0, 0x00);
rtc_write (RTC_MINUTES_ALARM1, 0x00);
rtc_write (RTC_HOURS_ALARM0, 0x00);
rtc_write (RTC_HOURS_ALARM1, 0x00);
rtc_write (RTC_DAY_OF_WEEK_ALARM0, 0x00);
rtc_write (RTC_DAY_OF_WEEK_ALARM1, 0x00);
spi_release_bus(slave);
}
/* ------------------------------------------------------------------------- */
static unsigned char rtc_read (unsigned char reg)
{
int ret;
ret = spi_w8r8(slave, reg);
return ret < 0 ? 0 : ret;
}
/* ------------------------------------------------------------------------- */
static void rtc_write (unsigned char reg, unsigned char val)
{
unsigned char dout[2]; /* SPI Output Data Bytes */
unsigned char din[2]; /* SPI Input Data Bytes */
dout[0] = 0x80 | reg;
dout[1] = val;
spi_xfer (slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END);
}
#endif /* end of code exclusion (see #ifdef CONFIG_SXNI855T above) */
@@ -0,0 +1,359 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001, 2002, 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Keith Outwater, keith_outwater@mvis.com`
* Steven Scholz, steven.scholz@imc-berlin.de
*/
/*
* Date & Time support (no alarms) for Dallas Semiconductor (now Maxim)
* DS1307 and DS1338/9 Real Time Clock (RTC).
*
* based on ds1337.c
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <i2c.h>
enum ds_type {
ds_1307,
ds_1337,
ds_1340,
m41t11,
mcp794xx,
};
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x00
#define RTC_MIN_REG_ADDR 0x01
#define RTC_HR_REG_ADDR 0x02
#define RTC_DAY_REG_ADDR 0x03
#define RTC_DATE_REG_ADDR 0x04
#define RTC_MON_REG_ADDR 0x05
#define RTC_YR_REG_ADDR 0x06
#define RTC_CTL_REG_ADDR 0x07
#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */
#define RTC_CTL_BIT_RS0 0x01 /* Rate select 0 */
#define RTC_CTL_BIT_RS1 0x02 /* Rate select 1 */
#define RTC_CTL_BIT_SQWE 0x10 /* Square Wave Enable */
#define RTC_CTL_BIT_OUT 0x80 /* Output Control */
/* MCP7941X-specific bits */
#define MCP7941X_BIT_ST 0x80
#define MCP7941X_BIT_VBATEN 0x08
#ifndef CONFIG_DM_RTC
/*---------------------------------------------------------------------*/
#undef DEBUG_RTC
#ifdef DEBUG_RTC
#define DEBUGR(fmt, args...) printf(fmt, ##args)
#else
#define DEBUGR(fmt, args...)
#endif
/*---------------------------------------------------------------------*/
#ifndef CONFIG_SYS_I2C_RTC_ADDR
# define CONFIG_SYS_I2C_RTC_ADDR 0x68
#endif
#if defined(CONFIG_RTC_DS1307) && (CONFIG_SYS_I2C_SPEED > 100000)
# error The DS1307 is specified only up to 100kHz!
#endif
static uchar rtc_read (uchar reg);
static void rtc_write (uchar reg, uchar val);
/*
* Get the current time from the RTC
*/
int rtc_get (struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon, year;
#ifdef CONFIG_RTC_MCP79411
read_rtc:
#endif
sec = rtc_read (RTC_SEC_REG_ADDR);
min = rtc_read (RTC_MIN_REG_ADDR);
hour = rtc_read (RTC_HR_REG_ADDR);
wday = rtc_read (RTC_DAY_REG_ADDR);
mday = rtc_read (RTC_DATE_REG_ADDR);
mon = rtc_read (RTC_MON_REG_ADDR);
year = rtc_read (RTC_YR_REG_ADDR);
DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
#ifdef CONFIG_RTC_DS1307
if (sec & RTC_SEC_BIT_CH) {
printf ("### Warning: RTC oscillator has stopped\n");
/* clear the CH flag */
rtc_write (RTC_SEC_REG_ADDR,
rtc_read (RTC_SEC_REG_ADDR) & ~RTC_SEC_BIT_CH);
rel = -1;
}
#endif
#ifdef CONFIG_RTC_MCP79411
/* make sure that the backup battery is enabled */
if (!(wday & MCP7941X_BIT_VBATEN)) {
rtc_write(RTC_DAY_REG_ADDR,
wday | MCP7941X_BIT_VBATEN);
}
/* clock halted? turn it on, so clock can tick. */
if (!(sec & MCP7941X_BIT_ST)) {
rtc_write(RTC_SEC_REG_ADDR, MCP7941X_BIT_ST);
printf("Started RTC\n");
goto read_rtc;
}
#endif
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon & 0x1F);
tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000);
tmp->tm_wday = bcd2bin ((wday - 1) & 0x07);
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
DEBUGR ("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);
return rel;
}
/*
* Set the RTC
*/
int rtc_set (struct rtc_time *tmp)
{
DEBUGR ("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 < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100));
rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon));
#ifdef CONFIG_RTC_MCP79411
rtc_write (RTC_DAY_REG_ADDR,
bin2bcd (tmp->tm_wday + 1) | MCP7941X_BIT_VBATEN);
#else
rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1));
#endif
rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday));
rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour));
rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min));
#ifdef CONFIG_RTC_MCP79411
rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec) | MCP7941X_BIT_ST);
#else
rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec));
#endif
return 0;
}
/*
* Reset the RTC. We setting the date back to 1970-01-01.
* We also enable the oscillator output on the SQW/OUT pin and program
* it for 32,768 Hz output. Note that according to the datasheet, turning
* on the square wave output increases the current drain on the backup
* battery to something between 480nA and 800nA.
*/
void rtc_reset (void)
{
rtc_write (RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */
rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS0);
}
/*
* Helper functions
*/
static
uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
#endif /* !CONFIG_DM_RTC */
#ifdef CONFIG_DM_RTC
static int ds1307_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
int ret;
uchar buf[7];
enum ds_type type = dev_get_driver_data(dev);
debug("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);
if (tm->tm_year < 1970 || tm->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
buf[RTC_YR_REG_ADDR] = bin2bcd(tm->tm_year % 100);
buf[RTC_MON_REG_ADDR] = bin2bcd(tm->tm_mon);
buf[RTC_DAY_REG_ADDR] = bin2bcd(tm->tm_wday + 1);
buf[RTC_DATE_REG_ADDR] = bin2bcd(tm->tm_mday);
buf[RTC_HR_REG_ADDR] = bin2bcd(tm->tm_hour);
buf[RTC_MIN_REG_ADDR] = bin2bcd(tm->tm_min);
buf[RTC_SEC_REG_ADDR] = bin2bcd(tm->tm_sec);
if (type == mcp794xx) {
buf[RTC_DAY_REG_ADDR] |= MCP7941X_BIT_VBATEN;
buf[RTC_SEC_REG_ADDR] |= MCP7941X_BIT_ST;
}
ret = dm_i2c_write(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
return 0;
}
static int ds1307_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
int ret;
uchar buf[7];
enum ds_type type = dev_get_driver_data(dev);
read_rtc:
ret = dm_i2c_read(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
if (type == ds_1307) {
if (buf[RTC_SEC_REG_ADDR] & RTC_SEC_BIT_CH) {
printf("### Warning: RTC oscillator has stopped\n");
/* clear the CH flag */
buf[RTC_SEC_REG_ADDR] &= ~RTC_SEC_BIT_CH;
dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR,
buf[RTC_SEC_REG_ADDR]);
return -1;
}
}
if (type == m41t11) {
/* clock halted? turn it on, so clock can tick. */
if (buf[RTC_SEC_REG_ADDR] & RTC_SEC_BIT_CH) {
buf[RTC_SEC_REG_ADDR] &= ~RTC_SEC_BIT_CH;
dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR,
MCP7941X_BIT_ST);
dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR,
buf[RTC_SEC_REG_ADDR]);
goto read_rtc;
}
}
if (type == mcp794xx) {
/* make sure that the backup battery is enabled */
if (!(buf[RTC_DAY_REG_ADDR] & MCP7941X_BIT_VBATEN)) {
dm_i2c_reg_write(dev, RTC_DAY_REG_ADDR,
buf[RTC_DAY_REG_ADDR] |
MCP7941X_BIT_VBATEN);
}
/* clock halted? turn it on, so clock can tick. */
if (!(buf[RTC_SEC_REG_ADDR] & MCP7941X_BIT_ST)) {
dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR,
MCP7941X_BIT_ST);
printf("Started RTC\n");
goto read_rtc;
}
}
tm->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F);
tm->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F);
tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x3F);
tm->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F);
tm->tm_mon = bcd2bin(buf[RTC_MON_REG_ADDR] & 0x1F);
tm->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) +
(bcd2bin(buf[RTC_YR_REG_ADDR]) >= 70 ?
1900 : 2000);
tm->tm_wday = bcd2bin((buf[RTC_DAY_REG_ADDR] - 1) & 0x07);
tm->tm_yday = 0;
tm->tm_isdst = 0;
debug("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;
}
static int ds1307_rtc_reset(struct udevice *dev)
{
int ret;
/* clear Clock Halt */
ret = dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR, 0x00);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 |
RTC_CTL_BIT_RS0);
if (ret < 0)
return ret;
return 0;
}
static int ds1307_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 ds1307_rtc_ops = {
.get = ds1307_rtc_get,
.set = ds1307_rtc_set,
.reset = ds1307_rtc_reset,
};
static const struct udevice_id ds1307_rtc_ids[] = {
{ .compatible = "dallas,ds1307", .data = ds_1307 },
{ .compatible = "dallas,ds1337", .data = ds_1337 },
{ .compatible = "dallas,ds1340", .data = ds_1340 },
{ .compatible = "microchip,mcp7941x", .data = mcp794xx },
{ .compatible = "st,m41t11", .data = m41t11 },
{ }
};
U_BOOT_DRIVER(rtc_ds1307) = {
.name = "rtc-ds1307",
.id = UCLASS_RTC,
.probe = ds1307_probe,
.of_match = ds1307_rtc_ids,
.ops = &ds1307_rtc_ops,
};
#endif /* CONFIG_DM_RTC */
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001-2008
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Keith Outwater, keith_outwater@mvis.com`
*/
/*
* Date & Time support (no alarms) for Dallas Semiconductor (now Maxim)
* DS1337 Real Time Clock (RTC).
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
/*
* RTC register addresses
*/
#if defined CONFIG_RTC_DS1337
#define RTC_SEC_REG_ADDR 0x0
#define RTC_MIN_REG_ADDR 0x1
#define RTC_HR_REG_ADDR 0x2
#define RTC_DAY_REG_ADDR 0x3
#define RTC_DATE_REG_ADDR 0x4
#define RTC_MON_REG_ADDR 0x5
#define RTC_YR_REG_ADDR 0x6
#define RTC_CTL_REG_ADDR 0x0e
#define RTC_STAT_REG_ADDR 0x0f
#define RTC_TC_REG_ADDR 0x10
#elif defined CONFIG_RTC_DS1388
#define RTC_SEC_REG_ADDR 0x1
#define RTC_MIN_REG_ADDR 0x2
#define RTC_HR_REG_ADDR 0x3
#define RTC_DAY_REG_ADDR 0x4
#define RTC_DATE_REG_ADDR 0x5
#define RTC_MON_REG_ADDR 0x6
#define RTC_YR_REG_ADDR 0x7
#define RTC_CTL_REG_ADDR 0x0c
#define RTC_STAT_REG_ADDR 0x0b
#define RTC_TC_REG_ADDR 0x0a
#endif
/*
* RTC control register bits
*/
#define RTC_CTL_BIT_A1IE 0x1 /* Alarm 1 interrupt enable */
#define RTC_CTL_BIT_A2IE 0x2 /* Alarm 2 interrupt enable */
#define RTC_CTL_BIT_INTCN 0x4 /* Interrupt control */
#define RTC_CTL_BIT_RS1 0x8 /* Rate select 1 */
#define RTC_CTL_BIT_RS2 0x10 /* Rate select 2 */
#define RTC_CTL_BIT_DOSC 0x80 /* Disable Oscillator */
/*
* RTC status register bits
*/
#define RTC_STAT_BIT_A1F 0x1 /* Alarm 1 flag */
#define RTC_STAT_BIT_A2F 0x2 /* Alarm 2 flag */
#define RTC_STAT_BIT_OSF 0x80 /* Oscillator stop flag */
static uchar rtc_read (uchar reg);
static void rtc_write (uchar reg, uchar val);
/*
* Get the current time from the RTC
*/
int rtc_get (struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon_cent, year, control, status;
control = rtc_read (RTC_CTL_REG_ADDR);
status = rtc_read (RTC_STAT_REG_ADDR);
sec = rtc_read (RTC_SEC_REG_ADDR);
min = rtc_read (RTC_MIN_REG_ADDR);
hour = rtc_read (RTC_HR_REG_ADDR);
wday = rtc_read (RTC_DAY_REG_ADDR);
mday = rtc_read (RTC_DATE_REG_ADDR);
mon_cent = rtc_read (RTC_MON_REG_ADDR);
year = rtc_read (RTC_YR_REG_ADDR);
/* No century bit, assume year 2000 */
#ifdef CONFIG_RTC_DS1388
mon_cent |= 0x80;
#endif
debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x control: %02x status: %02x\n",
year, mon_cent, mday, wday, hour, min, sec, control, status);
if (status & RTC_STAT_BIT_OSF) {
printf ("### Warning: RTC oscillator has stopped\n");
/* clear the OSF flag */
rtc_write (RTC_STAT_REG_ADDR,
rtc_read (RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_OSF);
rel = -1;
}
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon_cent & 0x1F);
tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900);
tmp->tm_wday = bcd2bin ((wday - 1) & 0x07);
tmp->tm_yday = 0;
tmp->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);
return rel;
}
/*
* Set the RTC
*/
int rtc_set (struct rtc_time *tmp)
{
uchar century;
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);
rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100));
century = (tmp->tm_year >= 2000) ? 0x80 : 0;
rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon) | century);
rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1));
rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday));
rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour));
rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min));
rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec));
return 0;
}
/*
* Reset the RTC. We also enable the oscillator output on the
* SQW/INTB* pin and program it for 32,768 Hz output. Note that
* according to the datasheet, turning on the square wave output
* increases the current drain on the backup battery from about
* 600 nA to 2uA. Define CONFIG_RTC_DS1337_NOOSC if you wish to turn
* off the OSC output.
*/
#ifdef CONFIG_RTC_DS1337_NOOSC
#define RTC_DS1337_RESET_VAL \
(RTC_CTL_BIT_INTCN | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2)
#else
#define RTC_DS1337_RESET_VAL (RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2)
#endif
void rtc_reset (void)
{
#ifdef CONFIG_RTC_DS1337
rtc_write (RTC_CTL_REG_ADDR, RTC_DS1337_RESET_VAL);
#elif defined CONFIG_RTC_DS1388
rtc_write(RTC_CTL_REG_ADDR, 0x0); /* hw default */
#endif
#ifdef CONFIG_RTC_DS1339_TCR_VAL
rtc_write (RTC_TC_REG_ADDR, CONFIG_RTC_DS1339_TCR_VAL);
#endif
#ifdef CONFIG_RTC_DS1388_TCR_VAL
rtc_write(RTC_TC_REG_ADDR, CONFIG_RTC_DS1388_TCR_VAL);
#endif
}
/*
* Helper functions
*/
static
uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001, 2002, 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Keith Outwater, keith_outwater@mvis.com`
* Steven Scholz, steven.scholz@imc-berlin.de
*/
/*
* Date & Time support (no alarms) for Dallas Semiconductor (now Maxim)
* DS1374 Real Time Clock (RTC).
*
* based on ds1337.c
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
/*---------------------------------------------------------------------*/
#undef DEBUG_RTC
#define DEBUG_RTC
#ifdef DEBUG_RTC
#define DEBUGR(fmt,args...) printf(fmt ,##args)
#else
#define DEBUGR(fmt,args...)
#endif
/*---------------------------------------------------------------------*/
#ifndef CONFIG_SYS_I2C_RTC_ADDR
# define CONFIG_SYS_I2C_RTC_ADDR 0x68
#endif
#if defined(CONFIG_RTC_DS1374) && (CONFIG_SYS_I2C_SPEED > 400000)
# error The DS1374 is specified up to 400kHz in fast mode!
#endif
/*
* RTC register addresses
*/
#define RTC_TOD_CNT_BYTE0_ADDR 0x00 /* TimeOfDay */
#define RTC_TOD_CNT_BYTE1_ADDR 0x01
#define RTC_TOD_CNT_BYTE2_ADDR 0x02
#define RTC_TOD_CNT_BYTE3_ADDR 0x03
#define RTC_WD_ALM_CNT_BYTE0_ADDR 0x04
#define RTC_WD_ALM_CNT_BYTE1_ADDR 0x05
#define RTC_WD_ALM_CNT_BYTE2_ADDR 0x06
#define RTC_CTL_ADDR 0x07 /* RTC-CoNTrol-register */
#define RTC_SR_ADDR 0x08 /* RTC-StatusRegister */
#define RTC_TCS_DS_ADDR 0x09 /* RTC-TrickleChargeSelect DiodeSelect-register */
#define RTC_CTL_BIT_AIE (1<<0) /* Bit 0 - Alarm Interrupt enable */
#define RTC_CTL_BIT_RS1 (1<<1) /* Bit 1/2 - Rate Select square wave output */
#define RTC_CTL_BIT_RS2 (1<<2) /* Bit 2/2 - Rate Select square wave output */
#define RTC_CTL_BIT_WDSTR (1<<3) /* Bit 3 - Watchdog Reset Steering */
#define RTC_CTL_BIT_BBSQW (1<<4) /* Bit 4 - Battery-Backed Square-Wave */
#define RTC_CTL_BIT_WD_ALM (1<<5) /* Bit 5 - Watchdoc/Alarm Counter Select */
#define RTC_CTL_BIT_WACE (1<<6) /* Bit 6 - Watchdog/Alarm Counter Enable WACE*/
#define RTC_CTL_BIT_EN_OSC (1<<7) /* Bit 7 - Enable Oscilator */
#define RTC_SR_BIT_AF 0x01 /* Bit 0 = Alarm Flag */
#define RTC_SR_BIT_OSF 0x80 /* Bit 7 - Osc Stop Flag */
const char RtcTodAddr[] = {
RTC_TOD_CNT_BYTE0_ADDR,
RTC_TOD_CNT_BYTE1_ADDR,
RTC_TOD_CNT_BYTE2_ADDR,
RTC_TOD_CNT_BYTE3_ADDR
};
static uchar rtc_read (uchar reg);
static void rtc_write(uchar reg, uchar val, bool set);
static void rtc_write_raw (uchar reg, uchar val);
/*
* Get the current time from the RTC
*/
int rtc_get (struct rtc_time *tm){
int rel = 0;
unsigned long time1, time2;
unsigned int limit;
unsigned char tmp;
unsigned int i;
/*
* Since the reads are being performed one byte at a time,
* there is a chance that a carry will occur during the read.
* To detect this, 2 reads are performed and compared.
*/
limit = 10;
do {
i = 4;
time1 = 0;
while (i--) {
tmp = rtc_read(RtcTodAddr[i]);
time1 = (time1 << 8) | (tmp & 0xff);
}
i = 4;
time2 = 0;
while (i--) {
tmp = rtc_read(RtcTodAddr[i]);
time2 = (time2 << 8) | (tmp & 0xff);
}
} while ((time1 != time2) && limit--);
if (time1 != time2) {
printf("can't get consistent time from rtc chip\n");
rel = -1;
}
DEBUGR ("Get RTC s since 1.1.1970: %ld\n", time1);
rtc_to_tm(time1, tm); /* To Gregorian Date */
if (rtc_read(RTC_SR_ADDR) & RTC_SR_BIT_OSF) {
printf ("### Warning: RTC oscillator has stopped\n");
rel = -1;
}
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 rel;
}
/*
* Set the RTC
*/
int rtc_set (struct rtc_time *tmp){
unsigned long time;
unsigned i;
DEBUGR ("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 < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
time = rtc_mktime(tmp);
DEBUGR ("Set RTC s since 1.1.1970: %ld (0x%02lx)\n", time, time);
/* write to RTC_TOD_CNT_BYTEn_ADDR */
for (i = 0; i <= 3; i++) {
rtc_write_raw(RtcTodAddr[i], (unsigned char)(time & 0xff));
time = time >> 8;
}
/* Start clock */
rtc_write(RTC_CTL_ADDR, RTC_CTL_BIT_EN_OSC, false);
return 0;
}
/*
* Reset the RTC. We setting the date back to 1970-01-01.
* We also enable the oscillator output on the SQW/OUT pin and program
* it for 32,768 Hz output. Note that according to the datasheet, turning
* on the square wave output increases the current drain on the backup
* battery to something between 480nA and 800nA.
*/
void rtc_reset (void){
/* clear status flags */
rtc_write(RTC_SR_ADDR, (RTC_SR_BIT_AF|RTC_SR_BIT_OSF), false); /* clearing OSF and AF */
/* Initialise DS1374 oriented to MPC8349E-ADS */
rtc_write (RTC_CTL_ADDR, (RTC_CTL_BIT_EN_OSC
|RTC_CTL_BIT_WACE
|RTC_CTL_BIT_AIE), false);/* start osc, disable WACE, clear AIE
- set to 0 */
rtc_write (RTC_CTL_ADDR, (RTC_CTL_BIT_WD_ALM
|RTC_CTL_BIT_WDSTR
|RTC_CTL_BIT_RS1
|RTC_CTL_BIT_RS2
|RTC_CTL_BIT_BBSQW), true);/* disable WD/ALM, WDSTR set to INT-pin,
set BBSQW and SQW to 32k
- set to 1 */
rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAC, true);
rtc_write(RTC_WD_ALM_CNT_BYTE1_ADDR, 0xDE, true);
rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAD, true);
}
/*
* Helper functions
*/
static uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write(uchar reg, uchar val, bool set)
{
if (set == true) {
val |= i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg);
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
} else {
val = i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg) & ~val;
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
}
static void rtc_write_raw (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* ARIO Data Networks, Inc. dchiu@ariodata.com
*
* modified for DS1556:
* Frank Panno <fpanno@delphintech.com>, Delphin Technology AG
*
* Based on MontaVista DS1743 code and U-Boot mc146818 code
*/
/*
* Date & Time support for the DS1556 RTC
*/
/*#define RTC_DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
#if defined(CONFIG_CMD_DATE)
static uchar rtc_read( unsigned int addr );
static void rtc_write( unsigned int addr, uchar val);
#define RTC_BASE ( CONFIG_SYS_NVRAM_SIZE + CONFIG_SYS_NVRAM_BASE_ADDR )
#define RTC_YEAR ( RTC_BASE + 0xf )
#define RTC_MONTH ( RTC_BASE + 0xe )
#define RTC_DAY_OF_MONTH ( RTC_BASE + 0xd )
#define RTC_DAY_OF_WEEK ( RTC_BASE + 0xc )
#define RTC_HOURS ( RTC_BASE + 0xb )
#define RTC_MINUTES ( RTC_BASE + 0xa )
#define RTC_SECONDS ( RTC_BASE + 0x9 )
#define RTC_CENTURY ( RTC_BASE + 0x8 )
#define RTC_CONTROLA RTC_CENTURY
#define RTC_CONTROLB RTC_SECONDS
#define RTC_CONTROLC RTC_BASE
#define RTC_CA_WRITE 0x80
#define RTC_CA_READ 0x40
#define RTC_CB_OSC_DISABLE 0x80
#define RTC_CC_BATTERY_FLAG 0x10
#define RTC_CC_FREQ_TEST 0x40
/* ------------------------------------------------------------------------- */
int rtc_get( struct rtc_time *tmp )
{
uchar sec, min, hour;
uchar mday, wday, mon, year;
int century;
uchar reg_a;
reg_a = rtc_read( RTC_CONTROLA );
/* lock clock registers for read */
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ ));
sec = rtc_read( RTC_SECONDS );
min = rtc_read( RTC_MINUTES );
hour = rtc_read( RTC_HOURS );
mday = rtc_read( RTC_DAY_OF_MONTH );
wday = rtc_read( RTC_DAY_OF_WEEK );
mon = rtc_read( RTC_MONTH );
year = rtc_read( RTC_YEAR );
century = rtc_read( RTC_CENTURY );
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ ));
#ifdef RTC_DEBUG
printf( "Get RTC year: %02x mon/cent: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, century, mon, mday, wday,
hour, min, sec );
#endif
tmp->tm_sec = bcd2bin( sec & 0x7F );
tmp->tm_min = bcd2bin( min & 0x7F );
tmp->tm_hour = bcd2bin( hour & 0x3F );
tmp->tm_mday = bcd2bin( mday & 0x3F );
tmp->tm_mon = bcd2bin( mon & 0x1F );
tmp->tm_wday = bcd2bin( wday & 0x07 );
/* glue year from century and year in century */
tmp->tm_year = bcd2bin( year ) +
( bcd2bin( century & 0x3F ) * 100 );
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
#ifdef RTC_DEBUG
printf( "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 );
#endif
return 0;
}
int rtc_set( struct rtc_time *tmp )
{
uchar reg_a;
#ifdef RTC_DEBUG
printf( "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);
#endif
/* lock clock registers for write */
reg_a = rtc_read( RTC_CONTROLA );
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE ));
rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon ));
rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday ));
rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday ));
rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour ));
rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min ));
rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec ));
/* break year up into century and year in century */
rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 ));
rtc_write( RTC_CENTURY, bin2bcd( tmp->tm_year / 100 ));
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE ));
return 0;
}
void rtc_reset (void)
{
uchar reg_a, reg_b, reg_c;
reg_a = rtc_read( RTC_CONTROLA );
reg_b = rtc_read( RTC_CONTROLB );
if ( reg_b & RTC_CB_OSC_DISABLE )
{
printf( "real-time-clock was stopped. Now starting...\n" );
reg_a |= RTC_CA_WRITE;
reg_b &= ~RTC_CB_OSC_DISABLE;
rtc_write( RTC_CONTROLA, reg_a );
rtc_write( RTC_CONTROLB, reg_b );
}
/* make sure read/write clock register bits are cleared */
reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ );
rtc_write( RTC_CONTROLA, reg_a );
reg_c = rtc_read( RTC_CONTROLC );
if (( reg_c & RTC_CC_BATTERY_FLAG ) == 0 )
printf( "RTC battery low. Clock setting may not be reliable.\n" );
}
/* ------------------------------------------------------------------------- */
static uchar rtc_read( unsigned int addr )
{
uchar val = *(volatile unsigned char*)(addr);
#ifdef RTC_DEBUG
printf( "rtc_read: %x:%x\n", addr, val );
#endif
return( val );
}
static void rtc_write( unsigned int addr, uchar val )
{
#ifdef RTC_DEBUG
printf( "rtc_write: %x:%x\n", addr, val );
#endif
*(volatile unsigned char*)(addr) = val;
}
#endif
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* ARIO Data Networks, Inc. dchiu@ariodata.com
*
* modified for DS164x:
* The LEOX team <team@leox.org>, http://www.leox.org
*
* Based on MontaVista DS1743 code and U-Boot mc146818 code
*/
/*
* Date & Time support for the DS164x RTC
*/
/* #define RTC_DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
static uchar rtc_read(unsigned int addr );
static void rtc_write(unsigned int addr, uchar val);
#define RTC_EPOCH 2000 /* century */
/*
* DS164x registers layout
*/
#define RTC_BASE ( CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE )
#define RTC_YEAR ( RTC_BASE + 0x07 )
#define RTC_MONTH ( RTC_BASE + 0x06 )
#define RTC_DAY_OF_MONTH ( RTC_BASE + 0x05 )
#define RTC_DAY_OF_WEEK ( RTC_BASE + 0x04 )
#define RTC_HOURS ( RTC_BASE + 0x03 )
#define RTC_MINUTES ( RTC_BASE + 0x02 )
#define RTC_SECONDS ( RTC_BASE + 0x01 )
#define RTC_CONTROL ( RTC_BASE + 0x00 )
#define RTC_CONTROLA RTC_CONTROL /* W=bit6, R=bit5 */
#define RTC_CA_WRITE 0x80
#define RTC_CA_READ 0x40
#define RTC_CONTROLB RTC_SECONDS /* OSC=bit7 */
#define RTC_CB_OSC_DISABLE 0x80
#define RTC_CONTROLC RTC_DAY_OF_WEEK /* FT=bit6 */
#define RTC_CC_FREQ_TEST 0x40
/* ------------------------------------------------------------------------- */
int rtc_get( struct rtc_time *tmp )
{
uchar sec, min, hour;
uchar mday, wday, mon, year;
uchar reg_a;
reg_a = rtc_read( RTC_CONTROLA );
/* lock clock registers for read */
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ ));
sec = rtc_read( RTC_SECONDS );
min = rtc_read( RTC_MINUTES );
hour = rtc_read( RTC_HOURS );
mday = rtc_read( RTC_DAY_OF_MONTH );
wday = rtc_read( RTC_DAY_OF_WEEK );
mon = rtc_read( RTC_MONTH );
year = rtc_read( RTC_YEAR );
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ ));
#ifdef RTC_DEBUG
printf( "Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday,
hour, min, sec );
#endif
tmp->tm_sec = bcd2bin( sec & 0x7F );
tmp->tm_min = bcd2bin( min & 0x7F );
tmp->tm_hour = bcd2bin( hour & 0x3F );
tmp->tm_mday = bcd2bin( mday & 0x3F );
tmp->tm_mon = bcd2bin( mon & 0x1F );
tmp->tm_wday = bcd2bin( wday & 0x07 );
/* glue year in century (2000) */
tmp->tm_year = bcd2bin( year ) + RTC_EPOCH;
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
#ifdef RTC_DEBUG
printf( "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 );
#endif
return 0;
}
int rtc_set( struct rtc_time *tmp )
{
uchar reg_a;
#ifdef RTC_DEBUG
printf( "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);
#endif
/* lock clock registers for write */
reg_a = rtc_read( RTC_CONTROLA );
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE ));
rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon ));
rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday ));
rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday ));
rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour ));
rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min ));
rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec ));
/* break year in century */
rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 ));
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE ));
return 0;
}
void rtc_reset (void)
{
uchar reg_a, reg_b;
reg_a = rtc_read( RTC_CONTROLA );
reg_b = rtc_read( RTC_CONTROLB );
if ( reg_b & RTC_CB_OSC_DISABLE )
{
printf( "real-time-clock was stopped. Now starting...\n" );
reg_a |= RTC_CA_WRITE;
reg_b &= ~RTC_CB_OSC_DISABLE;
rtc_write( RTC_CONTROLA, reg_a );
rtc_write( RTC_CONTROLB, reg_b );
}
/* make sure read/write clock register bits are cleared */
reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ );
rtc_write( RTC_CONTROLA, reg_a );
}
/* ------------------------------------------------------------------------- */
static uchar rtc_read( unsigned int addr )
{
uchar val = *(volatile unsigned char*)(addr);
#ifdef RTC_DEBUG
printf( "rtc_read: %x:%x\n", addr, val );
#endif
return( val );
}
static void rtc_write( unsigned int addr, uchar val )
{
#ifdef RTC_DEBUG
printf( "rtc_write: %x:%x\n", addr, val );
#endif
*(volatile unsigned char*)(addr) = val;
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001
* ARIO Data Networks, Inc. dchiu@ariodata.com
*
* Based on MontaVista DS1743 code and U-Boot mc146818 code
*/
/*
* Date & Time support for the DS174x RTC
*/
/*#define DEBUG*/
#include <common.h>
#include <command.h>
#include <rtc.h>
static uchar rtc_read( unsigned int addr );
static void rtc_write( unsigned int addr, uchar val);
#define RTC_BASE ( CONFIG_SYS_NVRAM_SIZE + CONFIG_SYS_NVRAM_BASE_ADDR )
#define RTC_YEAR ( RTC_BASE + 7 )
#define RTC_MONTH ( RTC_BASE + 6 )
#define RTC_DAY_OF_MONTH ( RTC_BASE + 5 )
#define RTC_DAY_OF_WEEK ( RTC_BASE + 4 )
#define RTC_HOURS ( RTC_BASE + 3 )
#define RTC_MINUTES ( RTC_BASE + 2 )
#define RTC_SECONDS ( RTC_BASE + 1 )
#define RTC_CENTURY ( RTC_BASE + 0 )
#define RTC_CONTROLA RTC_CENTURY
#define RTC_CONTROLB RTC_SECONDS
#define RTC_CONTROLC RTC_DAY_OF_WEEK
#define RTC_CA_WRITE 0x80
#define RTC_CA_READ 0x40
#define RTC_CB_OSC_DISABLE 0x80
#define RTC_CC_BATTERY_FLAG 0x80
#define RTC_CC_FREQ_TEST 0x40
/* ------------------------------------------------------------------------- */
int rtc_get( struct rtc_time *tmp )
{
uchar sec, min, hour;
uchar mday, wday, mon, year;
int century;
uchar reg_a;
reg_a = rtc_read( RTC_CONTROLA );
/* lock clock registers for read */
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_READ ));
sec = rtc_read( RTC_SECONDS );
min = rtc_read( RTC_MINUTES );
hour = rtc_read( RTC_HOURS );
mday = rtc_read( RTC_DAY_OF_MONTH );
wday = rtc_read( RTC_DAY_OF_WEEK );
mon = rtc_read( RTC_MONTH );
year = rtc_read( RTC_YEAR );
century = rtc_read( RTC_CENTURY );
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_READ ));
#ifdef RTC_DEBUG
printf( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon_cent, mday, wday,
hour, min, sec );
#endif
tmp->tm_sec = bcd2bin( sec & 0x7F );
tmp->tm_min = bcd2bin( min & 0x7F );
tmp->tm_hour = bcd2bin( hour & 0x3F );
tmp->tm_mday = bcd2bin( mday & 0x3F );
tmp->tm_mon = bcd2bin( mon & 0x1F );
tmp->tm_wday = bcd2bin( wday & 0x07 );
/* glue year from century and year in century */
tmp->tm_year = bcd2bin( year ) +
( bcd2bin( century & 0x3F ) * 100 );
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
#ifdef RTC_DEBUG
printf( "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 );
#endif
return 0;
}
int rtc_set( struct rtc_time *tmp )
{
uchar reg_a;
#ifdef RTC_DEBUG
printf( "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);
#endif
/* lock clock registers for write */
reg_a = rtc_read( RTC_CONTROLA );
rtc_write( RTC_CONTROLA, ( reg_a | RTC_CA_WRITE ));
rtc_write( RTC_MONTH, bin2bcd( tmp->tm_mon ));
rtc_write( RTC_DAY_OF_WEEK, bin2bcd( tmp->tm_wday ));
rtc_write( RTC_DAY_OF_MONTH, bin2bcd( tmp->tm_mday ));
rtc_write( RTC_HOURS, bin2bcd( tmp->tm_hour ));
rtc_write( RTC_MINUTES, bin2bcd( tmp->tm_min ));
rtc_write( RTC_SECONDS, bin2bcd( tmp->tm_sec ));
/* break year up into century and year in century */
rtc_write( RTC_YEAR, bin2bcd( tmp->tm_year % 100 ));
rtc_write( RTC_CENTURY, bin2bcd( tmp->tm_year / 100 ));
/* unlock clock registers after read */
rtc_write( RTC_CONTROLA, ( reg_a & ~RTC_CA_WRITE ));
return 0;
}
void rtc_reset (void)
{
uchar reg_a, reg_b, reg_c;
reg_a = rtc_read( RTC_CONTROLA );
reg_b = rtc_read( RTC_CONTROLB );
if ( reg_b & RTC_CB_OSC_DISABLE )
{
printf( "real-time-clock was stopped. Now starting...\n" );
reg_a |= RTC_CA_WRITE;
reg_b &= ~RTC_CB_OSC_DISABLE;
rtc_write( RTC_CONTROLA, reg_a );
rtc_write( RTC_CONTROLB, reg_b );
}
/* make sure read/write clock register bits are cleared */
reg_a &= ~( RTC_CA_WRITE | RTC_CA_READ );
rtc_write( RTC_CONTROLA, reg_a );
reg_c = rtc_read( RTC_CONTROLC );
if (( reg_c & RTC_CC_BATTERY_FLAG ) == 0 )
printf( "RTC battery low. Clock setting may not be reliable.\n" );
}
/* ------------------------------------------------------------------------- */
static uchar rtc_read( unsigned int addr )
{
uchar val = in8( addr );
#ifdef RTC_DEBUG
printf( "rtc_read: %x:%x\n", addr, val );
#endif
return( val );
}
static void rtc_write( unsigned int addr, uchar val )
{
#ifdef RTC_DEBUG
printf( "rtc_write: %x:%x\n", addr, val );
#endif
out8( addr, val );
}
@@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2006
* Markus Klotzbuecher, mk@denx.de
*
* (C) Copyright 2019 NXP
* Chuanhua Han <chuanhua.han@nxp.com>
*/
/*
* Date & Time support (no alarms) for Dallas Semiconductor (now Maxim)
* Extremly Accurate DS3231 Real Time Clock (RTC).
*
* copied from ds1337.c
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <i2c.h>
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x0
#define RTC_MIN_REG_ADDR 0x1
#define RTC_HR_REG_ADDR 0x2
#define RTC_DAY_REG_ADDR 0x3
#define RTC_DATE_REG_ADDR 0x4
#define RTC_MON_REG_ADDR 0x5
#define RTC_YR_REG_ADDR 0x6
#define RTC_CTL_REG_ADDR 0x0e
#define RTC_STAT_REG_ADDR 0x0f
/*
* RTC control register bits
*/
#define RTC_CTL_BIT_A1IE 0x1 /* Alarm 1 interrupt enable */
#define RTC_CTL_BIT_A2IE 0x2 /* Alarm 2 interrupt enable */
#define RTC_CTL_BIT_INTCN 0x4 /* Interrupt control */
#define RTC_CTL_BIT_RS1 0x8 /* Rate select 1 */
#define RTC_CTL_BIT_RS2 0x10 /* Rate select 2 */
#define RTC_CTL_BIT_DOSC 0x80 /* Disable Oscillator */
/*
* RTC status register bits
*/
#define RTC_STAT_BIT_A1F 0x1 /* Alarm 1 flag */
#define RTC_STAT_BIT_A2F 0x2 /* Alarm 2 flag */
#define RTC_STAT_BIT_OSF 0x80 /* Oscillator stop flag */
#define RTC_STAT_BIT_BB32KHZ 0x40 /* Battery backed 32KHz Output */
#define RTC_STAT_BIT_EN32KHZ 0x8 /* Enable 32KHz Output */
#if !CONFIG_IS_ENABLED(DM_RTC)
static uchar rtc_read (uchar reg);
static void rtc_write (uchar reg, uchar val);
/*
* Get the current time from the RTC
*/
int rtc_get (struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon_cent, year, control, status;
control = rtc_read (RTC_CTL_REG_ADDR);
status = rtc_read (RTC_STAT_REG_ADDR);
sec = rtc_read (RTC_SEC_REG_ADDR);
min = rtc_read (RTC_MIN_REG_ADDR);
hour = rtc_read (RTC_HR_REG_ADDR);
wday = rtc_read (RTC_DAY_REG_ADDR);
mday = rtc_read (RTC_DATE_REG_ADDR);
mon_cent = rtc_read (RTC_MON_REG_ADDR);
year = rtc_read (RTC_YR_REG_ADDR);
debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x control: %02x status: %02x\n",
year, mon_cent, mday, wday, hour, min, sec, control, status);
if (status & RTC_STAT_BIT_OSF) {
printf ("### Warning: RTC oscillator has stopped\n");
/* clear the OSF flag */
rtc_write (RTC_STAT_REG_ADDR,
rtc_read (RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_OSF);
rel = -1;
}
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon_cent & 0x1F);
tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900);
tmp->tm_wday = bcd2bin ((wday - 1) & 0x07);
tmp->tm_yday = 0;
tmp->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);
return rel;
}
/*
* Set the RTC
*/
int rtc_set (struct rtc_time *tmp)
{
uchar century;
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);
rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100));
century = (tmp->tm_year >= 2000) ? 0x80 : 0;
rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon) | century);
rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday + 1));
rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday));
rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour));
rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min));
rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec));
return 0;
}
/*
* Reset the RTC. We also enable the oscillator output on the
* SQW/INTB* pin and program it for 32,768 Hz output. Note that
* according to the datasheet, turning on the square wave output
* increases the current drain on the backup battery from about
* 600 nA to 2uA.
*/
void rtc_reset (void)
{
rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2);
}
/*
* Enable 32KHz output
*/
#ifdef CONFIG_RTC_ENABLE_32KHZ_OUTPUT
void rtc_enable_32khz_output(void)
{
rtc_write(RTC_STAT_REG_ADDR,
RTC_STAT_BIT_BB32KHZ | RTC_STAT_BIT_EN32KHZ);
}
#endif
/*
* Helper functions
*/
static
uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
#else
static int ds3231_rtc_get(struct udevice *dev, struct rtc_time *tmp)
{
uchar sec, min, hour, mday, wday, mon_cent, year, status;
status = dm_i2c_reg_read(dev, RTC_STAT_REG_ADDR);
sec = dm_i2c_reg_read(dev, RTC_SEC_REG_ADDR);
min = dm_i2c_reg_read(dev, RTC_MIN_REG_ADDR);
hour = dm_i2c_reg_read(dev, RTC_HR_REG_ADDR);
wday = dm_i2c_reg_read(dev, RTC_DAY_REG_ADDR);
mday = dm_i2c_reg_read(dev, RTC_DATE_REG_ADDR);
mon_cent = dm_i2c_reg_read(dev, RTC_MON_REG_ADDR);
year = dm_i2c_reg_read(dev, RTC_YR_REG_ADDR);
if (status & RTC_STAT_BIT_OSF) {
printf("### Warning: RTC oscillator has stopped\n");
/* clear the OSF flag */
dm_i2c_reg_write(dev, RTC_STAT_REG_ADDR,
dm_i2c_reg_read(dev, RTC_STAT_REG_ADDR)
& ~RTC_STAT_BIT_OSF);
return -EINVAL;
}
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
tmp->tm_hour = bcd2bin(hour & 0x3F);
tmp->tm_mday = bcd2bin(mday & 0x3F);
tmp->tm_mon = bcd2bin(mon_cent & 0x1F);
tmp->tm_year = bcd2bin(year) + ((mon_cent & 0x80) ? 2000 : 1900);
tmp->tm_wday = bcd2bin((wday - 1) & 0x07);
tmp->tm_yday = 0;
tmp->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);
return 0;
}
static int ds3231_rtc_set(struct udevice *dev, const struct rtc_time *tmp)
{
uchar century;
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);
dm_i2c_reg_write(dev, RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100));
century = (tmp->tm_year >= 2000) ? 0x80 : 0;
dm_i2c_reg_write(dev, RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon) | century);
dm_i2c_reg_write(dev, RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1));
dm_i2c_reg_write(dev, RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday));
dm_i2c_reg_write(dev, RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour));
dm_i2c_reg_write(dev, RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min));
dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec));
return 0;
}
static int ds3231_rtc_reset(struct udevice *dev)
{
int ret;
ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS2);
if (ret < 0)
return ret;
return 0;
}
static int ds3231_probe(struct udevice *dev)
{
i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS |
DM_I2C_CHIP_WR_ADDRESS);
return 0;
}
#ifdef CONFIG_RTC_ENABLE_32KHZ_OUTPUT
int rtc_enable_32khz_output(int busnum, int chip_addr)
{
int ret;
struct udevice *dev;
ret = i2c_get_chip_for_busnum(busnum, chip_addr, 1, &dev);
if (!ret) {
ret = dm_i2c_reg_write(dev, RTC_STAT_REG_ADDR,
RTC_STAT_BIT_BB32KHZ |
RTC_STAT_BIT_EN32KHZ);
}
return ret;
}
#endif
static const struct rtc_ops ds3231_rtc_ops = {
.get = ds3231_rtc_get,
.set = ds3231_rtc_set,
.reset = ds3231_rtc_reset,
};
static const struct udevice_id ds3231_rtc_ids[] = {
{ .compatible = "dallas,ds3231" },
{ .compatible = "dallas,ds3232" },
{ }
};
U_BOOT_DRIVER(rtc_ds3231) = {
.name = "rtc-ds3231",
.id = UCLASS_RTC,
.probe = ds3231_probe,
.of_match = ds3231_rtc_ids,
.ops = &ds3231_rtc_ops,
};
#endif
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Faraday FTRTC010 Real Time Clock
*
* (C) Copyright 2009 Faraday Technology
* Po-Yu Chuang <ratbert@faraday-tech.com>
*/
#include <config.h>
#include <common.h>
#include <rtc.h>
#include <asm/io.h>
struct ftrtc010 {
unsigned int sec; /* 0x00 */
unsigned int min; /* 0x04 */
unsigned int hour; /* 0x08 */
unsigned int day; /* 0x0c */
unsigned int alarm_sec; /* 0x10 */
unsigned int alarm_min; /* 0x14 */
unsigned int alarm_hour; /* 0x18 */
unsigned int record; /* 0x1c */
unsigned int cr; /* 0x20 */
unsigned int wsec; /* 0x24 */
unsigned int wmin; /* 0x28 */
unsigned int whour; /* 0x2c */
unsigned int wday; /* 0x30 */
unsigned int intr; /* 0x34 */
unsigned int div; /* 0x38 */
unsigned int rev; /* 0x3c */
};
/*
* RTC Control Register
*/
#define FTRTC010_CR_ENABLE (1 << 0)
#define FTRTC010_CR_INTERRUPT_SEC (1 << 1) /* per second irq */
#define FTRTC010_CR_INTERRUPT_MIN (1 << 2) /* per minute irq */
#define FTRTC010_CR_INTERRUPT_HR (1 << 3) /* per hour irq */
#define FTRTC010_CR_INTERRUPT_DAY (1 << 4) /* per day irq */
static struct ftrtc010 *rtc = (struct ftrtc010 *)CONFIG_FTRTC010_BASE;
static void ftrtc010_enable(void)
{
writel(FTRTC010_CR_ENABLE, &rtc->cr);
}
/*
* return current time in seconds
*/
static unsigned long ftrtc010_time(void)
{
unsigned long day;
unsigned long hour;
unsigned long minute;
unsigned long second;
unsigned long second2;
do {
second = readl(&rtc->sec);
day = readl(&rtc->day);
hour = readl(&rtc->hour);
minute = readl(&rtc->min);
second2 = readl(&rtc->sec);
} while (second != second2);
return day * 24 * 60 * 60 + hour * 60 * 60 + minute * 60 + second;
}
/*
* Get the current time from the RTC
*/
int rtc_get(struct rtc_time *tmp)
{
unsigned long now;
debug("%s(): record register: %x\n",
__func__, readl(&rtc->record));
#ifdef CONFIG_FTRTC010_PCLK
now = (ftrtc010_time() + readl(&rtc->record)) / RTC_DIV_COUNT;
#else /* CONFIG_FTRTC010_EXTCLK */
now = ftrtc010_time() + readl(&rtc->record);
#endif
rtc_to_tm(now, tmp);
return 0;
}
/*
* Set the RTC
*/
int rtc_set(struct rtc_time *tmp)
{
unsigned long new;
unsigned long now;
debug("%s(): DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
__func__,
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
new = rtc_mktime(tmp);
now = ftrtc010_time();
debug("%s(): write %lx to record register\n", __func__, new - now);
writel(new - now, &rtc->record);
return 0;
}
void rtc_reset(void)
{
debug("%s()\n", __func__);
ftrtc010_enable();
}
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Simulate an I2C real time clock
*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
/*
* This is a test driver. It starts off with the current time of the machine,
* but also supports setting the time, using an offset from the current
* clock. This driver is only intended for testing, not accurate
* time-keeping. It does not change the system time.
*/
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <os.h>
#include <rtc.h>
#include <asm/rtc.h>
#include <asm/test.h>
#ifdef DEBUG
#define debug_buffer print_buffer
#else
#define debug_buffer(x, ...)
#endif
/**
* struct sandbox_i2c_rtc_plat_data - platform data for the RTC
*
* @base_time: Base system time when RTC device was bound
* @offset: RTC offset from current system time
* @use_system_time: true to use system time, false to use @base_time
* @reg: Register values
*/
struct sandbox_i2c_rtc_plat_data {
long base_time;
long offset;
bool use_system_time;
u8 reg[REG_COUNT];
};
struct sandbox_i2c_rtc {
unsigned int offset_secs;
};
long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time,
int offset)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
long old_offset;
old_offset = plat->offset;
plat->use_system_time = use_system_time;
if (offset != -1)
plat->offset = offset;
return old_offset;
}
long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
long old_base_time;
old_base_time = plat->base_time;
if (base_time != -1)
plat->base_time = base_time;
return old_base_time;
}
static void reset_time(struct udevice *dev)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
struct rtc_time now;
os_localtime(&now);
plat->base_time = rtc_mktime(&now);
plat->offset = 0;
plat->use_system_time = true;
}
static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
struct rtc_time tm_now;
long now;
if (plat->use_system_time) {
os_localtime(&tm_now);
now = rtc_mktime(&tm_now);
} else {
now = plat->base_time;
}
rtc_to_tm(now + plat->offset, time);
return 0;
}
static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
struct rtc_time tm_now;
long now;
if (plat->use_system_time) {
os_localtime(&tm_now);
now = rtc_mktime(&tm_now);
} else {
now = plat->base_time;
}
plat->offset = rtc_mktime(time) - now;
return 0;
}
/* Update the current time in the registers */
static int sandbox_i2c_rtc_prepare_read(struct udevice *emul)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
struct rtc_time time;
int ret;
ret = sandbox_i2c_rtc_get(emul, &time);
if (ret)
return ret;
plat->reg[REG_SEC] = time.tm_sec;
plat->reg[REG_MIN] = time.tm_min;
plat->reg[REG_HOUR] = time.tm_hour;
plat->reg[REG_MDAY] = time.tm_mday;
plat->reg[REG_MON] = time.tm_mon;
plat->reg[REG_YEAR] = time.tm_year - 1900;
plat->reg[REG_WDAY] = time.tm_wday;
return 0;
}
static int sandbox_i2c_rtc_complete_write(struct udevice *emul)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
struct rtc_time time;
int ret;
time.tm_sec = plat->reg[REG_SEC];
time.tm_min = plat->reg[REG_MIN];
time.tm_hour = plat->reg[REG_HOUR];
time.tm_mday = plat->reg[REG_MDAY];
time.tm_mon = plat->reg[REG_MON];
time.tm_year = plat->reg[REG_YEAR] + 1900;
time.tm_wday = plat->reg[REG_WDAY];
ret = sandbox_i2c_rtc_set(emul, &time);
if (ret)
return ret;
return 0;
}
static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg,
int nmsgs)
{
struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
uint offset = 0;
int ret;
debug("\n%s\n", __func__);
ret = sandbox_i2c_rtc_prepare_read(emul);
if (ret)
return ret;
for (; nmsgs > 0; nmsgs--, msg++) {
int len;
u8 *ptr;
len = msg->len;
debug(" %s: msg->len=%d",
msg->flags & I2C_M_RD ? "read" : "write",
msg->len);
if (msg->flags & I2C_M_RD) {
debug(", offset %x, len %x: ", offset, len);
/* Read the register */
memcpy(msg->buf, plat->reg + offset, len);
memset(msg->buf + len, '\xff', msg->len - len);
debug_buffer(0, msg->buf, 1, msg->len, 0);
} else if (len >= 1) {
ptr = msg->buf;
offset = *ptr++ & (REG_COUNT - 1);
len--;
debug(", set offset %x: ", offset);
debug_buffer(0, msg->buf, 1, msg->len, 0);
/* Write the register */
memcpy(plat->reg + offset, ptr, len);
if (offset == REG_RESET)
reset_time(emul);
}
}
ret = sandbox_i2c_rtc_complete_write(emul);
if (ret)
return ret;
return 0;
}
struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = {
.xfer = sandbox_i2c_rtc_xfer,
};
static int sandbox_i2c_rtc_bind(struct udevice *dev)
{
reset_time(dev);
return 0;
}
static const struct udevice_id sandbox_i2c_rtc_ids[] = {
{ .compatible = "sandbox,i2c-rtc" },
{ }
};
U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = {
.name = "sandbox_i2c_rtc_emul",
.id = UCLASS_I2C_EMUL,
.of_match = sandbox_i2c_rtc_ids,
.bind = sandbox_i2c_rtc_bind,
.priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc),
.platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data),
.ops = &sandbox_i2c_rtc_emul_ops,
};
@@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009-2012 ADVANSEE
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
*
* Based on the Linux rtc-imxdi.c driver, which is:
* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2010 Orex Computed Radiography
*/
/*
* Date & Time support for Freescale i.MX DryIce RTC
*/
#include <common.h>
#include <command.h>
#include <linux/compat.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
/* DryIce Register Definitions */
struct imxdi_regs {
u32 dtcmr; /* Time Counter MSB Reg */
u32 dtclr; /* Time Counter LSB Reg */
u32 dcamr; /* Clock Alarm MSB Reg */
u32 dcalr; /* Clock Alarm LSB Reg */
u32 dcr; /* Control Reg */
u32 dsr; /* Status Reg */
u32 dier; /* Interrupt Enable Reg */
};
#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
#define DCR_TCE (1 << 3) /* Time Counter Enable */
#define DSR_WBF (1 << 10) /* Write Busy Flag */
#define DSR_WNF (1 << 9) /* Write Next Flag */
#define DSR_WCF (1 << 8) /* Write Complete Flag */
#define DSR_WEF (1 << 7) /* Write Error Flag */
#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
#define DSR_NVF (1 << 1) /* Non-Valid Flag */
#define DSR_SVF (1 << 0) /* Security Violation Flag */
#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
/* Driver Private Data */
struct imxdi_data {
struct imxdi_regs __iomem *regs;
int init_done;
};
static struct imxdi_data data;
/*
* This function attempts to clear the dryice write-error flag.
*
* A dryice write error is similar to a bus fault and should not occur in
* normal operation. Clearing the flag requires another write, so the root
* cause of the problem may need to be fixed before the flag can be cleared.
*/
static void clear_write_error(void)
{
int cnt;
puts("### Warning: RTC - Register write error!\n");
/* clear the write error flag */
__raw_writel(DSR_WEF, &data.regs->dsr);
/* wait for it to take effect */
for (cnt = 0; cnt < 1000; cnt++) {
if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0)
return;
udelay(10);
}
puts("### Error: RTC - Cannot clear write-error flag!\n");
}
/*
* Write a dryice register and wait until it completes.
*
* Use interrupt flags to determine when the write has completed.
*/
#define DI_WRITE_WAIT(val, reg) \
( \
/* do the register write */ \
__raw_writel((val), &data.regs->reg), \
\
di_write_wait((val), #reg) \
)
static int di_write_wait(u32 val, const char *reg)
{
int cnt;
int ret = 0;
int rc = 0;
/* wait for the write to finish */
for (cnt = 0; cnt < 100; cnt++) {
if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) {
ret = 1;
break;
}
udelay(10);
}
if (ret == 0)
printf("### Warning: RTC - Write-wait timeout "
"val = 0x%.8x reg = %s\n", val, reg);
/* check for write error */
if (__raw_readl(&data.regs->dsr) & DSR_WEF) {
clear_write_error();
rc = -1;
}
return rc;
}
/*
* Initialize dryice hardware
*/
static int di_init(void)
{
int rc = 0;
data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE;
/* mask all interrupts */
__raw_writel(0, &data.regs->dier);
/* put dryice into valid state */
if (__raw_readl(&data.regs->dsr) & DSR_NVF) {
rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr);
if (rc)
goto err;
}
/* initialize alarm */
rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr);
if (rc)
goto err;
rc = DI_WRITE_WAIT(0, dcalr);
if (rc)
goto err;
/* clear alarm flag */
if (__raw_readl(&data.regs->dsr) & DSR_CAF) {
rc = DI_WRITE_WAIT(DSR_CAF, dsr);
if (rc)
goto err;
}
/* the timer won't count if it has never been written to */
if (__raw_readl(&data.regs->dtcmr) == 0) {
rc = DI_WRITE_WAIT(0, dtcmr);
if (rc)
goto err;
}
/* start keeping time */
if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) {
rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr);
if (rc)
goto err;
}
data.init_done = 1;
return 0;
err:
return rc;
}
int rtc_get(struct rtc_time *tmp)
{
unsigned long now;
int rc = 0;
if (!data.init_done) {
rc = di_init();
if (rc)
goto err;
}
now = __raw_readl(&data.regs->dtcmr);
rtc_to_tm(now, tmp);
err:
return rc;
}
int rtc_set(struct rtc_time *tmp)
{
unsigned long now;
int rc;
if (!data.init_done) {
rc = di_init();
if (rc)
goto err;
}
now = rtc_mktime(tmp);
/* zero the fractional part first */
rc = DI_WRITE_WAIT(0, dtclr);
if (rc == 0)
rc = DI_WRITE_WAIT(now, dtcmr);
err:
return rc;
}
void rtc_reset(void)
{
di_init();
}
@@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008
* Tor Krill, Excito Elektronik i Skåne , tor@excito.com
*
* Modelled after the ds1337 driver
*/
/*
* Date & Time support (no alarms) for Intersil
* ISL1208 Real Time Clock (RTC).
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <i2c.h>
/*---------------------------------------------------------------------*/
#ifdef DEBUG_RTC
#define DEBUGR(fmt,args...) printf(fmt ,##args)
#else
#define DEBUGR(fmt,args...)
#endif
/*---------------------------------------------------------------------*/
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x0
#define RTC_MIN_REG_ADDR 0x1
#define RTC_HR_REG_ADDR 0x2
#define RTC_DATE_REG_ADDR 0x3
#define RTC_MON_REG_ADDR 0x4
#define RTC_YR_REG_ADDR 0x5
#define RTC_DAY_REG_ADDR 0x6
#define RTC_STAT_REG_ADDR 0x7
/*
* RTC control register bits
*/
/*
* RTC status register bits
*/
#define RTC_STAT_BIT_ARST 0x80 /* AUTO RESET ENABLE BIT */
#define RTC_STAT_BIT_XTOSCB 0x40 /* CRYSTAL OSCILLATOR ENABLE BIT */
#define RTC_STAT_BIT_WRTC 0x10 /* WRITE RTC ENABLE BIT */
#define RTC_STAT_BIT_ALM 0x04 /* ALARM BIT */
#define RTC_STAT_BIT_BAT 0x02 /* BATTERY BIT */
#define RTC_STAT_BIT_RTCF 0x01 /* REAL TIME CLOCK FAIL BIT */
/*
* Read an RTC register
*/
static int isl1208_rtc_read8(struct udevice *dev, unsigned int reg)
{
return dm_i2c_reg_read(dev, reg);
}
/*
* Write an RTC register
*/
static int isl1208_rtc_write8(struct udevice *dev, unsigned int reg, int val)
{
return dm_i2c_reg_write(dev, reg, val);
}
/*
* Get the current time from the RTC
*/
static int isl1208_rtc_get(struct udevice *dev, struct rtc_time *tmp)
{
int ret;
uchar buf[8], val;
ret = dm_i2c_read(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
if (buf[RTC_STAT_REG_ADDR] & RTC_STAT_BIT_RTCF) {
printf ("### Warning: RTC oscillator has stopped\n");
ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
val = val & ~(RTC_STAT_BIT_BAT | RTC_STAT_BIT_RTCF);
ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
}
tmp->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F);
tmp->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F);
tmp->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x3F);
tmp->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F);
tmp->tm_mon = bcd2bin(buf[RTC_MON_REG_ADDR] & 0x1F);
tmp->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) + 2000;
tmp->tm_wday = bcd2bin(buf[RTC_DAY_REG_ADDR] & 0x07);
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
DEBUGR ("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);
return 0;
}
/*
* Set the RTC
*/
static int isl1208_rtc_set(struct udevice *dev, const struct rtc_time *tmp)
{
int ret;
uchar val, buf[7];
DEBUGR ("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 || tmp->tm_year > 2099)
printf("WARNING: year should be between 2000 and 2099!\n");
/* enable write */
ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
val = val | RTC_STAT_BIT_WRTC;
ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
buf[RTC_YR_REG_ADDR] = bin2bcd(tmp->tm_year % 100);
buf[RTC_MON_REG_ADDR] = bin2bcd(tmp->tm_mon);
buf[RTC_DAY_REG_ADDR] = bin2bcd(tmp->tm_wday);
buf[RTC_DATE_REG_ADDR] = bin2bcd(tmp->tm_mday);
buf[RTC_HR_REG_ADDR] = bin2bcd(tmp->tm_hour) | 0x80; /* 24h clock */
buf[RTC_MIN_REG_ADDR] = bin2bcd(tmp->tm_min);
buf[RTC_SEC_REG_ADDR] = bin2bcd(tmp->tm_sec);
ret = dm_i2c_write(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
/* disable write */
ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
val = val & ~RTC_STAT_BIT_WRTC;
ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val));
if (ret < 0)
return ret;
return 0;
}
static int isl1208_rtc_reset(struct udevice *dev)
{
return 0;
}
static int isl1208_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 isl1208_rtc_ops = {
.get = isl1208_rtc_get,
.set = isl1208_rtc_set,
.reset = isl1208_rtc_reset,
.read8 = isl1208_rtc_read8,
.write8 = isl1208_rtc_write8,
};
static const struct udevice_id isl1208_rtc_ids[] = {
{ .compatible = "isil,isl1208" },
{ }
};
U_BOOT_DRIVER(rtc_isl1208) = {
.name = "rtc-isl1208",
.id = UCLASS_RTC,
.probe = isl1208_probe,
.of_match = isl1208_rtc_ids,
.ops = &isl1208_rtc_ops,
};
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* Andrew May, Viasat Inc, amay@viasat.com
*/
/*
* M41T11 Serial Access Timekeeper(R) SRAM
* can you believe a trademark on that?
*/
/* #define DEBUG 1 */
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
/*
I Don't have an example config file but this
is what should be done.
#define CONFIG_RTC_M41T11 1
#define CONFIG_SYS_I2C_RTC_ADDR 0x68
#if 0
#define CONFIG_SYS_M41T11_EXT_CENTURY_DATA
#else
#define CONFIG_SYS_M41T11_BASE_YEAR 2000
#endif
*/
/* ------------------------------------------------------------------------- */
/*
these are simple defines for the chip local to here so they aren't too
verbose
DAY/DATE aren't nice but that is how they are on the data sheet
*/
#define RTC_SEC_ADDR 0x0
#define RTC_MIN_ADDR 0x1
#define RTC_HOUR_ADDR 0x2
#define RTC_DAY_ADDR 0x3
#define RTC_DATE_ADDR 0x4
#define RTC_MONTH_ADDR 0x5
#define RTC_YEARS_ADDR 0x6
#define RTC_REG_CNT 7
#define RTC_CONTROL_ADDR 0x7
#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA
#define REG_CNT (RTC_REG_CNT+1)
/*
you only get 00-99 for the year we will asume you
want from the year 2000 if you don't set the config
*/
#ifndef CONFIG_SYS_M41T11_BASE_YEAR
#define CONFIG_SYS_M41T11_BASE_YEAR 2000
#endif
#else
/* we will store extra year info in byte 9*/
#define M41T11_YEAR_DATA 0x8
#define M41T11_YEAR_SIZE 1
#define REG_CNT (RTC_REG_CNT+1+M41T11_YEAR_SIZE)
#endif
#define M41T11_STORAGE_SZ (64-REG_CNT)
int rtc_get (struct rtc_time *tmp)
{
int rel = 0;
uchar data[RTC_REG_CNT];
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT);
if( data[RTC_SEC_ADDR] & 0x80 ){
printf( "m41t11 RTC Clock stopped!!!\n" );
rel = -1;
}
tmp->tm_sec = bcd2bin (data[RTC_SEC_ADDR] & 0x7F);
tmp->tm_min = bcd2bin (data[RTC_MIN_ADDR] & 0x7F);
tmp->tm_hour = bcd2bin (data[RTC_HOUR_ADDR] & 0x3F);
tmp->tm_mday = bcd2bin (data[RTC_DATE_ADDR] & 0x3F);
tmp->tm_mon = bcd2bin (data[RTC_MONTH_ADDR]& 0x1F);
#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA
tmp->tm_year = CONFIG_SYS_M41T11_BASE_YEAR
+ bcd2bin(data[RTC_YEARS_ADDR])
+ ((data[RTC_HOUR_ADDR]&0x40) ? 100 : 0);
#else
{
unsigned char cent;
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, &cent, M41T11_YEAR_SIZE);
if( !(data[RTC_HOUR_ADDR] & 0x80) ){
printf( "m41t11 RTC: cann't keep track of years without CEB set\n" );
rel = -1;
}
if( (cent & 0x1) != ((data[RTC_HOUR_ADDR]&0x40)>>7) ){
/*century flip store off new year*/
cent += 1;
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, &cent, M41T11_YEAR_SIZE);
}
tmp->tm_year =((int)cent*100)+bcd2bin(data[RTC_YEARS_ADDR]);
}
#endif
tmp->tm_wday = bcd2bin (data[RTC_DAY_ADDR] & 0x07);
tmp->tm_yday = 0;
tmp->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);
return rel;
}
int rtc_set (struct rtc_time *tmp)
{
uchar data[RTC_REG_CNT];
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);
data[RTC_SEC_ADDR] = bin2bcd(tmp->tm_sec) & 0x7F;/*just in case*/
data[RTC_MIN_ADDR] = bin2bcd(tmp->tm_min);
data[RTC_HOUR_ADDR] = bin2bcd(tmp->tm_hour) & 0x3F;/*handle cent stuff later*/
data[RTC_DATE_ADDR] = bin2bcd(tmp->tm_mday) & 0x3F;
data[RTC_MONTH_ADDR] = bin2bcd(tmp->tm_mon);
data[RTC_DAY_ADDR] = bin2bcd(tmp->tm_wday) & 0x07;
data[RTC_HOUR_ADDR] |= 0x80;/*we will always use CEB*/
data[RTC_YEARS_ADDR] = bin2bcd(tmp->tm_year%100);/*same thing either way*/
#ifndef CONFIG_SYS_M41T11_EXT_CENTURY_DATA
if( ((tmp->tm_year - CONFIG_SYS_M41T11_BASE_YEAR) > 200) ||
(tmp->tm_year < CONFIG_SYS_M41T11_BASE_YEAR) ){
printf( "m41t11 RTC setting year out of range!!need recompile\n" );
}
data[RTC_HOUR_ADDR] |= (tmp->tm_year - CONFIG_SYS_M41T11_BASE_YEAR) > 100 ? 0x40 : 0;
#else
{
unsigned char cent;
cent = tmp->tm_year ? tmp->tm_year / 100 : 0;
data[RTC_HOUR_ADDR] |= (cent & 0x1) ? 0x40 : 0;
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, &cent, M41T11_YEAR_SIZE);
}
#endif
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT);
return 0;
}
void rtc_reset (void)
{
unsigned char val;
/* clear all control & status registers */
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, 1);
val = val & 0x7F;/*make sure we are running*/
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, RTC_REG_CNT);
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1);
val = val & 0x3F;/*turn off freq test keep calibration*/
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1);
}
@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007
* Larry Johnson, lrj@acm.org
*
* based on rtc/m41t11.c which is ...
*
* (C) Copyright 2002
* Andrew May, Viasat Inc, amay@viasat.com
*/
/*
* STMicroelectronics M41T60 serial access real-time clock
*/
/* #define DEBUG 1 */
#include <common.h>
#include <command.h>
#include <env.h>
#include <rtc.h>
#include <i2c.h>
/*
* Convert between century and "century bits" (CB1 and CB0). These routines
* assume years are in the range 1900 - 2299.
*/
static unsigned char year2cb(unsigned const year)
{
if (year < 1900 || year >= 2300)
printf("M41T60 RTC: year %d out of range\n", year);
return (year / 100) & 0x3;
}
static unsigned cb2year(unsigned const cb)
{
return 1900 + 100 * ((cb + 1) & 0x3);
}
/*
* These are simple defines for the chip local to here so they aren't too
* verbose. DAY/DATE aren't nice but that is how they are on the data sheet.
*/
#define RTC_SEC 0x0
#define RTC_MIN 0x1
#define RTC_HOUR 0x2
#define RTC_DAY 0x3
#define RTC_DATE 0x4
#define RTC_MONTH 0x5
#define RTC_YEAR 0x6
#define RTC_REG_CNT 7
#define RTC_CTRL 0x7
#if defined(DEBUG)
static void rtc_dump(char const *const label)
{
uchar data[8];
if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) {
printf("I2C read failed in rtc_dump()\n");
return;
}
printf("RTC dump %s: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n",
label, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
}
#else
#define rtc_dump(label)
#endif
static uchar *rtc_validate(void)
{
/*
* This routine uses the OUT bit and the validity of the time values to
* determine whether there has been an initial power-up since the last
* time the routine was run. It assumes that the OUT bit is not being
* used for any other purpose.
*/
static const uchar daysInMonth[0x13] = {
0x00, 0x31, 0x29, 0x31, 0x30, 0x31, 0x30, 0x31,
0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x31, 0x30, 0x31
};
static uchar data[8];
uchar min, date, month, years;
rtc_dump("begin validate");
if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) {
printf("I2C read failed in rtc_validate()\n");
return 0;
}
/*
* If the OUT bit is "1", there has been a loss of power, so stop the
* oscillator so it can be "kick-started" as per data sheet.
*/
if (0x00 != (data[RTC_CTRL] & 0x80)) {
printf("M41T60 RTC clock lost power.\n");
data[RTC_SEC] = 0x80;
if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC, 1, data, 1)) {
printf("I2C write failed in rtc_validate()\n");
return 0;
}
}
/*
* If the oscillator is stopped or the date is invalid, then reset the
* OUT bit to "0", reset the date registers, and start the oscillator.
*/
min = data[RTC_MIN] & 0x7F;
date = data[RTC_DATE];
month = data[RTC_MONTH] & 0x3F;
years = data[RTC_YEAR];
if (0x59 < data[RTC_SEC] || 0x09 < (data[RTC_SEC] & 0x0F) ||
0x59 < min || 0x09 < (min & 0x0F) ||
0x23 < data[RTC_HOUR] || 0x09 < (data[RTC_HOUR] & 0x0F) ||
0x07 < data[RTC_DAY] || 0x00 == data[RTC_DAY] ||
0x12 < month ||
0x99 < years || 0x09 < (years & 0x0F) ||
daysInMonth[month] < date || 0x09 < (date & 0x0F) || 0x00 == date ||
(0x29 == date && 0x02 == month &&
((0x00 != (years & 0x03)) ||
(0x00 == years && 0x00 != (data[RTC_MONTH] & 0xC0))))) {
printf("Resetting M41T60 RTC clock.\n");
/*
* Set to 00:00:00 1900-01-01 (Monday)
*/
data[RTC_SEC] = 0x00;
data[RTC_MIN] &= 0x80; /* preserve OFIE bit */
data[RTC_HOUR] = 0x00;
data[RTC_DAY] = 0x02;
data[RTC_DATE] = 0x01;
data[RTC_MONTH] = 0xC1;
data[RTC_YEAR] = 0x00;
data[RTC_CTRL] &= 0x7F; /* reset OUT bit */
if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) {
printf("I2C write failed in rtc_validate()\n");
return 0;
}
}
return data;
}
int rtc_get(struct rtc_time *tmp)
{
uchar const *const data = rtc_validate();
if (!data)
return -1;
tmp->tm_sec = bcd2bin(data[RTC_SEC] & 0x7F);
tmp->tm_min = bcd2bin(data[RTC_MIN] & 0x7F);
tmp->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3F);
tmp->tm_mday = bcd2bin(data[RTC_DATE] & 0x3F);
tmp->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1F);
tmp->tm_year = cb2year(data[RTC_MONTH] >> 6) + bcd2bin(data[RTC_YEAR]);
tmp->tm_wday = bcd2bin(data[RTC_DAY] & 0x07) - 1;
tmp->tm_yday = 0;
tmp->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);
return 0;
}
int rtc_set(struct rtc_time *tmp)
{
uchar *const data = rtc_validate();
if (!data)
return -1;
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);
data[RTC_SEC] = (data[RTC_SEC] & 0x80) | (bin2bcd(tmp->tm_sec) & 0x7F);
data[RTC_MIN] = (data[RTC_MIN] & 0X80) | (bin2bcd(tmp->tm_min) & 0X7F);
data[RTC_HOUR] = bin2bcd(tmp->tm_hour) & 0x3F;
data[RTC_DATE] = bin2bcd(tmp->tm_mday) & 0x3F;
data[RTC_MONTH] = bin2bcd(tmp->tm_mon) & 0x1F;
data[RTC_YEAR] = bin2bcd(tmp->tm_year % 100);
data[RTC_MONTH] |= year2cb(tmp->tm_year) << 6;
data[RTC_DAY] = bin2bcd(tmp->tm_wday + 1) & 0x07;
if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, RTC_REG_CNT)) {
printf("I2C write failed in rtc_set()\n");
return -1;
}
return 0;
}
void rtc_reset(void)
{
uchar *const data = rtc_validate();
char const *const s = env_get("rtccal");
if (!data)
return;
rtc_dump("begin reset");
/*
* If environmental variable "rtccal" is present, it must be a hex value
* between 0x00 and 0x3F, inclusive. The five least-significan bits
* represent the calibration magnitude, and the sixth bit the sign bit.
* If these do not match the contents of the hardware register, that
* register is updated. The value 0x00 imples no correction. Consult
* the M41T60 documentation for further details.
*/
if (s) {
unsigned long const l = simple_strtoul(s, 0, 16);
if (l <= 0x3F) {
if ((data[RTC_CTRL] & 0x3F) != l) {
printf("Setting RTC calibration to 0x%02lX\n",
l);
data[RTC_CTRL] &= 0xC0;
data[RTC_CTRL] |= (uchar) l;
}
} else
printf("environment parameter \"rtccal\" not valid: "
"ignoring\n");
}
/*
* Turn off frequency test.
*/
data[RTC_CTRL] &= 0xBF;
if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_CTRL, 1, data + RTC_CTRL, 1)) {
printf("I2C write failed in rtc_reset()\n");
return;
}
rtc_dump("end reset");
}
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Lukasz Majewski, DENX Software Engineering, lukma@denx.de.
*
* (C) Copyright 2008
* Stefan Roese, DENX Software Engineering, sr@denx.de.
*
* based on a the Linux rtc-m41t80.c driver which is:
* Alexander Bigga <ab@mycable.de>, 2006 (c) mycable GmbH
*/
/*
* Date & Time support for STMicroelectronics M41T62
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <i2c.h>
#define M41T62_REG_SSEC 0
#define M41T62_REG_SEC 1
#define M41T62_REG_MIN 2
#define M41T62_REG_HOUR 3
#define M41T62_REG_WDAY 4
#define M41T62_REG_DAY 5
#define M41T62_REG_MON 6
#define M41T62_REG_YEAR 7
#define M41T62_REG_ALARM_MON 0xa
#define M41T62_REG_ALARM_DAY 0xb
#define M41T62_REG_ALARM_HOUR 0xc
#define M41T62_REG_ALARM_MIN 0xd
#define M41T62_REG_ALARM_SEC 0xe
#define M41T62_REG_FLAGS 0xf
#define M41T62_DATETIME_REG_SIZE (M41T62_REG_YEAR + 1)
#define M41T62_ALARM_REG_SIZE \
(M41T62_REG_ALARM_SEC + 1 - M41T62_REG_ALARM_MON)
#define M41T62_SEC_ST (1 << 7) /* ST: Stop Bit */
#define M41T62_ALMON_AFE (1 << 7) /* AFE: AF Enable Bit */
#define M41T62_ALMON_SQWE (1 << 6) /* SQWE: SQW Enable Bit */
#define M41T62_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */
#define M41T62_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */
#define M41T62_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */
#define M41T62_FEATURE_HT (1 << 0)
#define M41T62_FEATURE_BL (1 << 1)
#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */
static void m41t62_update_rtc_time(struct rtc_time *tm, u8 *buf)
{
debug("%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
tm->tm_sec = bcd2bin(buf[M41T62_REG_SEC] & 0x7f);
tm->tm_min = bcd2bin(buf[M41T62_REG_MIN] & 0x7f);
tm->tm_hour = bcd2bin(buf[M41T62_REG_HOUR] & 0x3f);
tm->tm_mday = bcd2bin(buf[M41T62_REG_DAY] & 0x3f);
tm->tm_wday = buf[M41T62_REG_WDAY] & 0x07;
tm->tm_mon = bcd2bin(buf[M41T62_REG_MON] & 0x1f);
/* assume 20YY not 19YY, and ignore the Century Bit */
/* U-Boot needs to add 1900 here */
tm->tm_year = bcd2bin(buf[M41T62_REG_YEAR]) + 100 + 1900;
debug("%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
}
static void m41t62_set_rtc_buf(const struct rtc_time *tm, u8 *buf)
{
debug("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);
/* Merge time-data and register flags into buf[0..7] */
buf[M41T62_REG_SSEC] = 0;
buf[M41T62_REG_SEC] =
bin2bcd(tm->tm_sec) | (buf[M41T62_REG_SEC] & ~0x7f);
buf[M41T62_REG_MIN] =
bin2bcd(tm->tm_min) | (buf[M41T62_REG_MIN] & ~0x7f);
buf[M41T62_REG_HOUR] =
bin2bcd(tm->tm_hour) | (buf[M41T62_REG_HOUR] & ~0x3f) ;
buf[M41T62_REG_WDAY] =
(tm->tm_wday & 0x07) | (buf[M41T62_REG_WDAY] & ~0x07);
buf[M41T62_REG_DAY] =
bin2bcd(tm->tm_mday) | (buf[M41T62_REG_DAY] & ~0x3f);
buf[M41T62_REG_MON] =
bin2bcd(tm->tm_mon) | (buf[M41T62_REG_MON] & ~0x1f);
/* assume 20YY not 19YY */
buf[M41T62_REG_YEAR] = bin2bcd(tm->tm_year % 100);
}
#ifdef CONFIG_DM_RTC
static int m41t62_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
u8 buf[M41T62_DATETIME_REG_SIZE];
int ret;
ret = dm_i2c_read(dev, 0, buf, sizeof(buf));
if (ret)
return ret;
m41t62_update_rtc_time(tm, buf);
return 0;
}
static int m41t62_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
u8 buf[M41T62_DATETIME_REG_SIZE];
int ret;
ret = dm_i2c_read(dev, 0, buf, sizeof(buf));
if (ret)
return ret;
m41t62_set_rtc_buf(tm, buf);
ret = dm_i2c_write(dev, 0, buf, sizeof(buf));
if (ret) {
printf("I2C write failed in %s()\n", __func__);
return ret;
}
return 0;
}
static int m41t62_rtc_reset(struct udevice *dev)
{
u8 val;
/*
* M41T82: Make sure HT (Halt Update) bit is cleared.
* This bit is 0 in M41T62 so its save to clear it always.
*/
int ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
val &= ~M41T80_ALHOUR_HT;
ret |= dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
return ret;
}
/*
* Make sure HT bit is cleared. This bit is set on entering battery backup
* mode, so do this before the first read access.
*/
static int m41t62_rtc_probe(struct udevice *dev)
{
return m41t62_rtc_reset(dev);
}
static const struct rtc_ops m41t62_rtc_ops = {
.get = m41t62_rtc_get,
.set = m41t62_rtc_set,
.reset = m41t62_rtc_reset,
};
static const struct udevice_id m41t62_rtc_ids[] = {
{ .compatible = "st,m41t62" },
{ .compatible = "st,m41t82" },
{ .compatible = "microcrystal,rv4162" },
{ }
};
U_BOOT_DRIVER(rtc_m41t62) = {
.name = "rtc-m41t62",
.id = UCLASS_RTC,
.of_match = m41t62_rtc_ids,
.ops = &m41t62_rtc_ops,
.probe = &m41t62_rtc_probe,
};
#else /* NON DM RTC code - will be removed */
int rtc_get(struct rtc_time *tm)
{
u8 buf[M41T62_DATETIME_REG_SIZE];
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE);
m41t62_update_rtc_time(tm, buf);
return 0;
}
int rtc_set(struct rtc_time *tm)
{
u8 buf[M41T62_DATETIME_REG_SIZE];
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE);
m41t62_set_rtc_buf(tm, buf);
if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, buf,
M41T62_DATETIME_REG_SIZE)) {
printf("I2C write failed in %s()\n", __func__);
return -1;
}
return 0;
}
void rtc_reset(void)
{
u8 val;
/*
* M41T82: Make sure HT (Halt Update) bit is cleared.
* This bit is 0 in M41T62 so its save to clear it always.
*/
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1);
val &= ~M41T80_ALHOUR_HT;
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1);
}
#endif /* CONFIG_DM_RTC */
@@ -0,0 +1,123 @@
/*
* Driver for ST M41T94 SPI RTC
*
* Taken from the Linux kernel drivier:
* Copyright (C) 2008 Kim B. Heino
*
* Adaptation for U-Boot:
* Copyright (C) 2009
* Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com>
*
* 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 <rtc.h>
#include <spi.h>
static struct spi_slave *slave;
#define M41T94_REG_SECONDS 0x01
#define M41T94_REG_MINUTES 0x02
#define M41T94_REG_HOURS 0x03
#define M41T94_REG_WDAY 0x04
#define M41T94_REG_DAY 0x05
#define M41T94_REG_MONTH 0x06
#define M41T94_REG_YEAR 0x07
#define M41T94_REG_HT 0x0c
#define M41T94_BIT_HALT 0x40
#define M41T94_BIT_STOP 0x80
#define M41T94_BIT_CB 0x40
#define M41T94_BIT_CEB 0x80
int rtc_set(struct rtc_time *tm)
{
u8 buf[8]; /* write cmd + 7 registers */
int ret;
if (!slave) {
slave = spi_setup_slave(CONFIG_M41T94_SPI_BUS,
CONFIG_M41T94_SPI_CS, 1000000,
SPI_MODE_3);
if (!slave)
return -1;
}
spi_claim_bus(slave);
buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */
buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec);
buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min);
buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour);
buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday);
buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1);
buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB;
if (tm->tm_year >= 100)
buf[M41T94_REG_HOURS] |= M41T94_BIT_CB;
buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100);
ret = spi_xfer(slave, 64, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
spi_release_bus(slave);
return ret;
}
int rtc_get(struct rtc_time *tm)
{
u8 buf[2];
int ret, hour;
if (!slave) {
slave = spi_setup_slave(CONFIG_M41T94_SPI_BUS,
CONFIG_M41T94_SPI_CS, 1000000,
SPI_MODE_3);
if (!slave)
return -1;
}
spi_claim_bus(slave);
/* clear halt update bit */
ret = spi_w8r8(slave, M41T94_REG_HT);
if (ret < 0)
return ret;
if (ret & M41T94_BIT_HALT) {
buf[0] = 0x80 | M41T94_REG_HT;
buf[1] = ret & ~M41T94_BIT_HALT;
spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
}
/* clear stop bit */
ret = spi_w8r8(slave, M41T94_REG_SECONDS);
if (ret < 0)
return ret;
if (ret & M41T94_BIT_STOP) {
buf[0] = 0x80 | M41T94_REG_SECONDS;
buf[1] = ret & ~M41T94_BIT_STOP;
spi_xfer(slave, 16, buf, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
}
tm->tm_sec = bcd2bin(spi_w8r8(slave, M41T94_REG_SECONDS));
tm->tm_min = bcd2bin(spi_w8r8(slave, M41T94_REG_MINUTES));
hour = spi_w8r8(slave, M41T94_REG_HOURS);
tm->tm_hour = bcd2bin(hour & 0x3f);
tm->tm_wday = bcd2bin(spi_w8r8(slave, M41T94_REG_WDAY)) - 1;
tm->tm_mday = bcd2bin(spi_w8r8(slave, M41T94_REG_DAY));
tm->tm_mon = bcd2bin(spi_w8r8(slave, M41T94_REG_MONTH)) - 1;
tm->tm_year = bcd2bin(spi_w8r8(slave, M41T94_REG_YEAR));
if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB))
tm->tm_year += 100;
spi_release_bus(slave);
return 0;
}
void rtc_reset(void)
{
/*
* Could not be tested as the reset pin is not wired on
* the sbc35-ag20 board
*/
}
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001
* Erik Theisen, Wave 7 Optics, etheisen@mindspring.com.
*/
/*
* Date & Time support for ST Electronics M48T35Ax RTC
*/
/*#define DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <config.h>
static uchar rtc_read (uchar reg);
static void rtc_write (uchar reg, uchar val);
/* ------------------------------------------------------------------------- */
int rtc_get (struct rtc_time *tmp)
{
uchar sec, min, hour, cent_day, date, month, year;
uchar ccr; /* Clock control register */
/* Lock RTC for read using clock control register */
ccr = rtc_read(0);
ccr = ccr | 0x40;
rtc_write(0, ccr);
sec = rtc_read (0x1);
min = rtc_read (0x2);
hour = rtc_read (0x3);
cent_day= rtc_read (0x4);
date = rtc_read (0x5);
month = rtc_read (0x6);
year = rtc_read (0x7);
/* UNLock RTC */
ccr = rtc_read(0);
ccr = ccr & 0xBF;
rtc_write(0, ccr);
debug ( "Get RTC year: %02x month: %02x date: %02x cent_day: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, month, date, cent_day,
hour, min, sec );
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (date & 0x3F);
tmp->tm_mon = bcd2bin (month & 0x1F);
tmp->tm_year = bcd2bin (year) + ((cent_day & 0x10) ? 2000 : 1900);
tmp->tm_wday = bcd2bin (cent_day & 0x07);
tmp->tm_yday = 0;
tmp->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);
return 0;
}
int rtc_set (struct rtc_time *tmp)
{
uchar ccr; /* Clock control register */
uchar century;
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);
/* Lock RTC for write using clock control register */
ccr = rtc_read(0);
ccr = ccr | 0x80;
rtc_write(0, ccr);
rtc_write (0x07, bin2bcd(tmp->tm_year % 100));
rtc_write (0x06, bin2bcd(tmp->tm_mon));
rtc_write (0x05, bin2bcd(tmp->tm_mday));
century = ((tmp->tm_year >= 2000) ? 0x10 : 0) | 0x20;
rtc_write (0x04, bin2bcd(tmp->tm_wday) | century);
rtc_write (0x03, bin2bcd(tmp->tm_hour));
rtc_write (0x02, bin2bcd(tmp->tm_min ));
rtc_write (0x01, bin2bcd(tmp->tm_sec ));
/* UNLock RTC */
ccr = rtc_read(0);
ccr = ccr & 0x7F;
rtc_write(0, ccr);
return 0;
}
void rtc_reset (void)
{
uchar val;
/* Clear all clock control registers */
rtc_write (0x0, 0x80); /* No Read Lock or calibration */
/* Clear stop bit */
val = rtc_read (0x1);
val &= 0x7f;
rtc_write(0x1, val);
/* Enable century / disable frequency test */
val = rtc_read (0x4);
val = (val & 0xBF) | 0x20;
rtc_write(0x4, val);
/* Clear write lock */
rtc_write(0x0, 0);
}
/* ------------------------------------------------------------------------- */
static uchar rtc_read (uchar reg)
{
return *(unsigned char *)
((CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE - 8) + reg);
}
static void rtc_write (uchar reg, uchar val)
{
*(unsigned char *)
((CONFIG_SYS_NVRAM_BASE_ADDR + CONFIG_SYS_NVRAM_SIZE - 8) + reg) = val;
}
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2004
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*/
/*
* Date & Time support for MAXIM MAX6900 RTC
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
#ifndef CONFIG_SYS_I2C_RTC_ADDR
#define CONFIG_SYS_I2C_RTC_ADDR 0x50
#endif
/* ------------------------------------------------------------------------- */
static uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
udelay(2500);
}
/* ------------------------------------------------------------------------- */
int rtc_get (struct rtc_time *tmp)
{
uchar sec, min, hour, mday, wday, mon, cent, year;
int retry = 1;
do {
sec = rtc_read (0x80);
min = rtc_read (0x82);
hour = rtc_read (0x84);
mday = rtc_read (0x86);
mon = rtc_read (0x88);
wday = rtc_read (0x8a);
year = rtc_read (0x8c);
cent = rtc_read (0x92);
/*
* Check for seconds rollover
*/
if ((sec != 59) || (rtc_read(0x80) == sec)){
retry = 0;
}
} while (retry);
debug ( "Get RTC year: %02x mon: %02x cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, cent, mday, wday,
hour, min, sec );
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon & 0x1F);
tmp->tm_year = bcd2bin (year) + bcd2bin(cent) * 100;
tmp->tm_wday = bcd2bin (wday & 0x07);
tmp->tm_yday = 0;
tmp->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);
return 0;
}
int rtc_set (struct rtc_time *tmp)
{
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);
rtc_write (0x9E, 0x00);
rtc_write (0x80, 0); /* Clear seconds to ensure no rollover */
rtc_write (0x92, bin2bcd(tmp->tm_year / 100));
rtc_write (0x8c, bin2bcd(tmp->tm_year % 100));
rtc_write (0x8a, bin2bcd(tmp->tm_wday));
rtc_write (0x88, bin2bcd(tmp->tm_mon));
rtc_write (0x86, bin2bcd(tmp->tm_mday));
rtc_write (0x84, bin2bcd(tmp->tm_hour));
rtc_write (0x82, bin2bcd(tmp->tm_min ));
rtc_write (0x80, bin2bcd(tmp->tm_sec ));
return 0;
}
void rtc_reset (void)
{
}
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2008, Guennadi Liakhovetski <lg@denx.de>
*/
#include <common.h>
#include <rtc.h>
#include <spi.h>
#include <power/pmic.h>
#include <fsl_pmic.h>
int rtc_get(struct rtc_time *rtc)
{
u32 day1, day2, time;
int tim, i = 0;
struct pmic *p = pmic_get("FSL_PMIC");
int ret;
if (!p)
return -1;
do {
ret = pmic_reg_read(p, REG_RTC_DAY, &day1);
if (ret < 0)
return -1;
ret = pmic_reg_read(p, REG_RTC_TIME, &time);
if (ret < 0)
return -1;
ret = pmic_reg_read(p, REG_RTC_DAY, &day2);
if (ret < 0)
return -1;
} while (day1 != day2 && i++ < 3);
tim = day1 * 86400 + time;
rtc_to_tm(tim, rtc);
rtc->tm_yday = 0;
rtc->tm_isdst = 0;
return 0;
}
int rtc_set(struct rtc_time *rtc)
{
u32 time, day;
struct pmic *p = pmic_get("FSL_PMIC");
if (!p)
return -1;
time = rtc_mktime(rtc);
day = time / 86400;
time %= 86400;
pmic_reg_write(p, REG_RTC_DAY, day);
pmic_reg_write(p, REG_RTC_TIME, time);
return 0;
}
void rtc_reset(void)
{
}
@@ -0,0 +1,308 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001
* Denis Peter MPL AG Switzerland. d.peter@mpl.ch
*/
/*
* Date & Time support for the MC146818 (PIXX4) RTC
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#if defined(CONFIG_X86) || defined(CONFIG_MALTA)
#include <asm/io.h>
#define in8(p) inb(p)
#define out8(p, v) outb(v, p)
#endif
/* Set this to 1 to clear the CMOS RAM */
#define CLEAR_CMOS 0
#define RTC_PORT_MC146818 CONFIG_SYS_ISA_IO_BASE_ADDRESS + 0x70
#define RTC_SECONDS 0x00
#define RTC_SECONDS_ALARM 0x01
#define RTC_MINUTES 0x02
#define RTC_MINUTES_ALARM 0x03
#define RTC_HOURS 0x04
#define RTC_HOURS_ALARM 0x05
#define RTC_DAY_OF_WEEK 0x06
#define RTC_DATE_OF_MONTH 0x07
#define RTC_MONTH 0x08
#define RTC_YEAR 0x09
#define RTC_CONFIG_A 0x0a
#define RTC_CONFIG_B 0x0b
#define RTC_CONFIG_C 0x0c
#define RTC_CONFIG_D 0x0d
#define RTC_REG_SIZE 0x80
#define RTC_CONFIG_A_REF_CLCK_32KHZ (1 << 5)
#define RTC_CONFIG_A_RATE_1024HZ 6
#define RTC_CONFIG_B_24H (1 << 1)
#define RTC_CONFIG_D_VALID_RAM_AND_TIME 0x80
static int mc146818_read8(int reg)
{
#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR
return in8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg);
#else
int ofs = 0;
if (reg >= 128) {
ofs = 2;
reg -= 128;
}
out8(RTC_PORT_MC146818 + ofs, reg);
return in8(RTC_PORT_MC146818 + ofs + 1);
#endif
}
static void mc146818_write8(int reg, uchar val)
{
#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR
out8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg, val);
#else
int ofs = 0;
if (reg >= 128) {
ofs = 2;
reg -= 128;
}
out8(RTC_PORT_MC146818 + ofs, reg);
out8(RTC_PORT_MC146818 + ofs + 1, val);
#endif
}
static int mc146818_get(struct rtc_time *tmp)
{
uchar sec, min, hour, mday, wday __attribute__((unused)),mon, year;
/* here check if rtc can be accessed */
while ((mc146818_read8(RTC_CONFIG_A) & 0x80) == 0x80)
;
sec = mc146818_read8(RTC_SECONDS);
min = mc146818_read8(RTC_MINUTES);
hour = mc146818_read8(RTC_HOURS);
mday = mc146818_read8(RTC_DATE_OF_MONTH);
wday = mc146818_read8(RTC_DAY_OF_WEEK);
mon = mc146818_read8(RTC_MONTH);
year = mc146818_read8(RTC_YEAR);
#ifdef RTC_DEBUG
printf("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
printf("Alarms: mday: %02x hour: %02x min: %02x sec: %02x\n",
mc146818_read8(RTC_CONFIG_D) & 0x3f,
mc146818_read8(RTC_HOURS_ALARM),
mc146818_read8(RTC_MINUTES_ALARM),
mc146818_read8(RTC_SECONDS_ALARM));
#endif
tmp->tm_sec = bcd2bin(sec & 0x7f);
tmp->tm_min = bcd2bin(min & 0x7f);
tmp->tm_hour = bcd2bin(hour & 0x3f);
tmp->tm_mday = bcd2bin(mday & 0x3f);
tmp->tm_mon = bcd2bin(mon & 0x1f);
tmp->tm_year = bcd2bin(year);
if (tmp->tm_year < 70)
tmp->tm_year += 2000;
else
tmp->tm_year += 1900;
tmp->tm_yday = 0;
tmp->tm_isdst = 0;
/*
* The mc146818 only updates wday if it is non-zero, sunday is 1
* saturday is 7. So let's use our library routine.
*/
rtc_calc_weekday(tmp);
#ifdef RTC_DEBUG
printf("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);
#endif
return 0;
}
static int mc146818_set(struct rtc_time *tmp)
{
#ifdef RTC_DEBUG
printf("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);
#endif
/* Disable the RTC to update the regs */
mc146818_write8(RTC_CONFIG_B, 0x82);
mc146818_write8(RTC_YEAR, bin2bcd(tmp->tm_year % 100));
mc146818_write8(RTC_MONTH, bin2bcd(tmp->tm_mon));
/* Sunday = 1, Saturday = 7 */
mc146818_write8(RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday + 1));
mc146818_write8(RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday));
mc146818_write8(RTC_HOURS, bin2bcd(tmp->tm_hour));
mc146818_write8(RTC_MINUTES, bin2bcd(tmp->tm_min));
mc146818_write8(RTC_SECONDS, bin2bcd(tmp->tm_sec));
/* Enable the RTC to update the regs */
mc146818_write8(RTC_CONFIG_B, 0x02);
return 0;
}
static void mc146818_reset(void)
{
/* Disable the RTC to update the regs */
mc146818_write8(RTC_CONFIG_B, 0x82);
/* Normal OP */
mc146818_write8(RTC_CONFIG_A, 0x20);
mc146818_write8(RTC_CONFIG_B, 0x00);
mc146818_write8(RTC_CONFIG_B, 0x00);
/* Enable the RTC to update the regs */
mc146818_write8(RTC_CONFIG_B, 0x02);
}
static void mc146818_init(void)
{
#if CLEAR_CMOS
int i;
rtc_write8(RTC_SECONDS_ALARM, 0);
rtc_write8(RTC_MINUTES_ALARM, 0);
rtc_write8(RTC_HOURS_ALARM, 0);
for (i = RTC_CONFIG_A; i < RTC_REG_SIZE; i++)
rtc_write8(i, 0);
printf("RTC: zeroing CMOS RAM\n");
#endif
/* Setup the real time clock */
mc146818_write8(RTC_CONFIG_B, RTC_CONFIG_B_24H);
/* Setup the frequency it operates at */
mc146818_write8(RTC_CONFIG_A, RTC_CONFIG_A_REF_CLCK_32KHZ |
RTC_CONFIG_A_RATE_1024HZ);
/* Ensure all reserved bits are 0 in register D */
mc146818_write8(RTC_CONFIG_D, RTC_CONFIG_D_VALID_RAM_AND_TIME);
/* Clear any pending interrupts */
mc146818_read8(RTC_CONFIG_C);
}
#ifdef CONFIG_DM_RTC
static int rtc_mc146818_get(struct udevice *dev, struct rtc_time *time)
{
return mc146818_get(time);
}
static int rtc_mc146818_set(struct udevice *dev, const struct rtc_time *time)
{
return mc146818_set((struct rtc_time *)time);
}
static int rtc_mc146818_reset(struct udevice *dev)
{
mc146818_reset();
return 0;
}
static int rtc_mc146818_read8(struct udevice *dev, unsigned int reg)
{
return mc146818_read8(reg);
}
static int rtc_mc146818_write8(struct udevice *dev, unsigned int reg, int val)
{
mc146818_write8(reg, val);
return 0;
}
static int rtc_mc146818_probe(struct udevice *dev)
{
mc146818_init();
return 0;
}
static const struct rtc_ops rtc_mc146818_ops = {
.get = rtc_mc146818_get,
.set = rtc_mc146818_set,
.reset = rtc_mc146818_reset,
.read8 = rtc_mc146818_read8,
.write8 = rtc_mc146818_write8,
};
static const struct udevice_id rtc_mc146818_ids[] = {
{ .compatible = "motorola,mc146818" },
{ }
};
U_BOOT_DRIVER(rtc_mc146818) = {
.name = "rtc_mc146818",
.id = UCLASS_RTC,
.of_match = rtc_mc146818_ids,
.probe = rtc_mc146818_probe,
.ops = &rtc_mc146818_ops,
};
#else /* !CONFIG_DM_RTC */
int rtc_get(struct rtc_time *tmp)
{
return mc146818_get(tmp);
}
int rtc_set(struct rtc_time *tmp)
{
return mc146818_set(tmp);
}
void rtc_reset(void)
{
mc146818_reset();
}
int rtc_read8(int reg)
{
return mc146818_read8(reg);
}
void rtc_write8(int reg, uchar val)
{
mc146818_write8(reg, val);
}
u32 rtc_read32(int reg)
{
u32 value = 0;
int i;
for (i = 0; i < sizeof(value); i++)
value |= rtc_read8(reg + i) << (i << 3);
return value;
}
void rtc_write32(int reg, u32 value)
{
int i;
for (i = 0; i < sizeof(value); i++)
rtc_write8(reg + i, (value >> (i << 3)) & 0xff);
}
void rtc_init(void)
{
mc146818_init();
}
#endif /* CONFIG_DM_RTC */
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <asm/immap.h>
#include <asm/rtc.h>
#undef RTC_DEBUG
#ifndef CONFIG_SYS_MCFRTC_BASE
#error RTC_BASE is not defined!
#endif
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
#define STARTOFTIME 1970
int rtc_get(struct rtc_time *tmp)
{
volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE);
int rtc_days, rtc_hrs, rtc_mins;
int tim;
rtc_days = rtc->days;
rtc_hrs = rtc->hourmin >> 8;
rtc_mins = RTC_HOURMIN_MINUTES(rtc->hourmin);
tim = (rtc_days * 24) + rtc_hrs;
tim = (tim * 60) + rtc_mins;
tim = (tim * 60) + rtc->seconds;
rtc_to_tm(tim, tmp);
tmp->tm_yday = 0;
tmp->tm_isdst = 0;
#ifdef RTC_DEBUG
printf("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);
#endif
return 0;
}
int rtc_set(struct rtc_time *tmp)
{
volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE);
static int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int days, i, months;
if (tmp->tm_year > 2037) {
printf("Unable to handle. Exceeding integer limitation!\n");
tmp->tm_year = 2027;
}
#ifdef RTC_DEBUG
printf("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);
#endif
/* calculate days by years */
for (i = STARTOFTIME, days = 0; i < tmp->tm_year; i++) {
days += 365 + isleap(i);
}
/* calculate days by months */
months = tmp->tm_mon - 1;
for (i = 0; i < months; i++) {
days += month_days[i];
if (i == 1)
days += isleap(i);
}
days += tmp->tm_mday - 1;
rtc->days = days;
rtc->hourmin = (tmp->tm_hour << 8) | tmp->tm_min;
rtc->seconds = tmp->tm_sec;
return 0;
}
void rtc_reset(void)
{
volatile rtc_t *rtc = (rtc_t *) (CONFIG_SYS_MCFRTC_BASE);
if ((rtc->cr & RTC_CR_EN) == 0) {
printf("real-time-clock was stopped. Now starting...\n");
rtc->cr |= RTC_CR_EN;
}
rtc->cr |= RTC_CR_SWR;
}
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Andreas Heppel <aheppel@sysgo.de>
*/
/*
* Date & Time support for the MK48T59 RTC
*/
#undef RTC_DEBUG
#include <common.h>
#include <command.h>
#include <config.h>
#include <rtc.h>
#include <mk48t59.h>
#if defined(CONFIG_BAB7xx)
static uchar rtc_read (short reg)
{
out8(RTC_PORT_ADDR0, reg & 0xFF);
out8(RTC_PORT_ADDR1, (reg>>8) & 0xFF);
return in8(RTC_PORT_DATA);
}
static void rtc_write (short reg, uchar val)
{
out8(RTC_PORT_ADDR0, reg & 0xFF);
out8(RTC_PORT_ADDR1, (reg>>8) & 0xFF);
out8(RTC_PORT_DATA, val);
}
#elif defined(CONFIG_EVAL5200)
static uchar rtc_read (short reg)
{
return in8(RTC(reg));
}
static void rtc_write (short reg, uchar val)
{
out8(RTC(reg),val);
}
#else
# error Board specific rtc access functions should be supplied
#endif
/* ------------------------------------------------------------------------- */
void *nvram_read(void *dest, const short src, size_t count)
{
uchar *d = (uchar *) dest;
short s = src;
while (count--)
*d++ = rtc_read(s++);
return dest;
}
void nvram_write(short dest, const void *src, size_t count)
{
short d = dest;
uchar *s = (uchar *) src;
while (count--)
rtc_write(d++, *s++);
}
/* ------------------------------------------------------------------------- */
int rtc_get (struct rtc_time *tmp)
{
uchar save_ctrl_a;
uchar sec, min, hour, mday, wday, mon, year;
/* Simple: freeze the clock, read it and allow updates again */
save_ctrl_a = rtc_read(RTC_CONTROLA);
/* Set the register to read the value. */
save_ctrl_a |= RTC_CA_READ;
rtc_write(RTC_CONTROLA, save_ctrl_a);
sec = rtc_read (RTC_SECONDS);
min = rtc_read (RTC_MINUTES);
hour = rtc_read (RTC_HOURS);
mday = rtc_read (RTC_DAY_OF_MONTH);
wday = rtc_read (RTC_DAY_OF_WEEK);
mon = rtc_read (RTC_MONTH);
year = rtc_read (RTC_YEAR);
/* re-enable update */
save_ctrl_a &= ~RTC_CA_READ;
rtc_write(RTC_CONTROLA, save_ctrl_a);
#ifdef RTC_DEBUG
printf ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday,
hour, min, sec );
#endif
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon & 0x1F);
tmp->tm_year = bcd2bin (year);
tmp->tm_wday = bcd2bin (wday & 0x07);
if(tmp->tm_year<70)
tmp->tm_year+=2000;
else
tmp->tm_year+=1900;
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
#ifdef RTC_DEBUG
printf ( "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);
#endif
return 0;
}
int rtc_set (struct rtc_time *tmp)
{
uchar save_ctrl_a;
#ifdef RTC_DEBUG
printf ( "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);
#endif
save_ctrl_a = rtc_read(RTC_CONTROLA);
save_ctrl_a |= RTC_CA_WRITE;
rtc_write(RTC_CONTROLA, save_ctrl_a); /* disables the RTC to update the regs */
rtc_write (RTC_YEAR, bin2bcd(tmp->tm_year % 100));
rtc_write (RTC_MONTH, bin2bcd(tmp->tm_mon));
rtc_write (RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday));
rtc_write (RTC_DAY_OF_MONTH, bin2bcd(tmp->tm_mday));
rtc_write (RTC_HOURS, bin2bcd(tmp->tm_hour));
rtc_write (RTC_MINUTES, bin2bcd(tmp->tm_min ));
rtc_write (RTC_SECONDS, bin2bcd(tmp->tm_sec ));
save_ctrl_a &= ~RTC_CA_WRITE;
rtc_write(RTC_CONTROLA, save_ctrl_a); /* enables the RTC to update the regs */
return 0;
}
void rtc_reset (void)
{
uchar control_b;
/*
* Start oscillator here.
*/
control_b = rtc_read(RTC_CONTROLB);
control_b &= ~RTC_CB_STOP;
rtc_write(RTC_CONTROLB, control_b);
}
void rtc_set_watchdog(short multi, short res)
{
uchar wd_value;
wd_value = RTC_WDS | ((multi & 0x1F) << 2) | (res & 0x3);
rtc_write(RTC_WATCHDOG, wd_value);
}
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2011
* Jason Cooper <u-boot@lakedaemon.net>
*/
/*
* Date & Time support for Marvell Integrated RTC
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <asm/io.h>
#include "mvrtc.h"
/* This RTC does not support century, so we assume 20 */
#define CENTURY 20
static int __mv_rtc_get(struct mvrtc_registers *regs, struct rtc_time *t)
{
u32 time;
u32 date;
/* read the time register */
time = readl(&regs->time);
/* read the date register */
date = readl(&regs->date);
/* test for 12 hour clock (can't tell if it's am/pm) */
if (time & MVRTC_HRFMT_MSK) {
printf("Error: RTC in 12 hour mode, can't determine AM/PM.\n");
return -1;
}
/* time */
t->tm_sec = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK);
t->tm_min = bcd2bin((time >> MVRTC_MIN_SFT) & MVRTC_MIN_MSK);
t->tm_hour = bcd2bin((time >> MVRTC_HOUR_SFT) & MVRTC_HOUR_MSK);
t->tm_wday = bcd2bin((time >> MVRTC_DAY_SFT) & MVRTC_DAY_MSK);
t->tm_wday--;
/* date */
t->tm_mday = bcd2bin((date >> MVRTC_DATE_SFT) & MVRTC_DATE_MSK);
t->tm_mon = bcd2bin((date >> MVRTC_MON_SFT) & MVRTC_MON_MSK);
t->tm_year = bcd2bin((date >> MVRTC_YEAR_SFT) & MVRTC_YEAR_MSK);
t->tm_year += CENTURY * 100;
/* not supported in this RTC */
t->tm_yday = 0;
t->tm_isdst = 0;
return 0;
}
#ifndef CONFIG_DM_RTC
int rtc_get(struct rtc_time *t)
{
struct mvrtc_registers *regs;
regs = (struct mvrtc_registers *)KW_RTC_BASE;
return __mv_rtc_get(regs, t);
}
#endif /* !CONFIG_DM_RTC */
static int __mv_rtc_set(struct mvrtc_registers *regs, const struct rtc_time *t)
{
u32 time = 0; /* sets hour format bit to zero, 24hr format. */
u32 date = 0;
/* check that this code isn't 80+ years old ;-) */
if ((t->tm_year / 100) != CENTURY)
printf("Warning: Only century %d supported.\n", CENTURY);
/* time */
time |= (bin2bcd(t->tm_sec) & MVRTC_SEC_MSK) << MVRTC_SEC_SFT;
time |= (bin2bcd(t->tm_min) & MVRTC_MIN_MSK) << MVRTC_MIN_SFT;
time |= (bin2bcd(t->tm_hour) & MVRTC_HOUR_MSK) << MVRTC_HOUR_SFT;
time |= (bin2bcd(t->tm_wday + 1) & MVRTC_DAY_MSK) << MVRTC_DAY_SFT;
/* date */
date |= (bin2bcd(t->tm_mday) & MVRTC_DATE_MSK) << MVRTC_DATE_SFT;
date |= (bin2bcd(t->tm_mon) & MVRTC_MON_MSK) << MVRTC_MON_SFT;
date |= (bin2bcd(t->tm_year % 100) & MVRTC_YEAR_MSK) << MVRTC_YEAR_SFT;
/* write the time register */
writel(time, &regs->time);
/* write the date register */
writel(date, &regs->date);
return 0;
}
#ifndef CONFIG_DM_RTC
int rtc_set(struct rtc_time *t)
{
struct mvrtc_registers *regs;
regs = (struct mvrtc_registers *)KW_RTC_BASE;
return __mv_rtc_set(regs, t);
}
#endif /* !CONFIG_DM_RTC */
static void __mv_rtc_reset(struct mvrtc_registers *regs)
{
u32 time;
u32 sec;
/* no init routine for this RTC needed, just check that it's working */
time = readl(&regs->time);
sec = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK);
udelay(1000000);
time = readl(&regs->time);
if (sec == bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK))
printf("Error: RTC did not increment.\n");
}
#ifndef CONFIG_DM_RTC
void rtc_reset(void)
{
struct mvrtc_registers *regs;
regs = (struct mvrtc_registers *)KW_RTC_BASE;
__mv_rtc_reset(regs);
}
#endif /* !CONFIG_DM_RTC */
#ifdef CONFIG_DM_RTC
static int mv_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
struct mvrtc_pdata *pdata = dev_get_platdata(dev);
struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
return __mv_rtc_get(regs, tm);
}
static int mv_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
struct mvrtc_pdata *pdata = dev_get_platdata(dev);
struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
return __mv_rtc_set(regs, tm);
}
static int mv_rtc_reset(struct udevice *dev)
{
struct mvrtc_pdata *pdata = dev_get_platdata(dev);
struct mvrtc_registers *regs = (struct mvrtc_registers *)pdata->iobase;
__mv_rtc_reset(regs);
return 0;
}
static const struct rtc_ops mv_rtc_ops = {
.get = mv_rtc_get,
.set = mv_rtc_set,
.reset = mv_rtc_reset,
};
static const struct udevice_id mv_rtc_ids[] = {
{ .compatible = "marvell,kirkwood-rtc" },
{ .compatible = "marvell,orion-rtc" },
{ }
};
static int mv_rtc_ofdata_to_platdata(struct udevice *dev)
{
struct mvrtc_pdata *pdata = dev_get_platdata(dev);
pdata->iobase = devfdt_get_addr(dev);
return 0;
}
U_BOOT_DRIVER(rtc_mv) = {
.name = "rtc-mv",
.id = UCLASS_RTC,
.ofdata_to_platdata = mv_rtc_ofdata_to_platdata,
.of_match = mv_rtc_ids,
.ops = &mv_rtc_ops,
};
#endif /* CONFIG_DM_RTC */
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2011
* Jason Cooper <u-boot@lakedaemon.net>
*/
/*
* Date & Time support for Marvell Integrated RTC
*/
#ifndef _MVRTC_H_
#define _MVRTC_H_
#include <asm/arch/soc.h>
#include <linux/compiler.h>
/* RTC registers */
struct mvrtc_registers {
u32 time;
u32 date;
};
/* Platform data */
struct mvrtc_pdata {
phys_addr_t iobase;
};
/* time register */
#define MVRTC_SEC_SFT 0
#define MVRTC_SEC_MSK 0x7f
#define MVRTC_MIN_SFT 8
#define MVRTC_MIN_MSK 0x7f
#define MVRTC_HOUR_SFT 16
#define MVRTC_HOUR_MSK 0x3f
#define MVRTC_DAY_SFT 24
#define MVRTC_DAY_MSK 0x7
/*
* Hour format bit
* 1 = 12 hour clock
* 0 = 24 hour clock
*/
#define MVRTC_HRFMT_MSK 0x00400000
/* date register */
#define MVRTC_DATE_SFT 0
#define MVRTC_DATE_MSK 0x3f
#define MVRTC_MON_SFT 8
#define MVRTC_MON_MSK 0x1f
#define MVRTC_YEAR_SFT 16
#define MVRTC_YEAR_MSK 0xff
#endif
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX27 RTC Driver
*
* Copyright (C) 2012 Philippe Reynes <tremyfr@yahoo.fr>
*/
#include <common.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#define HOUR_SHIFT 8
#define HOUR_MASK 0x1f
#define MIN_SHIFT 0
#define MIN_MASK 0x3f
int rtc_get(struct rtc_time *time)
{
struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
uint32_t day, hour, min, sec;
day = readl(&rtc_regs->dayr);
hour = readl(&rtc_regs->hourmin);
sec = readl(&rtc_regs->seconds);
min = (hour >> MIN_SHIFT) & MIN_MASK;
hour = (hour >> HOUR_SHIFT) & HOUR_MASK;
sec += min * 60 + hour * 3600 + day * 24 * 3600;
rtc_to_tm(sec, time);
return 0;
}
int rtc_set(struct rtc_time *time)
{
struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
uint32_t day, hour, min, sec;
sec = rtc_mktime(time);
day = sec / (24 * 3600);
sec = sec % (24 * 3600);
hour = sec / 3600;
sec = sec % 3600;
min = sec / 60;
sec = sec % 60;
hour = (hour & HOUR_MASK) << HOUR_SHIFT;
hour |= (min & MIN_MASK) << MIN_SHIFT;
writel(day, &rtc_regs->dayr);
writel(hour, &rtc_regs->hourmin);
writel(sec, &rtc_regs->seconds);
return 0;
}
void rtc_reset(void)
{
/* nothing to do */
}
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX28 RTC Driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*/
#include <common.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#define MXS_RTC_MAX_TIMEOUT 1000000
/* Set time in seconds since 1970-01-01 */
int mxs_rtc_set_time(uint32_t secs)
{
struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
int ret;
writel(secs, &rtc_regs->hw_rtc_seconds);
/*
* The 0x80 here means seconds were copied to analog. This information
* is taken from the linux kernel driver for the STMP37xx RTC since
* documentation doesn't mention it.
*/
ret = mxs_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg,
0x80 << RTC_STAT_STALE_REGS_OFFSET, MXS_RTC_MAX_TIMEOUT);
if (ret)
printf("MXS RTC: Timeout waiting for update\n");
return ret;
}
int rtc_get(struct rtc_time *time)
{
struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
uint32_t secs;
secs = readl(&rtc_regs->hw_rtc_seconds);
rtc_to_tm(secs, time);
return 0;
}
int rtc_set(struct rtc_time *time)
{
uint32_t secs;
secs = rtc_mktime(time);
return mxs_rtc_set_time(secs);
}
void rtc_reset(void)
{
struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;
int ret;
/* Set time to 1970-01-01 */
mxs_rtc_set_time(0);
/* Reset the RTC block */
ret = mxs_reset_block(&rtc_regs->hw_rtc_ctrl_reg);
if (ret)
printf("MXS RTC: Block reset timeout\n");
}
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2016 by NXP Semiconductors Inc.
* Date & Time support for PCF2127 RTC
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <dm.h>
#include <i2c.h>
#include <rtc.h>
#define PCF2127_REG_CTRL1 0x00
#define PCF2127_REG_CTRL2 0x01
#define PCF2127_REG_CTRL3 0x02
#define PCF2127_REG_SC 0x03
#define PCF2127_REG_MN 0x04
#define PCF2127_REG_HR 0x05
#define PCF2127_REG_DM 0x06
#define PCF2127_REG_DW 0x07
#define PCF2127_REG_MO 0x08
#define PCF2127_REG_YR 0x09
static int pcf2127_read_reg(struct udevice *dev, uint offset,
u8 *buffer, int len)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
struct i2c_msg msg;
int ret;
/* Set the address of the start register to be read */
ret = dm_i2c_write(dev, offset, NULL, 0);
if (ret < 0)
return ret;
/* Read register's data */
msg.addr = chip->chip_addr;
msg.flags |= I2C_M_RD;
msg.len = len;
msg.buf = buffer;
return dm_i2c_xfer(dev, &msg, 1);
}
static int pcf2127_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
uchar buf[7] = {0};
int i = 0, ret;
/* hours, minutes and seconds */
buf[i++] = bin2bcd(tm->tm_sec);
buf[i++] = bin2bcd(tm->tm_min);
buf[i++] = bin2bcd(tm->tm_hour);
buf[i++] = bin2bcd(tm->tm_mday);
buf[i++] = tm->tm_wday & 0x07;
/* month, 1 - 12 */
buf[i++] = bin2bcd(tm->tm_mon + 1);
/* year */
buf[i++] = bin2bcd(tm->tm_year % 100);
/* write register's data */
ret = dm_i2c_write(dev, PCF2127_REG_SC, buf, i);
return ret;
}
static int pcf2127_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
int ret = 0;
uchar buf[10] = { PCF2127_REG_CTRL1 };
ret = pcf2127_read_reg(dev, PCF2127_REG_CTRL1, buf, sizeof(buf));
if (ret < 0)
return ret;
if (buf[PCF2127_REG_CTRL3] & 0x04)
puts("### Warning: RTC Low Voltage - date/time not reliable\n");
tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F);
tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1;
tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]) + 1900;
if (tm->tm_year < 1970)
tm->tm_year += 100; /* assume we are in 1970...2069 */
tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
tm->tm_yday = 0;
tm->tm_isdst = 0;
debug("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 ret;
}
static int pcf2127_rtc_reset(struct udevice *dev)
{
/*Doing nothing here*/
return 0;
}
static const struct rtc_ops pcf2127_rtc_ops = {
.get = pcf2127_rtc_get,
.set = pcf2127_rtc_set,
.reset = pcf2127_rtc_reset,
};
static const struct udevice_id pcf2127_rtc_ids[] = {
{ .compatible = "pcf2127-rtc" },
{ }
};
U_BOOT_DRIVER(rtc_pcf2127) = {
.name = "rtc-pcf2127",
.id = UCLASS_RTC,
.of_match = pcf2127_rtc_ids,
.ops = &pcf2127_rtc_ops,
};
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2001
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*/
/*
* Date & Time support for Philips PCF8563 RTC
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
static uchar rtc_read (uchar reg);
static void rtc_write (uchar reg, uchar val);
/* ------------------------------------------------------------------------- */
int rtc_get (struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon_cent, year;
sec = rtc_read (0x02);
min = rtc_read (0x03);
hour = rtc_read (0x04);
mday = rtc_read (0x05);
wday = rtc_read (0x06);
mon_cent= rtc_read (0x07);
year = rtc_read (0x08);
debug ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon_cent, mday, wday,
hour, min, sec );
debug ( "Alarms: wday: %02x day: %02x hour: %02x min: %02x\n",
rtc_read (0x0C),
rtc_read (0x0B),
rtc_read (0x0A),
rtc_read (0x09) );
if (sec & 0x80) {
puts ("### Warning: RTC Low Voltage - date/time not reliable\n");
rel = -1;
}
tmp->tm_sec = bcd2bin (sec & 0x7F);
tmp->tm_min = bcd2bin (min & 0x7F);
tmp->tm_hour = bcd2bin (hour & 0x3F);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon_cent & 0x1F);
tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 1900 : 2000);
tmp->tm_wday = bcd2bin (wday & 0x07);
tmp->tm_yday = 0;
tmp->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);
return rel;
}
int rtc_set (struct rtc_time *tmp)
{
uchar century;
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);
rtc_write (0x08, bin2bcd(tmp->tm_year % 100));
century = (tmp->tm_year >= 2000) ? 0 : 0x80;
rtc_write (0x07, bin2bcd(tmp->tm_mon) | century);
rtc_write (0x06, bin2bcd(tmp->tm_wday));
rtc_write (0x05, bin2bcd(tmp->tm_mday));
rtc_write (0x04, bin2bcd(tmp->tm_hour));
rtc_write (0x03, bin2bcd(tmp->tm_min ));
rtc_write (0x02, bin2bcd(tmp->tm_sec ));
return 0;
}
void rtc_reset (void)
{
/* clear all control & status registers */
rtc_write (0x00, 0x00);
rtc_write (0x01, 0x00);
rtc_write (0x0D, 0x00);
/* clear Voltage Low bit */
rtc_write (0x02, rtc_read (0x02) & 0x7F);
/* reset all alarms */
rtc_write (0x09, 0x00);
rtc_write (0x0A, 0x00);
rtc_write (0x0B, 0x00);
rtc_write (0x0C, 0x00);
}
/* ------------------------------------------------------------------------- */
static uchar rtc_read (uchar reg)
{
return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg));
}
static void rtc_write (uchar reg, uchar val)
{
i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008
* Gururaja Hebbar gururajakr@sanyo.co.in
*
* reference linux-2.6.20.6/drivers/rtc/rtc-pl031.c
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <errno.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/types.h>
/*
* Register definitions
*/
#define RTC_DR 0x00 /* Data read register */
#define RTC_MR 0x04 /* Match register */
#define RTC_LR 0x08 /* Data load register */
#define RTC_CR 0x0c /* Control register */
#define RTC_IMSC 0x10 /* Interrupt mask and set register */
#define RTC_RIS 0x14 /* Raw interrupt status register */
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
#define RTC_CR_START (1 << 0)
struct pl031_platdata {
phys_addr_t base;
};
static inline u32 pl031_read_reg(struct udevice *dev, int reg)
{
struct pl031_platdata *pdata = dev_get_platdata(dev);
return readl(pdata->base + reg);
}
static inline u32 pl031_write_reg(struct udevice *dev, int reg, u32 value)
{
struct pl031_platdata *pdata = dev_get_platdata(dev);
return writel(value, pdata->base + reg);
}
/*
* Probe RTC device
*/
static int pl031_probe(struct udevice *dev)
{
/* Enable RTC Start in Control register*/
pl031_write_reg(dev, RTC_CR, RTC_CR_START);
return 0;
}
/*
* Get the current time from the RTC
*/
static int pl031_get(struct udevice *dev, struct rtc_time *tm)
{
unsigned long tim;
if (!tm)
return -EINVAL;
tim = pl031_read_reg(dev, RTC_DR);
rtc_to_tm(tim, tm);
debug("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 pl031_set(struct udevice *dev, const struct rtc_time *tm)
{
unsigned long tim;
if (!tm)
return -EINVAL;
debug("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);
/* Calculate number of seconds this incoming time represents */
tim = rtc_mktime(tm);
pl031_write_reg(dev, RTC_LR, tim);
return 0;
}
/*
* Reset the RTC. We set the date back to 1970-01-01.
*/
static int pl031_reset(struct udevice *dev)
{
pl031_write_reg(dev, RTC_LR, 0);
return 0;
}
static const struct rtc_ops pl031_ops = {
.get = pl031_get,
.set = pl031_set,
.reset = pl031_reset,
};
static const struct udevice_id pl031_ids[] = {
{ .compatible = "arm,pl031" },
{ }
};
static int pl031_ofdata_to_platdata(struct udevice *dev)
{
struct pl031_platdata *pdata = dev_get_platdata(dev);
pdata->base = dev_read_addr(dev);
return 0;
}
U_BOOT_DRIVER(rtc_pl031) = {
.name = "rtc-pl031",
.id = UCLASS_RTC,
.of_match = pl031_ids,
.probe = pl031_probe,
.ofdata_to_platdata = pl031_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct pl031_platdata),
.ops = &pl031_ops,
};
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2010 Freescale Semiconductor, Inc.
*
* Author: Priyanka Jain <Priyanka.Jain@freescale.com>
*/
/*
* This file provides Date & Time support (no alarms) for PT7C4338 chip.
*
* This file is based on drivers/rtc/ds1337.c
*
* PT7C4338 chip is manufactured by Pericom Technology Inc.
* It is a serial real-time clock which provides
* 1)Low-power clock/calendar.
* 2)Programmable square-wave output.
* It has 56 bytes of nonvolatile RAM.
*/
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
/* RTC register addresses */
#define RTC_SEC_REG_ADDR 0x0
#define RTC_MIN_REG_ADDR 0x1
#define RTC_HR_REG_ADDR 0x2
#define RTC_DAY_REG_ADDR 0x3
#define RTC_DATE_REG_ADDR 0x4
#define RTC_MON_REG_ADDR 0x5
#define RTC_YR_REG_ADDR 0x6
#define RTC_CTL_STAT_REG_ADDR 0x7
/* RTC second register address bit */
#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */
/* RTC control and status register bits */
#define RTC_CTL_STAT_BIT_RS0 0x1 /* Rate select 0 */
#define RTC_CTL_STAT_BIT_RS1 0x2 /* Rate select 1 */
#define RTC_CTL_STAT_BIT_SQWE 0x10 /* Square Wave Enable */
#define RTC_CTL_STAT_BIT_OSF 0x20 /* Oscillator Stop Flag */
#define RTC_CTL_STAT_BIT_OUT 0x80 /* Output Level Control */
/* RTC reset value */
#define RTC_PT7C4338_RESET_VAL \
(RTC_CTL_STAT_BIT_RS0 | RTC_CTL_STAT_BIT_RS1 | RTC_CTL_STAT_BIT_OUT)
/****** Helper functions ****************************************/
static u8 rtc_read(u8 reg)
{
return i2c_reg_read(CONFIG_SYS_I2C_RTC_ADDR, reg);
}
static void rtc_write(u8 reg, u8 val)
{
i2c_reg_write(CONFIG_SYS_I2C_RTC_ADDR, reg, val);
}
/****************************************************************/
/* Get the current time from the RTC */
int rtc_get(struct rtc_time *tmp)
{
int ret = 0;
u8 sec, min, hour, mday, wday, mon, year, ctl_stat;
ctl_stat = rtc_read(RTC_CTL_STAT_REG_ADDR);
sec = rtc_read(RTC_SEC_REG_ADDR);
min = rtc_read(RTC_MIN_REG_ADDR);
hour = rtc_read(RTC_HR_REG_ADDR);
wday = rtc_read(RTC_DAY_REG_ADDR);
mday = rtc_read(RTC_DATE_REG_ADDR);
mon = rtc_read(RTC_MON_REG_ADDR);
year = rtc_read(RTC_YR_REG_ADDR);
debug("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x control_status: %02x\n",
year, mon, mday, wday, hour, min, sec, ctl_stat);
if (ctl_stat & RTC_CTL_STAT_BIT_OSF) {
printf("### Warning: RTC oscillator has stopped\n");
/* clear the OSF flag */
rtc_write(RTC_CTL_STAT_REG_ADDR,
rtc_read(RTC_CTL_STAT_REG_ADDR)\
& ~RTC_CTL_STAT_BIT_OSF);
ret = -1;
}
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
tmp->tm_hour = bcd2bin(hour & 0x3F);
tmp->tm_mday = bcd2bin(mday & 0x3F);
tmp->tm_mon = bcd2bin(mon & 0x1F);
tmp->tm_year = bcd2bin(year) + 2000;
tmp->tm_wday = bcd2bin((wday - 1) & 0x07);
tmp->tm_yday = 0;
tmp->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);
return ret;
}
/* Set the RTC */
int rtc_set(struct rtc_time *tmp)
{
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);
rtc_write(RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100));
rtc_write(RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon));
rtc_write(RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1));
rtc_write(RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday));
rtc_write(RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour));
rtc_write(RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min));
rtc_write(RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec));
return 0;
}
/* Reset the RTC */
void rtc_reset(void)
{
rtc_write(RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */
rtc_write(RTC_CTL_STAT_REG_ADDR, RTC_PT7C4338_RESET_VAL);
}
@@ -0,0 +1,256 @@
/*
* rs5c372.c
*
* Device driver for Ricoh's Real Time Controller RS5C372A.
*
* Copyright (C) 2004 Gary Jennejohn garyj@denx.de
*
* Based in part in ds1307.c -
* (C) Copyright 2001, 2002, 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Keith Outwater, keith_outwater@mvis.com`
* Steven Scholz, steven.scholz@imc-berlin.de
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <command.h>
#include <rtc.h>
#include <i2c.h>
/*
* Reads are always done starting with register 15, which requires some
* jumping-through-hoops to access the data correctly.
*
* Writes are always done starting with register 0.
*/
#define DEBUG 0
#if DEBUG
static unsigned int rtc_debug = DEBUG;
#else
#define rtc_debug 0 /* gcc will remove all the debug code for us */
#endif
#ifndef CONFIG_SYS_I2C_RTC_ADDR
#define CONFIG_SYS_I2C_RTC_ADDR 0x32
#endif
#define RS5C372_RAM_SIZE 0x10
#define RATE_32000HZ 0x80 /* Rate Select 32.000KHz */
#define RATE_32768HZ 0x00 /* Rate Select 32.768KHz */
#define STATUS_XPT 0x10 /* data invalid because voltage was 0 */
#define USE_24HOUR_MODE 0x20
#define TWELVE_HOUR_MODE(n) ((((n) >> 5) & 1) == 0)
#define HOURS_AP(n) (((n) >> 5) & 1)
#define HOURS_12(n) bcd2bin((n) & 0x1F)
#define HOURS_24(n) bcd2bin((n) & 0x3F)
static int setup_done = 0;
static int
rs5c372_readram(unsigned char *buf, int len)
{
int ret;
ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, len);
if (ret != 0) {
printf("%s: failed to read\n", __FUNCTION__);
return ret;
}
if (buf[0] & STATUS_XPT)
printf("### Warning: RTC lost power\n");
return ret;
}
static void
rs5c372_enable(void)
{
unsigned char buf[RS5C372_RAM_SIZE + 1];
int ret;
/* note that this returns reg. 15 in buf[1] */
ret = rs5c372_readram(&buf[1], RS5C372_RAM_SIZE);
if (ret != 0) {
printf("%s: failed\n", __FUNCTION__);
return;
}
buf[0] = 0;
/* we want to start writing at register 0 so we have to copy the */
/* register contents up one slot */
for (ret = 2; ret < 9; ret++)
buf[ret - 1] = buf[ret];
/* registers 0 to 6 (time values) are not touched */
buf[8] = RATE_32768HZ; /* reg. 7 */
buf[9] = 0; /* reg. 8 */
buf[10] = 0; /* reg. 9 */
buf[11] = 0; /* reg. 10 */
buf[12] = 0; /* reg. 11 */
buf[13] = 0; /* reg. 12 */
buf[14] = 0; /* reg. 13 */
buf[15] = 0; /* reg. 14 */
buf[16] = USE_24HOUR_MODE; /* reg. 15 */
ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, RS5C372_RAM_SIZE+1);
if (ret != 0) {
printf("%s: failed\n", __FUNCTION__);
return;
}
setup_done = 1;
return;
}
static void
rs5c372_convert_to_time(struct rtc_time *dt, unsigned char *buf)
{
/* buf[0] is register 15 */
dt->tm_sec = bcd2bin(buf[1]);
dt->tm_min = bcd2bin(buf[2]);
if (TWELVE_HOUR_MODE(buf[0])) {
dt->tm_hour = HOURS_12(buf[3]);
if (HOURS_AP(buf[3])) /* PM */
dt->tm_hour += 12;
} else /* 24-hour-mode */
dt->tm_hour = HOURS_24(buf[3]);
dt->tm_mday = bcd2bin(buf[5]);
dt->tm_mon = bcd2bin(buf[6]);
dt->tm_year = bcd2bin(buf[7]);
if (dt->tm_year >= 70)
dt->tm_year += 1900;
else
dt->tm_year += 2000;
/* 0 is Sunday */
dt->tm_wday = bcd2bin(buf[4] & 0x07);
dt->tm_yday = 0;
dt->tm_isdst= 0;
if(rtc_debug > 2) {
printf("rs5c372_convert_to_time: year = %d\n", dt->tm_year);
printf("rs5c372_convert_to_time: mon = %d\n", dt->tm_mon);
printf("rs5c372_convert_to_time: mday = %d\n", dt->tm_mday);
printf("rs5c372_convert_to_time: hour = %d\n", dt->tm_hour);
printf("rs5c372_convert_to_time: min = %d\n", dt->tm_min);
printf("rs5c372_convert_to_time: sec = %d\n", dt->tm_sec);
}
}
/*
* Get the current time from the RTC
*/
int
rtc_get (struct rtc_time *tmp)
{
unsigned char buf[RS5C372_RAM_SIZE];
int ret;
if (!setup_done)
rs5c372_enable();
if (!setup_done)
return -1;
memset(buf, 0, sizeof(buf));
/* note that this returns reg. 15 in buf[0] */
ret = rs5c372_readram(buf, RS5C372_RAM_SIZE);
if (ret != 0) {
printf("%s: failed\n", __FUNCTION__);
return -1;
}
rs5c372_convert_to_time(tmp, buf);
return 0;
}
/*
* Set the RTC
*/
int rtc_set (struct rtc_time *tmp)
{
unsigned char buf[8], reg15;
int ret;
if (!setup_done)
rs5c372_enable();
if (!setup_done)
return -1;
if(rtc_debug > 2) {
printf("rtc_set: tm_year = %d\n", tmp->tm_year);
printf("rtc_set: tm_mon = %d\n", tmp->tm_mon);
printf("rtc_set: tm_mday = %d\n", tmp->tm_mday);
printf("rtc_set: tm_hour = %d\n", tmp->tm_hour);
printf("rtc_set: tm_min = %d\n", tmp->tm_min);
printf("rtc_set: tm_sec = %d\n", tmp->tm_sec);
}
memset(buf, 0, sizeof(buf));
/* only read register 15 */
ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 1);
if (ret == 0) {
/* need to save register 15 */
reg15 = buf[0];
buf[0] = 0; /* register address on RS5C372 */
buf[1] = bin2bcd(tmp->tm_sec);
buf[2] = bin2bcd(tmp->tm_min);
/* need to handle 12 hour mode */
if (TWELVE_HOUR_MODE(reg15)) {
if (tmp->tm_hour >= 12) { /* PM */
/* 12 PM is a special case */
if (tmp->tm_hour == 12)
buf[3] = bin2bcd(tmp->tm_hour);
else
buf[3] = bin2bcd(tmp->tm_hour - 12);
buf[3] |= 0x20;
}
} else {
buf[3] = bin2bcd(tmp->tm_hour);
}
buf[4] = bin2bcd(tmp->tm_wday);
buf[5] = bin2bcd(tmp->tm_mday);
buf[6] = bin2bcd(tmp->tm_mon);
if (tmp->tm_year < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
buf[7] = bin2bcd(tmp->tm_year % 100);
ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 0, buf, 8);
if (ret != 0) {
printf("rs5c372_set_datetime(), i2c_master_send() returned %d\n",ret);
return -1;
}
} else {
return -1;
}
return 0;
}
/*
* Reset the RTC.
*/
void
rtc_reset (void)
{
if (!setup_done)
rs5c372_enable();
}
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
/*
* rtc and date/time utility functions
*
* Copyright (C) 2005-06 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* U-Boot rtc_time differs from Linux rtc_time:
* - The year field takes the actual value, not year - 1900.
* - January is month 1.
*/
#include <common.h>
#include <rtc.h>
#include <linux/math64.h>
static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
#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);
}
/*
* rtc_to_tm - Converts u64 to rtc_time.
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*
* This function is copied from rtc_time64_to_tm() in the Linux kernel.
* But in U-Boot January is month 1 and we do not subtract 1900 from the year.
*/
void rtc_to_tm(u64 time, struct rtc_time *tm)
{
unsigned int month, year, secs;
int days;
days = div_u64_rem(time, 86400, &secs);
/* 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);
while (days < 0) {
year -= 1;
days += 365 + is_leap_year(year);
}
tm->tm_year = year; /* Not 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 + 1; /* January = 1 */
tm->tm_mday = days + 1;
tm->tm_hour = secs / 3600;
secs -= tm->tm_hour * 3600;
tm->tm_min = secs / 60;
tm->tm_sec = secs - tm->tm_min * 60;
/* Zero unused fields */
tm->tm_isdst = 0;
}
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <rtc.h>
int dm_rtc_get(struct udevice *dev, struct rtc_time *time)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->get)
return -ENOSYS;
return ops->get(dev, time);
}
int dm_rtc_set(struct udevice *dev, struct rtc_time *time)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->set)
return -ENOSYS;
return ops->set(dev, time);
}
int dm_rtc_reset(struct udevice *dev)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->reset)
return -ENOSYS;
return ops->reset(dev);
}
int rtc_read8(struct udevice *dev, unsigned int reg)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->read8)
return -ENOSYS;
return ops->read8(dev, reg);
}
int rtc_write8(struct udevice *dev, unsigned int reg, int val)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->write8)
return -ENOSYS;
return ops->write8(dev, reg, val);
}
int rtc_read16(struct udevice *dev, unsigned int reg, u16 *valuep)
{
u16 value = 0;
int ret;
int i;
for (i = 0; i < sizeof(value); i++) {
ret = rtc_read8(dev, reg + i);
if (ret < 0)
return ret;
value |= ret << (i << 3);
}
*valuep = value;
return 0;
}
int rtc_write16(struct udevice *dev, unsigned int reg, u16 value)
{
int i, ret;
for (i = 0; i < sizeof(value); i++) {
ret = rtc_write8(dev, reg + i, (value >> (i << 3)) & 0xff);
if (ret)
return ret;
}
return 0;
}
int rtc_read32(struct udevice *dev, unsigned int reg, u32 *valuep)
{
u32 value = 0;
int ret;
int i;
for (i = 0; i < sizeof(value); i++) {
ret = rtc_read8(dev, reg + i);
if (ret < 0)
return ret;
value |= ret << (i << 3);
}
*valuep = value;
return 0;
}
int rtc_write32(struct udevice *dev, unsigned int reg, u32 value)
{
int i, ret;
for (i = 0; i < sizeof(value); i++) {
ret = rtc_write8(dev, reg + i, (value >> (i << 3)) & 0xff);
if (ret)
return ret;
}
return 0;
}
UCLASS_DRIVER(rtc) = {
.name = "rtc",
.id = UCLASS_RTC,
.post_bind = dm_scan_fdt_dev,
};
@@ -0,0 +1,496 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
*
* Based on a the Linux rtc-rv3029c2.c driver written by:
* Gregory Hermant <gregory.hermant@calao-systems.com>
* Michael Buesch <m@bues.ch>
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <eeprom.h>
#include <i2c.h>
#include <rtc.h>
#define RTC_RV3029_PAGE_LEN 7
/* 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
/* 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
/* 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
static int rv3029_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
u8 regs[RTC_RV3029_PAGE_LEN];
int 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 -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);
}
}
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 */
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("%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 rv3029_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
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);
if (tm->tm_year < 2000) {
printf("%s: year %d (before 2000) not supported\n",
__func__, tm->tm_year);
return -EINVAL;
}
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));
}
static int rv3029_rtc_reset(struct udevice *dev)
{
u8 ctrl = RV3029_RST_CTRL_SYSR;
unsigned long start;
const unsigned long timeout_ms = 10000;
int ret;
/* trigger the system-reset */
ret = dm_i2c_write(dev, RV3029_RST_CTRL, &ctrl, 1);
if (ret < 0)
return -EIO;
/* wait for the system-reset to complete */
start = get_timer(0);
do {
if (get_timer(start) > timeout_ms)
return -ETIMEDOUT;
ret = dm_i2c_read(dev, RV3029_RST_CTRL, &ctrl, 1);
if (ret < 0)
return -EIO;
} while (ctrl & RV3029_RST_CTRL_SYSR);
return 0;
}
static int rv3029_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 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;
}
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);
}
if (i <= 0) {
dev_err(dev, "EEPROM busy wait timeout.\n");
return -ETIMEDOUT;
}
return ret;
}
static int rv3029_update_bits(struct udevice *dev, u8 reg, u8 mask, u8 set)
{
u8 buf;
int ret;
ret = dm_i2c_read(dev, reg, &buf, 1);
if (ret < 0)
return ret;
if ((buf & mask) == (set && mask))
return 0;
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);
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,
};
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Date & Time support for Micro Crystal RV-8803-C7.
*
* based on ds1307.c which is
* (C) Copyright 2001, 2002, 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Keith Outwater, keith_outwater@mvis.com`
* Steven Scholz, steven.scholz@imc-berlin.de
*
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <rtc.h>
#include <i2c.h>
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x00
#define RTC_MIN_REG_ADDR 0x01
#define RTC_HR_REG_ADDR 0x02
#define RTC_DAY_REG_ADDR 0x03
#define RTC_DATE_REG_ADDR 0x04
#define RTC_MON_REG_ADDR 0x05
#define RTC_YR_REG_ADDR 0x06
#define RTC_FLAG_REG_ADDR 0x0E
#define RTC_FLAG_BIT_V1F BIT(0)
#define RTC_FLAG_BIT_V2F BIT(1)
#define RTC_CTL_REG_ADDR 0x0F
#define RTC_CTL_BIT_RST BIT(0)
static int rv8803_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
int ret;
u8 buf[7];
debug("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);
if (tm->tm_year < 2000 || tm->tm_year > 2099)
printf("WARNING: year should be between 2000 and 2099!\n");
buf[RTC_YR_REG_ADDR] = bin2bcd(tm->tm_year % 100);
buf[RTC_MON_REG_ADDR] = bin2bcd(tm->tm_mon);
buf[RTC_DAY_REG_ADDR] = 1 << (tm->tm_wday & 0x7);
buf[RTC_DATE_REG_ADDR] = bin2bcd(tm->tm_mday);
buf[RTC_HR_REG_ADDR] = bin2bcd(tm->tm_hour);
buf[RTC_MIN_REG_ADDR] = bin2bcd(tm->tm_min);
buf[RTC_SEC_REG_ADDR] = bin2bcd(tm->tm_sec);
ret = dm_i2c_write(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
return 0;
}
static int rv8803_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
int ret;
u8 buf[7];
int flags;
flags = dm_i2c_reg_read(dev, RTC_FLAG_REG_ADDR);
if (flags < 0)
return flags;
debug("%s: flags=%Xh\n", __func__, flags);
if (flags & RTC_FLAG_BIT_V1F)
printf("### Warning: temperature compensation has stopped\n");
if (flags & RTC_FLAG_BIT_V2F) {
printf("### Warning: Voltage low, data is invalid\n");
return -1;
}
ret = dm_i2c_read(dev, 0, buf, sizeof(buf));
if (ret < 0)
return ret;
tm->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F);
tm->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F);
tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x3F);
tm->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F);
tm->tm_mon = bcd2bin(buf[RTC_MON_REG_ADDR] & 0x1F);
tm->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) + 2000;
tm->tm_wday = fls(buf[RTC_DAY_REG_ADDR] & 0x7F) - 1;
tm->tm_yday = 0;
tm->tm_isdst = 0;
debug("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;
}
static int rv8803_rtc_reset(struct udevice *dev)
{
int ret;
struct rtc_time tmp = {
.tm_year = 2000,
.tm_mon = 1,
.tm_mday = 1,
.tm_hour = 0,
.tm_min = 0,
.tm_sec = 0,
};
/* assert reset */
ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR, RTC_CTL_BIT_RST);
if (ret < 0)
return ret;
/* clear all flags */
ret = dm_i2c_reg_write(dev, RTC_FLAG_REG_ADDR, 0);
if (ret < 0)
return ret;
ret = rv8803_rtc_set(dev, &tmp);
if (ret < 0)
return ret;
/* clear reset */
ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR, 0);
if (ret < 0)
return ret;
debug("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n",
tmp.tm_year, tmp.tm_mon, tmp.tm_mday,
tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
return 0;
}
static int rv8803_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 rv8803_rtc_ops = {
.get = rv8803_rtc_get,
.set = rv8803_rtc_set,
.reset = rv8803_rtc_reset,
};
static const struct udevice_id rv8803_rtc_ids[] = {
{ .compatible = "microcrystal,rv8803", },
{ }
};
U_BOOT_DRIVER(rtc_rv8803) = {
.name = "rtc-rv8803",
.id = UCLASS_RTC,
.probe = rv8803_probe,
.of_match = rv8803_rtc_ids,
.ops = &rv8803_rtc_ops,
};
@@ -0,0 +1,379 @@
/*
* Epson RX8010 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/>.
*/
#include <command.h>
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <rtc.h>
/*---------------------------------------------------------------------*/
/* #undef DEBUG_RTC */
#ifdef DEBUG_RTC
#define DEBUGR(fmt, args...) printf(fmt, ##args)
#else
#define DEBUGR(fmt, args...)
#endif
/*---------------------------------------------------------------------*/
#ifndef CONFIG_SYS_I2C_RTC_ADDR
# define CONFIG_SYS_I2C_RTC_ADDR 0x32
#endif
/*
* RTC register addresses
*/
#define RX8010_SEC 0x10
#define RX8010_MIN 0x11
#define RX8010_HOUR 0x12
#define RX8010_WDAY 0x13
#define RX8010_MDAY 0x14
#define RX8010_MONTH 0x15
#define RX8010_YEAR 0x16
#define RX8010_YEAR 0x16
#define RX8010_RESV17 0x17
#define RX8010_ALMIN 0x18
#define RX8010_ALHOUR 0x19
#define RX8010_ALWDAY 0x1A
#define RX8010_TCOUNT0 0x1B
#define RX8010_TCOUNT1 0x1C
#define RX8010_EXT 0x1D
#define RX8010_FLAG 0x1E
#define RX8010_CTRL 0x1F
/* 0x20 to 0x2F are user registers */
#define RX8010_RESV30 0x30
#define RX8010_RESV31 0x32
#define RX8010_IRQ 0x32
#define RX8010_EXT_WADA BIT(3)
#define RX8010_FLAG_VLF BIT(1)
#define RX8010_FLAG_AF BIT(3)
#define RX8010_FLAG_TF BIT(4)
#define RX8010_FLAG_UF BIT(5)
#define RX8010_CTRL_AIE BIT(3)
#define RX8010_CTRL_UIE BIT(5)
#define RX8010_CTRL_STOP BIT(6)
#define RX8010_CTRL_TEST BIT(7)
#define RX8010_ALARM_AE BIT(7)
#ifdef CONFIG_DM_RTC
#define DEV_TYPE struct udevice
#else
/* Local udevice */
struct ludevice {
u8 chip;
};
#define DEV_TYPE struct ludevice
#endif
static int rx8010sj_rtc_read8(DEV_TYPE *dev, unsigned int reg)
{
u8 val;
int ret;
#ifdef CONFIG_DM_RTC
ret = dm_i2c_read(dev, reg, &val, sizeof(val));
#else
ret = i2c_read(dev->chip, reg, 1, &val, 1);
#endif
return ret < 0 ? ret : val;
}
static int rx8010sj_rtc_write8(DEV_TYPE *dev, unsigned int reg, int val)
{
int ret;
u8 lval = val;
#ifdef CONFIG_DM_RTC
ret = dm_i2c_write(dev, reg, &lval, 1);
#else
ret = i2c_write(dev->chip, reg, 1, &lval, 1);
#endif
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 rx8010sj_rtc_init(DEV_TYPE *dev)
{
u8 ctrl[2];
int need_clear = 0, ret = 0;
/* Initialize reserved registers as specified in datasheet */
ret = rx8010sj_rtc_write8(dev, RX8010_RESV17, 0xD8);
if (ret < 0)
goto error;
ret = rx8010sj_rtc_write8(dev, RX8010_RESV30, 0x00);
if (ret < 0)
goto error;
ret = rx8010sj_rtc_write8(dev, RX8010_RESV31, 0x08);
if (ret < 0)
goto error;
ret = rx8010sj_rtc_write8(dev, RX8010_IRQ, 0x00);
if (ret < 0)
goto error;
for (int i = 0; i < 2; i++) {
ret = rx8010sj_rtc_read8(dev, RX8010_FLAG + i);
if (ret < 0)
goto error;
ctrl[i] = ret;
}
if (ctrl[0] & RX8010_FLAG_VLF)
printf("RTC low voltage detected\n");
if (ctrl[0] & RX8010_FLAG_AF) {
printf("Alarm was detected\n");
need_clear = 1;
}
if (ctrl[0] & RX8010_FLAG_TF)
need_clear = 1;
if (ctrl[0] & RX8010_FLAG_UF)
need_clear = 1;
if (need_clear) {
ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);
ret = rx8010sj_rtc_write8(dev, RX8010_FLAG, ctrl[0]);
if (ret < 0)
goto error;
}
return;
error:
printf("Error rtc init.\n");
}
/* Get the current time from the RTC */
static int rx8010sj_rtc_get(DEV_TYPE *dev, struct rtc_time *tmp)
{
u8 date[7];
int flagreg;
int ret;
flagreg = rx8010sj_rtc_read8(dev, RX8010_FLAG);
if (flagreg < 0) {
DEBUGR("Error reading from RTC. err: %d\n", flagreg);
return -EIO;
}
if (flagreg & RX8010_FLAG_VLF) {
DEBUGR("RTC low voltage detected\n");
return -EINVAL;
}
for (int i = 0; i < 7; i++) {
ret = rx8010sj_rtc_read8(dev, RX8010_SEC + i);
if (ret < 0) {
DEBUGR("Error reading from RTC. err: %d\n", ret);
return -EIO;
}
date[i] = ret;
}
tmp->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);
tmp->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);
tmp->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f);
tmp->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f);
tmp->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f);
tmp->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 2000;
tmp->tm_wday = 0;
tmp->tm_yday = 0;
tmp->tm_isdst = 0;
DEBUGR("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);
return 0;
}
/* Set the RTC */
static int rx8010sj_rtc_set(DEV_TYPE *dev, const struct rtc_time *tm)
{
u8 date[7];
int ctrl, flagreg;
int ret;
ret = validate_time(tm);
if (ret < 0)
return -EINVAL;
/* set STOP bit before changing clock/calendar */
ctrl = rx8010sj_rtc_read8(dev, RX8010_CTRL);
if (ctrl < 0)
return ctrl;
ret = rx8010sj_rtc_write8(dev, RX8010_CTRL, ctrl | RX8010_CTRL_STOP);
if (ret < 0)
return ret;
date[RX8010_SEC - RX8010_SEC] = bin2bcd(tm->tm_sec);
date[RX8010_MIN - RX8010_SEC] = bin2bcd(tm->tm_min);
date[RX8010_HOUR - RX8010_SEC] = bin2bcd(tm->tm_hour);
date[RX8010_MDAY - RX8010_SEC] = bin2bcd(tm->tm_mday);
date[RX8010_MONTH - RX8010_SEC] = bin2bcd(tm->tm_mon);
date[RX8010_YEAR - RX8010_SEC] = bin2bcd(tm->tm_year - 2000);
date[RX8010_WDAY - RX8010_SEC] = bin2bcd(tm->tm_wday);
for (int i = 0; i < 7; i++) {
ret = rx8010sj_rtc_write8(dev, RX8010_SEC + i, date[i]);
if (ret < 0) {
DEBUGR("Error writing to RTC. err: %d\n", ret);
return -EIO;
}
}
/* clear STOP bit after changing clock/calendar */
ctrl = rx8010sj_rtc_read8(dev, RX8010_CTRL);
if (ctrl < 0)
return ctrl;
ret = rx8010sj_rtc_write8(dev, RX8010_CTRL, ctrl & ~RX8010_CTRL_STOP);
if (ret < 0)
return ret;
flagreg = rx8010sj_rtc_read8(dev, RX8010_FLAG);
if (flagreg < 0)
return flagreg;
if (flagreg & RX8010_FLAG_VLF)
ret = rx8010sj_rtc_write8(dev, RX8010_FLAG,
flagreg & ~RX8010_FLAG_VLF);
return 0;
}
/* Reset the RTC. */
static int rx8010sj_rtc_reset(DEV_TYPE *dev)
{
/* Not needed */
return 0;
}
#ifndef CONFIG_DM_RTC
int rtc_get(struct rtc_time *tm)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
return rx8010sj_rtc_get(&dev, tm);
}
int rtc_set(struct rtc_time *tm)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
return rx8010sj_rtc_set(&dev, tm);
}
void rtc_reset(void)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
rx8010sj_rtc_reset(&dev);
}
void rtc_init(void)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
rx8010sj_rtc_init(&dev);
}
#else
static int rx8010sj_probe(struct udevice *dev)
{
rx8010sj_rtc_init(dev);
return 0;
}
static const struct rtc_ops rx8010sj_rtc_ops = {
.get = rx8010sj_rtc_get,
.set = rx8010sj_rtc_set,
.read8 = rx8010sj_rtc_read8,
.write8 = rx8010sj_rtc_write8,
.reset = rx8010sj_rtc_reset,
};
static const struct udevice_id rx8010sj_rtc_ids[] = {
{ .compatible = "epson,rx8010sj-rtc" },
{ .compatible = "epson,rx8010" },
{ }
};
U_BOOT_DRIVER(rx8010sj_rtc) = {
.name = "rx8010sj_rtc",
.id = UCLASS_RTC,
.probe = rx8010sj_probe,
.of_match = rx8010sj_rtc_ids,
.ops = &rx8010sj_rtc_ops,
};
#endif
@@ -0,0 +1,306 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007
* Matthias Fuchs, esd gmbh, matthias.fuchs@esd-electronics.com.
*/
/*
* Epson RX8025 RTC driver.
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <i2c.h>
#include <rtc.h>
/*---------------------------------------------------------------------*/
#undef DEBUG_RTC
#ifdef DEBUG_RTC
#define DEBUGR(fmt,args...) printf(fmt ,##args)
#else
#define DEBUGR(fmt,args...)
#endif
/*---------------------------------------------------------------------*/
#ifndef CONFIG_SYS_I2C_RTC_ADDR
# define CONFIG_SYS_I2C_RTC_ADDR 0x32
#endif
#ifdef CONFIG_DM_RTC
#define DEV_TYPE struct udevice
#else
/* Local udevice */
struct ludevice {
u8 chip;
};
#define DEV_TYPE struct ludevice
#endif
/*
* RTC register addresses
*/
#define RTC_SEC_REG_ADDR 0x00
#define RTC_MIN_REG_ADDR 0x01
#define RTC_HR_REG_ADDR 0x02
#define RTC_DAY_REG_ADDR 0x03
#define RTC_DATE_REG_ADDR 0x04
#define RTC_MON_REG_ADDR 0x05
#define RTC_YR_REG_ADDR 0x06
#define RTC_CTL1_REG_ADDR 0x0e
#define RTC_CTL2_REG_ADDR 0x0f
/*
* Control register 1 bits
*/
#define RTC_CTL1_BIT_2412 0x20
/*
* Control register 2 bits
*/
#define RTC_CTL2_BIT_PON 0x10
#define RTC_CTL2_BIT_VDET 0x40
#define RTC_CTL2_BIT_XST 0x20
#define RTC_CTL2_BIT_VDSL 0x80
/*
* Note: the RX8025 I2C RTC requires register
* reads and write to consist of a single bus
* cycle. It is not allowed to write the register
* address in a first cycle that is terminated by
* a STOP condition. The chips needs a 'restart'
* sequence (start sequence without a prior stop).
* This driver has been written for a 4xx board.
* U-Boot's 4xx i2c driver is currently not capable
* to generate such cycles to some work arounds
* are used.
*/
/* static uchar rtc_read (uchar reg); */
#ifdef CONFIG_DM_RTC
/*
* on mpc85xx based board with DM and offset len 1
* accessing rtc works fine. May we can drop this ?
*/
#define rtc_read(reg) buf[(reg) & 0xf]
#else
#define rtc_read(reg) buf[((reg) + 1) & 0xf]
#endif
static int rtc_write(DEV_TYPE *dev, uchar reg, uchar val);
/*
* Get the current time from the RTC
*/
static int rx8025_rtc_get(DEV_TYPE *dev, struct rtc_time *tmp)
{
int rel = 0;
uchar sec, min, hour, mday, wday, mon, year, ctl2;
uchar buf[16];
#ifdef CONFIG_DM_RTC
if (dm_i2c_read(dev, 0, buf, sizeof(buf))) {
#else
if (i2c_read(dev->chip, 0, 0, buf, 16)) {
#endif
printf("Error reading from RTC\n");
return -EIO;
}
sec = rtc_read(RTC_SEC_REG_ADDR);
min = rtc_read(RTC_MIN_REG_ADDR);
hour = rtc_read(RTC_HR_REG_ADDR);
wday = rtc_read(RTC_DAY_REG_ADDR);
mday = rtc_read(RTC_DATE_REG_ADDR);
mon = rtc_read(RTC_MON_REG_ADDR);
year = rtc_read(RTC_YR_REG_ADDR);
DEBUGR("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
/* dump status */
ctl2 = rtc_read(RTC_CTL2_REG_ADDR);
if (ctl2 & RTC_CTL2_BIT_PON) {
printf("RTC: power-on detected\n");
rel = -1;
}
if (ctl2 & RTC_CTL2_BIT_VDET) {
printf("RTC: voltage drop detected\n");
rel = -1;
}
if (!(ctl2 & RTC_CTL2_BIT_XST)) {
printf("RTC: oscillator stop detected\n");
rel = -1;
}
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
if (rtc_read(RTC_CTL1_REG_ADDR) & RTC_CTL1_BIT_2412)
tmp->tm_hour = bcd2bin(hour & 0x3F);
else
tmp->tm_hour = bcd2bin(hour & 0x1F) % 12 +
((hour & 0x20) ? 12 : 0);
tmp->tm_mday = bcd2bin (mday & 0x3F);
tmp->tm_mon = bcd2bin (mon & 0x1F);
tmp->tm_year = bcd2bin (year) + ( bcd2bin (year) >= 70 ? 1900 : 2000);
tmp->tm_wday = bcd2bin (wday & 0x07);
tmp->tm_yday = 0;
tmp->tm_isdst= 0;
DEBUGR("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);
return rel;
}
/*
* Set the RTC
*/
static int rx8025_rtc_set(DEV_TYPE *dev, const struct rtc_time *tmp)
{
DEBUGR("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 < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
if (rtc_write(dev, RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100)))
return -EIO;
if (rtc_write(dev, RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon)))
return -EIO;
if (rtc_write(dev, RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday)))
return -EIO;
if (rtc_write(dev, RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday)))
return -EIO;
if (rtc_write(dev, RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour)))
return -EIO;
if (rtc_write(dev, RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min)))
return -EIO;
if (rtc_write(dev, RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec)))
return -EIO;
return rtc_write(dev, RTC_CTL1_REG_ADDR, RTC_CTL1_BIT_2412);
}
/*
* Reset the RTC
*/
static int rx8025_rtc_reset(DEV_TYPE *dev)
{
uchar buf[16];
uchar ctl2;
#ifdef CONFIG_DM_RTC
if (dm_i2c_read(dev, 0, buf, sizeof(buf))) {
#else
if (i2c_read(dev->chip, 0, 0, buf, 16)) {
#endif
printf("Error reading from RTC\n");
return -EIO;
}
ctl2 = rtc_read(RTC_CTL2_REG_ADDR);
ctl2 &= ~(RTC_CTL2_BIT_PON | RTC_CTL2_BIT_VDET);
ctl2 |= RTC_CTL2_BIT_XST | RTC_CTL2_BIT_VDSL;
return rtc_write(dev, RTC_CTL2_REG_ADDR, ctl2);
}
/*
* Helper functions
*/
static int rtc_write(DEV_TYPE *dev, uchar reg, uchar val)
{
uchar buf[2];
buf[0] = reg << 4;
buf[1] = val;
#ifdef CONFIG_DM_RTC
if (dm_i2c_write(dev, 0, buf, 2)) {
#else
if (i2c_write(dev->chip, 0, 0, buf, 2) != 0) {
#endif
printf("Error writing to RTC\n");
return -EIO;
}
return 0;
}
#ifdef CONFIG_DM_RTC
static int rx8025_probe(struct udevice *dev)
{
uchar buf[16];
int ret = 0;
if (i2c_get_chip_offset_len(dev) != 1)
ret = i2c_set_chip_offset_len(dev, 1);
if (ret)
return ret;
return dm_i2c_read(dev, 0, buf, sizeof(buf));
}
static const struct rtc_ops rx8025_rtc_ops = {
.get = rx8025_rtc_get,
.set = rx8025_rtc_set,
.reset = rx8025_rtc_reset,
};
static const struct udevice_id rx8025_rtc_ids[] = {
{ .compatible = "epson,rx8025" },
{ }
};
U_BOOT_DRIVER(rx8010sj_rtc) = {
.name = "rx8025_rtc",
.id = UCLASS_RTC,
.probe = rx8025_probe,
.of_match = rx8025_rtc_ids,
.ops = &rx8025_rtc_ops,
};
#else
int rtc_get(struct rtc_time *tm)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
return rx8025_rtc_get(&dev, tm);
}
int rtc_set(struct rtc_time *tm)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
return rx8025_rtc_set(&dev, tm);
}
void rtc_reset(void)
{
struct ludevice dev = {
.chip = CONFIG_SYS_I2C_RTC_ADDR,
};
rx8025_rtc_reset(&dev);
}
#endif
@@ -0,0 +1,366 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/>.
*/
#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" },
{ .compatible = "sii,s35392a" },
{ .compatible = "s35392a" },
{ }
};
U_BOOT_DRIVER(s35392a_rtc) = {
.name = "s35392a_rtc",
.id = UCLASS_RTC,
.probe = s35392a_probe,
.of_match = s35392a_rtc_ids,
.ops = &s35392a_rtc_ops,
};
#endif
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2003
* David Müller ELSOFT AG Switzerland. d.mueller@elsoft.ch
*/
/*
* Date & Time support for the built-in Samsung S3C24X0 RTC
*/
#include <common.h>
#include <command.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <rtc.h>
#include <asm/io.h>
#include <linux/compiler.h>
typedef enum {
RTC_ENABLE,
RTC_DISABLE
} RTC_ACCESS;
static inline void SetRTC_Access(RTC_ACCESS a)
{
struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc();
switch (a) {
case RTC_ENABLE:
writeb(readb(&rtc->rtccon) | 0x01, &rtc->rtccon);
break;
case RTC_DISABLE:
writeb(readb(&rtc->rtccon) & ~0x01, &rtc->rtccon);
break;
}
}
/* ------------------------------------------------------------------------- */
int rtc_get(struct rtc_time *tmp)
{
struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc();
uchar sec, min, hour, mday, wday, mon, year;
__maybe_unused uchar a_sec, a_min, a_hour, a_date,
a_mon, a_year, a_armed;
/* enable access to RTC registers */
SetRTC_Access(RTC_ENABLE);
/* read RTC registers */
do {
sec = readb(&rtc->bcdsec);
min = readb(&rtc->bcdmin);
hour = readb(&rtc->bcdhour);
mday = readb(&rtc->bcddate);
wday = readb(&rtc->bcdday);
mon = readb(&rtc->bcdmon);
year = readb(&rtc->bcdyear);
} while (sec != readb(&rtc->bcdsec));
/* read ALARM registers */
a_sec = readb(&rtc->almsec);
a_min = readb(&rtc->almmin);
a_hour = readb(&rtc->almhour);
a_date = readb(&rtc->almdate);
a_mon = readb(&rtc->almmon);
a_year = readb(&rtc->almyear);
a_armed = readb(&rtc->rtcalm);
/* disable access to RTC registers */
SetRTC_Access(RTC_DISABLE);
#ifdef RTC_DEBUG
printf("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
printf("Alarms: %02x: year: %02x month: %02x date: %02x hour: "
"%02x min: %02x sec: %02x\n",
a_armed, a_year, a_mon, a_date, a_hour, a_min, a_sec);
#endif
tmp->tm_sec = bcd2bin(sec & 0x7F);
tmp->tm_min = bcd2bin(min & 0x7F);
tmp->tm_hour = bcd2bin(hour & 0x3F);
tmp->tm_mday = bcd2bin(mday & 0x3F);
tmp->tm_mon = bcd2bin(mon & 0x1F);
tmp->tm_year = bcd2bin(year);
tmp->tm_wday = bcd2bin(wday & 0x07);
if (tmp->tm_year < 70)
tmp->tm_year += 2000;
else
tmp->tm_year += 1900;
tmp->tm_yday = 0;
tmp->tm_isdst = 0;
#ifdef RTC_DEBUG
printf("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);
#endif
return 0;
}
int rtc_set(struct rtc_time *tmp)
{
struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc();
uchar sec, min, hour, mday, wday, mon, year;
#ifdef RTC_DEBUG
printf("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);
#endif
year = bin2bcd(tmp->tm_year % 100);
mon = bin2bcd(tmp->tm_mon);
wday = bin2bcd(tmp->tm_wday);
mday = bin2bcd(tmp->tm_mday);
hour = bin2bcd(tmp->tm_hour);
min = bin2bcd(tmp->tm_min);
sec = bin2bcd(tmp->tm_sec);
/* enable access to RTC registers */
SetRTC_Access(RTC_ENABLE);
/* write RTC registers */
writeb(sec, &rtc->bcdsec);
writeb(min, &rtc->bcdmin);
writeb(hour, &rtc->bcdhour);
writeb(mday, &rtc->bcddate);
writeb(wday, &rtc->bcdday);
writeb(mon, &rtc->bcdmon);
writeb(year, &rtc->bcdyear);
/* disable access to RTC registers */
SetRTC_Access(RTC_DISABLE);
return 0;
}
void rtc_reset(void)
{
struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc();
writeb((readb(&rtc->rtccon) & ~0x06) | 0x08, &rtc->rtccon);
writeb(readb(&rtc->rtccon) & ~(0x08 | 0x01), &rtc->rtccon);
}
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <rtc.h>
#include <asm/rtc.h>
#define REG_COUNT 0x80
static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time)
{
time->tm_sec = dm_i2c_reg_read(dev, REG_SEC);
if (time->tm_sec < 0)
return time->tm_sec;
time->tm_min = dm_i2c_reg_read(dev, REG_MIN);
if (time->tm_min < 0)
return time->tm_min;
time->tm_hour = dm_i2c_reg_read(dev, REG_HOUR);
if (time->tm_hour < 0)
return time->tm_hour;
time->tm_mday = dm_i2c_reg_read(dev, REG_MDAY);
if (time->tm_mday < 0)
return time->tm_mday;
time->tm_mon = dm_i2c_reg_read(dev, REG_MON);
if (time->tm_mon < 0)
return time->tm_mon;
time->tm_year = dm_i2c_reg_read(dev, REG_YEAR);
if (time->tm_year < 0)
return time->tm_year;
time->tm_year += 1900;
time->tm_wday = dm_i2c_reg_read(dev, REG_WDAY);
if (time->tm_wday < 0)
return time->tm_wday;
return 0;
}
static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time)
{
int ret;
ret = dm_i2c_reg_write(dev, REG_SEC, time->tm_sec);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MIN, time->tm_min);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_HOUR, time->tm_hour);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MDAY, time->tm_mday);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MON, time->tm_mon);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_YEAR, time->tm_year - 1900);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_WDAY, time->tm_wday);
if (ret < 0)
return ret;
return 0;
}
static int sandbox_rtc_reset(struct udevice *dev)
{
return dm_i2c_reg_write(dev, REG_RESET, 0);
}
static int sandbox_rtc_read8(struct udevice *dev, unsigned int reg)
{
return dm_i2c_reg_read(dev, reg);
}
static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val)
{
return dm_i2c_reg_write(dev, reg, val);
}
static const struct rtc_ops sandbox_rtc_ops = {
.get = sandbox_rtc_get,
.set = sandbox_rtc_set,
.reset = sandbox_rtc_reset,
.read8 = sandbox_rtc_read8,
.write8 = sandbox_rtc_write8,
};
static const struct udevice_id sandbox_rtc_ids[] = {
{ .compatible = "sandbox-rtc" },
{ }
};
U_BOOT_DRIVER(rtc_sandbox) = {
.name = "rtc-sandbox",
.id = UCLASS_RTC,
.of_match = sandbox_rtc_ids,
.ops = &sandbox_rtc_ops,
};
@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <rtc.h>
#include <asm/io.h>
#include <linux/iopoll.h>
#define STM32_RTC_TR 0x00
#define STM32_RTC_DR 0x04
#define STM32_RTC_ISR 0x0C
#define STM32_RTC_PRER 0x10
#define STM32_RTC_CR 0x18
#define STM32_RTC_WPR 0x24
/* STM32_RTC_TR bit fields */
#define STM32_RTC_SEC_SHIFT 0
#define STM32_RTC_SEC GENMASK(6, 0)
#define STM32_RTC_MIN_SHIFT 8
#define STM32_RTC_MIN GENMASK(14, 8)
#define STM32_RTC_HOUR_SHIFT 16
#define STM32_RTC_HOUR GENMASK(21, 16)
/* STM32_RTC_DR bit fields */
#define STM32_RTC_DATE_SHIFT 0
#define STM32_RTC_DATE GENMASK(5, 0)
#define STM32_RTC_MONTH_SHIFT 8
#define STM32_RTC_MONTH GENMASK(12, 8)
#define STM32_RTC_WDAY_SHIFT 13
#define STM32_RTC_WDAY GENMASK(15, 13)
#define STM32_RTC_YEAR_SHIFT 16
#define STM32_RTC_YEAR GENMASK(23, 16)
/* STM32_RTC_CR bit fields */
#define STM32_RTC_CR_FMT BIT(6)
/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */
#define STM32_RTC_ISR_INITS BIT(4)
#define STM32_RTC_ISR_RSF BIT(5)
#define STM32_RTC_ISR_INITF BIT(6)
#define STM32_RTC_ISR_INIT BIT(7)
/* STM32_RTC_PRER bit fields */
#define STM32_RTC_PRER_PRED_S_SHIFT 0
#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
#define STM32_RTC_PRER_PRED_A_SHIFT 16
#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
/* STM32_RTC_WPR key constants */
#define RTC_WPR_1ST_KEY 0xCA
#define RTC_WPR_2ND_KEY 0x53
#define RTC_WPR_WRONG_KEY 0xFF
struct stm32_rtc_priv {
fdt_addr_t base;
};
static int stm32_rtc_get(struct udevice *dev, struct rtc_time *tm)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
u32 tr, dr;
tr = readl(priv->base + STM32_RTC_TR);
dr = readl(priv->base + STM32_RTC_DR);
tm->tm_sec = bcd2bin((tr & STM32_RTC_SEC) >> STM32_RTC_SEC_SHIFT);
tm->tm_min = bcd2bin((tr & STM32_RTC_MIN) >> STM32_RTC_MIN_SHIFT);
tm->tm_hour = bcd2bin((tr & STM32_RTC_HOUR) >> STM32_RTC_HOUR_SHIFT);
tm->tm_mday = bcd2bin((dr & STM32_RTC_DATE) >> STM32_RTC_DATE_SHIFT);
tm->tm_mon = bcd2bin((dr & STM32_RTC_MONTH) >> STM32_RTC_MONTH_SHIFT);
tm->tm_year = 2000 +
bcd2bin((dr & STM32_RTC_YEAR) >> STM32_RTC_YEAR_SHIFT);
tm->tm_wday = bcd2bin((dr & STM32_RTC_WDAY) >> STM32_RTC_WDAY_SHIFT);
tm->tm_yday = 0;
tm->tm_isdst = 0;
dev_dbg(dev, "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;
}
static void stm32_rtc_unlock(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
writel(RTC_WPR_1ST_KEY, priv->base + STM32_RTC_WPR);
writel(RTC_WPR_2ND_KEY, priv->base + STM32_RTC_WPR);
}
static void stm32_rtc_lock(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
writel(RTC_WPR_WRONG_KEY, priv->base + STM32_RTC_WPR);
}
static int stm32_rtc_enter_init_mode(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
u32 isr = readl(priv->base + STM32_RTC_ISR);
if (!(isr & STM32_RTC_ISR_INITF)) {
isr |= STM32_RTC_ISR_INIT;
writel(isr, priv->base + STM32_RTC_ISR);
return readl_poll_timeout(priv->base + STM32_RTC_ISR,
isr,
(isr & STM32_RTC_ISR_INITF),
100000);
}
return 0;
}
static int stm32_rtc_wait_sync(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
u32 isr = readl(priv->base + STM32_RTC_ISR);
isr &= ~STM32_RTC_ISR_RSF;
writel(isr, priv->base + STM32_RTC_ISR);
/*
* Wait for RSF to be set to ensure the calendar registers are
* synchronised, it takes around 2 rtc_ck clock cycles
*/
return readl_poll_timeout(priv->base + STM32_RTC_ISR,
isr, (isr & STM32_RTC_ISR_RSF),
100000);
}
static void stm32_rtc_exit_init_mode(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
u32 isr = readl(priv->base + STM32_RTC_ISR);
isr &= ~STM32_RTC_ISR_INIT;
writel(isr, priv->base + STM32_RTC_ISR);
}
static int stm32_rtc_set_time(struct udevice *dev, u32 time, u32 date)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
int ret;
stm32_rtc_unlock(dev);
ret = stm32_rtc_enter_init_mode(dev);
if (ret)
goto lock;
writel(time, priv->base + STM32_RTC_TR);
writel(date, priv->base + STM32_RTC_DR);
stm32_rtc_exit_init_mode(dev);
ret = stm32_rtc_wait_sync(dev);
lock:
stm32_rtc_lock(dev);
return ret;
}
static int stm32_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
u32 t, d;
dev_dbg(dev, "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);
if (tm->tm_year < 2000 || tm->tm_year > 2099)
return -EINVAL;
/* Time in BCD format */
t = (bin2bcd(tm->tm_sec) << STM32_RTC_SEC_SHIFT) & STM32_RTC_SEC;
t |= (bin2bcd(tm->tm_min) << STM32_RTC_MIN_SHIFT) & STM32_RTC_MIN;
t |= (bin2bcd(tm->tm_hour) << STM32_RTC_HOUR_SHIFT) & STM32_RTC_HOUR;
/* Date in BCD format */
d = (bin2bcd(tm->tm_mday) << STM32_RTC_DATE_SHIFT) & STM32_RTC_DATE;
d |= (bin2bcd(tm->tm_mon) << STM32_RTC_MONTH_SHIFT) & STM32_RTC_MONTH;
d |= (bin2bcd(tm->tm_year - 2000) << STM32_RTC_YEAR_SHIFT) &
STM32_RTC_YEAR;
d |= (bin2bcd(tm->tm_wday) << STM32_RTC_WDAY_SHIFT) & STM32_RTC_WDAY;
return stm32_rtc_set_time(dev, t, d);
}
static int stm32_rtc_reset(struct udevice *dev)
{
dev_dbg(dev, "Reset DATE\n");
return stm32_rtc_set_time(dev, 0, 0);
}
static int stm32_rtc_init(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
unsigned int rate;
struct clk clk;
int ret;
u32 isr = readl(priv->base + STM32_RTC_ISR);
if (isr & STM32_RTC_ISR_INITS)
return 0;
ret = clk_get_by_index(dev, 1, &clk);
if (ret)
return ret;
ret = clk_enable(&clk);
if (ret) {
clk_free(&clk);
return ret;
}
rate = clk_get_rate(&clk);
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) {
pred_s = (rate / (pred_a + 1)) - 1;
if (((pred_s + 1) * (pred_a + 1)) == rate)
break;
}
/*
* Can't find a 1Hz, so give priority to RTC power consumption
* by choosing the higher possible value for prediv_a
*/
if (pred_s > pred_s_max || pred_a > pred_a_max) {
pred_a = pred_a_max;
pred_s = (rate / (pred_a + 1)) - 1;
}
stm32_rtc_unlock(dev);
ret = stm32_rtc_enter_init_mode(dev);
if (ret) {
dev_err(dev,
"Can't enter in init mode. Prescaler config failed.\n");
goto unlock;
}
prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
writel(prer, priv->base + STM32_RTC_PRER);
/* Force 24h time format */
cr = readl(priv->base + STM32_RTC_CR);
cr &= ~STM32_RTC_CR_FMT;
writel(cr, priv->base + STM32_RTC_CR);
stm32_rtc_exit_init_mode(dev);
ret = stm32_rtc_wait_sync(dev);
unlock:
stm32_rtc_lock(dev);
if (ret) {
clk_disable(&clk);
clk_free(&clk);
}
return ret;
}
static int stm32_rtc_probe(struct udevice *dev)
{
struct stm32_rtc_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret;
priv->base = dev_read_addr(dev);
if (priv->base == FDT_ADDR_T_NONE)
return -EINVAL;
ret = clk_get_by_index(dev, 0, &clk);
if (ret)
return ret;
ret = clk_enable(&clk);
if (ret) {
clk_free(&clk);
return ret;
}
ret = stm32_rtc_init(dev);
if (ret) {
clk_disable(&clk);
clk_free(&clk);
}
return ret;
}
static const struct rtc_ops stm32_rtc_ops = {
.get = stm32_rtc_get,
.set = stm32_rtc_set,
.reset = stm32_rtc_reset,
};
static const struct udevice_id stm32_rtc_ids[] = {
{ .compatible = "st,stm32mp1-rtc" },
{ }
};
U_BOOT_DRIVER(rtc_stm32) = {
.name = "rtc-stm32",
.id = UCLASS_RTC,
.probe = stm32_rtc_probe,
.of_match = stm32_rtc_ids,
.ops = &stm32_rtc_ops,
.priv_auto_alloc_size = sizeof(struct stm32_rtc_priv),
};
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007
* Stefan Roese, DENX Software Engineering, sr@denx.de.
*
* based on a the Linux rtc-x1207.c driver which is:
* Copyright 2004 Karen Spearel
* Copyright 2005 Alessandro Zummo
*
* Information and datasheet:
* http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html
*/
/*
* Date & Time support for Xicor/Intersil X1205 RTC
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <rtc.h>
#include <i2c.h>
#define CCR_SEC 0
#define CCR_MIN 1
#define CCR_HOUR 2
#define CCR_MDAY 3
#define CCR_MONTH 4
#define CCR_YEAR 5
#define CCR_WDAY 6
#define CCR_Y2K 7
#define X1205_REG_SR 0x3F /* status register */
#define X1205_REG_Y2K 0x37
#define X1205_REG_DW 0x36
#define X1205_REG_YR 0x35
#define X1205_REG_MO 0x34
#define X1205_REG_DT 0x33
#define X1205_REG_HR 0x32
#define X1205_REG_MN 0x31
#define X1205_REG_SC 0x30
#define X1205_REG_DTR 0x13
#define X1205_REG_ATR 0x12
#define X1205_REG_INT 0x11
#define X1205_REG_0 0x10
#define X1205_REG_Y2K1 0x0F
#define X1205_REG_DWA1 0x0E
#define X1205_REG_YRA1 0x0D
#define X1205_REG_MOA1 0x0C
#define X1205_REG_DTA1 0x0B
#define X1205_REG_HRA1 0x0A
#define X1205_REG_MNA1 0x09
#define X1205_REG_SCA1 0x08
#define X1205_REG_Y2K0 0x07
#define X1205_REG_DWA0 0x06
#define X1205_REG_YRA0 0x05
#define X1205_REG_MOA0 0x04
#define X1205_REG_DTA0 0x03
#define X1205_REG_HRA0 0x02
#define X1205_REG_MNA0 0x01
#define X1205_REG_SCA0 0x00
#define X1205_CCR_BASE 0x30 /* Base address of CCR */
#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */
#define X1205_SR_RTCF 0x01 /* Clock failure */
#define X1205_SR_WEL 0x02 /* Write Enable Latch */
#define X1205_SR_RWEL 0x04 /* Register Write Enable */
#define X1205_DTR_DTR0 0x01
#define X1205_DTR_DTR1 0x02
#define X1205_DTR_DTR2 0x04
#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */
static void rtc_write(int reg, u8 val)
{
i2c_write(CONFIG_SYS_I2C_RTC_ADDR, reg, 2, &val, 1);
}
/*
* In the routines that deal directly with the x1205 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
* Epoch is initialized as 2000. Time is set to UTC.
*/
int rtc_get(struct rtc_time *tm)
{
u8 buf[8];
i2c_read(CONFIG_SYS_I2C_RTC_ADDR, X1205_CCR_BASE, 2, buf, 8);
debug("%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
tm->tm_sec = bcd2bin(buf[CCR_SEC]);
tm->tm_min = bcd2bin(buf[CCR_MIN]);
tm->tm_hour = bcd2bin(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
tm->tm_mday = bcd2bin(buf[CCR_MDAY]);
tm->tm_mon = bcd2bin(buf[CCR_MONTH]); /* mon is 0-11 */
tm->tm_year = bcd2bin(buf[CCR_YEAR])
+ (bcd2bin(buf[CCR_Y2K]) * 100);
tm->tm_wday = buf[CCR_WDAY];
debug("%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
return 0;
}
int rtc_set(struct rtc_time *tm)
{
int i;
u8 buf[8];
debug("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);
buf[CCR_SEC] = bin2bcd(tm->tm_sec);
buf[CCR_MIN] = bin2bcd(tm->tm_min);
/* set hour and 24hr bit */
buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | X1205_HR_MIL;
buf[CCR_MDAY] = bin2bcd(tm->tm_mday);
/* month, 1 - 12 */
buf[CCR_MONTH] = bin2bcd(tm->tm_mon);
/* year, since the rtc epoch*/
buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100);
buf[CCR_WDAY] = tm->tm_wday & 0x07;
buf[CCR_Y2K] = bin2bcd(tm->tm_year / 100);
/* this sequence is required to unlock the chip */
rtc_write(X1205_REG_SR, X1205_SR_WEL);
rtc_write(X1205_REG_SR, X1205_SR_WEL | X1205_SR_RWEL);
/* write register's data */
for (i = 0; i < 8; i++)
rtc_write(X1205_CCR_BASE + i, buf[i]);
rtc_write(X1205_REG_SR, 0);
return 0;
}
void rtc_reset(void)
{
/*
* Nothing to do
*/
}