lkml.org 
[lkml]   [2005]   [Nov]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 1/1] Added support of ST m41t85 rtc chip
    Hello Jean,

    Possible too late to include in 2.6.15,
    but better later then never :).

    Comments?

    --
    Regards
    Andrey Volkov

    P.S. Please CC to me directly, I'm not in lm-sensors@lm-sensors.org list.
    Added support of ST M41T85 RTC

    Signed-off-By: Andrey Volkov <avolkov@varma-el.com>
    ---

    drivers/i2c/chips/Kconfig | 88 ++++++++++++++
    drivers/i2c/chips/Makefile | 1
    drivers/i2c/chips/m41t85.c | 285 ++++++++++++++++++++++++++++++++++++++++++++
    include/linux/i2c-id.h | 1
    4 files changed, 375 insertions(+), 0 deletions(-)

    diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
    index f9fae28..6593fc5 100644
    --- a/drivers/i2c/chips/Kconfig
    +++ b/drivers/i2c/chips/Kconfig
    @@ -111,6 +111,94 @@ config SENSORS_M41T00
    This driver can also be built as a module. If so, the module
    will be called m41t00.

    +config SENSORS_M41T85
    + tristate "ST M41T85 RTC chip"
    + depends on I2C
    + help
    + If you say yes here you get support for the ST M41T85 RTC chip.
    +
    + This driver can also be built as a module. If so, the module
    + will be called m41t85.
    +
    +config SENSORS_M41T85_SQW_FRQ_ENABLE
    + depends on SENSORS_M41T85
    + bool "Square Wave Output"
    + help
    + If you say yes here, then m41t85 will generate clocks on it
    + SQW pin at frequency which you are select below.
    +
    +choice
    + prompt "SQW frequincy"
    + depends on SENSORS_M41T85_SQW_FRQ_ENABLE
    + default SENSORS_M41T85_SQW_FRQ_32KHZ
    +
    +config SENSORS_M41T85_SQW_FRQ_32KHZ
    + bool "32.768 kHz"
    +
    +config SENSORS_M41T85_SQW_FRQ_8KHZ
    + bool "8 kHz"
    +
    +config SENSORS_M41T85_SQW_FRQ_4KHZ
    + bool "4 kHz"
    +
    +config SENSORS_M41T85_SQW_FRQ_2KHZ
    + bool "2 kHz"
    +
    +config SENSORS_M41T85_SQW_FRQ_1KHZ
    + bool "1 kHz"
    +
    +config SENSORS_M41T85_SQW_FRQ_512HZ
    + bool "512 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_256HZ
    + bool "256 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_128HZ
    + bool "128 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_64HZ
    + bool "64 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_32HZ
    + bool "32 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_16HZ
    + bool "16 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_8HZ
    + bool "8 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_4HZ
    + bool "4 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_2HZ
    + bool "2 Hz"
    +
    +config SENSORS_M41T85_SQW_FRQ_1HZ
    + bool "1 Hz"
    +
    +endchoice
    +
    +config SENSORS_M41T85_SQW_FRQ
    + int
    + depends on SENSORS_M41T85_SQW_FRQ_ENABLE
    + default "1" if SENSORS_M41T85_SQW_FRQ_32KHZ
    + default "2" if SENSORS_M41T85_SQW_FRQ_8KHZ
    + default "3" if SENSORS_M41T85_SQW_FRQ_4KHZ
    + default "4" if SENSORS_M41T85_SQW_FRQ_2KHZ
    + default "5" if SENSORS_M41T85_SQW_FRQ_1KHZ
    + default "6" if SENSORS_M41T85_SQW_FRQ_512HZ
    + default "7" if SENSORS_M41T85_SQW_FRQ_256HZ
    + default "8" if SENSORS_M41T85_SQW_FRQ_128HZ
    + default "9" if SENSORS_M41T85_SQW_FRQ_64HZ
    + default "10" if SENSORS_M41T85_SQW_FRQ_32HZ
    + default "11" if SENSORS_M41T85_SQW_FRQ_16HZ
    + default "12" if SENSORS_M41T85_SQW_FRQ_8HZ
    + default "13" if SENSORS_M41T85_SQW_FRQ_4HZ
    + default "14" if SENSORS_M41T85_SQW_FRQ_2HZ
    + default "15" if SENSORS_M41T85_SQW_FRQ_1HZ
    +
    +
    config SENSORS_MAX6875
    tristate "Maxim MAX6875 Power supply supervisor"
    depends on I2C && EXPERIMENTAL
    diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
    index 46178b5..5637881 100644
    --- a/drivers/i2c/chips/Makefile
    +++ b/drivers/i2c/chips/Makefile
    @@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_RTC8564) += rtc8564
    obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
    obj-$(CONFIG_TPS65010) += tps65010.o
    obj-$(CONFIG_RTC_X1205_I2C) += x1205.o
    +obj-$(CONFIG_SENSORS_M41T85) += m41t85.o

    ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
    EXTRA_CFLAGS += -DDEBUG
    diff --git a/drivers/i2c/chips/m41t85.c b/drivers/i2c/chips/m41t85.c
    new file mode 100644
    index 0000000..f26a115
    --- /dev/null
    +++ b/drivers/i2c/chips/m41t85.c
    @@ -0,0 +1,285 @@
    +/*
    + * drivers/i2c/chips/m41t85.c
    + *
    + * DESCRIPTION:
    + * I2C client/driver for the ST M41T85 Real-Time Clock chip.
    + *
    + * AUTHOR:
    + * Andrey Volkov <avolkov@varma-el.com>
    + *
    + * COPYRIGHT:
    + * 2005, Varma Electronics Oy
    + *
    + * Based on driver for ST M41T00 by Mark A. Greer <mgreer@mvista.com>
    + * (see m41t00.c for copyright)
    + *
    + * LICENCE:
    + * This file is subject to the terms and conditions of the GNU General Public
    + * License version 2. See the file COPYING in the main directory of this archive
    + * for more details.
    + *
    + * HISTORY:
    + * 2005-10-12 Created
    + */
    +
    +/*
    + * 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/config.h>
    +#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>
    +
    +#define M41T85_DRV_NAME "m41t85"
    +
    +#define RTC_CSEC_ADDR 0x00
    +#define RTC_SEC_ADDR 0x01
    +#define RTC_MIN_ADDR 0x02
    +#define RTC_HOUR_ADDR 0x03
    +#define RTC_WDAY_ADDR 0x04
    +#define RTC_DAY_ADDR 0x05
    +#define RTC_MONTH_ADDR 0x06
    +#define RTC_YEARS_ADDR 0x07
    +#define RTC_CONTROL_ADDR 0x08
    +#define RTC_WATCHDOG_ADDR 0x09
    +#define RTC_ALARM_MONTH_ADDR 0x0a
    +#define RTC_ALARM_DATE_ADDR 0x0b
    +#define RTC_ALARM_HOUR_ADDR 0x0c
    +#define RTC_ALARM_MIN_ADDR 0x0d
    +#define RTC_ALARM_SEC_ADDR 0x0e
    +#define RTC_WDF_ADDR 0x0f
    +#define RTC_SQW_ADDR 0x13
    +
    +
    +static DECLARE_MUTEX(m41t85_mutex);
    +
    +static struct i2c_driver m41t85_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,
    + .probe = ignore,
    + .ignore = ignore,
    +};
    +
    +ulong
    +m41t85_get_rtc_time(void)
    +{
    + s32 sec, min, hour, day, mon, year;
    + s32 sec1, min1, hour1, day1, mon1, year1;
    + ulong limit = 10;
    +
    + sec = min = hour = day = mon = year = 0;
    + sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
    +
    + down(&m41t85_mutex);
    + do {
    +
    + if (((sec = i2c_smbus_read_byte_data(save_client, RTC_SEC_ADDR)) >= 0)
    + && ((min = i2c_smbus_read_byte_data(save_client, RTC_MIN_ADDR))
    + >= 0)
    + && ((hour = i2c_smbus_read_byte_data(save_client, RTC_HOUR_ADDR))
    + >= 0)
    + && ((day = i2c_smbus_read_byte_data(save_client, RTC_DAY_ADDR))
    + >= 0)
    + && ((mon = i2c_smbus_read_byte_data(save_client, RTC_MONTH_ADDR))
    + >= 0)
    + && ((year = i2c_smbus_read_byte_data(save_client, RTC_YEARS_ADDR))
    + >= 0)
    + && ((sec == sec1) && (min == min1) && (hour == hour1)
    + && (day == day1) && (mon == mon1)
    + && (year == year1)))
    +
    + break;
    +
    + sec1 = sec;
    + min1 = min;
    + hour1 = hour;
    + day1 = day;
    + mon1 = mon;
    + year1 = year;
    + } while (--limit > 0);
    + up(&m41t85_mutex);
    +
    + if (limit == 0) {
    + dev_warn(&save_client->dev,
    + "m41t85: can't read rtc chip\n");
    + sec = min = hour = day = mon = year = 0;
    + }
    +
    + sec = BCD2BIN(sec & 0x7f);
    + min = BCD2BIN(min & 0x7f);
    + hour = BCD2BIN(hour & 0x3f);
    + day = BCD2BIN(day & 0x3f);
    + mon = BCD2BIN(mon & 0x1f);
    + year = BCD2BIN(year & 0xff);
    +
    + year += 1900;
    + if (year < 1970)
    + year += 100;
    +
    + return mktime(year, mon, day, hour, min, sec);
    +}
    +
    +static void
    +m41t85_set_tlet(ulong arg)
    +{
    + struct rtc_time tm;
    + ulong nowtime = *(ulong *)arg;
    +
    + to_tm(nowtime, &tm);
    + tm.tm_year = (tm.tm_year - 1900) % 100;
    +
    + tm.tm_sec = BIN2BCD(tm.tm_sec);
    + tm.tm_min = BIN2BCD(tm.tm_min);
    + tm.tm_hour = BIN2BCD(tm.tm_hour);
    + tm.tm_mon = BIN2BCD(tm.tm_mon);
    + tm.tm_mday = BIN2BCD(tm.tm_mday);
    + tm.tm_year = BIN2BCD(tm.tm_year);
    +
    + down(&m41t85_mutex);
    + if ((i2c_smbus_write_byte_data(save_client, RTC_SEC_ADDR, tm.tm_sec & 0x7f) < 0)
    + || (i2c_smbus_write_byte_data(save_client, RTC_MIN_ADDR, tm.tm_min & 0x7f)
    + < 0)
    + || (i2c_smbus_write_byte_data(save_client, RTC_HOUR_ADDR, tm.tm_hour & 0x7f)
    + < 0)
    + || (i2c_smbus_write_byte_data(save_client, RTC_DAY_ADDR, tm.tm_mday & 0x7f)
    + < 0)
    + || (i2c_smbus_write_byte_data(save_client, RTC_MONTH_ADDR, tm.tm_mon & 0x7f)
    + < 0)
    + || (i2c_smbus_write_byte_data(save_client, RTC_YEARS_ADDR, tm.tm_year & 0x7f)
    + < 0))
    +
    + dev_warn(&save_client->dev,"m41t85: can't write to rtc chip\n");
    +
    + up(&m41t85_mutex);
    + return;
    +}
    +
    +static ulong new_time;
    +
    +DECLARE_TASKLET_DISABLED(m41t85_tasklet, m41t85_set_tlet, (ulong)&new_time);
    +
    +int
    +m41t85_set_rtc_time(ulong nowtime)
    +{
    + new_time = nowtime;
    +
    + if (in_interrupt())
    + tasklet_schedule(&m41t85_tasklet);
    + else
    + m41t85_set_tlet((ulong)&new_time);
    +
    + return 0;
    +}
    +
    +/*
    + *****************************************************************************
    + *
    + * Driver Interface
    + *
    + *****************************************************************************
    + */
    +static int
    +m41t85_probe(struct i2c_adapter *adap, int addr, int kind)
    +{
    + struct i2c_client *client;
    + int ret;
    + s32 reg;
    +
    + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
    + if (!client)
    + return -ENOMEM;
    +
    + strncpy(client->name, M41T85_DRV_NAME, I2C_NAME_SIZE);
    + client->addr = addr;
    + client->adapter = adap;
    + client->driver = &m41t85_driver;
    +
    + if ((ret = i2c_attach_client(client)) != 0) {
    + kfree(client);
    + return ret;
    + }
    +
    + #if defined (CONFIG_SENSORS_M41T85_SQW_FRQ)
    + reg = i2c_smbus_read_byte_data(client, RTC_ALARM_MONTH_ADDR);
    + if (reg >=0 && (reg & 0x40) == 0) {
    + ret = i2c_smbus_write_byte_data(client, RTC_SQW_ADDR, CONFIG_SENSORS_M41T85_SQW_FRQ);
    + if(ret == 0)
    + i2c_smbus_write_byte_data(client, RTC_ALARM_MONTH_ADDR, reg | 0x40);
    + }
    + #endif
    + /* Check if ST flag set, and if yes, then clear it */
    + reg = i2c_smbus_read_byte_data(client, RTC_SEC_ADDR);
    + if (reg >=0 && (reg & 0x80) != 0)
    + i2c_smbus_write_byte_data(client, RTC_SEC_ADDR, reg & ~0x80);
    +
    + /* Check if HT flag set, and if yes, then clear it */
    + reg = i2c_smbus_read_byte_data(client, RTC_ALARM_HOUR_ADDR);
    + if (reg >=0 && (reg & 0x40) != 0)
    + i2c_smbus_write_byte_data(client, RTC_ALARM_HOUR_ADDR, reg & ~0x40);
    +
    + save_client = client;
    + return 0;
    +}
    +
    +static int
    +m41t85_attach(struct i2c_adapter *adap)
    +{
    + return i2c_probe(adap, &addr_data, m41t85_probe);
    +}
    +
    +static int
    +m41t85_detach(struct i2c_client *client)
    +{
    + int rc;
    +
    + if ((rc = i2c_detach_client(client)) == 0) {
    + kfree(client);
    + tasklet_kill(&m41t85_tasklet);
    + }
    + return rc;
    +}
    +
    +static struct i2c_driver m41t85_driver = {
    + .owner = THIS_MODULE,
    + .name = M41T85_DRV_NAME,
    + .id = I2C_DRIVERID_STM41T85,
    + .flags = I2C_DF_NOTIFY,
    + .attach_adapter = m41t85_attach,
    + .detach_client = m41t85_detach,
    +};
    +
    +static int __init
    +m41t85_init(void)
    +{
    + return i2c_add_driver(&m41t85_driver);
    +}
    +
    +static void __exit
    +m41t85_exit(void)
    +{
    + i2c_del_driver(&m41t85_driver);
    + return;
    +}
    +
    +module_init(m41t85_init);
    +module_exit(m41t85_exit);
    +
    +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
    +MODULE_DESCRIPTION("ST Microelectronics M41T85 RTC I2C Client Driver");
    +MODULE_LICENSE("GPL v2");
    diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
    index 5c05f52..ac8ffcd 100644
    --- a/include/linux/i2c-id.h
    +++ b/include/linux/i2c-id.h
    @@ -108,6 +108,7 @@
    #define I2C_DRIVERID_SAA7127 72 /* saa7124 video encoder */
    #define I2C_DRIVERID_SAA711X 73 /* saa711x video encoders */
    #define I2C_DRIVERID_AKITAIOEXP 74 /* IO Expander on Sharp SL-C1000 */
    +#define I2C_DRIVERID_STM41T85 75 /* real time clock */

    #define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */
    #define I2C_DRIVERID_EXP1 0xF1
    \
     
     \ /
      Last update: 2005-11-14 14:56    [W:0.051 / U:92.208 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site