Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.13-rc2 260 lines 5.9 kB view raw
1/* 2 * drivers/i2c/chips/ds1374.c 3 * 4 * I2C client/driver for the Maxim/Dallas DS1374 Real-Time Clock 5 * 6 * Author: Randy Vinson <rvinson@mvista.com> 7 * 8 * Based on the m41t00.c by Mark Greer <mgreer@mvista.com> 9 * 10 * 2005 (c) MontaVista Software, Inc. This file is licensed under 11 * the terms of the GNU General Public License version 2. This program 12 * is licensed "as is" without any warranty of any kind, whether express 13 * or implied. 14 */ 15/* 16 * This i2c client/driver wedges between the drivers/char/genrtc.c RTC 17 * interface and the SMBus interface of the i2c subsystem. 18 * It would be more efficient to use i2c msgs/i2c_transfer directly but, as 19 * recommened in .../Documentation/i2c/writing-clients section 20 * "Sending and receiving", using SMBus level communication is preferred. 21 */ 22 23#include <linux/kernel.h> 24#include <linux/module.h> 25#include <linux/interrupt.h> 26#include <linux/i2c.h> 27#include <linux/rtc.h> 28#include <linux/bcd.h> 29 30#define DS1374_REG_TOD0 0x00 31#define DS1374_REG_TOD1 0x01 32#define DS1374_REG_TOD2 0x02 33#define DS1374_REG_TOD3 0x03 34#define DS1374_REG_WDALM0 0x04 35#define DS1374_REG_WDALM1 0x05 36#define DS1374_REG_WDALM2 0x06 37#define DS1374_REG_CR 0x07 38#define DS1374_REG_SR 0x08 39#define DS1374_REG_SR_OSF 0x80 40#define DS1374_REG_TCR 0x09 41 42#define DS1374_DRV_NAME "ds1374" 43 44static DECLARE_MUTEX(ds1374_mutex); 45 46static struct i2c_driver ds1374_driver; 47static struct i2c_client *save_client; 48 49static unsigned short ignore[] = { I2C_CLIENT_END }; 50static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END }; 51 52static struct i2c_client_address_data addr_data = { 53 .normal_i2c = normal_addr, 54 .probe = ignore, 55 .ignore = ignore, 56 .force = ignore, 57}; 58 59static ulong ds1374_read_rtc(void) 60{ 61 ulong time = 0; 62 int reg = DS1374_REG_WDALM0; 63 64 while (reg--) { 65 s32 tmp; 66 if ((tmp = i2c_smbus_read_byte_data(save_client, reg)) < 0) { 67 dev_warn(&save_client->dev, 68 "can't read from rtc chip\n"); 69 return 0; 70 } 71 time = (time << 8) | (tmp & 0xff); 72 } 73 return time; 74} 75 76static void ds1374_write_rtc(ulong time) 77{ 78 int reg; 79 80 for (reg = DS1374_REG_TOD0; reg < DS1374_REG_WDALM0; reg++) { 81 if (i2c_smbus_write_byte_data(save_client, reg, time & 0xff) 82 < 0) { 83 dev_warn(&save_client->dev, 84 "can't write to rtc chip\n"); 85 break; 86 } 87 time = time >> 8; 88 } 89} 90 91static void ds1374_check_rtc_status(void) 92{ 93 s32 tmp; 94 95 tmp = i2c_smbus_read_byte_data(save_client, DS1374_REG_SR); 96 if (tmp < 0) { 97 dev_warn(&save_client->dev, 98 "can't read status from rtc chip\n"); 99 return; 100 } 101 if (tmp & DS1374_REG_SR_OSF) { 102 dev_warn(&save_client->dev, 103 "oscillator discontinuity flagged, time unreliable\n"); 104 tmp &= ~DS1374_REG_SR_OSF; 105 tmp = i2c_smbus_write_byte_data(save_client, DS1374_REG_SR, 106 tmp & 0xff); 107 if (tmp < 0) 108 dev_warn(&save_client->dev, 109 "can't clear discontinuity notification\n"); 110 } 111} 112 113ulong ds1374_get_rtc_time(void) 114{ 115 ulong t1, t2; 116 int limit = 10; /* arbitrary retry limit */ 117 118 down(&ds1374_mutex); 119 120 /* 121 * Since the reads are being performed one byte at a time using 122 * the SMBus vs a 4-byte i2c transfer, there is a chance that a 123 * carry will occur during the read. To detect this, 2 reads are 124 * performed and compared. 125 */ 126 do { 127 t1 = ds1374_read_rtc(); 128 t2 = ds1374_read_rtc(); 129 } while (t1 != t2 && limit--); 130 131 up(&ds1374_mutex); 132 133 if (t1 != t2) { 134 dev_warn(&save_client->dev, 135 "can't get consistent time from rtc chip\n"); 136 t1 = 0; 137 } 138 139 return t1; 140} 141 142static void ds1374_set_tlet(ulong arg) 143{ 144 ulong t1, t2; 145 int limit = 10; /* arbitrary retry limit */ 146 147 t1 = *(ulong *) arg; 148 149 down(&ds1374_mutex); 150 151 /* 152 * Since the writes are being performed one byte at a time using 153 * the SMBus vs a 4-byte i2c transfer, there is a chance that a 154 * carry will occur during the write. To detect this, the write 155 * value is read back and compared. 156 */ 157 do { 158 ds1374_write_rtc(t1); 159 t2 = ds1374_read_rtc(); 160 } while (t1 != t2 && limit--); 161 162 up(&ds1374_mutex); 163 164 if (t1 != t2) 165 dev_warn(&save_client->dev, 166 "can't confirm time set from rtc chip\n"); 167} 168 169ulong new_time; 170 171DECLARE_TASKLET_DISABLED(ds1374_tasklet, ds1374_set_tlet, (ulong) & new_time); 172 173int ds1374_set_rtc_time(ulong nowtime) 174{ 175 new_time = nowtime; 176 177 if (in_interrupt()) 178 tasklet_schedule(&ds1374_tasklet); 179 else 180 ds1374_set_tlet((ulong) & new_time); 181 182 return 0; 183} 184 185/* 186 ***************************************************************************** 187 * 188 * Driver Interface 189 * 190 ***************************************************************************** 191 */ 192static int ds1374_probe(struct i2c_adapter *adap, int addr, int kind) 193{ 194 struct i2c_client *client; 195 int rc; 196 197 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); 198 if (!client) 199 return -ENOMEM; 200 201 memset(client, 0, sizeof(struct i2c_client)); 202 strncpy(client->name, DS1374_DRV_NAME, I2C_NAME_SIZE); 203 client->flags = I2C_DF_NOTIFY; 204 client->addr = addr; 205 client->adapter = adap; 206 client->driver = &ds1374_driver; 207 208 if ((rc = i2c_attach_client(client)) != 0) { 209 kfree(client); 210 return rc; 211 } 212 213 save_client = client; 214 215 ds1374_check_rtc_status(); 216 217 return 0; 218} 219 220static int ds1374_attach(struct i2c_adapter *adap) 221{ 222 return i2c_probe(adap, &addr_data, ds1374_probe); 223} 224 225static int ds1374_detach(struct i2c_client *client) 226{ 227 int rc; 228 229 if ((rc = i2c_detach_client(client)) == 0) { 230 kfree(i2c_get_clientdata(client)); 231 tasklet_kill(&ds1374_tasklet); 232 } 233 return rc; 234} 235 236static struct i2c_driver ds1374_driver = { 237 .owner = THIS_MODULE, 238 .name = DS1374_DRV_NAME, 239 .id = I2C_DRIVERID_DS1374, 240 .flags = I2C_DF_NOTIFY, 241 .attach_adapter = ds1374_attach, 242 .detach_client = ds1374_detach, 243}; 244 245static int __init ds1374_init(void) 246{ 247 return i2c_add_driver(&ds1374_driver); 248} 249 250static void __exit ds1374_exit(void) 251{ 252 i2c_del_driver(&ds1374_driver); 253} 254 255module_init(ds1374_init); 256module_exit(ds1374_exit); 257 258MODULE_AUTHOR("Randy Vinson <rvinson@mvista.com>"); 259MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC I2C Client Driver"); 260MODULE_LICENSE("GPL");