19734a1aeSNobuhiro Iwamatsu // SPDX-License-Identifier: GPL-2.0-only
20942a71eSRajeev Kumar /*
30942a71eSRajeev Kumar * drivers/rtc/rtc-spear.c
40942a71eSRajeev Kumar *
50942a71eSRajeev Kumar * Copyright (C) 2010 ST Microelectronics
60942a71eSRajeev Kumar * Rajeev Kumar<rajeev-dlh.kumar@st.com>
70942a71eSRajeev Kumar */
80942a71eSRajeev Kumar
90942a71eSRajeev Kumar #include <linux/bcd.h>
100942a71eSRajeev Kumar #include <linux/clk.h>
110942a71eSRajeev Kumar #include <linux/delay.h>
120942a71eSRajeev Kumar #include <linux/init.h>
130942a71eSRajeev Kumar #include <linux/io.h>
140942a71eSRajeev Kumar #include <linux/irq.h>
150942a71eSRajeev Kumar #include <linux/module.h>
160108c4ffSViresh Kumar #include <linux/of.h>
170942a71eSRajeev Kumar #include <linux/platform_device.h>
180942a71eSRajeev Kumar #include <linux/rtc.h>
190942a71eSRajeev Kumar #include <linux/slab.h>
200942a71eSRajeev Kumar #include <linux/spinlock.h>
210942a71eSRajeev Kumar
220942a71eSRajeev Kumar /* RTC registers */
230942a71eSRajeev Kumar #define TIME_REG 0x00
240942a71eSRajeev Kumar #define DATE_REG 0x04
250942a71eSRajeev Kumar #define ALARM_TIME_REG 0x08
260942a71eSRajeev Kumar #define ALARM_DATE_REG 0x0C
270942a71eSRajeev Kumar #define CTRL_REG 0x10
280942a71eSRajeev Kumar #define STATUS_REG 0x14
290942a71eSRajeev Kumar
300942a71eSRajeev Kumar /* TIME_REG & ALARM_TIME_REG */
310942a71eSRajeev Kumar #define SECONDS_UNITS (0xf<<0) /* seconds units position */
320942a71eSRajeev Kumar #define SECONDS_TENS (0x7<<4) /* seconds tens position */
330942a71eSRajeev Kumar #define MINUTES_UNITS (0xf<<8) /* minutes units position */
340942a71eSRajeev Kumar #define MINUTES_TENS (0x7<<12) /* minutes tens position */
350942a71eSRajeev Kumar #define HOURS_UNITS (0xf<<16) /* hours units position */
360942a71eSRajeev Kumar #define HOURS_TENS (0x3<<20) /* hours tens position */
370942a71eSRajeev Kumar
380942a71eSRajeev Kumar /* DATE_REG & ALARM_DATE_REG */
390942a71eSRajeev Kumar #define DAYS_UNITS (0xf<<0) /* days units position */
400942a71eSRajeev Kumar #define DAYS_TENS (0x3<<4) /* days tens position */
410942a71eSRajeev Kumar #define MONTHS_UNITS (0xf<<8) /* months units position */
420942a71eSRajeev Kumar #define MONTHS_TENS (0x1<<12) /* months tens position */
430942a71eSRajeev Kumar #define YEARS_UNITS (0xf<<16) /* years units position */
440942a71eSRajeev Kumar #define YEARS_TENS (0xf<<20) /* years tens position */
450942a71eSRajeev Kumar #define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */
460942a71eSRajeev Kumar #define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */
470942a71eSRajeev Kumar
480942a71eSRajeev Kumar /* MASK SHIFT TIME_REG & ALARM_TIME_REG*/
490942a71eSRajeev Kumar #define SECOND_SHIFT 0x00 /* seconds units */
500942a71eSRajeev Kumar #define MINUTE_SHIFT 0x08 /* minutes units position */
510942a71eSRajeev Kumar #define HOUR_SHIFT 0x10 /* hours units position */
520942a71eSRajeev Kumar #define MDAY_SHIFT 0x00 /* Month day shift */
530942a71eSRajeev Kumar #define MONTH_SHIFT 0x08 /* Month shift */
540942a71eSRajeev Kumar #define YEAR_SHIFT 0x10 /* Year shift */
550942a71eSRajeev Kumar
560942a71eSRajeev Kumar #define SECOND_MASK 0x7F
570942a71eSRajeev Kumar #define MIN_MASK 0x7F
580942a71eSRajeev Kumar #define HOUR_MASK 0x3F
590942a71eSRajeev Kumar #define DAY_MASK 0x3F
600942a71eSRajeev Kumar #define MONTH_MASK 0x7F
610942a71eSRajeev Kumar #define YEAR_MASK 0xFFFF
620942a71eSRajeev Kumar
630942a71eSRajeev Kumar /* date reg equal to time reg, for debug only */
640942a71eSRajeev Kumar #define TIME_BYP (1<<9)
650942a71eSRajeev Kumar #define INT_ENABLE (1<<31) /* interrupt enable */
660942a71eSRajeev Kumar
670942a71eSRajeev Kumar /* STATUS_REG */
680942a71eSRajeev Kumar #define CLK_UNCONNECTED (1<<0)
690942a71eSRajeev Kumar #define PEND_WR_TIME (1<<2)
700942a71eSRajeev Kumar #define PEND_WR_DATE (1<<3)
710942a71eSRajeev Kumar #define LOST_WR_TIME (1<<4)
720942a71eSRajeev Kumar #define LOST_WR_DATE (1<<5)
730942a71eSRajeev Kumar #define RTC_INT_MASK (1<<31)
740942a71eSRajeev Kumar #define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE)
750942a71eSRajeev Kumar #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE)
760942a71eSRajeev Kumar
770942a71eSRajeev Kumar struct spear_rtc_config {
78ee6c54caSViresh Kumar struct rtc_device *rtc;
790942a71eSRajeev Kumar struct clk *clk;
800942a71eSRajeev Kumar spinlock_t lock;
810942a71eSRajeev Kumar void __iomem *ioaddr;
82cd0e08a8SDeepak Sikri unsigned int irq_wake;
830942a71eSRajeev Kumar };
840942a71eSRajeev Kumar
spear_rtc_clear_interrupt(struct spear_rtc_config * config)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
spear_rtc_enable_interrupt(struct spear_rtc_config * config)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
spear_rtc_disable_interrupt(struct spear_rtc_config * config)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
is_write_complete(struct spear_rtc_config * config)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
rtc_wait_not_busy(struct spear_rtc_config * config)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
spear_rtc_irq(int irq,void * dev_id)1500942a71eSRajeev Kumar static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
1510942a71eSRajeev Kumar {
152ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_id;
153880f25d6STian Tao unsigned long events = 0;
1540942a71eSRajeev Kumar unsigned int irq_data;
1550942a71eSRajeev Kumar
156880f25d6STian Tao spin_lock(&config->lock);
1570942a71eSRajeev Kumar irq_data = readl(config->ioaddr + STATUS_REG);
158880f25d6STian Tao spin_unlock(&config->lock);
1590942a71eSRajeev Kumar
1600942a71eSRajeev Kumar if ((irq_data & RTC_INT_MASK)) {
1610942a71eSRajeev Kumar spear_rtc_clear_interrupt(config);
1620942a71eSRajeev Kumar events = RTC_IRQF | RTC_AF;
163ee6c54caSViresh Kumar rtc_update_irq(config->rtc, 1, events);
1640942a71eSRajeev Kumar return IRQ_HANDLED;
1650942a71eSRajeev Kumar } else
1660942a71eSRajeev Kumar return IRQ_NONE;
1670942a71eSRajeev Kumar
1680942a71eSRajeev Kumar }
1690942a71eSRajeev Kumar
tm2bcd(struct rtc_time * tm)1709fb7aa75SAlexandre Belloni static void tm2bcd(struct rtc_time *tm)
1710942a71eSRajeev Kumar {
1720942a71eSRajeev Kumar tm->tm_sec = bin2bcd(tm->tm_sec);
1730942a71eSRajeev Kumar tm->tm_min = bin2bcd(tm->tm_min);
1740942a71eSRajeev Kumar tm->tm_hour = bin2bcd(tm->tm_hour);
1750942a71eSRajeev Kumar tm->tm_mday = bin2bcd(tm->tm_mday);
1760942a71eSRajeev Kumar tm->tm_mon = bin2bcd(tm->tm_mon + 1);
1770942a71eSRajeev Kumar tm->tm_year = bin2bcd(tm->tm_year);
1780942a71eSRajeev Kumar }
1790942a71eSRajeev Kumar
bcd2tm(struct rtc_time * tm)1800942a71eSRajeev Kumar static void bcd2tm(struct rtc_time *tm)
1810942a71eSRajeev Kumar {
1820942a71eSRajeev Kumar tm->tm_sec = bcd2bin(tm->tm_sec);
1830942a71eSRajeev Kumar tm->tm_min = bcd2bin(tm->tm_min);
1840942a71eSRajeev Kumar tm->tm_hour = bcd2bin(tm->tm_hour);
1850942a71eSRajeev Kumar tm->tm_mday = bcd2bin(tm->tm_mday);
1860942a71eSRajeev Kumar tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
1870942a71eSRajeev Kumar /* epoch == 1900 */
1880942a71eSRajeev Kumar tm->tm_year = bcd2bin(tm->tm_year);
1890942a71eSRajeev Kumar }
1900942a71eSRajeev Kumar
1910942a71eSRajeev Kumar /*
1920942a71eSRajeev Kumar * spear_rtc_read_time - set the time
1930942a71eSRajeev Kumar * @dev: rtc device in use
1940942a71eSRajeev Kumar * @tm: holds date and time
1950942a71eSRajeev Kumar *
1960942a71eSRajeev Kumar * This function read time and date. On success it will return 0
1970942a71eSRajeev Kumar * otherwise -ve error is returned.
1980942a71eSRajeev Kumar */
spear_rtc_read_time(struct device * dev,struct rtc_time * tm)1990942a71eSRajeev Kumar static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
2000942a71eSRajeev Kumar {
201ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev);
2020942a71eSRajeev Kumar unsigned int time, date;
2030942a71eSRajeev Kumar
2040942a71eSRajeev Kumar /* we don't report wday/yday/isdst ... */
2050942a71eSRajeev Kumar rtc_wait_not_busy(config);
2060942a71eSRajeev Kumar
207a87a07a1SAlexandre Belloni do {
2080942a71eSRajeev Kumar time = readl(config->ioaddr + TIME_REG);
2090942a71eSRajeev Kumar date = readl(config->ioaddr + DATE_REG);
210a87a07a1SAlexandre Belloni } while (time == readl(config->ioaddr + TIME_REG));
2110942a71eSRajeev Kumar tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
2120942a71eSRajeev Kumar tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
2130942a71eSRajeev Kumar tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
2140942a71eSRajeev Kumar tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
2150942a71eSRajeev Kumar tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
2160942a71eSRajeev Kumar tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
2170942a71eSRajeev Kumar
2180942a71eSRajeev Kumar bcd2tm(tm);
2190942a71eSRajeev Kumar return 0;
2200942a71eSRajeev Kumar }
2210942a71eSRajeev Kumar
2220942a71eSRajeev Kumar /*
2230942a71eSRajeev Kumar * spear_rtc_set_time - set the time
2240942a71eSRajeev Kumar * @dev: rtc device in use
2250942a71eSRajeev Kumar * @tm: holds date and time
2260942a71eSRajeev Kumar *
2270942a71eSRajeev Kumar * This function set time and date. On success it will return 0
2280942a71eSRajeev Kumar * otherwise -ve error is returned.
2290942a71eSRajeev Kumar */
spear_rtc_set_time(struct device * dev,struct rtc_time * tm)2300942a71eSRajeev Kumar static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
2310942a71eSRajeev Kumar {
232ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev);
233a16e8393SLars-Peter Clausen unsigned int time, date;
2340942a71eSRajeev Kumar
2359fb7aa75SAlexandre Belloni tm2bcd(tm);
2360942a71eSRajeev Kumar
2370942a71eSRajeev Kumar rtc_wait_not_busy(config);
2380942a71eSRajeev Kumar time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) |
2390942a71eSRajeev Kumar (tm->tm_hour << HOUR_SHIFT);
2400942a71eSRajeev Kumar date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) |
2410942a71eSRajeev Kumar (tm->tm_year << YEAR_SHIFT);
2420942a71eSRajeev Kumar writel(time, config->ioaddr + TIME_REG);
2430942a71eSRajeev Kumar writel(date, config->ioaddr + DATE_REG);
2440942a71eSRajeev Kumar
245a16e8393SLars-Peter Clausen return is_write_complete(config);
2460942a71eSRajeev Kumar }
2470942a71eSRajeev Kumar
2480942a71eSRajeev Kumar /*
2490942a71eSRajeev Kumar * spear_rtc_read_alarm - read the alarm time
2500942a71eSRajeev Kumar * @dev: rtc device in use
2510942a71eSRajeev Kumar * @alm: holds alarm date and time
2520942a71eSRajeev Kumar *
2530942a71eSRajeev Kumar * This function read alarm time and date. On success it will return 0
2540942a71eSRajeev Kumar * otherwise -ve error is returned.
2550942a71eSRajeev Kumar */
spear_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alm)2560942a71eSRajeev Kumar static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
2570942a71eSRajeev Kumar {
258ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev);
2590942a71eSRajeev Kumar unsigned int time, date;
2600942a71eSRajeev Kumar
2610942a71eSRajeev Kumar rtc_wait_not_busy(config);
2620942a71eSRajeev Kumar
2630942a71eSRajeev Kumar time = readl(config->ioaddr + ALARM_TIME_REG);
2640942a71eSRajeev Kumar date = readl(config->ioaddr + ALARM_DATE_REG);
2650942a71eSRajeev Kumar alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
2660942a71eSRajeev Kumar alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
2670942a71eSRajeev Kumar alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
2680942a71eSRajeev Kumar alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
2690942a71eSRajeev Kumar alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
2700942a71eSRajeev Kumar alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
2710942a71eSRajeev Kumar
2720942a71eSRajeev Kumar bcd2tm(&alm->time);
2730942a71eSRajeev Kumar alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE;
2740942a71eSRajeev Kumar
2750942a71eSRajeev Kumar return 0;
2760942a71eSRajeev Kumar }
2770942a71eSRajeev Kumar
2780942a71eSRajeev Kumar /*
2790942a71eSRajeev Kumar * spear_rtc_set_alarm - set the alarm time
2800942a71eSRajeev Kumar * @dev: rtc device in use
2810942a71eSRajeev Kumar * @alm: holds alarm date and time
2820942a71eSRajeev Kumar *
2830942a71eSRajeev Kumar * This function set alarm time and date. On success it will return 0
2840942a71eSRajeev Kumar * otherwise -ve error is returned.
2850942a71eSRajeev Kumar */
spear_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alm)2860942a71eSRajeev Kumar static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
2870942a71eSRajeev Kumar {
288ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev);
289a16e8393SLars-Peter Clausen unsigned int time, date;
290a16e8393SLars-Peter Clausen int err;
2910942a71eSRajeev Kumar
2929fb7aa75SAlexandre Belloni tm2bcd(&alm->time);
2930942a71eSRajeev Kumar
2940942a71eSRajeev Kumar rtc_wait_not_busy(config);
2950942a71eSRajeev Kumar
2960942a71eSRajeev Kumar time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min <<
2970942a71eSRajeev Kumar MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT);
2980942a71eSRajeev Kumar date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon <<
2990942a71eSRajeev Kumar MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT);
3000942a71eSRajeev Kumar
3010942a71eSRajeev Kumar writel(time, config->ioaddr + ALARM_TIME_REG);
3020942a71eSRajeev Kumar writel(date, config->ioaddr + ALARM_DATE_REG);
3030942a71eSRajeev Kumar err = is_write_complete(config);
3040942a71eSRajeev Kumar if (err < 0)
3050942a71eSRajeev Kumar return err;
3060942a71eSRajeev Kumar
3070942a71eSRajeev Kumar if (alm->enabled)
3080942a71eSRajeev Kumar spear_rtc_enable_interrupt(config);
3090942a71eSRajeev Kumar else
3100942a71eSRajeev Kumar spear_rtc_disable_interrupt(config);
3110942a71eSRajeev Kumar
3120942a71eSRajeev Kumar return 0;
3130942a71eSRajeev Kumar }
314131f8b75SShiraz Hashim
spear_alarm_irq_enable(struct device * dev,unsigned int enabled)315131f8b75SShiraz Hashim static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled)
316131f8b75SShiraz Hashim {
317ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev);
318131f8b75SShiraz Hashim int ret = 0;
319131f8b75SShiraz Hashim
320131f8b75SShiraz Hashim spear_rtc_clear_interrupt(config);
321131f8b75SShiraz Hashim
322131f8b75SShiraz Hashim switch (enabled) {
323131f8b75SShiraz Hashim case 0:
324131f8b75SShiraz Hashim /* alarm off */
325131f8b75SShiraz Hashim spear_rtc_disable_interrupt(config);
326131f8b75SShiraz Hashim break;
327131f8b75SShiraz Hashim case 1:
328131f8b75SShiraz Hashim /* alarm on */
329131f8b75SShiraz Hashim spear_rtc_enable_interrupt(config);
330131f8b75SShiraz Hashim break;
331131f8b75SShiraz Hashim default:
332131f8b75SShiraz Hashim ret = -EINVAL;
333131f8b75SShiraz Hashim break;
334131f8b75SShiraz Hashim }
335131f8b75SShiraz Hashim
336131f8b75SShiraz Hashim return ret;
337131f8b75SShiraz Hashim }
338131f8b75SShiraz Hashim
33934c7b3acSJulia Lawall static const struct rtc_class_ops spear_rtc_ops = {
3400942a71eSRajeev Kumar .read_time = spear_rtc_read_time,
3410942a71eSRajeev Kumar .set_time = spear_rtc_set_time,
3420942a71eSRajeev Kumar .read_alarm = spear_rtc_read_alarm,
3430942a71eSRajeev Kumar .set_alarm = spear_rtc_set_alarm,
344131f8b75SShiraz Hashim .alarm_irq_enable = spear_alarm_irq_enable,
3450942a71eSRajeev Kumar };
3460942a71eSRajeev Kumar
spear_rtc_probe(struct platform_device * pdev)3475a167f45SGreg Kroah-Hartman static int spear_rtc_probe(struct platform_device *pdev)
3480942a71eSRajeev Kumar {
3490942a71eSRajeev Kumar struct spear_rtc_config *config;
350a16e8393SLars-Peter Clausen int status = 0;
3510942a71eSRajeev Kumar int irq;
3520942a71eSRajeev Kumar
353bdaa2c63SViresh Kumar config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
354681acc9fSJingoo Han if (!config)
355bdaa2c63SViresh Kumar return -ENOMEM;
3560942a71eSRajeev Kumar
3574fc4d333SAlexandre Belloni config->rtc = devm_rtc_allocate_device(&pdev->dev);
3584fc4d333SAlexandre Belloni if (IS_ERR(config->rtc))
3594fc4d333SAlexandre Belloni return PTR_ERR(config->rtc);
3604fc4d333SAlexandre Belloni
361bdaa2c63SViresh Kumar /* alarm irqs */
362bdaa2c63SViresh Kumar irq = platform_get_irq(pdev, 0);
363faac9102SStephen Boyd if (irq < 0)
364bdaa2c63SViresh Kumar return irq;
3650942a71eSRajeev Kumar
366bdaa2c63SViresh Kumar status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name,
367bdaa2c63SViresh Kumar config);
368bdaa2c63SViresh Kumar if (status) {
369bdaa2c63SViresh Kumar dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n",
370bdaa2c63SViresh Kumar irq);
371bdaa2c63SViresh Kumar return status;
372bdaa2c63SViresh Kumar }
373bdaa2c63SViresh Kumar
37409ef18bcSYueHaibing config->ioaddr = devm_platform_ioremap_resource(pdev, 0);
3758cbce1e5SThierry Reding if (IS_ERR(config->ioaddr))
3768cbce1e5SThierry Reding return PTR_ERR(config->ioaddr);
377bdaa2c63SViresh Kumar
378bdaa2c63SViresh Kumar config->clk = devm_clk_get(&pdev->dev, NULL);
379bdaa2c63SViresh Kumar if (IS_ERR(config->clk))
380bdaa2c63SViresh Kumar return PTR_ERR(config->clk);
381bdaa2c63SViresh Kumar
38293d78394SDeepak Sikri status = clk_prepare_enable(config->clk);
3830942a71eSRajeev Kumar if (status < 0)
384bdaa2c63SViresh Kumar return status;
3850942a71eSRajeev Kumar
3860942a71eSRajeev Kumar spin_lock_init(&config->lock);
387ee6c54caSViresh Kumar platform_set_drvdata(pdev, config);
3880942a71eSRajeev Kumar
3894fc4d333SAlexandre Belloni config->rtc->ops = &spear_rtc_ops;
390f395e1d3SAlexandre Belloni config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
39103c4cd6fSZeng Jingxiang config->rtc->range_max = RTC_TIMESTAMP_END_9999;
392c9f5c7e7SDeepak Sikri
3934fc4d333SAlexandre Belloni status = devm_rtc_register_device(config->rtc);
3944fc4d333SAlexandre Belloni if (status)
3954fc4d333SAlexandre Belloni goto err_disable_clock;
3964fc4d333SAlexandre Belloni
3970942a71eSRajeev Kumar if (!device_can_wakeup(&pdev->dev))
3980942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 1);
3990942a71eSRajeev Kumar
4000942a71eSRajeev Kumar return 0;
4010942a71eSRajeev Kumar
4020942a71eSRajeev Kumar err_disable_clock:
40393d78394SDeepak Sikri clk_disable_unprepare(config->clk);
4040942a71eSRajeev Kumar
4050942a71eSRajeev Kumar return status;
4060942a71eSRajeev Kumar }
4070942a71eSRajeev Kumar
spear_rtc_remove(struct platform_device * pdev)408*25ffc856SUwe Kleine-König static void spear_rtc_remove(struct platform_device *pdev)
4090942a71eSRajeev Kumar {
410ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev);
4110942a71eSRajeev Kumar
412bdaa2c63SViresh Kumar spear_rtc_disable_interrupt(config);
41393d78394SDeepak Sikri clk_disable_unprepare(config->clk);
414bdaa2c63SViresh Kumar device_init_wakeup(&pdev->dev, 0);
4150942a71eSRajeev Kumar }
4160942a71eSRajeev Kumar
417b086e392SJingoo Han #ifdef CONFIG_PM_SLEEP
spear_rtc_suspend(struct device * dev)418b086e392SJingoo Han static int spear_rtc_suspend(struct device *dev)
4190942a71eSRajeev Kumar {
420b086e392SJingoo Han struct platform_device *pdev = to_platform_device(dev);
421ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev);
4220942a71eSRajeev Kumar int irq;
4230942a71eSRajeev Kumar
4240942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0);
425cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) {
426cd0e08a8SDeepak Sikri if (!enable_irq_wake(irq))
427cd0e08a8SDeepak Sikri config->irq_wake = 1;
428cd0e08a8SDeepak Sikri } else {
4290942a71eSRajeev Kumar spear_rtc_disable_interrupt(config);
4300942a71eSRajeev Kumar clk_disable(config->clk);
4310942a71eSRajeev Kumar }
4320942a71eSRajeev Kumar
4330942a71eSRajeev Kumar return 0;
4340942a71eSRajeev Kumar }
4350942a71eSRajeev Kumar
spear_rtc_resume(struct device * dev)436b086e392SJingoo Han static int spear_rtc_resume(struct device *dev)
4370942a71eSRajeev Kumar {
438b086e392SJingoo Han struct platform_device *pdev = to_platform_device(dev);
439ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev);
4400942a71eSRajeev Kumar int irq;
4410942a71eSRajeev Kumar
4420942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0);
4430942a71eSRajeev Kumar
444cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) {
445cd0e08a8SDeepak Sikri if (config->irq_wake) {
4460942a71eSRajeev Kumar disable_irq_wake(irq);
447cd0e08a8SDeepak Sikri config->irq_wake = 0;
448cd0e08a8SDeepak Sikri }
449cd0e08a8SDeepak Sikri } else {
4500942a71eSRajeev Kumar clk_enable(config->clk);
4510942a71eSRajeev Kumar spear_rtc_enable_interrupt(config);
4520942a71eSRajeev Kumar }
4530942a71eSRajeev Kumar
4540942a71eSRajeev Kumar return 0;
4550942a71eSRajeev Kumar }
4560942a71eSRajeev Kumar #endif
4570942a71eSRajeev Kumar
458b086e392SJingoo Han static SIMPLE_DEV_PM_OPS(spear_rtc_pm_ops, spear_rtc_suspend, spear_rtc_resume);
459b086e392SJingoo Han
spear_rtc_shutdown(struct platform_device * pdev)4600942a71eSRajeev Kumar static void spear_rtc_shutdown(struct platform_device *pdev)
4610942a71eSRajeev Kumar {
462ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev);
4630942a71eSRajeev Kumar
4640942a71eSRajeev Kumar spear_rtc_disable_interrupt(config);
4650942a71eSRajeev Kumar clk_disable(config->clk);
4660942a71eSRajeev Kumar }
4670942a71eSRajeev Kumar
4680108c4ffSViresh Kumar #ifdef CONFIG_OF
4690108c4ffSViresh Kumar static const struct of_device_id spear_rtc_id_table[] = {
4700108c4ffSViresh Kumar { .compatible = "st,spear600-rtc" },
4710108c4ffSViresh Kumar {}
4720108c4ffSViresh Kumar };
4730108c4ffSViresh Kumar MODULE_DEVICE_TABLE(of, spear_rtc_id_table);
4740108c4ffSViresh Kumar #endif
4750108c4ffSViresh Kumar
4760942a71eSRajeev Kumar static struct platform_driver spear_rtc_driver = {
4770942a71eSRajeev Kumar .probe = spear_rtc_probe,
478*25ffc856SUwe Kleine-König .remove_new = spear_rtc_remove,
4790942a71eSRajeev Kumar .shutdown = spear_rtc_shutdown,
4800942a71eSRajeev Kumar .driver = {
4810942a71eSRajeev Kumar .name = "rtc-spear",
482b086e392SJingoo Han .pm = &spear_rtc_pm_ops,
4830108c4ffSViresh Kumar .of_match_table = of_match_ptr(spear_rtc_id_table),
4840942a71eSRajeev Kumar },
4850942a71eSRajeev Kumar };
4860942a71eSRajeev Kumar
4870c4eae66SAxel Lin module_platform_driver(spear_rtc_driver);
4880942a71eSRajeev Kumar
4890942a71eSRajeev Kumar MODULE_ALIAS("platform:rtc-spear");
4900942a71eSRajeev Kumar MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
4910942a71eSRajeev Kumar MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)");
4920942a71eSRajeev Kumar MODULE_LICENSE("GPL");
493