rtc-dm355evm.c (3629B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * rtc-dm355evm.c - access battery-backed counter in MSP430 firmware 4 * 5 * Copyright (c) 2008 by David Brownell 6 */ 7#include <linux/kernel.h> 8#include <linux/init.h> 9#include <linux/rtc.h> 10#include <linux/platform_device.h> 11 12#include <linux/mfd/dm355evm_msp.h> 13#include <linux/module.h> 14 15 16/* 17 * The MSP430 firmware on the DM355 EVM uses a watch crystal to feed 18 * a 1 Hz counter. When a backup battery is supplied, that makes a 19 * reasonable RTC for applications where alarms and non-NTP drift 20 * compensation aren't important. 21 * 22 * The only real glitch is the inability to read or write all four 23 * counter bytes atomically: the count may increment in the middle 24 * of an operation, causing trouble when the LSB rolls over. 25 * 26 * This driver was tested with firmware revision A4. 27 */ 28union evm_time { 29 u8 bytes[4]; 30 u32 value; 31}; 32 33static int dm355evm_rtc_read_time(struct device *dev, struct rtc_time *tm) 34{ 35 union evm_time time; 36 int status; 37 int tries = 0; 38 39 do { 40 /* 41 * Read LSB(0) to MSB(3) bytes. Defend against the counter 42 * rolling over by re-reading until the value is stable, 43 * and assuming the four reads take at most a few seconds. 44 */ 45 status = dm355evm_msp_read(DM355EVM_MSP_RTC_0); 46 if (status < 0) 47 return status; 48 if (tries && time.bytes[0] == status) 49 break; 50 time.bytes[0] = status; 51 52 status = dm355evm_msp_read(DM355EVM_MSP_RTC_1); 53 if (status < 0) 54 return status; 55 if (tries && time.bytes[1] == status) 56 break; 57 time.bytes[1] = status; 58 59 status = dm355evm_msp_read(DM355EVM_MSP_RTC_2); 60 if (status < 0) 61 return status; 62 if (tries && time.bytes[2] == status) 63 break; 64 time.bytes[2] = status; 65 66 status = dm355evm_msp_read(DM355EVM_MSP_RTC_3); 67 if (status < 0) 68 return status; 69 if (tries && time.bytes[3] == status) 70 break; 71 time.bytes[3] = status; 72 73 } while (++tries < 5); 74 75 dev_dbg(dev, "read timestamp %08x\n", time.value); 76 77 rtc_time64_to_tm(le32_to_cpu(time.value), tm); 78 return 0; 79} 80 81static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm) 82{ 83 union evm_time time; 84 unsigned long value; 85 int status; 86 87 value = rtc_tm_to_time64(tm); 88 time.value = cpu_to_le32(value); 89 90 dev_dbg(dev, "write timestamp %08x\n", time.value); 91 92 /* 93 * REVISIT handle non-atomic writes ... maybe just retry until 94 * byte[1] sticks (no rollover)? 95 */ 96 status = dm355evm_msp_write(time.bytes[0], DM355EVM_MSP_RTC_0); 97 if (status < 0) 98 return status; 99 100 status = dm355evm_msp_write(time.bytes[1], DM355EVM_MSP_RTC_1); 101 if (status < 0) 102 return status; 103 104 status = dm355evm_msp_write(time.bytes[2], DM355EVM_MSP_RTC_2); 105 if (status < 0) 106 return status; 107 108 status = dm355evm_msp_write(time.bytes[3], DM355EVM_MSP_RTC_3); 109 if (status < 0) 110 return status; 111 112 return 0; 113} 114 115static const struct rtc_class_ops dm355evm_rtc_ops = { 116 .read_time = dm355evm_rtc_read_time, 117 .set_time = dm355evm_rtc_set_time, 118}; 119 120/*----------------------------------------------------------------------*/ 121 122static int dm355evm_rtc_probe(struct platform_device *pdev) 123{ 124 struct rtc_device *rtc; 125 126 rtc = devm_rtc_allocate_device(&pdev->dev); 127 if (IS_ERR(rtc)) 128 return PTR_ERR(rtc); 129 130 platform_set_drvdata(pdev, rtc); 131 132 rtc->ops = &dm355evm_rtc_ops; 133 rtc->range_max = U32_MAX; 134 135 return devm_rtc_register_device(rtc); 136} 137 138/* 139 * I2C is used to talk to the MSP430, but this platform device is 140 * exposed by an MFD driver that manages I2C communications. 141 */ 142static struct platform_driver rtc_dm355evm_driver = { 143 .probe = dm355evm_rtc_probe, 144 .driver = { 145 .name = "rtc-dm355evm", 146 }, 147}; 148 149module_platform_driver(rtc_dm355evm_driver); 150 151MODULE_LICENSE("GPL");