Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * rtc-dm355evm.c - access battery-backed counter in MSP430 firmware
0004  *
0005  * Copyright (c) 2008 by David Brownell
0006  */
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/rtc.h>
0010 #include <linux/platform_device.h>
0011 
0012 #include <linux/mfd/dm355evm_msp.h>
0013 #include <linux/module.h>
0014 
0015 
0016 /*
0017  * The MSP430 firmware on the DM355 EVM uses a watch crystal to feed
0018  * a 1 Hz counter.  When a backup battery is supplied, that makes a
0019  * reasonable RTC for applications where alarms and non-NTP drift
0020  * compensation aren't important.
0021  *
0022  * The only real glitch is the inability to read or write all four
0023  * counter bytes atomically:  the count may increment in the middle
0024  * of an operation, causing trouble when the LSB rolls over.
0025  *
0026  * This driver was tested with firmware revision A4.
0027  */
0028 union evm_time {
0029     u8  bytes[4];
0030     u32 value;
0031 };
0032 
0033 static int dm355evm_rtc_read_time(struct device *dev, struct rtc_time *tm)
0034 {
0035     union evm_time  time;
0036     int     status;
0037     int     tries = 0;
0038 
0039     do {
0040         /*
0041          * Read LSB(0) to MSB(3) bytes.  Defend against the counter
0042          * rolling over by re-reading until the value is stable,
0043          * and assuming the four reads take at most a few seconds.
0044          */
0045         status = dm355evm_msp_read(DM355EVM_MSP_RTC_0);
0046         if (status < 0)
0047             return status;
0048         if (tries && time.bytes[0] == status)
0049             break;
0050         time.bytes[0] = status;
0051 
0052         status = dm355evm_msp_read(DM355EVM_MSP_RTC_1);
0053         if (status < 0)
0054             return status;
0055         if (tries && time.bytes[1] == status)
0056             break;
0057         time.bytes[1] = status;
0058 
0059         status = dm355evm_msp_read(DM355EVM_MSP_RTC_2);
0060         if (status < 0)
0061             return status;
0062         if (tries && time.bytes[2] == status)
0063             break;
0064         time.bytes[2] = status;
0065 
0066         status = dm355evm_msp_read(DM355EVM_MSP_RTC_3);
0067         if (status < 0)
0068             return status;
0069         if (tries && time.bytes[3] == status)
0070             break;
0071         time.bytes[3] = status;
0072 
0073     } while (++tries < 5);
0074 
0075     dev_dbg(dev, "read timestamp %08x\n", time.value);
0076 
0077     rtc_time64_to_tm(le32_to_cpu(time.value), tm);
0078     return 0;
0079 }
0080 
0081 static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm)
0082 {
0083     union evm_time  time;
0084     unsigned long   value;
0085     int     status;
0086 
0087     value = rtc_tm_to_time64(tm);
0088     time.value = cpu_to_le32(value);
0089 
0090     dev_dbg(dev, "write timestamp %08x\n", time.value);
0091 
0092     /*
0093      * REVISIT handle non-atomic writes ... maybe just retry until
0094      * byte[1] sticks (no rollover)?
0095      */
0096     status = dm355evm_msp_write(time.bytes[0], DM355EVM_MSP_RTC_0);
0097     if (status < 0)
0098         return status;
0099 
0100     status = dm355evm_msp_write(time.bytes[1], DM355EVM_MSP_RTC_1);
0101     if (status < 0)
0102         return status;
0103 
0104     status = dm355evm_msp_write(time.bytes[2], DM355EVM_MSP_RTC_2);
0105     if (status < 0)
0106         return status;
0107 
0108     status = dm355evm_msp_write(time.bytes[3], DM355EVM_MSP_RTC_3);
0109     if (status < 0)
0110         return status;
0111 
0112     return 0;
0113 }
0114 
0115 static const struct rtc_class_ops dm355evm_rtc_ops = {
0116     .read_time  = dm355evm_rtc_read_time,
0117     .set_time   = dm355evm_rtc_set_time,
0118 };
0119 
0120 /*----------------------------------------------------------------------*/
0121 
0122 static int dm355evm_rtc_probe(struct platform_device *pdev)
0123 {
0124     struct rtc_device *rtc;
0125 
0126     rtc = devm_rtc_allocate_device(&pdev->dev);
0127     if (IS_ERR(rtc))
0128         return PTR_ERR(rtc);
0129 
0130     platform_set_drvdata(pdev, rtc);
0131 
0132     rtc->ops = &dm355evm_rtc_ops;
0133     rtc->range_max = U32_MAX;
0134 
0135     return devm_rtc_register_device(rtc);
0136 }
0137 
0138 /*
0139  * I2C is used to talk to the MSP430, but this platform device is
0140  * exposed by an MFD driver that manages I2C communications.
0141  */
0142 static struct platform_driver rtc_dm355evm_driver = {
0143     .probe      = dm355evm_rtc_probe,
0144     .driver     = {
0145         .name   = "rtc-dm355evm",
0146     },
0147 };
0148 
0149 module_platform_driver(rtc_dm355evm_driver);
0150 
0151 MODULE_LICENSE("GPL");