Commit afd8d0f940ba5078f38e435440089117ac7d9eb4

Authored by David Brownell
Committed by Linus Torvalds
1 parent 77a592655c

rtc: rtc-dm355evm driver

Simple RTC driver for the MSP430 firmware on the DM355 EVM board.  Other
than not supporting atomic reads/writes of all four bytes, this is
reasonable as a basic no-alarm RTC.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 182 additions and 0 deletions Side-by-side Diff

... ... @@ -241,6 +241,12 @@
241 241 If you say Y here you will get support for the
242 242 watchdog timer in the ST M41T60 and M41T80 RTC chips series.
243 243  
  244 +config RTC_DRV_DM355EVM
  245 + tristate "TI DaVinci DM355 EVM RTC"
  246 + depends on MFD_DM355EVM_MSP
  247 + help
  248 + Supports the RTC firmware in the MSP430 on the DM355 EVM.
  249 +
244 250 config RTC_DRV_TWL92330
245 251 boolean "TI TWL92330/Menelaus"
246 252 depends on MENELAUS
drivers/rtc/Makefile
... ... @@ -23,6 +23,7 @@
23 23 obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
24 24 obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
25 25 obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
  26 +obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
26 27 obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
27 28 obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
28 29 obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
drivers/rtc/rtc-dm355evm.c
  1 +/*
  2 + * rtc-dm355evm.c - access battery-backed counter in MSP430 firmware
  3 + *
  4 + * Copyright (c) 2008 by David Brownell
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the License, or (at your option) any later version.
  10 + */
  11 +#include <linux/kernel.h>
  12 +#include <linux/init.h>
  13 +#include <linux/rtc.h>
  14 +#include <linux/platform_device.h>
  15 +
  16 +#include <linux/i2c/dm355evm_msp.h>
  17 +
  18 +
  19 +/*
  20 + * The MSP430 firmware on the DM355 EVM uses a watch crystal to feed
  21 + * a 1 Hz counter. When a backup battery is supplied, that makes a
  22 + * reasonable RTC for applications where alarms and non-NTP drift
  23 + * compensation aren't important.
  24 + *
  25 + * The only real glitch is the inability to read or write all four
  26 + * counter bytes atomically: the count may increment in the middle
  27 + * of an operation, causing trouble when the LSB rolls over.
  28 + *
  29 + * This driver was tested with firmware revision A4.
  30 + */
  31 +union evm_time {
  32 + u8 bytes[4];
  33 + u32 value;
  34 +};
  35 +
  36 +static int dm355evm_rtc_read_time(struct device *dev, struct rtc_time *tm)
  37 +{
  38 + union evm_time time;
  39 + int status;
  40 + int tries = 0;
  41 +
  42 + do {
  43 + /*
  44 + * Read LSB(0) to MSB(3) bytes. Defend against the counter
  45 + * rolling over by re-reading until the value is stable,
  46 + * and assuming the four reads take at most a few seconds.
  47 + */
  48 + status = dm355evm_msp_read(DM355EVM_MSP_RTC_0);
  49 + if (status < 0)
  50 + return status;
  51 + if (tries && time.bytes[0] == status)
  52 + break;
  53 + time.bytes[0] = status;
  54 +
  55 + status = dm355evm_msp_read(DM355EVM_MSP_RTC_1);
  56 + if (status < 0)
  57 + return status;
  58 + if (tries && time.bytes[1] == status)
  59 + break;
  60 + time.bytes[1] = status;
  61 +
  62 + status = dm355evm_msp_read(DM355EVM_MSP_RTC_2);
  63 + if (status < 0)
  64 + return status;
  65 + if (tries && time.bytes[2] == status)
  66 + break;
  67 + time.bytes[2] = status;
  68 +
  69 + status = dm355evm_msp_read(DM355EVM_MSP_RTC_3);
  70 + if (status < 0)
  71 + return status;
  72 + if (tries && time.bytes[3] == status)
  73 + break;
  74 + time.bytes[3] = status;
  75 +
  76 + } while (++tries < 5);
  77 +
  78 + dev_dbg(dev, "read timestamp %08x\n", time.value);
  79 +
  80 + rtc_time_to_tm(le32_to_cpu(time.value), tm);
  81 + return 0;
  82 +}
  83 +
  84 +static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm)
  85 +{
  86 + union evm_time time;
  87 + unsigned long value;
  88 + int status;
  89 +
  90 + rtc_tm_to_time(tm, &value);
  91 + time.value = cpu_to_le32(value);
  92 +
  93 + dev_dbg(dev, "write timestamp %08x\n", time.value);
  94 +
  95 + /*
  96 + * REVISIT handle non-atomic writes ... maybe just retry until
  97 + * byte[1] sticks (no rollover)?
  98 + */
  99 + status = dm355evm_msp_write(time.bytes[0], DM355EVM_MSP_RTC_0);
  100 + if (status < 0)
  101 + return status;
  102 +
  103 + status = dm355evm_msp_write(time.bytes[1], DM355EVM_MSP_RTC_1);
  104 + if (status < 0)
  105 + return status;
  106 +
  107 + status = dm355evm_msp_write(time.bytes[2], DM355EVM_MSP_RTC_2);
  108 + if (status < 0)
  109 + return status;
  110 +
  111 + status = dm355evm_msp_write(time.bytes[3], DM355EVM_MSP_RTC_3);
  112 + if (status < 0)
  113 + return status;
  114 +
  115 + return 0;
  116 +}
  117 +
  118 +static struct rtc_class_ops dm355evm_rtc_ops = {
  119 + .read_time = dm355evm_rtc_read_time,
  120 + .set_time = dm355evm_rtc_set_time,
  121 +};
  122 +
  123 +/*----------------------------------------------------------------------*/
  124 +
  125 +static int __devinit dm355evm_rtc_probe(struct platform_device *pdev)
  126 +{
  127 + struct rtc_device *rtc;
  128 +
  129 + rtc = rtc_device_register(pdev->name,
  130 + &pdev->dev, &dm355evm_rtc_ops, THIS_MODULE);
  131 + if (IS_ERR(rtc)) {
  132 + dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
  133 + PTR_ERR(rtc));
  134 + return PTR_ERR(rtc);
  135 + }
  136 + platform_set_drvdata(pdev, rtc);
  137 +
  138 + return 0;
  139 +}
  140 +
  141 +static int __devexit dm355evm_rtc_remove(struct platform_device *pdev)
  142 +{
  143 + struct rtc_device *rtc = platform_get_drvdata(pdev);
  144 +
  145 + rtc_device_unregister(rtc);
  146 + platform_set_drvdata(pdev, NULL);
  147 + return 0;
  148 +}
  149 +
  150 +/*
  151 + * I2C is used to talk to the MSP430, but this platform device is
  152 + * exposed by an MFD driver that manages I2C communications.
  153 + */
  154 +static struct platform_driver rtc_dm355evm_driver = {
  155 + .probe = dm355evm_rtc_probe,
  156 + .remove = __devexit_p(dm355evm_rtc_remove),
  157 + .driver = {
  158 + .owner = THIS_MODULE,
  159 + .name = "rtc-dm355evm",
  160 + },
  161 +};
  162 +
  163 +static int __init dm355evm_rtc_init(void)
  164 +{
  165 + return platform_driver_register(&rtc_dm355evm_driver);
  166 +}
  167 +module_init(dm355evm_rtc_init);
  168 +
  169 +static void __exit dm355evm_rtc_exit(void)
  170 +{
  171 + platform_driver_unregister(&rtc_dm355evm_driver);
  172 +}
  173 +module_exit(dm355evm_rtc_exit);
  174 +
  175 +MODULE_LICENSE("GPL");