From patchwork Sat Dec 11 10:34:13 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Artem Kukhta X-Patchwork-Id: 75206 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-ww0-f56.google.com (mail-ww0-f56.google.com [74.125.82.56]) by ozlabs.org (Postfix) with ESMTP id D1803B7080 for ; Sun, 12 Dec 2010 07:00:57 +1100 (EST) Received: by wwi14 with SMTP id 14sf3543089wwi.11 for ; Sat, 11 Dec 2010 12:00:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:received:x-beenthere:received:received:received :received:received-spf:received:mime-version:received:received:date :message-id:subject:from:to:cc:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:list-post:list-help:list-archive:sender:list-subscribe :list-unsubscribe:content-type; bh=JOR1bgeZ4QJBcwJG14AkFOWqmRvIuWnLqoO7Q4iMB4I=; b=3qnSrGSXbFtqoUm5LIjku9gEFJpqObz86r4xMEXv8LYYybS9w/Q+YlZTOkkhriPGdf hQ+fOsAF2DbN0DMn8PCkCU6FmzsYcWKT5aTvQYfwsTT666PA7XA66syfdT+ScDUinZrY rFUFOaBHzfsK+WblvefpwJsfK7ZUq/H94in1s= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-beenthere:received-spf:mime-version:date:message-id:subject:from :to:cc:x-original-sender:x-original-authentication-results:reply-to :precedence:mailing-list:list-id:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe:content-type; b=zq5AzJfe7sNDZ5lNH8BxSaJ6zMyP0n/dMUi1jcerDz3m9ej37qJ6jQ5HeOPQzXwj8H w0hEt6BivfJF2N0TD9HCqrgcBU1W7dSXk+9n6FsHylCbyNJYDaq9iqeVoXcY3MB32dwg z4ZJe89DL68WTn9bAqvID3THMkslldVL3PW3Y= Received: by 10.216.10.65 with SMTP id 43mr97460weu.22.1292097654433; Sat, 11 Dec 2010 12:00:54 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.216.208.4 with SMTP id p4ls1764344weo.2.p; Sat, 11 Dec 2010 12:00:53 -0800 (PST) Received: by 10.216.178.13 with SMTP id e13mr178014wem.11.1292097653637; Sat, 11 Dec 2010 12:00:53 -0800 (PST) Received: by 10.216.178.13 with SMTP id e13mr178013wem.11.1292097653598; Sat, 11 Dec 2010 12:00:53 -0800 (PST) Received: from mail-wy0-f179.google.com (mail-wy0-f179.google.com [74.125.82.179]) by gmr-mx.google.com with ESMTP id p4si360674weq.4.2010.12.11.12.00.52; Sat, 11 Dec 2010 12:00:52 -0800 (PST) Received-SPF: pass (google.com: domain of artem.kukhta@gmail.com designates 74.125.82.179 as permitted sender) client-ip=74.125.82.179; Received: by mail-wy0-f179.google.com with SMTP id 11so4710342wyi.10 for ; Sat, 11 Dec 2010 12:00:52 -0800 (PST) MIME-Version: 1.0 Received: by 10.216.159.69 with SMTP id r47mr510748wek.105.1292063653384; Sat, 11 Dec 2010 02:34:13 -0800 (PST) Received: by 10.216.169.211 with HTTP; Sat, 11 Dec 2010 02:34:13 -0800 (PST) Date: Sat, 11 Dec 2010 12:34:13 +0200 Message-ID: Subject: [rtc-linux] rtc: Add support for ST M41ST85W RTC chip From: Artem Kukhta To: Alessandro Zummo Cc: linux-kernel@vger.kernel.org, rtc-linux@googlegroups.com X-Original-Sender: artem.kukhta@gmail.com X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of artem.kukhta@gmail.com designates 74.125.82.179 as permitted sender) smtp.mail=artem.kukhta@gmail.com; dkim=pass (test mode) header.i=@gmail.com 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: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , rtc: Add support for ST M41ST85W RTC chip This driver based on a driver from xscaleiop project at sourceforge.net. The existing driver was based on Linux kernel 2.6.24, and used old-style I2C calls. The code was ported and refactored a bit. Signed-off-by: Artem Kukhta --- Hello, This driver was ported from Linux kernel 2.6.24 to 2.6.36. I've tested it on a real device, Sabio (IOP321 on IQ31244 platform), and it seems to work. Only basic RTC functions implemented (no RTC alarms yet, sorry). Also, I have a question: what should I put in MODULE_AUTHOR() macros (and others)? Thanks. --- diff -uNr a/arch/arm/mach-iop32x/iq31244.c b/arch/arm/mach-iop32x/iq31244.c --- a/arch/arm/mach-iop32x/iq31244.c 2010-10-20 23:30:22.000000000 +0300 +++ b/arch/arm/mach-iop32x/iq31244.c 2010-11-04 12:35:48.000000000 +0200 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -287,6 +288,12 @@ ; } +static struct i2c_board_info __initdata m41st85w_i2c_device[] = { + { + I2C_BOARD_INFO("m41st85w", 0x68), + }, +}; + static void __init iq31244_init_machine(void) { platform_device_register(&iop3xx_i2c0_device); @@ -296,6 +303,9 @@ platform_device_register(&iop3xx_dma_0_channel); platform_device_register(&iop3xx_dma_1_channel); + i2c_register_board_info(0, m41st85w_i2c_device, + ARRAY_SIZE(m41st85w_i2c_device)); + if (is_ep80219()) pm_power_off = ep80219_power_off; diff -uNr a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig --- a/drivers/rtc/Kconfig 2010-10-20 23:30:22.000000000 +0300 +++ b/drivers/rtc/Kconfig 2010-11-15 22:01:07.000000000 +0200 @@ -271,6 +271,14 @@ 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_M41ST85W + tristate "ST M41ST85W" + help + If you say yes here you get support for ST M41ST85W RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41st85w. + config RTC_DRV_BQ32K tristate "TI BQ32000" help @@ -952,4 +960,5 @@ This driver can also be buillt as a module. If so, the module will be called rtc-jz4740. + endif # RTC_CLASS diff -uNr a/drivers/rtc/Makefile b/drivers/rtc/Makefile --- a/drivers/rtc/Makefile 2010-10-20 23:30:22.000000000 +0300 +++ b/drivers/rtc/Makefile 2010-11-15 21:47:21.000000000 +0200 @@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o +obj-$(CONFIG_RTC_DRV_M41ST85W) += rtc-m41st85w.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o diff -uNr a/drivers/rtc/rtc-m41st85w.c b/drivers/rtc/rtc-m41st85w.c --- a/drivers/rtc/rtc-m41st85w.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/rtc/rtc-m41st85w.c 2010-12-11 11:32:09.000000000 +0200 @@ -0,0 +1,270 @@ +/* + * rtc-m41st85w.c + * + * Device driver for ST's Real Time Controller M41ST85W. + * Copyright (C) 2002 Intrinsyc Software Inc. + * Copyright (C) 2003 Intel Corp. + * Copyright (C) 2010 Artem Kukhta + * + * 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. + * + * This driver is adapted from the DS1307 driver originally + * Kernel time sync borrowed from RMK's acorn RTC driver + * Ported to the latest kernel from the M41ST85W driver iop-2.6.24 patches + */ + +#include +#include +#include +#include +#include +#include + +#define M41ST85W_REG_SECONDS 0x01 +#define M41ST85W_REG_MINUTES 0x02 +#define M41ST85W_REG_HOURS 0x03 +#define M41ST85W_REG_DAY 0x04 +#define M41ST85W_REG_DATE 0x05 +#define M41ST85W_REG_MONTH 0x06 +#define M41ST85W_REG_YEAR 0x07 +#define M41ST85W_REG_ALMONTH 0x0A /* Alarm month */ +#define M41ST85W_REG_ALHOUR 0x0C /* Alarm month */ +#define M41ST85W_REG_SQW 0x13 + +#define SQW_ENABLE 0x40 /* Square Wave Enable */ +#define SQW_DISABLE 0x00 /* Square Wave disable */ + +#define RATE_32768HZ 0x10 /* Rate Select 32.768KHz */ +#define RATE_8192HZ 0x20 /* Rate Select 8.192KHz */ +#define RATE_4096HZ 0x30 /* Rate Select 4.096KHz */ +#define RATE_2048HZ 0x40 /* Rate Select 2.048Khz */ +#define RATE_1024HZ 0x50 /* Rate Select 1.024Khz */ +#define RATE_512HZ 0x60 /* Rate Select 0.512Khz */ +#define RATE_256HZ 0x70 /* Rate Select 0.256Khz */ +#define RATE_128HZ 0x80 /* Rate Select 0.128Khz */ +#define RATE_64HZ 0x90 /* Rate Select 0.064Khz */ +#define RATE_32HZ 0xa0 /* Rate Select 0.032Khz */ +#define RATE_16HZ 0xb0 /* Rate Select 0.016Khz */ +#define RATE_8HZ 0xc0 /* Rate Select 0.008Khz */ +#define RATE_4HZ 0xd0 /* Rate Select 0.004Khz */ +#define RATE_2HZ 0xe0 /* Rate Select 0.0001Khz */ +#define RATE_1HZ 0xf0 /* Rate Select 1Hz */ +#define RATE_0HZ 0x00 /* Rate Select None */ + +#define CLOCK_STOP 0x80 /* Clock Stop */ +#define CLOCK_HALT 0x40 /* Clock Halt */ + + + +struct m41st85w { + struct i2c_client *client; + struct rtc_device *rtc; +}; + +static struct i2c_driver m41st85w_driver; + +static int m41st85w_set_ctrl(struct i2c_client *client, int *cinfo) +{ + int ret; + + /* need to set square wave first */ + ret = i2c_smbus_write_byte_data(client, M41ST85W_REG_SQW, cinfo[1]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, M41ST85W_REG_ALMONTH, cinfo[0]); +} + +static void m41st85w_enable_clock(struct i2c_client *client, int enable) +{ + int buf1, buf2; + int ctrl_info[2]; + int ret; + + if (enable) { + ctrl_info[0] = SQW_ENABLE; + ctrl_info[1] = RATE_32768HZ; + } else { + ctrl_info[0] = SQW_DISABLE; + ctrl_info[1] = RATE_0HZ; + } + ret = m41st85w_set_ctrl(client, ctrl_info); + if (ret < 0) + printk(KERN_ERR "%s %d: m41st85w_set_ctrl failed\n", + __func__, __LINE__); + + /* read Clock-Halt bit and second counter */ + buf1 = i2c_smbus_read_byte_data(client, M41ST85W_REG_ALHOUR); + if (buf1 < 0) + printk(KERN_ERR "%s %d: i2c_smbus_read_byte_data failed\n", + __func__, __LINE__); + + buf2 = i2c_smbus_read_byte_data(client, M41ST85W_REG_SECONDS); + if (buf2 < 0) + printk(KERN_ERR "%s %d: i2c_smbus_read_byte_data failed\n", + __func__, __LINE__); + + + if (enable) { + buf1 &= ~CLOCK_HALT; /* clear Clock-Halt bit */ + buf2 &= ~CLOCK_STOP; + } else { + buf1 |= CLOCK_HALT; /* set Clock-Halt bit */ + buf2 |= CLOCK_STOP; + } + + ret = i2c_smbus_write_byte_data(client, M41ST85W_REG_ALHOUR, buf1); + if (ret < 0) + printk(KERN_ERR "%s %d: i2c_smbus_write_byte_data failed\n", + __func__, __LINE__); + + ret = i2c_smbus_write_byte_data(client, M41ST85W_REG_SECONDS, buf2); + if (ret < 0) + printk(KERN_ERR "%s %d: i2c_smbus_write_byte_data failed\n", + __func__, __LINE__); +} + +static int m41st85w_read_time(struct device *dev, struct rtc_time *dt) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[7]; + int ret; + unsigned int year, month, day, week, hour, minute, second; + + ret = i2c_smbus_read_i2c_block_data(client, M41ST85W_REG_SECONDS, 7, buf); + + if (ret < 0) + return ret; + if (ret < 7) + return -EIO; + + second = buf[0]; + minute = buf[1]; + hour = buf[2]; + week = buf[3]; + day = buf[4]; + month = buf[5]; + year = buf[6]; + + dt->tm_sec = bcd2bin(second & 0x7f); + dt->tm_min = bcd2bin(minute & 0x7f); + dt->tm_hour = bcd2bin(hour & 0x3f); + dt->tm_wday = bcd2bin(week & 0x07); + dt->tm_mday = bcd2bin(day & 0x7f); + dt->tm_mon = bcd2bin(month & 0x3f); + dt->tm_year = bcd2bin(year); + + /* fix up values */ + /* dt->tm_mon is zero-based */ + dt->tm_mon--; + /* year is 1900 + dt->tm_year */ + if (dt->tm_year < 95) + dt->tm_year += 100; + + dt->tm_isdst = 0; + + return rtc_valid_tm(dt); +} + +static int + m41st85w_set_time(struct device *dev, struct rtc_time *dt) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[7]; + + buf[0] = bin2bcd(dt->tm_sec); + buf[1] = bin2bcd(dt->tm_min); + buf[2] = bin2bcd(dt->tm_hour); + buf[3] = bin2bcd(dt->tm_wday); + buf[4] = bin2bcd(dt->tm_mday); + buf[5] = bin2bcd(dt->tm_mon + 1); + /* The year only ranges from 0-99, we are being passed an offset + * from 1900, and the chip calulates leap years based on 2000, + * thus we adjust by 100. + */ + + buf[6] = bin2bcd(dt->tm_year - 100); + + return i2c_smbus_write_i2c_block_data(client, + M41ST85W_REG_SECONDS, 7, buf); +} + +static struct rtc_class_ops m41st85w_rtc_ops = { + .read_time = m41st85w_read_time, + .set_time = m41st85w_set_time, +}; + +static int __devinit m41st85w_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct m41st85w *m41st85w; + int ret; + + m41st85w = kzalloc(sizeof(struct m41st85w), GFP_KERNEL); + if (!m41st85w) + return -ENOMEM; + + m41st85w->client = client; + i2c_set_clientdata(client, m41st85w); + + m41st85w_enable_clock(client, 1); + + m41st85w->rtc = rtc_device_register(client->name, &client->dev, + &m41st85w_rtc_ops, THIS_MODULE); + if (IS_ERR(m41st85w->rtc)) { + ret = PTR_ERR(m41st85w->rtc); + dev_err(&client->dev, "unable to register the class device\n"); + goto out_free; + } + + return 0; + +out_free: + kfree(m41st85w); + return ret; +} + +static int __devexit m41st85w_remove(struct i2c_client *client) +{ + struct m41st85w *m41st85w = i2c_get_clientdata(client); + + m41st85w_enable_clock(client, 0); + rtc_device_unregister(m41st85w->rtc); + kfree(m41st85w); + return 0; +} + +static struct i2c_device_id m41st85w_id[] = { + { "m41st85w", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, m41st85w_id); + +static struct i2c_driver m41st85w_driver = { + .driver = { + .name = "rtc-m41st85w", + .owner = THIS_MODULE, + }, + .probe = m41st85w_probe, + .remove = __devexit_p(m41st85w_remove), + .id_table = m41st85w_id, +}; + +static int __init m41st85w_init(void) +{ + return i2c_add_driver(&m41st85w_driver); +} + +static void __exit m41st85w_exit(void) +{ + i2c_del_driver(&m41st85w_driver); +} + +module_init(m41st85w_init); +module_exit(m41st85w_exit); + +MODULE_AUTHOR("Intel Corp."); +MODULE_DESCRIPTION("ST M41ST85W RTC Driver"); +MODULE_LICENSE("GPL");