From patchwork Mon Jun 20 12:06:08 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Torsten Mehnert X-Patchwork-Id: 101099 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-ww0-f56.google.com (mail-ww0-f56.google.com [74.125.82.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 656B0B6F70 for ; Mon, 20 Jun 2011 22:06:17 +1000 (EST) Received: by wwf22 with SMTP id 22sf5419786wwf.11 for ; Mon, 20 Jun 2011 05:06:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:x-beenthere:received-spf:from:to:subject :thread-topic:thread-index:date:message-id:accept-language :x-ms-has-attach:x-ms-tnef-correlator:x-originating-ip:mime-version :x-original-sender:x-original-authentication-results:reply-to :precedence:mailing-list:list-id:x-google-group-id:list-post :list-help:list-archive:sender:list-subscribe:list-unsubscribe :content-language:content-type; bh=YgQ7Ime2IGJQS4l0fhAxN7WCM4ZS7njPdFjO/REV0qc=; b=3PV99PCwLwPos5zAUqK2ZCMTkwF1do5EeErFznQHeL99VRukw29xrm8GW97LmF+UJa iydYer09KBxklibilwqlh90ONU8/VnGoKdNhke9aJPJwMLntJABFjS2ErTLTAAhwtTye S9LvwAdgRrs5YN+nI+Z0PsEPJH9mgg6EfRj+o= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-beenthere:received-spf:from:to:subject:thread-topic:thread-index :date:message-id:accept-language:x-ms-has-attach :x-ms-tnef-correlator:x-originating-ip:mime-version :x-original-sender:x-original-authentication-results:reply-to :precedence:mailing-list:list-id:x-google-group-id:list-post :list-help:list-archive:sender:list-subscribe:list-unsubscribe :content-language:content-type; b=g1VibxDPtAVXYIm8YIluoIblcNwSD3FS54aFOjWwTPez2sVTvZ54xaDVbYyY+Ej4QE BzEmEu3dzic1g4nTYW98+H/aPuGZFGh3oqu0/VnXTq3vmcBzed9X037P0tH0YbpPab1a axbeoe2Hg2xhonw3QMTrzXXq+brPfN9todcwQ= Received: by 10.217.7.4 with SMTP id z4mr635673wes.4.1308571572112; Mon, 20 Jun 2011 05:06:12 -0700 (PDT) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.14.11.92 with SMTP id 68ls902560eew.4.gmail; Mon, 20 Jun 2011 05:06:11 -0700 (PDT) Received: by 10.14.11.79 with SMTP id 55mr525608eew.21.1308571571277; Mon, 20 Jun 2011 05:06:11 -0700 (PDT) Received: by 10.14.11.79 with SMTP id 55mr525607eew.21.1308571571251; Mon, 20 Jun 2011 05:06:11 -0700 (PDT) Received: from mail.eckelmann.de (mail.eckelmann.de [217.19.183.94]) by gmr-mx.google.com with ESMTPS id n13si3649523eea.3.2011.06.20.05.06.10 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 20 Jun 2011 05:06:10 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of prvs=11453ef3c9=T.Mehnert@eckelmann.de designates 217.19.183.94 as permitted sender) client-ip=217.19.183.94; Received: from mail.eckelmann.de (localhost.localdomain [127.0.0.1]) by localhost (Email Security Appliance) with SMTP id DBF58178AC3A_DFF37B1B for ; Mon, 20 Jun 2011 12:06:09 +0000 (GMT) Received: from dag.eckelmann.group (ex-dag02.eckelmann.group [192.168.13.9]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (Client CN "eckelmann.group", Issuer "Eckelmann Root CA" (not verified)) by mail.eckelmann.de (Sophos Email Appliance) with ESMTP id 43CD1178AC17_DFF37B1F for ; Mon, 20 Jun 2011 12:06:09 +0000 (GMT) Received: from EX-DAG01.eckelmann.group ([fe80::2496:326c:27e8:2b5d]) by EX-DAG02.eckelmann.group ([fe80::5d1b:3915:5c51:5117%13]) with mapi id 14.01.0289.001; Mon, 20 Jun 2011 14:06:09 +0200 From: "Mehnert, Torsten" To: "rtc-linux@googlegroups.com" Subject: [rtc-linux] [v2, 2/2] Add RTC driver for NXP PCF2127A (I2C) Thread-Topic: [v2, 2/2] Add RTC driver for NXP PCF2127A (I2C) Thread-Index: AcwvQnBAxen6I+OjQ3a2JW4eqlwAnA== Date: Mon, 20 Jun 2011 12:06:08 +0000 Message-ID: <3F263ABB4EDE344D974E1983B56807734BAFFCB0@EX-DAG01.eckelmann.group> Accept-Language: de-DE, en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [172.16.181.0] MIME-Version: 1.0 X-Original-Sender: t.mehnert@eckelmann.de X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of prvs=11453ef3c9=T.Mehnert@eckelmann.de designates 217.19.183.94 as permitted sender) smtp.mail=prvs=11453ef3c9=T.Mehnert@eckelmann.de Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , Content-Language: de-DE Hi, first thanks for your review and advice. I have tried the SMBus functions, they are all very nice, but they generate an "repeated Start" condition on the bus, which is disallowed for this device. The SMBus read commands all implemented "write_than_read", which is useful but also generate a "repeated Start" condition, so we cannot use it. Yes this chip exists, but NXP listed it under "RTCs with temp. compensation" section. The chip has an i2c and a SPI interface, but is not listed in one of them. Changes v2: - Fixed spelling mistakes in Kconfig and header - included - DRV_VERSION / MODULE_VERSION removed - struct pcf2127a removed, because it has only one member - Some unnecessary debug outputs removed - Fixed comments to match kernel style guidelines - module_init/_exit makros are now placed under the respective function From: T. Mehnert Date: Mon, 20 Jun 2011 13:15:32 +0200 Subject: [PATCH] RTC pcf2127a i2c implementation This patch adds the device driver for the RTC NXP PCF2127A. It only implement the i2c interface to the RTC. rtc_class_ops: read_time, set_time --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pcf2127a.c | 321 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 0 deletions(-) create mode 100644 drivers/rtc/rtc-pcf2127a.c -- 1.7.0.4 ECKELMANN AG Vorstand: Dr.-Ing. Gerd Eckelmann (Vorsitzender), Dr.-Ing. Peter Cordes, Dr.-Ing. Frank-Thomas Mellert Vorsitzender des Aufsichtsrats: Hubertus G. Krossa Sitz der Gesellschaft: Wiesbaden Amtsgericht Wiesbaden, HRB 12636 diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e187887..deee305 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -264,6 +264,17 @@ config RTC_DRV_PCF8583 This driver can also be built as a module. If so, the module will be called rtc-pcf8583. +config RTC_DRV_PCF2127A + tristate "Philips PCF2127A (only I2C)" + help + If you say yes here you get support for the Philips PCF2127A + RTC. This driver implements only the i2c-interface to the chip. + It may work with pcf2127, but is tested only against + the pcf2127a. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2127a. + config RTC_DRV_M41T80 tristate "ST M41T62/65/M41T80/81/82/83/84/85/87" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index dea00e2..ad2ff9b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o +obj-$(CONFIG_RTC_DRV_PCF2127A) += rtc-pcf2127a.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o diff --git a/drivers/rtc/rtc-pcf2127a.c b/drivers/rtc/rtc-pcf2127a.c new file mode 100644 index 0000000..bec903d --- /dev/null +++ b/drivers/rtc/rtc-pcf2127a.c @@ -0,0 +1,321 @@ +/* + * Driver the the NXP/Philips PCF2127A RTC (only I2C) + * + * Copyright (c) 2011 ECKELMANN AG. + * + * Authors: Matthias Wolf + * Torsten Mehnert + * + * RTC-Datasheet: http://www.nxp.com/documents/data_sheet/PCF2127A.pdf + * + * NOTE: This driver only implements the i2c-interface to the RTC. + * This driver is based on rtc-pcf8363 from Alessandro Zummo. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +/* PCF2127A Registers */ +#define PCF2127A_REG_CTL1 0x00 /* Control 1 */ +#define PCF2127A_REG_CTL2 0x01 /* Control 2 */ +#define PCF2127A_REG_CTL3 0x02 /* Control 3 */ + +#define PCF2127A_REG_SC 0x03 /* second value */ +#define PCF2127A_REG_MN 0x04 /* minute value */ +#define PCF2127A_REG_HR 0x05 /* hour value */ +#define PCF2127A_REG_DM 0x06 /* day value */ +#define PCF2127A_REG_DW 0x07 /* weekday value */ +#define PCF2127A_REG_MO 0x08 /* month value */ +#define PCF2127A_REG_YR 0x09 /* year value */ + +#define PCF2127A_REG_SC_ALARM 0x0A /* second alarm */ +#define PCF2127A_REG_MN_ALARM 0x0B /* minute alarm */ +#define PCF2127A_REG_HR_ALARM 0x0C /* hour alarm */ +#define PCF2127A_REG_DM_ALARM 0x0D /* day alarm */ +#define PCF2127A_REG_DW_ALARM 0x0E /* weekday alarm */ + +#define PCF2127A_REG_CLKO 0x0F /* clock out */ +#define PCF2127A_REG_WD_TMRC 0x10 /* watchdog timer control */ +#define PCF2127A_REG_WD_TMVAL 0x11 /* watchdog timer value */ + +#define PCF2127A_REG_TSCTL 0x12 /* timestamp control */ +#define PCF2127A_REG_SC_TS 0x13 /* second timestamp */ +#define PCF2127A_REG_MN_TS 0x14 /* minute timestamp */ +#define PCF2127A_REG_HR_TS 0x15 /* hour timestamp */ +#define PCF2127A_REG_DM_TS 0x16 /* day timestamp */ +#define PCF2127A_REG_MO_TS 0x17 /* month timestamp */ +#define PCF2127A_REG_YR_TS 0x18 /* year timestamp */ +#define PCF2127A_REG_AGI_OFF 0x19 /* aging offset */ +#define PCF2127A_REG_RAM_MSB 0x1A /* RAM address MSB */ +#define PCF2127A_REG_RAM_LSB 0x1B /* RAM address LSB */ +#define PCF2127A_REG_RAM_WCMD 0x1C /* RAM write command */ +#define PCF2127A_REG_RAM_RCMD 0x1D /* RAM read command */ + +/* Bits in PCF2127A_REG_CTL1 */ +#define PCF2127A_BIT_SI 0x01 /* second interrupt */ +#define PCF2127A_BIT_MI 0x02 /* minute interrupt */ +#define PCF2127A_BIT_H_24_12 0x04 /* 12/24 hour mode */ +#define PCF2127A_BIT_POR_OVRD 0x08 /* POR override */ +#define PCF2127A_BIT_TSF1 0x10 /* timestamp flag 1 */ +#define PCF2127A_BIT_STOP 0x20 /* stop bit */ +#define PCF2127A_BIT_EXTTEST 0x80 /* external clock test mode*/ + +/* Bits in PCF2127A_REG_CTL2 */ +#define PCF2127A_BIT_CDTIE 0x01 /* countdown timer flag */ +#define PCF2127A_BIT_AIE 0x02 /* alarm interrupt enable */ +#define PCF2127A_BIT_TSIE 0x04 /* timestamp int. enable */ +#define PCF2127A_BIT_CDTF 0x08 /* countdown timer flag */ +#define PCF2127A_BIT_AF 0x10 /* alarm flag */ +#define PCF2127A_BIT_TSF2 0x20 /* timestamp flag 2 */ +#define PCF2127A_BIT_WDTF 0x40 /* watchdog timer flag */ +#define PCF2127A_BIT_MSF 0x80 /* minute/second flag */ + +/* Bits in PCF2127A_REG_CTL3 */ +#define PCF2127A_BIT_BLIE 0x01 /* battery low Interrupt enable */ +#define PCF2127A_BIT_BIE 0x02 /* battery Switch int. enable */ +#define PCF2127A_BIT_BLF 0x04 /* battery low flag */ +#define PCF2127A_BIT_BF 0x08 /* battery switch flag */ +#define PCF2127A_BIT_BTSE 0x10 /* battery timestamp enable */ +#define PCF2127A_BIT_BLDOFF 0x20 /* battery low detection off */ +#define PCF2127A_BIT_BSM 0x40 /* battery switch mode */ +#define PCF2127A_BIT_BSOFF 0x80 /* battery switch off */ + +static struct i2c_driver pcf2127a_driver; + +static struct rtc_device *pcf2127a_device; + +/* + * In the routines that deal directly with the pcf2127a hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static +int pcf2127a_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char buf[10] = { PCF2127A_REG_CTL1 }; + + /* + * Note: We can't use i2c_transfer to send/recv multiple i2c_msg's + * to/from pcf2127a at once. The reason is that the pcf2127a doesn't + * handle (repeated START)-Conditions correctly. + * + * PCF2127A Manual, Section 9.2.2 START and STOP conditions: + * "Remark: For the PCF2127A a repeated START is not allowed. Therefore + * a STOP has to be released before the next START." + */ + + /* setup read ptr */ + if (!i2c_master_send(client,buf,1)) { + dev_err(&client->dev, "%s: write error\n", __func__); + return -EIO; + } + + /* read status + date */ + if (!i2c_master_recv(client,buf,10)) { + + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + if (buf[PCF2127A_REG_CTL3] & PCF2127A_BIT_BLF) + dev_info(&client->dev, + "low battery detected, date/time is not reliable.\n"); + +#ifdef DEBUG + unsigned char bf_buf[2]; + if (buf[PCF2127A_REG_CTL3] & PCF2127A_BIT_BF){ + dev_info(&client->dev, + "battery switch-over detected - reset BF flag.\n"); + + bf_buf[0] = PCF2127A_REG_CTL3; + bf_buf[1] = buf[PCF2127A_REG_CTL3] & ~PCF2127A_BIT_BF; + if (!i2c_master_send(client, bf_buf, 2)) { + dev_err(&client->dev, "%s: write error\n", __func__); + return -EIO; + } + } +#endif + + dev_dbg(&client->dev, + "%s: raw data is ctl1=%02x, ctl2=%02x, ctl3=%02x, sec=%02x, " + "min=%02x, hour=%02x, day=%02x, wday=%02x, month=%02x, " + "year=%02x\n", + __func__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9]); + + tm->tm_sec = bcd2bin(buf[PCF2127A_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[PCF2127A_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[PCF2127A_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(buf[PCF2127A_REG_DM] & 0x3F); + tm->tm_wday = buf[PCF2127A_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[PCF2127A_REG_MO] & 0x1F) -1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(buf[PCF2127A_REG_YR]); + + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + /* + * 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(&client->dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static +int pcf2127a_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int i, err; + unsigned char msgdata[2]; /* send data*/ + unsigned char buf[10]; /* Index 0 - 9 */ + + /* hours, minutes and seconds */ + buf[PCF2127A_REG_SC] = bin2bcd(tm->tm_sec); + buf[PCF2127A_REG_MN] = bin2bcd(tm->tm_min); + buf[PCF2127A_REG_HR] = bin2bcd(tm->tm_hour); + + /* day of month */ + buf[PCF2127A_REG_DM] = bin2bcd(tm->tm_mday); + + /* weekday */ + buf[PCF2127A_REG_DW] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + buf[PCF2127A_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year */ + buf[PCF2127A_REG_YR] = bin2bcd(tm->tm_year % 100); + + /* write register's data */ + for (i = 0; i < 7; i++) { + msgdata[0] = PCF2127A_REG_SC + i; + msgdata[1] = buf[PCF2127A_REG_SC + i]; + err = i2c_master_send(client, msgdata, sizeof(msgdata)); + if (err != sizeof(msgdata)) { + dev_err(&client->dev, + "%s: err=%d addr=%02x, data=%02x\n", + __func__, err, msgdata[0], msgdata[1]); + return -EIO; + } + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int pcf2127a_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return pcf2127a_get_datetime(to_i2c_client(dev), tm); +} + +static int pcf2127a_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return pcf2127a_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops pcf2127a_rtc_ops = { + .read_time = pcf2127a_rtc_read_time, + .set_time = pcf2127a_rtc_set_time, +}; + +/*---------------------------------------------------------------------------*/ + +static int pcf2127a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf2127a_device = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); + if (!pcf2127a_device) + return -ENOMEM; + + dev_info(&client->dev, "chip found\n"); + + pcf2127a_device = rtc_device_register(pcf2127a_driver.driver.name, + &client->dev, &pcf2127a_rtc_ops, THIS_MODULE); + + if (IS_ERR(pcf2127a_device)) { + err = PTR_ERR(pcf2127a_device); + goto exit_kfree; + } + + i2c_set_clientdata(client, pcf2127a_device); + + return 0; + +exit_kfree: + kfree(pcf2127a_device); + + return err; +} + +/*---------------------------------------------------------------------------*/ + +static int pcf2127a_remove(struct i2c_client *client) +{ + if (pcf2127a_device) + rtc_device_unregister(pcf2127a_device); + + kfree(pcf2127a_device); + + return 0; +} + +static const struct i2c_device_id pcf2127a_id[] = { + /* + * This driver should also work with the pcf2127, but + * I can't prove it. + */ + { "pcf2127a", 0 }, + { "rtc2127a", 0 }, +}; +MODULE_DEVICE_TABLE(i2c, pcf2127a_id); + +static struct i2c_driver pcf2127a_driver = { + .driver = { + .name = "rtc-pcf2127a", + }, + .probe = pcf2127a_probe, + .remove = pcf2127a_remove, + .id_table = pcf2127a_id, +}; + +static int __init pcf2127a_init(void) +{ + return i2c_add_driver(&pcf2127a_driver); +} +module_init(pcf2127a_init); + +static void __exit pcf2127a_exit(void) +{ + i2c_del_driver(&pcf2127a_driver); +} +module_exit(pcf2127a_exit); + +MODULE_AUTHOR("M. Wolf "); +MODULE_AUTHOR("T. Mehnert "); +MODULE_DESCRIPTION("NXP/Philips PCF2127A RTC (I2C) driver"); +MODULE_LICENSE("GPL");