1e07fa839SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0+ 235c86bf6SMark Brown /* 335c86bf6SMark Brown * Real Time Clock driver for Wolfson Microelectronics WM831x 435c86bf6SMark Brown * 535c86bf6SMark Brown * Copyright (C) 2009 Wolfson Microelectronics PLC. 635c86bf6SMark Brown * 735c86bf6SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 835c86bf6SMark Brown * 935c86bf6SMark Brown */ 1035c86bf6SMark Brown 1135c86bf6SMark Brown #include <linux/module.h> 1235c86bf6SMark Brown #include <linux/kernel.h> 1335c86bf6SMark Brown #include <linux/time.h> 1435c86bf6SMark Brown #include <linux/rtc.h> 155a0e3ad6STejun Heo #include <linux/slab.h> 1635c86bf6SMark Brown #include <linux/bcd.h> 1735c86bf6SMark Brown #include <linux/interrupt.h> 1835c86bf6SMark Brown #include <linux/ioctl.h> 1935c86bf6SMark Brown #include <linux/completion.h> 2035c86bf6SMark Brown #include <linux/mfd/wm831x/core.h> 2135c86bf6SMark Brown #include <linux/delay.h> 2235c86bf6SMark Brown #include <linux/platform_device.h> 239dccf55fSMark Brown #include <linux/random.h> 2435c86bf6SMark Brown 2535c86bf6SMark Brown /* 2635c86bf6SMark Brown * R16416 (0x4020) - RTC Write Counter 2735c86bf6SMark Brown */ 2835c86bf6SMark Brown #define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */ 2935c86bf6SMark Brown #define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */ 3035c86bf6SMark Brown #define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */ 3135c86bf6SMark Brown 3235c86bf6SMark Brown /* 3335c86bf6SMark Brown * R16417 (0x4021) - RTC Time 1 3435c86bf6SMark Brown */ 3535c86bf6SMark Brown #define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ 3635c86bf6SMark Brown #define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ 3735c86bf6SMark Brown #define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ 3835c86bf6SMark Brown 3935c86bf6SMark Brown /* 4035c86bf6SMark Brown * R16418 (0x4022) - RTC Time 2 4135c86bf6SMark Brown */ 4235c86bf6SMark Brown #define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ 4335c86bf6SMark Brown #define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ 4435c86bf6SMark Brown #define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ 4535c86bf6SMark Brown 4635c86bf6SMark Brown /* 4735c86bf6SMark Brown * R16419 (0x4023) - RTC Alarm 1 4835c86bf6SMark Brown */ 4935c86bf6SMark Brown #define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ 5035c86bf6SMark Brown #define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ 5135c86bf6SMark Brown #define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ 5235c86bf6SMark Brown 5335c86bf6SMark Brown /* 5435c86bf6SMark Brown * R16420 (0x4024) - RTC Alarm 2 5535c86bf6SMark Brown */ 5635c86bf6SMark Brown #define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ 5735c86bf6SMark Brown #define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ 5835c86bf6SMark Brown #define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ 5935c86bf6SMark Brown 6035c86bf6SMark Brown /* 6135c86bf6SMark Brown * R16421 (0x4025) - RTC Control 6235c86bf6SMark Brown */ 6335c86bf6SMark Brown #define WM831X_RTC_VALID 0x8000 /* RTC_VALID */ 6435c86bf6SMark Brown #define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */ 6535c86bf6SMark Brown #define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */ 6635c86bf6SMark Brown #define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */ 6735c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */ 6835c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */ 6935c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */ 7035c86bf6SMark Brown #define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */ 7135c86bf6SMark Brown #define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */ 7235c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */ 7335c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */ 7435c86bf6SMark Brown #define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */ 7535c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */ 7635c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */ 7735c86bf6SMark Brown #define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */ 7835c86bf6SMark Brown 7935c86bf6SMark Brown /* 8035c86bf6SMark Brown * R16422 (0x4026) - RTC Trim 8135c86bf6SMark Brown */ 8235c86bf6SMark Brown #define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */ 8335c86bf6SMark Brown #define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */ 8435c86bf6SMark Brown #define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */ 8535c86bf6SMark Brown 8635c86bf6SMark Brown #define WM831X_SET_TIME_RETRIES 5 8735c86bf6SMark Brown #define WM831X_GET_TIME_RETRIES 5 8835c86bf6SMark Brown 8935c86bf6SMark Brown struct wm831x_rtc { 9035c86bf6SMark Brown struct wm831x *wm831x; 9135c86bf6SMark Brown struct rtc_device *rtc; 9235c86bf6SMark Brown unsigned int alarm_enabled:1; 9335c86bf6SMark Brown }; 9435c86bf6SMark Brown 959dccf55fSMark Brown static void wm831x_rtc_add_randomness(struct wm831x *wm831x) 969dccf55fSMark Brown { 979dccf55fSMark Brown int ret; 989dccf55fSMark Brown u16 reg; 999dccf55fSMark Brown 1009dccf55fSMark Brown /* 1019dccf55fSMark Brown * The write counter contains a pseudo-random number which is 1029dccf55fSMark Brown * regenerated every time we set the RTC so it should be a 1039dccf55fSMark Brown * useful per-system source of entropy. 1049dccf55fSMark Brown */ 1059dccf55fSMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER); 1069dccf55fSMark Brown if (ret >= 0) { 1079dccf55fSMark Brown reg = ret; 1089dccf55fSMark Brown add_device_randomness(®, sizeof(reg)); 1099dccf55fSMark Brown } else { 1109dccf55fSMark Brown dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n", 1119dccf55fSMark Brown ret); 1129dccf55fSMark Brown } 1139dccf55fSMark Brown } 1149dccf55fSMark Brown 11535c86bf6SMark Brown /* 11635c86bf6SMark Brown * Read current time and date in RTC 11735c86bf6SMark Brown */ 11835c86bf6SMark Brown static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm) 11935c86bf6SMark Brown { 12035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 12135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 12235c86bf6SMark Brown u16 time1[2], time2[2]; 12335c86bf6SMark Brown int ret; 12435c86bf6SMark Brown int count = 0; 12535c86bf6SMark Brown 12635c86bf6SMark Brown /* Has the RTC been programmed? */ 12735c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 12835c86bf6SMark Brown if (ret < 0) { 12935c86bf6SMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret); 13035c86bf6SMark Brown return ret; 13135c86bf6SMark Brown } 13235c86bf6SMark Brown if (!(ret & WM831X_RTC_VALID)) { 13335c86bf6SMark Brown dev_dbg(dev, "RTC not yet configured\n"); 13435c86bf6SMark Brown return -EINVAL; 13535c86bf6SMark Brown } 13635c86bf6SMark Brown 13735c86bf6SMark Brown /* Read twice to make sure we don't read a corrupt, partially 13835c86bf6SMark Brown * incremented, value. 13935c86bf6SMark Brown */ 14035c86bf6SMark Brown do { 14135c86bf6SMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, 14235c86bf6SMark Brown 2, time1); 14335c86bf6SMark Brown if (ret != 0) 14435c86bf6SMark Brown continue; 14535c86bf6SMark Brown 14635c86bf6SMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, 14735c86bf6SMark Brown 2, time2); 14835c86bf6SMark Brown if (ret != 0) 14935c86bf6SMark Brown continue; 15035c86bf6SMark Brown 15135c86bf6SMark Brown if (memcmp(time1, time2, sizeof(time1)) == 0) { 15235c86bf6SMark Brown u32 time = (time1[0] << 16) | time1[1]; 15335c86bf6SMark Brown 15423992449SAlexandre Belloni rtc_time64_to_tm(time, tm); 155ab62670eSAlexandre Belloni return 0; 15635c86bf6SMark Brown } 15735c86bf6SMark Brown 15835c86bf6SMark Brown } while (++count < WM831X_GET_TIME_RETRIES); 15935c86bf6SMark Brown 16035c86bf6SMark Brown dev_err(dev, "Timed out reading current time\n"); 16135c86bf6SMark Brown 16235c86bf6SMark Brown return -EIO; 16335c86bf6SMark Brown } 16435c86bf6SMark Brown 16535c86bf6SMark Brown /* 16635c86bf6SMark Brown * Set current time and date in RTC 16735c86bf6SMark Brown */ 168*498ce4e7SAlexandre Belloni static int wm831x_rtc_settime(struct device *dev, struct rtc_time *tm) 16935c86bf6SMark Brown { 17035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 17135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 17235c86bf6SMark Brown struct rtc_time new_tm; 173*498ce4e7SAlexandre Belloni unsigned long time, new_time; 17435c86bf6SMark Brown int ret; 17535c86bf6SMark Brown int count = 0; 17635c86bf6SMark Brown 177*498ce4e7SAlexandre Belloni time = rtc_tm_to_time64(tm); 178*498ce4e7SAlexandre Belloni 17935c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1, 18035c86bf6SMark Brown (time >> 16) & 0xffff); 18135c86bf6SMark Brown if (ret < 0) { 18235c86bf6SMark Brown dev_err(dev, "Failed to write TIME_1: %d\n", ret); 18335c86bf6SMark Brown return ret; 18435c86bf6SMark Brown } 18535c86bf6SMark Brown 18635c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff); 18735c86bf6SMark Brown if (ret < 0) { 18835c86bf6SMark Brown dev_err(dev, "Failed to write TIME_2: %d\n", ret); 18935c86bf6SMark Brown return ret; 19035c86bf6SMark Brown } 19135c86bf6SMark Brown 19235c86bf6SMark Brown /* Wait for the update to complete - should happen first time 19335c86bf6SMark Brown * round but be conservative. 19435c86bf6SMark Brown */ 19535c86bf6SMark Brown do { 19635c86bf6SMark Brown msleep(1); 19735c86bf6SMark Brown 19835c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 19935c86bf6SMark Brown if (ret < 0) 20035c86bf6SMark Brown ret = WM831X_RTC_SYNC_BUSY; 20135c86bf6SMark Brown } while (!(ret & WM831X_RTC_SYNC_BUSY) && 20235c86bf6SMark Brown ++count < WM831X_SET_TIME_RETRIES); 20335c86bf6SMark Brown 20435c86bf6SMark Brown if (ret & WM831X_RTC_SYNC_BUSY) { 20535c86bf6SMark Brown dev_err(dev, "Timed out writing RTC update\n"); 20635c86bf6SMark Brown return -EIO; 20735c86bf6SMark Brown } 20835c86bf6SMark Brown 20935c86bf6SMark Brown /* Check that the update was accepted; security features may 21035c86bf6SMark Brown * have caused the update to be ignored. 21135c86bf6SMark Brown */ 21235c86bf6SMark Brown ret = wm831x_rtc_readtime(dev, &new_tm); 21335c86bf6SMark Brown if (ret < 0) 21435c86bf6SMark Brown return ret; 21535c86bf6SMark Brown 21623992449SAlexandre Belloni new_time = rtc_tm_to_time64(&new_tm); 21735c86bf6SMark Brown 21835c86bf6SMark Brown /* Allow a second of change in case of tick */ 21935c86bf6SMark Brown if (new_time - time > 1) { 22035c86bf6SMark Brown dev_err(dev, "RTC update not permitted by hardware\n"); 22135c86bf6SMark Brown return -EPERM; 22235c86bf6SMark Brown } 22335c86bf6SMark Brown 22435c86bf6SMark Brown return 0; 22535c86bf6SMark Brown } 22635c86bf6SMark Brown 22735c86bf6SMark Brown /* 22835c86bf6SMark Brown * Read alarm time and date in RTC 22935c86bf6SMark Brown */ 23035c86bf6SMark Brown static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) 23135c86bf6SMark Brown { 23235c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 23335c86bf6SMark Brown int ret; 23435c86bf6SMark Brown u16 data[2]; 23535c86bf6SMark Brown u32 time; 23635c86bf6SMark Brown 23735c86bf6SMark Brown ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1, 23835c86bf6SMark Brown 2, data); 23935c86bf6SMark Brown if (ret != 0) { 24035c86bf6SMark Brown dev_err(dev, "Failed to read alarm time: %d\n", ret); 24135c86bf6SMark Brown return ret; 24235c86bf6SMark Brown } 24335c86bf6SMark Brown 24435c86bf6SMark Brown time = (data[0] << 16) | data[1]; 24535c86bf6SMark Brown 24623992449SAlexandre Belloni rtc_time64_to_tm(time, &alrm->time); 24735c86bf6SMark Brown 24835c86bf6SMark Brown ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL); 24935c86bf6SMark Brown if (ret < 0) { 25035c86bf6SMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret); 25135c86bf6SMark Brown return ret; 25235c86bf6SMark Brown } 25335c86bf6SMark Brown 25435c86bf6SMark Brown if (ret & WM831X_RTC_ALM_ENA) 25535c86bf6SMark Brown alrm->enabled = 1; 25635c86bf6SMark Brown else 25735c86bf6SMark Brown alrm->enabled = 0; 25835c86bf6SMark Brown 25935c86bf6SMark Brown return 0; 26035c86bf6SMark Brown } 26135c86bf6SMark Brown 26235c86bf6SMark Brown static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc) 26335c86bf6SMark Brown { 26435c86bf6SMark Brown wm831x_rtc->alarm_enabled = 0; 26535c86bf6SMark Brown 26635c86bf6SMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 26735c86bf6SMark Brown WM831X_RTC_ALM_ENA, 0); 26835c86bf6SMark Brown } 26935c86bf6SMark Brown 27035c86bf6SMark Brown static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc) 27135c86bf6SMark Brown { 27235c86bf6SMark Brown wm831x_rtc->alarm_enabled = 1; 27335c86bf6SMark Brown 27435c86bf6SMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 27535c86bf6SMark Brown WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA); 27635c86bf6SMark Brown } 27735c86bf6SMark Brown 27835c86bf6SMark Brown static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 27935c86bf6SMark Brown { 28035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 28135c86bf6SMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 28235c86bf6SMark Brown int ret; 28335c86bf6SMark Brown unsigned long time; 28435c86bf6SMark Brown 28523992449SAlexandre Belloni time = rtc_tm_to_time64(&alrm->time); 28635c86bf6SMark Brown 28735c86bf6SMark Brown ret = wm831x_rtc_stop_alarm(wm831x_rtc); 28835c86bf6SMark Brown if (ret < 0) { 28935c86bf6SMark Brown dev_err(dev, "Failed to stop alarm: %d\n", ret); 29035c86bf6SMark Brown return ret; 29135c86bf6SMark Brown } 29235c86bf6SMark Brown 29335c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1, 29435c86bf6SMark Brown (time >> 16) & 0xffff); 29535c86bf6SMark Brown if (ret < 0) { 29635c86bf6SMark Brown dev_err(dev, "Failed to write ALARM_1: %d\n", ret); 29735c86bf6SMark Brown return ret; 29835c86bf6SMark Brown } 29935c86bf6SMark Brown 30035c86bf6SMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff); 30135c86bf6SMark Brown if (ret < 0) { 30235c86bf6SMark Brown dev_err(dev, "Failed to write ALARM_2: %d\n", ret); 30335c86bf6SMark Brown return ret; 30435c86bf6SMark Brown } 30535c86bf6SMark Brown 30635c86bf6SMark Brown if (alrm->enabled) { 30735c86bf6SMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc); 30835c86bf6SMark Brown if (ret < 0) { 30935c86bf6SMark Brown dev_err(dev, "Failed to start alarm: %d\n", ret); 31035c86bf6SMark Brown return ret; 31135c86bf6SMark Brown } 31235c86bf6SMark Brown } 31335c86bf6SMark Brown 31435c86bf6SMark Brown return 0; 31535c86bf6SMark Brown } 31635c86bf6SMark Brown 31735c86bf6SMark Brown static int wm831x_rtc_alarm_irq_enable(struct device *dev, 31835c86bf6SMark Brown unsigned int enabled) 31935c86bf6SMark Brown { 32035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 32135c86bf6SMark Brown 32235c86bf6SMark Brown if (enabled) 32335c86bf6SMark Brown return wm831x_rtc_start_alarm(wm831x_rtc); 32435c86bf6SMark Brown else 32535c86bf6SMark Brown return wm831x_rtc_stop_alarm(wm831x_rtc); 32635c86bf6SMark Brown } 32735c86bf6SMark Brown 32835c86bf6SMark Brown static irqreturn_t wm831x_alm_irq(int irq, void *data) 32935c86bf6SMark Brown { 33035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = data; 33135c86bf6SMark Brown 33235c86bf6SMark Brown rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF); 33335c86bf6SMark Brown 33435c86bf6SMark Brown return IRQ_HANDLED; 33535c86bf6SMark Brown } 33635c86bf6SMark Brown 33735c86bf6SMark Brown static const struct rtc_class_ops wm831x_rtc_ops = { 33835c86bf6SMark Brown .read_time = wm831x_rtc_readtime, 339*498ce4e7SAlexandre Belloni .set_time = wm831x_rtc_settime, 34035c86bf6SMark Brown .read_alarm = wm831x_rtc_readalarm, 34135c86bf6SMark Brown .set_alarm = wm831x_rtc_setalarm, 34235c86bf6SMark Brown .alarm_irq_enable = wm831x_rtc_alarm_irq_enable, 34335c86bf6SMark Brown }; 34435c86bf6SMark Brown 34535c86bf6SMark Brown #ifdef CONFIG_PM 34635c86bf6SMark Brown /* Turn off the alarm if it should not be a wake source. */ 34735c86bf6SMark Brown static int wm831x_rtc_suspend(struct device *dev) 34835c86bf6SMark Brown { 34935c86bf6SMark Brown struct platform_device *pdev = to_platform_device(dev); 35035c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 35135c86bf6SMark Brown int ret, enable; 35235c86bf6SMark Brown 35335c86bf6SMark Brown if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev)) 35435c86bf6SMark Brown enable = WM831X_RTC_ALM_ENA; 35535c86bf6SMark Brown else 35635c86bf6SMark Brown enable = 0; 35735c86bf6SMark Brown 35835c86bf6SMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 35935c86bf6SMark Brown WM831X_RTC_ALM_ENA, enable); 36035c86bf6SMark Brown if (ret != 0) 36135c86bf6SMark Brown dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret); 36235c86bf6SMark Brown 36335c86bf6SMark Brown return 0; 36435c86bf6SMark Brown } 36535c86bf6SMark Brown 36635c86bf6SMark Brown /* Enable the alarm if it should be enabled (in case it was disabled to 36735c86bf6SMark Brown * prevent use as a wake source). 36835c86bf6SMark Brown */ 36935c86bf6SMark Brown static int wm831x_rtc_resume(struct device *dev) 37035c86bf6SMark Brown { 37135c86bf6SMark Brown struct platform_device *pdev = to_platform_device(dev); 37235c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 37335c86bf6SMark Brown int ret; 37435c86bf6SMark Brown 37535c86bf6SMark Brown if (wm831x_rtc->alarm_enabled) { 37635c86bf6SMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc); 37735c86bf6SMark Brown if (ret != 0) 37835c86bf6SMark Brown dev_err(&pdev->dev, 37935c86bf6SMark Brown "Failed to restart RTC alarm: %d\n", ret); 38035c86bf6SMark Brown } 38135c86bf6SMark Brown 38235c86bf6SMark Brown return 0; 38335c86bf6SMark Brown } 38435c86bf6SMark Brown 38535c86bf6SMark Brown /* Unconditionally disable the alarm */ 38635c86bf6SMark Brown static int wm831x_rtc_freeze(struct device *dev) 38735c86bf6SMark Brown { 38835c86bf6SMark Brown struct platform_device *pdev = to_platform_device(dev); 38935c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 39035c86bf6SMark Brown int ret; 39135c86bf6SMark Brown 39235c86bf6SMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 39335c86bf6SMark Brown WM831X_RTC_ALM_ENA, 0); 39435c86bf6SMark Brown if (ret != 0) 39535c86bf6SMark Brown dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret); 39635c86bf6SMark Brown 39735c86bf6SMark Brown return 0; 39835c86bf6SMark Brown } 39935c86bf6SMark Brown #else 40035c86bf6SMark Brown #define wm831x_rtc_suspend NULL 40135c86bf6SMark Brown #define wm831x_rtc_resume NULL 40235c86bf6SMark Brown #define wm831x_rtc_freeze NULL 40335c86bf6SMark Brown #endif 40435c86bf6SMark Brown 40535c86bf6SMark Brown static int wm831x_rtc_probe(struct platform_device *pdev) 40635c86bf6SMark Brown { 40735c86bf6SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 40835c86bf6SMark Brown struct wm831x_rtc *wm831x_rtc; 409cd99758bSMark Brown int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM")); 41035c86bf6SMark Brown int ret = 0; 41135c86bf6SMark Brown 4125f85d20dSMark Brown wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL); 41335c86bf6SMark Brown if (wm831x_rtc == NULL) 41435c86bf6SMark Brown return -ENOMEM; 41535c86bf6SMark Brown 41635c86bf6SMark Brown platform_set_drvdata(pdev, wm831x_rtc); 41735c86bf6SMark Brown wm831x_rtc->wm831x = wm831x; 41835c86bf6SMark Brown 41935c86bf6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 42035c86bf6SMark Brown if (ret < 0) { 42135c86bf6SMark Brown dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); 422d315bc1bSAlexandre Belloni return ret; 42335c86bf6SMark Brown } 42435c86bf6SMark Brown if (ret & WM831X_RTC_ALM_ENA) 42535c86bf6SMark Brown wm831x_rtc->alarm_enabled = 1; 42635c86bf6SMark Brown 42735c86bf6SMark Brown device_init_wakeup(&pdev->dev, 1); 42835c86bf6SMark Brown 429b9a1d801SAlexandre Belloni wm831x_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); 430b9a1d801SAlexandre Belloni if (IS_ERR(wm831x_rtc->rtc)) 431b9a1d801SAlexandre Belloni return PTR_ERR(wm831x_rtc->rtc); 432b9a1d801SAlexandre Belloni 433b9a1d801SAlexandre Belloni wm831x_rtc->rtc->ops = &wm831x_rtc_ops; 434b9a1d801SAlexandre Belloni wm831x_rtc->rtc->range_max = U32_MAX; 435b9a1d801SAlexandre Belloni 436b9a1d801SAlexandre Belloni ret = rtc_register_device(wm831x_rtc->rtc); 437b9a1d801SAlexandre Belloni if (ret) 438b9a1d801SAlexandre Belloni return ret; 43935c86bf6SMark Brown 440fd5231ceSJingoo Han ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL, 441fd5231ceSJingoo Han wm831x_alm_irq, 4425815e5d3SMark Brown IRQF_TRIGGER_RISING, "RTC alarm", 44335c86bf6SMark Brown wm831x_rtc); 44435c86bf6SMark Brown if (ret != 0) { 44535c86bf6SMark Brown dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", 44635c86bf6SMark Brown alm_irq, ret); 44735c86bf6SMark Brown } 44835c86bf6SMark Brown 4499dccf55fSMark Brown wm831x_rtc_add_randomness(wm831x); 4509dccf55fSMark Brown 45135c86bf6SMark Brown return 0; 45235c86bf6SMark Brown } 45335c86bf6SMark Brown 45447145210SAlexey Dobriyan static const struct dev_pm_ops wm831x_rtc_pm_ops = { 45535c86bf6SMark Brown .suspend = wm831x_rtc_suspend, 45635c86bf6SMark Brown .resume = wm831x_rtc_resume, 45735c86bf6SMark Brown 45835c86bf6SMark Brown .freeze = wm831x_rtc_freeze, 45935c86bf6SMark Brown .thaw = wm831x_rtc_resume, 46035c86bf6SMark Brown .restore = wm831x_rtc_resume, 46135c86bf6SMark Brown 46235c86bf6SMark Brown .poweroff = wm831x_rtc_suspend, 46335c86bf6SMark Brown }; 46435c86bf6SMark Brown 46535c86bf6SMark Brown static struct platform_driver wm831x_rtc_driver = { 46635c86bf6SMark Brown .probe = wm831x_rtc_probe, 46735c86bf6SMark Brown .driver = { 46835c86bf6SMark Brown .name = "wm831x-rtc", 46935c86bf6SMark Brown .pm = &wm831x_rtc_pm_ops, 47035c86bf6SMark Brown }, 47135c86bf6SMark Brown }; 47235c86bf6SMark Brown 4730c4eae66SAxel Lin module_platform_driver(wm831x_rtc_driver); 47435c86bf6SMark Brown 47535c86bf6SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 47635c86bf6SMark Brown MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs"); 47735c86bf6SMark Brown MODULE_LICENSE("GPL"); 47835c86bf6SMark Brown MODULE_ALIAS("platform:wm831x-rtc"); 479