From patchwork Mon Oct 5 11:57:24 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Piotr Ziecik X-Patchwork-Id: 34992 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-vw0-f139.google.com (mail-vw0-f139.google.com [209.85.212.139]) by ozlabs.org (Postfix) with ESMTP id 9231DB7BB4 for ; Mon, 5 Oct 2009 22:58:12 +1100 (EST) Received: by vws3 with SMTP id 3so4734532vws.1 for ; Mon, 05 Oct 2009 04:58:10 -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:received-spf:received:from:to:cc:subject :date:message-id:x-mailer:reply-to:sender:precedence:x-google-loop :mailing-list:list-id:list-post:list-help:list-unsubscribe :x-beenthere-env:x-beenthere; bh=sQuz4bWMxfQUCn/OgTUflv+gpxyaFc6OEYUiict+nPQ=; b=NN6kuJ7aEwiPnjfGU9CEA0W5dK2BVfJRyc6Sy0b9IxcHBxeFhldHRxY7+m0z7nea3m 6G6m0LHip25sdgr+uXOkjVfZnmmflJc4eQXfAtyQdZoqR3BJ1k01C4NBqeHDPLbTEq2d weHOLVdzYbfj1cSRw+lMOG6OdsCaPPPIvHn34= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-sender:x-apparently-to:received-spf:authentication-results:from :to:cc:subject:date:message-id:x-mailer:reply-to:sender:precedence :x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; b=b5W/YOkvMIMq3YZzAuyTFZ7zkFGDXWtTHV95f3nJ2t+SXKIAV4Q9jGOxPi84lBJRxI 19LjmQM7zBGmk3umrUc4Gsego7fma67rPSLXd8sKb8U6wpxRNV923D+w5nz2H604Qhti kK5A+KPmKkywdpiuQQw5EJHfQ8qdmn7C4bauc= Received: by 10.229.115.35 with SMTP id g35mr316388qcq.29.1254743889126; Mon, 05 Oct 2009 04:58:09 -0700 (PDT) Received: by 10.177.108.35 with SMTP id k35gr2929yqm.0; Mon, 05 Oct 2009 04:58:09 -0700 (PDT) X-Sender: kosmo@semihalf.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.204.15.20 with SMTP id i20mr182672bka.33.1254743887807; Mon, 05 Oct 2009 04:58:07 -0700 (PDT) Received: by 10.204.15.20 with SMTP id i20mr182671bka.33.1254743887772; Mon, 05 Oct 2009 04:58:07 -0700 (PDT) Received: from angel.net.pl (angel.net.pl [82.177.44.3]) by gmr-mx.google.com with ESMTP id 14si630758bwz.5.2009.10.05.04.58.07; Mon, 05 Oct 2009 04:58:07 -0700 (PDT) Received-SPF: neutral (google.com: 82.177.44.3 is neither permitted nor denied by best guess record for domain of kosmo@semihalf.com) client-ip=82.177.44.3; Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 82.177.44.3 is neither permitted nor denied by best guess record for domain of kosmo@semihalf.com) smtp.mail=kosmo@semihalf.com Received: from cardhu.semihalf.com ([213.17.239.108] helo=localhost.localdomain) by angel.net.pl (envelope-from ) with esmtpa (Exim 4.69) id 1MumCL-0006Tm-7H; Mon, 05 Oct 2009 13:57:58 +0200 From: Piotr Ziecik To: rtc-linux@googlegroups.com Cc: office@semihalf.com, Piotr Ziecik Subject: [rtc-linux] [PATCH] rtc: Add driver for BQ32000 I2C RTC. Date: Mon, 5 Oct 2009 13:57:24 +0200 Message-Id: <1254743844-7537-1-git-send-email-kosmo@semihalf.com> X-Mailer: git-send-email 1.6.4.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 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 --- 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 3c20dae..689620a 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 aa3fbd5..0f1c6cc 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");