From: mgreer@mvista.com (Mark A. Greer)
To: Greg KH <greg@kroah.com>
Cc: LM Sensors <sensors@stimpy.netroedge.com>,
lkml <linux-kernel@vger.kernel.org>
Subject: [PATCH][I2C] ST M41T00 I2C RTC chip driver
Date: Thu, 19 May 2005 06:25:34 +0000 [thread overview]
Message-ID: <41FE7368.1000307@mvista.com> (raw)
This patch adds support for the ST M41T00 RTC chip.
You will likely notice that it implements a PPC-specific interface
(/dev/rtc->drivers/char/genrtc.h->include/asm-ppc/rtc.h->this file).
This was necessary to support a subset of ppc platforms that need to
hook up the rtc support at runtime. If I implemented /dev/rtc directly
or interfaced to genrtc.c directly, those platforms couldn't use this
driver. Eventually, I hope to work on more uniform rtc support across
all the processor architectures.
Also, on ppc at least, the hw clock can be set from a timer interrupt if
STA_UNSYNC is not set (e.g., ntpd is running). To handle this, a
tasklet is used to set the clock if in_interrupt() is true.
I'd appreciate an comments or to have it pushed into the kernel.org tree
if its acceptable.
Thanks,
Mark
Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--
-------------- next part --------------
diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig 2005-01-31 10:33:34 -07:00
+++ b/drivers/i2c/chips/Kconfig 2005-01-31 10:33:34 -07:00
@@ -371,4 +371,13 @@
This driver can also be built as a module. If so, the module
will be called isp1301_omap.
+config SENSORS_M41T00
+ tristate "ST M41T00 RTC chip"
+ depends on I2C && PPC32
+ help
+ If you say yes here you get support for the ST M41T00 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-m41t00.
+
endmenu
diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile 2005-01-31 10:33:34 -07:00
+++ b/drivers/i2c/chips/Makefile 2005-01-31 10:33:34 -07:00
@@ -26,6 +26,7 @@
obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
+obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff -Nru a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/chips/m41t00.c 2005-01-31 10:33:34 -07:00
@@ -0,0 +1,224 @@
+/*
+ * drivers/i2c/chips/m41t00.c
+ *
+ * I2C client/driver for the ST M41T00 Real-Time Clock chip.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+/*
+ * This i2c client/driver wedges between the drivers/char/genrtc.c RTC
+ * interface and the SMBus interface of the i2c subsystem.
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/time.h>
+#include <asm/rtc.h>
+
+static DECLARE_MUTEX(m41t00_mutex);
+
+struct m41t00_data {
+ struct i2c_client client;
+};
+
+static struct i2c_driver m41t00_driver;
+static struct i2c_client *save_client;
+
+static unsigned short ignore[] = { I2C_CLIENT_END };
+static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_addr,
+ .normal_i2c_range = ignore,
+ .probe = ignore,
+ .probe_range = ignore,
+ .ignore = ignore,
+ .ignore_range = ignore,
+ .force = ignore,
+};
+
+ulong
+m41t00_get_rtc_time(void)
+{
+ s32 sec, min, hour, day, mon, year;
+
+ down(&m41t00_mutex);
+ if (((sec = i2c_smbus_read_byte_data(save_client, 0)) < 0)
+ || ((min = i2c_smbus_read_byte_data(save_client, 1)) < 0)
+ || ((hour = i2c_smbus_read_byte_data(save_client, 2)) < 0)
+ || ((day = i2c_smbus_read_byte_data(save_client, 4)) < 0)
+ || ((mon = i2c_smbus_read_byte_data(save_client, 5)) < 0)
+ || ((year = i2c_smbus_read_byte_data(save_client, 6)) < 0)) {
+
+ dev_warn(&save_client->dev, "m41t00: can't read rtc chip\n");
+ sec = min = hour = day = mon = year = 0;
+ }
+ up(&m41t00_mutex);
+
+ sec &= 0x7f;
+ min &= 0x7f;
+ hour &= 0x3f;
+ day &= 0x3f;
+ mon &= 0x1f;
+ year &= 0xff;
+
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+
+ year += 1900;
+ if (year < 1970)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+static void
+m41t00_set_tlet(ulong arg)
+{
+ struct rtc_time tm;
+ ulong nowtime = *(ulong *)arg;
+
+ to_tm(nowtime, &tm);
+ tm.tm_year = (tm.tm_year - 1900) % 100;
+
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_mon);
+ BIN_TO_BCD(tm.tm_mday);
+ BIN_TO_BCD(tm.tm_year);
+
+ down(&m41t00_mutex);
+ if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
+ || (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0x7f)
+ < 0))
+
+ dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
+
+ up(&m41t00_mutex);
+ return;
+}
+
+ulong new_time;
+
+DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time);
+
+int
+m41t00_set_rtc_time(ulong nowtime)
+{
+ new_time = nowtime;
+
+ if (in_interrupt())
+ tasklet_schedule(&m41t00_tasklet);
+ else
+ m41t00_set_tlet((ulong)&new_time);
+
+ return 0;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Driver Interface
+ *
+ *****************************************************************************
+ */
+static int __devinit
+m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct i2c_client *new_client;
+ struct m41t00_data *drv_data;
+ int rc;
+
+ drv_data = kmalloc(sizeof(struct m41t00_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ memset(drv_data, 0, sizeof(struct m41t00_data));
+ new_client = &drv_data->client;
+
+ strncpy(new_client->name, "m41t00", I2C_NAME_SIZE);
+ i2c_set_clientdata(new_client, drv_data);
+ new_client->id = m41t00_driver.id;
+ new_client->flags = I2C_DF_NOTIFY;
+ new_client->addr = addr;
+ new_client->adapter = adap;
+ new_client->driver = &m41t00_driver;
+
+ if ((rc = i2c_attach_client(new_client)) != 0) {
+ kfree(drv_data);
+ return rc;
+ }
+
+ save_client = new_client;
+ return 0;
+}
+
+static int __devinit
+m41t00_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, m41t00_probe);
+}
+
+static int __devexit
+m41t00_detach(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(i2c_get_clientdata(client));
+ tasklet_kill(&m41t00_tasklet);
+ return 0;
+}
+
+static struct i2c_driver m41t00_driver = {
+ .owner = THIS_MODULE,
+ .name = "M41T00",
+ .id = I2C_DRIVERID_STM41T00,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = m41t00_attach,
+ .detach_client = m41t00_detach,
+};
+
+static int __devinit
+m41t00_init(void)
+{
+ return i2c_add_driver(&m41t00_driver);
+}
+
+static void __devexit
+m41t00_exit(void)
+{
+ i2c_del_driver(&m41t00_driver);
+ return;
+}
+
+module_init(m41t00_init);
+module_exit(m41t00_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
+MODULE_LICENSE("GPL");
WARNING: multiple messages have this Message-ID (diff)
From: "Mark A. Greer" <mgreer@mvista.com>
To: Greg KH <greg@kroah.com>
Cc: LM Sensors <sensors@stimpy.netroedge.com>,
lkml <linux-kernel@vger.kernel.org>
Subject: [PATCH][I2C] ST M41T00 I2C RTC chip driver
Date: Mon, 31 Jan 2005 11:05:28 -0700 [thread overview]
Message-ID: <41FE7368.1000307@mvista.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 875 bytes --]
This patch adds support for the ST M41T00 RTC chip.
You will likely notice that it implements a PPC-specific interface
(/dev/rtc->drivers/char/genrtc.h->include/asm-ppc/rtc.h->this file).
This was necessary to support a subset of ppc platforms that need to
hook up the rtc support at runtime. If I implemented /dev/rtc directly
or interfaced to genrtc.c directly, those platforms couldn't use this
driver. Eventually, I hope to work on more uniform rtc support across
all the processor architectures.
Also, on ppc at least, the hw clock can be set from a timer interrupt if
STA_UNSYNC is not set (e.g., ntpd is running). To handle this, a
tasklet is used to set the clock if in_interrupt() is true.
I'd appreciate an comments or to have it pushed into the kernel.org tree
if its acceptable.
Thanks,
Mark
Signed-off-by: Mark A. Greer <mgreer@mvista.com>
--
[-- Attachment #2: m41t00.patch --]
[-- Type: text/plain, Size: 6753 bytes --]
diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
--- a/drivers/i2c/chips/Kconfig 2005-01-31 10:33:34 -07:00
+++ b/drivers/i2c/chips/Kconfig 2005-01-31 10:33:34 -07:00
@@ -371,4 +371,13 @@
This driver can also be built as a module. If so, the module
will be called isp1301_omap.
+config SENSORS_M41T00
+ tristate "ST M41T00 RTC chip"
+ depends on I2C && PPC32
+ help
+ If you say yes here you get support for the ST M41T00 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-m41t00.
+
endmenu
diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
--- a/drivers/i2c/chips/Makefile 2005-01-31 10:33:34 -07:00
+++ b/drivers/i2c/chips/Makefile 2005-01-31 10:33:34 -07:00
@@ -26,6 +26,7 @@
obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
+obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff -Nru a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/i2c/chips/m41t00.c 2005-01-31 10:33:34 -07:00
@@ -0,0 +1,224 @@
+/*
+ * drivers/i2c/chips/m41t00.c
+ *
+ * I2C client/driver for the ST M41T00 Real-Time Clock chip.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+/*
+ * This i2c client/driver wedges between the drivers/char/genrtc.c RTC
+ * interface and the SMBus interface of the i2c subsystem.
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/time.h>
+#include <asm/rtc.h>
+
+static DECLARE_MUTEX(m41t00_mutex);
+
+struct m41t00_data {
+ struct i2c_client client;
+};
+
+static struct i2c_driver m41t00_driver;
+static struct i2c_client *save_client;
+
+static unsigned short ignore[] = { I2C_CLIENT_END };
+static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_addr,
+ .normal_i2c_range = ignore,
+ .probe = ignore,
+ .probe_range = ignore,
+ .ignore = ignore,
+ .ignore_range = ignore,
+ .force = ignore,
+};
+
+ulong
+m41t00_get_rtc_time(void)
+{
+ s32 sec, min, hour, day, mon, year;
+
+ down(&m41t00_mutex);
+ if (((sec = i2c_smbus_read_byte_data(save_client, 0)) < 0)
+ || ((min = i2c_smbus_read_byte_data(save_client, 1)) < 0)
+ || ((hour = i2c_smbus_read_byte_data(save_client, 2)) < 0)
+ || ((day = i2c_smbus_read_byte_data(save_client, 4)) < 0)
+ || ((mon = i2c_smbus_read_byte_data(save_client, 5)) < 0)
+ || ((year = i2c_smbus_read_byte_data(save_client, 6)) < 0)) {
+
+ dev_warn(&save_client->dev, "m41t00: can't read rtc chip\n");
+ sec = min = hour = day = mon = year = 0;
+ }
+ up(&m41t00_mutex);
+
+ sec &= 0x7f;
+ min &= 0x7f;
+ hour &= 0x3f;
+ day &= 0x3f;
+ mon &= 0x1f;
+ year &= 0xff;
+
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+
+ year += 1900;
+ if (year < 1970)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+static void
+m41t00_set_tlet(ulong arg)
+{
+ struct rtc_time tm;
+ ulong nowtime = *(ulong *)arg;
+
+ to_tm(nowtime, &tm);
+ tm.tm_year = (tm.tm_year - 1900) % 100;
+
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_mon);
+ BIN_TO_BCD(tm.tm_mday);
+ BIN_TO_BCD(tm.tm_year);
+
+ down(&m41t00_mutex);
+ if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
+ || (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x7f)
+ < 0)
+ || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0x7f)
+ < 0))
+
+ dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
+
+ up(&m41t00_mutex);
+ return;
+}
+
+ulong new_time;
+
+DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time);
+
+int
+m41t00_set_rtc_time(ulong nowtime)
+{
+ new_time = nowtime;
+
+ if (in_interrupt())
+ tasklet_schedule(&m41t00_tasklet);
+ else
+ m41t00_set_tlet((ulong)&new_time);
+
+ return 0;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Driver Interface
+ *
+ *****************************************************************************
+ */
+static int __devinit
+m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct i2c_client *new_client;
+ struct m41t00_data *drv_data;
+ int rc;
+
+ drv_data = kmalloc(sizeof(struct m41t00_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ memset(drv_data, 0, sizeof(struct m41t00_data));
+ new_client = &drv_data->client;
+
+ strncpy(new_client->name, "m41t00", I2C_NAME_SIZE);
+ i2c_set_clientdata(new_client, drv_data);
+ new_client->id = m41t00_driver.id;
+ new_client->flags = I2C_DF_NOTIFY;
+ new_client->addr = addr;
+ new_client->adapter = adap;
+ new_client->driver = &m41t00_driver;
+
+ if ((rc = i2c_attach_client(new_client)) != 0) {
+ kfree(drv_data);
+ return rc;
+ }
+
+ save_client = new_client;
+ return 0;
+}
+
+static int __devinit
+m41t00_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, m41t00_probe);
+}
+
+static int __devexit
+m41t00_detach(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(i2c_get_clientdata(client));
+ tasklet_kill(&m41t00_tasklet);
+ return 0;
+}
+
+static struct i2c_driver m41t00_driver = {
+ .owner = THIS_MODULE,
+ .name = "M41T00",
+ .id = I2C_DRIVERID_STM41T00,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = m41t00_attach,
+ .detach_client = m41t00_detach,
+};
+
+static int __devinit
+m41t00_init(void)
+{
+ return i2c_add_driver(&m41t00_driver);
+}
+
+static void __devexit
+m41t00_exit(void)
+{
+ i2c_del_driver(&m41t00_driver);
+ return;
+}
+
+module_init(m41t00_init);
+module_exit(m41t00_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
+MODULE_LICENSE("GPL");
next reply other threads:[~2005-05-19 6:25 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-01-31 18:05 Mark A. Greer [this message]
2005-05-19 6:25 ` [PATCH][I2C] ST M41T00 I2C RTC chip driver Mark A. Greer
2005-01-31 18:43 ` linux-os
2005-05-19 6:25 ` linux-os
2005-01-31 20:51 ` Mark A. Greer
2005-05-19 6:25 ` Mark A. Greer
2005-01-31 20:03 ` Jean Delvare
2005-05-19 6:25 ` Jean Delvare
2005-02-01 20:43 ` Mark A. Greer
2005-05-19 6:25 ` Mark A. Greer
2005-02-04 23:14 ` Mark A. Greer
2005-05-19 6:25 ` Mark A. Greer
2005-02-04 23:25 ` Greg KH
2005-05-19 6:25 ` Greg KH
2005-02-05 0:04 ` Mark A. Greer
2005-05-19 6:25 ` Mark A. Greer
2005-02-17 22:26 ` Greg KH
2005-05-19 6:25 ` Greg KH
2005-05-19 6:25 ` Mark Studebaker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=41FE7368.1000307@mvista.com \
--to=mgreer@mvista.com \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=sensors@stimpy.netroedge.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.