Blame view

drivers/rtc/rtc-da9052.c 6.58 KB
fef931ff9   Ashish Jangam   rtc: driver for D...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * Real time clock driver for DA9052
   *
   * Copyright(c) 2012 Dialog Semiconductor Ltd.
   *
   * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   */
  
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/rtc.h>
11f54d058   Sachin Kamat   drivers/rtc/rtc-d...
18
  #include <linux/err.h>
fef931ff9   Ashish Jangam   rtc: driver for D...
19
20
21
  
  #include <linux/mfd/da9052/da9052.h>
  #include <linux/mfd/da9052/reg.h>
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
22
23
  #define rtc_err(rtc, fmt, ...) \
  		dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
fef931ff9   Ashish Jangam   rtc: driver for D...
24
25
26
27
  
  struct da9052_rtc {
  	struct rtc_device *rtc;
  	struct da9052 *da9052;
fef931ff9   Ashish Jangam   rtc: driver for D...
28
  };
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
29
  static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable)
fef931ff9   Ashish Jangam   rtc: driver for D...
30
31
32
  {
  	int ret;
  	if (enable) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
33
34
35
  		ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
  				DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON,
  				DA9052_ALARM_Y_ALARM_ON);
fef931ff9   Ashish Jangam   rtc: driver for D...
36
  		if (ret != 0)
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
37
38
  			rtc_err(rtc, "Failed to enable ALM: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
39
  	} else {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
40
41
  		ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
  			DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0);
fef931ff9   Ashish Jangam   rtc: driver for D...
42
  		if (ret != 0)
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
43
44
  			rtc_err(rtc, "Write error: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
45
46
47
48
49
50
51
  	}
  	return ret;
  }
  
  static irqreturn_t da9052_rtc_irq(int irq, void *data)
  {
  	struct da9052_rtc *rtc = data;
fef931ff9   Ashish Jangam   rtc: driver for D...
52

7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
53
  	rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
fef931ff9   Ashish Jangam   rtc: driver for D...
54
55
56
  
  	return IRQ_HANDLED;
  }
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
57
  static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
fef931ff9   Ashish Jangam   rtc: driver for D...
58
59
60
  {
  	int ret;
  	uint8_t v[5];
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
61
  	ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, v);
fef931ff9   Ashish Jangam   rtc: driver for D...
62
  	if (ret != 0) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
63
64
  		rtc_err(rtc, "Failed to group read ALM: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
65
66
67
68
69
70
71
72
73
74
  		return ret;
  	}
  
  	rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100;
  	rtc_tm->tm_mon  = (v[3] & DA9052_RTC_MONTH) - 1;
  	rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY;
  	rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR;
  	rtc_tm->tm_min  = v[0] & DA9052_RTC_MIN;
  
  	ret = rtc_valid_tm(rtc_tm);
fef931ff9   Ashish Jangam   rtc: driver for D...
75
76
  	return ret;
  }
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
77
  static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
fef931ff9   Ashish Jangam   rtc: driver for D...
78
  {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
79
80
  	struct da9052 *da9052 = rtc->da9052;
  	unsigned long alm_time;
fef931ff9   Ashish Jangam   rtc: driver for D...
81
82
  	int ret;
  	uint8_t v[3];
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
83
84
85
86
87
88
89
90
91
  	ret = rtc_tm_to_time(rtc_tm, &alm_time);
  	if (ret != 0)
  		return ret;
  
  	if (rtc_tm->tm_sec > 0) {
  		alm_time += 60 - rtc_tm->tm_sec;
  		rtc_time_to_tm(alm_time, rtc_tm);
  	}
  	BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */
fef931ff9   Ashish Jangam   rtc: driver for D...
92
93
94
95
96
97
  	rtc_tm->tm_year -= 100;
  	rtc_tm->tm_mon += 1;
  
  	ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG,
  				DA9052_RTC_MIN, rtc_tm->tm_min);
  	if (ret != 0) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
98
99
  		rtc_err(rtc, "Failed to write ALRM MIN: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  		return ret;
  	}
  
  	v[0] = rtc_tm->tm_hour;
  	v[1] = rtc_tm->tm_mday;
  	v[2] = rtc_tm->tm_mon;
  
  	ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v);
  	if (ret < 0)
  		return ret;
  
  	ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
  				DA9052_RTC_YEAR, rtc_tm->tm_year);
  	if (ret != 0)
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
114
115
  		rtc_err(rtc, "Failed to write ALRM YEAR: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
116
117
118
  
  	return ret;
  }
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
119
  static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc)
fef931ff9   Ashish Jangam   rtc: driver for D...
120
121
  {
  	int ret;
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
122
  	ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG);
fef931ff9   Ashish Jangam   rtc: driver for D...
123
  	if (ret < 0) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
124
125
  		rtc_err(rtc, "Failed to read ALM: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
126
127
  		return ret;
  	}
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
128
129
  
  	return !!(ret&DA9052_ALARM_Y_ALARM_ON);
fef931ff9   Ashish Jangam   rtc: driver for D...
130
131
132
133
134
135
136
137
138
139
  }
  
  static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
  {
  	struct da9052_rtc *rtc = dev_get_drvdata(dev);
  	uint8_t v[6];
  	int ret;
  
  	ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
  	if (ret < 0) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
140
141
  		rtc_err(rtc, "Failed to read RTC time : %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
142
143
144
145
146
147
148
149
150
151
152
  		return ret;
  	}
  
  	rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100;
  	rtc_tm->tm_mon  = (v[4] & DA9052_RTC_MONTH) - 1;
  	rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY;
  	rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR;
  	rtc_tm->tm_min  = v[1] & DA9052_RTC_MIN;
  	rtc_tm->tm_sec  = v[0] & DA9052_RTC_SEC;
  
  	ret = rtc_valid_tm(rtc_tm);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
153
  	return ret;
fef931ff9   Ashish Jangam   rtc: driver for D...
154
155
156
157
158
159
  }
  
  static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
  	struct da9052_rtc *rtc;
  	uint8_t v[6];
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
160
  	int ret;
fef931ff9   Ashish Jangam   rtc: driver for D...
161
162
163
164
165
166
167
168
169
  
  	rtc = dev_get_drvdata(dev);
  
  	v[0] = tm->tm_sec;
  	v[1] = tm->tm_min;
  	v[2] = tm->tm_hour;
  	v[3] = tm->tm_mday;
  	v[4] = tm->tm_mon + 1;
  	v[5] = tm->tm_year - 100;
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
170
171
172
173
174
  	ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
  	if (ret < 0)
  		rtc_err(rtc, "failed to set RTC time: %d
  ", ret);
  	return ret;
fef931ff9   Ashish Jangam   rtc: driver for D...
175
176
177
178
179
180
181
  }
  
  static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	int ret;
  	struct rtc_time *tm = &alrm->time;
  	struct da9052_rtc *rtc = dev_get_drvdata(dev);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
182
183
184
185
  	ret = da9052_read_alarm(rtc, tm);
  	if (ret < 0) {
  		rtc_err(rtc, "failed to read RTC alarm: %d
  ", ret);
fef931ff9   Ashish Jangam   rtc: driver for D...
186
  		return ret;
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
187
  	}
fef931ff9   Ashish Jangam   rtc: driver for D...
188

7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
189
  	alrm->enabled = da9052_rtc_get_alarm_status(rtc);
fef931ff9   Ashish Jangam   rtc: driver for D...
190
191
192
193
194
195
196
197
  	return 0;
  }
  
  static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	int ret;
  	struct rtc_time *tm = &alrm->time;
  	struct da9052_rtc *rtc = dev_get_drvdata(dev);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
198
  	ret = da9052_rtc_enable_alarm(rtc, 0);
fef931ff9   Ashish Jangam   rtc: driver for D...
199
200
  	if (ret < 0)
  		return ret;
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
201
202
  	ret = da9052_set_alarm(rtc, tm);
  	if (ret < 0)
fef931ff9   Ashish Jangam   rtc: driver for D...
203
  		return ret;
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
204
  	ret = da9052_rtc_enable_alarm(rtc, 1);
fef931ff9   Ashish Jangam   rtc: driver for D...
205
206
207
208
209
210
  	return ret;
  }
  
  static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
  {
  	struct da9052_rtc *rtc = dev_get_drvdata(dev);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
211
  	return da9052_rtc_enable_alarm(rtc, enabled);
fef931ff9   Ashish Jangam   rtc: driver for D...
212
213
214
215
216
217
218
219
220
  }
  
  static const struct rtc_class_ops da9052_rtc_ops = {
  	.read_time	= da9052_rtc_read_time,
  	.set_time	= da9052_rtc_set_time,
  	.read_alarm	= da9052_rtc_read_alarm,
  	.set_alarm	= da9052_rtc_set_alarm,
  	.alarm_irq_enable = da9052_rtc_alarm_irq_enable,
  };
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
221
  static int da9052_rtc_probe(struct platform_device *pdev)
fef931ff9   Ashish Jangam   rtc: driver for D...
222
223
224
225
226
227
228
229
230
231
  {
  	struct da9052_rtc *rtc;
  	int ret;
  
  	rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL);
  	if (!rtc)
  		return -ENOMEM;
  
  	rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
  	platform_set_drvdata(pdev, rtc);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  
  	ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE);
  	if (ret < 0) {
  		rtc_err(rtc,
  			"Failed to setup RTC battery charging: %d
  ", ret);
  		return ret;
  	}
  
  	ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
  				DA9052_ALARM_Y_TICK_ON, 0);
  	if (ret != 0)
  		rtc_err(rtc, "Failed to disable TICKS: %d
  ", ret);
c2c0eed7f   Anthony Olech   drivers/rtc/rtc-d...
246
  	ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM",
925e8ea6b   Ashish Jangam   drivers/rtc/rtc-d...
247
  				da9052_rtc_irq, rtc);
fef931ff9   Ashish Jangam   rtc: driver for D...
248
  	if (ret != 0) {
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
249
250
  		rtc_err(rtc, "irq registration failed: %d
  ", ret);
007def046   Devendra Naga   rtc/rtc-da9052: r...
251
  		return ret;
fef931ff9   Ashish Jangam   rtc: driver for D...
252
  	}
3689cd741   Jingoo Han   rtc: rtc-da9052: ...
253
  	rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
fef931ff9   Ashish Jangam   rtc: driver for D...
254
  				       &da9052_rtc_ops, THIS_MODULE);
dac30a984   Sachin Kamat   drivers/rtc: Repl...
255
  	return PTR_ERR_OR_ZERO(rtc->rtc);
fef931ff9   Ashish Jangam   rtc: driver for D...
256
  }
fef931ff9   Ashish Jangam   rtc: driver for D...
257
258
  static struct platform_driver da9052_rtc_driver = {
  	.probe	= da9052_rtc_probe,
fef931ff9   Ashish Jangam   rtc: driver for D...
259
260
261
262
263
264
265
  	.driver = {
  		.name	= "da9052-rtc",
  		.owner	= THIS_MODULE,
  	},
  };
  
  module_platform_driver(da9052_rtc_driver);
7c994c08c   Anthony Olech   drivers/rtc/rtc-d...
266
  MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
fef931ff9   Ashish Jangam   rtc: driver for D...
267
268
269
  MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:da9052-rtc");