* [PATCH 3/4] RTC: add Xicor 1241 driver
@ 2007-09-30 9:55 Mark Zhan
2007-10-01 13:59 ` [i2c] " Jean Delvare
0 siblings, 1 reply; 2+ messages in thread
From: Mark Zhan @ 2007-09-30 9:55 UTC (permalink / raw)
To: i2c, linux-mips, rtc-linux; +Cc: ralf, a.zummo, rongkai.zhan
This patch add the Xicor 1241 RTC driver which is used in
MIPS Sibyte 1250/1480 boards.
Signed-off-by: Mark Zhan <rongkai.zhan@windriver.com>
---
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 2
drivers/rtc/rtc-xicor1241.c | 184 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 196 insertions(+)
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -184,6 +184,16 @@ 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_XICOR1241
+ tristate "Xicor 1241"
+ depends on RTC_CLASS && I2C && MIPS
+ help
+ If you say yes here you get support for the
+ Xicor 1241 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-xicor1241.
+
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
help
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -48,3 +48,5 @@ obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_XICOR1241) += rtc-xicor1241.o
+
--- /dev/null
+++ b/drivers/rtc/rtc-xicor1241.c
@@ -0,0 +1,184 @@
+/*
+ * Xicor 1241 RTC driver
+ *
+ * Copyright (c) 2007 Wind River Systems, Inc.
+ *
+ * Author: Mark Zhan <rongkai.zhan@windriver.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+
+/*
+ * Register bits
+ */
+#define X1241_SR_BAT 0x80 /* currently on battery power */
+#define X1241_SR_RWEL 0x04 /* r/w latch is enabled, can write RTC */
+#define X1241_SR_WEL 0x02 /* r/w latch is unlocked, can enable r/w now */
+#define X1241_SR_RTCF 0x01 /* clock failed */
+#define X1241_HR_MIL 0x80 /* 24 hours format */
+
+/*
+ * Register Offset
+ */
+#define X1241_SEC 0x30 /* Seconds */
+#define X1241_MIN 0x31 /* Minutes */
+#define X1241_HOUR 0x32 /* Hours */
+#define X1241_MDAY 0x33 /* Day of month */
+#define X1241_MON 0x34 /* Month */
+#define X1241_YEAR 0x35 /* Year */
+#define X1241_WDAY 0x36 /* Day of Week */
+#define X1241_Y2K 0x37 /* Year 2K */
+#define X1241_SR 0x3F /* Status register */
+
+DEFINE_SPINLOCK(xicor1241_lock);
+
+static int xicor1241_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned int y2k, year, mon, mday, wday, hour, min, sec;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xicor1241_lock, flags);
+
+ i2c_smbus_write_byte_data(client, X1241_SEC, X1241_SEC);
+ sec = i2c_smbus_read_byte_data(client, X1241_SEC);
+ min = i2c_smbus_read_byte_data(client, X1241_MIN);
+ hour = i2c_smbus_read_byte_data(client, X1241_HOUR);
+ mday = i2c_smbus_read_byte_data(client, X1241_MDAY);
+ mon = i2c_smbus_read_byte_data(client, X1241_MON);
+ year = i2c_smbus_read_byte_data(client, X1241_YEAR);
+ wday = i2c_smbus_read_byte_data(client, X1241_WDAY);
+ y2k = i2c_smbus_read_byte_data(client, X1241_Y2K);
+
+ spin_unlock_irqrestore(&xicor1241_lock, flags);
+
+ dev_dbg(dev, "X1241 read reg: y2k=%02x,year=%02x,mon=%02x,"
+ "mday=%02x,wday=%02x,hour=%02x,min=%02x,sec=%02x\n",
+ y2k, year, mon, mday, wday, hour, min, sec);
+
+ tm->tm_sec = BCD2BIN(sec);
+ tm->tm_min = BCD2BIN(min);
+ tm->tm_hour = BCD2BIN(hour & 0x3F); /* hr is 0-23 */
+ tm->tm_mday = BCD2BIN(mday);
+ tm->tm_wday = BCD2BIN(wday);
+ tm->tm_mon = BCD2BIN(mon) - 1; /* tm_mon is 0-11 */
+ tm->tm_year = BCD2BIN(year);
+ tm->tm_year += (BCD2BIN(y2k) * 100) - 1900;
+
+ dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d:%02d:%02d, y2k=%02d\n",
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, y2k);
+ return 0;
+}
+
+static int xicor1241_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned int y2k, year, mon, mday, wday, hour, min, sec;
+ unsigned long flags;
+
+ dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d:%02d:%02d\n",
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ sec = BIN2BCD(tm->tm_sec);
+ min = BIN2BCD(tm->tm_min);
+ hour = BIN2BCD(tm->tm_hour) | X1241_HR_MIL; /* 24 hours format */
+ mday = BIN2BCD(tm->tm_mday);
+ wday = BIN2BCD(tm->tm_wday);
+ mon = BIN2BCD(tm->tm_mon + 1); /* tm_mon is 0-11 */
+ year = BIN2BCD(tm->tm_year % 100);
+ y2k = BIN2BCD((tm->tm_year / 100) + 19);
+
+ dev_dbg(dev, "X1241 write reg: y2k=%02x,year=%02x,mon=%02x,"
+ "mday=%02x,wday=%02x,hour=%02x,min=%02x,sec=%02x\n",
+ y2k, year, mon, mday, wday, hour, min, sec);
+
+ spin_lock_irqsave(&xicor1241_lock, flags);
+
+ /* unlock writes to the CCR */
+ i2c_smbus_write_word_data(client, X1241_SR,
+ (X1241_SR_WEL << 8) | X1241_SR);
+ i2c_smbus_write_word_data(client, X1241_SR,
+ ((X1241_SR_WEL | X1241_SR_RWEL) << 8) | X1241_SR);
+
+ i2c_smbus_write_word_data(client, X1241_SEC, (sec << 8) | X1241_SEC);
+ i2c_smbus_write_word_data(client, X1241_MIN, (min << 8) | X1241_MIN);
+ i2c_smbus_write_word_data(client, X1241_HOUR, (hour << 8) | X1241_HOUR);
+ i2c_smbus_write_word_data(client, X1241_MDAY, (mday << 8) | X1241_MDAY);
+ i2c_smbus_write_word_data(client, X1241_WDAY, (wday << 8) | X1241_WDAY);
+ i2c_smbus_write_word_data(client, X1241_MON, (mon << 8) | X1241_MON);
+ i2c_smbus_write_word_data(client, X1241_YEAR, (year << 8) | X1241_YEAR);
+ i2c_smbus_write_word_data(client, X1241_Y2K, (y2k << 8) | X1241_Y2K);
+
+ i2c_smbus_write_word_data(client, X1241_SR, (0 << 8) | X1241_SR);
+
+ spin_unlock_irqrestore(&xicor1241_lock, flags);
+ return 0;
+}
+
+static const struct rtc_class_ops xicor1241_rtc_ops = {
+ .read_time = xicor1241_get_time,
+ .set_time = xicor1241_set_time,
+};
+
+static int __devinit xicor1241_rtc_probe(struct i2c_client *client)
+{
+ struct rtc_device *rtc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_dbg(&client->dev, "I2C adapter function check failure!\n");
+ return -ENODEV;
+ }
+
+ rtc = rtc_device_register(client->name, &client->dev,
+ &xicor1241_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+ return 0;
+}
+
+static int __devexit xicor1241_rtc_remove(struct i2c_client *client)
+{
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ rtc_device_unregister(rtc);
+ return 0;
+}
+
+static struct i2c_driver xicor1241_i2c_driver = {
+ .driver = {
+ .name = "rtc-xicor1241",
+ .owner = THIS_MODULE,
+ },
+ .probe = xicor1241_rtc_probe,
+ .remove = __devexit_p(xicor1241_rtc_remove),
+};
+
+static int __init xicor1241_rtc_init(void)
+{
+ return i2c_add_driver(&xicor1241_i2c_driver);
+}
+
+static void __exit xicor1241_rtc_exit(void)
+{
+ i2c_del_driver(&xicor1241_i2c_driver);
+}
+
+module_init(xicor1241_rtc_init);
+module_exit(xicor1241_rtc_exit);
+
+MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>");
+MODULE_DESCRIPTION("Xicor1241 RTC driver");
+MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [i2c] [PATCH 3/4] RTC: add Xicor 1241 driver
2007-09-30 9:55 [PATCH 3/4] RTC: add Xicor 1241 driver Mark Zhan
@ 2007-10-01 13:59 ` Jean Delvare
0 siblings, 0 replies; 2+ messages in thread
From: Jean Delvare @ 2007-10-01 13:59 UTC (permalink / raw)
To: Mark Zhan; +Cc: i2c, linux-mips, rtc-linux, a.zummo, ralf
Hi Mark,
On Sun, 30 Sep 2007 17:55:05 +0800, Mark Zhan wrote:
> This patch add the Xicor 1241 RTC driver which is used in
> MIPS Sibyte 1250/1480 boards.
So this chip is using two-byte register addressing, which isn't
compatible with SMBus. Which explains why the register reads and writes
in your driver look strange. I don't think it's quite correct.
> +/*
> + * Register Offset
> + */
> +#define X1241_SEC 0x30 /* Seconds */
> +#define X1241_MIN 0x31 /* Minutes */
> +#define X1241_HOUR 0x32 /* Hours */
> +#define X1241_MDAY 0x33 /* Day of month */
> +#define X1241_MON 0x34 /* Month */
> +#define X1241_YEAR 0x35 /* Year */
> +#define X1241_WDAY 0x36 /* Day of Week */
> +#define X1241_Y2K 0x37 /* Year 2K */
> +#define X1241_SR 0x3F /* Status register */
> +
> +DEFINE_SPINLOCK(xicor1241_lock);
> +
> +static int xicor1241_get_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + unsigned int y2k, year, mon, mday, wday, hour, min, sec;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&xicor1241_lock, flags);
> +
> + i2c_smbus_write_byte_data(client, X1241_SEC, X1241_SEC);
If I read the datasheet properly, this should be:
i2c_smbus_write_byte_data(client, 0, X1241_SEC);
The SC register is at 0x0030, not 0x3030.
> + sec = i2c_smbus_read_byte_data(client, X1241_SEC);
> + min = i2c_smbus_read_byte_data(client, X1241_MIN);
> + hour = i2c_smbus_read_byte_data(client, X1241_HOUR);
> + mday = i2c_smbus_read_byte_data(client, X1241_MDAY);
> + mon = i2c_smbus_read_byte_data(client, X1241_MON);
> + year = i2c_smbus_read_byte_data(client, X1241_YEAR);
> + wday = i2c_smbus_read_byte_data(client, X1241_WDAY);
> + y2k = i2c_smbus_read_byte_data(client, X1241_Y2K);
You are using the "Current Address Read" mode here, right? If so, you
aren't supposed to send any address information at all, i.e. you should
use i2c_smbus_read_byte() instead of i2c_smbus_read_byte_data(). You
are probably just lucky that the chip ignores the extra byte you're
sending.
> (...)
> +static int xicor1241_set_time(struct device *dev, struct rtc_time *tm)
> +{
> (...)
> + /* unlock writes to the CCR */
> + i2c_smbus_write_word_data(client, X1241_SR,
> + (X1241_SR_WEL << 8) | X1241_SR);
> + i2c_smbus_write_word_data(client, X1241_SR,
> + ((X1241_SR_WEL | X1241_SR_RWEL) << 8) | X1241_SR);
> +
> + i2c_smbus_write_word_data(client, X1241_SEC, (sec << 8) | X1241_SEC);
> + i2c_smbus_write_word_data(client, X1241_MIN, (min << 8) | X1241_MIN);
> + i2c_smbus_write_word_data(client, X1241_HOUR, (hour << 8) | X1241_HOUR);
> + i2c_smbus_write_word_data(client, X1241_MDAY, (mday << 8) | X1241_MDAY);
> + i2c_smbus_write_word_data(client, X1241_WDAY, (wday << 8) | X1241_WDAY);
> + i2c_smbus_write_word_data(client, X1241_MON, (mon << 8) | X1241_MON);
> + i2c_smbus_write_word_data(client, X1241_YEAR, (year << 8) | X1241_YEAR);
> + i2c_smbus_write_word_data(client, X1241_Y2K, (y2k << 8) | X1241_Y2K);
> +
> + i2c_smbus_write_word_data(client, X1241_SR, (0 << 8) | X1241_SR);
Here again I am surprised. I expected:
i2c_smbus_write_word_data(client, 0, (sec << 8) | X1241_SEC);
So that you write to register 0x0030, not 0x3030. Same for all the
other register writes. Or am I misreading the datasheet?
> +
> + spin_unlock_irqrestore(&xicor1241_lock, flags);
> + return 0;
> +}
> +
> +static const struct rtc_class_ops xicor1241_rtc_ops = {
> + .read_time = xicor1241_get_time,
> + .set_time = xicor1241_set_time,
> +};
> +
> +static int __devinit xicor1241_rtc_probe(struct i2c_client *client)
> +{
> + struct rtc_device *rtc;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
> + dev_dbg(&client->dev, "I2C adapter function check failure!\n");
> + return -ENODEV;
> + }
This check isn't sufficient, you must check for
I2C_FUNC_SMBUS_WRITE_WORD_DATA as well, and possibly
I2C_FUNC_SMBUS_READ_BYTE if my comment above is correct.
--
Jean Delvare
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2007-10-01 14:00 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-30 9:55 [PATCH 3/4] RTC: add Xicor 1241 driver Mark Zhan
2007-10-01 13:59 ` [i2c] " Jean Delvare
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.