diff --git a/ChangeLog b/ChangeLog index 24b20e48c9..59ba77c5ca 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11129,4 +11129,7 @@ (2015-11-19). * drivers/mtd/w25.c: Add support for byte write mode. From Ken Peit (2015-11-20). + * drivers/timers/pcf85263.c and include/nuttx/times/pcf85263.h: Add + a driver for the NXP PCF85263 I2C RTC. Untested on initial commit + (2015-11-20). diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 333b65d2aa..af8b8a4a9e 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -181,9 +181,28 @@ endchoice # Maxim Integrated RTC config DS3231_I2C_FREQUENCY int "DS1307/DS323x I2C frequency" default 400000 - range 1 4000000 + range 1 400000 endif # RTC_DSXXXX + +config RTC_PCF85263 + bool "PCF85263 RTC Driver" + default n + select I2C + select I2C_TRANSFER + select RTC_EXTERNAL + select RTC_DATETIME + ---help--- + Enables support for the Maxim Integrated DS3231 I2C RTC timer. + +if RTC_PCF85263 + +config PCF85263_I2C_FREQUENCY + int "PCF85263 I2C frequency" + default 400000 + range 1 400000 + +endif # RTC_PCF85263 endif # RTC menuconfig WATCHDOG diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index 0707c244a6..7e3073d324 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -57,6 +57,12 @@ ifeq ($(CONFIG_RTC_DSXXXX),y) TMRVPATH = :timers endif +ifeq ($(CONFIG_RTC_PCF85263),y) + CSRCS += pcf85263.c + TMRDEPPATH = --dep-path timers + TMRVPATH = :timers +endif + ifeq ($(CONFIG_RTC_DRIVER),y) CSRCS += rtc.c TMRDEPPATH = --dep-path timers diff --git a/drivers/timers/ds3231.c b/drivers/timers/ds3231.c index 5466c99f33..894c069d22 100644 --- a/drivers/timers/ds3231.c +++ b/drivers/timers/ds3231.c @@ -463,7 +463,7 @@ int up_rtc_settime(FAR const struct timespec *tp) return -EINVAL; } #endif - + rtc_dumptime(&tm, "New time"); /* Construct the message */ @@ -479,7 +479,7 @@ int up_rtc_settime(FAR const struct timespec *tp) buffer[2] = rtc_bin2bcd(newtm.tm_min); - /* Save hour (0-23) with 24-hour time indicatin */ + /* Save hour (0-23) with 24-hour time indication */ buffer[3] = rtc_bin2bcd(newtm.tm_hour) | DSXXXX_TIME_24; @@ -491,7 +491,7 @@ int up_rtc_settime(FAR const struct timespec *tp) buffer[4] = 1; #endif - /* Save the day of the week (1-31) */ + /* Save the day of the month (1-31) */ buffer[5] = rtc_bin2bcd(newtm.tm_mday); diff --git a/drivers/timers/pcf85263.c b/drivers/timers/pcf85263.c new file mode 100644 index 0000000000..7eca679715 --- /dev/null +++ b/drivers/timers/pcf85263.c @@ -0,0 +1,531 @@ +/************************************************************************************ + * drivers/timers/pcf85263.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "pcf85263.h" + +#ifdef CONFIG_RTC_PCF85263 + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ +/* This RTC implementation supports only date/time RTC hardware */ + +#ifndef CONFIG_RTC_DATETIME +# error CONFIG_RTC_DATETIME must be set to use this driver +#endif + +#ifdef CONFIG_RTC_HIRES +# error CONFIG_RTC_HIRES must NOT be set with this driver +#endif + +#ifndef CONFIG_I2C_TRANSFER +# error CONFIG_I2C_TRANSFER is required by this driver +#endif + +#ifndef CONFIG_PCF85263_I2C_FREQUENCY +# error CONFIG_PCF85263_I2C_FREQUENCY is not configured +# define CONFIG_PCF85263_I2C_FREQUENCY 400000 +#endif + +#if CONFIG_PCF85263_I2C_FREQUENCY > 400000 +# error CONFIG_PCF85263_I2C_FREQUENCY is out of range +#endif + +#define PCF85263_I2C_ADDRESS 0x68 + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_RTC +#endif + +/* Debug ****************************************************************************/ + +#ifdef CONFIG_DEBUG_RTC +# define rtcdbg dbg +# define rtcvdbg vdbg +# define rtclldbg lldbg +# define rtcllvdbg llvdbg +#else +# define rtcdbg(x...) +# define rtcvdbg(x...) +# define rtclldbg(x...) +# define rtcllvdbg(x...) +#endif + +/************************************************************************************ + * Priviate Types + ************************************************************************************/ +/* This structure describes the state of the PCF85263 chip. Only a single RTC is + * supported. + */ + +struct pcf85263_dev_s +{ + FAR struct i2c_dev_s *i2c; /* Contained reference to the I2C bus driver */ +}; + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +/* g_rtc_enabled is set true after the RTC has successfully initialized */ + +volatile bool g_rtc_enabled = false; + +/************************************************************************************ + * Private Data + ************************************************************************************/ +/* The state of the PCF85263 chip. Only a single RTC is supported */ + +static struct pcf85263_dev_s g_pcf85263; + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: rtc_dumptime + * + * Description: + * Show the broken out time. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ************************************************************************************/ + +#ifdef CONFIG_DEBUG_RTC +static void rtc_dumptime(FAR struct tm *tp, FAR const char *msg) +{ + rtclldbg("%s:\n", msg); + rtclldbg(" tm_sec: %08x\n", tp->tm_sec); + rtclldbg(" tm_min: %08x\n", tp->tm_min); + rtclldbg(" tm_hour: %08x\n", tp->tm_hour); + rtclldbg(" tm_mday: %08x\n", tp->tm_mday); + rtclldbg(" tm_mon: %08x\n", tp->tm_mon); + rtclldbg(" tm_year: %08x\n", tp->tm_year); +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + rtclldbg(" tm_wday: %08x\n", tp->tm_wday); + rtclldbg(" tm_yday: %08x\n", tp->tm_yday); + rtclldbg(" tm_isdst: %08x\n", tp->tm_isdst); +#endif +} +#else +# define rtc_dumptime(tp, msg) +#endif + +/************************************************************************************ + * Name: rtc_bin2bcd + * + * Description: + * Converts a 2 digit binary to BCD format + * + * Input Parameters: + * value - The byte to be converted. + * + * Returned Value: + * The value in BCD representation + * + ************************************************************************************/ + +static uint8_t rtc_bin2bcd(int value) +{ + uint8_t msbcd = 0; + + while (value >= 10) + { + msbcd++; + value -= 10; + } + + return (msbcd << 4) | value; +} + +/************************************************************************************ + * Name: rtc_bcd2bin + * + * Description: + * Convert from 2 digit BCD to binary. + * + * Input Parameters: + * value - The BCD value to be converted. + * + * Returned Value: + * The value in binary representation + * + ************************************************************************************/ + +static int rtc_bcd2bin(uint8_t value) +{ + int tens = ((int)value >> 4) * 10; + return tens + (value & 0x0f); +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: pcf85263_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This function is + * called once during the OS initialization sequence by board-specific logic. + * + * After pcf85263_rtc_initialize() is called, the OS function clock_synchronize() + * should also be called to synchronize the system timer to a hardware RTC. That + * operation is normally performed automatically by the system during clock + * initialization. However, when an external RTC is used, the board logic will + * need to explicitly re-synchronize the system timer to the RTC when the RTC + * becomes available. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int pcf85263_rtc_initialize(FAR struct i2c_dev_s *i2c) +{ + /* Remember the i2c device and claim that the RTC is enabled */ + + g_pcf85263.i2c = i2c; + g_rtc_enabled = true; + return OK; +} + +/************************************************************************************ + * Name: up_rtc_getdatetime + * + * Description: + * Get the current date and time from the date/time RTC. This interface + * is only supported by the date/time RTC hardware implementation. + * It is used to replace the system timer. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC and CONFIG_RTC_DATETIME + * are selected (and CONFIG_RTC_HIRES is not). + * + * NOTE: Some date/time RTC hardware is capability of sub-second accuracy. That + * sub-second accuracy is lost in this interface. However, since the system time + * is reinitialized on each power-up/reset, there will be no timing inaccuracy in + * the long run. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_getdatetime(FAR struct tm *tp) +{ + struct i2c_msg_s msg[4]; + uint8_t secaddr; + uint8_t buffer[7]; + uint8_t seconds; + int ret; + + /* If this function is called before the RTC has been initialized (and it will be), + * then just return the data/time of the epoch, 12:00 am, Jan 1, 1970. + */ + + if (!g_rtc_enabled) + { + tp->tm_sec = 0; + tp->tm_min = 0; + tp->tm_hour = 0; + +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + /* Jan 1, 1970 was a Thursday */ + + tp->tm_wday = 4; +#endif + + tp->tm_mday = 1; + tp->tm_mon = 0; + tp->tm_year = 70; + return -EAGAIN; + } + + /* Select to begin reading at the seconds register */ + + secaddr = PCF85263_RTC_SECONDS; + + msg[0].addr = PCF85263_I2C_ADDRESS; + msg[0].flags = 0; + msg[0].buffer = &secaddr; + msg[0].length = 1; + + /* Set up to read 7 registers: secondss, minutes, hour, day-of-week, date, + * month, year + */ + + msg[1].addr = PCF85263_I2C_ADDRESS; + msg[1].flags = I2C_M_READ; + msg[1].buffer = buffer; + msg[1].length = 7; + + /* Read the seconds register again */ + + msg[2].addr = PCF85263_I2C_ADDRESS; + msg[2].flags = 0; + msg[2].buffer = &secaddr; + msg[2].length = 1; + + msg[3].addr = PCF85263_I2C_ADDRESS; + msg[3].flags = I2C_M_READ; + msg[3].buffer = &seconds; + msg[3].length = 1; + + /* Configure I2C before using it */ + + I2C_SETFREQUENCY(g_pcf85263.i2c, CONFIG_PCF85263_I2C_FREQUENCY); + + /* Perform the transfer (This could be done with I2C_WRITEREAD()). The + * transfer may be performed repeatedly of the seconds values decreases, + * meaning that that was a rollover in the seconds. + */ + + do + { + ret = I2C_TRANSFER(g_pcf85263.i2c, msg, 4); + if (ret < 0) + { + rtcdbg("ERROR: I2C_TRANSFER failed: %d\n", ret) + return ret; + } + } + while ((buffer[0] & PCF85263_RTC_SECONDS_MASK) > + (seconds & PCF85263_RTC_SECONDS_MASK)); + + /* Format the return time */ + /* Return seconds (0-61) */ + + tp->tm_sec = rtc_bcd2bin(buffer[0] & PCF85263_RTC_SECONDS_MASK); + + /* Return minutes (0-59) */ + + tp->tm_min = rtc_bcd2bin(buffer[1] & PCF85263_RTC_MINUTES_MASK); + + /* Return hour (0-23). This assumes 24-hour time was set. */ + + tp->tm_hour = rtc_bcd2bin(buffer[2] & PCF85263_RTC_HOURS24_MASK); + + /* Return the day of the month (1-31) */ + + tp->tm_mday = rtc_bcd2bin(buffer[3] & PCF85263_RTC_DAYS_MASK); + +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + /* Return the day of the week (0-6) */ + + tp->tm_wday = (rtc_bcd2bin(buffer[4]) & PCF85263_RTC_WEEKDAYS_MASK); +#endif + + /* Return the month (0-11) */ + + tp->tm_mon = rtc_bcd2bin(buffer[5] & PCF85263_RTC_MONTHS_MASK) - 1; + + /* Return the years since 1900. The RTC will hold years since 1968 (a leap year + * like 2000). + */ + + tp->tm_year = rtc_bcd2bin(buffer[6]) + 68; + + rtc_dumptime(tp, "Returning"); + return OK; +} + +/************************************************************************************ + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. All RTC implementations must be able to + * set their time based on a standard timespec. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + struct i2c_msg_s msg[3]; + struct tm newtm; + time_t newtime; + uint8_t buffer[8]; + uint8_t seconds; + int ret; + + /* If this function is called before the RTC has been initialized then just return + * an error. + */ + + if (!g_rtc_enabled) + { + return -EAGAIN; + } + + rtc_dumptime(tp, "Setting time"); + + /* Get the broken out time */ + + newtime = (time_t)tp->tv_sec; + if (tp->tv_nsec >= 500000000) + { + /* Round up */ + + newtime++; + } + + #ifdef CONFIG_LIBC_LOCALTIME + if (localtime_r(&newtime, &newtm) == NULL) + { + rtcdbg("ERROR: localtime_r failed\n") + return -EINVAL; + } +#else + if (gmtime_r(&newtime, &newtm) == NULL) + { + rtcdbg("ERROR: gmtime_r failed\n") + return -EINVAL; + } +#endif + + rtc_dumptime(&tm, "New time"); + + /* Construct the message */ + /* Write starting with the seconds regiser */ + + buffer[0] = PCF85263_RTC_SECONDS; + + /* Save seconds (0-59) converted to BCD */ + + buffer[1] = rtc_bin2bcd(newtm.tm_sec); + + /* Save minutes (0-59) converted to BCD */ + + buffer[2] = rtc_bin2bcd(newtm.tm_min); + + /* Save hour (0-23) with 24-hour time indication */ + + buffer[3] = rtc_bin2bcd(newtm.tm_hour); + + /* Save the day of the month (1-31) */ + + buffer[4] = rtc_bin2bcd(newtm.tm_mday); + + /* Save the day of the week (1-7) */ + +#if defined(CONFIG_LIBC_LOCALTIME) || defined(CONFIG_TIME_EXTENDED) + buffer[5] = rtc_bin2bcd(newtm.tm_wday); +#else + buffer[5] = 0; +#endif + + /* Save the month (1-12) */ + + buffer[6] = rtc_bin2bcd(newtm.tm_mon + 1); + + /* Save the year. Use years since 1968 (a leap year like 2000) */ + + buffer[7] = rtc_bin2bcd(newtm.tm_year - 68); + + /* Setup the I2C message */ + + msg[0].addr = PCF85263_I2C_ADDRESS; + msg[0].flags = 0; + msg[0].buffer = buffer; + msg[0].length = 8; + + /* Read back the seconds register */ + + msg[1].addr = PCF85263_I2C_ADDRESS; + msg[1].flags = 0; + msg[1].buffer = buffer; + msg[1].length = 1; + + msg[2].addr = PCF85263_I2C_ADDRESS; + msg[2].flags = I2C_M_READ; + msg[2].buffer = &seconds; + msg[2].length = 1; + + /* Configure I2C before using it */ + + I2C_SETFREQUENCY(g_pcf85263.i2c, CONFIG_PCF85263_I2C_FREQUENCY); + + /* Perform the transfer (This could be done with I2C_READ). This transfer + * will be repeated if the seconds count rolls over to a smaller value + * while writing. + */ + + do + { + ret = I2C_TRANSFER(g_pcf85263.i2c, msg, 3); + if (ret < 0) + { + rtcdbg("ERROR: I2C_TRANSFER failed: %d\n", ret) + return ret; + } + } + while ((buffer[1] & PCF85263_RTC_SECONDS_MASK) > + (seconds & PCF85263_RTC_SECONDS_MASK)); + + return OK; +} + +#endif /* CONFIG_RTC_PCF85263 */ diff --git a/drivers/timers/pcf85263.h b/drivers/timers/pcf85263.h index f8e5b7e559..344a3f2873 100644 --- a/drivers/timers/pcf85263.h +++ b/drivers/timers/pcf85263.h @@ -357,7 +357,7 @@ # define PCF85263_CTL_OSC_CLKIV (1 << 7) /* Bit 7: Output clock inversion */ #define PCF85263_CTL_BATTERY_SWITCH 0x26 /* Battery switch control register */ -# define PCF85263_CTL_BATTERY_BSTH (1 << 0) /* Bit 0: +# define PCF85263_CTL_BATTERY_BSTH (1 << 0) /* Bit 0: Threshold voltage control */ # define PCF85263_CTL_BATTERY_BSM_SHIFT (1) /* Bits 1-2: Battery switch mode bits */ # define PCF85263_CTL_BATTERY_BSM_MASK (3 << PCF85263_CTL_BATTERY_BSM_SHIFT) # define PCF85263_CTL_BATTERY_BSM_VTH (0 << PCF85263_CTL_BATTERY_BSM_SHIFT) /* Switching at the Vth level */ @@ -453,7 +453,7 @@ # define PCF85263_CTL_WDR_SHIFT (2) /* Bits 2-6: Watchdog register bits */ # define PCF85263_CTL_WDR_MASK (31 << PCF85263_CTL_WDR_SHIFT) # define PCF85263_CTL_WDR(n) ((uint9_t)(n) << PCF85263_CTL_WDR_SHIFT) -# define PCF85263_CTL_WDM (1 << 7) /* Bit 7: Watchdog mode +# define PCF85263_CTL_WDM (1 << 7) /* Bit 7: Watchdog mode */ /* Stop */ diff --git a/include/nuttx/timers/pcf85263.h b/include/nuttx/timers/pcf85263.h new file mode 100644 index 0000000000..fdda2b815e --- /dev/null +++ b/include/nuttx/timers/pcf85263.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * include/nuttx/timers/pcf85263.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_PCF85263_H +#define __INCLUDE_NUTTX_TIMERS_PCF85263_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_RTC_PCF85263 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Name: pcf85263_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This function is + * called once during the OS initialization sequence by board-specific logic. + * + * After pcf85263_rtc_initialize() is called, the OS function clock_synchronize() + * should also be called to synchronize the system timer to a hardware RTC. That + * operation is normally performed automatically by the system during clock + * initialization. However, when an external RTC is used, the board logic will + * need to explicitly re-synchronize the system timer to the RTC when the RTC + * becomes available. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +struct i2c_dev_s; /* Forward reference */ +int pcf85263_rtc_initialize(FAR struct i2c_dev_s *i2c); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_RTC_PCF85263 */ +#endif /* __INCLUDE_NUTTX_TIMERS_PCF85263_H */