Add support for M41T60 serial access real-time clock
Based on the comment I received, I have made some modifications to the patch I submitted to add support for the M41T60 RTC to u-boot. The main changes are to fix and amplify the comments, but I have also changed the code so it will not modify the OFIE bit on a power up or invalid time.
Signed-off-by Larry Johnson <[email protected]>
rtc/m41t60.c +237 -0
@@ -0,0 +1,237 @@ +/* +* (C) Copyright 2007 +* Larry Johnson, lrj at acm.org +* +* based on rtc/m41t11.c which is ... +* +* (C) Copyright 2002 +* Andrew May, Viasat Inc, amay at viasat.com +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 USA +*/ + +/* +* STMicroelectronics M41T60 serial access real-time clock +*/ + +/* #define DEBUG 1 */ + +#include <common.h> +#include <command.h> +#include <rtc.h> +#include <i2c.h> + +#if defined(CONFIG_RTC_M41T60 ) && defined(CFG_I2C_RTC_ADDR ) && \ + (CONFIG_COMMANDS &CFG_CMD_DATE ) + +static unsigned bcd2bin (uchar n) +{ +return ((((n >>4 ) &0x0F ) *10 ) + (n &0x0F )); +} + +static unsigned char bin2bcd (unsigned int n) +{ +return (((n /10 ) <<4 ) | (n %10 )); +} + +/* +* Convert between century and "century bits" (CB1 and CB0). These routines +* assume years are in the range 1900 - 2299. +*/ + +static unsigned char year2cb (unsigned const year) +{ +if (year <1900 || year >=2300 ) { +printf ("M41T60 RTC: year %d out of range\n" , year); + } +return (year /100 ) &0x3 ; +} + +static unsigned cb2year (unsigned const cb) +{ +return 1900 +100 * ((cb +1 ) &0x3 ); +} + +/* +* These are simple defines for the chip local to here so they aren't too +* verbose. DAY/DATE aren't nice but that is how they are on the data sheet. +*/ +#define RTC_SEC 0x0 +#define RTC_MIN 0x1 +#define RTC_HOUR 0x2 +#define RTC_DAY 0x3 +#define RTC_DATE 0x4 +#define RTC_MONTH 0x5 +#define RTC_YEARS 0x6 + +#define RTC_REG_CNT 7 + +#define RTC_CTRL 0x7 + +#if defined(DEBUG ) +static void rtc_dump (char const *const label) +{ +uchar data[8 ]; + +if (i2c_read (CFG_I2C_RTC_ADDR ,0 ,1 , data,sizeof (data))) { +printf ("I2C read failed in rtc_dump()\n" ); +return ; + } +printf ("RTC dump %s: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n" , + label, data[0 ], data[1 ], data[2 ], data[3 ], + data[4 ], data[5 ], data[6 ], data[7 ]); +} +#else /* !defined(DEBUG) */ +#define rtc_dump (label) +#endif /* defined(DEBUG) */ + +static uchar *rtc_validate (void ) +{ +/* +* This routine uses the OUT bit and the validity of the time values to +* determine whether there has been an initial power-up since the last +* time the routine was run. It assumes that the OUT bit is not being +* used for any other purpose. +*/ +static const uchar daysInMonth[0x13 ] = { +0x00 ,0x31 ,0x29 ,0x31 ,0x30 ,0x31 ,0x30 ,0x31 , +0x31 ,0x30 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 , +0x31 ,0x30 ,0x31 + }; +static uchar data[8 ]; +uchar min, date, month, years; + +rtc_dump ("begin validate" ); +if (i2c_read (CFG_I2C_RTC_ADDR ,0 ,1 , data,sizeof (data))) { +printf ("I2C read failed in rtc_validate()\n" ); +return 0 ; + } +/* +* If the OUT bit is "1", there has been a loss of power, so stop the +* oscillator so it can be "kick-started" as per data sheet. +*/ +if (0x00 != (data[RTC_CTRL ] &0x80 )) { +printf ("M41T60 RTC clock lost power.\n" ); + data[RTC_SEC ] =0x80 ; +if (i2c_write (CFG_I2C_RTC_ADDR ,RTC_SEC ,1 , data,1 )) { +printf ("I2C write failed in rtc_validate()\n" ); +return 0 ; + } + } +/* +* If the oscillator is stopped or the date is invalid, then reset the +* OUT bit to "0", reset the date registers, and start the oscillator. +*/ + min = data[RTC_MIN ] &0x7F ; + date = data[RTC_DATE ]; + month = data[RTC_MONTH ] &0x3F ; + years = data[RTC_YEARS ]; +if (0x59 < data[RTC_SEC ] ||0x09 < (data[RTC_SEC ] &0x0F ) || +0x59 < min ||0x09 < (min &0x0F ) || +0x23 < data[RTC_HOUR ] ||0x09 < (data[RTC_HOUR ] &0x0F ) || +0x07 < data[RTC_DAY ] ||0x00 == data[RTC_DAY ] || +0x12 < month || +0x99 < years ||0x09 < (years &0x0F ) || + daysInMonth[month] < date ||0x09 < (date &0x0F ) ||0x00 == date || + (0x29 == date &&0x02 == month && + ((0x00 != (years &0x03 )) || + (0x00 == years &&0x00 != (data[RTC_MONTH ] &0xC0 ))))) { +printf ("Resetting M41T60 RTC clock.\n" ); +/* +* Set to 00:00:00 1900-01-01 (Monday) +*/ + data[RTC_SEC ] =0x00 ; + data[RTC_MIN ] &=0x80 ;/* preserve OFIE bit */ + data[RTC_HOUR ] =0x00 ; + data[RTC_DAY ] =0x02 ; + data[RTC_DATE ] =0x01 ; + data[RTC_MONTH ] =0xC1 ; + data[RTC_YEARS ] =0x00 ; + data[RTC_CTRL ] &=0x7F ;/* reset OUT bit */ + } +if (i2c_write (CFG_I2C_RTC_ADDR ,0 ,1 , data,sizeof (data))) { +printf ("I2C write failed in rtc_validate()\n" ); +return 0 ; + } +return data; +} + +void rtc_get (struct rtc_time *tmp) +{ +uchar const *const data =rtc_validate (); + +if (!data) { +return ; + } + tmp->tm_sec =bcd2bin (data[RTC_SEC ] &0x7F ); + tmp->tm_min =bcd2bin (data[RTC_MIN ] &0x7F ); + tmp->tm_hour =bcd2bin (data[RTC_HOUR ] &0x3F ); + tmp->tm_mday =bcd2bin (data[RTC_DATE ] &0x3F ); + tmp->tm_mon =bcd2bin (data[RTC_MONTH ] &0x1F ); + tmp->tm_year =cb2year (data[RTC_MONTH ] >>6 ) +bcd2bin (data[RTC_YEARS ]); + tmp->tm_wday =bcd2bin (data[RTC_DAY ] &0x07 ) -1 ; + tmp->tm_yday =0 ; + tmp->tm_isdst =0 ; + +debug ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n" , + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +void rtc_set (struct rtc_time *tmp) +{ +uchar *const data =rtc_validate (); + +if (!data) { +return ; + } +debug ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n" , + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + data[RTC_SEC ] = (data[RTC_SEC ] &0x80 ) | (bin2bcd (tmp->tm_sec) &0x7F ); + data[RTC_MIN ] = (data[RTC_MIN ] &0X80 ) | (bin2bcd (tmp->tm_min) &0X7F ); + data[RTC_HOUR ] =bin2bcd (tmp->tm_hour) &0x3F ; + data[RTC_DATE ] =bin2bcd (tmp->tm_mday) &0x3F ; + data[RTC_MONTH ] =bin2bcd (tmp->tm_mon) &0x1F ; + data[RTC_YEARS ] =bin2bcd (tmp->tm_year %100 ); + data[RTC_MONTH ] |=year2cb (tmp->tm_year) <<6 ; + data[RTC_DAY ] =bin2bcd (tmp->tm_wday +1 ) &0x07 ; +if (i2c_write (CFG_I2C_RTC_ADDR ,0 ,1 , data,RTC_REG_CNT )) { +printf ("I2C write failed in rtc_set()\n" ); +return ; + } +} + +void rtc_reset (void ) +{ +uchar *const data =rtc_validate (); + +if (!data) { +return ; + } +rtc_dump ("begin reset" ); +/* +* Turn off frequency test. +*/ + data[RTC_CTRL ] &=0xBF ; +if (i2c_write (CFG_I2C_RTC_ADDR ,RTC_CTRL ,1 , data +RTC_CTRL ,1 )) { +printf ("I2C write failed in rtc_reset()\n" ); +return ; + } +rtc_dump ("end reset" ); +} +#endif /* CONFIG_RTC_M41T60 && CFG_I2C_RTC_ADDR && CFG_CMD_DATE */
rtc/Makefile +2 -1
@@ -31,7 +31,8 @@ bf5xx_rtc.o ds12887.o ds1302.o ds1306.o ds1307.o \ ds1337.o ds1374.o ds1556.o ds164x.o ds174x.o ds3231.o \ m41t11.o max6900.o m48t35ax.o mc146818.o mk48t59.o \ - mpc5xxx.o mpc8xx.o pcf8563.o s3c24x0_rtc.o rs5c372.o + mpc5xxx.o mpc8xx.o pcf8563.o s3c24x0_rtc.o rs5c372.o \ + m41t60.o SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS))
-- Best regards, Larry