15f119f29SThomas Bogendoerfer /* 25f119f29SThomas Bogendoerfer * DS1286 Real Time Clock interface for Linux 35f119f29SThomas Bogendoerfer * 45f119f29SThomas Bogendoerfer * Copyright (C) 1998, 1999, 2000 Ralf Baechle 55f119f29SThomas Bogendoerfer * Copyright (C) 2008 Thomas Bogendoerfer 65f119f29SThomas Bogendoerfer * 75f119f29SThomas Bogendoerfer * Based on code written by Paul Gortmaker. 85f119f29SThomas Bogendoerfer * 95f119f29SThomas Bogendoerfer * This program is free software; you can redistribute it and/or modify it 105f119f29SThomas Bogendoerfer * under the terms of the GNU General Public License as published by the 115f119f29SThomas Bogendoerfer * Free Software Foundation; either version 2 of the License, or (at your 125f119f29SThomas Bogendoerfer * option) any later version. 135f119f29SThomas Bogendoerfer */ 145f119f29SThomas Bogendoerfer 155f119f29SThomas Bogendoerfer #include <linux/module.h> 165f119f29SThomas Bogendoerfer #include <linux/rtc.h> 175f119f29SThomas Bogendoerfer #include <linux/platform_device.h> 185f119f29SThomas Bogendoerfer #include <linux/bcd.h> 195f119f29SThomas Bogendoerfer #include <linux/ds1286.h> 20d7a6119fSGeert Uytterhoeven #include <linux/io.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 225f119f29SThomas Bogendoerfer 235f119f29SThomas Bogendoerfer #define DRV_VERSION "1.0" 245f119f29SThomas Bogendoerfer 255f119f29SThomas Bogendoerfer struct ds1286_priv { 265f119f29SThomas Bogendoerfer struct rtc_device *rtc; 275f119f29SThomas Bogendoerfer u32 __iomem *rtcregs; 285f119f29SThomas Bogendoerfer size_t size; 295f119f29SThomas Bogendoerfer unsigned long baseaddr; 305f119f29SThomas Bogendoerfer spinlock_t lock; 315f119f29SThomas Bogendoerfer }; 325f119f29SThomas Bogendoerfer 335f119f29SThomas Bogendoerfer static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg) 345f119f29SThomas Bogendoerfer { 355f119f29SThomas Bogendoerfer return __raw_readl(&priv->rtcregs[reg]) & 0xff; 365f119f29SThomas Bogendoerfer } 375f119f29SThomas Bogendoerfer 385f119f29SThomas Bogendoerfer static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg) 395f119f29SThomas Bogendoerfer { 405f119f29SThomas Bogendoerfer __raw_writel(data, &priv->rtcregs[reg]); 415f119f29SThomas Bogendoerfer } 425f119f29SThomas Bogendoerfer 4316380c15SJohn Stultz 4416380c15SJohn Stultz static int ds1286_alarm_irq_enable(struct device *dev, unsigned int enabled) 4516380c15SJohn Stultz { 4616380c15SJohn Stultz struct ds1286_priv *priv = dev_get_drvdata(dev); 4716380c15SJohn Stultz unsigned long flags; 4816380c15SJohn Stultz unsigned char val; 4916380c15SJohn Stultz 5016380c15SJohn Stultz /* Allow or mask alarm interrupts */ 5116380c15SJohn Stultz spin_lock_irqsave(&priv->lock, flags); 5216380c15SJohn Stultz val = ds1286_rtc_read(priv, RTC_CMD); 5316380c15SJohn Stultz if (enabled) 5416380c15SJohn Stultz val &= ~RTC_TDM; 5516380c15SJohn Stultz else 5616380c15SJohn Stultz val |= RTC_TDM; 5716380c15SJohn Stultz ds1286_rtc_write(priv, val, RTC_CMD); 5816380c15SJohn Stultz spin_unlock_irqrestore(&priv->lock, flags); 5916380c15SJohn Stultz 6016380c15SJohn Stultz return 0; 6116380c15SJohn Stultz } 6216380c15SJohn Stultz 635f119f29SThomas Bogendoerfer #ifdef CONFIG_RTC_INTF_DEV 645f119f29SThomas Bogendoerfer 655f119f29SThomas Bogendoerfer static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 665f119f29SThomas Bogendoerfer { 675f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 685f119f29SThomas Bogendoerfer unsigned long flags; 695f119f29SThomas Bogendoerfer unsigned char val; 705f119f29SThomas Bogendoerfer 715f119f29SThomas Bogendoerfer switch (cmd) { 725f119f29SThomas Bogendoerfer case RTC_WIE_OFF: 735f119f29SThomas Bogendoerfer /* Mask watchdog int. enab. bit */ 745f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 755f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 765f119f29SThomas Bogendoerfer val |= RTC_WAM; 775f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 785f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 795f119f29SThomas Bogendoerfer break; 805f119f29SThomas Bogendoerfer case RTC_WIE_ON: 815f119f29SThomas Bogendoerfer /* Allow watchdog interrupts. */ 825f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 835f119f29SThomas Bogendoerfer val = ds1286_rtc_read(priv, RTC_CMD); 845f119f29SThomas Bogendoerfer val &= ~RTC_WAM; 855f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, val, RTC_CMD); 865f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 875f119f29SThomas Bogendoerfer break; 885f119f29SThomas Bogendoerfer default: 895f119f29SThomas Bogendoerfer return -ENOIOCTLCMD; 905f119f29SThomas Bogendoerfer } 915f119f29SThomas Bogendoerfer return 0; 925f119f29SThomas Bogendoerfer } 935f119f29SThomas Bogendoerfer 945f119f29SThomas Bogendoerfer #else 955f119f29SThomas Bogendoerfer #define ds1286_ioctl NULL 965f119f29SThomas Bogendoerfer #endif 975f119f29SThomas Bogendoerfer 985f119f29SThomas Bogendoerfer #ifdef CONFIG_PROC_FS 995f119f29SThomas Bogendoerfer 1005f119f29SThomas Bogendoerfer static int ds1286_proc(struct device *dev, struct seq_file *seq) 1015f119f29SThomas Bogendoerfer { 1025f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 1035f119f29SThomas Bogendoerfer unsigned char month, cmd, amode; 1045f119f29SThomas Bogendoerfer const char *s; 1055f119f29SThomas Bogendoerfer 1065f119f29SThomas Bogendoerfer month = ds1286_rtc_read(priv, RTC_MONTH); 1075f119f29SThomas Bogendoerfer seq_printf(seq, 1085f119f29SThomas Bogendoerfer "oscillator\t: %s\n" 1095f119f29SThomas Bogendoerfer "square_wave\t: %s\n", 1105f119f29SThomas Bogendoerfer (month & RTC_EOSC) ? "disabled" : "enabled", 1115f119f29SThomas Bogendoerfer (month & RTC_ESQW) ? "disabled" : "enabled"); 1125f119f29SThomas Bogendoerfer 1135f119f29SThomas Bogendoerfer amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) | 1145f119f29SThomas Bogendoerfer ((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) | 1155f119f29SThomas Bogendoerfer ((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7); 1165f119f29SThomas Bogendoerfer switch (amode) { 1175f119f29SThomas Bogendoerfer case 7: 1185f119f29SThomas Bogendoerfer s = "each minute"; 1195f119f29SThomas Bogendoerfer break; 1205f119f29SThomas Bogendoerfer case 3: 1215f119f29SThomas Bogendoerfer s = "minutes match"; 1225f119f29SThomas Bogendoerfer break; 1235f119f29SThomas Bogendoerfer case 1: 1245f119f29SThomas Bogendoerfer s = "hours and minutes match"; 1255f119f29SThomas Bogendoerfer break; 1265f119f29SThomas Bogendoerfer case 0: 1275f119f29SThomas Bogendoerfer s = "days, hours and minutes match"; 1285f119f29SThomas Bogendoerfer break; 1295f119f29SThomas Bogendoerfer default: 1305f119f29SThomas Bogendoerfer s = "invalid"; 1315f119f29SThomas Bogendoerfer break; 1325f119f29SThomas Bogendoerfer } 1335f119f29SThomas Bogendoerfer seq_printf(seq, "alarm_mode\t: %s\n", s); 1345f119f29SThomas Bogendoerfer 1355f119f29SThomas Bogendoerfer cmd = ds1286_rtc_read(priv, RTC_CMD); 1365f119f29SThomas Bogendoerfer seq_printf(seq, 1375f119f29SThomas Bogendoerfer "alarm_enable\t: %s\n" 1385f119f29SThomas Bogendoerfer "wdog_alarm\t: %s\n" 1395f119f29SThomas Bogendoerfer "alarm_mask\t: %s\n" 1405f119f29SThomas Bogendoerfer "wdog_alarm_mask\t: %s\n" 1415f119f29SThomas Bogendoerfer "interrupt_mode\t: %s\n" 1425f119f29SThomas Bogendoerfer "INTB_mode\t: %s_active\n" 1435f119f29SThomas Bogendoerfer "interrupt_pins\t: %s\n", 1445f119f29SThomas Bogendoerfer (cmd & RTC_TDF) ? "yes" : "no", 1455f119f29SThomas Bogendoerfer (cmd & RTC_WAF) ? "yes" : "no", 1465f119f29SThomas Bogendoerfer (cmd & RTC_TDM) ? "disabled" : "enabled", 1475f119f29SThomas Bogendoerfer (cmd & RTC_WAM) ? "disabled" : "enabled", 1485f119f29SThomas Bogendoerfer (cmd & RTC_PU_LVL) ? "pulse" : "level", 1495f119f29SThomas Bogendoerfer (cmd & RTC_IBH_LO) ? "low" : "high", 1505f119f29SThomas Bogendoerfer (cmd & RTC_IPSW) ? "unswapped" : "swapped"); 1515f119f29SThomas Bogendoerfer return 0; 1525f119f29SThomas Bogendoerfer } 1535f119f29SThomas Bogendoerfer 1545f119f29SThomas Bogendoerfer #else 1555f119f29SThomas Bogendoerfer #define ds1286_proc NULL 1565f119f29SThomas Bogendoerfer #endif 1575f119f29SThomas Bogendoerfer 1585f119f29SThomas Bogendoerfer static int ds1286_read_time(struct device *dev, struct rtc_time *tm) 1595f119f29SThomas Bogendoerfer { 1605f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 1615f119f29SThomas Bogendoerfer unsigned char save_control; 1625f119f29SThomas Bogendoerfer unsigned long flags; 1635f119f29SThomas Bogendoerfer unsigned long uip_watchdog = jiffies; 1645f119f29SThomas Bogendoerfer 1655f119f29SThomas Bogendoerfer /* 1665f119f29SThomas Bogendoerfer * read RTC once any update in progress is done. The update 1675f119f29SThomas Bogendoerfer * can take just over 2ms. We wait 10 to 20ms. There is no need to 1685f119f29SThomas Bogendoerfer * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. 1695f119f29SThomas Bogendoerfer * If you need to know *exactly* when a second has started, enable 1705f119f29SThomas Bogendoerfer * periodic update complete interrupts, (via ioctl) and then 1715f119f29SThomas Bogendoerfer * immediately read /dev/rtc which will block until you get the IRQ. 1725f119f29SThomas Bogendoerfer * Once the read clears, read the RTC time (again via ioctl). Easy. 1735f119f29SThomas Bogendoerfer */ 1745f119f29SThomas Bogendoerfer 1755f119f29SThomas Bogendoerfer if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE) 1765f119f29SThomas Bogendoerfer while (time_before(jiffies, uip_watchdog + 2*HZ/100)) 1775f119f29SThomas Bogendoerfer barrier(); 1785f119f29SThomas Bogendoerfer 1795f119f29SThomas Bogendoerfer /* 1805f119f29SThomas Bogendoerfer * Only the values that we read from the RTC are set. We leave 1815f119f29SThomas Bogendoerfer * tm_wday, tm_yday and tm_isdst untouched. Even though the 1825f119f29SThomas Bogendoerfer * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated 1835f119f29SThomas Bogendoerfer * by the RTC when initially set to a non-zero value. 1845f119f29SThomas Bogendoerfer */ 1855f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 1865f119f29SThomas Bogendoerfer save_control = ds1286_rtc_read(priv, RTC_CMD); 1875f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); 1885f119f29SThomas Bogendoerfer 1895f119f29SThomas Bogendoerfer tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS); 1905f119f29SThomas Bogendoerfer tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES); 1915f119f29SThomas Bogendoerfer tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f; 1925f119f29SThomas Bogendoerfer tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE); 1935f119f29SThomas Bogendoerfer tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f; 1945f119f29SThomas Bogendoerfer tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR); 1955f119f29SThomas Bogendoerfer 1965f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, save_control, RTC_CMD); 1975f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 1985f119f29SThomas Bogendoerfer 1995f119f29SThomas Bogendoerfer tm->tm_sec = bcd2bin(tm->tm_sec); 2005f119f29SThomas Bogendoerfer tm->tm_min = bcd2bin(tm->tm_min); 2015f119f29SThomas Bogendoerfer tm->tm_hour = bcd2bin(tm->tm_hour); 2025f119f29SThomas Bogendoerfer tm->tm_mday = bcd2bin(tm->tm_mday); 2035f119f29SThomas Bogendoerfer tm->tm_mon = bcd2bin(tm->tm_mon); 2045f119f29SThomas Bogendoerfer tm->tm_year = bcd2bin(tm->tm_year); 2055f119f29SThomas Bogendoerfer 2065f119f29SThomas Bogendoerfer /* 2075f119f29SThomas Bogendoerfer * Account for differences between how the RTC uses the values 2085f119f29SThomas Bogendoerfer * and how they are defined in a struct rtc_time; 2095f119f29SThomas Bogendoerfer */ 2105f119f29SThomas Bogendoerfer if (tm->tm_year < 45) 2115f119f29SThomas Bogendoerfer tm->tm_year += 30; 2125f119f29SThomas Bogendoerfer tm->tm_year += 40; 2135f119f29SThomas Bogendoerfer if (tm->tm_year < 70) 2145f119f29SThomas Bogendoerfer tm->tm_year += 100; 2155f119f29SThomas Bogendoerfer 2165f119f29SThomas Bogendoerfer tm->tm_mon--; 2175f119f29SThomas Bogendoerfer 2185f119f29SThomas Bogendoerfer return rtc_valid_tm(tm); 2195f119f29SThomas Bogendoerfer } 2205f119f29SThomas Bogendoerfer 2215f119f29SThomas Bogendoerfer static int ds1286_set_time(struct device *dev, struct rtc_time *tm) 2225f119f29SThomas Bogendoerfer { 2235f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 2245f119f29SThomas Bogendoerfer unsigned char mon, day, hrs, min, sec; 2255f119f29SThomas Bogendoerfer unsigned char save_control; 2265f119f29SThomas Bogendoerfer unsigned int yrs; 2275f119f29SThomas Bogendoerfer unsigned long flags; 2285f119f29SThomas Bogendoerfer 2295f119f29SThomas Bogendoerfer yrs = tm->tm_year + 1900; 2305f119f29SThomas Bogendoerfer mon = tm->tm_mon + 1; /* tm_mon starts at zero */ 2315f119f29SThomas Bogendoerfer day = tm->tm_mday; 2325f119f29SThomas Bogendoerfer hrs = tm->tm_hour; 2335f119f29SThomas Bogendoerfer min = tm->tm_min; 2345f119f29SThomas Bogendoerfer sec = tm->tm_sec; 2355f119f29SThomas Bogendoerfer 2365f119f29SThomas Bogendoerfer if (yrs < 1970) 2375f119f29SThomas Bogendoerfer return -EINVAL; 2385f119f29SThomas Bogendoerfer 2395f119f29SThomas Bogendoerfer yrs -= 1940; 2405f119f29SThomas Bogendoerfer if (yrs > 255) /* They are unsigned */ 2415f119f29SThomas Bogendoerfer return -EINVAL; 2425f119f29SThomas Bogendoerfer 2435f119f29SThomas Bogendoerfer if (yrs >= 100) 2445f119f29SThomas Bogendoerfer yrs -= 100; 2455f119f29SThomas Bogendoerfer 2465f119f29SThomas Bogendoerfer sec = bin2bcd(sec); 2475f119f29SThomas Bogendoerfer min = bin2bcd(min); 2485f119f29SThomas Bogendoerfer hrs = bin2bcd(hrs); 2495f119f29SThomas Bogendoerfer day = bin2bcd(day); 2505f119f29SThomas Bogendoerfer mon = bin2bcd(mon); 2515f119f29SThomas Bogendoerfer yrs = bin2bcd(yrs); 2525f119f29SThomas Bogendoerfer 2535f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 2545f119f29SThomas Bogendoerfer save_control = ds1286_rtc_read(priv, RTC_CMD); 2555f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); 2565f119f29SThomas Bogendoerfer 2575f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, yrs, RTC_YEAR); 2585f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, mon, RTC_MONTH); 2595f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, day, RTC_DATE); 2605f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, hrs, RTC_HOURS); 2615f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, min, RTC_MINUTES); 2625f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, sec, RTC_SECONDS); 2635f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND); 2645f119f29SThomas Bogendoerfer 2655f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, save_control, RTC_CMD); 2665f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 2675f119f29SThomas Bogendoerfer return 0; 2685f119f29SThomas Bogendoerfer } 2695f119f29SThomas Bogendoerfer 2705f119f29SThomas Bogendoerfer static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2715f119f29SThomas Bogendoerfer { 2725f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 2735f119f29SThomas Bogendoerfer unsigned char cmd; 2745f119f29SThomas Bogendoerfer unsigned long flags; 2755f119f29SThomas Bogendoerfer 2765f119f29SThomas Bogendoerfer /* 2775f119f29SThomas Bogendoerfer * Only the values that we read from the RTC are set. That 2785f119f29SThomas Bogendoerfer * means only tm_wday, tm_hour, tm_min. 2795f119f29SThomas Bogendoerfer */ 2805f119f29SThomas Bogendoerfer spin_lock_irqsave(&priv->lock, flags); 2815f119f29SThomas Bogendoerfer alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f; 2825f119f29SThomas Bogendoerfer alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f; 2835f119f29SThomas Bogendoerfer alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07; 2845f119f29SThomas Bogendoerfer cmd = ds1286_rtc_read(priv, RTC_CMD); 2855f119f29SThomas Bogendoerfer spin_unlock_irqrestore(&priv->lock, flags); 2865f119f29SThomas Bogendoerfer 2875f119f29SThomas Bogendoerfer alm->time.tm_min = bcd2bin(alm->time.tm_min); 2885f119f29SThomas Bogendoerfer alm->time.tm_hour = bcd2bin(alm->time.tm_hour); 2895f119f29SThomas Bogendoerfer alm->time.tm_sec = 0; 2905f119f29SThomas Bogendoerfer return 0; 2915f119f29SThomas Bogendoerfer } 2925f119f29SThomas Bogendoerfer 2935f119f29SThomas Bogendoerfer static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 2945f119f29SThomas Bogendoerfer { 2955f119f29SThomas Bogendoerfer struct ds1286_priv *priv = dev_get_drvdata(dev); 2965f119f29SThomas Bogendoerfer unsigned char hrs, min, sec; 2975f119f29SThomas Bogendoerfer 2985f119f29SThomas Bogendoerfer hrs = alm->time.tm_hour; 2995f119f29SThomas Bogendoerfer min = alm->time.tm_min; 3005f119f29SThomas Bogendoerfer sec = alm->time.tm_sec; 3015f119f29SThomas Bogendoerfer 3025f119f29SThomas Bogendoerfer if (hrs >= 24) 3035f119f29SThomas Bogendoerfer hrs = 0xff; 3045f119f29SThomas Bogendoerfer 3055f119f29SThomas Bogendoerfer if (min >= 60) 3065f119f29SThomas Bogendoerfer min = 0xff; 3075f119f29SThomas Bogendoerfer 3085f119f29SThomas Bogendoerfer if (sec != 0) 3095f119f29SThomas Bogendoerfer return -EINVAL; 3105f119f29SThomas Bogendoerfer 3115f119f29SThomas Bogendoerfer min = bin2bcd(min); 3125f119f29SThomas Bogendoerfer hrs = bin2bcd(hrs); 3135f119f29SThomas Bogendoerfer 3145f119f29SThomas Bogendoerfer spin_lock(&priv->lock); 3155f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM); 3165f119f29SThomas Bogendoerfer ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM); 3175f119f29SThomas Bogendoerfer spin_unlock(&priv->lock); 3185f119f29SThomas Bogendoerfer 3195f119f29SThomas Bogendoerfer return 0; 3205f119f29SThomas Bogendoerfer } 3215f119f29SThomas Bogendoerfer 3225f119f29SThomas Bogendoerfer static const struct rtc_class_ops ds1286_ops = { 3235f119f29SThomas Bogendoerfer .ioctl = ds1286_ioctl, 3245f119f29SThomas Bogendoerfer .proc = ds1286_proc, 3255f119f29SThomas Bogendoerfer .read_time = ds1286_read_time, 3265f119f29SThomas Bogendoerfer .set_time = ds1286_set_time, 3275f119f29SThomas Bogendoerfer .read_alarm = ds1286_read_alarm, 3285f119f29SThomas Bogendoerfer .set_alarm = ds1286_set_alarm, 32916380c15SJohn Stultz .alarm_irq_enable = ds1286_alarm_irq_enable, 3305f119f29SThomas Bogendoerfer }; 3315f119f29SThomas Bogendoerfer 3325f119f29SThomas Bogendoerfer static int __devinit ds1286_probe(struct platform_device *pdev) 3335f119f29SThomas Bogendoerfer { 3345f119f29SThomas Bogendoerfer struct rtc_device *rtc; 3355f119f29SThomas Bogendoerfer struct resource *res; 3365f119f29SThomas Bogendoerfer struct ds1286_priv *priv; 3375f119f29SThomas Bogendoerfer int ret = 0; 3385f119f29SThomas Bogendoerfer 3395f119f29SThomas Bogendoerfer res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3405f119f29SThomas Bogendoerfer if (!res) 3415f119f29SThomas Bogendoerfer return -ENODEV; 3425f119f29SThomas Bogendoerfer priv = kzalloc(sizeof(struct ds1286_priv), GFP_KERNEL); 3435f119f29SThomas Bogendoerfer if (!priv) 3445f119f29SThomas Bogendoerfer return -ENOMEM; 3455f119f29SThomas Bogendoerfer 346*28f65c11SJoe Perches priv->size = resource_size(res); 3475f119f29SThomas Bogendoerfer if (!request_mem_region(res->start, priv->size, pdev->name)) { 3485f119f29SThomas Bogendoerfer ret = -EBUSY; 3495f119f29SThomas Bogendoerfer goto out; 3505f119f29SThomas Bogendoerfer } 3515f119f29SThomas Bogendoerfer priv->baseaddr = res->start; 3525f119f29SThomas Bogendoerfer priv->rtcregs = ioremap(priv->baseaddr, priv->size); 3535f119f29SThomas Bogendoerfer if (!priv->rtcregs) { 3545f119f29SThomas Bogendoerfer ret = -ENOMEM; 3555f119f29SThomas Bogendoerfer goto out; 3565f119f29SThomas Bogendoerfer } 3575f119f29SThomas Bogendoerfer spin_lock_init(&priv->lock); 3589a281a67SJohn Stultz platform_set_drvdata(pdev, priv); 3595f119f29SThomas Bogendoerfer rtc = rtc_device_register("ds1286", &pdev->dev, 3605f119f29SThomas Bogendoerfer &ds1286_ops, THIS_MODULE); 3615f119f29SThomas Bogendoerfer if (IS_ERR(rtc)) { 3625f119f29SThomas Bogendoerfer ret = PTR_ERR(rtc); 3635f119f29SThomas Bogendoerfer goto out; 3645f119f29SThomas Bogendoerfer } 3655f119f29SThomas Bogendoerfer priv->rtc = rtc; 3665f119f29SThomas Bogendoerfer return 0; 3675f119f29SThomas Bogendoerfer 3685f119f29SThomas Bogendoerfer out: 3695f119f29SThomas Bogendoerfer if (priv->rtc) 3705f119f29SThomas Bogendoerfer rtc_device_unregister(priv->rtc); 3715f119f29SThomas Bogendoerfer if (priv->rtcregs) 3725f119f29SThomas Bogendoerfer iounmap(priv->rtcregs); 3735f119f29SThomas Bogendoerfer if (priv->baseaddr) 3745f119f29SThomas Bogendoerfer release_mem_region(priv->baseaddr, priv->size); 3755f119f29SThomas Bogendoerfer kfree(priv); 3765f119f29SThomas Bogendoerfer return ret; 3775f119f29SThomas Bogendoerfer } 3785f119f29SThomas Bogendoerfer 3795f119f29SThomas Bogendoerfer static int __devexit ds1286_remove(struct platform_device *pdev) 3805f119f29SThomas Bogendoerfer { 3815f119f29SThomas Bogendoerfer struct ds1286_priv *priv = platform_get_drvdata(pdev); 3825f119f29SThomas Bogendoerfer 3835f119f29SThomas Bogendoerfer rtc_device_unregister(priv->rtc); 3845f119f29SThomas Bogendoerfer iounmap(priv->rtcregs); 3855f119f29SThomas Bogendoerfer release_mem_region(priv->baseaddr, priv->size); 3865f119f29SThomas Bogendoerfer kfree(priv); 3875f119f29SThomas Bogendoerfer return 0; 3885f119f29SThomas Bogendoerfer } 3895f119f29SThomas Bogendoerfer 3905f119f29SThomas Bogendoerfer static struct platform_driver ds1286_platform_driver = { 3915f119f29SThomas Bogendoerfer .driver = { 3925f119f29SThomas Bogendoerfer .name = "rtc-ds1286", 3935f119f29SThomas Bogendoerfer .owner = THIS_MODULE, 3945f119f29SThomas Bogendoerfer }, 3955f119f29SThomas Bogendoerfer .probe = ds1286_probe, 3965f119f29SThomas Bogendoerfer .remove = __devexit_p(ds1286_remove), 3975f119f29SThomas Bogendoerfer }; 3985f119f29SThomas Bogendoerfer 3995f119f29SThomas Bogendoerfer static int __init ds1286_init(void) 4005f119f29SThomas Bogendoerfer { 4015f119f29SThomas Bogendoerfer return platform_driver_register(&ds1286_platform_driver); 4025f119f29SThomas Bogendoerfer } 4035f119f29SThomas Bogendoerfer 4045f119f29SThomas Bogendoerfer static void __exit ds1286_exit(void) 4055f119f29SThomas Bogendoerfer { 4065f119f29SThomas Bogendoerfer platform_driver_unregister(&ds1286_platform_driver); 4075f119f29SThomas Bogendoerfer } 4085f119f29SThomas Bogendoerfer 4095f119f29SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); 4105f119f29SThomas Bogendoerfer MODULE_DESCRIPTION("DS1286 RTC driver"); 4115f119f29SThomas Bogendoerfer MODULE_LICENSE("GPL"); 4125f119f29SThomas Bogendoerfer MODULE_VERSION(DRV_VERSION); 4135f119f29SThomas Bogendoerfer MODULE_ALIAS("platform:rtc-ds1286"); 4145f119f29SThomas Bogendoerfer 4155f119f29SThomas Bogendoerfer module_init(ds1286_init); 4165f119f29SThomas Bogendoerfer module_exit(ds1286_exit); 417