diff options
Diffstat (limited to 'drivers/rtc')
37 files changed, 9005 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig new file mode 100644 index 00000000000..7fc53a6d61e --- /dev/null +++ b/drivers/rtc/Kconfig @@ -0,0 +1,310 @@ +# +# 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 VPL_DM_RTC + bool "Enable Driver Model for RTC drivers in VPL" + depends on VPL_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_ARMADA38X + bool "Enable Armada 38x Marvell SoC RTC" + depends on DM_RTC && ARCH_MVEBU + help + This adds support for the in-chip RTC that can be found in the + Armada 38x Marvell's SoC devices. + +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 DS1339 and + compatible Real Time Clock devices. + +config RTC_DS1337 + bool "Enable DS1337 driver" + help + Support for Dallas Semiconductor (now Maxim) DS1337/8/9 compatible + Real Time Clock devices. + +config RTC_DS1337_NOOSC + bool "Enable support for no oscillator output in DS1337 driver" + depends on RTC_DS1337 + +config RTC_DS1338 + bool "Enable DS1338 driver" + help + Support for Dallas Semiconductor (now Maxim) DS1338 and compatible + Real Time Clock devices. + +config RTC_DS1374 + bool "Enable DS1374 driver" + depends on !DM_RTC + help + Support for Dallas Semiconductor (now Maxim) DS1374 and compatible + Real Time Clock devices. + +config RTC_DS3231 + bool "Enable DS3231 driver" + help + Support for Dallas Semiconductor (now Maxim) DS3231 compatible + Real Time Clock devices. + +config RTC_DS3232 + bool "Enable DS3232 driver" + depends on DM_RTC + depends on DM_I2C + help + Support for Dallas Semiconductor (now Maxim) DS3232 compatible + Real Time Clock devices. + +config RTC_EMULATION + bool "Enable emulated RTC" + depends on DM_RTC + help + On a board without hardware clock this software real time clock can be + used. The initial time may be provided via the environment variable + 'rtc_emul_epoch' as a decimal string indicating seconds since + 1970-01-01. If the environment variable is missing, the build time is + used to initialize the RTC. The time can be adjusted manually via the + 'date' command or the 'sntp' command can be used to update the RTC + with the time from a network time server. See CONFIG_CMD_SNTP and + CONFIG_BOOTP_NTPSERVER. The RTC time is advanced according to CPU + ticks. + +config RTC_GOLDFISH + bool "Enable Goldfish driver" + depends on DM_RTC + help + The Goldfish RTC is a virtual device which may be supplied by QEMU. + It is enabled by default on QEMU's RISC-V virt machine. + +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_MAX313XX + bool "Analog Devices MAX313XX RTC driver" + depends on DM_RTC + depends on DM_I2C + help + If you say yes here you will get support for the + Analog Devices MAX313XX series RTC family. + + Chip features not currently supported: + - Timestamp registers as SRAM + - Temperature sensor + - CLKOUT generation + +config RTC_PCF8563 + tristate "Philips PCF8563" + help + If you say yes here you get support for the Philips PCF8563 RTC + and compatible chips. + +config RTC_PT7C4338 + bool "Enable Pericom Technology PT7C4338 RTC driver" + +config RTC_RV3028 + bool "Enable RV3028 driver" + depends on DM_RTC + help + The MicroCrystal RV3028 is a I2C Real Time Clock (RTC) + +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" + depends on DM_RTC + 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_MC13XXX + bool "Enable MC13XXX RTC driver" + depends on !DM_RTC + +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 MCFRTC + bool "Use common CF RTC driver" + depends on M68K + +config SYS_MCFRTC_BASE + hex "Base address for RTC in immap.h" + depends on MCFRTC + +config RTC_MXS + bool "Enable i.MXS RTC driver" + depends on ARCH_MX23 || ARCH_MX28 + +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_SANDBOX + bool "Enable sandbox RTC driver" + depends on SANDBOX && DM_RTC + default y + help + Enable the sandbox RTC driver. This driver connects to the RTC + emulator and is used to test the RTC uclasses and associated code, + as well as the I2C subsystem. + +config SPL_RTC_SANDBOX + bool "Enable sandbox RTC driver (SPL)" + depends on SANDBOX && SPL_DM_RTC + default y + help + Enable the sandbox RTC driver. This driver connects to the RTC + emulator and is used to test the RTC uclasses and associated code, + as well as the I2C subsystem. + +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. + +config RTC_ABX80X + bool "Enable Abracon ABx80x RTC driver" + depends on DM_RTC + help + If you say yes here you get support for Abracon AB080X and AB180X + families of ultra-low-power battery- and capacitor-backed real-time + clock chips. + +config RTC_DAVINCI + bool "Enable TI OMAP RTC driver" + depends on ARCH_DAVINCI || ARCH_OMAP2PLUS + help + Say "yes" here to support the on chip real time clock + present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx. + +config RTC_ZYNQMP + bool "Enable ZynqMP RTC driver" + depends on ARCH_ZYNQMP + help + Say "yes" here to support the on chip real time clock + present on Xilinx ZynqMP SoC. + +config RTC_HT1380 + bool "Enable Holtek HT1380/HT1381 RTC driver" + depends on DM_RTC && DM_GPIO + help + Say "yes" here to get support for Holtek HT1380/HT1381 + Serial Timekeeper IC which provides seconds, minutes, hours, + day of the week, date, month and year information. It is to be + connected via 3 GPIO pins which work as reset, clock, and data. + +endmenu diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile new file mode 100644 index 00000000000..03a424c31a5 --- /dev/null +++ b/drivers/rtc/Makefile @@ -0,0 +1,42 @@ +# 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_ARMADA38X) += armada38x.o +obj-$(CONFIG_RTC_DAVINCI) += davinci.o +obj-$(CONFIG_RTC_DS1307) += ds1307.o +obj-$(CONFIG_RTC_DS1338) += ds1307.o +obj-$(CONFIG_RTC_DS1337) += ds1337.o +obj-$(CONFIG_RTC_DS1374) += ds1374.o +obj-$(CONFIG_RTC_DS3231) += ds3231.o +obj-$(CONFIG_RTC_DS3232) += ds3232.o +obj-$(CONFIG_RTC_EMULATION) += emul_rtc.o +obj-$(CONFIG_RTC_GOLDFISH) += goldfish_rtc.o +obj-$(CONFIG_RTC_HT1380) += ht1380.o +obj-$(CONFIG_$(SPL_TPL_)RTC_SANDBOX) += i2c_rtc_emul.o +obj-$(CONFIG_RTC_ISL1208) += isl1208.o +obj-$(CONFIG_RTC_M41T62) += m41t62.o +obj-$(CONFIG_RTC_MAX313XX) += max313xx.o +obj-$(CONFIG_RTC_MC13XXX) += mc13xxx-rtc.o +obj-$(CONFIG_RTC_MC146818) += mc146818.o +obj-$(CONFIG_MCFRTC) += mcfrtc.o +obj-$(CONFIG_RTC_MV) += mvrtc.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_RV3028) += rv3028.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_S35392A) += s35392a.o +obj-$(CONFIG_RTC_STM32) += stm32_rtc.o +obj-$(CONFIG_$(SPL_TPL_)RTC_SANDBOX) += sandbox_rtc.o +obj-$(CONFIG_RTC_ABX80X) += abx80x.o +obj-$(CONFIG_RTC_ZYNQMP) += zynqmp_rtc.o diff --git a/drivers/rtc/abx80x.c b/drivers/rtc/abx80x.c new file mode 100644 index 00000000000..1235b840ab0 --- /dev/null +++ b/drivers/rtc/abx80x.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A driver for the I2C members of the Abracon AB x8xx RTC family, + * and compatible: AB 1805 and AB 0805 + * + * Copyright 2014-2015 Macq S.A. + * Copyright 2020 Linaro + * + * Author: Philippe De Muyter <phdm@macqel.be> + * Author: Alexandre Belloni <alexandre.belloni@bootlin.com> + * Author: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + * + */ + +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <log.h> +#include <linux/bitfield.h> + +#define ABX8XX_REG_HTH 0x00 +#define ABX8XX_REG_SC 0x01 +#define ABX8XX_REG_MN 0x02 +#define ABX8XX_REG_HR 0x03 +#define ABX8XX_REG_DA 0x04 +#define ABX8XX_REG_MO 0x05 +#define ABX8XX_REG_YR 0x06 +#define ABX8XX_REG_WD 0x07 + +#define ABX8XX_REG_AHTH 0x08 +#define ABX8XX_REG_ASC 0x09 +#define ABX8XX_REG_AMN 0x0a +#define ABX8XX_REG_AHR 0x0b +#define ABX8XX_REG_ADA 0x0c +#define ABX8XX_REG_AMO 0x0d +#define ABX8XX_REG_AWD 0x0e + +#define ABX8XX_REG_STATUS 0x0f +#define ABX8XX_STATUS_AF BIT(2) +#define ABX8XX_STATUS_BLF BIT(4) +#define ABX8XX_STATUS_WDT BIT(6) + +#define ABX8XX_REG_CTRL1 0x10 +#define ABX8XX_CTRL_WRITE BIT(0) +#define ABX8XX_CTRL_ARST BIT(2) +#define ABX8XX_CTRL_12_24 BIT(6) + +#define ABX8XX_REG_CTRL2 0x11 +#define ABX8XX_CTRL2_RSVD BIT(5) + +#define ABX8XX_REG_IRQ 0x12 +#define ABX8XX_IRQ_AIE BIT(2) +#define ABX8XX_IRQ_IM_1_4 (0x3 << 5) + +#define ABX8XX_REG_CD_TIMER_CTL 0x18 + +#define ABX8XX_REG_OSC 0x1c +#define ABX8XX_OSC_FOS BIT(3) +#define ABX8XX_OSC_BOS BIT(4) +#define ABX8XX_OSC_ACAL_512 BIT(5) +#define ABX8XX_OSC_ACAL_1024 BIT(6) + +#define ABX8XX_OSC_OSEL BIT(7) + +#define ABX8XX_REG_OSS 0x1d +#define ABX8XX_OSS_OF BIT(1) +#define ABX8XX_OSS_OMODE BIT(4) + +#define ABX8XX_REG_WDT 0x1b +#define ABX8XX_WDT_WDS BIT(7) +#define ABX8XX_WDT_BMB_MASK 0x7c +#define ABX8XX_WDT_BMB_SHIFT 2 +#define ABX8XX_WDT_MAX_TIME (ABX8XX_WDT_BMB_MASK >> ABX8XX_WDT_BMB_SHIFT) +#define ABX8XX_WDT_WRB_MASK 0x03 +#define ABX8XX_WDT_WRB_1HZ 0x02 + +#define ABX8XX_REG_CFG_KEY 0x1f +#define ABX8XX_CFG_KEY_OSC 0xa1 +#define ABX8XX_CFG_KEY_MISC 0x9d + +#define ABX8XX_REG_ID0 0x28 + +#define ABX8XX_REG_OUT_CTRL 0x30 +#define ABX8XX_OUT_CTRL_EXDS BIT(4) + +#define ABX8XX_REG_TRICKLE 0x20 +#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0 +#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 +#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4 + +#define ABX8XX_REG_EXTRAM 0x3f +#define ABX8XX_EXTRAM_XADS GENMASK(1, 0) + +#define ABX8XX_SRAM_BASE 0x40 +#define ABX8XX_SRAM_WIN_SIZE 0x40U +#define ABX8XX_RAM_SIZE 256 + +#define RAM_ADDR_LOWER GENMASK(5, 0) +#define RAM_ADDR_UPPER GENMASK(7, 6) + +static u8 trickle_resistors[] = {0, 3, 6, 11}; + +enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, + AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X}; + +struct abx80x_cap { + u16 pn; + bool has_tc; + bool has_wdog; +}; + +static struct abx80x_cap abx80x_caps[] = { + [AB0801] = {.pn = 0x0801}, + [AB0803] = {.pn = 0x0803}, + [AB0804] = {.pn = 0x0804, .has_tc = true, .has_wdog = true}, + [AB0805] = {.pn = 0x0805, .has_tc = true, .has_wdog = true}, + [AB1801] = {.pn = 0x1801}, + [AB1803] = {.pn = 0x1803}, + [AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true}, + [AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [ABX80X] = {.pn = 0} +}; + +static int abx80x_rtc_xfer(struct udevice *dev, unsigned int offset, + u8 *val, unsigned int bytes, bool write) +{ + int ret; + + if (offset + bytes > ABX8XX_RAM_SIZE) + return -EINVAL; + + while (bytes) { + u8 extram, reg, len, lower, upper; + + lower = FIELD_GET(RAM_ADDR_LOWER, offset); + upper = FIELD_GET(RAM_ADDR_UPPER, offset); + extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper); + reg = ABX8XX_SRAM_BASE + lower; + len = min(lower + bytes, ABX8XX_SRAM_WIN_SIZE) - lower; + + ret = dm_i2c_reg_write(dev, ABX8XX_REG_EXTRAM, extram); + if (ret) + return ret; + + if (write) + ret = dm_i2c_write(dev, reg, val, len); + else + ret = dm_i2c_read(dev, reg, val, len); + if (ret) + return ret; + + offset += len; + val += len; + bytes -= len; + } + + return 0; +} + +static int abx80x_rtc_read(struct udevice *dev, unsigned int offset, u8 *val, + unsigned int bytes) +{ + return abx80x_rtc_xfer(dev, offset, val, bytes, false); +} + +static int abx80x_rtc_write(struct udevice *dev, unsigned int offset, + const u8 *val, unsigned int bytes) +{ + return abx80x_rtc_xfer(dev, offset, (u8 *)val, bytes, true); +} + +static int abx80x_is_rc_mode(struct udevice *dev) +{ + int flags = 0; + + flags = dm_i2c_reg_read(dev, ABX8XX_REG_OSS); + if (flags < 0) { + log_err("Failed to read autocalibration attribute\n"); + return flags; + } + + return (flags & ABX8XX_OSS_OMODE) ? 1 : 0; +} + +static int abx80x_enable_trickle_charger(struct udevice *dev, u8 trickle_cfg) +{ + int err; + + /* + * Write the configuration key register to enable access to the Trickle + * register + */ + err = dm_i2c_reg_write(dev, ABX8XX_REG_CFG_KEY, ABX8XX_CFG_KEY_MISC); + if (err < 0) { + log_err("Unable to write configuration key\n"); + return -EIO; + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_TRICKLE, + ABX8XX_TRICKLE_CHARGE_ENABLE | trickle_cfg); + if (err < 0) { + log_err("Unable to write trickle register\n"); + return -EIO; + } + + return 0; +} + +static int abx80x_rtc_read_time(struct udevice *dev, struct rtc_time *tm) +{ + unsigned char buf[8]; + int err, flags, rc_mode = 0; + + /* Read the Oscillator Failure only in XT mode */ + rc_mode = abx80x_is_rc_mode(dev); + if (rc_mode < 0) + return rc_mode; + + if (!rc_mode) { + flags = dm_i2c_reg_read(dev, ABX8XX_REG_OSS); + if (flags < 0) { + log_err("Unable to read oscillator status.\n"); + return flags; + } + + if (flags & ABX8XX_OSS_OF) + log_debug("Oscillator fail, data is not accurate.\n"); + } + + err = dm_i2c_read(dev, ABX8XX_REG_HTH, + buf, sizeof(buf)); + if (err < 0) { + log_err("Unable to read date\n"); + return -EIO; + } + + tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[ABX8XX_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[ABX8XX_REG_HR] & 0x3F); + tm->tm_wday = buf[ABX8XX_REG_WD] & 0x7; + tm->tm_mday = bcd2bin(buf[ABX8XX_REG_DA] & 0x3F); + tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F); + tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 2000; + + return 0; +} + +static int abx80x_rtc_set_time(struct udevice *dev, const struct rtc_time *tm) +{ + unsigned char buf[8]; + int err, flags; + + if (tm->tm_year < 2000) + return -EINVAL; + + buf[ABX8XX_REG_HTH] = 0; + buf[ABX8XX_REG_SC] = bin2bcd(tm->tm_sec); + buf[ABX8XX_REG_MN] = bin2bcd(tm->tm_min); + buf[ABX8XX_REG_HR] = bin2bcd(tm->tm_hour); + buf[ABX8XX_REG_DA] = bin2bcd(tm->tm_mday); + buf[ABX8XX_REG_MO] = bin2bcd(tm->tm_mon); + buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 2000); + buf[ABX8XX_REG_WD] = tm->tm_wday; + + err = dm_i2c_write(dev, ABX8XX_REG_HTH, + buf, sizeof(buf)); + if (err < 0) { + log_err("Unable to write to date registers\n"); + return -EIO; + } + + /* Clear the OF bit of Oscillator Status Register */ + flags = dm_i2c_reg_read(dev, ABX8XX_REG_OSS); + if (flags < 0) { + log_err("Unable to read oscillator status.\n"); + return flags; + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_OSS, + flags & ~ABX8XX_OSS_OF); + if (err < 0) { + log_err("Unable to write oscillator status register\n"); + return err; + } + + return 0; +} + +static int abx80x_rtc_set_autocalibration(struct udevice *dev, + int autocalibration) +{ + int retval, flags = 0; + + if (autocalibration != 0 && autocalibration != 1024 && + autocalibration != 512) { + log_err("autocalibration value outside permitted range\n"); + return -EINVAL; + } + + flags = dm_i2c_reg_read(dev, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (autocalibration == 0) { + flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024); + } else if (autocalibration == 1024) { + /* 1024 autocalibration is 0x10 */ + flags |= ABX8XX_OSC_ACAL_1024; + flags &= ~(ABX8XX_OSC_ACAL_512); + } else { + /* 512 autocalibration is 0x11 */ + flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512); + } + + /* Unlock write access to Oscillator Control Register */ + retval = dm_i2c_reg_write(dev, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_OSC); + if (retval < 0) { + log_err("Failed to write CONFIG_KEY register\n"); + return retval; + } + + retval = dm_i2c_reg_write(dev, ABX8XX_REG_OSC, flags); + + return retval; +} + +static int abx80x_rtc_get_autocalibration(struct udevice *dev) +{ + int flags = 0, autocalibration; + + flags = dm_i2c_reg_read(dev, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (flags & ABX8XX_OSC_ACAL_512) + autocalibration = 512; + else if (flags & ABX8XX_OSC_ACAL_1024) + autocalibration = 1024; + else + autocalibration = 0; + + return autocalibration; +} + +static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 }; + +static int abx80x_rtc_reset(struct udevice *dev) +{ + int ret = 0; + + int autocalib = abx80x_rtc_get_autocalibration(dev); + + if (autocalib != 0) + abx80x_rtc_set_autocalibration(dev, 0); + + ret = abx80x_rtc_set_time(dev, &default_tm); + if (ret != 0) { + log_err("cannot set time to default_tm. error %d\n", ret); + return ret; + } + + return ret; +} + +static const struct rtc_ops abx80x_rtc_ops = { + .get = abx80x_rtc_read_time, + .set = abx80x_rtc_set_time, + .reset = abx80x_rtc_reset, + .read = abx80x_rtc_read, + .write = abx80x_rtc_write, +}; + +static int abx80x_dt_trickle_cfg(struct udevice *dev) +{ + const char *diode; + int trickle_cfg = 0; + int i, ret = 0; + u32 tmp; + + diode = ofnode_read_string(dev_ofnode(dev), "abracon,tc-diode"); + if (!diode) + return ret; + + if (!strcmp(diode, "standard")) { + trickle_cfg |= ABX8XX_TRICKLE_STANDARD_DIODE; + } else if (!strcmp(diode, "schottky")) { + trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE; + } else { + log_err("Invalid tc-diode value: %s\n", diode); + return -EINVAL; + } + + ret = ofnode_read_u32(dev_ofnode(dev), "abracon,tc-resistor", &tmp); + if (ret) + return ret; + + for (i = 0; i < sizeof(trickle_resistors); i++) + if (trickle_resistors[i] == tmp) + break; + + if (i == sizeof(trickle_resistors)) { + log_err("Invalid tc-resistor value: %u\n", tmp); + return -EINVAL; + } + + return (trickle_cfg | i); +} + +static int abx80x_probe(struct udevice *dev) +{ + int i, data, err, trickle_cfg = -EINVAL; + unsigned char buf[7]; + unsigned int part = dev->driver_data; + unsigned int partnumber; + unsigned int majrev, minrev; + unsigned int lot; + unsigned int wafer; + unsigned int uid; + + err = dm_i2c_read(dev, ABX8XX_REG_ID0, buf, sizeof(buf)); + if (err < 0) { + log_err("Unable to read partnumber\n"); + return -EIO; + } + + partnumber = (buf[0] << 8) | buf[1]; + majrev = buf[2] >> 3; + minrev = buf[2] & 0x7; + lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3]; + uid = ((buf[4] & 0x7f) << 8) | buf[5]; + wafer = (buf[6] & 0x7c) >> 2; + log_debug("model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n", + partnumber, majrev, minrev, lot, wafer, uid); + + data = dm_i2c_reg_read(dev, ABX8XX_REG_CTRL1); + if (data < 0) { + log_err("Unable to read control register\n"); + return -EIO; + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_CTRL1, + ((data & ~(ABX8XX_CTRL_12_24 | + ABX8XX_CTRL_ARST)) | + ABX8XX_CTRL_WRITE)); + if (err < 0) { + log_err("Unable to write control register\n"); + return -EIO; + } + + /* Configure RV1805 specifics */ + if (part == RV1805) { + /* + * Avoid accidentally entering test mode. This can happen + * on the RV1805 in case the reserved bit 5 in control2 + * register is set. RV-1805-C3 datasheet indicates that + * the bit should be cleared in section 11h - Control2. + */ + data = dm_i2c_reg_read(dev, ABX8XX_REG_CTRL2); + if (data < 0) { + log_err("Unable to read control2 register\n"); + return -EIO; + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_CTRL2, + data & ~ABX8XX_CTRL2_RSVD); + if (err < 0) { + log_err("Unable to write control2 register\n"); + return -EIO; + } + + /* + * Avoid extra power leakage. The RV1805 uses smaller + * 10pin package and the EXTI input is not present. + * Disable it to avoid leakage. + */ + data = dm_i2c_reg_read(dev, ABX8XX_REG_OUT_CTRL); + if (data < 0) { + log_err("Unable to read output control register\n"); + return -EIO; + } + + /* + * Write the configuration key register to enable access to + * the config2 register + */ + err = dm_i2c_reg_write(dev, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_MISC); + if (err < 0) { + log_err("Unable to write configuration key\n"); + return -EIO; + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_OUT_CTRL, + data | ABX8XX_OUT_CTRL_EXDS); + if (err < 0) { + log_err("Unable to write output control register\n"); + return -EIO; + } + } + + /* part autodetection */ + if (part == ABX80X) { + for (i = 0; abx80x_caps[i].pn; i++) + if (partnumber == abx80x_caps[i].pn) + break; + if (abx80x_caps[i].pn == 0) { + log_err("Unknown part: %04x\n", partnumber); + return -EINVAL; + } + part = i; + } + + if (partnumber != abx80x_caps[part].pn) { + log_err("partnumber mismatch %04x != %04x\n", + partnumber, abx80x_caps[part].pn); + return -EINVAL; + } + + if (abx80x_caps[part].has_tc) + trickle_cfg = abx80x_dt_trickle_cfg(dev); + + if (trickle_cfg > 0) { + log_debug("Enabling trickle charger: %02x\n", trickle_cfg); + abx80x_enable_trickle_charger(dev, trickle_cfg); + } + + err = dm_i2c_reg_write(dev, ABX8XX_REG_CD_TIMER_CTL, BIT(2)); + if (err) + return err; + + return 0; +} + +static const struct udevice_id abx80x_of_match[] = { + { + .compatible = "abracon,abx80x", + .data = ABX80X + }, + { + .compatible = "abracon,ab0801", + .data = AB0801 + }, + { + .compatible = "abracon,ab0803", + .data = AB0803 + }, + { + .compatible = "abracon,ab0804", + .data = AB0804 + }, + { + .compatible = "abracon,ab0805", + .data = AB0805 + }, + { + .compatible = "abracon,ab1801", + .data = AB1801 + }, + { + .compatible = "abracon,ab1803", + .data = AB1803 + }, + { + .compatible = "abracon,ab1804", + .data = AB1804 + }, + { + .compatible = "abracon,ab1805", + .data = AB1805 + }, + { + .compatible = "microcrystal,rv1805", + .data = RV1805 + }, + { } +}; + +U_BOOT_DRIVER(abx80x_rtc) = { + .name = "rtc-abx80x", + .id = UCLASS_RTC, + .probe = abx80x_probe, + .of_match = abx80x_of_match, + .ops = &abx80x_rtc_ops, +}; diff --git a/drivers/rtc/armada38x.c b/drivers/rtc/armada38x.c new file mode 100644 index 00000000000..d0fe5dd7a8a --- /dev/null +++ b/drivers/rtc/armada38x.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RTC driver for the Armada 38x Marvell SoCs + * + * Copyright (C) 2021 Marek Behún <kabel@kernel.org> + * + * Based on Linux' driver by Gregory Clement and Marvell + */ + +#include <asm/io.h> +#include <dm.h> +#include <linux/delay.h> +#include <rtc.h> + +#define RTC_STATUS 0x0 +#define RTC_TIME 0xC +#define RTC_CONF_TEST 0x1C + +/* Armada38x SoC registers */ +#define RTC_38X_BRIDGE_TIMING_CTL 0x0 +#define RTC_38X_PERIOD_OFFS 0 +#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) +#define RTC_38X_READ_DELAY_OFFS 26 +#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) + +#define SAMPLE_NR 100 + +struct armada38x_rtc { + void __iomem *regs; + void __iomem *regs_soc; +}; + +/* + * According to Erratum RES-3124064 we have to do some configuration in MBUS. + * To read an RTC register we need to read it 100 times and return the most + * frequent value. + * To write an RTC register we need to write 2x zero into STATUS register, + * followed by the proper write. Linux adds an 5 us delay after this, so we do + * it here as well. + */ +static void update_38x_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); + reg &= ~RTC_38X_PERIOD_MASK; + reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ + reg &= ~RTC_38X_READ_DELAY_MASK; + reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ + writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); +} + +static void armada38x_rtc_write(u32 val, struct armada38x_rtc *rtc, u8 reg) +{ + writel(0, rtc->regs + RTC_STATUS); + writel(0, rtc->regs + RTC_STATUS); + writel(val, rtc->regs + reg); + udelay(5); +} + +static u32 armada38x_rtc_read(struct armada38x_rtc *rtc, u8 reg) +{ + u8 counts[SAMPLE_NR], max_idx; + u32 samples[SAMPLE_NR], max; + int i, j, last; + + for (i = 0, last = 0; i < SAMPLE_NR; ++i) { + u32 sample = readl(rtc->regs + reg); + + /* find if this value was already read */ + for (j = 0; j < last; ++j) { + if (samples[j] == sample) + break; + } + + if (j < last) { + /* if yes, increment count */ + ++counts[j]; + } else { + /* if not, add */ + samples[last] = sample; + counts[last] = 1; + ++last; + } + } + + /* finally find the sample that was read the most */ + max = 0; + max_idx = 0; + + for (i = 0; i < last; ++i) { + if (counts[i] > max) { + max = counts[i]; + max_idx = i; + } + } + + return samples[max_idx]; +} + +static int armada38x_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct armada38x_rtc *rtc = dev_get_priv(dev); + u32 time; + + time = armada38x_rtc_read(rtc, RTC_TIME); + + rtc_to_tm(time, tm); + + return 0; +} + +static int armada38x_rtc_reset(struct udevice *dev) +{ + struct armada38x_rtc *rtc = dev_get_priv(dev); + u32 reg; + + reg = armada38x_rtc_read(rtc, RTC_CONF_TEST); + + if (reg & 0xff) { + armada38x_rtc_write(0, rtc, RTC_CONF_TEST); + mdelay(500); + armada38x_rtc_write(0, rtc, RTC_TIME); + armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS); + } + + return 0; +} + +static int armada38x_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct armada38x_rtc *rtc = dev_get_priv(dev); + unsigned long time; + + time = rtc_mktime(tm); + + if (time > U32_MAX) + printf("%s: requested time to set will overflow\n", dev->name); + + armada38x_rtc_reset(dev); + armada38x_rtc_write(time, rtc, RTC_TIME); + + return 0; +} + +static int armada38x_probe(struct udevice *dev) +{ + struct armada38x_rtc *rtc = dev_get_priv(dev); + + rtc->regs = dev_remap_addr_name(dev, "rtc"); + if (!rtc->regs) + goto err; + + rtc->regs_soc = dev_remap_addr_name(dev, "rtc-soc"); + if (!rtc->regs_soc) + goto err; + + update_38x_mbus_timing_params(rtc); + + return 0; +err: + printf("%s: io address missing\n", dev->name); + return -ENODEV; +} + +static const struct rtc_ops armada38x_rtc_ops = { + .get = armada38x_rtc_get, + .set = armada38x_rtc_set, + .reset = armada38x_rtc_reset, +}; + +static const struct udevice_id armada38x_rtc_ids[] = { + { .compatible = "marvell,armada-380-rtc", .data = 0 }, + { } +}; + +U_BOOT_DRIVER(rtc_armada38x) = { + .name = "rtc-armada38x", + .id = UCLASS_RTC, + .of_match = armada38x_rtc_ids, + .probe = armada38x_probe, + .priv_auto = sizeof(struct armada38x_rtc), + .ops = &armada38x_rtc_ops, +}; diff --git a/drivers/rtc/davinci.c b/drivers/rtc/davinci.c new file mode 100644 index 00000000000..a20b73e1990 --- /dev/null +++ b/drivers/rtc/davinci.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 DENX Software Engineering GmbH + * Heiko Schocher <hs@denx.de> + * Copyright (C) 2021 Dario Binacchi <dariobin@libero.it> + */ +#include <command.h> +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <rtc.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/delay.h> + +/* RTC registers */ +#define OMAP_RTC_SECONDS_REG 0x00 +#define OMAP_RTC_MINUTES_REG 0x04 +#define OMAP_RTC_HOURS_REG 0x08 +#define OMAP_RTC_DAYS_REG 0x0C +#define OMAP_RTC_MONTHS_REG 0x10 +#define OMAP_RTC_YEARS_REG 0x14 +#define OMAP_RTC_WEEKS_REG 0x18 + +#define OMAP_RTC_CTRL_REG 0x40 +#define OMAP_RTC_STATUS_REG 0x44 +#define OMAP_RTC_INTERRUPTS_REG 0x48 + +#define OMAP_RTC_OSC_REG 0x54 + +#define OMAP_RTC_SCRATCH0_REG 0x60 +#define OMAP_RTC_SCRATCH1_REG 0x64 +#define OMAP_RTC_SCRATCH2_REG 0x68 + +#define OMAP_RTC_KICK0_REG 0x6c +#define OMAP_RTC_KICK1_REG 0x70 + +#define OMAP_RTC_PMIC_REG 0x98 + +/* OMAP_RTC_CTRL_REG bit fields: */ +#define OMAP_RTC_CTRL_SPLIT BIT(7) +#define OMAP_RTC_CTRL_DISABLE BIT(6) +#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) +#define OMAP_RTC_CTRL_TEST BIT(4) +#define OMAP_RTC_CTRL_MODE_12_24 BIT(3) +#define OMAP_RTC_CTRL_AUTO_COMP BIT(2) +#define OMAP_RTC_CTRL_ROUND_30S BIT(1) +#define OMAP_RTC_CTRL_STOP BIT(0) + +/* OMAP_RTC_STATUS_REG bit fields */ +#define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM2 BIT(7) +#define OMAP_RTC_STATUS_ALARM BIT(6) +#define OMAP_RTC_STATUS_1D_EVENT BIT(5) +#define OMAP_RTC_STATUS_1H_EVENT BIT(4) +#define OMAP_RTC_STATUS_1M_EVENT BIT(3) +#define OMAP_RTC_STATUS_1S_EVENT BIT(2) +#define OMAP_RTC_STATUS_RUN BIT(1) +#define OMAP_RTC_STATUS_BUSY BIT(0) + +/* OMAP_RTC_OSC_REG bit fields */ +#define OMAP_RTC_OSC_32KCLK_EN BIT(6) +#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) +#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) + +/* OMAP_RTC_KICKER values */ +#define OMAP_RTC_KICK0_VALUE 0x83e70b13 +#define OMAP_RTC_KICK1_VALUE 0x95a4f1e0 + +struct omap_rtc_device_type { + bool has_32kclk_en; + bool has_irqwakeen; + bool has_pmic_mode; + bool has_power_up_reset; +}; + +struct omap_rtc_priv { + fdt_addr_t base; + u8 max_reg; + struct udevice *dev; + struct clk clk; + bool has_ext_clk; + const struct omap_rtc_device_type *type; +}; + +static inline u8 omap_rtc_readb(struct omap_rtc_priv *priv, unsigned int reg) +{ + return readb(priv->base + reg); +} + +static inline u32 omap_rtc_readl(struct omap_rtc_priv *priv, unsigned int reg) +{ + return readl(priv->base + reg); +} + +static inline void omap_rtc_writeb(struct omap_rtc_priv *priv, unsigned int reg, + u8 val) +{ + writeb(val, priv->base + reg); +} + +static inline void omap_rtc_writel(struct omap_rtc_priv *priv, unsigned int reg, + u32 val) +{ + writel(val, priv->base + reg); +} + +static inline void omap_rtc_unlock(struct omap_rtc_priv *priv) +{ + omap_rtc_writel(priv, OMAP_RTC_KICK0_REG, OMAP_RTC_KICK0_VALUE); + omap_rtc_writel(priv, OMAP_RTC_KICK1_REG, OMAP_RTC_KICK1_VALUE); +} + +static inline void omap_rtc_lock(struct omap_rtc_priv *priv) +{ + omap_rtc_writel(priv, OMAP_RTC_KICK0_REG, 0); + omap_rtc_writel(priv, OMAP_RTC_KICK1_REG, 0); +} + +static int omap_rtc_wait_not_busy(struct omap_rtc_priv *priv) +{ + int count; + u8 status; + + status = omap_rtc_readb(priv, OMAP_RTC_STATUS_REG); + if ((status & OMAP_RTC_STATUS_RUN) != OMAP_RTC_STATUS_RUN) { + printf("RTC doesn't run\n"); + return -1; + } + + /* BUSY may stay active for 1/32768 second (~30 usec) */ + for (count = 0; count < 50; count++) { + if (!(status & OMAP_RTC_STATUS_BUSY)) + break; + + udelay(1); + status = omap_rtc_readb(priv, OMAP_RTC_STATUS_REG); + } + + /* now we have ~15 usec to read/write various registers */ + return 0; +} + +static int omap_rtc_reset(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + /* run RTC counter */ + omap_rtc_writeb(priv, OMAP_RTC_CTRL_REG, 0x01); + return 0; +} + +static int omap_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + int ret; + + ret = omap_rtc_wait_not_busy(priv); + if (ret) + return ret; + + omap_rtc_unlock(priv); + omap_rtc_writeb(priv, OMAP_RTC_YEARS_REG, bin2bcd(tm->tm_year % 100)); + omap_rtc_writeb(priv, OMAP_RTC_MONTHS_REG, bin2bcd(tm->tm_mon)); + omap_rtc_writeb(priv, OMAP_RTC_WEEKS_REG, bin2bcd(tm->tm_wday)); + omap_rtc_writeb(priv, OMAP_RTC_DAYS_REG, bin2bcd(tm->tm_mday)); + omap_rtc_writeb(priv, OMAP_RTC_HOURS_REG, bin2bcd(tm->tm_hour)); + omap_rtc_writeb(priv, OMAP_RTC_MINUTES_REG, bin2bcd(tm->tm_min)); + omap_rtc_writeb(priv, OMAP_RTC_SECONDS_REG, bin2bcd(tm->tm_sec)); + omap_rtc_lock(priv); + + 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); + + return 0; +} + +static int omap_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + unsigned long sec, min, hour, mday, wday, mon_cent, year; + int ret; + + ret = omap_rtc_wait_not_busy(priv); + if (ret) + return ret; + + sec = omap_rtc_readb(priv, OMAP_RTC_SECONDS_REG); + min = omap_rtc_readb(priv, OMAP_RTC_MINUTES_REG); + hour = omap_rtc_readb(priv, OMAP_RTC_HOURS_REG); + mday = omap_rtc_readb(priv, OMAP_RTC_DAYS_REG); + wday = omap_rtc_readb(priv, OMAP_RTC_WEEKS_REG); + mon_cent = omap_rtc_readb(priv, OMAP_RTC_MONTHS_REG); + year = omap_rtc_readb(priv, OMAP_RTC_YEARS_REG); + + dev_dbg(dev, + "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); + + tm->tm_sec = bcd2bin(sec & 0x7F); + tm->tm_min = bcd2bin(min & 0x7F); + tm->tm_hour = bcd2bin(hour & 0x3F); + tm->tm_mday = bcd2bin(mday & 0x3F); + tm->tm_mon = bcd2bin(mon_cent & 0x1F); + tm->tm_year = bcd2bin(year) + 2000; + tm->tm_wday = bcd2bin(wday & 0x07); + 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 int omap_rtc_scratch_read(struct udevice *dev, uint offset, + u8 *buffer, uint len) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u32 *val = (u32 *)buffer; + unsigned int reg; + int i; + + if (len & 3) + return -EFAULT; + + for (i = 0; i < len / 4; i++) { + reg = OMAP_RTC_SCRATCH0_REG + offset + (i * 4); + if (reg >= OMAP_RTC_KICK0_REG) + return -EFAULT; + + val[i] = omap_rtc_readl(priv, reg); + } + + return 0; +} + +static int omap_rtc_scratch_write(struct udevice *dev, uint offset, + const u8 *buffer, uint len) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u32 *val = (u32 *)buffer; + unsigned int reg; + int i; + + if (len & 3) + return -EFAULT; + + omap_rtc_unlock(priv); + for (i = 0; i < len / 4; i++) { + reg = OMAP_RTC_SCRATCH0_REG + offset + (i * 4); + if (reg >= OMAP_RTC_KICK0_REG) + return -EFAULT; + + omap_rtc_writel(priv, reg, val[i]); + } + omap_rtc_lock(priv); + + return 0; +} + +static int omap_rtc_remove(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + u8 reg; + + if (priv->clk.dev) + clk_disable(&priv->clk); + + omap_rtc_unlock(priv); + + /* leave rtc running, but disable irqs */ + omap_rtc_writeb(priv, OMAP_RTC_INTERRUPTS_REG, 0); + + if (priv->has_ext_clk) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, reg); + } + + omap_rtc_lock(priv); + return 0; +} + +static int omap_rtc_probe(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + struct rtc_time tm; + u8 reg, mask, new_ctrl; + + priv->dev = dev; + priv->type = (struct omap_rtc_device_type *)dev_get_driver_data(dev); + priv->max_reg = OMAP_RTC_PMIC_REG; + + if (!clk_get_by_name(dev, "ext-clk", &priv->clk)) + priv->has_ext_clk = true; + else + clk_get_by_name(dev, "int-clk", &priv->clk); + + if (priv->clk.dev) + clk_enable(&priv->clk); + else + dev_warn(dev, "missing clock\n"); + + omap_rtc_unlock(priv); + + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used + */ + omap_rtc_writel(priv, OMAP_RTC_INTERRUPTS_REG, 0); + + if (priv->type->has_32kclk_en) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, + reg | OMAP_RTC_OSC_32KCLK_EN); + } + + /* clear old status */ + reg = omap_rtc_readb(priv, OMAP_RTC_STATUS_REG); + + mask = OMAP_RTC_STATUS_ALARM; + + if (priv->type->has_pmic_mode) + mask |= OMAP_RTC_STATUS_ALARM2; + + if (priv->type->has_power_up_reset) { + mask |= OMAP_RTC_STATUS_POWER_UP; + if (reg & OMAP_RTC_STATUS_POWER_UP) + dev_info(dev, "RTC power up reset detected\n"); + } + + if (reg & mask) + omap_rtc_writeb(priv, OMAP_RTC_STATUS_REG, reg & mask); + + /* On boards with split power, RTC_ON_NOFF won't reset the RTC */ + reg = omap_rtc_readb(priv, OMAP_RTC_CTRL_REG); + if (reg & OMAP_RTC_CTRL_STOP) + dev_info(dev, "already running\n"); + + /* force to 24 hour mode */ + new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); + new_ctrl |= OMAP_RTC_CTRL_STOP; + + /* + * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: + * + * - Device wake-up capability setting should come through chip + * init logic. OMAP1 boards should initialize the "wakeup capable" + * flag in the platform device if the board is wired right for + * being woken up by RTC alarm. For OMAP-L138, this capability + * is built into the SoC by the "Deep Sleep" capability. + * + * - Boards wired so RTC_ON_nOFF is used as the reset signal, + * rather than nPWRON_RESET, should forcibly enable split + * power mode. (Some chip errata report that RTC_CTRL_SPLIT + * is write-only, and always reads as zero...) + */ + + if (new_ctrl & OMAP_RTC_CTRL_SPLIT) + dev_info(dev, "split power mode\n"); + + if (reg != new_ctrl) + omap_rtc_writeb(priv, OMAP_RTC_CTRL_REG, new_ctrl); + + /* + * If we have the external clock then switch to it so we can keep + * ticking across suspend. + */ + if (priv->has_ext_clk) { + reg = omap_rtc_readb(priv, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; + reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; + omap_rtc_writeb(priv, OMAP_RTC_OSC_REG, reg); + } + + omap_rtc_lock(priv); + + if (omap_rtc_get(dev, &tm)) { + dev_err(dev, "failed to get datetime\n"); + } else if (tm.tm_year == 2000 && tm.tm_mon == 1 && tm.tm_mday == 1 && + tm.tm_wday == 0) { + tm.tm_wday = 6; + omap_rtc_set(dev, &tm); + } + + return 0; +} + +static int omap_rtc_of_to_plat(struct udevice *dev) +{ + struct omap_rtc_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) { + dev_err(dev, "invalid address\n"); + return -EINVAL; + } + + dev_dbg(dev, "base=%pa\n", &priv->base); + return 0; +} + +static const struct rtc_ops omap_rtc_ops = { + .get = omap_rtc_get, + .set = omap_rtc_set, + .reset = omap_rtc_reset, + .read = omap_rtc_scratch_read, + .write = omap_rtc_scratch_write, +}; + +static const struct omap_rtc_device_type omap_rtc_am3352_type = { + .has_32kclk_en = true, + .has_irqwakeen = true, + .has_pmic_mode = true, +}; + +static const struct omap_rtc_device_type omap_rtc_da830_type = { + .has_32kclk_en = false, + .has_irqwakeen = false, + .has_pmic_mode = false, +}; + +static const struct udevice_id omap_rtc_ids[] = { + {.compatible = "ti,am3352-rtc", .data = (ulong)&omap_rtc_am3352_type}, + {.compatible = "ti,da830-rtc", .data = (ulong)&omap_rtc_da830_type } +}; + +U_BOOT_DRIVER(omap_rtc) = { + .name = "omap_rtc", + .id = UCLASS_RTC, + .of_match = omap_rtc_ids, + .ops = &omap_rtc_ops, + .of_to_plat = omap_rtc_of_to_plat, + .probe = omap_rtc_probe, + .remove = omap_rtc_remove, + .priv_auto = sizeof(struct omap_rtc_priv), +}; diff --git a/drivers/rtc/ds1307.c b/drivers/rtc/ds1307.c new file mode 100644 index 00000000000..ba06ff9f0be --- /dev/null +++ b/drivers/rtc/ds1307.c @@ -0,0 +1,374 @@ +// 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.h> +#include <rtc.h> +#include <i2c.h> + +enum ds_type { + ds_1307, + ds_1337, + ds_1339, + 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 DS1337_CTL_REG_ADDR 0x0e +#define DS1337_STAT_REG_ADDR 0x0f +#define DS1340_STAT_REG_ADDR 0x09 + +#define RTC_STAT_BIT_OSF 0x80 + +#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */ + +/* DS1307-specific bits */ +#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 */ + +/* DS1337-specific bits */ +#define DS1337_CTL_BIT_RS1 0x08 /* Rate select 1 */ +#define DS1337_CTL_BIT_RS2 0x10 /* Rate select 2 */ +#define DS1337_CTL_BIT_EOSC 0x80 /* Enable Oscillator */ + +/* DS1340-specific bits */ +#define DS1340_SEC_BIT_EOSC 0x80 /* Enable Oscillator */ +#define DS1340_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 CFG_SYS_I2C_RTC_ADDR +# define CFG_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 (CFG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CFG_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; + + if (type == ds_1337) { + /* Ensure oscillator is enabled */ + dm_i2c_reg_write(dev, DS1337_CTL_REG_ADDR, 0); + } + + 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); + + ret = dm_i2c_read(dev, 0, buf, sizeof(buf)); + if (ret < 0) + return ret; + + if (type == ds_1337 || type == ds_1340) { + uint reg = (type == ds_1337) ? DS1337_STAT_REG_ADDR : + DS1340_STAT_REG_ADDR; + int status = dm_i2c_reg_read(dev, reg); + + if (status >= 0 && (status & RTC_STAT_BIT_OSF)) { + printf("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + dm_i2c_reg_write(dev, reg, status & ~RTC_STAT_BIT_OSF); + } + } + + 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; + enum ds_type type = dev_get_driver_data(dev); + + /* + * reset clock/oscillator in the seconds register: + * on DS1307 bit 7 enables Clock Halt (CH), + * on DS1340 bit 7 disables the oscillator (not EOSC) + * on MCP794xx bit 7 enables Start Oscillator (ST) + */ + ret = dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR, 0x00); + if (ret < 0) + return ret; + + if (type == ds_1307) { + /* Write control register in order to enable square-wave + * output (SQWE) and set a default rate of 32.768kHz (RS1|RS0). + */ + ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR, + RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 | + RTC_CTL_BIT_RS0); + } else if (type == ds_1337) { + /* Write control register in order to enable oscillator output + * (not EOSC) and set a default rate of 32.768kHz (RS2|RS1). + */ + ret = dm_i2c_reg_write(dev, DS1337_CTL_REG_ADDR, + DS1337_CTL_BIT_RS2 | DS1337_CTL_BIT_RS1); + } else if (type == ds_1340 || type == mcp794xx || type == m41t11) { + /* Reset clock calibration, frequency test and output level. */ + ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR, 0x00); + } + + return ret; +} + +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,ds1339", .data = ds_1339 }, + { .compatible = "dallas,ds1340", .data = ds_1340 }, + { .compatible = "microchip,mcp7940x", .data = mcp794xx }, + { .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 */ diff --git a/drivers/rtc/ds1337.c b/drivers/rtc/ds1337.c new file mode 100644 index 00000000000..7eccf1cb8c5 --- /dev/null +++ b/drivers/rtc/ds1337.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2001-2008 + * Copyright 2020 NXP + * 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.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 */ + + +#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); + + /* 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 (CFG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val); +} +#else +static uchar rtc_read(struct udevice *dev, uchar reg) +{ + return dm_i2c_reg_read(dev, reg); +} + +static void rtc_write(struct udevice *dev, uchar reg, uchar val) +{ + dm_i2c_reg_write(dev, reg, val); +} + +static int ds1337_rtc_get(struct udevice *dev, struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon_cent, year, control, status; + + control = rtc_read(dev, RTC_CTL_REG_ADDR); + status = rtc_read(dev, RTC_STAT_REG_ADDR); + sec = rtc_read(dev, RTC_SEC_REG_ADDR); + min = rtc_read(dev, RTC_MIN_REG_ADDR); + hour = rtc_read(dev, RTC_HR_REG_ADDR); + wday = rtc_read(dev, RTC_DAY_REG_ADDR); + mday = rtc_read(dev, RTC_DATE_REG_ADDR); + mon_cent = rtc_read(dev, RTC_MON_REG_ADDR); + year = rtc_read(dev, 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\n", + year, mon_cent, mday, wday); + debug("hr: %02x min: %02x sec: %02x control: %02x status: %02x\n", + hour, min, sec, control, status); + + if (status & RTC_STAT_BIT_OSF) { + printf("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + rtc_write(dev, RTC_STAT_REG_ADDR, + rtc_read(dev, 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; +} + +static int ds1337_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); + + rtc_write(dev, RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100)); + + century = (tmp->tm_year >= 2000) ? 0x80 : 0; + rtc_write(dev, RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon) | century); + + rtc_write(dev, RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1)); + rtc_write(dev, RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday)); + rtc_write(dev, RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour)); + rtc_write(dev, RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min)); + rtc_write(dev, RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec)); + + return 0; +} + +#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 +static int ds1337_rtc_reset(struct udevice *dev) +{ +#ifdef CONFIG_RTC_DS1337 + rtc_write(dev, RTC_CTL_REG_ADDR, RTC_DS1337_RESET_VAL); +#elif defined CONFIG_RTC_DS1388 + rtc_write(dev, RTC_CTL_REG_ADDR, 0x0); /* hw default */ +#endif +#ifdef CONFIG_RTC_DS1339_TCR_VAL + rtc_write(dev, RTC_TC_REG_ADDR, CONFIG_RTC_DS1339_TCR_VAL); +#endif +#ifdef CONFIG_RTC_DS1388_TCR_VAL + rtc_write(dev, RTC_TC_REG_ADDR, CONFIG_RTC_DS1388_TCR_VAL); +#endif + return 0; +} + +static const struct rtc_ops ds1337_rtc_ops = { + .get = ds1337_rtc_get, + .set = ds1337_rtc_set, + .reset = ds1337_rtc_reset, +}; + +static const struct udevice_id ds1337_rtc_ids[] = { + { .compatible = "ds1337" }, + { .compatible = "ds1338" }, + { .compatible = "ds1339" }, + { } +}; + +U_BOOT_DRIVER(rtc_ds1337) = { + .name = "rtc-ds1337", + .id = UCLASS_RTC, + .of_match = ds1337_rtc_ids, + .ops = &ds1337_rtc_ops, +}; +#endif diff --git a/drivers/rtc/ds1374.c b/drivers/rtc/ds1374.c new file mode 100644 index 00000000000..895dbbaf1c7 --- /dev/null +++ b/drivers/rtc/ds1374.c @@ -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 <config.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 CFG_SYS_I2C_RTC_ADDR +# define CFG_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 - Watchdog/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 (CFG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write(uchar reg, uchar val, bool set) +{ + if (set == true) { + val |= i2c_reg_read (CFG_SYS_I2C_RTC_ADDR, reg); + i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val); + } else { + val = i2c_reg_read (CFG_SYS_I2C_RTC_ADDR, reg) & ~val; + i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val); + } +} + +static void rtc_write_raw (uchar reg, uchar val) +{ + i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val); +} diff --git a/drivers/rtc/ds3231.c b/drivers/rtc/ds3231.c new file mode 100644 index 00000000000..d6267d660d0 --- /dev/null +++ b/drivers/rtc/ds3231.c @@ -0,0 +1,292 @@ +// 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.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 (CFG_SYS_I2C_RTC_ADDR, reg)); +} + + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CFG_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 diff --git a/drivers/rtc/ds3232.c b/drivers/rtc/ds3232.c new file mode 100644 index 00000000000..7314ba219da --- /dev/null +++ b/drivers/rtc/ds3232.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019, Vaisala Oyj + */ + +#include <command.h> +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <dm/device_compat.h> +#include <linux/bitops.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_CTL_REG_ADDR 0x0e +#define RTC_STAT_REG_ADDR 0x0f +#define RTC_TEST_REG_ADDR 0x13 + +/* + * RTC control register bits + */ +#define RTC_CTL_BIT_A1IE BIT(0) /* Alarm 1 interrupt enable */ +#define RTC_CTL_BIT_A2IE BIT(1) /* Alarm 2 interrupt enable */ +#define RTC_CTL_BIT_INTCN BIT(2) /* Interrupt control */ +#define RTC_CTL_BIT_DOSC BIT(7) /* Disable Oscillator */ + +/* + * RTC status register bits + */ +#define RTC_STAT_BIT_A1F BIT(0) /* Alarm 1 flag */ +#define RTC_STAT_BIT_A2F BIT(1) /* Alarm 2 flag */ +#define RTC_STAT_BIT_EN32KHZ BIT(3) /* Enable 32KHz Output */ +#define RTC_STAT_BIT_BB32KHZ BIT(6) /* Battery backed 32KHz Output */ +#define RTC_STAT_BIT_OSF BIT(7) /* Oscillator stop flag */ + +/* + * RTC test register bits + */ +#define RTC_TEST_BIT_SWRST BIT(7) /* Software reset */ + +#define RTC_DATE_TIME_REG_SIZE 7 +#define RTC_SRAM_START 0x14 +#define RTC_SRAM_END 0xFF +#define RTC_SRAM_SIZE 236 + +struct ds3232_priv_data { + u8 max_register; + u8 sram_start; + int sram_size; +}; + +static int ds3232_rtc_read8(struct udevice *dev, unsigned int reg) +{ + int ret; + u8 buf; + struct ds3232_priv_data *priv_data; + + priv_data = dev_get_priv(dev); + if (!priv_data) + return -EINVAL; + + if (reg > priv_data->max_register) + return -EINVAL; + + ret = dm_i2c_read(dev, reg, &buf, sizeof(buf)); + if (ret < 0) + return ret; + + return buf; +} + +static int ds3232_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 buf = (u8)val; + struct ds3232_priv_data *priv_data; + + priv_data = dev_get_priv(dev); + if (!priv_data) + return -EINVAL; + + if (reg > priv_data->max_register) + return -EINVAL; + + return dm_i2c_write(dev, reg, &buf, sizeof(buf)); +} + +static int reset_sram(struct udevice *dev) +{ + int ret, sram_end, reg; + struct ds3232_priv_data *priv_data; + + priv_data = dev_get_priv(dev); + if (!priv_data) + return -EINVAL; + + sram_end = priv_data->sram_start + priv_data->sram_size; + + for (reg = priv_data->sram_start; reg < sram_end; reg++) { + ret = ds3232_rtc_write8(dev, reg, 0x00); + if (ret < 0) + return ret; + } + + return 0; +} + +static int verify_osc(struct udevice *dev) +{ + int ret, rtc_status; + + ret = ds3232_rtc_read8(dev, RTC_STAT_REG_ADDR); + if (ret < 0) + return ret; + + rtc_status = ret; + + if (rtc_status & RTC_STAT_BIT_OSF) { + dev_warn(dev, + "oscillator discontinuity flagged, time unreliable\n"); + /* + * In case OSC was off we cannot trust the SRAM data anymore. + * Reset it to 0x00. + */ + ret = reset_sram(dev); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ds3232_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + u8 buf[RTC_DATE_TIME_REG_SIZE]; + u8 is_century; + + if (tm->tm_year < 1900 || tm->tm_year > 2099) + dev_warn(dev, "WARNING: year should be between 1900 and 2099!\n"); + + is_century = (tm->tm_year >= 2000) ? 0x80 : 0; + + buf[RTC_SEC_REG_ADDR] = bin2bcd(tm->tm_sec); + buf[RTC_MIN_REG_ADDR] = bin2bcd(tm->tm_min); + buf[RTC_HR_REG_ADDR] = bin2bcd(tm->tm_hour); + buf[RTC_DAY_REG_ADDR] = bin2bcd(tm->tm_wday + 1); + buf[RTC_DATE_REG_ADDR] = bin2bcd(tm->tm_mday); + buf[RTC_MON_REG_ADDR] = bin2bcd(tm->tm_mon) | is_century; + buf[RTC_YR_REG_ADDR] = bin2bcd(tm->tm_year % 100); + + return dm_i2c_write(dev, 0, buf, sizeof(buf)); +} + +static int ds3232_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + int ret; + u8 buf[RTC_DATE_TIME_REG_SIZE]; + u8 is_twelve_hr; + u8 is_pm; + u8 is_century; + + ret = verify_osc(dev); + if (ret < 0) + return ret; + + ret = dm_i2c_read(dev, 0, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* Extract additional information for AM/PM and century */ + is_twelve_hr = buf[RTC_HR_REG_ADDR] & 0x40; + is_pm = buf[RTC_HR_REG_ADDR] & 0x20; + is_century = buf[RTC_MON_REG_ADDR] & 0x80; + + tm->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F); + tm->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F); + + if (is_twelve_hr) + tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x1F) + + (is_pm ? 12 : 0); + else + tm->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR]); + + tm->tm_wday = bcd2bin((buf[RTC_DAY_REG_ADDR] & 0x07) - 1); + tm->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F); + tm->tm_mon = bcd2bin((buf[RTC_MON_REG_ADDR] & 0x7F)); + tm->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) + + (is_century ? 2000 : 1900); + tm->tm_yday = 0; + tm->tm_isdst = 0; + + return 0; +} + +static int ds3232_rtc_reset(struct udevice *dev) +{ + int ret; + + ret = reset_sram(dev); + if (ret < 0) + return ret; + + /* + * From datasheet + * (https://datasheets.maximintegrated.com/en/ds/DS3232M.pdf): + * + * The device reset occurs during the normal acknowledge time slot + * following the receipt of the data byte carrying that + * SWRST instruction a NACK occurs due to the resetting action. + * + * Therefore we don't verify the result of I2C write operation since it + * will fail due the NACK. + */ + ds3232_rtc_write8(dev, RTC_TEST_REG_ADDR, RTC_TEST_BIT_SWRST); + + return 0; +} + +static int ds3232_probe(struct udevice *dev) +{ + int rtc_status; + int ret; + struct ds3232_priv_data *priv_data; + + priv_data = dev_get_priv(dev); + if (!priv_data) + return -EINVAL; + + priv_data->sram_start = RTC_SRAM_START; + priv_data->max_register = RTC_SRAM_END; + priv_data->sram_size = RTC_SRAM_SIZE; + + ret = ds3232_rtc_read8(dev, RTC_STAT_REG_ADDR); + if (ret < 0) + return ret; + + rtc_status = ret; + + ret = verify_osc(dev); + if (ret < 0) + return ret; + + rtc_status &= ~(RTC_STAT_BIT_OSF | RTC_STAT_BIT_A1F | RTC_STAT_BIT_A2F); + + return ds3232_rtc_write8(dev, RTC_STAT_REG_ADDR, rtc_status); +} + +static const struct rtc_ops ds3232_rtc_ops = { + .get = ds3232_rtc_get, + .set = ds3232_rtc_set, + .reset = ds3232_rtc_reset, + .read8 = ds3232_rtc_read8, + .write8 = ds3232_rtc_write8 +}; + +static const struct udevice_id ds3232_rtc_ids[] = { + { .compatible = "dallas,ds3232" }, + { } +}; + +U_BOOT_DRIVER(rtc_ds3232) = { + .name = "rtc-ds3232", + .id = UCLASS_RTC, + .probe = ds3232_probe, + .of_match = ds3232_rtc_ids, + .ops = &ds3232_rtc_ops, + .priv_auto = sizeof(struct ds3232_priv_data), +}; diff --git a/drivers/rtc/emul_rtc.c b/drivers/rtc/emul_rtc.c new file mode 100644 index 00000000000..97a8d9bb7df --- /dev/null +++ b/drivers/rtc/emul_rtc.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * This driver emulates a real time clock based on timer ticks. + */ + +#include <div64.h> +#include <dm.h> +#include <env.h> +#include <rtc.h> +#include <time.h> +#include <timestamp.h> + +/** + * struct emul_rtc - private data for emulated RTC driver + */ +struct emul_rtc { + /** + * @offset_us: microseconds from 1970-01-01 to timer_get_us() base + */ + u64 offset_us; + /** + * @isdst: daylight saving time + */ + int isdst; +}; + +static int emul_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + struct emul_rtc *priv = dev_get_priv(dev); + u64 now; + + now = timer_get_us() + priv->offset_us; + do_div(now, 1000000); + rtc_to_tm(now, time); + time->tm_isdst = priv->isdst; + + return 0; +} + +static int emul_rtc_set(struct udevice *dev, const struct rtc_time *time) +{ + struct emul_rtc *priv = dev_get_priv(dev); + + if (time->tm_year < 1970) + return -EINVAL; + + priv->offset_us = rtc_mktime(time) * 1000000ULL - timer_get_us(); + + if (time->tm_isdst > 0) + priv->isdst = 1; + else if (time->tm_isdst < 0) + priv->isdst = -1; + else + priv->isdst = 0; + + return 0; +} + +int emul_rtc_probe(struct udevice *dev) +{ + struct emul_rtc *priv = dev_get_priv(dev); + const char *epoch_str; + u64 epoch; + + epoch_str = env_get("rtc_emul_epoch"); + + if (epoch_str) { + epoch = simple_strtoull(epoch_str, NULL, 10); + } else { + /* Use the build date as initial time */ + epoch = U_BOOT_EPOCH; + } + priv->offset_us = epoch * 1000000ULL - timer_get_us(); + priv->isdst = -1; + + return 0; +} + +static const struct rtc_ops emul_rtc_ops = { + .get = emul_rtc_get, + .set = emul_rtc_set, +}; + +U_BOOT_DRIVER(rtc_emul) = { + .name = "rtc_emul", + .id = UCLASS_RTC, + .ops = &emul_rtc_ops, + .probe = emul_rtc_probe, + .priv_auto = sizeof(struct emul_rtc), +}; + +U_BOOT_DRVINFO(rtc_emul) = { + .name = "rtc_emul", +}; diff --git a/drivers/rtc/goldfish_rtc.c b/drivers/rtc/goldfish_rtc.c new file mode 100644 index 00000000000..3231eb0daf8 --- /dev/null +++ b/drivers/rtc/goldfish_rtc.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2023, Heinrich Schuchardt <heinrich.schuchardt@canonical.com> + * + * This driver emulates a real time clock based on timer ticks. + */ + +#include <div64.h> +#include <dm.h> +#include <mapmem.h> +#include <rtc.h> +#include <linux/io.h> + +/** + * struct goldfish_rtc - private data for RTC driver + */ +struct goldfish_rtc { + /** + * @base: base address for register file + */ + void __iomem *base; + /** + * @isdst: daylight saving time + */ + int isdst; +}; + +/* Register offsets */ +#define GOLDFISH_TIME_LOW 0x00 +#define GOLDFISH_TIME_HIGH 0x04 + +static int goldfish_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + struct goldfish_rtc *priv = dev_get_priv(dev); + void __iomem *base = priv->base; + u64 time_high; + u64 time_low; + u64 now; + + time_low = ioread32(base + GOLDFISH_TIME_LOW); + time_high = ioread32(base + GOLDFISH_TIME_HIGH); + now = (time_high << 32) | time_low; + + do_div(now, 1000000000U); + + rtc_to_tm(now, time); + time->tm_isdst = priv->isdst; + + return 0; +} + +static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time) +{ + struct goldfish_rtc *priv = dev_get_priv(dev); + void __iomem *base = priv->base; + u64 now; + + if (time->tm_year < 1970) + return -EINVAL; + + now = rtc_mktime(time) * 1000000000ULL; + iowrite32(now >> 32, base + GOLDFISH_TIME_HIGH); + iowrite32(now, base + GOLDFISH_TIME_LOW); + + if (time->tm_isdst > 0) + priv->isdst = 1; + else if (time->tm_isdst < 0) + priv->isdst = -1; + else + priv->isdst = 0; + + return 0; +} + +static int goldfish_rtc_probe(struct udevice *dev) +{ + struct goldfish_rtc *priv = dev_get_priv(dev); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->base = map_sysmem(addr, 0x20); + + return 0; +} + +static const struct rtc_ops goldfish_rtc_ops = { + .get = goldfish_rtc_get, + .set = goldfish_rtc_set, +}; + +static const struct udevice_id goldfish_rtc_of_match[] = { + { .compatible = "google,goldfish-rtc", }, + {}, +}; + +U_BOOT_DRIVER(rtc_goldfish) = { + .name = "rtc_goldfish", + .id = UCLASS_RTC, + .ops = &goldfish_rtc_ops, + .probe = goldfish_rtc_probe, + .of_match = goldfish_rtc_of_match, + .priv_auto = sizeof(struct goldfish_rtc), +}; diff --git a/drivers/rtc/ht1380.c b/drivers/rtc/ht1380.c new file mode 100644 index 00000000000..c202261e999 --- /dev/null +++ b/drivers/rtc/ht1380.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Holtek HT1380/HT1381 Serial Timekeeper Chip + * + * Communication with the chip is vendor-specific. + * It is done via 3 GPIO pins: reset, clock, and data. + * Describe in .dts this way: + * + * rtc { + * compatible = "holtek,ht1380"; + * rst-gpios = <&gpio 19 GPIO_ACTIVE_LOW>; + * clk-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>; + * dat-gpios = <&gpio 21 GPIO_ACTIVE_HIGH>; + * }; + * + */ + +#include <dm.h> +#include <rtc.h> +#include <bcd.h> +#include <asm/gpio.h> +#include <linux/delay.h> + +struct ht1380_priv { + struct gpio_desc rst_desc; + struct gpio_desc clk_desc; + struct gpio_desc dat_desc; +}; + +enum registers { + SEC, + MIN, + HOUR, + MDAY, + MONTH, + WDAY, + YEAR, + WP, + N_REGS +}; + +enum hour_mode { + AMPM_MODE = 0x80, /* RTC is in AM/PM mode */ + PM_NOW = 0x20, /* set if PM, clear if AM */ +}; + +static const int BURST = 0xbe; +static const int READ = 1; + +static void ht1380_half_period_delay(void) +{ + /* + * Delay for half a period. 1 us complies with the 500 KHz maximum + * input serial clock limit given by the datasheet. + */ + udelay(1); +} + +static int ht1380_send_byte(struct ht1380_priv *priv, int byte) +{ + int ret; + + for (int bit = 0; bit < 8; bit++) { + ret = dm_gpio_set_value(&priv->dat_desc, byte >> bit & 1); + if (ret) + break; + ht1380_half_period_delay(); + + ret = dm_gpio_set_value(&priv->clk_desc, 1); + if (ret) + break; + ht1380_half_period_delay(); + + ret = dm_gpio_set_value(&priv->clk_desc, 0); + if (ret) + break; + } + + return ret; +} + +/* + * Leave reset state. The transfer operation can then be started. + */ +static int ht1380_reset_off(struct ht1380_priv *priv) +{ + const unsigned int T_CC = 4; /* us, Reset to Clock Setup */ + int ret; + + /* + * Leave RESET state. + * Make sure we make the minimal delay required by the datasheet. + */ + ret = dm_gpio_set_value(&priv->rst_desc, 0); + udelay(T_CC); + + return ret; +} + +/* + * Enter reset state. Completes the transfer operation. + */ +static int ht1380_reset_on(struct ht1380_priv *priv) +{ + const unsigned int T_CWH = 4; /* us, Reset Inactive Time */ + int ret; + + /* + * Enter RESET state. + * Make sure we make the minimal delay required by the datasheet. + */ + ret = dm_gpio_set_value(&priv->rst_desc, 1); + udelay(T_CWH); + + return ret; +} + +static int ht1380_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct ht1380_priv *priv = dev_get_priv(dev); + int ret, i, bit, reg[N_REGS]; + + ret = dm_gpio_set_value(&priv->clk_desc, 0); + if (ret) + return ret; + + ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT); + if (ret) + return ret; + + ret = ht1380_reset_off(priv); + if (ret) + goto exit; + + ret = ht1380_send_byte(priv, BURST + READ); + if (ret) + goto exit; + + ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_IN); + if (ret) + goto exit; + + for (i = 0; i < N_REGS; i++) { + reg[i] = 0; + + for (bit = 0; bit < 8; bit++) { + ht1380_half_period_delay(); + + ret = dm_gpio_set_value(&priv->clk_desc, 1); + if (ret) + goto exit; + ht1380_half_period_delay(); + + reg[i] |= dm_gpio_get_value(&priv->dat_desc) << bit; + ret = dm_gpio_set_value(&priv->clk_desc, 0); + if (ret) + goto exit; + } + } + + ret = -EINVAL; + + /* Correctness check: some bits are always zero */ + if (reg[MIN] & 0x80 || reg[HOUR] & 0x40 || reg[MDAY] & 0xc0 || + reg[MONTH] & 0xe0 || reg[WDAY] & 0xf8 || reg[WP] & 0x7f) + goto exit; + + /* Correctness check: some registers are always non-zero */ + if (!reg[MDAY] || !reg[MONTH] || !reg[WDAY]) + goto exit; + + tm->tm_sec = bcd2bin(reg[SEC]); + tm->tm_min = bcd2bin(reg[MIN]); + if (reg[HOUR] & AMPM_MODE) { + /* AM-PM Mode, range is 01-12 */ + tm->tm_hour = bcd2bin(reg[HOUR] & 0x1f) % 12; + if (reg[HOUR] & PM_NOW) { + /* it is PM (otherwise AM) */ + tm->tm_hour += 12; + } + } else { + /* 24-hour Mode, range is 0-23 */ + tm->tm_hour = bcd2bin(reg[HOUR]); + } + tm->tm_mday = bcd2bin(reg[MDAY]); + tm->tm_mon = bcd2bin(reg[MONTH]); + tm->tm_year = 2000 + bcd2bin(reg[YEAR]); + tm->tm_wday = bcd2bin(reg[WDAY]) - 1; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + ret = 0; + +exit: + ht1380_reset_on(priv); + + return ret; +} + +static int ht1380_write_protection_off(struct ht1380_priv *priv) +{ + int ret; + const int PROTECT = 0x8e; + + ret = ht1380_reset_off(priv); + if (ret) + return ret; + + ret = ht1380_send_byte(priv, PROTECT); + if (ret) + return ret; + ret = ht1380_send_byte(priv, 0); /* WP bit is 0 */ + if (ret) + return ret; + + return ht1380_reset_on(priv); +} + +static int ht1380_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct ht1380_priv *priv = dev_get_priv(dev); + int ret, i, reg[N_REGS]; + + ret = dm_gpio_set_value(&priv->clk_desc, 0); + if (ret) + return ret; + + ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT); + if (ret) + goto exit; + + ret = ht1380_write_protection_off(priv); + if (ret) + goto exit; + + reg[SEC] = bin2bcd(tm->tm_sec); + reg[MIN] = bin2bcd(tm->tm_min); + reg[HOUR] = bin2bcd(tm->tm_hour); + reg[MDAY] = bin2bcd(tm->tm_mday); + reg[MONTH] = bin2bcd(tm->tm_mon); + reg[WDAY] = bin2bcd(tm->tm_wday) + 1; + reg[YEAR] = bin2bcd(tm->tm_year - 2000); + reg[WP] = 0x80; /* WP bit is 1 */ + + ret = ht1380_reset_off(priv); + if (ret) + goto exit; + + ret = ht1380_send_byte(priv, BURST); + for (i = 0; i < N_REGS && ret; i++) + ret = ht1380_send_byte(priv, reg[i]); + +exit: + ht1380_reset_on(priv); + + return ret; +} + +static int ht1380_probe(struct udevice *dev) +{ + int ret; + struct ht1380_priv *priv; + + priv = dev_get_priv(dev); + if (!priv) + return -EINVAL; + + ret = gpio_request_by_name(dev, "rst-gpios", 0, + &priv->rst_desc, GPIOD_IS_OUT); + if (ret) + goto fail_rst; + + ret = gpio_request_by_name(dev, "clk-gpios", 0, + &priv->clk_desc, GPIOD_IS_OUT); + if (ret) + goto fail_clk; + + ret = gpio_request_by_name(dev, "dat-gpios", 0, + &priv->dat_desc, 0); + if (ret) + goto fail_dat; + + ret = ht1380_reset_on(priv); + if (ret) + goto fail; + + return 0; + +fail: + dm_gpio_free(dev, &priv->dat_desc); +fail_dat: + dm_gpio_free(dev, &priv->clk_desc); +fail_clk: + dm_gpio_free(dev, &priv->rst_desc); +fail_rst: + return ret; +} + +static int ht1380_remove(struct udevice *dev) +{ + struct ht1380_priv *priv = dev_get_priv(dev); + + dm_gpio_free(dev, &priv->rst_desc); + dm_gpio_free(dev, &priv->clk_desc); + dm_gpio_free(dev, &priv->dat_desc); + + return 0; +} + +static const struct rtc_ops ht1380_rtc_ops = { + .get = ht1380_rtc_get, + .set = ht1380_rtc_set, +}; + +static const struct udevice_id ht1380_rtc_ids[] = { + { .compatible = "holtek,ht1380" }, + { } +}; + +U_BOOT_DRIVER(rtc_ht1380) = { + .name = "rtc-ht1380", + .id = UCLASS_RTC, + .probe = ht1380_probe, + .remove = ht1380_remove, + .of_match = ht1380_rtc_ids, + .ops = &ht1380_rtc_ops, + .priv_auto = sizeof(struct ht1380_priv), +}; diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c new file mode 100644 index 00000000000..ea11c72c964 --- /dev/null +++ b/drivers/rtc/i2c_rtc_emul.c @@ -0,0 +1,228 @@ +// 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 <dm.h> +#include <i2c.h> +#include <log.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 + +long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, + int offset) +{ + struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev); + long old_offset; + + old_offset = plat->offset; + plat->use_system_time = use_system_time; + if (offset != -1) + plat->offset = offset; + os_set_time_offset(plat->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_plat(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_plat(dev); + struct rtc_time now; + + os_localtime(&now); + plat->base_time = rtc_mktime(&now); + plat->offset = os_get_time_offset(); + 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_plat(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_plat(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; + os_set_time_offset(plat->offset); + + 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_plat(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_plat(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_plat(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 the reset register was written to, do reset. */ + if (offset <= REG_RESET && REG_RESET < offset + len) + 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 int sandbox_i2c_rtc_probe(struct udevice *dev) +{ + const u8 mac[] = { 0x02, 0x00, 0x11, 0x22, 0x33, 0x48 }; + struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev); + + memcpy(&plat->reg[0x40], mac, sizeof(mac)); + return 0; +} + +static const struct udevice_id sandbox_i2c_rtc_ids[] = { + { .compatible = "sandbox,i2c-rtc-emul" }, + { } +}; + +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, + .probe = sandbox_i2c_rtc_probe, + .priv_auto = sizeof(struct sandbox_i2c_rtc), + .plat_auto = sizeof(struct sandbox_i2c_rtc_plat_data), + .ops = &sandbox_i2c_rtc_emul_ops, +}; diff --git a/drivers/rtc/isl1208.c b/drivers/rtc/isl1208.c new file mode 100644 index 00000000000..83db505afe9 --- /dev/null +++ b/drivers/rtc/isl1208.c @@ -0,0 +1,196 @@ +// 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 <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, +}; diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c new file mode 100644 index 00000000000..7bfea9e0b31 --- /dev/null +++ b/drivers/rtc/m41t62.c @@ -0,0 +1,368 @@ +// 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.h> +#include <rtc.h> +#include <i2c.h> +#include <linux/log2.h> +#include <linux/delay.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_OF (1 << 2) /* OF: Oscillator Flag Bit */ +#define M41T62_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */ + +#define M41T62_WDAY_SQW_FREQ_MASK 0xf0 +#define M41T62_WDAY_SQW_FREQ_SHIFT 4 + +#define M41T62_SQW_MAX_FREQ 32768 + +#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_sqw_enable(struct udevice *dev, bool enable) +{ + u8 val; + int ret; + + ret = dm_i2c_read(dev, M41T62_REG_ALARM_MON, &val, sizeof(val)); + if (ret) + return ret; + + if (enable) + val |= M41T62_ALMON_SQWE; + else + val &= ~M41T62_ALMON_SQWE; + + return dm_i2c_write(dev, M41T62_REG_ALARM_MON, &val, sizeof(val)); +} + +static int m41t62_sqw_set_rate(struct udevice *dev, unsigned int rate) +{ + u8 val, newval, sqwrateval; + int ret; + + if (rate >= M41T62_SQW_MAX_FREQ) + sqwrateval = 1; + else if (rate >= M41T62_SQW_MAX_FREQ / 4) + sqwrateval = 2; + else if (rate) + sqwrateval = 15 - ilog2(rate); + + ret = dm_i2c_read(dev, M41T62_REG_WDAY, &val, sizeof(val)); + if (ret) + return ret; + + newval = val; + newval &= ~M41T62_WDAY_SQW_FREQ_MASK; + newval |= (sqwrateval << M41T62_WDAY_SQW_FREQ_SHIFT); + + /* + * Try to avoid writing unchanged values. Writing to this register + * will reset the internal counter pipeline and thus affect system + * time. + */ + if (newval == val) + return 0; + + return dm_i2c_write(dev, M41T62_REG_WDAY, &newval, sizeof(newval)); +} + +static int m41t62_rtc_restart_osc(struct udevice *dev) +{ + u8 val; + int ret; + + /* 0. check if oscillator failure happened */ + ret = dm_i2c_read(dev, M41T62_REG_FLAGS, &val, sizeof(val)); + if (ret) + return ret; + if (!(val & M41T62_FLAGS_OF)) + return 0; + + ret = dm_i2c_read(dev, M41T62_REG_SEC, &val, sizeof(val)); + if (ret) + return ret; + + /* 1. Set stop bit */ + val |= M41T62_SEC_ST; + ret = dm_i2c_write(dev, M41T62_REG_SEC, &val, sizeof(val)); + if (ret) + return ret; + + /* 2. Clear stop bit */ + val &= ~M41T62_SEC_ST; + ret = dm_i2c_write(dev, M41T62_REG_SEC, &val, sizeof(val)); + if (ret) + return ret; + + /* 3. wait 4 seconds */ + mdelay(4000); + + ret = dm_i2c_read(dev, M41T62_REG_FLAGS, &val, sizeof(val)); + if (ret) + return ret; + + /* 4. clear M41T62_FLAGS_OF bit */ + val &= ~M41T62_FLAGS_OF; + ret = dm_i2c_write(dev, M41T62_REG_FLAGS, &val, sizeof(val)); + if (ret) + return ret; + + return 0; +} + +static int m41t62_rtc_clear_ht(struct udevice *dev) +{ + u8 val; + int ret; + + /* + * M41T82: Make sure HT (Halt Update) bit is cleared. + * This bit is 0 in M41T62 so its save to clear it always. + */ + + ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + if (ret) + return ret; + val &= ~M41T80_ALHOUR_HT; + ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + if (ret) + return ret; + + return 0; +} + +static int m41t62_rtc_reset(struct udevice *dev) +{ + int ret; + + ret = m41t62_rtc_restart_osc(dev); + if (ret) + return ret; + + ret = m41t62_rtc_clear_ht(dev); + if (ret) + return ret; + + /* + * Some boards feed the square wave as clock input into + * the SoC. This enables a 32.768kHz square wave, which is + * also the hardware default after power-loss. + */ + ret = m41t62_sqw_set_rate(dev, 32768); + if (ret) + return ret; + return m41t62_sqw_enable(dev, true); +} + +static int m41t62_rtc_read8(struct udevice *dev, unsigned int reg) +{ + return dm_i2c_reg_read(dev, reg); +} + +static int m41t62_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + return dm_i2c_reg_write(dev, reg, val); +} + +/* + * 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_clear_ht(dev); +} + +static const struct rtc_ops m41t62_rtc_ops = { + .get = m41t62_rtc_get, + .set = m41t62_rtc_set, + .reset = m41t62_rtc_reset, + .read8 = m41t62_rtc_read8, + .write8 = m41t62_rtc_write8, +}; + +static const struct udevice_id m41t62_rtc_ids[] = { + { .compatible = "st,m41t62" }, + { .compatible = "st,m41t82" }, + { .compatible = "st,m41st87" }, + { .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(CFG_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(CFG_SYS_I2C_RTC_ADDR, 0, 1, buf, M41T62_DATETIME_REG_SIZE); + m41t62_set_rtc_buf(tm, buf); + + if (i2c_write(CFG_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(CFG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); + val &= ~M41T80_ALHOUR_HT; + i2c_write(CFG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); +} +#endif /* CONFIG_DM_RTC */ diff --git a/drivers/rtc/max313xx.c b/drivers/rtc/max313xx.c new file mode 100644 index 00000000000..60400235dd0 --- /dev/null +++ b/drivers/rtc/max313xx.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices MAX313XX series I2C RTC driver + * + * Copyright 2022 Analog Devices Inc. + */ +#include <bcd.h> +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <dm/device_compat.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/kernel.h> + +/* common registers */ +#define MAX313XX_INT_ALARM1 BIT(0) +#define MAX313XX_INT_ALARM2 BIT(1) +#define MAX313XX_HRS_F_12_24 BIT(6) +#define MAX313XX_HRS_F_AM_PM BIT(5) +#define MAX313XX_MONTH_CENTURY BIT(7) + +#define MAX313XX_TMR_CFG_ENABLE BIT(4) +#define MAX313XX_TMR_CFG_FREQ_MASK GENMASK(1, 0) +#define MAX313XX_TMR_CFG_FREQ_16HZ 0x03 + +#define MAX313XX_REG_MINUTE 0x01 +#define MAX313XX_REG_HOUR 0x02 + +#define MAX313XX_TIME_SIZE 0x07 + +/* device specific registers */ +#define MAX3134X_CFG2_REG 0x01 +#define MAX3134X_CFG2_SET_RTC BIT(1) + +#define MAX31341_TRICKLE_RES_MASK GENMASK(1, 0) +#define MAX31341_TRICKLE_DIODE_EN BIT(2) +#define MAX31341_TRICKLE_ENABLE_BIT BIT(3) +#define MAX31341_POWER_MGMT_REG 0x56 +#define MAX31341_POWER_MGMT_TRICKLE_BIT BIT(0) + +#define MAX3133X_TRICKLE_RES_MASK GENMASK(2, 1) +#define MAX3133X_TRICKLE_DIODE_EN BIT(3) +#define MAX3133X_TRICKLE_ENABLE_BIT BIT(0) + +#define MAX31329_TRICKLE_ENABLE_BIT BIT(7) +#define MAX31343_TRICKLE_ENABLE_MASK GENMASK(7, 4) +#define MAX31343_TRICKLE_ENABLE_CODE 5 +#define MAX31329_43_TRICKLE_RES_MASK GENMASK(1, 0) +#define MAX31329_43_TRICKLE_DIODE_EN BIT(2) + +#define MAX31329_CONFIG2_REG 0x04 +#define MAX31329_CONFIG2_CLKIN_EN BIT(2) +#define MAX31329_CONFIG2_CLKIN_FREQ GENMASK(1, 0) + +#define MAX31341_42_CONFIG1_REG 0x00 +#define MAX31341_42_CONFIG1_CLKIN_EN BIT(7) +#define MAX31341_42_CONFIG1_CLKIN_FREQ GENMASK(5, 4) +#define MAX31341_42_CONFIG1_OSC_DISABLE BIT(3) +#define MAX31341_42_CONFIG1_SWRST BIT(0) + +enum max313xx_ids { + ID_MAX31328, + ID_MAX31329, + ID_MAX31331, + ID_MAX31334, + ID_MAX31341, + ID_MAX31342, + ID_MAX31343, + MAX313XX_ID_NR +}; + +/** + * struct chip_desc - descriptor for MAX313xx variants + * @sec_reg: Offset to seconds register. Used to denote the start of the + * current time registers. + * @alarm1_sec_reg: Offset to Alarm1 seconds register. Used to denote the + * start of the alarm registers. + * @int_en_reg: Offset to the interrupt enable register. + * @int_status_reg: Offset to the interrupt status register. + * @ram_reg: Offset to the timestamp RAM (which can be used as SRAM). + * @ram_size: Size of the timestamp RAM. + * @temp_reg: Offset to the temperature register (or 0 if temperature + * sensor is not supported). + * @trickle_reg: Offset to the trickle charger configuration register (or + * 0 if trickle charger is not supported). + * @rst_reg: Offset to the reset register. + * @rst_bit: Bit within the reset register for the software reset. + */ +struct chip_desc { + u8 sec_reg; + u8 alarm1_sec_reg; + + u8 int_en_reg; + u8 int_status_reg; + + u8 ram_reg; + u8 ram_size; + + u8 temp_reg; + + u8 trickle_reg; + + u8 rst_reg; + u8 rst_bit; +}; + +struct max313xx_priv { + enum max313xx_ids id; + const struct chip_desc *chip; +}; + +static const struct chip_desc chip[MAX313XX_ID_NR] = { + [ID_MAX31328] = { + .int_en_reg = 0x0E, + .int_status_reg = 0x0F, + .sec_reg = 0x00, + .alarm1_sec_reg = 0x07, + .temp_reg = 0x11, + }, + [ID_MAX31329] = { + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x06, + .alarm1_sec_reg = 0x0D, + .ram_reg = 0x22, + .ram_size = 64, + .trickle_reg = 0x19, + .rst_reg = 0x02, + .rst_bit = BIT(0), + }, + [ID_MAX31331] = { + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x08, + .alarm1_sec_reg = 0x0F, + .ram_reg = 0x20, + .ram_size = 32, + .trickle_reg = 0x1B, + .rst_reg = 0x02, + .rst_bit = BIT(0), + }, + [ID_MAX31334] = { + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x09, + .alarm1_sec_reg = 0x10, + .ram_reg = 0x30, + .ram_size = 32, + .trickle_reg = 0x1E, + .rst_reg = 0x02, + .rst_bit = BIT(0), + }, + [ID_MAX31341] = { + .int_en_reg = 0x04, + .int_status_reg = 0x05, + .sec_reg = 0x06, + .alarm1_sec_reg = 0x0D, + .ram_reg = 0x16, + .ram_size = 64, + .trickle_reg = 0x57, + .rst_reg = 0x00, + .rst_bit = BIT(0), + }, + [ID_MAX31342] = { + .int_en_reg = 0x04, + .int_status_reg = 0x05, + .sec_reg = 0x06, + .alarm1_sec_reg = 0x0D, + .rst_reg = 0x00, + .rst_bit = BIT(0), + }, + [ID_MAX31343] = { + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x06, + .alarm1_sec_reg = 0x0D, + .ram_reg = 0x22, + .ram_size = 64, + .temp_reg = 0x1A, + .trickle_reg = 0x19, + .rst_reg = 0x02, + .rst_bit = BIT(0), + }, +}; + +static const u32 max313xx_trickle_ohms[] = { 3000, 6000, 11000 }; + +static int max313xx_set_bits(struct udevice *dev, unsigned int reg, unsigned int bits) +{ + int ret; + + ret = dm_i2c_reg_read(dev, reg); + if (ret < 0) + return ret; + + return dm_i2c_reg_write(dev, reg, ret | bits); +} + +static int max313xx_clear_bits(struct udevice *dev, unsigned int reg, unsigned int bits) +{ + int ret; + + ret = dm_i2c_reg_read(dev, reg); + if (ret < 0) + return ret; + + return dm_i2c_reg_write(dev, reg, ret & ~bits); +} + +static int max313xx_get_hour(u8 hour_reg) +{ + int hour; + + /* 24Hr mode */ + if (!FIELD_GET(MAX313XX_HRS_F_12_24, hour_reg)) + return bcd2bin(hour_reg & 0x3f); + + /* 12Hr mode */ + hour = bcd2bin(hour_reg & 0x1f); + if (hour == 12) + hour = 0; + + if (FIELD_GET(MAX313XX_HRS_F_AM_PM, hour_reg)) + hour += 12; + + return hour; +} + +static int max313xx_read_time(struct udevice *dev, struct rtc_time *t) +{ + struct max313xx_priv *rtc = dev_get_priv(dev); + u8 regs[7]; + int ret; + + ret = dm_i2c_read(dev, rtc->chip->sec_reg, regs, 7); + if (ret) + return ret; + + t->tm_sec = bcd2bin(regs[0] & 0x7f); + t->tm_min = bcd2bin(regs[1] & 0x7f); + t->tm_hour = max313xx_get_hour(regs[2]); + t->tm_wday = bcd2bin(regs[3] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[4] & 0x3f); + t->tm_mon = bcd2bin(regs[5] & 0x1f); + t->tm_year = bcd2bin(regs[6]) + 2000; + + if (FIELD_GET(MAX313XX_MONTH_CENTURY, regs[5])) + t->tm_year += 100; + + dev_dbg(dev, "read %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", + t->tm_year, t->tm_mon, t->tm_mday, + t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec); + + return 0; +} + +static int max313xx_set_time(struct udevice *dev, const struct rtc_time *t) +{ + struct max313xx_priv *rtc = dev_get_priv(dev); + u8 regs[7]; + int ret; + + dev_dbg(dev, "set %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", + t->tm_year, t->tm_mon, t->tm_mday, + t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec); + + if (t->tm_year < 2000) { + dev_err(dev, "year %d (before 2000) not supported\n", + t->tm_year); + return -EINVAL; + } + + if (rtc->chip->rst_bit) { + ret = max313xx_clear_bits(dev, rtc->chip->rst_reg, rtc->chip->rst_bit); + if (ret) + return ret; + } + + regs[0] = bin2bcd(t->tm_sec); + regs[1] = bin2bcd(t->tm_min); + regs[2] = bin2bcd(t->tm_hour); + regs[3] = bin2bcd(t->tm_wday + 1); + regs[4] = bin2bcd(t->tm_mday); + regs[5] = bin2bcd(t->tm_mon); + regs[6] = bin2bcd((t->tm_year - 2000) % 100); + + if ((t->tm_year - 2000) >= 200) + regs[5] |= FIELD_PREP(MAX313XX_MONTH_CENTURY, 1); + + ret = dm_i2c_write(dev, rtc->chip->sec_reg, regs, 7); + if (ret) + return ret; + + switch (rtc->id) { + case ID_MAX31341: + case ID_MAX31342: + ret = max313xx_set_bits(dev, MAX3134X_CFG2_REG, + MAX3134X_CFG2_SET_RTC); + if (ret) + return ret; + + udelay(10000); + + ret = max313xx_clear_bits(dev, MAX3134X_CFG2_REG, + MAX3134X_CFG2_SET_RTC); + if (ret) + return ret; + + break; + default: + break; + } + + return ret; +} + +static int max313xx_reset(struct udevice *dev) +{ + struct max313xx_priv *rtc = dev_get_priv(dev); + int ret = -EINVAL; + + if (rtc->chip->rst_bit) + ret = max313xx_set_bits(dev, rtc->chip->rst_reg, rtc->chip->rst_bit); + + return ret; +} + +static int max313xx_read8(struct udevice *dev, unsigned int reg) +{ + return dm_i2c_reg_read(dev, reg); +} + +static int max313xx_write8(struct udevice *dev, unsigned int reg, int val) +{ + return dm_i2c_reg_write(dev, reg, val); +} + +static const struct rtc_ops max3133x_rtc_ops = { + .get = max313xx_read_time, + .set = max313xx_set_time, + .reset = max313xx_reset, + .read8 = max313xx_read8, + .write8 = max313xx_write8, +}; + +static int max313xx_init(struct udevice *dev) +{ + struct max313xx_priv *rtc = dev_get_priv(dev); + int ret; + + switch (rtc->id) { + case ID_MAX31341: + case ID_MAX31342: + ret = max313xx_clear_bits(dev, MAX31341_42_CONFIG1_REG, + MAX31341_42_CONFIG1_OSC_DISABLE); + if (ret) + return ret; + + return max313xx_set_bits(dev, MAX31341_42_CONFIG1_REG, + MAX31341_42_CONFIG1_SWRST); + default: + return 0; + } +} + +static int max313xx_trickle_charger_setup(struct udevice *dev) +{ + struct max313xx_priv *rtc = dev_get_priv(dev); + bool diode; + int index, reg; + u32 ohms; + u32 chargeable; + int ret; + + if (dev_read_u32(dev, "trickle-resistor-ohms", &ohms) || + dev_read_u32(dev, "aux-voltage-chargeable", &chargeable)) + return 0; + + switch (chargeable) { + case 0: + diode = false; + break; + case 1: + diode = true; + break; + default: + dev_dbg(dev, "unsupported aux-voltage-chargeable value\n"); + return -EINVAL; + } + + if (!rtc->chip->trickle_reg) { + dev_warn(dev, "device does not have trickle charger\n"); + return -ENOTSUPP; + } + + index = find_closest(ohms, max313xx_trickle_ohms, + ARRAY_SIZE(max313xx_trickle_ohms)) + 1; + + switch (rtc->id) { + case ID_MAX31329: + reg = FIELD_PREP(MAX31329_TRICKLE_ENABLE_BIT, 1) | + FIELD_PREP(MAX31329_43_TRICKLE_RES_MASK, index) | + FIELD_PREP(MAX31329_43_TRICKLE_DIODE_EN, diode); + break; + case ID_MAX31331: + case ID_MAX31334: + reg = FIELD_PREP(MAX3133X_TRICKLE_ENABLE_BIT, 1) | + FIELD_PREP(MAX3133X_TRICKLE_DIODE_EN, diode) | + FIELD_PREP(MAX3133X_TRICKLE_RES_MASK, index); + break; + case ID_MAX31341: + if (index == 1) + index = 0; + reg = FIELD_PREP(MAX31341_TRICKLE_ENABLE_BIT, 1) | + FIELD_PREP(MAX31341_TRICKLE_DIODE_EN, diode) | + FIELD_PREP(MAX31341_TRICKLE_RES_MASK, index); + + ret = max313xx_set_bits(dev, MAX31341_POWER_MGMT_REG, + MAX31341_POWER_MGMT_TRICKLE_BIT); + if (ret) + return ret; + + break; + case ID_MAX31343: + reg = FIELD_PREP(MAX31329_43_TRICKLE_RES_MASK, index) | + FIELD_PREP(MAX31329_43_TRICKLE_DIODE_EN, diode) | + FIELD_PREP(MAX31343_TRICKLE_ENABLE_MASK, + MAX31343_TRICKLE_ENABLE_CODE); + break; + default: + return -EOPNOTSUPP; + } + + return dm_i2c_reg_write(dev, rtc->chip->trickle_reg, reg); +} + +static int max313xx_probe(struct udevice *dev) +{ + struct max313xx_priv *max313xx = dev_get_priv(dev); + int ret; + + max313xx->id = dev_get_driver_data(dev); + max313xx->chip = &chip[max313xx->id]; + + ret = max313xx_init(dev); + if (ret) + return ret; + + return max313xx_trickle_charger_setup(dev); +} + +static const struct udevice_id max313xx_of_id[] = { + { .compatible = "adi,max31328", .data = ID_MAX31328 }, + { .compatible = "adi,max31329", .data = ID_MAX31329 }, + { .compatible = "adi,max31331", .data = ID_MAX31331 }, + { .compatible = "adi,max31334", .data = ID_MAX31334 }, + { .compatible = "adi,max31341", .data = ID_MAX31341 }, + { .compatible = "adi,max31342", .data = ID_MAX31342 }, + { .compatible = "adi,max31343", .data = ID_MAX31343 }, + { } +}; + +U_BOOT_DRIVER(rtc_max313xx) = { + .name = "rtc-max313xx", + .id = UCLASS_RTC, + .probe = max313xx_probe, + .of_match = max313xx_of_id, + .priv_auto = sizeof(struct max313xx_priv), + .ops = &max3133x_rtc_ops, +}; diff --git a/drivers/rtc/mc13xxx-rtc.c b/drivers/rtc/mc13xxx-rtc.c new file mode 100644 index 00000000000..9e396bcdae9 --- /dev/null +++ b/drivers/rtc/mc13xxx-rtc.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2008, Guennadi Liakhovetski <lg@denx.de> + */ + +#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) +{ +} diff --git a/drivers/rtc/mc146818.c b/drivers/rtc/mc146818.c new file mode 100644 index 00000000000..c0d86c6d063 --- /dev/null +++ b/drivers/rtc/mc146818.c @@ -0,0 +1,307 @@ +// 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 <command.h> +#include <dm.h> +#include <rtc.h> + +#if defined(CONFIG_X86) || defined(CONFIG_TARGET_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 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(motorola_mc146818) = { + .name = "motorola_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 */ diff --git a/drivers/rtc/mcfrtc.c b/drivers/rtc/mcfrtc.c new file mode 100644 index 00000000000..b5cc6b96881 --- /dev/null +++ b/drivers/rtc/mcfrtc.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + */ + + +#include <command.h> +#include <rtc.h> +#include <asm/immap.h> +#include <asm/rtc.h> + +#undef RTC_DEBUG + +#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; +} diff --git a/drivers/rtc/mvrtc.c b/drivers/rtc/mvrtc.c new file mode 100644 index 00000000000..f070c681b94 --- /dev/null +++ b/drivers/rtc/mvrtc.c @@ -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 <command.h> +#include <dm.h> +#include <rtc.h> +#include <asm/io.h> +#include <linux/delay.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_plat(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_plat(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_plat(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_of_to_plat(struct udevice *dev) +{ + struct mvrtc_pdata *pdata = dev_get_plat(dev); + + pdata->iobase = dev_read_addr(dev); + return 0; +} + +U_BOOT_DRIVER(rtc_mv) = { + .name = "rtc-mv", + .id = UCLASS_RTC, + .of_to_plat = mv_rtc_of_to_plat, + .of_match = mv_rtc_ids, + .ops = &mv_rtc_ops, +}; +#endif /* CONFIG_DM_RTC */ diff --git a/drivers/rtc/mvrtc.h b/drivers/rtc/mvrtc.h new file mode 100644 index 00000000000..87ff43299ca --- /dev/null +++ b/drivers/rtc/mvrtc.h @@ -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 diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c new file mode 100644 index 00000000000..69d22a4bdcb --- /dev/null +++ b/drivers/rtc/mxsrtc.c @@ -0,0 +1,70 @@ +// 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 <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"); +} diff --git a/drivers/rtc/pcf2127.c b/drivers/rtc/pcf2127.c new file mode 100644 index 00000000000..27a340f07d6 --- /dev/null +++ b/drivers/rtc/pcf2127.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 by NXP Semiconductors Inc. + * Date & Time support for PCF2127 RTC + */ + +/* #define DEBUG */ + +#include <command.h> +#include <dm.h> +#include <i2c.h> +#include <log.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_rtc_read(struct udevice *dev, uint offset, u8 *buffer, uint len) +{ + struct dm_i2c_chip *chip = dev_get_parent_plat(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_write(struct udevice *dev, uint offset, + const u8 *buffer, uint len) +{ + return dm_i2c_write(dev, offset, buffer, len); +} + +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); + + /* 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_rtc_read(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); + 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, + .read = pcf2127_rtc_read, + .write = pcf2127_rtc_write, +}; + +static const struct udevice_id pcf2127_rtc_ids[] = { + { .compatible = "nxp,pcf2127" }, + { .compatible = "nxp,pcf2129" }, + { .compatible = "nxp,pca2129" }, + { } +}; + +U_BOOT_DRIVER(rtc_pcf2127) = { + .name = "rtc-pcf2127", + .id = UCLASS_RTC, + .of_match = pcf2127_rtc_ids, + .ops = &pcf2127_rtc_ops, +}; diff --git a/drivers/rtc/pcf8563.c b/drivers/rtc/pcf8563.c new file mode 100644 index 00000000000..03bef68051b --- /dev/null +++ b/drivers/rtc/pcf8563.c @@ -0,0 +1,225 @@ +// 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.h> +#include <rtc.h> +#include <i2c.h> + +#if !CONFIG_IS_ENABLED(DM_RTC) +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 (CFG_SYS_I2C_RTC_ADDR, reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + i2c_reg_write (CFG_SYS_I2C_RTC_ADDR, reg, val); +} +#else +static int pcf8563_rtc_get(struct udevice *dev, struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon_cent, year; + + sec = dm_i2c_reg_read(dev, 0x02); + min = dm_i2c_reg_read(dev, 0x03); + hour = dm_i2c_reg_read(dev, 0x04); + mday = dm_i2c_reg_read(dev, 0x05); + wday = dm_i2c_reg_read(dev, 0x06); + mon_cent = dm_i2c_reg_read(dev, 0x07); + year = dm_i2c_reg_read(dev, 0x08); + + debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x ", + year, mon_cent, mday, wday); + debug("hr: %02x min: %02x sec: %02x\n", + hour, min, sec); + debug("Alarms: wday: %02x day: %02x hour: %02x min: %02x\n", + dm_i2c_reg_read(dev, 0x0C), + dm_i2c_reg_read(dev, 0x0B), + dm_i2c_reg_read(dev, 0x0A), + dm_i2c_reg_read(dev, 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; +} + +static int pcf8563_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, 0x08, bin2bcd(tmp->tm_year % 100)); + + century = (tmp->tm_year >= 2000) ? 0 : 0x80; + dm_i2c_reg_write(dev, 0x07, bin2bcd(tmp->tm_mon) | century); + + dm_i2c_reg_write(dev, 0x06, bin2bcd(tmp->tm_wday)); + dm_i2c_reg_write(dev, 0x05, bin2bcd(tmp->tm_mday)); + dm_i2c_reg_write(dev, 0x04, bin2bcd(tmp->tm_hour)); + dm_i2c_reg_write(dev, 0x03, bin2bcd(tmp->tm_min)); + dm_i2c_reg_write(dev, 0x02, bin2bcd(tmp->tm_sec)); + + return 0; +} + +static int pcf8563_rtc_reset(struct udevice *dev) +{ + /* clear all control & status registers */ + dm_i2c_reg_write(dev, 0x00, 0x00); + dm_i2c_reg_write(dev, 0x01, 0x00); + dm_i2c_reg_write(dev, 0x0D, 0x00); + + /* clear Voltage Low bit */ + dm_i2c_reg_write(dev, 0x02, dm_i2c_reg_read(dev, 0x02) & 0x7F); + + /* reset all alarms */ + dm_i2c_reg_write(dev, 0x09, 0x00); + dm_i2c_reg_write(dev, 0x0A, 0x00); + dm_i2c_reg_write(dev, 0x0B, 0x00); + dm_i2c_reg_write(dev, 0x0C, 0x00); + + return 0; +} + +static const struct rtc_ops pcf8563_rtc_ops = { + .get = pcf8563_rtc_get, + .set = pcf8563_rtc_set, + .reset = pcf8563_rtc_reset, +}; + +static const struct udevice_id pcf8563_rtc_ids[] = { + { .compatible = "nxp,pcf8563" }, + { } +}; + +U_BOOT_DRIVER(rtc_pcf8563) = { + .name = "rtc-pcf8563", + .id = UCLASS_RTC, + .of_match = pcf8563_rtc_ids, + .ops = &pcf8563_rtc_ops, +}; +#endif diff --git a/drivers/rtc/pl031.c b/drivers/rtc/pl031.c new file mode 100644 index 00000000000..855ee913416 --- /dev/null +++ b/drivers/rtc/pl031.c @@ -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 <command.h> +#include <dm.h> +#include <errno.h> +#include <log.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_plat { + phys_addr_t base; +}; + +static inline u32 pl031_read_reg(struct udevice *dev, int reg) +{ + struct pl031_plat *pdata = dev_get_plat(dev); + + return readl(pdata->base + reg); +} + +static inline u32 pl031_write_reg(struct udevice *dev, int reg, u32 value) +{ + struct pl031_plat *pdata = dev_get_plat(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_of_to_plat(struct udevice *dev) +{ + struct pl031_plat *pdata = dev_get_plat(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, + .of_to_plat = pl031_of_to_plat, + .plat_auto = sizeof(struct pl031_plat), + .ops = &pl031_ops, +}; diff --git a/drivers/rtc/pt7c4338.c b/drivers/rtc/pt7c4338.c new file mode 100644 index 00000000000..79df07814a6 --- /dev/null +++ b/drivers/rtc/pt7c4338.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2010 Freescale Semiconductor, Inc. + * Copyright 2020 NXP + * + * 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 <config.h> +#include <command.h> +#include <dm.h> +#include <log.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) + +#if !CONFIG_IS_ENABLED(DM_RTC) +/****** Helper functions ****************************************/ +static u8 rtc_read(u8 reg) +{ + return i2c_reg_read(CFG_SYS_I2C_RTC_ADDR, reg); +} + +static void rtc_write(u8 reg, u8 val) +{ + i2c_reg_write(CFG_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); +} +#else +static u8 rtc_read(struct udevice *dev, u8 reg) +{ + return dm_i2c_reg_read(dev, reg); +} + +static void rtc_write(struct udevice *dev, u8 reg, u8 val) +{ + dm_i2c_reg_write(dev, reg, val); +} + +static int pt7c4338_rtc_get(struct udevice *dev, struct rtc_time *tmp) +{ + int ret = 0; + u8 sec, min, hour, mday, wday, mon, year, ctl_stat; + + ctl_stat = rtc_read(dev, RTC_CTL_STAT_REG_ADDR); + sec = rtc_read(dev, RTC_SEC_REG_ADDR); + min = rtc_read(dev, RTC_MIN_REG_ADDR); + hour = rtc_read(dev, RTC_HR_REG_ADDR); + wday = rtc_read(dev, RTC_DAY_REG_ADDR); + mday = rtc_read(dev, RTC_DATE_REG_ADDR); + mon = rtc_read(dev, RTC_MON_REG_ADDR); + year = rtc_read(dev, RTC_YR_REG_ADDR); + debug("Get RTC year: %02x mon: %02x mday: %02x wday: %02x\n", + year, mon, mday, wday); + debug("hr: %02x min: %02x sec: %02x control_status: %02x\n", + 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(dev, RTC_CTL_STAT_REG_ADDR, + rtc_read(dev, + 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; +} + +static int pt7c4338_rtc_set(struct udevice *dev, const 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(dev, RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100)); + rtc_write(dev, RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon)); + rtc_write(dev, RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1)); + rtc_write(dev, RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday)); + rtc_write(dev, RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour)); + rtc_write(dev, RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min)); + rtc_write(dev, RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec)); + + return 0; +} + +static int pt7c4338_rtc_reset(struct udevice *dev) +{ + rtc_write(dev, RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */ + rtc_write(dev, RTC_CTL_STAT_REG_ADDR, RTC_PT7C4338_RESET_VAL); + return 0; +} + +static const struct rtc_ops pt7c4338_rtc_ops = { + .get = pt7c4338_rtc_get, + .set = pt7c4338_rtc_set, + .reset = pt7c4338_rtc_reset, +}; + +static const struct udevice_id pt7c4338_rtc_ids[] = { + { .compatible = "pericom,pt7c4338" }, + { } +}; + +U_BOOT_DRIVER(rtc_pt7c4338) = { + .name = "rtc-pt7c4338", + .id = UCLASS_RTC, + .of_match = pt7c4338_rtc_ids, + .ops = &pt7c4338_rtc_ops, +}; +#endif diff --git a/drivers/rtc/rtc-uclass.c b/drivers/rtc/rtc-uclass.c new file mode 100644 index 00000000000..8f6c0c6a0a7 --- /dev/null +++ b/drivers/rtc/rtc-uclass.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_RTC + +#include <dm.h> +#include <errno.h> +#include <log.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 dm_rtc_read(struct udevice *dev, unsigned int reg, u8 *buf, unsigned int len) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (ops->read) + return ops->read(dev, reg, buf, len); + if (!ops->read8) + return -ENOSYS; + while (len--) { + int ret = ops->read8(dev, reg++); + + if (ret < 0) + return ret; + *buf++ = ret; + } + return 0; +} + +int dm_rtc_write(struct udevice *dev, unsigned int reg, + const u8 *buf, unsigned int len) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (ops->write) + return ops->write(dev, reg, buf, len); + if (!ops->write8) + return -ENOSYS; + while (len--) { + int ret = ops->write8(dev, reg++, *buf++); + + if (ret < 0) + return ret; + } + return 0; +} + +int rtc_read8(struct udevice *dev, unsigned int reg) +{ + struct rtc_ops *ops = rtc_get_ops(dev); + + assert(ops); + if (ops->read8) + return ops->read8(dev, reg); + if (ops->read) { + u8 buf[1]; + int ret = ops->read(dev, reg, buf, 1); + + if (ret < 0) + return ret; + return buf[0]; + } + return -ENOSYS; +} + +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 ops->write8(dev, reg, val); + if (ops->write) { + u8 buf[1] = { val }; + + return ops->write(dev, reg, buf, 1); + } + return -ENOSYS; +} + +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, + .flags = DM_UC_FLAG_SEQ_ALIAS, +#if CONFIG_IS_ENABLED(OF_REAL) + .post_bind = dm_scan_fdt_dev, +#endif +}; diff --git a/drivers/rtc/rv3028.c b/drivers/rtc/rv3028.c new file mode 100644 index 00000000000..9f63afc14a8 --- /dev/null +++ b/drivers/rtc/rv3028.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RTC driver for the Micro Crystal RV3028 + * + * based on linux driver from + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni <alexandre.belloni@bootlin.com> + * + */ + +#include <dm.h> +#include <i2c.h> +#include <rtc.h> + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CLKOUT_FD_MASK GENMASK(2, 0) +#define RV3028_CLKOUT_PORIE BIT(3) +#define RV3028_CLKOUT_CLKSY BIT(6) +#define RV3028_CLKOUT_CLKOE BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_UPDATE 0x11 +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1, 0) + +#define OFFSET_STEP_PPT 953674 + +#define RTC_RV3028_LEN 7 + +static int rv3028_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + + if (status & RV3028_STATUS_PORF) { + printf("Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = dm_i2c_read(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret < 0) { + printf("%s: error reading RTC: %x\n", __func__, ret); + return -EIO; + } + + tm->tm_sec = bcd2bin(regs[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3028_HOUR] & 0x3f); + tm->tm_wday = regs[RV3028_WDAY] & 0x7; + tm->tm_mday = bcd2bin(regs[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3028_MONTH] & 0x1f); + tm->tm_year = bcd2bin(regs[RV3028_YEAR]) + 2000; + 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 rv3028_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + 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[RV3028_SEC] = bin2bcd(tm->tm_sec); + regs[RV3028_MIN] = bin2bcd(tm->tm_min); + regs[RV3028_HOUR] = bin2bcd(tm->tm_hour); + regs[RV3028_WDAY] = tm->tm_wday; + regs[RV3028_DAY] = bin2bcd(tm->tm_mday); + regs[RV3028_MONTH] = bin2bcd(tm->tm_mon); + regs[RV3028_YEAR] = bin2bcd(tm->tm_year - 2000); + + ret = dm_i2c_write(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret) { + printf("%s: set rtc error: %d\n", __func__, ret); + return ret; + } + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + status |= RV3028_STATUS_PORF; + return dm_i2c_write(dev, RV3028_STATUS, &status, 1); +} + +static int rv3028_rtc_reset(struct udevice *dev) +{ + return 0; +} + +static int rv3028_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 rv3028_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 data = val; + + return dm_i2c_write(dev, reg, &data, 1); +} + +static int rv3028_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 rv3028_rtc_ops = { + .get = rv3028_rtc_get, + .set = rv3028_rtc_set, + .read8 = rv3028_rtc_read8, + .write8 = rv3028_rtc_write8, + .reset = rv3028_rtc_reset, +}; + +static const struct udevice_id rv3028_rtc_ids[] = { + { .compatible = "microcrystal,rv3028" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3028) = { + .name = "rtc-rv3028", + .id = UCLASS_RTC, + .probe = rv3028_probe, + .of_match = rv3028_rtc_ids, + .ops = &rv3028_rtc_ops, +}; diff --git a/drivers/rtc/rv3029.c b/drivers/rtc/rv3029.c new file mode 100644 index 00000000000..a82acec6f7e --- /dev/null +++ b/drivers/rtc/rv3029.c @@ -0,0 +1,498 @@ +// 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 <command.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <rtc.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/delay.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, +}; diff --git a/drivers/rtc/rv8803.c b/drivers/rtc/rv8803.c new file mode 100644 index 00000000000..82b43722ff5 --- /dev/null +++ b/drivers/rtc/rv8803.c @@ -0,0 +1,170 @@ +// 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 <command.h> +#include <dm.h> +#include <log.h> +#include <rtc.h> +#include <i2c.h> +#include <linux/bitops.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 + 1); + 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) - 1; + 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", }, + { .compatible = "epson,rx8803" }, + { .compatible = "epson,rx8900" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv8803) = { + .name = "rtc-rv8803", + .id = UCLASS_RTC, + .probe = rv8803_probe, + .of_match = rv8803_rtc_ids, + .ops = &rv8803_rtc_ops, +}; diff --git a/drivers/rtc/rx8010sj.c b/drivers/rtc/rx8010sj.c new file mode 100644 index 00000000000..0d778f4c328 --- /dev/null +++ b/drivers/rtc/rx8010sj.c @@ -0,0 +1,380 @@ +/* + * 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 <config.h> +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <linux/bitops.h> + +/*---------------------------------------------------------------------*/ +/* #undef DEBUG_RTC */ + +#ifdef DEBUG_RTC +#define DEBUGR(fmt, args...) printf(fmt, ##args) +#else +#define DEBUGR(fmt, args...) +#endif +/*---------------------------------------------------------------------*/ + +#ifndef CFG_SYS_I2C_RTC_ADDR +# define CFG_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 = CFG_SYS_I2C_RTC_ADDR, + }; + + return rx8010sj_rtc_get(&dev, tm); +} + +int rtc_set(struct rtc_time *tm) +{ + struct ludevice dev = { + .chip = CFG_SYS_I2C_RTC_ADDR, + }; + + return rx8010sj_rtc_set(&dev, tm); +} + +void rtc_reset(void) +{ + struct ludevice dev = { + .chip = CFG_SYS_I2C_RTC_ADDR, + }; + + rx8010sj_rtc_reset(&dev); +} + +void rtc_init(void) +{ + struct ludevice dev = { + .chip = CFG_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 diff --git a/drivers/rtc/rx8025.c b/drivers/rtc/rx8025.c new file mode 100644 index 00000000000..c7895244283 --- /dev/null +++ b/drivers/rtc/rx8025.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007 + * Matthias Fuchs, esd gmbh, matthias.fuchs@esd-electronics.com. + */ + +/* + * Epson RX8025 RTC driver. + */ + +#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 +/*---------------------------------------------------------------------*/ + +enum rx_model { + model_rx_8025, + model_rx_8035, +}; + +/* + * 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_OFFSET_REG_ADDR 0x07 + +#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). + */ + +#define rtc_read(reg) buf[(reg) & 0xf] + +static int rtc_write(struct udevice *dev, uchar reg, uchar val); + +static int rx8025_is_osc_stopped(enum rx_model model, int ctrl2) +{ + int xstp = ctrl2 & RTC_CTL2_BIT_XST; + /* XSTP bit has different polarity on RX-8025 vs RX-8035. + * RX-8025: 0 == oscillator stopped + * RX-8035: 1 == oscillator stopped + */ + + if (model == model_rx_8025) + xstp = !xstp; + + return xstp; +} + +/* + * Get the current time from the RTC + */ +static int rx8025_rtc_get(struct udevice *dev, struct rtc_time *tmp) +{ + int rel = 0; + uchar sec, min, hour, mday, wday, mon, year, ctl2; + uchar buf[16]; + + if (dm_i2c_read(dev, 0, buf, sizeof(buf))) { + 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 (rx8025_is_osc_stopped(dev->driver_data, ctl2)) { + 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(struct udevice *dev, const struct rtc_time *tmp) +{ + /* To work around the read/write cycle issue mentioned + * at the top of this file, write all the time registers + * in one I2C transaction + */ + u8 write_op[8]; + + /* 2412 flag must be set before doing a RTC write, + * otherwise the seconds and minute register + * will be cleared when the flag is set + */ + if (rtc_write(dev, RTC_CTL1_REG_ADDR, RTC_CTL1_BIT_2412)) + return -EIO; + + 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"); + + write_op[RTC_SEC_REG_ADDR] = bin2bcd(tmp->tm_sec); + write_op[RTC_MIN_REG_ADDR] = bin2bcd(tmp->tm_min); + write_op[RTC_HR_REG_ADDR] = bin2bcd(tmp->tm_hour); + write_op[RTC_DAY_REG_ADDR] = bin2bcd(tmp->tm_wday); + write_op[RTC_DATE_REG_ADDR] = bin2bcd(tmp->tm_mday); + write_op[RTC_MON_REG_ADDR] = bin2bcd(tmp->tm_mon); + write_op[RTC_YR_REG_ADDR] = bin2bcd(tmp->tm_year % 100); + write_op[RTC_OFFSET_REG_ADDR] = 0; + + return dm_i2c_write(dev, 0, &write_op[0], 8); +} + +/* + * Reset the RTC + */ +static int rx8025_rtc_reset(struct udevice *dev) +{ + uchar buf[16]; + uchar ctl2; + + if (dm_i2c_read(dev, 0, buf, sizeof(buf))) { + printf("Error reading from RTC\n"); + return -EIO; + } + + ctl2 = rtc_read(RTC_CTL2_REG_ADDR); + ctl2 &= ~(RTC_CTL2_BIT_PON | RTC_CTL2_BIT_VDET); + + if (dev->driver_data == model_rx_8035) + ctl2 &= ~(RTC_CTL2_BIT_XST); + else + ctl2 |= RTC_CTL2_BIT_XST; + + return rtc_write(dev, RTC_CTL2_REG_ADDR, ctl2); +} + +/* + * Helper functions + */ +static int rtc_write(struct udevice *dev, uchar reg, uchar val) +{ + /* The RX8025/RX8035 uses the top 4 bits of the + * 'offset' byte as the start register address, + * and the bottom 4 bits as a 'transfer' mode setting + * (only applicable for reads) + */ + u8 offset = (reg << 4); + + if (dm_i2c_reg_write(dev, offset, val)) { + printf("Error writing to RTC\n"); + return -EIO; + } + + return 0; +} + +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", .data = model_rx_8025 }, + { .compatible = "epson,rx8035", .data = model_rx_8035 }, + { } +}; + +U_BOOT_DRIVER(rx8025_rtc) = { + .name = "rx8025_rtc", + .id = UCLASS_RTC, + .probe = rx8025_probe, + .of_match = rx8025_rtc_ids, + .ops = &rx8025_rtc_ops, +}; diff --git a/drivers/rtc/s35392a.c b/drivers/rtc/s35392a.c new file mode 100644 index 00000000000..03fb9a0be91 --- /dev/null +++ b/drivers/rtc/s35392a.c @@ -0,0 +1,373 @@ +// 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 <dm.h> +#include <i2c.h> +#include <linux/bitrev.h> +#include <rtc.h> +#include <linux/delay.h> + +#define S35390A_CHIP_ADDR 0x30 + +#define S35390A_CMD_STATUS1 0x0 +#define S35390A_CMD_STATUS2 0x1 +#define S35390A_CMD_TIME1 0x2 +#define S35390A_CMD_TIME2 0x3 +#define S35390A_CMD_INT2_REG1 0x5 + +#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 + ret = dm_i2c_read(dev, reg, buf, len); +#else + (void)dev; + ret = i2c_read(S35390A_CHIP_ADDR | 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 + ret = dm_i2c_write(dev, reg, buf, len); +#else + (void)dev; + ret = i2c_write(S35390A_CHIP_ADDR | 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) +{ +#if defined(CONFIG_DM_RTC) + /* 3-bit "command", or register, is encoded within the device address. + */ + i2c_set_chip_offset_len(dev, 0); + i2c_set_chip_addr_offset_mask(dev, 0x7); +#endif + + 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 diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c new file mode 100644 index 00000000000..4404501c2f6 --- /dev/null +++ b/drivers/rtc/sandbox_rtc.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <dm.h> +#include <i2c.h> +#include <rtc.h> +#include <asm/rtc.h> +#include <dm/acpi.h> + +#define REG_COUNT 0x80 + +static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time) +{ + u8 buf[7]; + int ret; + + ret = dm_i2c_read(dev, REG_SEC, buf, sizeof(buf)); + if (ret < 0) + return ret; + + time->tm_sec = buf[REG_SEC - REG_SEC]; + time->tm_min = buf[REG_MIN - REG_SEC]; + time->tm_hour = buf[REG_HOUR - REG_SEC]; + time->tm_mday = buf[REG_MDAY - REG_SEC]; + time->tm_mon = buf[REG_MON - REG_SEC]; + time->tm_year = buf[REG_YEAR - REG_SEC] + 1900; + time->tm_wday = buf[REG_WDAY - REG_SEC]; + + return 0; +} + +static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time) +{ + u8 buf[7]; + int ret; + + buf[REG_SEC - REG_SEC] = time->tm_sec; + buf[REG_MIN - REG_SEC] = time->tm_min; + buf[REG_HOUR - REG_SEC] = time->tm_hour; + buf[REG_MDAY - REG_SEC] = time->tm_mday; + buf[REG_MON - REG_SEC] = time->tm_mon; + buf[REG_YEAR - REG_SEC] = time->tm_year - 1900; + buf[REG_WDAY - REG_SEC] = time->tm_wday; + + ret = dm_i2c_write(dev, REG_SEC, buf, sizeof(buf)); + 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); +} + +#if CONFIG_IS_ENABLED(ACPIGEN) +static int sandbox_rtc_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "RTCC"); +} + +struct acpi_ops sandbox_rtc_acpi_ops = { + .get_name = sandbox_rtc_get_name, +}; +#endif + +static int sandbox_rtc_bind(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(PLATDATA) + struct sandbox_i2c_rtc_plat_data *plat = dev_get_plat(dev); + + /* Set up the emul_idx for i2c_emul_find() */ + i2c_emul_set_idx(dev, plat->dtplat.sandbox_emul->idx); +#endif + + return 0; +} + +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(sandbox_rtc) = { + .name = "sandbox_rtc", + .id = UCLASS_RTC, + .of_match = sandbox_rtc_ids, + .ops = &sandbox_rtc_ops, + .bind = sandbox_rtc_bind, + ACPI_OPS_PTR(&sandbox_rtc_acpi_ops) +}; diff --git a/drivers/rtc/stm32_rtc.c b/drivers/rtc/stm32_rtc.c new file mode 100644 index 00000000000..ee70c11c8bc --- /dev/null +++ b/drivers/rtc/stm32_rtc.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ + +#define LOG_CATEGORY UCLASS_RTC + +#include <clk.h> +#include <dm.h> +#include <malloc.h> +#include <rtc.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitops.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) + 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); + + 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) + return ret; + + ret = stm32_rtc_init(dev); + + if (ret) + clk_disable(&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 = sizeof(struct stm32_rtc_priv), +}; diff --git a/drivers/rtc/zynqmp_rtc.c b/drivers/rtc/zynqmp_rtc.c new file mode 100644 index 00000000000..15122a04838 --- /dev/null +++ b/drivers/rtc/zynqmp_rtc.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021, Xilinx, Inc. + */ + +#define LOG_CATEGORY UCLASS_RTC + +#include <dm.h> +#include <rtc.h> +#include <asm/io.h> + +/* RTC Registers */ +#define RTC_SET_TM_WR 0x00 +#define RTC_SET_TM_RD 0x04 +#define RTC_CALIB_WR 0x08 +#define RTC_CUR_TM 0x10 +#define RTC_INT_STS 0x20 +#define RTC_CTRL 0x40 + +#define RTC_INT_SEC BIT(0) +#define RTC_BATT_EN BIT(31) +#define RTC_CALIB_DEF 0x198233 +#define RTC_CALIB_MASK 0x1FFFFF + +struct zynqmp_rtc_priv { + fdt_addr_t base; + unsigned int calibval; +}; + +static int zynqmp_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + struct zynqmp_rtc_priv *priv = dev_get_priv(dev); + u32 status; + unsigned long read_time; + + status = readl(priv->base + RTC_INT_STS); + + if (status & RTC_INT_SEC) { + /* + * RTC has updated the CURRENT_TIME with the time written into + * SET_TIME_WRITE register. + */ + read_time = readl(priv->base + RTC_CUR_TM); + } else { + /* + * Time written in SET_TIME_WRITE has not yet updated into + * the seconds read register, so read the time from the + * SET_TIME_WRITE instead of CURRENT_TIME register. + * Since we add +1 sec while writing, we need to -1 sec while + * reading. + */ + read_time = readl(priv->base + RTC_SET_TM_RD) - 1; + } + + rtc_to_tm(read_time, tm); + + return 0; +} + +static int zynqmp_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + struct zynqmp_rtc_priv *priv = dev_get_priv(dev); + unsigned long new_time = 0; + + if (tm) + /* + * The value written will be updated after 1 sec into the + * seconds read register, so we need to program time +1 sec + * to get the correct time on read. + */ + new_time = rtc_mktime(tm) + 1; + + /* + * Writing into calibration register will clear the Tick Counter and + * force the next second to be signaled exactly in 1 second period + */ + priv->calibval &= RTC_CALIB_MASK; + writel(priv->calibval, (priv->base + RTC_CALIB_WR)); + + writel(new_time, priv->base + RTC_SET_TM_WR); + + /* + * Clear the rtc interrupt status register after setting the + * time. During a read_time function, the code should read the + * RTC_INT_STATUS register and if bit 0 is still 0, it means + * that one second has not elapsed yet since RTC was set and + * the current time should be read from SET_TIME_READ register; + * otherwise, CURRENT_TIME register is read to report the time + */ + writel(RTC_INT_SEC, priv->base + RTC_INT_STS); + + return 0; +} + +static int zynqmp_rtc_reset(struct udevice *dev) +{ + return zynqmp_rtc_set(dev, NULL); +} + +static int zynqmp_rtc_init(struct udevice *dev) +{ + struct zynqmp_rtc_priv *priv = dev_get_priv(dev); + u32 rtc_ctrl; + + /* Enable RTC switch to battery when VCC_PSAUX is not available */ + rtc_ctrl = readl(priv->base + RTC_CTRL); + rtc_ctrl |= RTC_BATT_EN; + writel(rtc_ctrl, priv->base + RTC_CTRL); + + /* + * Based on crystal freq of 33.330 KHz + * set the seconds counter and enable, set fractions counter + * to default value suggested as per design spec + * to correct RTC delay in frequency over period of time. + */ + priv->calibval &= RTC_CALIB_MASK; + writel(priv->calibval, (priv->base + RTC_CALIB_WR)); + + return 0; +} + +static int zynqmp_rtc_probe(struct udevice *dev) +{ + struct zynqmp_rtc_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->calibval = dev_read_u32_default(dev, "calibration", + RTC_CALIB_DEF); + + ret = zynqmp_rtc_init(dev); + + return ret; +} + +static const struct rtc_ops zynqmp_rtc_ops = { + .get = zynqmp_rtc_get, + .set = zynqmp_rtc_set, + .reset = zynqmp_rtc_reset, +}; + +static const struct udevice_id zynqmp_rtc_ids[] = { + { .compatible = "xlnx,zynqmp-rtc" }, + { } +}; + +U_BOOT_DRIVER(rtc_zynqmp) = { + .name = "rtc-zynqmp", + .id = UCLASS_RTC, + .probe = zynqmp_rtc_probe, + .of_match = zynqmp_rtc_ids, + .ops = &zynqmp_rtc_ops, + .priv_auto = sizeof(struct zynqmp_rtc_priv), +}; |