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