1*5f119f29SThomas Bogendoerfer /* 2*5f119f29SThomas Bogendoerfer * DS1286 Real Time Clock interface for Linux 3*5f119f29SThomas Bogendoerfer * 4*5f119f29SThomas Bogendoerfer * Copyright (C) 1998, 1999, 2000 Ralf Baechle 5*5f119f29SThomas Bogendoerfer * Copyright (C) 2008 Thomas Bogendoerfer 6*5f119f29SThomas Bogendoerfer * 7*5f119f29SThomas Bogendoerfer * Based on code written by Paul Gortmaker. 8*5f119f29SThomas Bogendoerfer * 9*5f119f29SThomas Bogendoerfer * This program is free software; you can redistribute it and/or modify it 10*5f119f29SThomas Bogendoerfer * under the terms of the GNU General Public License as published by the 11*5f119f29SThomas Bogendoerfer * Free Software Foundation; either version 2 of the License, or (at your 12*5f119f29SThomas Bogendoerfer * option) any later version. 13*5f119f29SThomas Bogendoerfer */ 14*5f119f29SThomas Bogendoerfer 15*5f119f29SThomas Bogendoerfer #include <linux/module.h> 16*5f119f29SThomas Bogendoerfer #include <linux/rtc.h> 17*5f119f29SThomas Bogendoerfer #include <linux/platform_device.h> 18*5f119f29SThomas Bogendoerfer #include <linux/bcd.h> 19*5f119f29SThomas Bogendoerfer #include <linux/ds1286.h> 20*5f119f29SThomas Bogendoerfer 21*5f119f29SThomas Bogendoerfer #define DRV_VERSION "1.0" 22*5f119f29SThomas Bogendoerfer 23*5f119f29SThomas Bogendoerfer struct ds1286_priv { 24*5f119f29SThomas Bogendoerfer struct rtc_device *rtc; 25*5f119f29SThomas Bogendoerfer u32 __iomem *rtcregs; 26*5f119f29SThomas Bogendoerfer size_t size; 27*5f119f29SThomas Bogendoerfer unsigned long baseaddr; 28*5f119f29SThomas Bogendoerfer spinlock_t lock; 29*5f119f29SThomas Bogendoerfer }; 30*5f119f29SThomas Bogendoerfer 31*5f119f29SThomas Bogendoerfer static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg) 32*5f119f29SThomas Bogendoerfer { 33*5f119f29SThomas Bogendoerfer return __raw_readl(&priv->rtcregs[reg]) & 0xff; 34*5f119f29SThomas Bogendoerfer } 35*5f119f29SThomas Bogendoerfer 36*5f119f29SThomas Bogendoerfer static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg) 37*5f119f29SThomas Bogendoerfer { 38*5f119f29SThomas Bogendoerfer __raw_writel(data, &priv->rtcregs[reg]); 39*5f119f29SThomas Bogendoerfer } 40*5f119f29SThomas Bogendoerfer 41*5f119f29SThomas Bogendoerfer #ifdef CONFIG_RTC_INTF_DEV 42*5f119f29SThomas Bogendoerfer 43*5f119f29SThomas Bogendoerfer static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 44*5f119f29SThomas Bogendoerfer { 45*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 46*5f119f29SThomas Bogendoerfer unsigned long flags; 47*5f119f29SThomas Bogendoerfer unsigned char val; 48*5f119f29SThomas Bogendoerfer 49*5f119f29SThomas Bogendoerfer switch (cmd) { 50*5f119f29SThomas Bogendoerfer case RTC_AIE_OFF: 51*5f119f29SThomas Bogendoerfer /* Mask alarm int. enab. bit */ 52*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 53*5f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 54*5f119f29SThomas Bogendoerfer val |= RTC_TDM; 55*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 56*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 57*5f119f29SThomas Bogendoerfer break; 58*5f119f29SThomas Bogendoerfer case RTC_AIE_ON: 59*5f119f29SThomas Bogendoerfer /* Allow alarm interrupts. */ 60*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 61*5f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 62*5f119f29SThomas Bogendoerfer val &= ~RTC_TDM; 63*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 64*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 65*5f119f29SThomas Bogendoerfer break; 66*5f119f29SThomas Bogendoerfer case RTC_WIE_OFF: 67*5f119f29SThomas Bogendoerfer /* Mask watchdog int. enab. bit */ 68*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 69*5f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 70*5f119f29SThomas Bogendoerfer val |= RTC_WAM; 71*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 72*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 73*5f119f29SThomas Bogendoerfer break; 74*5f119f29SThomas Bogendoerfer case RTC_WIE_ON: 75*5f119f29SThomas Bogendoerfer /* Allow watchdog interrupts. */ 76*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 77*5f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 78*5f119f29SThomas Bogendoerfer val &= ~RTC_WAM; 79*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 80*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 81*5f119f29SThomas Bogendoerfer break; 82*5f119f29SThomas Bogendoerfer default: 83*5f119f29SThomas Bogendoerfer return -ENOIOCTLCMD; 84*5f119f29SThomas Bogendoerfer } 85*5f119f29SThomas Bogendoerfer return 0; 86*5f119f29SThomas Bogendoerfer } 87*5f119f29SThomas Bogendoerfer 88*5f119f29SThomas Bogendoerfer #else 89*5f119f29SThomas Bogendoerfer #define ds1286_ioctl NULL 90*5f119f29SThomas Bogendoerfer #endif 91*5f119f29SThomas Bogendoerfer 92*5f119f29SThomas Bogendoerfer #ifdef CONFIG_PROC_FS 93*5f119f29SThomas Bogendoerfer 94*5f119f29SThomas Bogendoerfer static int ds1286_proc(struct device *dev, struct seq_file *seq) 95*5f119f29SThomas Bogendoerfer { 96*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 97*5f119f29SThomas Bogendoerfer unsigned char month, cmd, amode; 98*5f119f29SThomas Bogendoerfer const char *s; 99*5f119f29SThomas Bogendoerfer 100*5f119f29SThomas Bogendoerfer month = ds1286_rtc_read(priv, RTC_MONTH); 101*5f119f29SThomas Bogendoerfer seq_printf(seq, 102*5f119f29SThomas Bogendoerfer "oscillator\t: %s\n" 103*5f119f29SThomas Bogendoerfer "square_wave\t: %s\n", 104*5f119f29SThomas Bogendoerfer (month & RTC_EOSC) ? "disabled" : "enabled", 105*5f119f29SThomas Bogendoerfer (month & RTC_ESQW) ? "disabled" : "enabled"); 106*5f119f29SThomas Bogendoerfer 107*5f119f29SThomas Bogendoerfer amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) | 108*5f119f29SThomas Bogendoerfer ((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) | 109*5f119f29SThomas Bogendoerfer ((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7); 110*5f119f29SThomas Bogendoerfer switch (amode) { 111*5f119f29SThomas Bogendoerfer case 7: 112*5f119f29SThomas Bogendoerfer s = "each minute"; 113*5f119f29SThomas Bogendoerfer break; 114*5f119f29SThomas Bogendoerfer case 3: 115*5f119f29SThomas Bogendoerfer s = "minutes match"; 116*5f119f29SThomas Bogendoerfer break; 117*5f119f29SThomas Bogendoerfer case 1: 118*5f119f29SThomas Bogendoerfer s = "hours and minutes match"; 119*5f119f29SThomas Bogendoerfer break; 120*5f119f29SThomas Bogendoerfer case 0: 121*5f119f29SThomas Bogendoerfer s = "days, hours and minutes match"; 122*5f119f29SThomas Bogendoerfer break; 123*5f119f29SThomas Bogendoerfer default: 124*5f119f29SThomas Bogendoerfer s = "invalid"; 125*5f119f29SThomas Bogendoerfer break; 126*5f119f29SThomas Bogendoerfer } 127*5f119f29SThomas Bogendoerfer seq_printf(seq, "alarm_mode\t: %s\n", s); 128*5f119f29SThomas Bogendoerfer 129*5f119f29SThomas Bogendoerfer cmd = ds1286_rtc_read(priv, RTC_CMD); 130*5f119f29SThomas Bogendoerfer seq_printf(seq, 131*5f119f29SThomas Bogendoerfer "alarm_enable\t: %s\n" 132*5f119f29SThomas Bogendoerfer "wdog_alarm\t: %s\n" 133*5f119f29SThomas Bogendoerfer "alarm_mask\t: %s\n" 134*5f119f29SThomas Bogendoerfer "wdog_alarm_mask\t: %s\n" 135*5f119f29SThomas Bogendoerfer "interrupt_mode\t: %s\n" 136*5f119f29SThomas Bogendoerfer "INTB_mode\t: %s_active\n" 137*5f119f29SThomas Bogendoerfer "interrupt_pins\t: %s\n", 138*5f119f29SThomas Bogendoerfer (cmd & RTC_TDF) ? "yes" : "no", 139*5f119f29SThomas Bogendoerfer (cmd & RTC_WAF) ? "yes" : "no", 140*5f119f29SThomas Bogendoerfer (cmd & RTC_TDM) ? "disabled" : "enabled", 141*5f119f29SThomas Bogendoerfer (cmd & RTC_WAM) ? "disabled" : "enabled", 142*5f119f29SThomas Bogendoerfer (cmd & RTC_PU_LVL) ? "pulse" : "level", 143*5f119f29SThomas Bogendoerfer (cmd & RTC_IBH_LO) ? "low" : "high", 144*5f119f29SThomas Bogendoerfer (cmd & RTC_IPSW) ? "unswapped" : "swapped"); 145*5f119f29SThomas Bogendoerfer return 0; 146*5f119f29SThomas Bogendoerfer } 147*5f119f29SThomas Bogendoerfer 148*5f119f29SThomas Bogendoerfer #else 149*5f119f29SThomas Bogendoerfer #define ds1286_proc NULL 150*5f119f29SThomas Bogendoerfer #endif 151*5f119f29SThomas Bogendoerfer 152*5f119f29SThomas Bogendoerfer static int ds1286_read_time(struct device *dev, struct rtc_time *tm) 153*5f119f29SThomas Bogendoerfer { 154*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 155*5f119f29SThomas Bogendoerfer unsigned char save_control; 156*5f119f29SThomas Bogendoerfer unsigned long flags; 157*5f119f29SThomas Bogendoerfer unsigned long uip_watchdog = jiffies; 158*5f119f29SThomas Bogendoerfer 159*5f119f29SThomas Bogendoerfer /* 160*5f119f29SThomas Bogendoerfer * read RTC once any update in progress is done. The update 161*5f119f29SThomas Bogendoerfer * can take just over 2ms. We wait 10 to 20ms. There is no need to 162*5f119f29SThomas Bogendoerfer * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. 163*5f119f29SThomas Bogendoerfer * If you need to know *exactly* when a second has started, enable 164*5f119f29SThomas Bogendoerfer * periodic update complete interrupts, (via ioctl) and then 165*5f119f29SThomas Bogendoerfer * immediately read /dev/rtc which will block until you get the IRQ. 166*5f119f29SThomas Bogendoerfer * Once the read clears, read the RTC time (again via ioctl). Easy. 167*5f119f29SThomas Bogendoerfer */ 168*5f119f29SThomas Bogendoerfer 169*5f119f29SThomas Bogendoerfer if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE) 170*5f119f29SThomas Bogendoerfer while (time_before(jiffies, uip_watchdog + 2*HZ/100)) 171*5f119f29SThomas Bogendoerfer barrier(); 172*5f119f29SThomas Bogendoerfer 173*5f119f29SThomas Bogendoerfer /* 174*5f119f29SThomas Bogendoerfer * Only the values that we read from the RTC are set. We leave 175*5f119f29SThomas Bogendoerfer * tm_wday, tm_yday and tm_isdst untouched. Even though the 176*5f119f29SThomas Bogendoerfer * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated 177*5f119f29SThomas Bogendoerfer * by the RTC when initially set to a non-zero value. 178*5f119f29SThomas Bogendoerfer */ 179*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 180*5f119f29SThomas Bogendoerfer save_control = ds1286_rtc_read(priv, RTC_CMD); 181*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); 182*5f119f29SThomas Bogendoerfer 183*5f119f29SThomas Bogendoerfer tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS); 184*5f119f29SThomas Bogendoerfer tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES); 185*5f119f29SThomas Bogendoerfer tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f; 186*5f119f29SThomas Bogendoerfer tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE); 187*5f119f29SThomas Bogendoerfer tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f; 188*5f119f29SThomas Bogendoerfer tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR); 189*5f119f29SThomas Bogendoerfer 190*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, save_control, RTC_CMD); 191*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 192*5f119f29SThomas Bogendoerfer 193*5f119f29SThomas Bogendoerfer tm->tm_sec = bcd2bin(tm->tm_sec); 194*5f119f29SThomas Bogendoerfer tm->tm_min = bcd2bin(tm->tm_min); 195*5f119f29SThomas Bogendoerfer tm->tm_hour = bcd2bin(tm->tm_hour); 196*5f119f29SThomas Bogendoerfer tm->tm_mday = bcd2bin(tm->tm_mday); 197*5f119f29SThomas Bogendoerfer tm->tm_mon = bcd2bin(tm->tm_mon); 198*5f119f29SThomas Bogendoerfer tm->tm_year = bcd2bin(tm->tm_year); 199*5f119f29SThomas Bogendoerfer 200*5f119f29SThomas Bogendoerfer /* 201*5f119f29SThomas Bogendoerfer * Account for differences between how the RTC uses the values 202*5f119f29SThomas Bogendoerfer * and how they are defined in a struct rtc_time; 203*5f119f29SThomas Bogendoerfer */ 204*5f119f29SThomas Bogendoerfer if (tm->tm_year < 45) 205*5f119f29SThomas Bogendoerfer tm->tm_year += 30; 206*5f119f29SThomas Bogendoerfer tm->tm_year += 40; 207*5f119f29SThomas Bogendoerfer if (tm->tm_year < 70) 208*5f119f29SThomas Bogendoerfer tm->tm_year += 100; 209*5f119f29SThomas Bogendoerfer 210*5f119f29SThomas Bogendoerfer tm->tm_mon--; 211*5f119f29SThomas Bogendoerfer 212*5f119f29SThomas Bogendoerfer return rtc_valid_tm(tm); 213*5f119f29SThomas Bogendoerfer } 214*5f119f29SThomas Bogendoerfer 215*5f119f29SThomas Bogendoerfer static int ds1286_set_time(struct device *dev, struct rtc_time *tm) 216*5f119f29SThomas Bogendoerfer { 217*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 218*5f119f29SThomas Bogendoerfer unsigned char mon, day, hrs, min, sec; 219*5f119f29SThomas Bogendoerfer unsigned char save_control; 220*5f119f29SThomas Bogendoerfer unsigned int yrs; 221*5f119f29SThomas Bogendoerfer unsigned long flags; 222*5f119f29SThomas Bogendoerfer 223*5f119f29SThomas Bogendoerfer yrs = tm->tm_year + 1900; 224*5f119f29SThomas Bogendoerfer mon = tm->tm_mon + 1; /* tm_mon starts at zero */ 225*5f119f29SThomas Bogendoerfer day = tm->tm_mday; 226*5f119f29SThomas Bogendoerfer hrs = tm->tm_hour; 227*5f119f29SThomas Bogendoerfer min = tm->tm_min; 228*5f119f29SThomas Bogendoerfer sec = tm->tm_sec; 229*5f119f29SThomas Bogendoerfer 230*5f119f29SThomas Bogendoerfer if (yrs < 1970) 231*5f119f29SThomas Bogendoerfer return -EINVAL; 232*5f119f29SThomas Bogendoerfer 233*5f119f29SThomas Bogendoerfer yrs -= 1940; 234*5f119f29SThomas Bogendoerfer if (yrs > 255) /* They are unsigned */ 235*5f119f29SThomas Bogendoerfer return -EINVAL; 236*5f119f29SThomas Bogendoerfer 237*5f119f29SThomas Bogendoerfer if (yrs >= 100) 238*5f119f29SThomas Bogendoerfer yrs -= 100; 239*5f119f29SThomas Bogendoerfer 240*5f119f29SThomas Bogendoerfer sec = bin2bcd(sec); 241*5f119f29SThomas Bogendoerfer min = bin2bcd(min); 242*5f119f29SThomas Bogendoerfer hrs = bin2bcd(hrs); 243*5f119f29SThomas Bogendoerfer day = bin2bcd(day); 244*5f119f29SThomas Bogendoerfer mon = bin2bcd(mon); 245*5f119f29SThomas Bogendoerfer yrs = bin2bcd(yrs); 246*5f119f29SThomas Bogendoerfer 247*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 248*5f119f29SThomas Bogendoerfer save_control = ds1286_rtc_read(priv, RTC_CMD); 249*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); 250*5f119f29SThomas Bogendoerfer 251*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, yrs, RTC_YEAR); 252*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, mon, RTC_MONTH); 253*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, day, RTC_DATE); 254*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, hrs, RTC_HOURS); 255*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, min, RTC_MINUTES); 256*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, sec, RTC_SECONDS); 257*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND); 258*5f119f29SThomas Bogendoerfer 259*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, save_control, RTC_CMD); 260*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 261*5f119f29SThomas Bogendoerfer return 0; 262*5f119f29SThomas Bogendoerfer } 263*5f119f29SThomas Bogendoerfer 264*5f119f29SThomas Bogendoerfer static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 265*5f119f29SThomas Bogendoerfer { 266*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 267*5f119f29SThomas Bogendoerfer unsigned char cmd; 268*5f119f29SThomas Bogendoerfer unsigned long flags; 269*5f119f29SThomas Bogendoerfer 270*5f119f29SThomas Bogendoerfer /* 271*5f119f29SThomas Bogendoerfer * Only the values that we read from the RTC are set. That 272*5f119f29SThomas Bogendoerfer * means only tm_wday, tm_hour, tm_min. 273*5f119f29SThomas Bogendoerfer */ 274*5f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 275*5f119f29SThomas Bogendoerfer alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f; 276*5f119f29SThomas Bogendoerfer alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f; 277*5f119f29SThomas Bogendoerfer alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07; 278*5f119f29SThomas Bogendoerfer cmd = ds1286_rtc_read(priv, RTC_CMD); 279*5f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 280*5f119f29SThomas Bogendoerfer 281*5f119f29SThomas Bogendoerfer alm->time.tm_min = bcd2bin(alm->time.tm_min); 282*5f119f29SThomas Bogendoerfer alm->time.tm_hour = bcd2bin(alm->time.tm_hour); 283*5f119f29SThomas Bogendoerfer alm->time.tm_sec = 0; 284*5f119f29SThomas Bogendoerfer return 0; 285*5f119f29SThomas Bogendoerfer } 286*5f119f29SThomas Bogendoerfer 287*5f119f29SThomas Bogendoerfer static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 288*5f119f29SThomas Bogendoerfer { 289*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 290*5f119f29SThomas Bogendoerfer unsigned char hrs, min, sec; 291*5f119f29SThomas Bogendoerfer 292*5f119f29SThomas Bogendoerfer hrs = alm->time.tm_hour; 293*5f119f29SThomas Bogendoerfer min = alm->time.tm_min; 294*5f119f29SThomas Bogendoerfer sec = alm->time.tm_sec; 295*5f119f29SThomas Bogendoerfer 296*5f119f29SThomas Bogendoerfer if (hrs >= 24) 297*5f119f29SThomas Bogendoerfer hrs = 0xff; 298*5f119f29SThomas Bogendoerfer 299*5f119f29SThomas Bogendoerfer if (min >= 60) 300*5f119f29SThomas Bogendoerfer min = 0xff; 301*5f119f29SThomas Bogendoerfer 302*5f119f29SThomas Bogendoerfer if (sec != 0) 303*5f119f29SThomas Bogendoerfer return -EINVAL; 304*5f119f29SThomas Bogendoerfer 305*5f119f29SThomas Bogendoerfer min = bin2bcd(min); 306*5f119f29SThomas Bogendoerfer hrs = bin2bcd(hrs); 307*5f119f29SThomas Bogendoerfer 308*5f119f29SThomas Bogendoerfer spin_lock(&priv->lock); 309*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM); 310*5f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM); 311*5f119f29SThomas Bogendoerfer spin_unlock(&priv->lock); 312*5f119f29SThomas Bogendoerfer 313*5f119f29SThomas Bogendoerfer return 0; 314*5f119f29SThomas Bogendoerfer } 315*5f119f29SThomas Bogendoerfer 316*5f119f29SThomas Bogendoerfer static const struct rtc_class_ops ds1286_ops = { 317*5f119f29SThomas Bogendoerfer .ioctl = ds1286_ioctl, 318*5f119f29SThomas Bogendoerfer .proc = ds1286_proc, 319*5f119f29SThomas Bogendoerfer .read_time = ds1286_read_time, 320*5f119f29SThomas Bogendoerfer .set_time = ds1286_set_time, 321*5f119f29SThomas Bogendoerfer .read_alarm = ds1286_read_alarm, 322*5f119f29SThomas Bogendoerfer .set_alarm = ds1286_set_alarm, 323*5f119f29SThomas Bogendoerfer }; 324*5f119f29SThomas Bogendoerfer 325*5f119f29SThomas Bogendoerfer static int __devinit ds1286_probe(struct platform_device *pdev) 326*5f119f29SThomas Bogendoerfer { 327*5f119f29SThomas Bogendoerfer struct rtc_device *rtc; 328*5f119f29SThomas Bogendoerfer struct resource *res; 329*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv; 330*5f119f29SThomas Bogendoerfer int ret = 0; 331*5f119f29SThomas Bogendoerfer 332*5f119f29SThomas Bogendoerfer res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 333*5f119f29SThomas Bogendoerfer if (!res) 334*5f119f29SThomas Bogendoerfer return -ENODEV; 335*5f119f29SThomas Bogendoerfer priv = kzalloc(sizeof(struct ds1286_priv), GFP_KERNEL); 336*5f119f29SThomas Bogendoerfer if (!priv) 337*5f119f29SThomas Bogendoerfer return -ENOMEM; 338*5f119f29SThomas Bogendoerfer 339*5f119f29SThomas Bogendoerfer priv->size = res->end - res->start + 1; 340*5f119f29SThomas Bogendoerfer if (!request_mem_region(res->start, priv->size, pdev->name)) { 341*5f119f29SThomas Bogendoerfer ret = -EBUSY; 342*5f119f29SThomas Bogendoerfer goto out; 343*5f119f29SThomas Bogendoerfer } 344*5f119f29SThomas Bogendoerfer priv->baseaddr = res->start; 345*5f119f29SThomas Bogendoerfer priv->rtcregs = ioremap(priv->baseaddr, priv->size); 346*5f119f29SThomas Bogendoerfer if (!priv->rtcregs) { 347*5f119f29SThomas Bogendoerfer ret = -ENOMEM; 348*5f119f29SThomas Bogendoerfer goto out; 349*5f119f29SThomas Bogendoerfer } 350*5f119f29SThomas Bogendoerfer spin_lock_init(&priv->lock); 351*5f119f29SThomas Bogendoerfer rtc = rtc_device_register("ds1286", &pdev->dev, 352*5f119f29SThomas Bogendoerfer &ds1286_ops, THIS_MODULE); 353*5f119f29SThomas Bogendoerfer if (IS_ERR(rtc)) { 354*5f119f29SThomas Bogendoerfer ret = PTR_ERR(rtc); 355*5f119f29SThomas Bogendoerfer goto out; 356*5f119f29SThomas Bogendoerfer } 357*5f119f29SThomas Bogendoerfer priv->rtc = rtc; 358*5f119f29SThomas Bogendoerfer platform_set_drvdata(pdev, priv); 359*5f119f29SThomas Bogendoerfer return 0; 360*5f119f29SThomas Bogendoerfer 361*5f119f29SThomas Bogendoerfer out: 362*5f119f29SThomas Bogendoerfer if (priv->rtc) 363*5f119f29SThomas Bogendoerfer rtc_device_unregister(priv->rtc); 364*5f119f29SThomas Bogendoerfer if (priv->rtcregs) 365*5f119f29SThomas Bogendoerfer iounmap(priv->rtcregs); 366*5f119f29SThomas Bogendoerfer if (priv->baseaddr) 367*5f119f29SThomas Bogendoerfer release_mem_region(priv->baseaddr, priv->size); 368*5f119f29SThomas Bogendoerfer kfree(priv); 369*5f119f29SThomas Bogendoerfer return ret; 370*5f119f29SThomas Bogendoerfer } 371*5f119f29SThomas Bogendoerfer 372*5f119f29SThomas Bogendoerfer static int __devexit ds1286_remove(struct platform_device *pdev) 373*5f119f29SThomas Bogendoerfer { 374*5f119f29SThomas Bogendoerfer struct ds1286_priv *priv = platform_get_drvdata(pdev); 375*5f119f29SThomas Bogendoerfer 376*5f119f29SThomas Bogendoerfer rtc_device_unregister(priv->rtc); 377*5f119f29SThomas Bogendoerfer iounmap(priv->rtcregs); 378*5f119f29SThomas Bogendoerfer release_mem_region(priv->baseaddr, priv->size); 379*5f119f29SThomas Bogendoerfer kfree(priv); 380*5f119f29SThomas Bogendoerfer return 0; 381*5f119f29SThomas Bogendoerfer } 382*5f119f29SThomas Bogendoerfer 383*5f119f29SThomas Bogendoerfer static struct platform_driver ds1286_platform_driver = { 384*5f119f29SThomas Bogendoerfer .driver = { 385*5f119f29SThomas Bogendoerfer .name = "rtc-ds1286", 386*5f119f29SThomas Bogendoerfer .owner = THIS_MODULE, 387*5f119f29SThomas Bogendoerfer }, 388*5f119f29SThomas Bogendoerfer .probe = ds1286_probe, 389*5f119f29SThomas Bogendoerfer .remove = __devexit_p(ds1286_remove), 390*5f119f29SThomas Bogendoerfer }; 391*5f119f29SThomas Bogendoerfer 392*5f119f29SThomas Bogendoerfer static int __init ds1286_init(void) 393*5f119f29SThomas Bogendoerfer { 394*5f119f29SThomas Bogendoerfer return platform_driver_register(&ds1286_platform_driver); 395*5f119f29SThomas Bogendoerfer } 396*5f119f29SThomas Bogendoerfer 397*5f119f29SThomas Bogendoerfer static void __exit ds1286_exit(void) 398*5f119f29SThomas Bogendoerfer { 399*5f119f29SThomas Bogendoerfer platform_driver_unregister(&ds1286_platform_driver); 400*5f119f29SThomas Bogendoerfer } 401*5f119f29SThomas Bogendoerfer 402*5f119f29SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); 403*5f119f29SThomas Bogendoerfer MODULE_DESCRIPTION("DS1286 RTC driver"); 404*5f119f29SThomas Bogendoerfer MODULE_LICENSE("GPL"); 405*5f119f29SThomas Bogendoerfer MODULE_VERSION(DRV_VERSION); 406*5f119f29SThomas Bogendoerfer MODULE_ALIAS("platform:rtc-ds1286"); 407*5f119f29SThomas Bogendoerfer 408*5f119f29SThomas Bogendoerfer module_init(ds1286_init); 409*5f119f29SThomas Bogendoerfer module_exit(ds1286_exit); 410