/* * Provide RTC information in /proc/driver/rtc */ static int at91_rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *p = page; int len; struct rtc_time tm; at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" "rtc_epoch\t: %04d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year, tm.tm_mon + 1, tm.tm_mday, EPOCH); at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" "alrm_date\t: %04d-%02d-%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, at91_alarm_year, tm.tm_mon + 1, tm.tm_mday); p += sprintf(p, "alarm_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ALARM) ? "yes" : "no"); p += sprintf(p, "update_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ACKUPD) ? "yes" : "no"); p += sprintf(p, "periodic_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_SECEV) ? "yes" : "no"); p += sprintf(p, "periodic_freq\t: %ld\n", (unsigned long) AT91_RTC_FREQ); len = (p - page) - off; if (len < 0) len = 0; *eof = (len <= count) ? 1 : 0; *start = page + off; return len; }
/* * Set alarm time and date in RTC */ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rtc_time tm; at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm); at91_alarm_year = tm.tm_year; tm.tm_hour = alrm->time.tm_hour; tm.tm_min = alrm->time.tm_min; tm.tm_sec = alrm->time.tm_sec; at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM); at91_sys_write(AT91_RTC_TIMALR, bin2bcd(tm.tm_sec) << 0 | bin2bcd(tm.tm_min) << 8 | bin2bcd(tm.tm_hour) << 16 | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN); at91_sys_write(AT91_RTC_CALALR, bin2bcd(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */ | bin2bcd(tm.tm_mday) << 24 | AT91_RTC_DATEEN | AT91_RTC_MTHEN); if (alrm->enabled) { at91_sys_write(AT91_RTC_SCCR, AT91_RTC_ALARM); at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM); } pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); return 0; }
/* * Set alarm time and date in RTC */ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rtc_time tm; at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm); tm.tm_mon = alrm->time.tm_mon; tm.tm_mday = alrm->time.tm_mday; tm.tm_hour = alrm->time.tm_hour; tm.tm_min = alrm->time.tm_min; tm.tm_sec = alrm->time.tm_sec; at91_rtc_write_idr(AT91_RTC_ALARM); at91_rtc_write(AT91_RTC_TIMALR, bin2bcd(tm.tm_sec) << 0 | bin2bcd(tm.tm_min) << 8 | bin2bcd(tm.tm_hour) << 16 | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN); at91_rtc_write(AT91_RTC_CALALR, bin2bcd(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */ | bin2bcd(tm.tm_mday) << 24 | AT91_RTC_DATEEN | AT91_RTC_MTHEN); if (alrm->enabled) { at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); at91_rtc_write_ier(AT91_RTC_ALARM); } dev_dbg(dev, "%s(): %ptR\n", __func__, &tm); return 0; }
/* * Read current time and date in RTC */ static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) { at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm); tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); tm->tm_year = tm->tm_year - 1900; dev_dbg(dev, "%s(): %ptR\n", __func__, tm); return 0; }
/* * Read current time and date in RTC */ static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) { at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm); tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); tm->tm_year = tm->tm_year - 1900; pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return 0; }
/* * Read alarm time and date in RTC */ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rtc_time *tm = &alrm->time; at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm); tm->tm_year = -1; alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) ? 1 : 0; dev_dbg(dev, "%s(): %ptR %sabled\n", __func__, tm, alrm->enabled ? "en" : "dis"); return 0; }
/* * Read alarm time and date in RTC */ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rtc_time *tm = &alrm->time; at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm); tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); tm->tm_year = at91_alarm_year - 1900; alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) ? 1 : 0; dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return 0; }
/* * Handle commands from user-space */ static int at91_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct rtc_time tm, tm2; int ret = 0; spin_lock_irq(&at91_rtc_lock); switch (cmd) { case RTC_AIE_OFF: /* alarm off */ AT91_SYS->RTC_IDR = AT91C_RTC_ALARM; rtc_irq_data = 0; break; case RTC_AIE_ON: /* alarm on */ AT91_SYS->RTC_IER = AT91C_RTC_ALARM; rtc_irq_data = 0; break; case RTC_UIE_OFF: /* update off */ AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; rtc_irq_data = 0; break; case RTC_UIE_ON: /* update on */ AT91_SYS->RTC_IER = AT91C_RTC_SECEV; rtc_irq_data = 0; break; case RTC_PIE_OFF: /* periodic off */ AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; rtc_irq_data = 0; break; case RTC_PIE_ON: /* periodic on */ AT91_SYS->RTC_IER = AT91C_RTC_SECEV; rtc_irq_data = 0; break; case RTC_ALM_READ: /* read alarm */ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); tm.tm_year = at91_alarm_year - 1900; ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; break; case RTC_ALM_SET: /* set alarm */ if (copy_from_user(&tm2, (struct rtc_time *) arg, sizeof(tm2))) ret = -EFAULT; else { at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); at91_alarm_year = tm.tm_year; if ((unsigned) tm2.tm_hour < 24) /* do some range checking */ tm.tm_hour = tm2.tm_hour; if ((unsigned) tm2.tm_min < 60) tm.tm_min = tm2.tm_min; if ((unsigned) tm2.tm_sec < 60) tm.tm_sec = tm2.tm_sec; AT91_SYS->RTC_TIMALR = BIN2BCD(tm.tm_sec) << 0 | BIN2BCD(tm.tm_min) << 8 | BIN2BCD(tm.tm_hour) << 16 | AT91C_RTC_HOUREN | AT91C_RTC_MINEN | AT91C_RTC_SECEN; AT91_SYS->RTC_CALALR = BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */ | BIN2BCD(tm.tm_mday) << 24 | AT91C_RTC_DATEEN | AT91C_RTC_MONTHEN; } break; case RTC_RD_TIME: /* read time */ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); tm.tm_year = tm.tm_year - 1900; ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; break; case RTC_SET_TIME: /* set time */ if (!capable(CAP_SYS_TIME)) ret = -EACCES; else { if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm))) ret = -EFAULT; else { int tm_year = tm.tm_year + 1900; if (tm_year < EPOCH || (unsigned) tm.tm_mon >= 12 || tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + (tm.tm_mon == 1 && is_leap(tm_year))) || (unsigned) tm.tm_hour >= 24 || (unsigned) tm.tm_min >= 60 || (unsigned) tm.tm_sec >= 60) ret = -EINVAL; else at91_rtc_settime(&tm); } } break; case RTC_IRQP_READ: /* read periodic alarm frequency */ ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg); break; case RTC_IRQP_SET: /* set periodic alarm frequency */ if (arg != AT91_RTC_FREQ) ret = -EINVAL; break; case RTC_EPOCH_READ: /* read epoch */ ret = put_user(EPOCH, (unsigned long *) arg); break; default: ret = -EINVAL; break; } spin_unlock_irq(&at91_rtc_lock); return ret; }