From patchwork Tue Jun 30 14:49:50 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Verges X-Patchwork-Id: 29340 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-yx0-f158.google.com (mail-yx0-f158.google.com [209.85.210.158]) by bilbo.ozlabs.org (Postfix) with ESMTP id D6C37B70DC for ; Wed, 1 Jul 2009 15:15:40 +1000 (EST) Received: by yxe30 with SMTP id 30so412342yxe.29 for ; Tue, 30 Jun 2009 22:15:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:received:received:x-sender:x-apparently-to :received:received:received-spf:authentication-results:received :x-mimeole:content-class:mime-version:content-type:subject:date :message-id:x-ms-has-attach:x-ms-tnef-correlator:thread-topic :thread-index:references:from:to:cc:x-spam:x-mail-from:x-source-ip :x-analysisout:x-analysisout:x-analysisout:x-analysisout :x-analysisout:reply-to:sender:precedence:x-google-loop:mailing-list :list-id:list-post:list-help:list-unsubscribe:x-beenthere-env :x-beenthere; bh=D5mb+yZXmN9hbDJ98SVPF9FyyE0Yuo4zNy6KIrRJ+ck=; b=eVW4h7RS+MmpYOKMFkFz3h4OmVWYurhIIB18zZbrtztFrf65fABFbj9tXsGZqRfEEA qL4NdU/iJQSr8Ucm63N3CGP9Mf/SF3/1+1taaj0iZai8C4TgJ/k0oFnnR+Jqpt8XloaG gmmhcPPrdHGApRqrIn615BzOpOermjrx3WIGg= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-sender:x-apparently-to:received-spf:authentication-results :x-mimeole:content-class:mime-version:content-type:subject:date :message-id:x-ms-has-attach:x-ms-tnef-correlator:thread-topic :thread-index:references:from:to:cc:x-spam:x-mail-from:x-source-ip :x-analysisout:reply-to:sender:precedence:x-google-loop:mailing-list :list-id:list-post:list-help:list-unsubscribe:x-beenthere-env :x-beenthere; b=hCOMs9p6oRQtGAgVHf+P/Mo7vWOYZKsKnFOwXroP1n8recZfq/V/Ge7+iYR1oVlOyp lUcxm3q15Qu3cL2cjBXuV0x7I/Qs0WBaqlYbZ/89Mbu5j8RRMBBR1tHIQKZrDpYMe4Oh sJBzRHTukfqPImlUbf6FhE306IFkRQpYbyLJo= Received: by 10.90.117.13 with SMTP id p13mr1281348agc.11.1246373397678; Tue, 30 Jun 2009 07:49:57 -0700 (PDT) Received: by 10.177.124.26 with SMTP id b26gr2542yqn.0; Tue, 30 Jun 2009 07:49:57 -0700 (PDT) X-Sender: chrisv@cyberswitching.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.142.84.3 with SMTP id h3mr125159wfb.5.1246373394418; Tue, 30 Jun 2009 07:49:54 -0700 (PDT) Received: from p01c12o145.mxlogic.net (p01c12o145.mxlogic.net [208.65.145.68]) by gmr-mx.google.com with ESMTP id 19si12031pzk.9.2009.06.30.07.49.53; Tue, 30 Jun 2009 07:49:54 -0700 (PDT) Received-SPF: neutral (google.com: 208.65.145.68 is neither permitted nor denied by best guess record for domain of chrisv@cyberswitching.com) client-ip=208.65.145.68; Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 208.65.145.68 is neither permitted nor denied by best guess record for domain of chrisv@cyberswitching.com) smtp.mail=chrisv@cyberswitching.com Received: from unknown [64.81.28.110] (EHLO mail03.cyberswitching.local) by p01c12o145.mxlogic.net (mxl_mta-6.2.0-4) with ESMTP id 1162a4a4.2641845136.672009.00-026.p01c12o145.mxlogic.net (envelope-from ); Tue, 30 Jun 2009 08:49:53 -0600 (MDT) X-MimeOLE: Produced By Microsoft Exchange V6.5 Content-class: urn:content-classes:message Mime-Version: 1.0 Subject: [rtc-linux] RE: [PATCH 1/1] rtc: Philips/NXP PCF2123 driver - v0.3 Date: Tue, 30 Jun 2009 07:49:50 -0700 Message-ID: <68FBE0F3CE97264395875AC1C468F22C152B2B@mail03.cyberswitching.local> X-MS-Has-Attach: yes X-MS-TNEF-Correlator: Thread-Topic: [PATCH 1/1] rtc: Philips/NXP PCF2123 driver - v0.3 Thread-Index: Acn485amdt8fFwBHTs+zrXsuFWOf4AAm7hgA References: <20090620160839.6953.41960.stgit@i1501.lan.towertech.it><20090620160919.6953.41552.stgit@i1501.lan.towertech.it><20090626160702.c831185a.akpm@linux-foundation.org><68FBE0F3CE97264395875AC1C468F22C152AB4@mail03.cyberswitching.local> <20090629125620.0d923f3a.akpm@linux-foundation.org> From: "Chris Verges" To: "Andrew Morton" Cc: , X-Spam: [F=0.2000000000; CM=0.500; S=0.200(2009062201)] X-MAIL-FROM: X-SOURCE-IP: [64.81.28.110] X-AnalysisOut: [v=1.0 c=1 a=8jta-Zf9M1QA:10 a=4zsJQXJisSY22NXBO5KRuA==:17 ] X-AnalysisOut: [a=wzgqfsuUAAAA:8 a=QHlW-pKwhuE75E6hyf4A:9 a=Vr92LL7mf7YU6p] X-AnalysisOut: [T1ZVIA:7 a=Zmsly-Q_nQMU-OWMVyFVjt0V6tsA:4 a=yvfu_RGVus0A:1] X-AnalysisOut: [0 a=jix-F13FtTlK0_DF6oIA:7 a=1jBAxgdUXUPlaR5iBVX1gD0amDIA:] X-AnalysisOut: [4] Reply-To: rtc-linux@googlegroups.com Sender: rtc-linux@googlegroups.com Precedence: bulk X-Google-Loop: groups Mailing-List: list rtc-linux@googlegroups.com; contact rtc-linux+owner@googlegroups.com List-Id: List-Post: List-Help: List-Unsubscribe: , X-BeenThere-Env: rtc-linux@googlegroups.com X-BeenThere: rtc-linux@googlegroups.com Hi Andrew, > static inline void pcf2123_delay_trec(void) You're right ... much cleaner. Proof that one should not code over vacation. :-) Full patch re-created and attached. Thanks for the feedback! Chris --- PATCH BELOW --- Signed-off-by: Chris Verges --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist before submitting a driver. -~----------~----~----~----~------~----~------~--~--- Index: drivers/rtc/rtc-pcf2123.c =================================================================== --- linux-2.6.29.3-orig/drivers/rtc/rtc-pcf2123.c (revision 0) +++ linux-2.6.29.3/drivers/rtc/rtc-pcf2123.c (revision 43) @@ -0,0 +1,255 @@ +/* + * An SPI driver for the Philips PCF2123 RTC + * Copyright 2009 Cyber Switching, Inc. + * + * Author: Chris Verges + * Maintainers: http://www.cyberswitching.com + * + * based on the RS5C348 driver in this same directory. + * + * 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 +#include +#include +#include +#include + +#define DRV_VERSION "0.3" + +#define PCF2123_REG_CTRL1 0x00 /* Control Register 1 */ +#define PCF2123_REG_CTRL2 0x01 /* Control Register 2 */ + +#define PCF2123_REG_SC 0x02 /* datetime */ +#define PCF2123_REG_MN 0x03 +#define PCF2123_REG_HR 0x04 +#define PCF2123_REG_DM 0x05 +#define PCF2123_REG_DW 0x06 +#define PCF2123_REG_MO 0x07 +#define PCF2123_REG_YR 0x08 + +#define PCF2123_CMD_W(addr) (((addr) & 0x0F) | 0x40) /* single write */ +#define PCF2123_CMD_R(addr) (((addr) & 0x0F) | 0x90) /* single read */ + +static struct spi_driver pcf2123_driver; + +struct pcf2123_plat_data { + struct rtc_device *rtc; +}; + +/* + * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select + * is released properly after an SPI write. This function should be + * called after EVERY read/write call over SPI. + */ +static inline void pcf2123_delay_trec(void) +{ + ndelay(30); +} + +static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[1], rxbuf[7]; + int ret; + + txbuf[0] = PCF2123_CMD_R(PCF2123_REG_SC); + ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), + rxbuf, sizeof(rxbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); + tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); + tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); + tm->tm_wday = rxbuf[4] & 0x07; + tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(rxbuf[6]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[8]; + int ret; + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* Stop the counter first */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_CTRL1); + txbuf[1] = 0x20; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Set the new time */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_SC); + txbuf[1] = bin2bcd(tm->tm_sec & 0x7F); + txbuf[2] = bin2bcd(tm->tm_min & 0x7F); + txbuf[3] = bin2bcd(tm->tm_hour & 0x3F); + txbuf[4] = bin2bcd(tm->tm_mday & 0x3F); + txbuf[5] = tm->tm_wday & 0x07; + txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ + txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100); + + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Start the counter */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_CTRL1); + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + return 0; +} + +static const struct rtc_class_ops pcf2123_rtc_ops = { + .read_time = pcf2123_rtc_read_time, + .set_time = pcf2123_rtc_set_time, +}; + +static int __devinit pcf2123_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct pcf2123_plat_data *pdata; + u8 txbuf[2], rxbuf[1]; + int ret; + + pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + spi->dev.platform_data = pdata; + + /* Send a software reset command */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_CTRL1); + txbuf[1] = 0x58; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Stop the counter */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_CTRL1); + txbuf[1] = 0x20; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* See if the counter was actually stopped */ + txbuf[0] = PCF2123_CMD_R(PCF2123_REG_CTRL1); + ret = spi_write_then_read(spi, txbuf, 1, rxbuf, sizeof(rxbuf)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + if (!(rxbuf[0] & 0x20)) { + dev_err(&spi->dev, "not found.\n"); + goto kfree_exit; + } + + dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n"); + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + /* Start the counter */ + txbuf[0] = PCF2123_CMD_W(PCF2123_REG_CTRL1); + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* Finalize the initialization */ + rtc = rtc_device_register(pcf2123_driver.driver.name, &spi->dev, + &pcf2123_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto kfree_exit; + } + + pdata->rtc = rtc; + + return 0; +kfree_exit: + kfree(pdata); + return ret; +} + +static int pcf2123_remove(struct spi_device *spi) +{ + struct pcf2123_plat_data *pdata = spi->dev.platform_data; + struct rtc_device *rtc = pdata->rtc; + + if (rtc) + rtc_device_unregister(rtc); + + kfree(pdata); + + return 0; +} + +static struct spi_driver pcf2123_driver = { + .driver = { + .name = "rtc-pcf2123", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = pcf2123_probe, + .remove = __devexit_p(pcf2123_remove), +}; + +static int __init pcf2123_init(void) +{ + return spi_register_driver(&pcf2123_driver); +} + +static void __exit pcf2123_exit(void) +{ + spi_unregister_driver(&pcf2123_driver); +} + +MODULE_AUTHOR("Chris Verges "); +MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(pcf2123_init); +module_exit(pcf2123_exit); Index: drivers/rtc/Makefile =================================================================== --- linux-2.6.29.3-orig/drivers/rtc/Makefile (revision 4) +++ linux-2.6.29.3/drivers/rtc/Makefile (revision 43) @@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o +obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_PARISC) += rtc-parisc.o Index: drivers/rtc/Kconfig =================================================================== --- linux-2.6.29.3-orig/drivers/rtc/Kconfig (revision 4) +++ linux-2.6.29.3/drivers/rtc/Kconfig (revision 43) @@ -368,6 +368,15 @@ This driver can also be built as a module. If so, the module will be called rtc-ds3234. +config RTC_DRV_PCF2123 + tristate "NXP PCF2123" + help + If you say yes here you get support for the NXP PCF2123 + RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2123. + endif # SPI_MASTER comment "Platform RTC drivers"