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