From patchwork Wed Nov 18 15:30:33 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessandro Zummo X-Patchwork-Id: 38758 Return-Path: <3GxMESw0MCX0b0vnnp-hpphmfupxfsufdi.jusud-mjovyhpphmfhspvqt.dpn@groups.bounces.google.com> X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-yw0-f159.google.com (mail-yw0-f159.google.com [209.85.211.159]) by ozlabs.org (Postfix) with ESMTP id 0A4A3B7B66 for ; Thu, 19 Nov 2009 02:30:35 +1100 (EST) Received: by mail-yw0-f159.google.com with SMTP id 31sf1909941ywh.29 for ; Wed, 18 Nov 2009 07:30:35 -0800 (PST) Received: by 10.91.204.26 with SMTP id g26mr228191agq.18.1258558235443; Wed, 18 Nov 2009 07:30:35 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.204.15.19 with SMTP id i19ls6002103bka.3.p; Wed, 18 Nov 2009 07:30:34 -0800 (PST) Received: by 10.204.33.6 with SMTP id f6mr374556bkd.18.1258558234247; Wed, 18 Nov 2009 07:30:34 -0800 (PST) Received: by 10.204.33.6 with SMTP id f6mr374555bkd.18.1258558234205; Wed, 18 Nov 2009 07:30:34 -0800 (PST) Received: from elettra.colt-to.towertech.it (elettra.colt-to.towertech.it [213.215.222.70]) by gmr-mx.google.com with ESMTP id 18si16642bwz.9.2009.11.18.07.30.34; Wed, 18 Nov 2009 07:30:34 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of a.zummo@towertech.it designates 213.215.222.70 as permitted sender) client-ip=213.215.222.70; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of a.zummo@towertech.it designates 213.215.222.70 as permitted sender) smtp.mail=a.zummo@towertech.it Received: from linux.lan.towertech.it (93-39-51-125.ip74.fastwebnet.it [93.39.51.125]) by elettra.colt-to.towertech.it (Postfix) with ESMTPA id 8D8011167E1; Wed, 18 Nov 2009 16:30:33 +0100 (CET) Subject: [rtc-linux] [PATCH 5/7] rtc: Add driver for BQ32000 I2C RTC. To: akpm@linux-foundation.org From: Alessandro Zummo Cc: Piotr Ziecik , rtc-linux@googlegroups.com Date: Wed, 18 Nov 2009 16:30:33 +0100 Message-ID: <20091118153033.24824.57645.stgit@linux.lan.towertech.it> In-Reply-To: <20091118153010.24824.14155.stgit@linux.lan.towertech.it> References: <20091118153010.24824.14155.stgit@linux.lan.towertech.it> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.95.2 at elettra X-Virus-Status: Clean 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: X-Thread-Url: http://groups.google.com/group/rtc-linux/t/3d37b7123be786a4 X-Message-Url: http://groups.google.com/group/rtc-linux/msg/2736314380abf39a From: Piotr Ziecik This patch adds basic support for Texas Instruments BQ32000 I2C RTC. Only time reading/writing is implemented. Advanced features, such as trickle charger and crystal calibration are not supported. Signed-off-by: Piotr Ziecik Signed-off-by: Alessandro Zummo patch-name: 34992-rtc-bq32k-driver patch-rev1: 8a98771b272e8ab3a6cd10f2901fc3f91c959968 patch-rev2: f50c52ad7f6f8be4f899e8780486f73b93760a56 --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 drivers/rtc/rtc-bq32k.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 0 deletions(-) create mode 100644 drivers/rtc/rtc-bq32k.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f811501..3f1750c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -242,6 +242,15 @@ config RTC_DRV_M41T80_WDT If you say Y here you will get support for the watchdog timer in the ST M41T60 and M41T80 RTC chips series. +config RTC_DRV_BQ32K + tristate "TI BQ32000" + help + If you say Y here you will get support for the TI + BQ32000 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq32k. + config RTC_DRV_DM355EVM tristate "TI DaVinci DM355 EVM RTC" depends on MFD_DM355EVM_MSP diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f4d01ba..21cd67b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c new file mode 100644 index 0000000..408cc8f --- /dev/null +++ b/drivers/rtc/rtc-bq32k.c @@ -0,0 +1,204 @@ +/* + * Driver for TI BQ32000 RTC. + * + * Copyright (C) 2009 Semihalf. + * + * 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 BQ32K_SECONDS 0x00 /* Seconds register address */ +#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */ +#define BQ32K_STOP 0x80 /* Oscillator Stop flat */ + +#define BQ32K_MINUTES 0x01 /* Minutes register address */ +#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */ +#define BQ32K_OF 0x80 /* Oscillator Failure flag */ + +#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */ +#define BQ32K_CENT 0x40 /* Century flag */ +#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ + +struct bq32k_regs { + uint8_t seconds; + uint8_t minutes; + uint8_t cent_hours; + uint8_t day; + uint8_t date; + uint8_t month; + uint8_t years; +}; + +static struct i2c_driver bq32k_driver; + +static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &off, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + } + }; + + if (i2c_transfer(client->adapter, msgs, 2) == 2) + return 0; + + return -EIO; +} + +static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + uint8_t buffer[len + 1]; + + buffer[0] = off; + memcpy(&buffer[1], data, len); + + if (i2c_master_send(client, buffer, len + 1) == len + 1) + return 0; + + return -EIO; +} + +static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct bq32k_regs regs; + int error; + + error = bq32k_read(dev, ®s, 0, sizeof(regs)); + if (error) + return error; + + tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK); + tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK); + tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK); + tm->tm_mday = bcd2bin(regs.date); + tm->tm_wday = bcd2bin(regs.day) - 1; + tm->tm_mon = bcd2bin(regs.month) - 1; + tm->tm_year = bcd2bin(regs.years) + + ((regs.cent_hours & BQ32K_CENT) ? 100 : 0); + + return rtc_valid_tm(tm); +} + +static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct bq32k_regs regs; + + regs.seconds = bin2bcd(tm->tm_sec); + regs.minutes = bin2bcd(tm->tm_min); + regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN; + regs.day = bin2bcd(tm->tm_wday + 1); + regs.date = bin2bcd(tm->tm_mday); + regs.month = bin2bcd(tm->tm_mon + 1); + + if (tm->tm_year >= 100) { + regs.cent_hours |= BQ32K_CENT; + regs.years = bin2bcd(tm->tm_year - 100); + } else + regs.years = bin2bcd(tm->tm_year); + + return bq32k_write(dev, ®s, 0, sizeof(regs)); +} + +static const struct rtc_class_ops bq32k_rtc_ops = { + .read_time = bq32k_rtc_read_time, + .set_time = bq32k_rtc_set_time, +}; + +static int bq32k_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct rtc_device *rtc; + uint8_t reg; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* Check Oscillator Stop flag */ + error = bq32k_read(dev, ®, BQ32K_SECONDS, 1); + if (!error && (reg & BQ32K_STOP)) { + dev_warn(dev, "Oscillator was halted. Restarting...\n"); + reg &= ~BQ32K_STOP; + error = bq32k_write(dev, ®, BQ32K_SECONDS, 1); + } + if (error) + return error; + + /* Check Oscillator Failure flag */ + error = bq32k_read(dev, ®, BQ32K_MINUTES, 1); + if (!error && (reg & BQ32K_OF)) { + dev_warn(dev, "Oscillator Failure. Check RTC battery.\n"); + reg &= ~BQ32K_OF; + error = bq32k_write(dev, ®, BQ32K_MINUTES, 1); + } + if (error) + return error; + + rtc = rtc_device_register(bq32k_driver.driver.name, &client->dev, + &bq32k_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int __devexit bq32k_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + rtc_device_unregister(rtc); + return 0; +} + +static const struct i2c_device_id bq32k_id[] = { + { "bq32000", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq32k_id); + +static struct i2c_driver bq32k_driver = { + .driver = { + .name = "bq32k", + .owner = THIS_MODULE, + }, + .probe = bq32k_probe, + .remove = __devexit_p(bq32k_remove), + .id_table = bq32k_id, +}; + +static __init int bq32k_init(void) +{ + return i2c_add_driver(&bq32k_driver); +} +module_init(bq32k_init); + +static __exit void bq32k_exit(void) +{ + i2c_del_driver(&bq32k_driver); +} +module_exit(bq32k_exit); + +MODULE_AUTHOR("Semihalf, Piotr Ziecik "); +MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver"); +MODULE_LICENSE("GPL");