rtc-wm831x.c revision 9dccf55f4cb011a7552a8a2749a580662f5ed8ed
135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Real Time Clock driver for Wolfson Microelectronics WM831x 335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * 435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Copyright (C) 2009 Wolfson Microelectronics PLC. 535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * 635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * 835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * This program is free software; you can redistribute it and/or modify it 935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * under the terms of the GNU General Public License as published by the 1035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Free Software Foundation; either version 2 of the License, or (at your 1135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * option) any later version. 1235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * 1335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 1435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 1535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/module.h> 1635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/kernel.h> 1735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/time.h> 1835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/rtc.h> 195a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 2035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/bcd.h> 2135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/interrupt.h> 2235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/ioctl.h> 2335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/completion.h> 2435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/mfd/wm831x/core.h> 2535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/delay.h> 2635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#include <linux/platform_device.h> 279dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown#include <linux/random.h> 2835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 2935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 3035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16416 (0x4020) - RTC Write Counter 3135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 3235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */ 3335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */ 3435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */ 3535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 3635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 3735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16417 (0x4021) - RTC Time 1 3835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 3935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ 4035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ 4135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ 4235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 4435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16418 (0x4022) - RTC Time 2 4535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 4635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ 4735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ 4835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ 4935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 5035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 5135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16419 (0x4023) - RTC Alarm 1 5235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 5335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ 5435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ 5535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ 5635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 5735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 5835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16420 (0x4024) - RTC Alarm 2 5935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 6035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ 6135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ 6235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ 6335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 6435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 6535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16421 (0x4025) - RTC Control 6635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 6735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID 0x8000 /* RTC_VALID */ 6835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */ 6935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */ 7035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */ 7135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */ 7235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */ 7335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */ 7435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */ 7535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */ 7635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */ 7735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */ 7835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */ 7935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */ 8035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */ 8135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */ 8235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 8335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 8435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * R16422 (0x4026) - RTC Trim 8535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 8635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */ 8735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */ 8835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */ 8935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 9035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_SET_TIME_RETRIES 5 9135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define WM831X_GET_TIME_RETRIES 5 9235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 9335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstruct wm831x_rtc { 9435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x *wm831x; 9535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct rtc_device *rtc; 9635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown unsigned int alarm_enabled:1; 9735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}; 9835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 999dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brownstatic void wm831x_rtc_add_randomness(struct wm831x *wm831x) 1009dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown{ 1019dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown int ret; 1029dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown u16 reg; 1039dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown 1049dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown /* 1059dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown * The write counter contains a pseudo-random number which is 1069dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown * regenerated every time we set the RTC so it should be a 1079dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown * useful per-system source of entropy. 1089dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown */ 1099dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER); 1109dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown if (ret >= 0) { 1119dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown reg = ret; 1129dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown add_device_randomness(®, sizeof(reg)); 1139dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown } else { 1149dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n", 1159dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown ret); 1169dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown } 1179dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown} 1189dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown 11935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 12035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Read current time and date in RTC 12135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 12235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm) 12335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 12435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 12535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 12635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown u16 time1[2], time2[2]; 12735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 12835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int count = 0; 12935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 13035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown /* Has the RTC been programmed? */ 13135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 13235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 13335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret); 13435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 13535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 13635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (!(ret & WM831X_RTC_VALID)) { 13735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_dbg(dev, "RTC not yet configured\n"); 13835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return -EINVAL; 13935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 14035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 14135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown /* Read twice to make sure we don't read a corrupt, partially 14235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * incremented, value. 14335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 14435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown do { 14535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, 14635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 2, time1); 14735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) 14835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown continue; 14935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 15035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, 15135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 2, time2); 15235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) 15335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown continue; 15435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 15535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (memcmp(time1, time2, sizeof(time1)) == 0) { 15635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown u32 time = (time1[0] << 16) | time1[1]; 15735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 15835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown rtc_time_to_tm(time, tm); 15935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return rtc_valid_tm(tm); 16035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 16135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 16235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } while (++count < WM831X_GET_TIME_RETRIES); 16335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 16435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Timed out reading current time\n"); 16535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 16635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return -EIO; 16735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 16835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 16935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 17035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Set current time and date in RTC 17135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 17235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_set_mmss(struct device *dev, unsigned long time) 17335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 17435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 17535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 17635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct rtc_time new_tm; 17735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown unsigned long new_time; 17835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 17935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int count = 0; 18035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 18135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1, 18235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown (time >> 16) & 0xffff); 18335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 18435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to write TIME_1: %d\n", ret); 18535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 18635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 18735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 18835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff); 18935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 19035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to write TIME_2: %d\n", ret); 19135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 19235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 19335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 19435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown /* Wait for the update to complete - should happen first time 19535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * round but be conservative. 19635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 19735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown do { 19835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown msleep(1); 19935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 20035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 20135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) 20235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = WM831X_RTC_SYNC_BUSY; 20335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } while (!(ret & WM831X_RTC_SYNC_BUSY) && 20435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ++count < WM831X_SET_TIME_RETRIES); 20535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 20635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret & WM831X_RTC_SYNC_BUSY) { 20735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Timed out writing RTC update\n"); 20835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return -EIO; 20935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 21035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 21135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown /* Check that the update was accepted; security features may 21235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * have caused the update to be ignored. 21335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 21435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_rtc_readtime(dev, &new_tm); 21535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) 21635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 21735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 21835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = rtc_tm_to_time(&new_tm, &new_time); 21935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 22035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to convert time: %d\n", ret); 22135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 22235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 22335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 22435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown /* Allow a second of change in case of tick */ 22535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (new_time - time > 1) { 22635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "RTC update not permitted by hardware\n"); 22735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return -EPERM; 22835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 22935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 23035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 23135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 23235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 23335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* 23435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * Read alarm time and date in RTC 23535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 23635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) 23735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 23835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 23935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 24035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown u16 data[2]; 24135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown u32 time; 24235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 24335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1, 24435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 2, data); 24535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) { 24635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to read alarm time: %d\n", ret); 24735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 24835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 24935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 25035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown time = (data[0] << 16) | data[1]; 25135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 25235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown rtc_time_to_tm(time, &alrm->time); 25335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 25435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL); 25535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 25635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to read RTC control: %d\n", ret); 25735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 25835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 25935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 26035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret & WM831X_RTC_ALM_ENA) 26135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown alrm->enabled = 1; 26235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown else 26335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown alrm->enabled = 0; 26435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 26535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 26635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 26735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 26835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc) 26935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 27035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown wm831x_rtc->alarm_enabled = 0; 27135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 27235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 27335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown WM831X_RTC_ALM_ENA, 0); 27435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 27535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 27635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc) 27735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 27835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown wm831x_rtc->alarm_enabled = 1; 27935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 28035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 28135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA); 28235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 28335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 28435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 28535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 28635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 28735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x *wm831x = wm831x_rtc->wm831x; 28835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 28935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown unsigned long time; 29035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 29135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = rtc_tm_to_time(&alrm->time, &time); 29235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 29335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to convert time: %d\n", ret); 29435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 29535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 29635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 29735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_rtc_stop_alarm(wm831x_rtc); 29835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 29935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to stop alarm: %d\n", ret); 30035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 30135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 30235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 30335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1, 30435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown (time >> 16) & 0xffff); 30535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 30635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to write ALARM_1: %d\n", ret); 30735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 30835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 30935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 31035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff); 31135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 31235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to write ALARM_2: %d\n", ret); 31335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 31435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 31535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 31635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (alrm->enabled) { 31735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc); 31835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 31935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(dev, "Failed to start alarm: %d\n", ret); 32035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 32135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 32235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 32335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 32435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 32535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 32635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 32735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_alarm_irq_enable(struct device *dev, 32835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown unsigned int enabled) 32935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 33035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); 33135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 33235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (enabled) 33335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return wm831x_rtc_start_alarm(wm831x_rtc); 33435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown else 33535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return wm831x_rtc_stop_alarm(wm831x_rtc); 33635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 33735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 33835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic irqreturn_t wm831x_alm_irq(int irq, void *data) 33935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 34035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = data; 34135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 34235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF); 34335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 34435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return IRQ_HANDLED; 34535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 34635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 34735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic const struct rtc_class_ops wm831x_rtc_ops = { 34835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .read_time = wm831x_rtc_readtime, 34935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .set_mmss = wm831x_rtc_set_mmss, 35035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .read_alarm = wm831x_rtc_readalarm, 35135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .set_alarm = wm831x_rtc_setalarm, 35235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .alarm_irq_enable = wm831x_rtc_alarm_irq_enable, 35335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}; 35435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 35535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#ifdef CONFIG_PM 35635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* Turn off the alarm if it should not be a wake source. */ 35735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_suspend(struct device *dev) 35835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 35935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct platform_device *pdev = to_platform_device(dev); 36035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 36135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret, enable; 36235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 36335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev)) 36435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown enable = WM831X_RTC_ALM_ENA; 36535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown else 36635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown enable = 0; 36735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 36835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 36935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown WM831X_RTC_ALM_ENA, enable); 37035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) 37135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret); 37235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 37335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 37435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 37535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 37635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* Enable the alarm if it should be enabled (in case it was disabled to 37735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown * prevent use as a wake source). 37835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown */ 37935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_resume(struct device *dev) 38035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 38135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct platform_device *pdev = to_platform_device(dev); 38235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 38335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 38435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 38535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (wm831x_rtc->alarm_enabled) { 38635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_rtc_start_alarm(wm831x_rtc); 38735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) 38835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(&pdev->dev, 38935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown "Failed to restart RTC alarm: %d\n", ret); 39035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 39135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 39235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 39335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 39435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 39535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown/* Unconditionally disable the alarm */ 39635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_freeze(struct device *dev) 39735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 39835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct platform_device *pdev = to_platform_device(dev); 39935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev); 40035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret; 40135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 40235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, 40335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown WM831X_RTC_ALM_ENA, 0); 40435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) 40535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret); 40635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 40735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 40835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 40935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#else 41035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define wm831x_rtc_suspend NULL 41135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define wm831x_rtc_resume NULL 41235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#define wm831x_rtc_freeze NULL 41335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown#endif 41435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 41535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int wm831x_rtc_probe(struct platform_device *pdev) 41635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 41735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 41835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc; 419cd99758ba3bde64347a8ece381cbae2fb5c745b2Mark Brown int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM")); 42035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int ret = 0; 42135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4225f85d20d04cdc4c6ed15022a5ed76907ad88d4aeMark Brown wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL); 42335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (wm831x_rtc == NULL) 42435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return -ENOMEM; 42535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 42635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown platform_set_drvdata(pdev, wm831x_rtc); 42735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown wm831x_rtc->wm831x = wm831x; 42835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 42935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); 43035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret < 0) { 43135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); 43235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown goto err; 43335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 43435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret & WM831X_RTC_ALM_ENA) 43535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown wm831x_rtc->alarm_enabled = 1; 43635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 43735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown device_init_wakeup(&pdev->dev, 1); 43835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 43935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown wm831x_rtc->rtc = rtc_device_register("wm831x", &pdev->dev, 44035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown &wm831x_rtc_ops, THIS_MODULE); 44135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (IS_ERR(wm831x_rtc->rtc)) { 44235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown ret = PTR_ERR(wm831x_rtc->rtc); 44335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown goto err; 44435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 44535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4465815e5d36eff44b3dd38943f3a98a4d9ce985118Mark Brown ret = request_threaded_irq(alm_irq, NULL, wm831x_alm_irq, 4475815e5d36eff44b3dd38943f3a98a4d9ce985118Mark Brown IRQF_TRIGGER_RISING, "RTC alarm", 4485815e5d36eff44b3dd38943f3a98a4d9ce985118Mark Brown wm831x_rtc); 44935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown if (ret != 0) { 45035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", 45135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown alm_irq, ret); 45235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown } 45335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4549dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown wm831x_rtc_add_randomness(wm831x); 4559dccf55f4cb011a7552a8a2749a580662f5ed8edMark Brown 45635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 45735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 45835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownerr: 45935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return ret; 46035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 46135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 46235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic int __devexit wm831x_rtc_remove(struct platform_device *pdev) 46335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown{ 46435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown struct wm831x_rtc *wm831x_rtc = platform_get_drvdata(pdev); 46535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown int alm_irq = platform_get_irq_byname(pdev, "ALM"); 46635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4675815e5d36eff44b3dd38943f3a98a4d9ce985118Mark Brown free_irq(alm_irq, wm831x_rtc); 46835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown rtc_device_unregister(wm831x_rtc->rtc); 46935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 47035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown return 0; 47135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown} 47235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 473471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops wm831x_rtc_pm_ops = { 47435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .suspend = wm831x_rtc_suspend, 47535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .resume = wm831x_rtc_resume, 47635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 47735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .freeze = wm831x_rtc_freeze, 47835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .thaw = wm831x_rtc_resume, 47935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .restore = wm831x_rtc_resume, 48035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 48135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .poweroff = wm831x_rtc_suspend, 48235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}; 48335c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 48435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brownstatic struct platform_driver wm831x_rtc_driver = { 48535c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .probe = wm831x_rtc_probe, 48635c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .remove = __devexit_p(wm831x_rtc_remove), 48735c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .driver = { 48835c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .name = "wm831x-rtc", 48935c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown .pm = &wm831x_rtc_pm_ops, 49035c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown }, 49135c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown}; 49235c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 4930c4eae66591a292fee70051ea363a8d27aa54102Axel Linmodule_platform_driver(wm831x_rtc_driver); 49435c86bf66d9d0ebc3f32f8c56251197b3921394eMark Brown 49535c86bf66d9d0ebc3f32f8c56251197b3921394eMark BrownMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 49635c86bf66d9d0ebc3f32f8c56251197b3921394eMark BrownMODULE_DESCRIPTION("RTC driver for the WM831x series PMICs"); 49735c86bf66d9d0ebc3f32f8c56251197b3921394eMark BrownMODULE_LICENSE("GPL"); 49835c86bf66d9d0ebc3f32f8c56251197b3921394eMark BrownMODULE_ALIAS("platform:wm831x-rtc"); 499