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> 190108c4ffSViresh Kumar #include <linux/of.h> 200942a71eSRajeev Kumar #include <linux/platform_device.h> 210942a71eSRajeev Kumar #include <linux/rtc.h> 220942a71eSRajeev Kumar #include <linux/slab.h> 230942a71eSRajeev Kumar #include <linux/spinlock.h> 240942a71eSRajeev Kumar 250942a71eSRajeev Kumar /* RTC registers */ 260942a71eSRajeev Kumar #define TIME_REG 0x00 270942a71eSRajeev Kumar #define DATE_REG 0x04 280942a71eSRajeev Kumar #define ALARM_TIME_REG 0x08 290942a71eSRajeev Kumar #define ALARM_DATE_REG 0x0C 300942a71eSRajeev Kumar #define CTRL_REG 0x10 310942a71eSRajeev Kumar #define STATUS_REG 0x14 320942a71eSRajeev Kumar 330942a71eSRajeev Kumar /* TIME_REG & ALARM_TIME_REG */ 340942a71eSRajeev Kumar #define SECONDS_UNITS (0xf<<0) /* seconds units position */ 350942a71eSRajeev Kumar #define SECONDS_TENS (0x7<<4) /* seconds tens position */ 360942a71eSRajeev Kumar #define MINUTES_UNITS (0xf<<8) /* minutes units position */ 370942a71eSRajeev Kumar #define MINUTES_TENS (0x7<<12) /* minutes tens position */ 380942a71eSRajeev Kumar #define HOURS_UNITS (0xf<<16) /* hours units position */ 390942a71eSRajeev Kumar #define HOURS_TENS (0x3<<20) /* hours tens position */ 400942a71eSRajeev Kumar 410942a71eSRajeev Kumar /* DATE_REG & ALARM_DATE_REG */ 420942a71eSRajeev Kumar #define DAYS_UNITS (0xf<<0) /* days units position */ 430942a71eSRajeev Kumar #define DAYS_TENS (0x3<<4) /* days tens position */ 440942a71eSRajeev Kumar #define MONTHS_UNITS (0xf<<8) /* months units position */ 450942a71eSRajeev Kumar #define MONTHS_TENS (0x1<<12) /* months tens position */ 460942a71eSRajeev Kumar #define YEARS_UNITS (0xf<<16) /* years units position */ 470942a71eSRajeev Kumar #define YEARS_TENS (0xf<<20) /* years tens position */ 480942a71eSRajeev Kumar #define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */ 490942a71eSRajeev Kumar #define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */ 500942a71eSRajeev Kumar 510942a71eSRajeev Kumar /* MASK SHIFT TIME_REG & ALARM_TIME_REG*/ 520942a71eSRajeev Kumar #define SECOND_SHIFT 0x00 /* seconds units */ 530942a71eSRajeev Kumar #define MINUTE_SHIFT 0x08 /* minutes units position */ 540942a71eSRajeev Kumar #define HOUR_SHIFT 0x10 /* hours units position */ 550942a71eSRajeev Kumar #define MDAY_SHIFT 0x00 /* Month day shift */ 560942a71eSRajeev Kumar #define MONTH_SHIFT 0x08 /* Month shift */ 570942a71eSRajeev Kumar #define YEAR_SHIFT 0x10 /* Year shift */ 580942a71eSRajeev Kumar 590942a71eSRajeev Kumar #define SECOND_MASK 0x7F 600942a71eSRajeev Kumar #define MIN_MASK 0x7F 610942a71eSRajeev Kumar #define HOUR_MASK 0x3F 620942a71eSRajeev Kumar #define DAY_MASK 0x3F 630942a71eSRajeev Kumar #define MONTH_MASK 0x7F 640942a71eSRajeev Kumar #define YEAR_MASK 0xFFFF 650942a71eSRajeev Kumar 660942a71eSRajeev Kumar /* date reg equal to time reg, for debug only */ 670942a71eSRajeev Kumar #define TIME_BYP (1<<9) 680942a71eSRajeev Kumar #define INT_ENABLE (1<<31) /* interrupt enable */ 690942a71eSRajeev Kumar 700942a71eSRajeev Kumar /* STATUS_REG */ 710942a71eSRajeev Kumar #define CLK_UNCONNECTED (1<<0) 720942a71eSRajeev Kumar #define PEND_WR_TIME (1<<2) 730942a71eSRajeev Kumar #define PEND_WR_DATE (1<<3) 740942a71eSRajeev Kumar #define LOST_WR_TIME (1<<4) 750942a71eSRajeev Kumar #define LOST_WR_DATE (1<<5) 760942a71eSRajeev Kumar #define RTC_INT_MASK (1<<31) 770942a71eSRajeev Kumar #define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE) 780942a71eSRajeev Kumar #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) 790942a71eSRajeev Kumar 800942a71eSRajeev Kumar struct spear_rtc_config { 81ee6c54caSViresh Kumar struct rtc_device *rtc; 820942a71eSRajeev Kumar struct clk *clk; 830942a71eSRajeev Kumar spinlock_t lock; 840942a71eSRajeev Kumar void __iomem *ioaddr; 85cd0e08a8SDeepak Sikri unsigned int irq_wake; 860942a71eSRajeev Kumar }; 870942a71eSRajeev Kumar 880942a71eSRajeev Kumar static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) 890942a71eSRajeev Kumar { 900942a71eSRajeev Kumar unsigned int val; 910942a71eSRajeev Kumar unsigned long flags; 920942a71eSRajeev Kumar 930942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 940942a71eSRajeev Kumar val = readl(config->ioaddr + STATUS_REG); 950942a71eSRajeev Kumar val |= RTC_INT_MASK; 960942a71eSRajeev Kumar writel(val, config->ioaddr + STATUS_REG); 970942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 980942a71eSRajeev Kumar } 990942a71eSRajeev Kumar 1000942a71eSRajeev Kumar static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config) 1010942a71eSRajeev Kumar { 1020942a71eSRajeev Kumar unsigned int val; 1030942a71eSRajeev Kumar 1040942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1050942a71eSRajeev Kumar if (!(val & INT_ENABLE)) { 1060942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1070942a71eSRajeev Kumar val |= INT_ENABLE; 1080942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1090942a71eSRajeev Kumar } 1100942a71eSRajeev Kumar } 1110942a71eSRajeev Kumar 1120942a71eSRajeev Kumar static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config) 1130942a71eSRajeev Kumar { 1140942a71eSRajeev Kumar unsigned int val; 1150942a71eSRajeev Kumar 1160942a71eSRajeev Kumar val = readl(config->ioaddr + CTRL_REG); 1170942a71eSRajeev Kumar if (val & INT_ENABLE) { 1180942a71eSRajeev Kumar val &= ~INT_ENABLE; 1190942a71eSRajeev Kumar writel(val, config->ioaddr + CTRL_REG); 1200942a71eSRajeev Kumar } 1210942a71eSRajeev Kumar } 1220942a71eSRajeev Kumar 1230942a71eSRajeev Kumar static inline int is_write_complete(struct spear_rtc_config *config) 1240942a71eSRajeev Kumar { 1250942a71eSRajeev Kumar int ret = 0; 1260942a71eSRajeev Kumar unsigned long flags; 1270942a71eSRajeev Kumar 1280942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1290942a71eSRajeev Kumar if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL) 1300942a71eSRajeev Kumar ret = -EIO; 1310942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1320942a71eSRajeev Kumar 1330942a71eSRajeev Kumar return ret; 1340942a71eSRajeev Kumar } 1350942a71eSRajeev Kumar 1360942a71eSRajeev Kumar static void rtc_wait_not_busy(struct spear_rtc_config *config) 1370942a71eSRajeev Kumar { 1380942a71eSRajeev Kumar int status, count = 0; 1390942a71eSRajeev Kumar unsigned long flags; 1400942a71eSRajeev Kumar 1410942a71eSRajeev Kumar /* Assuming BUSY may stay active for 80 msec) */ 1420942a71eSRajeev Kumar for (count = 0; count < 80; count++) { 1430942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1440942a71eSRajeev Kumar status = readl(config->ioaddr + STATUS_REG); 1450942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1460942a71eSRajeev Kumar if ((status & STATUS_BUSY) == 0) 1470942a71eSRajeev Kumar break; 1480942a71eSRajeev Kumar /* check status busy, after each msec */ 1490942a71eSRajeev Kumar msleep(1); 1500942a71eSRajeev Kumar } 1510942a71eSRajeev Kumar } 1520942a71eSRajeev Kumar 1530942a71eSRajeev Kumar static irqreturn_t spear_rtc_irq(int irq, void *dev_id) 1540942a71eSRajeev Kumar { 155ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_id; 1560942a71eSRajeev Kumar unsigned long flags, events = 0; 1570942a71eSRajeev Kumar unsigned int irq_data; 1580942a71eSRajeev Kumar 1590942a71eSRajeev Kumar spin_lock_irqsave(&config->lock, flags); 1600942a71eSRajeev Kumar irq_data = readl(config->ioaddr + STATUS_REG); 1610942a71eSRajeev Kumar spin_unlock_irqrestore(&config->lock, flags); 1620942a71eSRajeev Kumar 1630942a71eSRajeev Kumar if ((irq_data & RTC_INT_MASK)) { 1640942a71eSRajeev Kumar spear_rtc_clear_interrupt(config); 1650942a71eSRajeev Kumar events = RTC_IRQF | RTC_AF; 166ee6c54caSViresh Kumar rtc_update_irq(config->rtc, 1, events); 1670942a71eSRajeev Kumar return IRQ_HANDLED; 1680942a71eSRajeev Kumar } else 1690942a71eSRajeev Kumar return IRQ_NONE; 1700942a71eSRajeev Kumar 1710942a71eSRajeev Kumar } 1720942a71eSRajeev Kumar 1730942a71eSRajeev Kumar static int tm2bcd(struct rtc_time *tm) 1740942a71eSRajeev Kumar { 1750942a71eSRajeev Kumar if (rtc_valid_tm(tm) != 0) 1760942a71eSRajeev Kumar return -EINVAL; 1770942a71eSRajeev Kumar tm->tm_sec = bin2bcd(tm->tm_sec); 1780942a71eSRajeev Kumar tm->tm_min = bin2bcd(tm->tm_min); 1790942a71eSRajeev Kumar tm->tm_hour = bin2bcd(tm->tm_hour); 1800942a71eSRajeev Kumar tm->tm_mday = bin2bcd(tm->tm_mday); 1810942a71eSRajeev Kumar tm->tm_mon = bin2bcd(tm->tm_mon + 1); 1820942a71eSRajeev Kumar tm->tm_year = bin2bcd(tm->tm_year); 1830942a71eSRajeev Kumar 1840942a71eSRajeev Kumar return 0; 1850942a71eSRajeev Kumar } 1860942a71eSRajeev Kumar 1870942a71eSRajeev Kumar static void bcd2tm(struct rtc_time *tm) 1880942a71eSRajeev Kumar { 1890942a71eSRajeev Kumar tm->tm_sec = bcd2bin(tm->tm_sec); 1900942a71eSRajeev Kumar tm->tm_min = bcd2bin(tm->tm_min); 1910942a71eSRajeev Kumar tm->tm_hour = bcd2bin(tm->tm_hour); 1920942a71eSRajeev Kumar tm->tm_mday = bcd2bin(tm->tm_mday); 1930942a71eSRajeev Kumar tm->tm_mon = bcd2bin(tm->tm_mon) - 1; 1940942a71eSRajeev Kumar /* epoch == 1900 */ 1950942a71eSRajeev Kumar tm->tm_year = bcd2bin(tm->tm_year); 1960942a71eSRajeev Kumar } 1970942a71eSRajeev Kumar 1980942a71eSRajeev Kumar /* 1990942a71eSRajeev Kumar * spear_rtc_read_time - set the time 2000942a71eSRajeev Kumar * @dev: rtc device in use 2010942a71eSRajeev Kumar * @tm: holds date and time 2020942a71eSRajeev Kumar * 2030942a71eSRajeev Kumar * This function read time and date. On success it will return 0 2040942a71eSRajeev Kumar * otherwise -ve error is returned. 2050942a71eSRajeev Kumar */ 2060942a71eSRajeev Kumar static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) 2070942a71eSRajeev Kumar { 208ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(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 { 237ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 238a16e8393SLars-Peter Clausen unsigned int time, date; 2390942a71eSRajeev Kumar 2400942a71eSRajeev Kumar if (tm2bcd(tm) < 0) 2410942a71eSRajeev Kumar return -EINVAL; 2420942a71eSRajeev Kumar 2430942a71eSRajeev Kumar rtc_wait_not_busy(config); 2440942a71eSRajeev Kumar time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | 2450942a71eSRajeev Kumar (tm->tm_hour << HOUR_SHIFT); 2460942a71eSRajeev Kumar date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | 2470942a71eSRajeev Kumar (tm->tm_year << YEAR_SHIFT); 2480942a71eSRajeev Kumar writel(time, config->ioaddr + TIME_REG); 2490942a71eSRajeev Kumar writel(date, config->ioaddr + DATE_REG); 2500942a71eSRajeev Kumar 251a16e8393SLars-Peter Clausen return is_write_complete(config); 2520942a71eSRajeev Kumar } 2530942a71eSRajeev Kumar 2540942a71eSRajeev Kumar /* 2550942a71eSRajeev Kumar * spear_rtc_read_alarm - read the alarm time 2560942a71eSRajeev Kumar * @dev: rtc device in use 2570942a71eSRajeev Kumar * @alm: holds alarm date and time 2580942a71eSRajeev Kumar * 2590942a71eSRajeev Kumar * This function read alarm time and date. On success it will return 0 2600942a71eSRajeev Kumar * otherwise -ve error is returned. 2610942a71eSRajeev Kumar */ 2620942a71eSRajeev Kumar static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2630942a71eSRajeev Kumar { 264ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2650942a71eSRajeev Kumar unsigned int time, date; 2660942a71eSRajeev Kumar 2670942a71eSRajeev Kumar rtc_wait_not_busy(config); 2680942a71eSRajeev Kumar 2690942a71eSRajeev Kumar time = readl(config->ioaddr + ALARM_TIME_REG); 2700942a71eSRajeev Kumar date = readl(config->ioaddr + ALARM_DATE_REG); 2710942a71eSRajeev Kumar alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2720942a71eSRajeev Kumar alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2730942a71eSRajeev Kumar alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2740942a71eSRajeev Kumar alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2750942a71eSRajeev Kumar alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2760942a71eSRajeev Kumar alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2770942a71eSRajeev Kumar 2780942a71eSRajeev Kumar bcd2tm(&alm->time); 2790942a71eSRajeev Kumar alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; 2800942a71eSRajeev Kumar 2810942a71eSRajeev Kumar return 0; 2820942a71eSRajeev Kumar } 2830942a71eSRajeev Kumar 2840942a71eSRajeev Kumar /* 2850942a71eSRajeev Kumar * spear_rtc_set_alarm - set the alarm time 2860942a71eSRajeev Kumar * @dev: rtc device in use 2870942a71eSRajeev Kumar * @alm: holds alarm date and time 2880942a71eSRajeev Kumar * 2890942a71eSRajeev Kumar * This function set alarm time and date. On success it will return 0 2900942a71eSRajeev Kumar * otherwise -ve error is returned. 2910942a71eSRajeev Kumar */ 2920942a71eSRajeev Kumar static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 2930942a71eSRajeev Kumar { 294ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 295a16e8393SLars-Peter Clausen unsigned int time, date; 296a16e8393SLars-Peter Clausen int err; 2970942a71eSRajeev Kumar 2980942a71eSRajeev Kumar if (tm2bcd(&alm->time) < 0) 2990942a71eSRajeev Kumar return -EINVAL; 3000942a71eSRajeev Kumar 3010942a71eSRajeev Kumar rtc_wait_not_busy(config); 3020942a71eSRajeev Kumar 3030942a71eSRajeev Kumar time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << 3040942a71eSRajeev Kumar MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); 3050942a71eSRajeev Kumar date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << 3060942a71eSRajeev Kumar MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); 3070942a71eSRajeev Kumar 3080942a71eSRajeev Kumar writel(time, config->ioaddr + ALARM_TIME_REG); 3090942a71eSRajeev Kumar writel(date, config->ioaddr + ALARM_DATE_REG); 3100942a71eSRajeev Kumar err = is_write_complete(config); 3110942a71eSRajeev Kumar if (err < 0) 3120942a71eSRajeev Kumar return err; 3130942a71eSRajeev Kumar 3140942a71eSRajeev Kumar if (alm->enabled) 3150942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 3160942a71eSRajeev Kumar else 3170942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 3180942a71eSRajeev Kumar 3190942a71eSRajeev Kumar return 0; 3200942a71eSRajeev Kumar } 321131f8b75SShiraz Hashim 322131f8b75SShiraz Hashim static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) 323131f8b75SShiraz Hashim { 324ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 325131f8b75SShiraz Hashim int ret = 0; 326131f8b75SShiraz Hashim 327131f8b75SShiraz Hashim spear_rtc_clear_interrupt(config); 328131f8b75SShiraz Hashim 329131f8b75SShiraz Hashim switch (enabled) { 330131f8b75SShiraz Hashim case 0: 331131f8b75SShiraz Hashim /* alarm off */ 332131f8b75SShiraz Hashim spear_rtc_disable_interrupt(config); 333131f8b75SShiraz Hashim break; 334131f8b75SShiraz Hashim case 1: 335131f8b75SShiraz Hashim /* alarm on */ 336131f8b75SShiraz Hashim spear_rtc_enable_interrupt(config); 337131f8b75SShiraz Hashim break; 338131f8b75SShiraz Hashim default: 339131f8b75SShiraz Hashim ret = -EINVAL; 340131f8b75SShiraz Hashim break; 341131f8b75SShiraz Hashim } 342131f8b75SShiraz Hashim 343131f8b75SShiraz Hashim return ret; 344131f8b75SShiraz Hashim } 345131f8b75SShiraz Hashim 3460942a71eSRajeev Kumar static struct rtc_class_ops spear_rtc_ops = { 3470942a71eSRajeev Kumar .read_time = spear_rtc_read_time, 3480942a71eSRajeev Kumar .set_time = spear_rtc_set_time, 3490942a71eSRajeev Kumar .read_alarm = spear_rtc_read_alarm, 3500942a71eSRajeev Kumar .set_alarm = spear_rtc_set_alarm, 351131f8b75SShiraz Hashim .alarm_irq_enable = spear_alarm_irq_enable, 3520942a71eSRajeev Kumar }; 3530942a71eSRajeev Kumar 3545a167f45SGreg Kroah-Hartman static int spear_rtc_probe(struct platform_device *pdev) 3550942a71eSRajeev Kumar { 3560942a71eSRajeev Kumar struct resource *res; 3570942a71eSRajeev Kumar struct spear_rtc_config *config; 358a16e8393SLars-Peter Clausen int status = 0; 3590942a71eSRajeev Kumar int irq; 3600942a71eSRajeev Kumar 3610942a71eSRajeev Kumar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3620942a71eSRajeev Kumar if (!res) { 3630942a71eSRajeev Kumar dev_err(&pdev->dev, "no resource defined\n"); 3640942a71eSRajeev Kumar return -EBUSY; 3650942a71eSRajeev Kumar } 3660942a71eSRajeev Kumar 367bdaa2c63SViresh Kumar config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); 3680942a71eSRajeev Kumar if (!config) { 3690942a71eSRajeev Kumar dev_err(&pdev->dev, "out of memory\n"); 370bdaa2c63SViresh Kumar return -ENOMEM; 3710942a71eSRajeev Kumar } 3720942a71eSRajeev Kumar 373bdaa2c63SViresh Kumar /* alarm irqs */ 374bdaa2c63SViresh Kumar irq = platform_get_irq(pdev, 0); 375bdaa2c63SViresh Kumar if (irq < 0) { 376bdaa2c63SViresh Kumar dev_err(&pdev->dev, "no update irq?\n"); 377bdaa2c63SViresh Kumar return irq; 3780942a71eSRajeev Kumar } 3790942a71eSRajeev Kumar 380bdaa2c63SViresh Kumar status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, 381bdaa2c63SViresh Kumar config); 382bdaa2c63SViresh Kumar if (status) { 383bdaa2c63SViresh Kumar dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n", 384bdaa2c63SViresh Kumar irq); 385bdaa2c63SViresh Kumar return status; 386bdaa2c63SViresh Kumar } 387bdaa2c63SViresh Kumar 388*8cbce1e5SThierry Reding config->ioaddr = devm_ioremap_resource(&pdev->dev, res); 389*8cbce1e5SThierry Reding if (IS_ERR(config->ioaddr)) 390*8cbce1e5SThierry Reding return PTR_ERR(config->ioaddr); 391bdaa2c63SViresh Kumar 392bdaa2c63SViresh Kumar config->clk = devm_clk_get(&pdev->dev, NULL); 393bdaa2c63SViresh Kumar if (IS_ERR(config->clk)) 394bdaa2c63SViresh Kumar return PTR_ERR(config->clk); 395bdaa2c63SViresh Kumar 39693d78394SDeepak Sikri status = clk_prepare_enable(config->clk); 3970942a71eSRajeev Kumar if (status < 0) 398bdaa2c63SViresh Kumar return status; 3990942a71eSRajeev Kumar 4000942a71eSRajeev Kumar spin_lock_init(&config->lock); 401ee6c54caSViresh Kumar platform_set_drvdata(pdev, config); 4020942a71eSRajeev Kumar 403ee6c54caSViresh Kumar config->rtc = rtc_device_register(pdev->name, &pdev->dev, 404ee6c54caSViresh Kumar &spear_rtc_ops, THIS_MODULE); 405ee6c54caSViresh Kumar if (IS_ERR(config->rtc)) { 4060942a71eSRajeev Kumar dev_err(&pdev->dev, "can't register RTC device, err %ld\n", 407ee6c54caSViresh Kumar PTR_ERR(config->rtc)); 408ee6c54caSViresh Kumar status = PTR_ERR(config->rtc); 409bdaa2c63SViresh Kumar goto err_disable_clock; 4100942a71eSRajeev Kumar } 4110942a71eSRajeev Kumar 412c9f5c7e7SDeepak Sikri config->rtc->uie_unsupported = 1; 413c9f5c7e7SDeepak Sikri 4140942a71eSRajeev Kumar if (!device_can_wakeup(&pdev->dev)) 4150942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 1); 4160942a71eSRajeev Kumar 4170942a71eSRajeev Kumar return 0; 4180942a71eSRajeev Kumar 4190942a71eSRajeev Kumar err_disable_clock: 420bdaa2c63SViresh Kumar platform_set_drvdata(pdev, NULL); 42193d78394SDeepak Sikri clk_disable_unprepare(config->clk); 4220942a71eSRajeev Kumar 4230942a71eSRajeev Kumar return status; 4240942a71eSRajeev Kumar } 4250942a71eSRajeev Kumar 4265a167f45SGreg Kroah-Hartman static int spear_rtc_remove(struct platform_device *pdev) 4270942a71eSRajeev Kumar { 428ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4290942a71eSRajeev Kumar 430ee6c54caSViresh Kumar rtc_device_unregister(config->rtc); 431bdaa2c63SViresh Kumar spear_rtc_disable_interrupt(config); 43293d78394SDeepak Sikri clk_disable_unprepare(config->clk); 433bdaa2c63SViresh Kumar device_init_wakeup(&pdev->dev, 0); 4340942a71eSRajeev Kumar 4350942a71eSRajeev Kumar return 0; 4360942a71eSRajeev Kumar } 4370942a71eSRajeev Kumar 4380942a71eSRajeev Kumar #ifdef CONFIG_PM 4390942a71eSRajeev Kumar 4400942a71eSRajeev Kumar static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) 4410942a71eSRajeev Kumar { 442ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4430942a71eSRajeev Kumar int irq; 4440942a71eSRajeev Kumar 4450942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 446cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 447cd0e08a8SDeepak Sikri if (!enable_irq_wake(irq)) 448cd0e08a8SDeepak Sikri config->irq_wake = 1; 449cd0e08a8SDeepak Sikri } else { 4500942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4510942a71eSRajeev Kumar clk_disable(config->clk); 4520942a71eSRajeev Kumar } 4530942a71eSRajeev Kumar 4540942a71eSRajeev Kumar return 0; 4550942a71eSRajeev Kumar } 4560942a71eSRajeev Kumar 4570942a71eSRajeev Kumar static int spear_rtc_resume(struct platform_device *pdev) 4580942a71eSRajeev Kumar { 459ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4600942a71eSRajeev Kumar int irq; 4610942a71eSRajeev Kumar 4620942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4630942a71eSRajeev Kumar 464cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 465cd0e08a8SDeepak Sikri if (config->irq_wake) { 4660942a71eSRajeev Kumar disable_irq_wake(irq); 467cd0e08a8SDeepak Sikri config->irq_wake = 0; 468cd0e08a8SDeepak Sikri } 469cd0e08a8SDeepak Sikri } else { 4700942a71eSRajeev Kumar clk_enable(config->clk); 4710942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 4720942a71eSRajeev Kumar } 4730942a71eSRajeev Kumar 4740942a71eSRajeev Kumar return 0; 4750942a71eSRajeev Kumar } 4760942a71eSRajeev Kumar 4770942a71eSRajeev Kumar #else 4780942a71eSRajeev Kumar #define spear_rtc_suspend NULL 4790942a71eSRajeev Kumar #define spear_rtc_resume NULL 4800942a71eSRajeev Kumar #endif 4810942a71eSRajeev Kumar 4820942a71eSRajeev Kumar static void spear_rtc_shutdown(struct platform_device *pdev) 4830942a71eSRajeev Kumar { 484ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4850942a71eSRajeev Kumar 4860942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4870942a71eSRajeev Kumar clk_disable(config->clk); 4880942a71eSRajeev Kumar } 4890942a71eSRajeev Kumar 4900108c4ffSViresh Kumar #ifdef CONFIG_OF 4910108c4ffSViresh Kumar static const struct of_device_id spear_rtc_id_table[] = { 4920108c4ffSViresh Kumar { .compatible = "st,spear600-rtc" }, 4930108c4ffSViresh Kumar {} 4940108c4ffSViresh Kumar }; 4950108c4ffSViresh Kumar MODULE_DEVICE_TABLE(of, spear_rtc_id_table); 4960108c4ffSViresh Kumar #endif 4970108c4ffSViresh Kumar 4980942a71eSRajeev Kumar static struct platform_driver spear_rtc_driver = { 4990942a71eSRajeev Kumar .probe = spear_rtc_probe, 5005a167f45SGreg Kroah-Hartman .remove = spear_rtc_remove, 5010942a71eSRajeev Kumar .suspend = spear_rtc_suspend, 5020942a71eSRajeev Kumar .resume = spear_rtc_resume, 5030942a71eSRajeev Kumar .shutdown = spear_rtc_shutdown, 5040942a71eSRajeev Kumar .driver = { 5050942a71eSRajeev Kumar .name = "rtc-spear", 5060108c4ffSViresh Kumar .of_match_table = of_match_ptr(spear_rtc_id_table), 5070942a71eSRajeev Kumar }, 5080942a71eSRajeev Kumar }; 5090942a71eSRajeev Kumar 5100c4eae66SAxel Lin module_platform_driver(spear_rtc_driver); 5110942a71eSRajeev Kumar 5120942a71eSRajeev Kumar MODULE_ALIAS("platform:rtc-spear"); 5130942a71eSRajeev Kumar MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 5140942a71eSRajeev Kumar MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); 5150942a71eSRajeev Kumar MODULE_LICENSE("GPL"); 516