aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Bartczak <emilbart@gmail.com>2016-12-25 17:07:43 -0500
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-01-11 11:23:04 -0500
commit644d4c366b28faaee5d21dc1575a8b9b95a73cac (patch)
treee057ff0390e33c54beece4a201a23ca622fa5c0c
parent72877b51d0c5abf21646723f8347c5acf8fb8a45 (diff)
rtc: mcp795: add alarm support.
This patch adds alarm support. This allows to configure the chip to generate an interrupt when the alarm matches current time value. Alarm can be programmed up to one year in the future and is accurate to the second. Signed-off-by: Emil Bartczak <emilbart@gmail.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r--drivers/rtc/rtc-mcp795.c171
1 files changed, 170 insertions, 1 deletions
diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c
index 8107fc0a2be5..77f21331ae21 100644
--- a/drivers/rtc/rtc-mcp795.c
+++ b/drivers/rtc/rtc-mcp795.c
@@ -44,12 +44,22 @@
44#define MCP795_REG_DAY 0x04 44#define MCP795_REG_DAY 0x04
45#define MCP795_REG_MONTH 0x06 45#define MCP795_REG_MONTH 0x06
46#define MCP795_REG_CONTROL 0x08 46#define MCP795_REG_CONTROL 0x08
47#define MCP795_REG_ALM0_SECONDS 0x0C
48#define MCP795_REG_ALM0_DAY 0x0F
47 49
48#define MCP795_ST_BIT BIT(7) 50#define MCP795_ST_BIT BIT(7)
49#define MCP795_24_BIT BIT(6) 51#define MCP795_24_BIT BIT(6)
50#define MCP795_LP_BIT BIT(5) 52#define MCP795_LP_BIT BIT(5)
51#define MCP795_EXTOSC_BIT BIT(3) 53#define MCP795_EXTOSC_BIT BIT(3)
52#define MCP795_OSCON_BIT BIT(5) 54#define MCP795_OSCON_BIT BIT(5)
55#define MCP795_ALM0_BIT BIT(4)
56#define MCP795_ALM1_BIT BIT(5)
57#define MCP795_ALM0IF_BIT BIT(3)
58#define MCP795_ALM0C0_BIT BIT(4)
59#define MCP795_ALM0C1_BIT BIT(5)
60#define MCP795_ALM0C2_BIT BIT(6)
61
62#define SEC_PER_DAY (24 * 60 * 60)
53 63
54static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) 64static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count)
55{ 65{
@@ -150,6 +160,30 @@ static int mcp795_start_oscillator(struct device *dev, bool *extosc)
150 dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT); 160 dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT);
151} 161}
152 162
163/* Enable or disable Alarm 0 in RTC */
164static int mcp795_update_alarm(struct device *dev, bool enable)
165{
166 int ret;
167
168 dev_dbg(dev, "%s alarm\n", enable ? "Enable" : "Disable");
169
170 if (enable) {
171 /* clear ALM0IF (Alarm 0 Interrupt Flag) bit */
172 ret = mcp795_rtcc_set_bits(dev, MCP795_REG_ALM0_DAY,
173 MCP795_ALM0IF_BIT, 0);
174 if (ret)
175 return ret;
176 /* enable alarm 0 */
177 ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL,
178 MCP795_ALM0_BIT, MCP795_ALM0_BIT);
179 } else {
180 /* disable alarm 0 and alarm 1 */
181 ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL,
182 MCP795_ALM0_BIT | MCP795_ALM1_BIT, 0);
183 }
184 return ret;
185}
186
153static int mcp795_set_time(struct device *dev, struct rtc_time *tim) 187static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
154{ 188{
155 int ret; 189 int ret;
@@ -231,9 +265,127 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
231 return rtc_valid_tm(tim); 265 return rtc_valid_tm(tim);
232} 266}
233 267
268static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
269{
270 struct rtc_time now_tm;
271 time64_t now;
272 time64_t later;
273 u8 tmp[6];
274 int ret;
275
276 /* Read current time from RTC hardware */
277 ret = mcp795_read_time(dev, &now_tm);
278 if (ret)
279 return ret;
280 /* Get the number of seconds since 1970 */
281 now = rtc_tm_to_time64(&now_tm);
282 later = rtc_tm_to_time64(&alm->time);
283 if (later <= now)
284 return -EINVAL;
285 /* make sure alarm fires within the next one year */
286 if ((later - now) >=
287 (SEC_PER_DAY * (365 + is_leap_year(alm->time.tm_year))))
288 return -EDOM;
289 /* disable alarm */
290 ret = mcp795_update_alarm(dev, false);
291 if (ret)
292 return ret;
293 /* Read registers, so we can leave configuration bits untouched */
294 ret = mcp795_rtcc_read(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp));
295 if (ret)
296 return ret;
297
298 alm->time.tm_year = -1;
299 alm->time.tm_isdst = -1;
300 alm->time.tm_yday = -1;
301
302 tmp[0] = (tmp[0] & 0x80) | bin2bcd(alm->time.tm_sec);
303 tmp[1] = (tmp[1] & 0x80) | bin2bcd(alm->time.tm_min);
304 tmp[2] = (tmp[2] & 0xE0) | bin2bcd(alm->time.tm_hour);
305 tmp[3] = (tmp[3] & 0x80) | bin2bcd(alm->time.tm_wday + 1);
306 /* set alarm match: seconds, minutes, hour, day, date and month */
307 tmp[3] |= (MCP795_ALM0C2_BIT | MCP795_ALM0C1_BIT | MCP795_ALM0C0_BIT);
308 tmp[4] = (tmp[4] & 0xC0) | bin2bcd(alm->time.tm_mday);
309 tmp[5] = (tmp[5] & 0xE0) | bin2bcd(alm->time.tm_mon + 1);
310
311 ret = mcp795_rtcc_write(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp));
312 if (ret)
313 return ret;
314
315 /* enable alarm if requested */
316 if (alm->enabled) {
317 ret = mcp795_update_alarm(dev, true);
318 if (ret)
319 return ret;
320 dev_dbg(dev, "Alarm IRQ armed\n");
321 }
322 dev_dbg(dev, "Set alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
323 alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
324 alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
325 return 0;
326}
327
328static int mcp795_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
329{
330 u8 data[6];
331 int ret;
332
333 ret = mcp795_rtcc_read(
334 dev, MCP795_REG_ALM0_SECONDS, data, sizeof(data));
335 if (ret)
336 return ret;
337
338 alm->time.tm_sec = bcd2bin(data[0] & 0x7F);
339 alm->time.tm_min = bcd2bin(data[1] & 0x7F);
340 alm->time.tm_hour = bcd2bin(data[2] & 0x1F);
341 alm->time.tm_wday = bcd2bin(data[3] & 0x07) - 1;
342 alm->time.tm_mday = bcd2bin(data[4] & 0x3F);
343 alm->time.tm_mon = bcd2bin(data[5] & 0x1F) - 1;
344 alm->time.tm_year = -1;
345 alm->time.tm_isdst = -1;
346 alm->time.tm_yday = -1;
347
348 dev_dbg(dev, "Read alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
349 alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
350 alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
351 return 0;
352}
353
354static int mcp795_alarm_irq_enable(struct device *dev, unsigned int enabled)
355{
356 return mcp795_update_alarm(dev, !!enabled);
357}
358
359static irqreturn_t mcp795_irq(int irq, void *data)
360{
361 struct spi_device *spi = data;
362 struct rtc_device *rtc = spi_get_drvdata(spi);
363 struct mutex *lock = &rtc->ops_lock;
364 int ret;
365
366 mutex_lock(lock);
367
368 /* Disable alarm.
369 * There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit,
370 * because it is done every time when alarm is enabled.
371 */
372 ret = mcp795_update_alarm(&spi->dev, false);
373 if (ret)
374 dev_err(&spi->dev,
375 "Failed to disable alarm in IRQ (ret=%d)\n", ret);
376 rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
377
378 mutex_unlock(lock);
379
380 return IRQ_HANDLED;
381}
382
234static const struct rtc_class_ops mcp795_rtc_ops = { 383static const struct rtc_class_ops mcp795_rtc_ops = {
235 .read_time = mcp795_read_time, 384 .read_time = mcp795_read_time,
236 .set_time = mcp795_set_time 385 .set_time = mcp795_set_time,
386 .read_alarm = mcp795_read_alarm,
387 .set_alarm = mcp795_set_alarm,
388 .alarm_irq_enable = mcp795_alarm_irq_enable
237}; 389};
238 390
239static int mcp795_probe(struct spi_device *spi) 391static int mcp795_probe(struct spi_device *spi)
@@ -261,6 +413,23 @@ static int mcp795_probe(struct spi_device *spi)
261 413
262 spi_set_drvdata(spi, rtc); 414 spi_set_drvdata(spi, rtc);
263 415
416 if (spi->irq > 0) {
417 dev_dbg(&spi->dev, "Alarm support enabled\n");
418
419 /* Clear any pending alarm (ALM0IF bit) before requesting
420 * the interrupt.
421 */
422 mcp795_rtcc_set_bits(&spi->dev, MCP795_REG_ALM0_DAY,
423 MCP795_ALM0IF_BIT, 0);
424 ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
425 mcp795_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
426 dev_name(&rtc->dev), spi);
427 if (ret)
428 dev_err(&spi->dev, "Failed to request IRQ: %d: %d\n",
429 spi->irq, ret);
430 else
431 device_init_wakeup(&spi->dev, true);
432 }
264 return 0; 433 return 0;
265} 434}
266 435