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 { 800942a71eSRajeev Kumar struct clk *clk; 810942a71eSRajeev Kumar spinlock_t lock; 820942a71eSRajeev Kumar void __iomem *ioaddr; 830942a71eSRajeev Kumar }; 840942a71eSRajeev Kumar 850942a71eSRajeev Kumar static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) 860942a71eSRajeev Kumar { 870942a71eSRajeev Kumar unsigned int val; 880942a71eSRajeev Kumar unsigned long flags; 890942a71eSRajeev Kumar 900942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 910942a71eSRajeev Kumar val = readl(config->ioaddr + STATUS_REG); 920942a71eSRajeev Kumar val |= RTC_INT_MASK; 930942a71eSRajeev Kumar writel(val, config->ioaddr + STATUS_REG); 940942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 950942a71eSRajeev Kumar } 960942a71eSRajeev Kumar 970942a71eSRajeev Kumar static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config) 980942a71eSRajeev Kumar { 990942a71eSRajeev Kumar unsigned int val; 1000942a71eSRajeev Kumar 1010942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1020942a71eSRajeev Kumar if (!(val & INT_ENABLE)) { 1030942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1040942a71eSRajeev Kumar val |= INT_ENABLE; 1050942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1060942a71eSRajeev Kumar } 1070942a71eSRajeev Kumar } 1080942a71eSRajeev Kumar 1090942a71eSRajeev Kumar static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config) 1100942a71eSRajeev Kumar { 1110942a71eSRajeev Kumar unsigned int val; 1120942a71eSRajeev Kumar 1130942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1140942a71eSRajeev Kumar if (val & INT_ENABLE) { 1150942a71eSRajeev Kumar val &= ~INT_ENABLE; 1160942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1170942a71eSRajeev Kumar } 1180942a71eSRajeev Kumar } 1190942a71eSRajeev Kumar 1200942a71eSRajeev Kumar static inline int is_write_complete(struct spear_rtc_config *config) 1210942a71eSRajeev Kumar { 1220942a71eSRajeev Kumar int ret = 0; 1230942a71eSRajeev Kumar unsigned long flags; 1240942a71eSRajeev Kumar 1250942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1260942a71eSRajeev Kumar if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL) 1270942a71eSRajeev Kumar ret = -EIO; 1280942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1290942a71eSRajeev Kumar 1300942a71eSRajeev Kumar return ret; 1310942a71eSRajeev Kumar } 1320942a71eSRajeev Kumar 1330942a71eSRajeev Kumar static void rtc_wait_not_busy(struct spear_rtc_config *config) 1340942a71eSRajeev Kumar { 1350942a71eSRajeev Kumar int status, count = 0; 1360942a71eSRajeev Kumar unsigned long flags; 1370942a71eSRajeev Kumar 1380942a71eSRajeev Kumar /* Assuming BUSY may stay active for 80 msec) */ 1390942a71eSRajeev Kumar for (count = 0; count < 80; count++) { 1400942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1410942a71eSRajeev Kumar status = readl(config->ioaddr + STATUS_REG); 1420942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1430942a71eSRajeev Kumar if ((status & STATUS_BUSY) == 0) 1440942a71eSRajeev Kumar break; 1450942a71eSRajeev Kumar /* check status busy, after each msec */ 1460942a71eSRajeev Kumar msleep(1); 1470942a71eSRajeev Kumar } 1480942a71eSRajeev Kumar } 1490942a71eSRajeev Kumar 1500942a71eSRajeev Kumar static irqreturn_t spear_rtc_irq(int irq, void *dev_id) 1510942a71eSRajeev Kumar { 1520942a71eSRajeev Kumar struct rtc_device *rtc = (struct rtc_device *)dev_id; 1530942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 1540942a71eSRajeev Kumar unsigned long flags, events = 0; 1550942a71eSRajeev Kumar unsigned int irq_data; 1560942a71eSRajeev Kumar 1570942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1580942a71eSRajeev Kumar irq_data = readl(config->ioaddr + STATUS_REG); 1590942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1600942a71eSRajeev Kumar 1610942a71eSRajeev Kumar if ((irq_data & RTC_INT_MASK)) { 1620942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1630942a71eSRajeev Kumar events = RTC_IRQF | RTC_AF; 1640942a71eSRajeev Kumar rtc_update_irq(rtc, 1, events); 1650942a71eSRajeev Kumar return IRQ_HANDLED; 1660942a71eSRajeev Kumar } else 1670942a71eSRajeev Kumar return IRQ_NONE; 1680942a71eSRajeev Kumar 1690942a71eSRajeev Kumar } 1700942a71eSRajeev Kumar 1710942a71eSRajeev Kumar static int tm2bcd(struct rtc_time *tm) 1720942a71eSRajeev Kumar { 1730942a71eSRajeev Kumar if (rtc_valid_tm(tm) != 0) 1740942a71eSRajeev Kumar return -EINVAL; 1750942a71eSRajeev Kumar tm->tm_sec = bin2bcd(tm->tm_sec); 1760942a71eSRajeev Kumar tm->tm_min = bin2bcd(tm->tm_min); 1770942a71eSRajeev Kumar tm->tm_hour = bin2bcd(tm->tm_hour); 1780942a71eSRajeev Kumar tm->tm_mday = bin2bcd(tm->tm_mday); 1790942a71eSRajeev Kumar tm->tm_mon = bin2bcd(tm->tm_mon + 1); 1800942a71eSRajeev Kumar tm->tm_year = bin2bcd(tm->tm_year); 1810942a71eSRajeev Kumar 1820942a71eSRajeev Kumar return 0; 1830942a71eSRajeev Kumar } 1840942a71eSRajeev Kumar 1850942a71eSRajeev Kumar static void bcd2tm(struct rtc_time *tm) 1860942a71eSRajeev Kumar { 1870942a71eSRajeev Kumar tm->tm_sec = bcd2bin(tm->tm_sec); 1880942a71eSRajeev Kumar tm->tm_min = bcd2bin(tm->tm_min); 1890942a71eSRajeev Kumar tm->tm_hour = bcd2bin(tm->tm_hour); 1900942a71eSRajeev Kumar tm->tm_mday = bcd2bin(tm->tm_mday); 1910942a71eSRajeev Kumar tm->tm_mon = bcd2bin(tm->tm_mon) - 1; 1920942a71eSRajeev Kumar /* epoch == 1900 */ 1930942a71eSRajeev Kumar tm->tm_year = bcd2bin(tm->tm_year); 1940942a71eSRajeev Kumar } 1950942a71eSRajeev Kumar 1960942a71eSRajeev Kumar /* 1970942a71eSRajeev Kumar * spear_rtc_read_time - set the time 1980942a71eSRajeev Kumar * @dev: rtc device in use 1990942a71eSRajeev Kumar * @tm: holds date and time 2000942a71eSRajeev Kumar * 2010942a71eSRajeev Kumar * This function read time and date. On success it will return 0 2020942a71eSRajeev Kumar * otherwise -ve error is returned. 2030942a71eSRajeev Kumar */ 2040942a71eSRajeev Kumar static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) 2050942a71eSRajeev Kumar { 2060942a71eSRajeev Kumar struct platform_device *pdev = to_platform_device(dev); 2070942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 2080942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 2090942a71eSRajeev Kumar unsigned int time, date; 2100942a71eSRajeev Kumar 2110942a71eSRajeev Kumar /* we don't report wday/yday/isdst ... */ 2120942a71eSRajeev Kumar rtc_wait_not_busy(config); 2130942a71eSRajeev Kumar 2140942a71eSRajeev Kumar time = readl(config->ioaddr + TIME_REG); 2150942a71eSRajeev Kumar date = readl(config->ioaddr + DATE_REG); 2160942a71eSRajeev Kumar tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2170942a71eSRajeev Kumar tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2180942a71eSRajeev Kumar tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2190942a71eSRajeev Kumar tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2200942a71eSRajeev Kumar tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2210942a71eSRajeev Kumar tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2220942a71eSRajeev Kumar 2230942a71eSRajeev Kumar bcd2tm(tm); 2240942a71eSRajeev Kumar return 0; 2250942a71eSRajeev Kumar } 2260942a71eSRajeev Kumar 2270942a71eSRajeev Kumar /* 2280942a71eSRajeev Kumar * spear_rtc_set_time - set the time 2290942a71eSRajeev Kumar * @dev: rtc device in use 2300942a71eSRajeev Kumar * @tm: holds date and time 2310942a71eSRajeev Kumar * 2320942a71eSRajeev Kumar * This function set time and date. On success it will return 0 2330942a71eSRajeev Kumar * otherwise -ve error is returned. 2340942a71eSRajeev Kumar */ 2350942a71eSRajeev Kumar static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) 2360942a71eSRajeev Kumar { 2370942a71eSRajeev Kumar struct platform_device *pdev = to_platform_device(dev); 2380942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 2390942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 2400942a71eSRajeev Kumar unsigned int time, date, err = 0; 2410942a71eSRajeev Kumar 2420942a71eSRajeev Kumar if (tm2bcd(tm) < 0) 2430942a71eSRajeev Kumar return -EINVAL; 2440942a71eSRajeev Kumar 2450942a71eSRajeev Kumar rtc_wait_not_busy(config); 2460942a71eSRajeev Kumar time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | 2470942a71eSRajeev Kumar (tm->tm_hour << HOUR_SHIFT); 2480942a71eSRajeev Kumar date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | 2490942a71eSRajeev Kumar (tm->tm_year << YEAR_SHIFT); 2500942a71eSRajeev Kumar writel(time, config->ioaddr + TIME_REG); 2510942a71eSRajeev Kumar writel(date, config->ioaddr + DATE_REG); 2520942a71eSRajeev Kumar err = is_write_complete(config); 2530942a71eSRajeev Kumar if (err < 0) 2540942a71eSRajeev Kumar return err; 2550942a71eSRajeev Kumar 2560942a71eSRajeev Kumar return 0; 2570942a71eSRajeev Kumar } 2580942a71eSRajeev Kumar 2590942a71eSRajeev Kumar /* 2600942a71eSRajeev Kumar * spear_rtc_read_alarm - read the alarm time 2610942a71eSRajeev Kumar * @dev: rtc device in use 2620942a71eSRajeev Kumar * @alm: holds alarm date and time 2630942a71eSRajeev Kumar * 2640942a71eSRajeev Kumar * This function read alarm time and date. On success it will return 0 2650942a71eSRajeev Kumar * otherwise -ve error is returned. 2660942a71eSRajeev Kumar */ 2670942a71eSRajeev Kumar static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2680942a71eSRajeev Kumar { 2690942a71eSRajeev Kumar struct platform_device *pdev = to_platform_device(dev); 2700942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 2710942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 2720942a71eSRajeev Kumar unsigned int time, date; 2730942a71eSRajeev Kumar 2740942a71eSRajeev Kumar rtc_wait_not_busy(config); 2750942a71eSRajeev Kumar 2760942a71eSRajeev Kumar time = readl(config->ioaddr + ALARM_TIME_REG); 2770942a71eSRajeev Kumar date = readl(config->ioaddr + ALARM_DATE_REG); 2780942a71eSRajeev Kumar alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2790942a71eSRajeev Kumar alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2800942a71eSRajeev Kumar alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2810942a71eSRajeev Kumar alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2820942a71eSRajeev Kumar alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2830942a71eSRajeev Kumar alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2840942a71eSRajeev Kumar 2850942a71eSRajeev Kumar bcd2tm(&alm->time); 2860942a71eSRajeev Kumar alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; 2870942a71eSRajeev Kumar 2880942a71eSRajeev Kumar return 0; 2890942a71eSRajeev Kumar } 2900942a71eSRajeev Kumar 2910942a71eSRajeev Kumar /* 2920942a71eSRajeev Kumar * spear_rtc_set_alarm - set the alarm time 2930942a71eSRajeev Kumar * @dev: rtc device in use 2940942a71eSRajeev Kumar * @alm: holds alarm date and time 2950942a71eSRajeev Kumar * 2960942a71eSRajeev Kumar * This function set alarm time and date. On success it will return 0 2970942a71eSRajeev Kumar * otherwise -ve error is returned. 2980942a71eSRajeev Kumar */ 2990942a71eSRajeev Kumar static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 3000942a71eSRajeev Kumar { 3010942a71eSRajeev Kumar struct platform_device *pdev = to_platform_device(dev); 3020942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 3030942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 3040942a71eSRajeev Kumar unsigned int time, date, err = 0; 3050942a71eSRajeev Kumar 3060942a71eSRajeev Kumar if (tm2bcd(&alm->time) < 0) 3070942a71eSRajeev Kumar return -EINVAL; 3080942a71eSRajeev Kumar 3090942a71eSRajeev Kumar rtc_wait_not_busy(config); 3100942a71eSRajeev Kumar 3110942a71eSRajeev Kumar time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << 3120942a71eSRajeev Kumar MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); 3130942a71eSRajeev Kumar date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << 3140942a71eSRajeev Kumar MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); 3150942a71eSRajeev Kumar 3160942a71eSRajeev Kumar writel(time, config->ioaddr + ALARM_TIME_REG); 3170942a71eSRajeev Kumar writel(date, config->ioaddr + ALARM_DATE_REG); 3180942a71eSRajeev Kumar err = is_write_complete(config); 3190942a71eSRajeev Kumar if (err < 0) 3200942a71eSRajeev Kumar return err; 3210942a71eSRajeev Kumar 3220942a71eSRajeev Kumar if (alm->enabled) 3230942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 3240942a71eSRajeev Kumar else 3250942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 3260942a71eSRajeev Kumar 3270942a71eSRajeev Kumar return 0; 3280942a71eSRajeev Kumar } 3290942a71eSRajeev Kumar static struct rtc_class_ops spear_rtc_ops = { 3300942a71eSRajeev Kumar .read_time = spear_rtc_read_time, 3310942a71eSRajeev Kumar .set_time = spear_rtc_set_time, 3320942a71eSRajeev Kumar .read_alarm = spear_rtc_read_alarm, 3330942a71eSRajeev Kumar .set_alarm = spear_rtc_set_alarm, 3340942a71eSRajeev Kumar }; 3350942a71eSRajeev Kumar 3360942a71eSRajeev Kumar static int __devinit spear_rtc_probe(struct platform_device *pdev) 3370942a71eSRajeev Kumar { 3380942a71eSRajeev Kumar struct resource *res; 3390942a71eSRajeev Kumar struct rtc_device *rtc; 3400942a71eSRajeev Kumar struct spear_rtc_config *config; 3410942a71eSRajeev Kumar unsigned int status = 0; 3420942a71eSRajeev Kumar int irq; 3430942a71eSRajeev Kumar 3440942a71eSRajeev Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3450942a71eSRajeev Kumar if (!res) { 3460942a71eSRajeev Kumar dev_err(&pdev->dev, "no resource defined\n"); 3470942a71eSRajeev Kumar return -EBUSY; 3480942a71eSRajeev Kumar } 3490942a71eSRajeev Kumar if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 3500942a71eSRajeev Kumar dev_err(&pdev->dev, "rtc region already claimed\n"); 3510942a71eSRajeev Kumar return -EBUSY; 3520942a71eSRajeev Kumar } 3530942a71eSRajeev Kumar 3540942a71eSRajeev Kumar config = kzalloc(sizeof(*config), GFP_KERNEL); 3550942a71eSRajeev Kumar if (!config) { 3560942a71eSRajeev Kumar dev_err(&pdev->dev, "out of memory\n"); 3570942a71eSRajeev Kumar status = -ENOMEM; 3580942a71eSRajeev Kumar goto err_release_region; 3590942a71eSRajeev Kumar } 3600942a71eSRajeev Kumar 3610942a71eSRajeev Kumar config->clk = clk_get(&pdev->dev, NULL); 3620942a71eSRajeev Kumar if (IS_ERR(config->clk)) { 3630942a71eSRajeev Kumar status = PTR_ERR(config->clk); 3640942a71eSRajeev Kumar goto err_kfree; 3650942a71eSRajeev Kumar } 3660942a71eSRajeev Kumar 3670942a71eSRajeev Kumar status = clk_enable(config->clk); 3680942a71eSRajeev Kumar if (status < 0) 3690942a71eSRajeev Kumar goto err_clk_put; 3700942a71eSRajeev Kumar 3710942a71eSRajeev Kumar config->ioaddr = ioremap(res->start, resource_size(res)); 3720942a71eSRajeev Kumar if (!config->ioaddr) { 3730942a71eSRajeev Kumar dev_err(&pdev->dev, "ioremap fail\n"); 3740942a71eSRajeev Kumar status = -ENOMEM; 3750942a71eSRajeev Kumar goto err_disable_clock; 3760942a71eSRajeev Kumar } 3770942a71eSRajeev Kumar 3780942a71eSRajeev Kumar spin_lock_init(&config->lock); 3790942a71eSRajeev Kumar 3800942a71eSRajeev Kumar rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops, 3810942a71eSRajeev Kumar THIS_MODULE); 3820942a71eSRajeev Kumar if (IS_ERR(rtc)) { 3830942a71eSRajeev Kumar dev_err(&pdev->dev, "can't register RTC device, err %ld\n", 3840942a71eSRajeev Kumar PTR_ERR(rtc)); 3850942a71eSRajeev Kumar status = PTR_ERR(rtc); 3860942a71eSRajeev Kumar goto err_iounmap; 3870942a71eSRajeev Kumar } 3880942a71eSRajeev Kumar 3890942a71eSRajeev Kumar platform_set_drvdata(pdev, rtc); 3900942a71eSRajeev Kumar dev_set_drvdata(&rtc->dev, config); 3910942a71eSRajeev Kumar 3920942a71eSRajeev Kumar /* alarm irqs */ 3930942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 3940942a71eSRajeev Kumar if (irq < 0) { 3950942a71eSRajeev Kumar dev_err(&pdev->dev, "no update irq?\n"); 3960942a71eSRajeev Kumar status = irq; 3970942a71eSRajeev Kumar goto err_clear_platdata; 3980942a71eSRajeev Kumar } 3990942a71eSRajeev Kumar 4000942a71eSRajeev Kumar status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc); 4010942a71eSRajeev Kumar if (status) { 4020942a71eSRajeev Kumar dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ 4030942a71eSRajeev Kumar claimed\n", irq); 4040942a71eSRajeev Kumar goto err_clear_platdata; 4050942a71eSRajeev Kumar } 4060942a71eSRajeev Kumar 4070942a71eSRajeev Kumar if (!device_can_wakeup(&pdev->dev)) 4080942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 1); 4090942a71eSRajeev Kumar 4100942a71eSRajeev Kumar return 0; 4110942a71eSRajeev Kumar 4120942a71eSRajeev Kumar err_clear_platdata: 4130942a71eSRajeev Kumar platform_set_drvdata(pdev, NULL); 4140942a71eSRajeev Kumar dev_set_drvdata(&rtc->dev, NULL); 4150942a71eSRajeev Kumar rtc_device_unregister(rtc); 4160942a71eSRajeev Kumar err_iounmap: 4170942a71eSRajeev Kumar iounmap(config->ioaddr); 4180942a71eSRajeev Kumar err_disable_clock: 4190942a71eSRajeev Kumar clk_disable(config->clk); 4200942a71eSRajeev Kumar err_clk_put: 4210942a71eSRajeev Kumar clk_put(config->clk); 4220942a71eSRajeev Kumar err_kfree: 4230942a71eSRajeev Kumar kfree(config); 4240942a71eSRajeev Kumar err_release_region: 4250942a71eSRajeev Kumar release_mem_region(res->start, resource_size(res)); 4260942a71eSRajeev Kumar 4270942a71eSRajeev Kumar return status; 4280942a71eSRajeev Kumar } 4290942a71eSRajeev Kumar 4300942a71eSRajeev Kumar static int __devexit spear_rtc_remove(struct platform_device *pdev) 4310942a71eSRajeev Kumar { 4320942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 4330942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 4340942a71eSRajeev Kumar int irq; 4350942a71eSRajeev Kumar struct resource *res; 4360942a71eSRajeev Kumar 4370942a71eSRajeev Kumar /* leave rtc running, but disable irqs */ 4380942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4390942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 0); 4400942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4410942a71eSRajeev Kumar if (irq) 4420942a71eSRajeev Kumar free_irq(irq, pdev); 4430942a71eSRajeev Kumar clk_disable(config->clk); 4440942a71eSRajeev Kumar clk_put(config->clk); 4450942a71eSRajeev Kumar iounmap(config->ioaddr); 4460942a71eSRajeev Kumar kfree(config); 4470942a71eSRajeev Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4480942a71eSRajeev Kumar if (res) 4490942a71eSRajeev Kumar release_mem_region(res->start, resource_size(res)); 4500942a71eSRajeev Kumar platform_set_drvdata(pdev, NULL); 4510942a71eSRajeev Kumar dev_set_drvdata(&rtc->dev, NULL); 4520942a71eSRajeev Kumar rtc_device_unregister(rtc); 4530942a71eSRajeev Kumar 4540942a71eSRajeev Kumar return 0; 4550942a71eSRajeev Kumar } 4560942a71eSRajeev Kumar 4570942a71eSRajeev Kumar #ifdef CONFIG_PM 4580942a71eSRajeev Kumar 4590942a71eSRajeev Kumar static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) 4600942a71eSRajeev Kumar { 4610942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 4620942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 4630942a71eSRajeev Kumar int irq; 4640942a71eSRajeev Kumar 4650942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4660942a71eSRajeev Kumar if (device_may_wakeup(&pdev->dev)) 4670942a71eSRajeev Kumar enable_irq_wake(irq); 4680942a71eSRajeev Kumar else { 4690942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4700942a71eSRajeev Kumar clk_disable(config->clk); 4710942a71eSRajeev Kumar } 4720942a71eSRajeev Kumar 4730942a71eSRajeev Kumar return 0; 4740942a71eSRajeev Kumar } 4750942a71eSRajeev Kumar 4760942a71eSRajeev Kumar static int spear_rtc_resume(struct platform_device *pdev) 4770942a71eSRajeev Kumar { 4780942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 4790942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 4800942a71eSRajeev Kumar int irq; 4810942a71eSRajeev Kumar 4820942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4830942a71eSRajeev Kumar 4840942a71eSRajeev Kumar if (device_may_wakeup(&pdev->dev)) 4850942a71eSRajeev Kumar disable_irq_wake(irq); 4860942a71eSRajeev Kumar else { 4870942a71eSRajeev Kumar clk_enable(config->clk); 4880942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 4890942a71eSRajeev Kumar } 4900942a71eSRajeev Kumar 4910942a71eSRajeev Kumar return 0; 4920942a71eSRajeev Kumar } 4930942a71eSRajeev Kumar 4940942a71eSRajeev Kumar #else 4950942a71eSRajeev Kumar #define spear_rtc_suspend NULL 4960942a71eSRajeev Kumar #define spear_rtc_resume NULL 4970942a71eSRajeev Kumar #endif 4980942a71eSRajeev Kumar 4990942a71eSRajeev Kumar static void spear_rtc_shutdown(struct platform_device *pdev) 5000942a71eSRajeev Kumar { 5010942a71eSRajeev Kumar struct rtc_device *rtc = platform_get_drvdata(pdev); 5020942a71eSRajeev Kumar struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); 5030942a71eSRajeev Kumar 5040942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 5050942a71eSRajeev Kumar clk_disable(config->clk); 5060942a71eSRajeev Kumar } 5070942a71eSRajeev Kumar 5080942a71eSRajeev Kumar static struct platform_driver spear_rtc_driver = { 5090942a71eSRajeev Kumar .probe = spear_rtc_probe, 5100942a71eSRajeev Kumar .remove = __devexit_p(spear_rtc_remove), 5110942a71eSRajeev Kumar .suspend = spear_rtc_suspend, 5120942a71eSRajeev Kumar .resume = spear_rtc_resume, 5130942a71eSRajeev Kumar .shutdown = spear_rtc_shutdown, 5140942a71eSRajeev Kumar .driver = { 5150942a71eSRajeev Kumar .name = "rtc-spear", 5160942a71eSRajeev Kumar }, 5170942a71eSRajeev Kumar }; 5180942a71eSRajeev Kumar 519*0c4eae66SAxel Lin module_platform_driver(spear_rtc_driver); 5200942a71eSRajeev Kumar 5210942a71eSRajeev Kumar MODULE_ALIAS("platform:rtc-spear"); 5220942a71eSRajeev Kumar MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 5230942a71eSRajeev Kumar MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); 5240942a71eSRajeev Kumar MODULE_LICENSE("GPL"); 525