From patchwork Mon Nov 19 08:38:08 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 199952 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-vc0-f184.google.com (mail-vc0-f184.google.com [209.85.220.184]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id DB7D72C008F for ; Mon, 19 Nov 2012 19:38:26 +1100 (EST) Received: by mail-vc0-f184.google.com with SMTP id p16sf3225388vcq.11 for ; Mon, 19 Nov 2012 00:38:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20120806; h=mime-version:x-beenthere:received-spf:x-virus-scanned:from:to:cc :subject:date:message-id:x-mailer:x-provags-id: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-type; bh=lOc6mstCbTgcia/DtbFqebt0ob39XiA7yrP772E9g8E=; b=ZgIuNmmwcRlj1d4Ke3sQwuNLF3+HYeAdeVHqZk3/v8OyXIVrKavcB3tJj6pUsxj18h p8I/Dk0ckDLN3/2405T3oz1wfSsAEpQrg1aE94cOjWxhHJmTECiiYjY5Htsvc2QjnnCq L1jq3NjV2DukjA5hWGkPua5OP7ToCb74Ji41AdD9I0aqzV6j8F7Dx1dNS31xDGqJzzVL ukqLqv53gOuqy6a3xtnQhSRWNfHZyG3l200O1J8BupljoPsxzV1clX9USi8/XEf3E2Ee BCNODjPhriIXkZGrETmNW67YkPQuSBMJ2NmGOm1go8H6D5a3U/HdE9brpyDlZshTBJpF M7mw== Received: by 10.49.116.193 with SMTP id jy1mr2256742qeb.2.1353314303208; Mon, 19 Nov 2012 00:38:23 -0800 (PST) MIME-Version: 1.0 X-BeenThere: rtc-linux@googlegroups.com Received: by 10.49.24.19 with SMTP id q19ls2285102qef.84.gmail; Mon, 19 Nov 2012 00:38:22 -0800 (PST) Received: by 10.236.81.72 with SMTP id l48mr9311585yhe.4.1353314302578; Mon, 19 Nov 2012 00:38:22 -0800 (PST) Received: by 10.236.81.72 with SMTP id l48mr9311584yhe.4.1353314302564; Mon, 19 Nov 2012 00:38:22 -0800 (PST) Received: from moutng.kundenserver.de (moutng.kundenserver.de. [212.227.17.9]) by gmr-mx.google.com with ESMTPS id l20si682224yhi.2.2012.11.19.00.38.22 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 19 Nov 2012 00:38:22 -0800 (PST) Received-SPF: neutral (google.com: 212.227.17.9 is neither permitted nor denied by best guess record for domain of thierry.reding@avionic-design.de) client-ip=212.227.17.9; Received: from mailbox.adnet.avionic-design.de (mailbox.avionic-design.de [109.75.18.3]) by mrelayeu.kundenserver.de (node=mrbap4) with ESMTP (Nemesis) id 0MC1E8-1TRXAD14s4-008tPg; Mon, 19 Nov 2012 09:38:13 +0100 Received: from localhost (localhost [127.0.0.1]) by mailbox.adnet.avionic-design.de (Postfix) with ESMTP id 9D3622A280EB; Mon, 19 Nov 2012 09:38:12 +0100 (CET) X-Virus-Scanned: amavisd-new at avionic-design.de Received: from mailbox.adnet.avionic-design.de ([127.0.0.1]) by localhost (mailbox.avionic-design.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id VwlA+qPeEumj; Mon, 19 Nov 2012 09:38:11 +0100 (CET) Received: from localhost (avionic-0098.adnet.avionic-design.de [172.20.31.233]) (Authenticated sender: thierry.reding) by mailbox.adnet.avionic-design.de (Postfix) with ESMTPA id 04EB02A280E4; Mon, 19 Nov 2012 09:38:08 +0100 (CET) From: Thierry Reding To: Alessandro Zummo Cc: rtc-linux@googlegroups.com, linux-kernel@vger.kernel.org Subject: [rtc-linux] [PATCH] rtc: Add NXP PCF8523 support Date: Mon, 19 Nov 2012 09:38:08 +0100 Message-Id: <1353314288-6512-1-git-send-email-thierry.reding@avionic-design.de> X-Mailer: git-send-email 1.8.0 X-Provags-ID: V02:K0:x3d8lhYIz9K934zul2e/J2bkqiEKm5tG1ChtLeEJWt0 LW7fJuyYvHGMe4PUxeah8iol4wvv2gmjakmbPB/4VWzw+I4WEd DL1XYSXXfb8clvFHFDNP2F1niU9GcT+WVwPgDyNJ2PN9TTBktB nTfhVidIBEaA0E5m0ZbrzTsrecy5wgZTpxRAsLCaOEurB1gFqT yW6lO2tO1OqljemosnJpbi1Uzq24mh43hHtWjw7Obbe+yi/vcQ RUys7sO4dsoZ/09jchhdF6Q01T0kmzZ1kwMbmJDwHSDwxFGw5b 5ItHsCSDJdISC9LNbvKgPm/kFHqkXq1OCu8KdrcFXxS6ztsQfg qLw2lizxPyuRACOPRvDXQVapbBsFDsu+qKf9GTSDFCshgKgqNc FMipXaIr3GXjgNEXFvqx+x007oi2YBKHOEtKMzkWB5/xXHB2Jj 6I2c7 X-Original-Sender: thierry.reding@avionic-design.de X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 212.227.17.9 is neither permitted nor denied by best guess record for domain of thierry.reding@avionic-design.de) smtp.mail=thierry.reding@avionic-design.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: , This commit adds an RTC driver for PCF8523 chips by NXP Semiconductors. No support is currently provided for the alarm and interrupt functions. Only the time and date functionality is implemented. Signed-off-by: Thierry Reding --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pcf8523.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 drivers/rtc/rtc-pcf8523.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 608a7b1..5bb0314 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -269,6 +269,15 @@ config RTC_DRV_X1205 This driver can also be built as a module. If so, the module will be called rtc-x1205. +config RTC_DRV_PCF8523 + tristate "NXP PCF8523" + help + If you say yes here you get support for the NXP PCF8523 RTC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf8523. + config RTC_DRV_PCF8563 tristate "Philips PCF8563/Epson RTC8564" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 56297f0..59e2132 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o 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_PCF8523) += rtc-pcf8523.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 diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c new file mode 100644 index 0000000..b29ea6d --- /dev/null +++ b/drivers/rtc/rtc-pcf8523.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2012 Avionic Design 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 + +#define DRIVER_NAME "rtc-pcf8523" + +#define REG_CONTROL1 0x00 +#define REG_CONTROL1_CAP_SEL (1 << 7) +#define REG_CONTROL1_STOP (1 << 5) + +#define REG_CONTROL3 0x02 +#define REG_CONTROL3_PM_BLD (1 << 7) /* battery low detection disabled */ +#define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */ +#define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */ +#define REG_CONTROL3_PM_MASK 0xe0 + +#define REG_SECONDS 0x03 +#define REG_SECONDS_OS (1 << 7) + +#define REG_MINUTES 0x04 +#define REG_HOURS 0x05 +#define REG_DAYS 0x06 +#define REG_WEEKDAYS 0x07 +#define REG_MONTHS 0x08 +#define REG_YEARS 0x09 + +struct pcf8523 { + struct rtc_device *rtc; +}; + +static int pcf8523_read(struct i2c_client *client, u8 reg, u8 *valuep) +{ + struct i2c_msg msgs[2]; + u8 value = 0; + int err; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(reg); + msgs[0].buf = ® + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(value); + msgs[1].buf = &value; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err < 0) + return err; + + *valuep = value; + + return 0; +} + +static int pcf8523_write(struct i2c_client *client, u8 reg, u8 value) +{ + u8 buffer[2] = { reg, value }; + struct i2c_msg msg; + int err; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = sizeof(buffer); + msg.buf = buffer; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err < 0) + return err; + + return 0; +} + +static int pcf8523_select_capacitance(struct i2c_client *client, bool high) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL1, &value); + if (err < 0) + return err; + + if (!high) + value &= ~REG_CONTROL1_CAP_SEL; + else + value |= REG_CONTROL1_CAP_SEL; + + err = pcf8523_write(client, REG_CONTROL1, value); + if (err < 0) + return err; + + return err; +} + +static int pcf8523_set_pm(struct i2c_client *client, u8 pm) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL3, &value); + if (err < 0) + return err; + + value = (value & ~REG_CONTROL3_PM_MASK) | pm; + + err = pcf8523_write(client, REG_CONTROL3, value); + if (err < 0) + return err; + + return 0; +} + +static int pcf8523_stop_rtc(struct i2c_client *client) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL1, &value); + if (err < 0) + return err; + + value |= REG_CONTROL1_STOP; + + err = pcf8523_write(client, REG_CONTROL1, value); + if (err < 0) + return err; + + return 0; +} + +static int pcf8523_start_rtc(struct i2c_client *client) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL1, &value); + if (err < 0) + return err; + + value &= ~REG_CONTROL1_STOP; + + err = pcf8523_write(client, REG_CONTROL1, value); + if (err < 0) + return err; + + return 0; +} + +static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 start = REG_SECONDS, regs[7]; + struct i2c_msg msgs[2]; + int err; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &start; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(regs); + msgs[1].buf = regs; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err < 0) + return err; + + if (regs[0] & REG_SECONDS_OS) { + dev_dbg(dev, "clock integrity is not guaranteed\n"); + + /* try to clear the flag */ + regs[0] &= ~REG_SECONDS_OS; + + err = pcf8523_write(client, REG_SECONDS, regs[0]); + if (err < 0) + return err; + } + + tm->tm_sec = bcd2bin(regs[0] & 0x7f); + tm->tm_min = bcd2bin(regs[1] & 0x7f); + tm->tm_hour = bcd2bin(regs[2] & 0x3f); + tm->tm_mday = bcd2bin(regs[3] & 0x3f); + tm->tm_wday = regs[4] & 0x7; + tm->tm_mon = bcd2bin(regs[5] & 0x1f); + tm->tm_year = bcd2bin(regs[6]) + 100; + + return rtc_valid_tm(tm); +} + +static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg; + u8 regs[8]; + int err; + + err = pcf8523_stop_rtc(client); + if (err < 0) + return err; + + regs[0] = REG_SECONDS; + regs[1] = bin2bcd(tm->tm_sec); + regs[2] = bin2bcd(tm->tm_min); + regs[3] = bin2bcd(tm->tm_hour); + regs[4] = bin2bcd(tm->tm_mday); + regs[5] = tm->tm_wday; + regs[6] = bin2bcd(tm->tm_mon); + regs[7] = bin2bcd(tm->tm_year - 100); + + msg.addr = client->addr; + msg.flags = 0; + msg.len = sizeof(regs); + msg.buf = regs; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err < 0) + return err; + + return pcf8523_start_rtc(client); +} + +static const struct rtc_class_ops pcf8523_rtc_ops = { + .read_time = pcf8523_rtc_read_time, + .set_time = pcf8523_rtc_set_time, +}; + +static int pcf8523_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf8523 *pcf; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL); + if (!pcf) + return -ENOMEM; + + err = pcf8523_select_capacitance(client, true); + if (err < 0) + return err; + + err = pcf8523_set_pm(client, 0); + if (err < 0) + return err; + + pcf->rtc = rtc_device_register(DRIVER_NAME, &client->dev, + &pcf8523_rtc_ops, THIS_MODULE); + if (IS_ERR(pcf->rtc)) + return PTR_ERR(pcf->rtc); + + i2c_set_clientdata(client, pcf); + + return 0; +} + +static int pcf8523_remove(struct i2c_client *client) +{ + struct pcf8523 *pcf = i2c_get_clientdata(client); + + rtc_device_unregister(pcf->rtc); + + return 0; +} + +static const struct i2c_device_id pcf8523_id[] = { + { "pcf8523", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8523_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcf8523_of_match[] = { + { .compatible = "nxp,pcf8523" }, + { } +}; +MODULE_DEVICE_TABLE(of, pcf8523_of_match); +#endif + +static struct i2c_driver pcf8523_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf8523_of_match), + }, + .probe = pcf8523_probe, + .remove = pcf8523_remove, + .id_table = pcf8523_id, +}; +module_i2c_driver(pcf8523_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NXP PCF8523 RTC driver"); +MODULE_LICENSE("GPL v2");