10942a71eSRajeev Kumar /* 20942a71eSRajeev Kumar * drivers/rtc/rtc-spear.c 30942a71eSRajeev Kumar * 40942a71eSRajeev Kumar * Copyright (C) 2010 ST Microelectronics 50942a71eSRajeev Kumar * Rajeev Kumar<rajeev-dlh.kumar@st.com> 60942a71eSRajeev Kumar * 70942a71eSRajeev Kumar * This file is licensed under the terms of the GNU General Public 80942a71eSRajeev Kumar * License version 2. This program is licensed "as is" without any 90942a71eSRajeev Kumar * warranty of any kind, whether express or implied. 100942a71eSRajeev Kumar */ 110942a71eSRajeev Kumar 120942a71eSRajeev Kumar #include <linux/bcd.h> 130942a71eSRajeev Kumar #include <linux/clk.h> 140942a71eSRajeev Kumar #include <linux/delay.h> 150942a71eSRajeev Kumar #include <linux/init.h> 160942a71eSRajeev Kumar #include <linux/io.h> 170942a71eSRajeev Kumar #include <linux/irq.h> 180942a71eSRajeev Kumar #include <linux/module.h> 190942a71eSRajeev Kumar #include <linux/platform_device.h> 200942a71eSRajeev Kumar #include <linux/rtc.h> 210942a71eSRajeev Kumar #include <linux/slab.h> 220942a71eSRajeev Kumar #include <linux/spinlock.h> 230942a71eSRajeev Kumar 240942a71eSRajeev Kumar /* RTC registers */ 250942a71eSRajeev Kumar #define TIME_REG 0x00 260942a71eSRajeev Kumar #define DATE_REG 0x04 270942a71eSRajeev Kumar #define ALARM_TIME_REG 0x08 280942a71eSRajeev Kumar #define ALARM_DATE_REG 0x0C 290942a71eSRajeev Kumar #define CTRL_REG 0x10 300942a71eSRajeev Kumar #define STATUS_REG 0x14 310942a71eSRajeev Kumar 320942a71eSRajeev Kumar /* TIME_REG & ALARM_TIME_REG */ 330942a71eSRajeev Kumar #define SECONDS_UNITS (0xf<<0) /* seconds units position */ 340942a71eSRajeev Kumar #define SECONDS_TENS (0x7<<4) /* seconds tens position */ 350942a71eSRajeev Kumar #define MINUTES_UNITS (0xf<<8) /* minutes units position */ 360942a71eSRajeev Kumar #define MINUTES_TENS (0x7<<12) /* minutes tens position */ 370942a71eSRajeev Kumar #define HOURS_UNITS (0xf<<16) /* hours units position */ 380942a71eSRajeev Kumar #define HOURS_TENS (0x3<<20) /* hours tens position */ 390942a71eSRajeev Kumar 400942a71eSRajeev Kumar /* DATE_REG & ALARM_DATE_REG */ 410942a71eSRajeev Kumar #define DAYS_UNITS (0xf<<0) /* days units position */ 420942a71eSRajeev Kumar #define DAYS_TENS (0x3<<4) /* days tens position */ 430942a71eSRajeev Kumar #define MONTHS_UNITS (0xf<<8) /* months units position */ 440942a71eSRajeev Kumar #define MONTHS_TENS (0x1<<12) /* months tens position */ 450942a71eSRajeev Kumar #define YEARS_UNITS (0xf<<16) /* years units position */ 460942a71eSRajeev Kumar #define YEARS_TENS (0xf<<20) /* years tens position */ 470942a71eSRajeev Kumar #define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */ 480942a71eSRajeev Kumar #define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */ 490942a71eSRajeev Kumar 500942a71eSRajeev Kumar /* MASK SHIFT TIME_REG & ALARM_TIME_REG*/ 510942a71eSRajeev Kumar #define SECOND_SHIFT 0x00 /* seconds units */ 520942a71eSRajeev Kumar #define MINUTE_SHIFT 0x08 /* minutes units position */ 530942a71eSRajeev Kumar #define HOUR_SHIFT 0x10 /* hours units position */ 540942a71eSRajeev Kumar #define MDAY_SHIFT 0x00 /* Month day shift */ 550942a71eSRajeev Kumar #define MONTH_SHIFT 0x08 /* Month shift */ 560942a71eSRajeev Kumar #define YEAR_SHIFT 0x10 /* Year shift */ 570942a71eSRajeev Kumar 580942a71eSRajeev Kumar #define SECOND_MASK 0x7F 590942a71eSRajeev Kumar #define MIN_MASK 0x7F 600942a71eSRajeev Kumar #define HOUR_MASK 0x3F 610942a71eSRajeev Kumar #define DAY_MASK 0x3F 620942a71eSRajeev Kumar #define MONTH_MASK 0x7F 630942a71eSRajeev Kumar #define YEAR_MASK 0xFFFF 640942a71eSRajeev Kumar 650942a71eSRajeev Kumar /* date reg equal to time reg, for debug only */ 660942a71eSRajeev Kumar #define TIME_BYP (1<<9) 670942a71eSRajeev Kumar #define INT_ENABLE (1<<31) /* interrupt enable */ 680942a71eSRajeev Kumar 690942a71eSRajeev Kumar /* STATUS_REG */ 700942a71eSRajeev Kumar #define CLK_UNCONNECTED (1<<0) 710942a71eSRajeev Kumar #define PEND_WR_TIME (1<<2) 720942a71eSRajeev Kumar #define PEND_WR_DATE (1<<3) 730942a71eSRajeev Kumar #define LOST_WR_TIME (1<<4) 740942a71eSRajeev Kumar #define LOST_WR_DATE (1<<5) 750942a71eSRajeev Kumar #define RTC_INT_MASK (1<<31) 760942a71eSRajeev Kumar #define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE) 770942a71eSRajeev Kumar #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) 780942a71eSRajeev Kumar 790942a71eSRajeev Kumar struct spear_rtc_config { 80*ee6c54caSViresh Kumar struct rtc_device *rtc; 810942a71eSRajeev Kumar struct clk *clk; 820942a71eSRajeev Kumar spinlock_t lock; 830942a71eSRajeev Kumar void __iomem *ioaddr; 84cd0e08a8SDeepak Sikri unsigned int irq_wake; 850942a71eSRajeev Kumar }; 860942a71eSRajeev Kumar 870942a71eSRajeev Kumar static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) 880942a71eSRajeev Kumar { 890942a71eSRajeev Kumar unsigned int val; 900942a71eSRajeev Kumar unsigned long flags; 910942a71eSRajeev Kumar 920942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 930942a71eSRajeev Kumar val = readl(config->ioaddr + STATUS_REG); 940942a71eSRajeev Kumar val |= RTC_INT_MASK; 950942a71eSRajeev Kumar writel(val, config->ioaddr + STATUS_REG); 960942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 970942a71eSRajeev Kumar } 980942a71eSRajeev Kumar 990942a71eSRajeev Kumar static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config) 1000942a71eSRajeev Kumar { 1010942a71eSRajeev Kumar unsigned int val; 1020942a71eSRajeev Kumar 1030942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1040942a71eSRajeev Kumar if (!(val & INT_ENABLE)) { 1050942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1060942a71eSRajeev Kumar val |= INT_ENABLE; 1070942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1080942a71eSRajeev Kumar } 1090942a71eSRajeev Kumar } 1100942a71eSRajeev Kumar 1110942a71eSRajeev Kumar static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config) 1120942a71eSRajeev Kumar { 1130942a71eSRajeev Kumar unsigned int val; 1140942a71eSRajeev Kumar 1150942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1160942a71eSRajeev Kumar if (val & INT_ENABLE) { 1170942a71eSRajeev Kumar val &= ~INT_ENABLE; 1180942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1190942a71eSRajeev Kumar } 1200942a71eSRajeev Kumar } 1210942a71eSRajeev Kumar 1220942a71eSRajeev Kumar static inline int is_write_complete(struct spear_rtc_config *config) 1230942a71eSRajeev Kumar { 1240942a71eSRajeev Kumar int ret = 0; 1250942a71eSRajeev Kumar unsigned long flags; 1260942a71eSRajeev Kumar 1270942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1280942a71eSRajeev Kumar if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL) 1290942a71eSRajeev Kumar ret = -EIO; 1300942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1310942a71eSRajeev Kumar 1320942a71eSRajeev Kumar return ret; 1330942a71eSRajeev Kumar } 1340942a71eSRajeev Kumar 1350942a71eSRajeev Kumar static void rtc_wait_not_busy(struct spear_rtc_config *config) 1360942a71eSRajeev Kumar { 1370942a71eSRajeev Kumar int status, count = 0; 1380942a71eSRajeev Kumar unsigned long flags; 1390942a71eSRajeev Kumar 1400942a71eSRajeev Kumar /* Assuming BUSY may stay active for 80 msec) */ 1410942a71eSRajeev Kumar for (count = 0; count < 80; count++) { 1420942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1430942a71eSRajeev Kumar status = readl(config->ioaddr + STATUS_REG); 1440942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1450942a71eSRajeev Kumar if ((status & STATUS_BUSY) == 0) 1460942a71eSRajeev Kumar break; 1470942a71eSRajeev Kumar /* check status busy, after each msec */ 1480942a71eSRajeev Kumar msleep(1); 1490942a71eSRajeev Kumar } 1500942a71eSRajeev Kumar } 1510942a71eSRajeev Kumar 1520942a71eSRajeev Kumar static irqreturn_t spear_rtc_irq(int irq, void *dev_id) 1530942a71eSRajeev Kumar { 154*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_id; 1550942a71eSRajeev Kumar unsigned long flags, events = 0; 1560942a71eSRajeev Kumar unsigned int irq_data; 1570942a71eSRajeev Kumar 1580942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1590942a71eSRajeev Kumar irq_data = readl(config->ioaddr + STATUS_REG); 1600942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1610942a71eSRajeev Kumar 1620942a71eSRajeev Kumar if ((irq_data & RTC_INT_MASK)) { 1630942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1640942a71eSRajeev Kumar events = RTC_IRQF | RTC_AF; 165*ee6c54caSViresh Kumar rtc_update_irq(config->rtc, 1, events); 1660942a71eSRajeev Kumar return IRQ_HANDLED; 1670942a71eSRajeev Kumar } else 1680942a71eSRajeev Kumar return IRQ_NONE; 1690942a71eSRajeev Kumar 1700942a71eSRajeev Kumar } 1710942a71eSRajeev Kumar 1720942a71eSRajeev Kumar static int tm2bcd(struct rtc_time *tm) 1730942a71eSRajeev Kumar { 1740942a71eSRajeev Kumar if (rtc_valid_tm(tm) != 0) 1750942a71eSRajeev Kumar return -EINVAL; 1760942a71eSRajeev Kumar tm->tm_sec = bin2bcd(tm->tm_sec); 1770942a71eSRajeev Kumar tm->tm_min = bin2bcd(tm->tm_min); 1780942a71eSRajeev Kumar tm->tm_hour = bin2bcd(tm->tm_hour); 1790942a71eSRajeev Kumar tm->tm_mday = bin2bcd(tm->tm_mday); 1800942a71eSRajeev Kumar tm->tm_mon = bin2bcd(tm->tm_mon + 1); 1810942a71eSRajeev Kumar tm->tm_year = bin2bcd(tm->tm_year); 1820942a71eSRajeev Kumar 1830942a71eSRajeev Kumar return 0; 1840942a71eSRajeev Kumar } 1850942a71eSRajeev Kumar 1860942a71eSRajeev Kumar static void bcd2tm(struct rtc_time *tm) 1870942a71eSRajeev Kumar { 1880942a71eSRajeev Kumar tm->tm_sec = bcd2bin(tm->tm_sec); 1890942a71eSRajeev Kumar tm->tm_min = bcd2bin(tm->tm_min); 1900942a71eSRajeev Kumar tm->tm_hour = bcd2bin(tm->tm_hour); 1910942a71eSRajeev Kumar tm->tm_mday = bcd2bin(tm->tm_mday); 1920942a71eSRajeev Kumar tm->tm_mon = bcd2bin(tm->tm_mon) - 1; 1930942a71eSRajeev Kumar /* epoch == 1900 */ 1940942a71eSRajeev Kumar tm->tm_year = bcd2bin(tm->tm_year); 1950942a71eSRajeev Kumar } 1960942a71eSRajeev Kumar 1970942a71eSRajeev Kumar /* 1980942a71eSRajeev Kumar * spear_rtc_read_time - set the time 1990942a71eSRajeev Kumar * @dev: rtc device in use 2000942a71eSRajeev Kumar * @tm: holds date and time 2010942a71eSRajeev Kumar * 2020942a71eSRajeev Kumar * This function read time and date. On success it will return 0 2030942a71eSRajeev Kumar * otherwise -ve error is returned. 2040942a71eSRajeev Kumar */ 2050942a71eSRajeev Kumar static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) 2060942a71eSRajeev Kumar { 207*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2080942a71eSRajeev Kumar unsigned int time, date; 2090942a71eSRajeev Kumar 2100942a71eSRajeev Kumar /* we don't report wday/yday/isdst ... */ 2110942a71eSRajeev Kumar rtc_wait_not_busy(config); 2120942a71eSRajeev Kumar 2130942a71eSRajeev Kumar time = readl(config->ioaddr + TIME_REG); 2140942a71eSRajeev Kumar date = readl(config->ioaddr + DATE_REG); 2150942a71eSRajeev Kumar tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2160942a71eSRajeev Kumar tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2170942a71eSRajeev Kumar tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2180942a71eSRajeev Kumar tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2190942a71eSRajeev Kumar tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2200942a71eSRajeev Kumar tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2210942a71eSRajeev Kumar 2220942a71eSRajeev Kumar bcd2tm(tm); 2230942a71eSRajeev Kumar return 0; 2240942a71eSRajeev Kumar } 2250942a71eSRajeev Kumar 2260942a71eSRajeev Kumar /* 2270942a71eSRajeev Kumar * spear_rtc_set_time - set the time 2280942a71eSRajeev Kumar * @dev: rtc device in use 2290942a71eSRajeev Kumar * @tm: holds date and time 2300942a71eSRajeev Kumar * 2310942a71eSRajeev Kumar * This function set time and date. On success it will return 0 2320942a71eSRajeev Kumar * otherwise -ve error is returned. 2330942a71eSRajeev Kumar */ 2340942a71eSRajeev Kumar static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) 2350942a71eSRajeev Kumar { 236*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2370942a71eSRajeev Kumar unsigned int time, date, err = 0; 2380942a71eSRajeev Kumar 2390942a71eSRajeev Kumar if (tm2bcd(tm) < 0) 2400942a71eSRajeev Kumar return -EINVAL; 2410942a71eSRajeev Kumar 2420942a71eSRajeev Kumar rtc_wait_not_busy(config); 2430942a71eSRajeev Kumar time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | 2440942a71eSRajeev Kumar (tm->tm_hour << HOUR_SHIFT); 2450942a71eSRajeev Kumar date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | 2460942a71eSRajeev Kumar (tm->tm_year << YEAR_SHIFT); 2470942a71eSRajeev Kumar writel(time, config->ioaddr + TIME_REG); 2480942a71eSRajeev Kumar writel(date, config->ioaddr + DATE_REG); 2490942a71eSRajeev Kumar err = is_write_complete(config); 2500942a71eSRajeev Kumar if (err < 0) 2510942a71eSRajeev Kumar return err; 2520942a71eSRajeev Kumar 2530942a71eSRajeev Kumar return 0; 2540942a71eSRajeev Kumar } 2550942a71eSRajeev Kumar 2560942a71eSRajeev Kumar /* 2570942a71eSRajeev Kumar * spear_rtc_read_alarm - read the alarm time 2580942a71eSRajeev Kumar * @dev: rtc device in use 2590942a71eSRajeev Kumar * @alm: holds alarm date and time 2600942a71eSRajeev Kumar * 2610942a71eSRajeev Kumar * This function read alarm time and date. On success it will return 0 2620942a71eSRajeev Kumar * otherwise -ve error is returned. 2630942a71eSRajeev Kumar */ 2640942a71eSRajeev Kumar static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2650942a71eSRajeev Kumar { 266*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2670942a71eSRajeev Kumar unsigned int time, date; 2680942a71eSRajeev Kumar 2690942a71eSRajeev Kumar rtc_wait_not_busy(config); 2700942a71eSRajeev Kumar 2710942a71eSRajeev Kumar time = readl(config->ioaddr + ALARM_TIME_REG); 2720942a71eSRajeev Kumar date = readl(config->ioaddr + ALARM_DATE_REG); 2730942a71eSRajeev Kumar alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2740942a71eSRajeev Kumar alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2750942a71eSRajeev Kumar alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2760942a71eSRajeev Kumar alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2770942a71eSRajeev Kumar alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2780942a71eSRajeev Kumar alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2790942a71eSRajeev Kumar 2800942a71eSRajeev Kumar bcd2tm(&alm->time); 2810942a71eSRajeev Kumar alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; 2820942a71eSRajeev Kumar 2830942a71eSRajeev Kumar return 0; 2840942a71eSRajeev Kumar } 2850942a71eSRajeev Kumar 2860942a71eSRajeev Kumar /* 2870942a71eSRajeev Kumar * spear_rtc_set_alarm - set the alarm time 2880942a71eSRajeev Kumar * @dev: rtc device in use 2890942a71eSRajeev Kumar * @alm: holds alarm date and time 2900942a71eSRajeev Kumar * 2910942a71eSRajeev Kumar * This function set alarm time and date. On success it will return 0 2920942a71eSRajeev Kumar * otherwise -ve error is returned. 2930942a71eSRajeev Kumar */ 2940942a71eSRajeev Kumar static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 2950942a71eSRajeev Kumar { 296*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2970942a71eSRajeev Kumar unsigned int time, date, err = 0; 2980942a71eSRajeev Kumar 2990942a71eSRajeev Kumar if (tm2bcd(&alm->time) < 0) 3000942a71eSRajeev Kumar return -EINVAL; 3010942a71eSRajeev Kumar 3020942a71eSRajeev Kumar rtc_wait_not_busy(config); 3030942a71eSRajeev Kumar 3040942a71eSRajeev Kumar time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << 3050942a71eSRajeev Kumar MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); 3060942a71eSRajeev Kumar date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << 3070942a71eSRajeev Kumar MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); 3080942a71eSRajeev Kumar 3090942a71eSRajeev Kumar writel(time, config->ioaddr + ALARM_TIME_REG); 3100942a71eSRajeev Kumar writel(date, config->ioaddr + ALARM_DATE_REG); 3110942a71eSRajeev Kumar err = is_write_complete(config); 3120942a71eSRajeev Kumar if (err < 0) 3130942a71eSRajeev Kumar return err; 3140942a71eSRajeev Kumar 3150942a71eSRajeev Kumar if (alm->enabled) 3160942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 3170942a71eSRajeev Kumar else 3180942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 3190942a71eSRajeev Kumar 3200942a71eSRajeev Kumar return 0; 3210942a71eSRajeev Kumar } 322131f8b75SShiraz Hashim 323131f8b75SShiraz Hashim static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) 324131f8b75SShiraz Hashim { 325*ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 326131f8b75SShiraz Hashim int ret = 0; 327131f8b75SShiraz Hashim 328131f8b75SShiraz Hashim spear_rtc_clear_interrupt(config); 329131f8b75SShiraz Hashim 330131f8b75SShiraz Hashim switch (enabled) { 331131f8b75SShiraz Hashim case 0: 332131f8b75SShiraz Hashim /* alarm off */ 333131f8b75SShiraz Hashim spear_rtc_disable_interrupt(config); 334131f8b75SShiraz Hashim break; 335131f8b75SShiraz Hashim case 1: 336131f8b75SShiraz Hashim /* alarm on */ 337131f8b75SShiraz Hashim spear_rtc_enable_interrupt(config); 338131f8b75SShiraz Hashim break; 339131f8b75SShiraz Hashim default: 340131f8b75SShiraz Hashim ret = -EINVAL; 341131f8b75SShiraz Hashim break; 342131f8b75SShiraz Hashim } 343131f8b75SShiraz Hashim 344131f8b75SShiraz Hashim return ret; 345131f8b75SShiraz Hashim } 346131f8b75SShiraz Hashim 3470942a71eSRajeev Kumar static struct rtc_class_ops spear_rtc_ops = { 3480942a71eSRajeev Kumar .read_time = spear_rtc_read_time, 3490942a71eSRajeev Kumar .set_time = spear_rtc_set_time, 3500942a71eSRajeev Kumar .read_alarm = spear_rtc_read_alarm, 3510942a71eSRajeev Kumar .set_alarm = spear_rtc_set_alarm, 352131f8b75SShiraz Hashim .alarm_irq_enable = spear_alarm_irq_enable, 3530942a71eSRajeev Kumar }; 3540942a71eSRajeev Kumar 3550942a71eSRajeev Kumar static int __devinit spear_rtc_probe(struct platform_device *pdev) 3560942a71eSRajeev Kumar { 3570942a71eSRajeev Kumar struct resource *res; 3580942a71eSRajeev Kumar struct spear_rtc_config *config; 3590942a71eSRajeev Kumar unsigned int status = 0; 3600942a71eSRajeev Kumar int irq; 3610942a71eSRajeev Kumar 3620942a71eSRajeev Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3630942a71eSRajeev Kumar if (!res) { 3640942a71eSRajeev Kumar dev_err(&pdev->dev, "no resource defined\n"); 3650942a71eSRajeev Kumar return -EBUSY; 3660942a71eSRajeev Kumar } 3670942a71eSRajeev Kumar if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 3680942a71eSRajeev Kumar dev_err(&pdev->dev, "rtc region already claimed\n"); 3690942a71eSRajeev Kumar return -EBUSY; 3700942a71eSRajeev Kumar } 3710942a71eSRajeev Kumar 3720942a71eSRajeev Kumar config = kzalloc(sizeof(*config), GFP_KERNEL); 3730942a71eSRajeev Kumar if (!config) { 3740942a71eSRajeev Kumar dev_err(&pdev->dev, "out of memory\n"); 3750942a71eSRajeev Kumar status = -ENOMEM; 3760942a71eSRajeev Kumar goto err_release_region; 3770942a71eSRajeev Kumar } 3780942a71eSRajeev Kumar 3790942a71eSRajeev Kumar config->clk = clk_get(&pdev->dev, NULL); 3800942a71eSRajeev Kumar if (IS_ERR(config->clk)) { 3810942a71eSRajeev Kumar status = PTR_ERR(config->clk); 3820942a71eSRajeev Kumar goto err_kfree; 3830942a71eSRajeev Kumar } 3840942a71eSRajeev Kumar 3850942a71eSRajeev Kumar status = clk_enable(config->clk); 3860942a71eSRajeev Kumar if (status < 0) 3870942a71eSRajeev Kumar goto err_clk_put; 3880942a71eSRajeev Kumar 3890942a71eSRajeev Kumar config->ioaddr = ioremap(res->start, resource_size(res)); 3900942a71eSRajeev Kumar if (!config->ioaddr) { 3910942a71eSRajeev Kumar dev_err(&pdev->dev, "ioremap fail\n"); 3920942a71eSRajeev Kumar status = -ENOMEM; 3930942a71eSRajeev Kumar goto err_disable_clock; 3940942a71eSRajeev Kumar } 3950942a71eSRajeev Kumar 3960942a71eSRajeev Kumar spin_lock_init(&config->lock); 397*ee6c54caSViresh Kumar platform_set_drvdata(pdev, config); 3980942a71eSRajeev Kumar 399*ee6c54caSViresh Kumar config->rtc = rtc_device_register(pdev->name, &pdev->dev, 400*ee6c54caSViresh Kumar &spear_rtc_ops, THIS_MODULE); 401*ee6c54caSViresh Kumar if (IS_ERR(config->rtc)) { 4020942a71eSRajeev Kumar dev_err(&pdev->dev, "can't register RTC device, err %ld\n", 403*ee6c54caSViresh Kumar PTR_ERR(config->rtc)); 404*ee6c54caSViresh Kumar status = PTR_ERR(config->rtc); 4050942a71eSRajeev Kumar goto err_iounmap; 4060942a71eSRajeev Kumar } 4070942a71eSRajeev Kumar 4080942a71eSRajeev Kumar /* alarm irqs */ 4090942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4100942a71eSRajeev Kumar if (irq < 0) { 4110942a71eSRajeev Kumar dev_err(&pdev->dev, "no update irq?\n"); 4120942a71eSRajeev Kumar status = irq; 4130942a71eSRajeev Kumar goto err_clear_platdata; 4140942a71eSRajeev Kumar } 4150942a71eSRajeev Kumar 416*ee6c54caSViresh Kumar status = request_irq(irq, spear_rtc_irq, 0, pdev->name, config); 4170942a71eSRajeev Kumar if (status) { 4180942a71eSRajeev Kumar dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ 4190942a71eSRajeev Kumar claimed\n", irq); 4200942a71eSRajeev Kumar goto err_clear_platdata; 4210942a71eSRajeev Kumar } 4220942a71eSRajeev Kumar 4230942a71eSRajeev Kumar if (!device_can_wakeup(&pdev->dev)) 4240942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 1); 4250942a71eSRajeev Kumar 4260942a71eSRajeev Kumar return 0; 4270942a71eSRajeev Kumar 4280942a71eSRajeev Kumar err_clear_platdata: 4290942a71eSRajeev Kumar platform_set_drvdata(pdev, NULL); 430*ee6c54caSViresh Kumar rtc_device_unregister(config->rtc); 4310942a71eSRajeev Kumar err_iounmap: 4320942a71eSRajeev Kumar iounmap(config->ioaddr); 4330942a71eSRajeev Kumar err_disable_clock: 4340942a71eSRajeev Kumar clk_disable(config->clk); 4350942a71eSRajeev Kumar err_clk_put: 4360942a71eSRajeev Kumar clk_put(config->clk); 4370942a71eSRajeev Kumar err_kfree: 4380942a71eSRajeev Kumar kfree(config); 4390942a71eSRajeev Kumar err_release_region: 4400942a71eSRajeev Kumar release_mem_region(res->start, resource_size(res)); 4410942a71eSRajeev Kumar 4420942a71eSRajeev Kumar return status; 4430942a71eSRajeev Kumar } 4440942a71eSRajeev Kumar 4450942a71eSRajeev Kumar static int __devexit spear_rtc_remove(struct platform_device *pdev) 4460942a71eSRajeev Kumar { 447*ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4480942a71eSRajeev Kumar int irq; 4490942a71eSRajeev Kumar struct resource *res; 4500942a71eSRajeev Kumar 4510942a71eSRajeev Kumar /* leave rtc running, but disable irqs */ 4520942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4530942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 0); 4540942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4550942a71eSRajeev Kumar if (irq) 4560942a71eSRajeev Kumar free_irq(irq, pdev); 4570942a71eSRajeev Kumar clk_disable(config->clk); 4580942a71eSRajeev Kumar clk_put(config->clk); 4590942a71eSRajeev Kumar iounmap(config->ioaddr); 4600942a71eSRajeev Kumar kfree(config); 4610942a71eSRajeev Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4620942a71eSRajeev Kumar if (res) 4630942a71eSRajeev Kumar release_mem_region(res->start, resource_size(res)); 4640942a71eSRajeev Kumar platform_set_drvdata(pdev, NULL); 465*ee6c54caSViresh Kumar rtc_device_unregister(config->rtc); 4660942a71eSRajeev Kumar 4670942a71eSRajeev Kumar return 0; 4680942a71eSRajeev Kumar } 4690942a71eSRajeev Kumar 4700942a71eSRajeev Kumar #ifdef CONFIG_PM 4710942a71eSRajeev Kumar 4720942a71eSRajeev Kumar static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) 4730942a71eSRajeev Kumar { 474*ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4750942a71eSRajeev Kumar int irq; 4760942a71eSRajeev Kumar 4770942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 478cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 479cd0e08a8SDeepak Sikri if (!enable_irq_wake(irq)) 480cd0e08a8SDeepak Sikri config->irq_wake = 1; 481cd0e08a8SDeepak Sikri } else { 4820942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4830942a71eSRajeev Kumar clk_disable(config->clk); 4840942a71eSRajeev Kumar } 4850942a71eSRajeev Kumar 4860942a71eSRajeev Kumar return 0; 4870942a71eSRajeev Kumar } 4880942a71eSRajeev Kumar 4890942a71eSRajeev Kumar static int spear_rtc_resume(struct platform_device *pdev) 4900942a71eSRajeev Kumar { 491*ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4920942a71eSRajeev Kumar int irq; 4930942a71eSRajeev Kumar 4940942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4950942a71eSRajeev Kumar 496cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 497cd0e08a8SDeepak Sikri if (config->irq_wake) { 4980942a71eSRajeev Kumar disable_irq_wake(irq); 499cd0e08a8SDeepak Sikri config->irq_wake = 0; 500cd0e08a8SDeepak Sikri } 501cd0e08a8SDeepak Sikri } else { 5020942a71eSRajeev Kumar clk_enable(config->clk); 5030942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 5040942a71eSRajeev Kumar } 5050942a71eSRajeev Kumar 5060942a71eSRajeev Kumar return 0; 5070942a71eSRajeev Kumar } 5080942a71eSRajeev Kumar 5090942a71eSRajeev Kumar #else 5100942a71eSRajeev Kumar #define spear_rtc_suspend NULL 5110942a71eSRajeev Kumar #define spear_rtc_resume NULL 5120942a71eSRajeev Kumar #endif 5130942a71eSRajeev Kumar 5140942a71eSRajeev Kumar static void spear_rtc_shutdown(struct platform_device *pdev) 5150942a71eSRajeev Kumar { 516*ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 5170942a71eSRajeev Kumar 5180942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 5190942a71eSRajeev Kumar clk_disable(config->clk); 5200942a71eSRajeev Kumar } 5210942a71eSRajeev Kumar 5220942a71eSRajeev Kumar static struct platform_driver spear_rtc_driver = { 5230942a71eSRajeev Kumar .probe = spear_rtc_probe, 5240942a71eSRajeev Kumar .remove = __devexit_p(spear_rtc_remove), 5250942a71eSRajeev Kumar .suspend = spear_rtc_suspend, 5260942a71eSRajeev Kumar .resume = spear_rtc_resume, 5270942a71eSRajeev Kumar .shutdown = spear_rtc_shutdown, 5280942a71eSRajeev Kumar .driver = { 5290942a71eSRajeev Kumar .name = "rtc-spear", 5300942a71eSRajeev Kumar }, 5310942a71eSRajeev Kumar }; 5320942a71eSRajeev Kumar 5330c4eae66SAxel Lin module_platform_driver(spear_rtc_driver); 5340942a71eSRajeev Kumar 5350942a71eSRajeev Kumar MODULE_ALIAS("platform:rtc-spear"); 5360942a71eSRajeev Kumar MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 5370942a71eSRajeev Kumar MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); 5380942a71eSRajeev Kumar MODULE_LICENSE("GPL"); 539