static int hym8563_set_time(struct i2c_client *client, struct rtc_time *tm) { u8 ret = 0; struct hym8563 *hym8563 = i2c_get_clientdata(client); u8 regs[HYM8563_RTC_SECTION_LEN] = { 0, }; u8 mon_day; // u8 i; pr_debug("%4d-%02d-%02d(%d) %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); mon_day = rtc_month_days((tm->tm_mon), tm->tm_year + 1900); if(tm->tm_sec >= 60 || tm->tm_sec < 0 ) //set sec regs[0x00] = bin2bcd(0x00); else regs[0x00] = bin2bcd(tm->tm_sec); if(tm->tm_min >= 60 || tm->tm_min < 0 ) //set min regs[0x01] = bin2bcd(0x00); else regs[0x01] = bin2bcd(tm->tm_min); if(tm->tm_hour >= 24 || tm->tm_hour < 0 ) //set hour regs[0x02] = bin2bcd(0x00); else regs[0x02] = bin2bcd(tm->tm_hour); if((tm->tm_mday) > mon_day) //if the input month day is bigger than the biggest day of this month, set the biggest day regs[0x03] = bin2bcd(mon_day); else if((tm->tm_mday) > 0) regs[0x03] = bin2bcd(tm->tm_mday); else if((tm->tm_mday) <= 0) regs[0x03] = bin2bcd(0x01); if( tm->tm_year >= 200) // year >= 2100 regs[0x06] = bin2bcd(99); //year = 2099 else if(tm->tm_year >= 100) // 2000 <= year < 2100 regs[0x06] = bin2bcd(tm->tm_year - 100); else if(tm->tm_year >= 0){ // 1900 <= year < 2000 regs[0x06] = bin2bcd(tm->tm_year); regs[0x05] |= 0x80; }else{ // year < 1900 regs[0x06] = bin2bcd(0); //year = 1900 regs[0x05] |= 0x80; } regs[0x04] = bin2bcd(tm->tm_wday); //set the weekday regs[0x05] = (regs[0x05] & 0x80)| (bin2bcd(tm->tm_mon + 1) & 0x7F); //set the month mutex_lock(&hym8563->mutex); // for(i=0;i<HYM8563_RTC_SECTION_LEN;i++){ // ret = hym8563_i2c_set_regs(client, RTC_SEC+i, ®s[i], 1); // } hym8563_i2c_set_regs(client, RTC_SEC, regs, HYM8563_RTC_SECTION_LEN); mutex_unlock(&hym8563->mutex); return ret; }
static int convertfromdays(u16 days, struct rtc_time *tm) { int tmp_days, year, mon; for (year = 2000;; year++) { tmp_days = rtc_year_days(1, 12, year); if (days >= tmp_days) days -= tmp_days; else { for (mon = 0;; mon++) { tmp_days = rtc_month_days(mon, year); if (days >= tmp_days) { days -= tmp_days; } else { tm->tm_year = year - 1900; tm->tm_mon = mon; tm->tm_mday = days + 1; break; } } break; } } return 0; }
/* * set the rtc chip's idea of the time. * stupidly, some callers call with year unmolested; * and some call with year = year - 1900. thanks. */ static int ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) { u8 mon, day, dow, hrs, min, sec, yrs, cen; unsigned long flags; /* * won't have to change this for a while */ if (rtc_tm->tm_year < 1900) rtc_tm->tm_year += 1900; if (rtc_tm->tm_year < 1970) return -EINVAL; yrs = rtc_tm->tm_year % 100; cen = rtc_tm->tm_year / 100; mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ day = rtc_tm->tm_mday; dow = rtc_tm->tm_wday & 0x7; /* automatic BCD */ hrs = rtc_tm->tm_hour; min = rtc_tm->tm_min; sec = rtc_tm->tm_sec; if ((mon > 12) || (day == 0)) return -EINVAL; if (day > rtc_month_days(rtc_tm->tm_mon, rtc_tm->tm_year)) return -EINVAL; if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; /* * each register is a different number of valid bits */ sec = bin2bcd(sec) & 0x7f; min = bin2bcd(min) & 0x7f; hrs = bin2bcd(hrs) & 0x3f; day = bin2bcd(day) & 0x3f; mon = bin2bcd(mon) & 0x1f; yrs = bin2bcd(yrs) & 0xff; cen = bin2bcd(cen) & 0xff; spin_lock_irqsave(&ds1511_lock, flags); rtc_disable_update(); rtc_write(cen, RTC_CENTURY); rtc_write(yrs, RTC_YEAR); rtc_write((rtc_read(RTC_MON) & 0xe0) | mon, RTC_MON); rtc_write(day, RTC_DOM); rtc_write(hrs, RTC_HOUR); rtc_write(min, RTC_MIN); rtc_write(sec, RTC_SEC); rtc_write(dow, RTC_DOW); rtc_enable_update(); spin_unlock_irqrestore(&ds1511_lock, flags); return 0; }
static int tps6591x_rtc_valid_tm(struct rtc_time *tm) { if (tm->tm_year >= (RTC_YEAR_OFFSET + 99) || tm->tm_mon >= 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + OS_REF_YEAR) || tm->tm_hour >= 24 || tm->tm_min >= 60 || tm->tm_sec >= 60){ printk("tps6591x_rtc_valid_tm: rtc time not valid %u %u %u %u %u %u\n",tm->tm_year >= (RTC_YEAR_OFFSET + 99), tm->tm_mon >= 12, tm->tm_mday < 1, tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + OS_REF_YEAR), tm->tm_hour >= 24, tm->tm_min >= 60, tm->tm_sec >= 60); return -EINVAL; } return 0; }
static int rtc_time_is_valid(struct rtc_time_t * time) { if((!time) || (time->year < 1970) || ((time->month) > 12) || (time->day < 1) || (time->day > rtc_month_days(time->year, time->month)) || ((time->hour) >= 24) || ((time->minute) >= 60) || ((time->second) >= 60)) return 0; return 1; }
/* * Does the rtc_time represent a valid date/time? */ int rtc_valid_tm(struct rtc_time *tm) { if (((unsigned)tm->tm_mon) >= 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) || ((unsigned)tm->tm_hour) >= 24 || ((unsigned)tm->tm_min) >= 60 || ((unsigned)tm->tm_sec) >= 60) return -EINVAL; return 0; }
/* * does the time represent a valid date/time? */ bool_t rtc_valid_time(struct time * tm) { if (tm->year < 1970 || (tm->mon) > 12 || tm->day < 1 || tm->day > rtc_month_days(tm->year, tm->mon) || (tm->hour) >= 24 || (tm->min) >= 60 || (tm->sec) >= 60) return FALSE; return TRUE; }
static bool_t rtc_valid_time(struct rtc_time_t * rt) { if (rt->year < 1970 || (rt->mon) > 12 || rt->day < 1 || rt->day > rtc_month_days(rt->year, rt->mon) || (rt->hour) >= 24 || (rt->min) >= 60 || (rt->sec) >= 60) return FALSE; return TRUE; }
static int tps6591x_rtc_valid_tm(struct rtc_time *tm) { if (tm->tm_year >= (RTC_YEAR_OFFSET + 99) || tm->tm_year < (RTC_YEAR_OFFSET) || tm->tm_mon >= 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + OS_REF_YEAR) || tm->tm_hour >= 24 || tm->tm_min >= 60 || tm->tm_sec >= 60) return -EINVAL; return 0; }
/* * Does the rtc_time represent a valid date/time? */ int rtc_valid_tm(struct rtc_time *tm) { if (tm->tm_year < 70 || tm->tm_mon >= 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) || tm->tm_hour >= 24 || tm->tm_min >= 60 || tm->tm_sec >= 60) return -EINVAL; return 0; }
static int ricoh583_rtc_valid_tm(struct device *dev, struct rtc_time *tm) { if (tm->tm_year >= (rtc_year_offset + 99) || tm->tm_mon > 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + os_ref_year) || tm->tm_hour >= 24 || tm->tm_min >= 60 || tm->tm_sec >= 60) { dev_err(dev->parent, "\n returning error due to time" "%d/%d/%d %d:%d:%d", tm->tm_mon, tm->tm_mday, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); return -EINVAL; } return 0; }
/* * Does the rtc_time represent a valid date/time? */ int rtc_valid_tm(struct rtc_time *tm) { if (tm->tm_year < 70 || ((unsigned)tm->tm_mon) >= 12 || tm->tm_mday < 1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) || ((unsigned)tm->tm_hour) >= 24 || ((unsigned)tm->tm_min) >= 60 || ((unsigned)tm->tm_sec) >= 60){ pr_err("error: invalid rtc_time" " tm_year=%d,tm_mon=%d,tm_mday=%d,tm_hour=%d,tm_min=%d,tm_sec=%d\n", tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); return -EINVAL; } return 0; }
/* * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. */ static void rtc_time_to_tm(u32 time, struct rtc_time *tm) { unsigned int month, year; int days; tm->tm_value = time; days = (int)(time / 86400); time -= (unsigned int) days * 86400; /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; year = (unsigned int)(1970 + days / 365); days -= (s32)((year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1)); if (days < 0) { year -= 1; days += 365 + is_leap_year(year); } tm->tm_year = (int)year; tm->tm_yday = days + 1; for (month = 0; month < 11; month++) { int newdays; newdays = days - rtc_month_days(month, year); if (newdays < 0) break; days = newdays; } tm->tm_mon = (int)(month) + 1; tm->tm_mday = days + 1; tm->tm_hour = (int)(time / 3600); time -= (u32)(tm->tm_hour * 3600); tm->tm_min = (int)(time / 60); tm->tm_sec = (int)((int)time - tm->tm_min * 60); tm->tm_isdst = 0; }
/* * rtc_time64_to_tm - Converts time64_t to rtc_time. * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. */ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) { unsigned int month, year, secs; int days; /* time must be positive */ days = div_s64_rem(time, 86400, &secs); /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; year = 1970 + days / 365; days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); while (days < 0) { year -= 1; days += 365 + is_leap_year(year); } tm->tm_year = year - 1900; tm->tm_yday = days + 1; for (month = 0; month < 11; month++) { int newdays; newdays = days - rtc_month_days(month, year); if (newdays < 0) break; days = newdays; } tm->tm_mon = month; tm->tm_mday = days + 1; tm->tm_hour = secs / 3600; secs -= tm->tm_hour * 3600; tm->tm_min = secs / 60; tm->tm_sec = secs - tm->tm_min * 60; tm->tm_isdst = 0; }
void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) { unsigned int month, year; int days; days = time / 86400; time -= (unsigned int) days * 86400; tm->tm_wday = (days + 4) % 7; year = 1970 + days / 365; days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); if (days < 0) { year -= 1; days += 365 + is_leap_year(year); } tm->tm_year = year - 1900; tm->tm_yday = days + 1; for (month = 0; month < 11; month++) { int newdays; newdays = days - rtc_month_days(month, year); if (newdays < 0) break; days = newdays; } tm->tm_mon = month; tm->tm_mday = days + 1; tm->tm_hour = time / 3600; time -= tm->tm_hour * 3600; tm->tm_min = time / 60; tm->tm_sec = time - tm->tm_min * 60; tm->tm_isdst = 0; }
/* * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. * Added support for other years than 1970 * needed for example for Palm PDAs */ void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) { register int days, month, year; days = time / 86400; time -= days * 86400; /* day of the week, 1970-01-01 was a Thursday */ /* and between 1970-01-01 and EPOCH_YEAR was EPOCH_YEAR_DAYS_DIFFERENCE days */ tm->tm_wday = (days + EPOCH_YEAR_DAYS_DIFFERENCE + 4) % 7; year = EPOCH_YEAR + days / 365; days -= (year - EPOCH_YEAR) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(EPOCH_YEAR - 1); if (days < 0) { year -= 1; days += 365 + LEAP_YEAR(year); } tm->tm_year = year - 1900; tm->tm_yday = days + 1; for (month = 0; month < 11; month++) { int newdays; newdays = days - rtc_month_days(month, year); if (newdays < 0) break; days = newdays; } tm->tm_mon = month; tm->tm_mday = days + 1; tm->tm_hour = time / 3600; time -= tm->tm_hour * 3600; tm->tm_min = time / 60; tm->tm_sec = time - tm->tm_min * 60; }
/* * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. * For PMU59040, 00 in RTCY register means year 2000. */ static void rtc59040_utc_to_tm(unsigned long time, struct rtc_time *tm) { unsigned int month, year; int days; days = time / 86400; //1 day = 86400s time -= (unsigned int) days * 86400; /* day of the week, 1970-01-01 was a Thursday */ tm->tm_wday = (days + 4) % 7; year = 1970 + days / 365; days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); if (days < 0) { year -= 1; days += 365 + LEAP_YEAR(year); } tm->tm_year = year - 2000; tm->tm_yday = days + 1; for (month = 0; month < 11; month++) { int newdays; newdays = days - rtc_month_days(month, year); if (newdays < 0) break; days = newdays; } tm->tm_mon = month + 1; tm->tm_mday = days + 1; tm->tm_hour = time / 3600; time -= tm->tm_hour * 3600; tm->tm_min = time / 60; tm->tm_sec = time - tm->tm_min * 60; }
/* * convert seconds since 01-01-1970 00:00:00 to gregorian date. */ void rtc_to_time(unsigned long time, struct time *tm) { u32_t month, year; s32_t days; s32_t newdays; days = time / 86400; time -= (u32_t) days * 86400; /* day of the week, 1970-01-01 was a thursday */ tm->week = (days + 4) % 7; year = 1970 + days / 365; days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); if(days < 0) { year -= 1; days += 365 + LEAP_YEAR(year); } tm->year = year; tm->day = days + 1; for(month = 1; month < 12; month++) { newdays = days - rtc_month_days(year, month); if(newdays < 0) break; days = newdays; } tm->mon = month; tm->day = days + 1; tm->hour = time / 3600; time -= tm->hour * 3600; tm->min = time / 60; tm->sec = time - tm->min * 60; }
static void rtc_to_time(unsigned long time, struct rtc_time_t * rt) { u32_t month, year; s32_t days; s32_t newdays; days = time / 86400; time -= (u32_t) days * 86400; rt->week = (days + 4) % 7; year = 1970 + days / 365; days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); if(days < 0) { year -= 1; days += 365 + LEAP_YEAR(year); } rt->year = year; rt->day = days + 1; for(month = 1; month < 12; month++) { newdays = days - rtc_month_days(year, month); if(newdays < 0) break; days = newdays; } rt->mon = month; rt->day = days + 1; rt->hour = time / 3600; time -= rt->hour * 3600; rt->min = time / 60; rt->sec = time - rt->min * 60; }
static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct i2c_client *client = to_i2c_client(dev); struct hym8563 *hym8563 = i2c_get_clientdata(client); struct rtc_time now, *tm = &alarm->time; u8 regs[4] = { 0, }; u8 mon_day; unsigned long alarm_sec, now_sec; int diff_sec = 0; pr_debug("%4d-%02d-%02d(%d) %02d:%02d:%02d enabled %d\n", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec, alarm->enabled); hym8563_read_datetime(client, &now); mutex_lock(&hym8563->mutex); rtc_tm_to_time(tm, &alarm_sec); rtc_tm_to_time(&now, &now_sec); diff_sec = alarm_sec - now_sec; if((diff_sec > 0) && (diff_sec < 256)) { printk("%s:diff_sec= %ds , use time\n",__func__, diff_sec); if (alarm->enabled == 1) { hym8563_set_count(client, diff_sec); hym8563_enable_count(client, 1); } else { hym8563_enable_count(client, 0); } } else { printk("%s:diff_sec= %ds , use alarm\n",__func__, diff_sec); hym8563_enable_count(client, 0); if(tm->tm_sec > 0) { rtc_tm_to_time(tm, &alarm_sec); rtc_time_to_tm(alarm_sec, tm); } hym8563->alarm = *alarm; regs[0] = 0x0; hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); mon_day = rtc_month_days(tm->tm_mon, tm->tm_year + 1900); hym8563_i2c_read_regs(client, RTC_A_MIN, regs, 4); if (tm->tm_min >= 60 || tm->tm_min < 0) //set min regs[0x00] = bin2bcd(0x00) & 0x7f; else regs[0x00] = bin2bcd(tm->tm_min) & 0x7f; if (tm->tm_hour >= 24 || tm->tm_hour < 0) //set hour regs[0x01] = bin2bcd(0x00) & 0x7f; else regs[0x01] = bin2bcd(tm->tm_hour) & 0x7f; regs[0x03] = bin2bcd (tm->tm_wday) & 0x7f; /* if the input month day is bigger than the biggest day of this month, set the biggest day */ if (tm->tm_mday > mon_day) regs[0x02] = bin2bcd(mon_day) & 0x7f; else if (tm->tm_mday > 0) regs[0x02] = bin2bcd(tm->tm_mday) & 0x7f; else if (tm->tm_mday <= 0) regs[0x02] = bin2bcd(0x01) & 0x7f; hym8563_i2c_set_regs(client, RTC_A_MIN, regs, 4); hym8563_i2c_read_regs(client, RTC_A_MIN, regs, 4); hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); if (alarm->enabled == 1) regs[0] |= AIE; else regs[0] &= 0x0; hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); if(diff_sec <= 0) { pr_info("alarm sec <= now sec\n"); } } mutex_unlock(&hym8563->mutex); return 0; }
/** * ds1685_rtc_set_time - sets the time registers. * @dev: pointer to device structure. * @tm: pointer to rtc_time structure. */ static int ds1685_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct platform_device *pdev = to_platform_device(dev); struct ds1685_priv *rtc = platform_get_drvdata(pdev); u8 ctrlb, seconds, minutes, hours, wday, mday, month, years, century; /* Fetch the time info from rtc_time. */ seconds = ds1685_rtc_bin2bcd(rtc, tm->tm_sec, RTC_SECS_BIN_MASK, RTC_SECS_BCD_MASK); minutes = ds1685_rtc_bin2bcd(rtc, tm->tm_min, RTC_MINS_BIN_MASK, RTC_MINS_BCD_MASK); hours = ds1685_rtc_bin2bcd(rtc, tm->tm_hour, RTC_HRS_24_BIN_MASK, RTC_HRS_24_BCD_MASK); wday = ds1685_rtc_bin2bcd(rtc, (tm->tm_wday + 1), RTC_WDAY_MASK, RTC_WDAY_MASK); mday = ds1685_rtc_bin2bcd(rtc, tm->tm_mday, RTC_MDAY_BIN_MASK, RTC_MDAY_BCD_MASK); month = ds1685_rtc_bin2bcd(rtc, (tm->tm_mon + 1), RTC_MONTH_BIN_MASK, RTC_MONTH_BCD_MASK); years = ds1685_rtc_bin2bcd(rtc, (tm->tm_year % 100), RTC_YEAR_BIN_MASK, RTC_YEAR_BCD_MASK); century = ds1685_rtc_bin2bcd(rtc, ((tm->tm_year + 1900) / 100), RTC_CENTURY_MASK, RTC_CENTURY_MASK); /* * Perform Sanity Checks: * - Months: !> 12, Month Day != 0. * - Month Day !> Max days in current month. * - Hours !>= 24, Mins !>= 60, Secs !>= 60, & Weekday !> 7. */ if ((tm->tm_mon > 11) || (mday == 0)) return -EDOM; if (tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year)) return -EDOM; if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60) || (wday > 7)) return -EDOM; /* * Set the data mode to use and store the time values in the * RTC registers. */ ds1685_rtc_begin_data_access(rtc); ctrlb = rtc->read(rtc, RTC_CTRL_B); if (rtc->bcd_mode) ctrlb &= ~(RTC_CTRL_B_DM); else ctrlb |= RTC_CTRL_B_DM; rtc->write(rtc, RTC_CTRL_B, ctrlb); rtc->write(rtc, RTC_SECS, seconds); rtc->write(rtc, RTC_MINS, minutes); rtc->write(rtc, RTC_HRS, hours); rtc->write(rtc, RTC_WDAY, wday); rtc->write(rtc, RTC_MDAY, mday); rtc->write(rtc, RTC_MONTH, month); rtc->write(rtc, RTC_YEAR, years); rtc->write(rtc, RTC_CENTURY, century); ds1685_rtc_end_data_access(rtc); return 0; }
int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { int err; struct rtc_time before, now; int first_time = 1; time64_t t_now, t_alm; enum { none, day, month, year } missing = none; unsigned days; /* The lower level RTC driver may return -1 in some fields, * creating invalid alarm->time values, for reasons like: * * - The hardware may not be capable of filling them in; * many alarms match only on time-of-day fields, not * day/month/year calendar data. * * - Some hardware uses illegal values as "wildcard" match * values, which non-Linux firmware (like a BIOS) may try * to set up as e.g. "alarm 15 minutes after each hour". * Linux uses only oneshot alarms. * * When we see that here, we deal with it by using values from * a current RTC timestamp for any missing (-1) values. The * RTC driver prevents "periodic alarm" modes. * * But this can be racey, because some fields of the RTC timestamp * may have wrapped in the interval since we read the RTC alarm, * which would lead to us inserting inconsistent values in place * of the -1 fields. * * Reading the alarm and timestamp in the reverse sequence * would have the same race condition, and not solve the issue. * * So, we must first read the RTC timestamp, * then read the RTC alarm value, * and then read a second RTC timestamp. * * If any fields of the second timestamp have changed * when compared with the first timestamp, then we know * our timestamp may be inconsistent with that used by * the low-level rtc_read_alarm_internal() function. * * So, when the two timestamps disagree, we just loop and do * the process again to get a fully consistent set of values. * * This could all instead be done in the lower level driver, * but since more than one lower level RTC implementation needs it, * then it's probably best best to do it here instead of there.. */ /* Get the "before" timestamp */ err = rtc_read_time(rtc, &before); if (err < 0) return err; do { if (!first_time) memcpy(&before, &now, sizeof(struct rtc_time)); first_time = 0; /* get the RTC alarm values, which may be incomplete */ err = rtc_read_alarm_internal(rtc, alarm); if (err) return err; /* full-function RTCs won't have such missing fields */ if (rtc_valid_tm(&alarm->time) == 0) { rtc_add_offset(rtc, &alarm->time); return 0; } /* get the "after" timestamp, to detect wrapped fields */ err = rtc_read_time(rtc, &now); if (err < 0) return err; /* note that tm_sec is a "don't care" value here: */ } while ( before.tm_min != now.tm_min || before.tm_hour != now.tm_hour || before.tm_mon != now.tm_mon || before.tm_year != now.tm_year); /* Fill in the missing alarm fields using the timestamp; we * know there's at least one since alarm->time is invalid. */ if (alarm->time.tm_sec == -1) alarm->time.tm_sec = now.tm_sec; if (alarm->time.tm_min == -1) alarm->time.tm_min = now.tm_min; if (alarm->time.tm_hour == -1) alarm->time.tm_hour = now.tm_hour; /* For simplicity, only support date rollover for now */ if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) { alarm->time.tm_mday = now.tm_mday; missing = day; } if ((unsigned)alarm->time.tm_mon >= 12) { alarm->time.tm_mon = now.tm_mon; if (missing == none) missing = month; } if (alarm->time.tm_year == -1) { alarm->time.tm_year = now.tm_year; if (missing == none) missing = year; } /* Can't proceed if alarm is still invalid after replacing * missing fields. */ err = rtc_valid_tm(&alarm->time); if (err) goto done; /* with luck, no rollover is needed */ t_now = rtc_tm_to_time64(&now); t_alm = rtc_tm_to_time64(&alarm->time); if (t_now < t_alm) goto done; switch (missing) { /* 24 hour rollover ... if it's now 10am Monday, an alarm that * that will trigger at 5am will do so at 5am Tuesday, which * could also be in the next month or year. This is a common * case, especially for PCs. */ case day: dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); t_alm += 24 * 60 * 60; rtc_time64_to_tm(t_alm, &alarm->time); break; /* Month rollover ... if it's the 31th, an alarm on the 3rd will * be next month. An alarm matching on the 30th, 29th, or 28th * may end up in the month after that! Many newer PCs support * this type of alarm. */ case month: dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); do { if (alarm->time.tm_mon < 11) alarm->time.tm_mon++; else { alarm->time.tm_mon = 0; alarm->time.tm_year++; } days = rtc_month_days(alarm->time.tm_mon, alarm->time.tm_year); } while (days < alarm->time.tm_mday); break; /* Year rollover ... easy except for leap years! */ case year: dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); do { alarm->time.tm_year++; } while (!is_leap_year(alarm->time.tm_year + 1900) && rtc_valid_tm(&alarm->time) != 0); break; default: dev_warn(&rtc->dev, "alarm rollover not handled\n"); } err = rtc_valid_tm(&alarm->time); done: if (err) { dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n", alarm->time.tm_year + 1900, alarm->time.tm_mon + 1, alarm->time.tm_mday, alarm->time.tm_hour, alarm->time.tm_min, alarm->time.tm_sec); } return err; }
static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct i2c_client *client = to_i2c_client(dev); struct hym8563 *hym8563 = i2c_get_clientdata(client); struct rtc_time now, *tm = &alarm->time; u8 regs[4] = { 0, }; u8 mon_day; pr_debug("%4d-%02d-%02d(%d) %02d:%02d:%02d enabled %d\n", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec, alarm->enabled); hym8563_read_datetime(client, &now); if (alarm->enabled && now.tm_year == tm->tm_year && now.tm_mon == tm->tm_mon && now.tm_mday == tm->tm_mday && now.tm_hour == tm->tm_hour && now.tm_min == tm->tm_min && tm->tm_sec > now.tm_sec) { long timeout = tm->tm_sec - now.tm_sec + 1; pr_info("stay awake %lds\n", timeout); wake_lock_timeout(&hym8563->wake_lock, timeout * HZ); } mutex_lock(&hym8563->mutex); hym8563->alarm = *alarm; regs[0] = 0x0; hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); mon_day = rtc_month_days(tm->tm_mon, tm->tm_year + 1900); hym8563_i2c_read_regs(client, RTC_A_MIN, regs, 4); if (tm->tm_min >= 60 || tm->tm_min < 0) //set min regs[0x00] = bin2bcd(0x00) & 0x7f; else regs[0x00] = bin2bcd(tm->tm_min) & 0x7f; if (tm->tm_hour >= 24 || tm->tm_hour < 0) //set hour regs[0x01] = bin2bcd(0x00) & 0x7f; else regs[0x01] = bin2bcd(tm->tm_hour) & 0x7f; regs[0x03] = bin2bcd (tm->tm_wday) & 0x7f; /* if the input month day is bigger than the biggest day of this month, set the biggest day */ if (tm->tm_mday > mon_day) regs[0x02] = bin2bcd(mon_day) & 0x7f; else if (tm->tm_mday > 0) regs[0x02] = bin2bcd(tm->tm_mday) & 0x7f; else if (tm->tm_mday <= 0) regs[0x02] = bin2bcd(0x01) & 0x7f; hym8563_i2c_set_regs(client, RTC_A_MIN, regs, 4); hym8563_i2c_read_regs(client, RTC_A_MIN, regs, 4); hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); if (alarm->enabled == 1) regs[0] |= AIE; else regs[0] &= 0x0; hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); mutex_unlock(&hym8563->mutex); return 0; }