From patchwork Wed Mar 9 15:14:54 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Voss, Nikolaus" X-Patchwork-Id: 86122 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-fx0-f56.google.com (mail-fx0-f56.google.com [209.85.161.56]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id 7DB6DB6FB4 for ; Thu, 10 Mar 2011 02:15:27 +1100 (EST) Received: by fxm3 with SMTP id 3sf425432fxm.11 for ; Wed, 09 Mar 2011 07:15:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:x-beenthere:received-spf:from:to:cc:date :subject:thread-topic:thread-index:message-id:accept-language :x-ms-has-attach:x-ms-tnef-correlator:acceptlanguage:mime-version :x-original-sender:x-original-authentication-results :x-google-group-id:reply-to:precedence:mailing-list:list-id :list-post:list-help:list-archive:sender:list-subscribe :list-unsubscribe:content-language:content-type; bh=eu8zsAbU6NuQ/Is7R6XOj45MyvYxQpXNm3uwjJf4k1k=; b=PPSBasLT2YYDG9g2iz6V6Z0hHddNcib9aicgGgTvYsa5zLQpsmBB2m7IOvXlwBKsh7 vCmr6DRlivvCRqIckg9qeA8sKkPicYFKiBEJidQY0tckI8WtOcj4WIUbEzZd1230dbxb LRYqNhimveaKeg92tqOP9iC1PSuFdi8yeX5UY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-beenthere:received-spf:from:to:cc:date:subject:thread-topic :thread-index:message-id:accept-language:x-ms-has-attach :x-ms-tnef-correlator:acceptlanguage:mime-version:x-original-sender :x-original-authentication-results:x-google-group-id:reply-to :precedence:mailing-list:list-id:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe:content-language :content-type; b=m+b5NCdkGTpLHM9UC2lqt2Da3+5+WIZOyctsQedpRVmBuamUyaxJQeLmvBqPGh7fuP q2iMMi+K/JGD3tTw5I+kK/zYhjyUXbkJpA973IFKHIv5+75jitqanfAQfMy4i6nDVLN2 iJe/n93/Fe6XQDrQIfO5tAZhYVSpH3mWdDcFM= Received: by 10.223.69.76 with SMTP id y12mr421033fai.15.1299683701908; Wed, 09 Mar 2011 07:15:01 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.223.15.10 with SMTP id i10ls2083863faa.1.p; Wed, 09 Mar 2011 07:15:01 -0800 (PST) Received: by 10.223.83.134 with SMTP id f6mr836699fal.26.1299683701086; Wed, 09 Mar 2011 07:15:01 -0800 (PST) Received: by 10.223.83.134 with SMTP id f6mr836698fal.26.1299683701065; Wed, 09 Mar 2011 07:15:01 -0800 (PST) Received: from portal.weinmann.de (portal.weinmann.de [62.8.140.122]) by gmr-mx.google.com with ESMTP id 6si173253fay.2.2011.03.09.07.14.59; Wed, 09 Mar 2011 07:15:00 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of prvs=10497c7ac9=N.Voss@weinmann.de designates 62.8.140.122 as permitted sender) client-ip=62.8.140.122; From: "Voss, Nikolaus" To: "'rtc-linux@googlegroups.com'" CC: "'Linux Kernel'" Date: Wed, 9 Mar 2011 16:14:54 +0100 Subject: [rtc-linux] [PATCH][RESENT] Add basic support for ST M41T93 SPI RTC Thread-Topic: [PATCH][RESENT] Add basic support for ST M41T93 SPI RTC Thread-Index: Act8wW76yYYdc2KtRm++LbQjEizoKg== Message-ID: Accept-Language: en-US, de-DE X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US, de-DE MIME-Version: 1.0 X-Original-Sender: n.voss@weinmann.de X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of prvs=10497c7ac9=N.Voss@weinmann.de designates 62.8.140.122 as permitted sender) smtp.mail=prvs=10497c7ac9=N.Voss@weinmann.de X-Google-Group-Id: 712029733259 Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: List-Post: , List-Help: , List-Archive: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , Content-Language: de-DE Hi, any comments on this patch? Got no feedback so far. Niko ----------------------------------------------------------------------------------- This driver adds basic support for ST m41t93 SPI RTCs and has been tested with factory-new and with "run-in" species with and without backup batteries. Signed-off-by: Nikolaus Voss --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-m41t93.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 0 deletions(-) create mode 100644 drivers/rtc/rtc-m41t93.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 48ca713..230c1cd 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -349,6 +349,15 @@ comment "SPI RTC drivers" if SPI_MASTER +config RTC_DRV_M41T93 + tristate "ST M41T93" + help + If you say yes here you will get support for the + ST M41T93 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t93. + config RTC_DRV_M41T94 tristate "ST M41T94" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0f207b3..70327e6 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o +obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c new file mode 100644 index 0000000..28d203a --- /dev/null +++ b/drivers/rtc/rtc-m41t93.c @@ -0,0 +1,225 @@ +/* + * + * Driver for ST M41T93 SPI RTC + * + * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define M41T93_REG_SSEC 0 +#define M41T93_REG_ST_SEC 1 +#define M41T93_REG_MIN 2 +#define M41T93_REG_CENT_HOUR 3 +#define M41T93_REG_WDAY 4 +#define M41T93_REG_DAY 5 +#define M41T93_REG_MON 6 +#define M41T93_REG_YEAR 7 + + +#define M41T93_REG_ALM_HOUR_HT 0xc +#define M41T93_REG_FLAGS 0xf + +#define M41T93_FLAG_ST (1 << 7) +#define M41T93_FLAG_OF (1 << 2) +#define M41T93_FLAG_BL (1 << 4) +#define M41T93_FLAG_HT (1 << 6) + +static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data) +{ + u8 buf[2]; + + /* MSB must be '1' to write */ + buf[0] = addr | 0x80; + buf[1] = data; + + return spi_write(spi, buf, sizeof(buf)); +} + +static int m41t93_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */ + u8 * const data = &buf[1]; /* ptr to first data byte */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + if (tm->tm_year < 100) { + dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n"); + return -EINVAL; + } + + data[M41T93_REG_SSEC] = 0; + data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec); + data[M41T93_REG_MIN] = bin2bcd(tm->tm_min); + data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) | + ((tm->tm_year/100-1) << 6); + data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday); + data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1); + data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + return spi_write(spi, buf, sizeof(buf)); +} + + +static int m41t93_get_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + const u8 start_addr = 0; + u8 buf[8]; + int century_after_1900; + int tmp; + int ret = 0; + + /* Check status of clock. Two states must be considered: + 1. halt bit (HT) is set: the clock is running but update of readout + registers has been disabled due to power failure. This is normal + case after poweron. Time is valid after resetting HT bit. + 2. oscillator fail bit (OF) is set. Oscillator has be stopped and + time is invalid: + a) OF can be immeditely reset. + b) OF cannot be immediately reset: oscillator has to be restarted. + */ + tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT); + if (tmp < 0) + return tmp; + + if (tmp & M41T93_FLAG_HT) { + dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n"); + m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT, + tmp & ~M41T93_FLAG_HT); + } + + tmp = spi_w8r8(spi, M41T93_REG_FLAGS); + if (tmp < 0) + return tmp; + + if (tmp & M41T93_FLAG_OF) { + ret = -EINVAL; + dev_warn(&spi->dev, "OF bit is set, resetting.\n"); + m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF); + + tmp = spi_w8r8(spi, M41T93_REG_FLAGS); + if (tmp < 0) + return tmp; + else if (tmp & M41T93_FLAG_OF) { + u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST; + + dev_warn(&spi->dev, + "OF bit is still set, kickstarting clock.\n"); + m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + reset_osc &= ~M41T93_FLAG_ST; + m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + } + } + + if (tmp & M41T93_FLAG_BL) + dev_warn(&spi->dev, "BL bit is set, replace battery.\n"); + + /* read actual time/date */ + tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf)); + if (tmp < 0) + return tmp; + + tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]); + tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]); + tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]); + tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1; + tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1; + + century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1; + tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + return ret < 0 ? ret : rtc_valid_tm(tm); +} + + +static const struct rtc_class_ops m41t93_rtc_ops = { + .read_time = m41t93_get_time, + .set_time = m41t93_set_time, +}; + +static struct spi_driver m41t93_driver; + +static int __devinit m41t93_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int res; + + spi->bits_per_word = 8; + spi_setup(spi); + + res = spi_w8r8(spi, M41T93_REG_WDAY); + if (res < 0 || (res & 0xf8) != 0) { + dev_err(&spi->dev, "not found 0x%x.\n", res); + return -ENODEV; + } + + rtc = rtc_device_register(m41t93_driver.driver.name, + &spi->dev, &m41t93_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + dev_set_drvdata(&spi->dev, rtc); + + return 0; +} + + +static int __devexit m41t93_remove(struct spi_device *spi) +{ + struct rtc_device *rtc = platform_get_drvdata(spi); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +static struct spi_driver m41t93_driver = { + .driver = { + .name = "rtc-m41t93", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = m41t93_probe, + .remove = __devexit_p(m41t93_remove), +}; + +static __init int m41t93_init(void) +{ + return spi_register_driver(&m41t93_driver); +} +module_init(m41t93_init); + +static __exit void m41t93_exit(void) +{ + spi_unregister_driver(&m41t93_driver); +} +module_exit(m41t93_exit); + +MODULE_AUTHOR("Nikolaus Voss "); +MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t93");