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 */ 31union evm_time { 32 u8 bytes[4]; 33 u32 value; 34}; 35 36static 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 84static 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 118static 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 125static 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 141static 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 */ 154static 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 163static int __init dm355evm_rtc_init(void) 164{ 165 return platform_driver_register(&rtc_dm355evm_driver); 166} 167module_init(dm355evm_rtc_init); 168 169static void __exit dm355evm_rtc_exit(void) 170{ 171 platform_driver_unregister(&rtc_dm355evm_driver); 172} 173module_exit(dm355evm_rtc_exit); 174 175MODULE_LICENSE("GPL"); 176