xref: /linux/drivers/rtc/rtc-spear.c (revision ee6c54ca64416c75aa6f5021e139f270192bae49)
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