| /* | |
| * ds1302.c - Support for the Dallas Semiconductor DS1302 Timekeeping Chip | |
| * | |
| * Rex G. Feany <rfeany@zumanetworks.com> | |
| * | |
| */ | |
| #include <common.h> | |
| #include <command.h> | |
| #include <rtc.h> | |
| #if defined(CONFIG_RTC_DS1302) && (CONFIG_COMMANDS & CFG_CMD_DATE) | |
| /* GPP Pins */ | |
| #define DATA 0x200 | |
| #define SCLK 0x400 | |
| #define RST 0x800 | |
| /* Happy Fun Defines(tm) */ | |
| #define RESET rtc_go_low(RST), rtc_go_low(SCLK) | |
| #define N_RESET rtc_go_high(RST), rtc_go_low(SCLK) | |
| #define CLOCK_HIGH rtc_go_high(SCLK) | |
| #define CLOCK_LOW rtc_go_low(SCLK) | |
| #define DATA_HIGH rtc_go_high(DATA) | |
| #define DATA_LOW rtc_go_low(DATA) | |
| #define DATA_READ (GTREGREAD(GPP_VALUE) & DATA) | |
| #undef RTC_DEBUG | |
| #ifdef RTC_DEBUG | |
| # define DPRINTF(x,args...) printf("ds1302: " x , ##args) | |
| static inline void DUMP(const char *ptr, int num) | |
| { | |
| while (num--) printf("%x ", *ptr++); | |
| printf("]\n"); | |
| } | |
| #else | |
| # define DPRINTF(x,args...) | |
| # define DUMP(ptr, num) | |
| #endif | |
| /* time data format for DS1302 */ | |
| struct ds1302_st | |
| { | |
| unsigned char CH:1; /* clock halt 1=stop 0=start */ | |
| unsigned char sec10:3; | |
| unsigned char sec:4; | |
| unsigned char zero0:1; | |
| unsigned char min10:3; | |
| unsigned char min:4; | |
| unsigned char fmt:1; /* 1=12 hour 0=24 hour */ | |
| unsigned char zero1:1; | |
| unsigned char hr10:2; /* 10 (0-2) or am/pm (am/pm, 0-1) */ | |
| unsigned char hr:4; | |
| unsigned char zero2:2; | |
| unsigned char date10:2; | |
| unsigned char date:4; | |
| unsigned char zero3:3; | |
| unsigned char month10:1; | |
| unsigned char month:4; | |
| unsigned char zero4:5; | |
| unsigned char day:3; /* day of week */ | |
| unsigned char year10:4; | |
| unsigned char year:4; | |
| unsigned char WP:1; /* write protect 1=protect 0=unprot */ | |
| unsigned char zero5:7; | |
| }; | |
| static int ds1302_initted=0; | |
| /* Pin control */ | |
| static inline void | |
| rtc_go_high(unsigned int mask) | |
| { | |
| unsigned int f = GTREGREAD(GPP_VALUE) | mask; | |
| GT_REG_WRITE(GPP_VALUE, f); | |
| } | |
| static inline void | |
| rtc_go_low(unsigned int mask) | |
| { | |
| unsigned int f = GTREGREAD(GPP_VALUE) & ~mask; | |
| GT_REG_WRITE(GPP_VALUE, f); | |
| } | |
| static inline void | |
| rtc_go_input(unsigned int mask) | |
| { | |
| unsigned int f = GTREGREAD(GPP_IO_CONTROL) & ~mask; | |
| GT_REG_WRITE(GPP_IO_CONTROL, f); | |
| } | |
| static inline void | |
| rtc_go_output(unsigned int mask) | |
| { | |
| unsigned int f = GTREGREAD(GPP_IO_CONTROL) | mask; | |
| GT_REG_WRITE(GPP_IO_CONTROL, f); | |
| } | |
| /* Access data in RTC */ | |
| static void | |
| write_byte(unsigned char b) | |
| { | |
| int i; | |
| unsigned char mask=1; | |
| for(i=0;i<8;i++) { | |
| CLOCK_LOW; /* Lower clock */ | |
| (b&mask)?DATA_HIGH:DATA_LOW; /* set data */ | |
| udelay(1); | |
| CLOCK_HIGH; /* latch data with rising clock */ | |
| udelay(1); | |
| mask=mask<<1; | |
| } | |
| } | |
| static unsigned char | |
| read_byte(void) | |
| { | |
| int i; | |
| unsigned char mask=1; | |
| unsigned char b=0; | |
| for(i=0;i<8;i++) { | |
| CLOCK_LOW; | |
| udelay(1); | |
| if (DATA_READ) b|=mask; /* if this bit is high, set in b */ | |
| CLOCK_HIGH; /* clock out next bit */ | |
| udelay(1); | |
| mask=mask<<1; | |
| } | |
| return b; | |
| } | |
| static void | |
| read_ser_drv(unsigned char addr, unsigned char *buf, int count) | |
| { | |
| int i; | |
| #ifdef RTC_DEBUG | |
| char *foo = buf; | |
| #endif | |
| DPRINTF("READ 0x%x bytes @ 0x%x [ ", count, addr); | |
| addr|=1; /* READ */ | |
| N_RESET; | |
| udelay(4); | |
| write_byte(addr); | |
| rtc_go_input(DATA); /* Put gpp pin into input mode */ | |
| udelay(1); | |
| for(i=0;i<count;i++) *(buf++)=read_byte(); | |
| RESET; | |
| rtc_go_output(DATA);/* Reset gpp for output */ | |
| udelay(4); | |
| DUMP(foo, count); | |
| } | |
| static void | |
| write_ser_drv(unsigned char addr, unsigned char *buf, int count) | |
| { | |
| int i; | |
| DPRINTF("WRITE 0x%x bytes @ 0x%x [ ", count, addr); | |
| DUMP(buf, count); | |
| addr&=~1; /* WRITE */ | |
| N_RESET; | |
| udelay(4); | |
| write_byte(addr); | |
| for(i=0;i<count;i++) write_byte(*(buf++)); | |
| RESET; | |
| udelay(4); | |
| } | |
| void | |
| rtc_init(void) | |
| { | |
| struct ds1302_st bbclk; | |
| unsigned char b; | |
| int mod; | |
| DPRINTF("init\n"); | |
| rtc_go_output(DATA|SCLK|RST); | |
| /* disable write protect */ | |
| b = 0; | |
| write_ser_drv(0x8e,&b,1); | |
| /* enable trickle */ | |
| b = 0xa5; /* 1010.0101 */ | |
| write_ser_drv(0x90,&b,1); | |
| /* read burst */ | |
| read_ser_drv(0xbe, (unsigned char *)&bbclk, 8); | |
| /* Sanity checks */ | |
| mod = 0; | |
| if (bbclk.CH) { | |
| printf("ds1302: Clock was halted, starting clock\n"); | |
| bbclk.CH=0; | |
| mod=1; | |
| } | |
| if (bbclk.fmt) { | |
| printf("ds1302: Clock was in 12 hour mode, fixing\n"); | |
| bbclk.fmt=0; | |
| mod=1; | |
| } | |
| if (bbclk.year>9) { | |
| printf("ds1302: Year was corrupted, fixing\n"); | |
| bbclk.year10=100/10; /* 2000 - why not? ;) */ | |
| bbclk.year=0; | |
| mod=1; | |
| } | |
| /* Write out the changes if needed */ | |
| if (mod) { | |
| /* enable write protect */ | |
| bbclk.WP = 1; | |
| write_ser_drv(0xbe,(unsigned char *)&bbclk,8); | |
| } else { | |
| /* Else just turn write protect on */ | |
| b = 0x80; | |
| write_ser_drv(0x8e,&b,1); | |
| } | |
| DPRINTF("init done\n"); | |
| ds1302_initted=1; | |
| } | |
| void | |
| rtc_reset(void) | |
| { | |
| if(!ds1302_initted) rtc_init(); | |
| /* TODO */ | |
| } | |
| void | |
| rtc_get(struct rtc_time *tmp) | |
| { | |
| struct ds1302_st bbclk; | |
| if(!ds1302_initted) rtc_init(); | |
| read_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* read burst */ | |
| if (bbclk.CH) { | |
| printf("ds1302: rtc_get: Clock was halted, clock probably " | |
| "corrupt\n"); | |
| } | |
| tmp->tm_sec=10*bbclk.sec10+bbclk.sec; | |
| tmp->tm_min=10*bbclk.min10+bbclk.min; | |
| tmp->tm_hour=10*bbclk.hr10+bbclk.hr; | |
| tmp->tm_wday=bbclk.day; | |
| tmp->tm_mday=10*bbclk.date10+bbclk.date; | |
| tmp->tm_mon=10*bbclk.month10+bbclk.month; | |
| tmp->tm_year=10*bbclk.year10+bbclk.year + 1900; | |
| tmp->tm_yday = 0; | |
| tmp->tm_isdst= 0; | |
| DPRINTF("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", | |
| tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, | |
| tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); | |
| } | |
| void | |
| rtc_set(struct rtc_time *tmp) | |
| { | |
| struct ds1302_st bbclk; | |
| unsigned char b=0; | |
| if(!ds1302_initted) rtc_init(); | |
| DPRINTF("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", | |
| tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, | |
| tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
| memset(&bbclk,0,sizeof(bbclk)); | |
| bbclk.CH=0; /* dont halt */ | |
| bbclk.WP=1; /* write protect when we're done */ | |
| bbclk.sec10=tmp->tm_sec/10; | |
| bbclk.sec=tmp->tm_sec%10; | |
| bbclk.min10=tmp->tm_min/10; | |
| bbclk.min=tmp->tm_min%10; | |
| bbclk.hr10=tmp->tm_hour/10; | |
| bbclk.hr=tmp->tm_hour%10; | |
| bbclk.day=tmp->tm_wday; | |
| bbclk.date10=tmp->tm_mday/10; | |
| bbclk.date=tmp->tm_mday%10; | |
| bbclk.month10=tmp->tm_mon/10; | |
| bbclk.month=tmp->tm_mon%10; | |
| tmp->tm_year -= 1900; | |
| bbclk.year10=tmp->tm_year/10; | |
| bbclk.year=tmp->tm_year%10; | |
| write_ser_drv(0x8e,&b,1); /* disable write protect */ | |
| write_ser_drv(0xbe,(unsigned char *)&bbclk, 8); /* write burst */ | |
| } | |
| #endif |