X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fi2c%2Fchips%2Fm41t00.c;fp=drivers%2Fi2c%2Fchips%2Fm41t00.c;h=2836fb3eba83c100010f8c6aaf44a7ba400492f3;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=2dd0a34d9472e17496dff2692aec74cd5cd3ac61;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c index 2dd0a34d9..2836fb3eb 100644 --- a/drivers/i2c/chips/m41t00.c +++ b/drivers/i2c/chips/m41t00.c @@ -1,9 +1,11 @@ /* - * I2C client/driver for the ST M41T00 family of i2c rtc chips. + * drivers/i2c/chips/m41t00.c + * + * I2C client/driver for the ST M41T00 Real-Time Clock chip. * * Author: Mark A. Greer * - * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under + * 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. @@ -11,6 +13,9 @@ /* * 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 @@ -19,110 +24,54 @@ #include #include #include -#include -#include -#include + #include #include +#define M41T00_DRV_NAME "m41t00" + +static DECLARE_MUTEX(m41t00_mutex); + static struct i2c_driver m41t00_driver; static struct i2c_client *save_client; static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { I2C_CLIENT_END, 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, + .normal_i2c = normal_addr, + .probe = ignore, + .ignore = ignore, }; -struct m41t00_chip_info { - u8 type; - char *name; - u8 read_limit; - u8 sec; /* Offsets for chip regs */ - u8 min; - u8 hour; - u8 day; - u8 mon; - u8 year; - u8 alarm_mon; - u8 alarm_hour; - u8 sqw; - u8 sqw_freq; -}; - -static struct m41t00_chip_info m41t00_chip_info_tbl[] = { - { - .type = M41T00_TYPE_M41T00, - .name = "m41t00", - .read_limit = 5, - .sec = 0, - .min = 1, - .hour = 2, - .day = 4, - .mon = 5, - .year = 6, - }, - { - .type = M41T00_TYPE_M41T81, - .name = "m41t81", - .read_limit = 1, - .sec = 1, - .min = 2, - .hour = 3, - .day = 5, - .mon = 6, - .year = 7, - .alarm_mon = 0xa, - .alarm_hour = 0xc, - .sqw = 0x13, - }, - { - .type = M41T00_TYPE_M41T85, - .name = "m41t85", - .read_limit = 1, - .sec = 1, - .min = 2, - .hour = 3, - .day = 5, - .mon = 6, - .year = 7, - .alarm_mon = 0xa, - .alarm_hour = 0xc, - .sqw = 0x13, - }, -}; -static struct m41t00_chip_info *m41t00_chip; - ulong m41t00_get_rtc_time(void) { - s32 sec, min, hour, day, mon, year; - s32 sec1, min1, hour1, day1, mon1, year1; - u8 reads = 0; - u8 buf[8], msgbuf[1] = { 0 }; /* offset into rtc's regs */ - struct i2c_msg msgs[] = { - { - .addr = save_client->addr, - .flags = 0, - .len = 1, - .buf = msgbuf, - }, - { - .addr = save_client->addr, - .flags = I2C_M_RD, - .len = 8, - .buf = buf, - }, - }; + 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(&m41t00_mutex); do { - if (i2c_transfer(save_client->adapter, msgs, 2) < 0) - goto read_err; + 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) + && ((sec == sec1) && (min == min1) && (hour == hour1) + && (day == day1) && (mon == mon1) + && (year == year1))) + + break; sec1 = sec; min1 = min; @@ -130,93 +79,74 @@ m41t00_get_rtc_time(void) day1 = day; mon1 = mon; year1 = year; + } while (--limit > 0); + up(&m41t00_mutex); + + if (limit == 0) { + dev_warn(&save_client->dev, + "m41t00: can't read rtc chip\n"); + sec = min = hour = day = mon = year = 0; + } - sec = buf[m41t00_chip->sec] & 0x7f; - min = buf[m41t00_chip->min] & 0x7f; - hour = buf[m41t00_chip->hour] & 0x3f; - day = buf[m41t00_chip->day] & 0x3f; - mon = buf[m41t00_chip->mon] & 0x1f; - year = buf[m41t00_chip->year]; - } while ((++reads < m41t00_chip->read_limit) && ((sec != sec1) - || (min != min1) || (hour != hour1) || (day != day1) - || (mon != mon1) || (year != year1))); - - if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1) - || (hour != hour1) || (day != day1) || (mon != mon1) - || (year != year1))) - goto read_err; - - sec = BCD2BIN(sec); - min = BCD2BIN(min); - hour = BCD2BIN(hour); - day = BCD2BIN(day); - mon = BCD2BIN(mon); - year = BCD2BIN(year); + 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); - -read_err: - dev_err(&save_client->dev, "m41t00_get_rtc_time: Read error\n"); - return 0; } -EXPORT_SYMBOL_GPL(m41t00_get_rtc_time); static void -m41t00_set(void *arg) +m41t00_set_tlet(ulong arg) { struct rtc_time tm; - int nowtime = *(int *)arg; - s32 sec, min, hour, day, mon, year; - u8 wbuf[9], *buf = &wbuf[1], msgbuf[1] = { 0 }; - struct i2c_msg msgs[] = { - { - .addr = save_client->addr, - .flags = 0, - .len = 1, - .buf = msgbuf, - }, - { - .addr = save_client->addr, - .flags = I2C_M_RD, - .len = 8, - .buf = buf, - }, - }; + ulong nowtime = *(ulong *)arg; to_tm(nowtime, &tm); tm.tm_year = (tm.tm_year - 1900) % 100; - sec = BIN2BCD(tm.tm_sec); - min = BIN2BCD(tm.tm_min); - hour = BIN2BCD(tm.tm_hour); - day = BIN2BCD(tm.tm_mday); - mon = BIN2BCD(tm.tm_mon); - year = BIN2BCD(tm.tm_year); - - /* Read reg values into buf[0..7]/wbuf[1..8] */ - if (i2c_transfer(save_client->adapter, msgs, 2) < 0) { - dev_err(&save_client->dev, "m41t00_set: Read error\n"); - return; - } - - wbuf[0] = 0; /* offset into rtc's regs */ - buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f); - buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f); - buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f); - buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f); - buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f); - - if (i2c_master_send(save_client, wbuf, 9) < 0) - dev_err(&save_client->dev, "m41t00_set: Write error\n"); + 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 & 0x3f) + < 0) + || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x3f) + < 0) + || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x1f) + < 0) + || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0xff) + < 0)) + + dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n"); + + up(&m41t00_mutex); + return; } -static ulong new_time; -static struct workqueue_struct *m41t00_wq; -static DECLARE_WORK(m41t00_work, m41t00_set, &new_time); +static ulong new_time; + +DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time); int m41t00_set_rtc_time(ulong nowtime) @@ -224,54 +154,12 @@ m41t00_set_rtc_time(ulong nowtime) new_time = nowtime; if (in_interrupt()) - queue_work(m41t00_wq, &m41t00_work); + tasklet_schedule(&m41t00_tasklet); else - m41t00_set(&new_time); + m41t00_set_tlet((ulong)&new_time); return 0; } -EXPORT_SYMBOL_GPL(m41t00_set_rtc_time); - -/* - ***************************************************************************** - * - * platform_data Driver Interface - * - ***************************************************************************** - */ -static int __init -m41t00_platform_probe(struct platform_device *pdev) -{ - struct m41t00_platform_data *pdata; - int i; - - if (pdev && (pdata = pdev->dev.platform_data)) { - normal_addr[0] = pdata->i2c_addr; - - for (i=0; itype) { - m41t00_chip = &m41t00_chip_info_tbl[i]; - m41t00_chip->sqw_freq = pdata->sqw_freq; - return 0; - } - } - return -ENODEV; -} - -static int __exit -m41t00_platform_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver m41t00_platform_driver = { - .probe = m41t00_platform_probe, - .remove = m41t00_platform_remove, - .driver = { - .owner = THIS_MODULE, - .name = M41T00_DRV_NAME, - }, -}; /* ***************************************************************************** @@ -286,71 +174,22 @@ m41t00_probe(struct i2c_adapter *adap, int addr, int kind) struct i2c_client *client; int rc; - if (!i2c_check_functionality(adap, I2C_FUNC_I2C - | I2C_FUNC_SMBUS_BYTE_DATA)) - return 0; - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (!client) return -ENOMEM; - strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE); + strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); client->addr = addr; client->adapter = adap; client->driver = &m41t00_driver; - if ((rc = i2c_attach_client(client))) - goto attach_err; - - if (m41t00_chip->type != M41T00_TYPE_M41T00) { - /* If asked, disable SQW, set SQW frequency & re-enable */ - if (m41t00_chip->sqw_freq) - if (((rc = i2c_smbus_read_byte_data(client, - m41t00_chip->alarm_mon)) < 0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_mon, rc & ~0x40)) <0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->sqw, - m41t00_chip->sqw_freq)) < 0) - || ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_mon, rc | 0x40)) <0)) - goto sqw_err; - - /* Make sure HT (Halt Update) bit is cleared */ - if ((rc = i2c_smbus_read_byte_data(client, - m41t00_chip->alarm_hour)) < 0) - goto ht_err; - - if (rc & 0x40) - if ((rc = i2c_smbus_write_byte_data(client, - m41t00_chip->alarm_hour, rc & ~0x40))<0) - goto ht_err; + if ((rc = i2c_attach_client(client)) != 0) { + kfree(client); + return rc; } - /* Make sure ST (stop) bit is cleared */ - if ((rc = i2c_smbus_read_byte_data(client, m41t00_chip->sec)) < 0) - goto st_err; - - if (rc & 0x80) - if ((rc = i2c_smbus_write_byte_data(client, m41t00_chip->sec, - rc & ~0x80)) < 0) - goto st_err; - - m41t00_wq = create_singlethread_workqueue(m41t00_chip->name); save_client = client; return 0; - -st_err: - dev_err(&client->dev, "m41t00_probe: Can't clear ST bit\n"); - goto attach_err; -ht_err: - dev_err(&client->dev, "m41t00_probe: Can't clear HT bit\n"); - goto attach_err; -sqw_err: - dev_err(&client->dev, "m41t00_probe: Can't set SQW Frequency\n"); -attach_err: - kfree(client); - return rc; } static int @@ -362,11 +201,11 @@ m41t00_attach(struct i2c_adapter *adap) static int m41t00_detach(struct i2c_client *client) { - int rc; + int rc; if ((rc = i2c_detach_client(client)) == 0) { kfree(client); - destroy_workqueue(m41t00_wq); + tasklet_kill(&m41t00_tasklet); } return rc; } @@ -383,18 +222,14 @@ static struct i2c_driver m41t00_driver = { static int __init m41t00_init(void) { - int rc; - - if (!(rc = platform_driver_register(&m41t00_platform_driver))) - rc = i2c_add_driver(&m41t00_driver); - return rc; + return i2c_add_driver(&m41t00_driver); } static void __exit m41t00_exit(void) { i2c_del_driver(&m41t00_driver); - platform_driver_unregister(&m41t00_platform_driver); + return; } module_init(m41t00_init);