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 77b2555b52a894a2e39a42e43d993df875c46a6a 259 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}; 57 58static ulong ds1374_read_rtc(void) 59{ 60 ulong time = 0; 61 int reg = DS1374_REG_WDALM0; 62 63 while (reg--) { 64 s32 tmp; 65 if ((tmp = i2c_smbus_read_byte_data(save_client, reg)) < 0) { 66 dev_warn(&save_client->dev, 67 "can't read from rtc chip\n"); 68 return 0; 69 } 70 time = (time << 8) | (tmp & 0xff); 71 } 72 return time; 73} 74 75static void ds1374_write_rtc(ulong time) 76{ 77 int reg; 78 79 for (reg = DS1374_REG_TOD0; reg < DS1374_REG_WDALM0; reg++) { 80 if (i2c_smbus_write_byte_data(save_client, reg, time & 0xff) 81 < 0) { 82 dev_warn(&save_client->dev, 83 "can't write to rtc chip\n"); 84 break; 85 } 86 time = time >> 8; 87 } 88} 89 90static void ds1374_check_rtc_status(void) 91{ 92 s32 tmp; 93 94 tmp = i2c_smbus_read_byte_data(save_client, DS1374_REG_SR); 95 if (tmp < 0) { 96 dev_warn(&save_client->dev, 97 "can't read status from rtc chip\n"); 98 return; 99 } 100 if (tmp & DS1374_REG_SR_OSF) { 101 dev_warn(&save_client->dev, 102 "oscillator discontinuity flagged, time unreliable\n"); 103 tmp &= ~DS1374_REG_SR_OSF; 104 tmp = i2c_smbus_write_byte_data(save_client, DS1374_REG_SR, 105 tmp & 0xff); 106 if (tmp < 0) 107 dev_warn(&save_client->dev, 108 "can't clear discontinuity notification\n"); 109 } 110} 111 112ulong ds1374_get_rtc_time(void) 113{ 114 ulong t1, t2; 115 int limit = 10; /* arbitrary retry limit */ 116 117 down(&ds1374_mutex); 118 119 /* 120 * Since the reads are being performed one byte at a time using 121 * the SMBus vs a 4-byte i2c transfer, there is a chance that a 122 * carry will occur during the read. To detect this, 2 reads are 123 * performed and compared. 124 */ 125 do { 126 t1 = ds1374_read_rtc(); 127 t2 = ds1374_read_rtc(); 128 } while (t1 != t2 && limit--); 129 130 up(&ds1374_mutex); 131 132 if (t1 != t2) { 133 dev_warn(&save_client->dev, 134 "can't get consistent time from rtc chip\n"); 135 t1 = 0; 136 } 137 138 return t1; 139} 140 141static void ds1374_set_tlet(ulong arg) 142{ 143 ulong t1, t2; 144 int limit = 10; /* arbitrary retry limit */ 145 146 t1 = *(ulong *) arg; 147 148 down(&ds1374_mutex); 149 150 /* 151 * Since the writes are being performed one byte at a time using 152 * the SMBus vs a 4-byte i2c transfer, there is a chance that a 153 * carry will occur during the write. To detect this, the write 154 * value is read back and compared. 155 */ 156 do { 157 ds1374_write_rtc(t1); 158 t2 = ds1374_read_rtc(); 159 } while (t1 != t2 && limit--); 160 161 up(&ds1374_mutex); 162 163 if (t1 != t2) 164 dev_warn(&save_client->dev, 165 "can't confirm time set from rtc chip\n"); 166} 167 168static ulong new_time; 169 170DECLARE_TASKLET_DISABLED(ds1374_tasklet, ds1374_set_tlet, (ulong) & new_time); 171 172int ds1374_set_rtc_time(ulong nowtime) 173{ 174 new_time = nowtime; 175 176 if (in_interrupt()) 177 tasklet_schedule(&ds1374_tasklet); 178 else 179 ds1374_set_tlet((ulong) & new_time); 180 181 return 0; 182} 183 184/* 185 ***************************************************************************** 186 * 187 * Driver Interface 188 * 189 ***************************************************************************** 190 */ 191static int ds1374_probe(struct i2c_adapter *adap, int addr, int kind) 192{ 193 struct i2c_client *client; 194 int rc; 195 196 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); 197 if (!client) 198 return -ENOMEM; 199 200 memset(client, 0, sizeof(struct i2c_client)); 201 strncpy(client->name, DS1374_DRV_NAME, I2C_NAME_SIZE); 202 client->flags = I2C_DF_NOTIFY; 203 client->addr = addr; 204 client->adapter = adap; 205 client->driver = &ds1374_driver; 206 207 if ((rc = i2c_attach_client(client)) != 0) { 208 kfree(client); 209 return rc; 210 } 211 212 save_client = client; 213 214 ds1374_check_rtc_status(); 215 216 return 0; 217} 218 219static int ds1374_attach(struct i2c_adapter *adap) 220{ 221 return i2c_probe(adap, &addr_data, ds1374_probe); 222} 223 224static int ds1374_detach(struct i2c_client *client) 225{ 226 int rc; 227 228 if ((rc = i2c_detach_client(client)) == 0) { 229 kfree(i2c_get_clientdata(client)); 230 tasklet_kill(&ds1374_tasklet); 231 } 232 return rc; 233} 234 235static struct i2c_driver ds1374_driver = { 236 .owner = THIS_MODULE, 237 .name = DS1374_DRV_NAME, 238 .id = I2C_DRIVERID_DS1374, 239 .flags = I2C_DF_NOTIFY, 240 .attach_adapter = ds1374_attach, 241 .detach_client = ds1374_detach, 242}; 243 244static int __init ds1374_init(void) 245{ 246 return i2c_add_driver(&ds1374_driver); 247} 248 249static void __exit ds1374_exit(void) 250{ 251 i2c_del_driver(&ds1374_driver); 252} 253 254module_init(ds1374_init); 255module_exit(ds1374_exit); 256 257MODULE_AUTHOR("Randy Vinson <rvinson@mvista.com>"); 258MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC I2C Client Driver"); 259MODULE_LICENSE("GPL");