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