GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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, ¢, 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, ¢, 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, ¢, 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(®s->time);
|
||||
|
||||
/* read the date register */
|
||||
date = readl(®s->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, ®s->time);
|
||||
|
||||
/* write the date register */
|
||||
writel(date, ®s->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(®s->time);
|
||||
sec = bcd2bin((time >> MVRTC_SEC_SFT) & MVRTC_SEC_MSK);
|
||||
udelay(1000000);
|
||||
time = readl(®s->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
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user