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.17-rc4 290 lines 7.0 kB view raw
1/* 2 * An I2C driver for the Ricoh RS5C372 RTC 3 * 4 * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> 5 * Copyright (C) 2006 Tower Technologies 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/i2c.h> 13#include <linux/rtc.h> 14#include <linux/bcd.h> 15 16#define DRV_VERSION "0.2" 17 18/* Addresses to scan */ 19static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END }; 20 21/* Insmod parameters */ 22I2C_CLIENT_INSMOD; 23 24#define RS5C372_REG_SECS 0 25#define RS5C372_REG_MINS 1 26#define RS5C372_REG_HOURS 2 27#define RS5C372_REG_WDAY 3 28#define RS5C372_REG_DAY 4 29#define RS5C372_REG_MONTH 5 30#define RS5C372_REG_YEAR 6 31#define RS5C372_REG_TRIM 7 32 33#define RS5C372_TRIM_XSL 0x80 34#define RS5C372_TRIM_MASK 0x7F 35 36#define RS5C372_REG_BASE 0 37 38static int rs5c372_attach(struct i2c_adapter *adapter); 39static int rs5c372_detach(struct i2c_client *client); 40static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind); 41 42static struct i2c_driver rs5c372_driver = { 43 .driver = { 44 .name = "rs5c372", 45 }, 46 .attach_adapter = &rs5c372_attach, 47 .detach_client = &rs5c372_detach, 48}; 49 50static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm) 51{ 52 unsigned char buf[7] = { RS5C372_REG_BASE }; 53 54 /* this implements the 1st reading method, according 55 * to the datasheet. buf[0] is initialized with 56 * address ptr and transmission format register. 57 */ 58 struct i2c_msg msgs[] = { 59 { client->addr, 0, 1, buf }, 60 { client->addr, I2C_M_RD, 7, buf }, 61 }; 62 63 if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { 64 dev_err(&client->dev, "%s: read error\n", __FUNCTION__); 65 return -EIO; 66 } 67 68 tm->tm_sec = BCD2BIN(buf[RS5C372_REG_SECS] & 0x7f); 69 tm->tm_min = BCD2BIN(buf[RS5C372_REG_MINS] & 0x7f); 70 tm->tm_hour = BCD2BIN(buf[RS5C372_REG_HOURS] & 0x3f); 71 tm->tm_wday = BCD2BIN(buf[RS5C372_REG_WDAY] & 0x07); 72 tm->tm_mday = BCD2BIN(buf[RS5C372_REG_DAY] & 0x3f); 73 74 /* tm->tm_mon is zero-based */ 75 tm->tm_mon = BCD2BIN(buf[RS5C372_REG_MONTH] & 0x1f) - 1; 76 77 /* year is 1900 + tm->tm_year */ 78 tm->tm_year = BCD2BIN(buf[RS5C372_REG_YEAR]) + 100; 79 80 dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " 81 "mday=%d, mon=%d, year=%d, wday=%d\n", 82 __FUNCTION__, 83 tm->tm_sec, tm->tm_min, tm->tm_hour, 84 tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); 85 86 return 0; 87} 88 89static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) 90{ 91 unsigned char buf[8] = { RS5C372_REG_BASE }; 92 93 dev_dbg(&client->dev, 94 "%s: secs=%d, mins=%d, hours=%d ", 95 "mday=%d, mon=%d, year=%d, wday=%d\n", 96 __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, 97 tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); 98 99 buf[1] = BIN2BCD(tm->tm_sec); 100 buf[2] = BIN2BCD(tm->tm_min); 101 buf[3] = BIN2BCD(tm->tm_hour); 102 buf[4] = BIN2BCD(tm->tm_wday); 103 buf[5] = BIN2BCD(tm->tm_mday); 104 buf[6] = BIN2BCD(tm->tm_mon + 1); 105 buf[7] = BIN2BCD(tm->tm_year - 100); 106 107 if ((i2c_master_send(client, buf, 8)) != 8) { 108 dev_err(&client->dev, "%s: write error\n", __FUNCTION__); 109 return -EIO; 110 } 111 112 return 0; 113} 114 115static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) 116{ 117 unsigned char buf = RS5C372_REG_TRIM; 118 119 struct i2c_msg msgs[] = { 120 { client->addr, 0, 1, &buf }, 121 { client->addr, I2C_M_RD, 1, &buf }, 122 }; 123 124 if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { 125 dev_err(&client->dev, "%s: read error\n", __FUNCTION__); 126 return -EIO; 127 } 128 129 dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim); 130 131 if (osc) 132 *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768; 133 134 if (trim) 135 *trim = buf & RS5C372_TRIM_MASK; 136 137 return 0; 138} 139 140static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) 141{ 142 return rs5c372_get_datetime(to_i2c_client(dev), tm); 143} 144 145static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) 146{ 147 return rs5c372_set_datetime(to_i2c_client(dev), tm); 148} 149 150static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) 151{ 152 int err, osc, trim; 153 154 err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim); 155 if (err == 0) { 156 seq_printf(seq, "%d.%03d KHz\n", osc / 1000, osc % 1000); 157 seq_printf(seq, "trim\t: %d\n", trim); 158 } 159 160 return 0; 161} 162 163static struct rtc_class_ops rs5c372_rtc_ops = { 164 .proc = rs5c372_rtc_proc, 165 .read_time = rs5c372_rtc_read_time, 166 .set_time = rs5c372_rtc_set_time, 167}; 168 169static ssize_t rs5c372_sysfs_show_trim(struct device *dev, 170 struct device_attribute *attr, char *buf) 171{ 172 int err, trim; 173 174 err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim); 175 if (err) 176 return err; 177 178 return sprintf(buf, "0x%2x\n", trim); 179} 180static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL); 181 182static ssize_t rs5c372_sysfs_show_osc(struct device *dev, 183 struct device_attribute *attr, char *buf) 184{ 185 int err, osc; 186 187 err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL); 188 if (err) 189 return err; 190 191 return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); 192} 193static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); 194 195static int rs5c372_attach(struct i2c_adapter *adapter) 196{ 197 return i2c_probe(adapter, &addr_data, rs5c372_probe); 198} 199 200static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) 201{ 202 int err = 0; 203 struct i2c_client *client; 204 struct rtc_device *rtc; 205 206 dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); 207 208 if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { 209 err = -ENODEV; 210 goto exit; 211 } 212 213 if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { 214 err = -ENOMEM; 215 goto exit; 216 } 217 218 /* I2C client */ 219 client->addr = address; 220 client->driver = &rs5c372_driver; 221 client->adapter = adapter; 222 223 strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE); 224 225 /* Inform the i2c layer */ 226 if ((err = i2c_attach_client(client))) 227 goto exit_kfree; 228 229 dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); 230 231 rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev, 232 &rs5c372_rtc_ops, THIS_MODULE); 233 234 if (IS_ERR(rtc)) { 235 err = PTR_ERR(rtc); 236 goto exit_detach; 237 } 238 239 i2c_set_clientdata(client, rtc); 240 241 device_create_file(&client->dev, &dev_attr_trim); 242 device_create_file(&client->dev, &dev_attr_osc); 243 244 return 0; 245 246exit_detach: 247 i2c_detach_client(client); 248 249exit_kfree: 250 kfree(client); 251 252exit: 253 return err; 254} 255 256static int rs5c372_detach(struct i2c_client *client) 257{ 258 int err; 259 struct rtc_device *rtc = i2c_get_clientdata(client); 260 261 if (rtc) 262 rtc_device_unregister(rtc); 263 264 if ((err = i2c_detach_client(client))) 265 return err; 266 267 kfree(client); 268 269 return 0; 270} 271 272static __init int rs5c372_init(void) 273{ 274 return i2c_add_driver(&rs5c372_driver); 275} 276 277static __exit void rs5c372_exit(void) 278{ 279 i2c_del_driver(&rs5c372_driver); 280} 281 282module_init(rs5c372_init); 283module_exit(rs5c372_exit); 284 285MODULE_AUTHOR( 286 "Pavel Mironchik <pmironchik@optifacio.net>, " 287 "Alessandro Zummo <a.zummo@towertech.it>"); 288MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); 289MODULE_LICENSE("GPL"); 290MODULE_VERSION(DRV_VERSION);