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 173*9fb7aa75SAlexandre Belloni static void tm2bcd(struct rtc_time *tm) 1740942a71eSRajeev Kumar { 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 1830942a71eSRajeev Kumar static void bcd2tm(struct rtc_time *tm) 1840942a71eSRajeev Kumar { 1850942a71eSRajeev Kumar tm->tm_sec = bcd2bin(tm->tm_sec); 1860942a71eSRajeev Kumar tm->tm_min = bcd2bin(tm->tm_min); 1870942a71eSRajeev Kumar tm->tm_hour = bcd2bin(tm->tm_hour); 1880942a71eSRajeev Kumar tm->tm_mday = bcd2bin(tm->tm_mday); 1890942a71eSRajeev Kumar tm->tm_mon = bcd2bin(tm->tm_mon) - 1; 1900942a71eSRajeev Kumar /* epoch == 1900 */ 1910942a71eSRajeev Kumar tm->tm_year = bcd2bin(tm->tm_year); 1920942a71eSRajeev Kumar } 1930942a71eSRajeev Kumar 1940942a71eSRajeev Kumar /* 1950942a71eSRajeev Kumar * spear_rtc_read_time - set the time 1960942a71eSRajeev Kumar * @dev: rtc device in use 1970942a71eSRajeev Kumar * @tm: holds date and time 1980942a71eSRajeev Kumar * 1990942a71eSRajeev Kumar * This function read time and date. On success it will return 0 2000942a71eSRajeev Kumar * otherwise -ve error is returned. 2010942a71eSRajeev Kumar */ 2020942a71eSRajeev Kumar static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) 2030942a71eSRajeev Kumar { 204ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2050942a71eSRajeev Kumar unsigned int time, date; 2060942a71eSRajeev Kumar 2070942a71eSRajeev Kumar /* we don't report wday/yday/isdst ... */ 2080942a71eSRajeev Kumar rtc_wait_not_busy(config); 2090942a71eSRajeev Kumar 2100942a71eSRajeev Kumar time = readl(config->ioaddr + TIME_REG); 2110942a71eSRajeev Kumar date = readl(config->ioaddr + DATE_REG); 2120942a71eSRajeev Kumar tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2130942a71eSRajeev Kumar tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2140942a71eSRajeev Kumar tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2150942a71eSRajeev Kumar tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2160942a71eSRajeev Kumar tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2170942a71eSRajeev Kumar tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2180942a71eSRajeev Kumar 2190942a71eSRajeev Kumar bcd2tm(tm); 2200942a71eSRajeev Kumar return 0; 2210942a71eSRajeev Kumar } 2220942a71eSRajeev Kumar 2230942a71eSRajeev Kumar /* 2240942a71eSRajeev Kumar * spear_rtc_set_time - set the time 2250942a71eSRajeev Kumar * @dev: rtc device in use 2260942a71eSRajeev Kumar * @tm: holds date and time 2270942a71eSRajeev Kumar * 2280942a71eSRajeev Kumar * This function set time and date. On success it will return 0 2290942a71eSRajeev Kumar * otherwise -ve error is returned. 2300942a71eSRajeev Kumar */ 2310942a71eSRajeev Kumar static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) 2320942a71eSRajeev Kumar { 233ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 234a16e8393SLars-Peter Clausen unsigned int time, date; 2350942a71eSRajeev Kumar 236*9fb7aa75SAlexandre Belloni tm2bcd(tm); 2370942a71eSRajeev Kumar 2380942a71eSRajeev Kumar rtc_wait_not_busy(config); 2390942a71eSRajeev Kumar time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | 2400942a71eSRajeev Kumar (tm->tm_hour << HOUR_SHIFT); 2410942a71eSRajeev Kumar date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | 2420942a71eSRajeev Kumar (tm->tm_year << YEAR_SHIFT); 2430942a71eSRajeev Kumar writel(time, config->ioaddr + TIME_REG); 2440942a71eSRajeev Kumar writel(date, config->ioaddr + DATE_REG); 2450942a71eSRajeev Kumar 246a16e8393SLars-Peter Clausen return is_write_complete(config); 2470942a71eSRajeev Kumar } 2480942a71eSRajeev Kumar 2490942a71eSRajeev Kumar /* 2500942a71eSRajeev Kumar * spear_rtc_read_alarm - read the alarm time 2510942a71eSRajeev Kumar * @dev: rtc device in use 2520942a71eSRajeev Kumar * @alm: holds alarm date and time 2530942a71eSRajeev Kumar * 2540942a71eSRajeev Kumar * This function read alarm time and date. On success it will return 0 2550942a71eSRajeev Kumar * otherwise -ve error is returned. 2560942a71eSRajeev Kumar */ 2570942a71eSRajeev Kumar static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2580942a71eSRajeev Kumar { 259ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 2600942a71eSRajeev Kumar unsigned int time, date; 2610942a71eSRajeev Kumar 2620942a71eSRajeev Kumar rtc_wait_not_busy(config); 2630942a71eSRajeev Kumar 2640942a71eSRajeev Kumar time = readl(config->ioaddr + ALARM_TIME_REG); 2650942a71eSRajeev Kumar date = readl(config->ioaddr + ALARM_DATE_REG); 2660942a71eSRajeev Kumar alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; 2670942a71eSRajeev Kumar alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; 2680942a71eSRajeev Kumar alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; 2690942a71eSRajeev Kumar alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; 2700942a71eSRajeev Kumar alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; 2710942a71eSRajeev Kumar alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; 2720942a71eSRajeev Kumar 2730942a71eSRajeev Kumar bcd2tm(&alm->time); 2740942a71eSRajeev Kumar alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; 2750942a71eSRajeev Kumar 2760942a71eSRajeev Kumar return 0; 2770942a71eSRajeev Kumar } 2780942a71eSRajeev Kumar 2790942a71eSRajeev Kumar /* 2800942a71eSRajeev Kumar * spear_rtc_set_alarm - set the alarm time 2810942a71eSRajeev Kumar * @dev: rtc device in use 2820942a71eSRajeev Kumar * @alm: holds alarm date and time 2830942a71eSRajeev Kumar * 2840942a71eSRajeev Kumar * This function set alarm time and date. On success it will return 0 2850942a71eSRajeev Kumar * otherwise -ve error is returned. 2860942a71eSRajeev Kumar */ 2870942a71eSRajeev Kumar static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 2880942a71eSRajeev Kumar { 289ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 290a16e8393SLars-Peter Clausen unsigned int time, date; 291a16e8393SLars-Peter Clausen int err; 2920942a71eSRajeev Kumar 293*9fb7aa75SAlexandre Belloni tm2bcd(&alm->time); 2940942a71eSRajeev Kumar 2950942a71eSRajeev Kumar rtc_wait_not_busy(config); 2960942a71eSRajeev Kumar 2970942a71eSRajeev Kumar time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << 2980942a71eSRajeev Kumar MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); 2990942a71eSRajeev Kumar date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << 3000942a71eSRajeev Kumar MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); 3010942a71eSRajeev Kumar 3020942a71eSRajeev Kumar writel(time, config->ioaddr + ALARM_TIME_REG); 3030942a71eSRajeev Kumar writel(date, config->ioaddr + ALARM_DATE_REG); 3040942a71eSRajeev Kumar err = is_write_complete(config); 3050942a71eSRajeev Kumar if (err < 0) 3060942a71eSRajeev Kumar return err; 3070942a71eSRajeev Kumar 3080942a71eSRajeev Kumar if (alm->enabled) 3090942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 3100942a71eSRajeev Kumar else 3110942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 3120942a71eSRajeev Kumar 3130942a71eSRajeev Kumar return 0; 3140942a71eSRajeev Kumar } 315131f8b75SShiraz Hashim 316131f8b75SShiraz Hashim static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) 317131f8b75SShiraz Hashim { 318ee6c54caSViresh Kumar struct spear_rtc_config *config = dev_get_drvdata(dev); 319131f8b75SShiraz Hashim int ret = 0; 320131f8b75SShiraz Hashim 321131f8b75SShiraz Hashim spear_rtc_clear_interrupt(config); 322131f8b75SShiraz Hashim 323131f8b75SShiraz Hashim switch (enabled) { 324131f8b75SShiraz Hashim case 0: 325131f8b75SShiraz Hashim /* alarm off */ 326131f8b75SShiraz Hashim spear_rtc_disable_interrupt(config); 327131f8b75SShiraz Hashim break; 328131f8b75SShiraz Hashim case 1: 329131f8b75SShiraz Hashim /* alarm on */ 330131f8b75SShiraz Hashim spear_rtc_enable_interrupt(config); 331131f8b75SShiraz Hashim break; 332131f8b75SShiraz Hashim default: 333131f8b75SShiraz Hashim ret = -EINVAL; 334131f8b75SShiraz Hashim break; 335131f8b75SShiraz Hashim } 336131f8b75SShiraz Hashim 337131f8b75SShiraz Hashim return ret; 338131f8b75SShiraz Hashim } 339131f8b75SShiraz Hashim 34034c7b3acSJulia Lawall static const struct rtc_class_ops spear_rtc_ops = { 3410942a71eSRajeev Kumar .read_time = spear_rtc_read_time, 3420942a71eSRajeev Kumar .set_time = spear_rtc_set_time, 3430942a71eSRajeev Kumar .read_alarm = spear_rtc_read_alarm, 3440942a71eSRajeev Kumar .set_alarm = spear_rtc_set_alarm, 345131f8b75SShiraz Hashim .alarm_irq_enable = spear_alarm_irq_enable, 3460942a71eSRajeev Kumar }; 3470942a71eSRajeev Kumar 3485a167f45SGreg Kroah-Hartman static int spear_rtc_probe(struct platform_device *pdev) 3490942a71eSRajeev Kumar { 3500942a71eSRajeev Kumar struct resource *res; 3510942a71eSRajeev Kumar struct spear_rtc_config *config; 352a16e8393SLars-Peter Clausen int status = 0; 3530942a71eSRajeev Kumar int irq; 3540942a71eSRajeev Kumar 355bdaa2c63SViresh Kumar config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); 356681acc9fSJingoo Han if (!config) 357bdaa2c63SViresh Kumar return -ENOMEM; 3580942a71eSRajeev Kumar 359bdaa2c63SViresh Kumar /* alarm irqs */ 360bdaa2c63SViresh Kumar irq = platform_get_irq(pdev, 0); 361bdaa2c63SViresh Kumar if (irq < 0) { 362bdaa2c63SViresh Kumar dev_err(&pdev->dev, "no update irq?\n"); 363bdaa2c63SViresh Kumar return irq; 3640942a71eSRajeev Kumar } 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 374e044253bSJulia Lawall res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3758cbce1e5SThierry Reding config->ioaddr = devm_ioremap_resource(&pdev->dev, res); 3768cbce1e5SThierry Reding if (IS_ERR(config->ioaddr)) 3778cbce1e5SThierry Reding return PTR_ERR(config->ioaddr); 378bdaa2c63SViresh Kumar 379bdaa2c63SViresh Kumar config->clk = devm_clk_get(&pdev->dev, NULL); 380bdaa2c63SViresh Kumar if (IS_ERR(config->clk)) 381bdaa2c63SViresh Kumar return PTR_ERR(config->clk); 382bdaa2c63SViresh Kumar 38393d78394SDeepak Sikri status = clk_prepare_enable(config->clk); 3840942a71eSRajeev Kumar if (status < 0) 385bdaa2c63SViresh Kumar return status; 3860942a71eSRajeev Kumar 3870942a71eSRajeev Kumar spin_lock_init(&config->lock); 388ee6c54caSViresh Kumar platform_set_drvdata(pdev, config); 3890942a71eSRajeev Kumar 390246b1a99SJingoo Han config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, 391ee6c54caSViresh Kumar &spear_rtc_ops, THIS_MODULE); 392ee6c54caSViresh Kumar if (IS_ERR(config->rtc)) { 3930942a71eSRajeev Kumar dev_err(&pdev->dev, "can't register RTC device, err %ld\n", 394ee6c54caSViresh Kumar PTR_ERR(config->rtc)); 395ee6c54caSViresh Kumar status = PTR_ERR(config->rtc); 396bdaa2c63SViresh Kumar goto err_disable_clock; 3970942a71eSRajeev Kumar } 3980942a71eSRajeev Kumar 399c9f5c7e7SDeepak Sikri config->rtc->uie_unsupported = 1; 400c9f5c7e7SDeepak Sikri 4010942a71eSRajeev Kumar if (!device_can_wakeup(&pdev->dev)) 4020942a71eSRajeev Kumar device_init_wakeup(&pdev->dev, 1); 4030942a71eSRajeev Kumar 4040942a71eSRajeev Kumar return 0; 4050942a71eSRajeev Kumar 4060942a71eSRajeev Kumar err_disable_clock: 40793d78394SDeepak Sikri clk_disable_unprepare(config->clk); 4080942a71eSRajeev Kumar 4090942a71eSRajeev Kumar return status; 4100942a71eSRajeev Kumar } 4110942a71eSRajeev Kumar 4125a167f45SGreg Kroah-Hartman static int spear_rtc_remove(struct platform_device *pdev) 4130942a71eSRajeev Kumar { 414ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4150942a71eSRajeev Kumar 416bdaa2c63SViresh Kumar spear_rtc_disable_interrupt(config); 41793d78394SDeepak Sikri clk_disable_unprepare(config->clk); 418bdaa2c63SViresh Kumar device_init_wakeup(&pdev->dev, 0); 4190942a71eSRajeev Kumar 4200942a71eSRajeev Kumar return 0; 4210942a71eSRajeev Kumar } 4220942a71eSRajeev Kumar 423b086e392SJingoo Han #ifdef CONFIG_PM_SLEEP 424b086e392SJingoo Han static int spear_rtc_suspend(struct device *dev) 4250942a71eSRajeev Kumar { 426b086e392SJingoo Han struct platform_device *pdev = to_platform_device(dev); 427ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4280942a71eSRajeev Kumar int irq; 4290942a71eSRajeev Kumar 4300942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 431cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 432cd0e08a8SDeepak Sikri if (!enable_irq_wake(irq)) 433cd0e08a8SDeepak Sikri config->irq_wake = 1; 434cd0e08a8SDeepak Sikri } else { 4350942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4360942a71eSRajeev Kumar clk_disable(config->clk); 4370942a71eSRajeev Kumar } 4380942a71eSRajeev Kumar 4390942a71eSRajeev Kumar return 0; 4400942a71eSRajeev Kumar } 4410942a71eSRajeev Kumar 442b086e392SJingoo Han static int spear_rtc_resume(struct device *dev) 4430942a71eSRajeev Kumar { 444b086e392SJingoo Han struct platform_device *pdev = to_platform_device(dev); 445ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4460942a71eSRajeev Kumar int irq; 4470942a71eSRajeev Kumar 4480942a71eSRajeev Kumar irq = platform_get_irq(pdev, 0); 4490942a71eSRajeev Kumar 450cd0e08a8SDeepak Sikri if (device_may_wakeup(&pdev->dev)) { 451cd0e08a8SDeepak Sikri if (config->irq_wake) { 4520942a71eSRajeev Kumar disable_irq_wake(irq); 453cd0e08a8SDeepak Sikri config->irq_wake = 0; 454cd0e08a8SDeepak Sikri } 455cd0e08a8SDeepak Sikri } else { 4560942a71eSRajeev Kumar clk_enable(config->clk); 4570942a71eSRajeev Kumar spear_rtc_enable_interrupt(config); 4580942a71eSRajeev Kumar } 4590942a71eSRajeev Kumar 4600942a71eSRajeev Kumar return 0; 4610942a71eSRajeev Kumar } 4620942a71eSRajeev Kumar #endif 4630942a71eSRajeev Kumar 464b086e392SJingoo Han static SIMPLE_DEV_PM_OPS(spear_rtc_pm_ops, spear_rtc_suspend, spear_rtc_resume); 465b086e392SJingoo Han 4660942a71eSRajeev Kumar static void spear_rtc_shutdown(struct platform_device *pdev) 4670942a71eSRajeev Kumar { 468ee6c54caSViresh Kumar struct spear_rtc_config *config = platform_get_drvdata(pdev); 4690942a71eSRajeev Kumar 4700942a71eSRajeev Kumar spear_rtc_disable_interrupt(config); 4710942a71eSRajeev Kumar clk_disable(config->clk); 4720942a71eSRajeev Kumar } 4730942a71eSRajeev Kumar 4740108c4ffSViresh Kumar #ifdef CONFIG_OF 4750108c4ffSViresh Kumar static const struct of_device_id spear_rtc_id_table[] = { 4760108c4ffSViresh Kumar { .compatible = "st,spear600-rtc" }, 4770108c4ffSViresh Kumar {} 4780108c4ffSViresh Kumar }; 4790108c4ffSViresh Kumar MODULE_DEVICE_TABLE(of, spear_rtc_id_table); 4800108c4ffSViresh Kumar #endif 4810108c4ffSViresh Kumar 4820942a71eSRajeev Kumar static struct platform_driver spear_rtc_driver = { 4830942a71eSRajeev Kumar .probe = spear_rtc_probe, 4845a167f45SGreg Kroah-Hartman .remove = spear_rtc_remove, 4850942a71eSRajeev Kumar .shutdown = spear_rtc_shutdown, 4860942a71eSRajeev Kumar .driver = { 4870942a71eSRajeev Kumar .name = "rtc-spear", 488b086e392SJingoo Han .pm = &spear_rtc_pm_ops, 4890108c4ffSViresh Kumar .of_match_table = of_match_ptr(spear_rtc_id_table), 4900942a71eSRajeev Kumar }, 4910942a71eSRajeev Kumar }; 4920942a71eSRajeev Kumar 4930c4eae66SAxel Lin module_platform_driver(spear_rtc_driver); 4940942a71eSRajeev Kumar 4950942a71eSRajeev Kumar MODULE_ALIAS("platform:rtc-spear"); 4960942a71eSRajeev Kumar MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 4970942a71eSRajeev Kumar MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); 4980942a71eSRajeev Kumar MODULE_LICENSE("GPL"); 499