diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 128 |
1 files changed, 53 insertions, 75 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 5306a1a5b873..5158a625671f 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c | |||
@@ -27,13 +27,8 @@ | |||
27 | * This is currently a simple no-alarms driver. If your board has the | 27 | * This is currently a simple no-alarms driver. If your board has the |
28 | * alarm irq wired up on a ds1337 or ds1339, and you want to use that, | 28 | * alarm irq wired up on a ds1337 or ds1339, and you want to use that, |
29 | * then look at the rtc-rs5c372 driver for code to steal... | 29 | * then look at the rtc-rs5c372 driver for code to steal... |
30 | * | ||
31 | * If the I2C "force" mechanism is used, we assume the chip is a ds1337. | ||
32 | * (Much better would be board-specific tables of I2C devices, along with | ||
33 | * the platform_data drivers would use to sort such issues out.) | ||
34 | */ | 30 | */ |
35 | enum ds_type { | 31 | enum ds_type { |
36 | unknown = 0, | ||
37 | ds_1307, | 32 | ds_1307, |
38 | ds_1337, | 33 | ds_1337, |
39 | ds_1338, | 34 | ds_1338, |
@@ -43,11 +38,6 @@ enum ds_type { | |||
43 | // rs5c372 too? different address... | 38 | // rs5c372 too? different address... |
44 | }; | 39 | }; |
45 | 40 | ||
46 | static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; | ||
47 | |||
48 | I2C_CLIENT_INSMOD; | ||
49 | |||
50 | |||
51 | 41 | ||
52 | /* RTC registers don't differ much, except for the century flag */ | 42 | /* RTC registers don't differ much, except for the century flag */ |
53 | #define DS1307_REG_SECS 0x00 /* 00-59 */ | 43 | #define DS1307_REG_SECS 0x00 /* 00-59 */ |
@@ -55,6 +45,8 @@ I2C_CLIENT_INSMOD; | |||
55 | # define DS1340_BIT_nEOSC 0x80 | 45 | # define DS1340_BIT_nEOSC 0x80 |
56 | #define DS1307_REG_MIN 0x01 /* 00-59 */ | 46 | #define DS1307_REG_MIN 0x01 /* 00-59 */ |
57 | #define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ | 47 | #define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ |
48 | # define DS1307_BIT_12HR 0x40 /* in REG_HOUR */ | ||
49 | # define DS1307_BIT_PM 0x20 /* in REG_HOUR */ | ||
58 | # define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */ | 50 | # define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */ |
59 | # define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */ | 51 | # define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */ |
60 | #define DS1307_REG_WDAY 0x03 /* 01-07 */ | 52 | #define DS1307_REG_WDAY 0x03 /* 01-07 */ |
@@ -252,39 +244,27 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { | |||
252 | 244 | ||
253 | static struct i2c_driver ds1307_driver; | 245 | static struct i2c_driver ds1307_driver; |
254 | 246 | ||
255 | static int __devinit | 247 | static int __devinit ds1307_probe(struct i2c_client *client) |
256 | ds1307_detect(struct i2c_adapter *adapter, int address, int kind) | ||
257 | { | 248 | { |
258 | struct ds1307 *ds1307; | 249 | struct ds1307 *ds1307; |
259 | int err = -ENODEV; | 250 | int err = -ENODEV; |
260 | struct i2c_client *client; | ||
261 | int tmp; | 251 | int tmp; |
262 | const struct chip_desc *chip; | 252 | const struct chip_desc *chip; |
253 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
263 | 254 | ||
264 | if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) { | 255 | chip = find_chip(client->name); |
265 | err = -ENOMEM; | 256 | if (!chip) { |
266 | goto exit; | 257 | dev_err(&client->dev, "unknown chip type '%s'\n", |
258 | client->name); | ||
259 | return -ENODEV; | ||
267 | } | 260 | } |
268 | 261 | ||
269 | /* REVISIT: pending driver model conversion, set up "client" | 262 | if (!i2c_check_functionality(adapter, |
270 | * ourselves, and use a hack to determine the RTC type (instead | 263 | I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) |
271 | * of reading the client->name we're given) | 264 | return -EIO; |
272 | */ | 265 | |
273 | client = &ds1307->dev; | 266 | if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) |
274 | client->addr = address; | 267 | return -ENOMEM; |
275 | client->adapter = adapter; | ||
276 | client->driver = &ds1307_driver; | ||
277 | |||
278 | /* HACK: "force" implies "needs ds1337-style-oscillator setup", and | ||
279 | * that's the only kind of chip setup we'll know about. Until the | ||
280 | * driver model conversion, here's where to add any board-specific | ||
281 | * code to say what kind of chip is present... | ||
282 | */ | ||
283 | if (kind >= 0) | ||
284 | chip = find_chip("ds1337"); | ||
285 | else | ||
286 | chip = find_chip("ds1307"); | ||
287 | strlcpy(client->name, chip->name, I2C_NAME_SIZE); | ||
288 | 268 | ||
289 | ds1307->client = client; | 269 | ds1307->client = client; |
290 | i2c_set_clientdata(client, ds1307); | 270 | i2c_set_clientdata(client, ds1307); |
@@ -378,94 +358,92 @@ read_rtc: | |||
378 | goto read_rtc; | 358 | goto read_rtc; |
379 | } | 359 | } |
380 | break; | 360 | break; |
381 | default: | 361 | case ds_1337: |
362 | case ds_1339: | ||
382 | break; | 363 | break; |
383 | } | 364 | } |
384 | 365 | ||
385 | tmp = ds1307->regs[DS1307_REG_SECS]; | 366 | tmp = ds1307->regs[DS1307_REG_SECS]; |
386 | tmp = BCD2BIN(tmp & 0x7f); | 367 | tmp = BCD2BIN(tmp & 0x7f); |
387 | if (tmp > 60) | 368 | if (tmp > 60) |
388 | goto exit_free; | 369 | goto exit_bad; |
389 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f); | 370 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f); |
390 | if (tmp > 60) | 371 | if (tmp > 60) |
391 | goto exit_free; | 372 | goto exit_bad; |
392 | 373 | ||
393 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f); | 374 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f); |
394 | if (tmp == 0 || tmp > 31) | 375 | if (tmp == 0 || tmp > 31) |
395 | goto exit_free; | 376 | goto exit_bad; |
396 | 377 | ||
397 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MONTH] & 0x1f); | 378 | tmp = BCD2BIN(ds1307->regs[DS1307_REG_MONTH] & 0x1f); |
398 | if (tmp == 0 || tmp > 12) | 379 | if (tmp == 0 || tmp > 12) |
399 | goto exit_free; | 380 | goto exit_bad; |
400 | 381 | ||
401 | /* force into in 24 hour mode (most chips) or | ||
402 | * disable century bit (ds1340) | ||
403 | * | ||
404 | * REVISIT forcing 24 hour mode can prevent multi-master | ||
405 | * configs from sharing this RTC ... don't do this. | ||
406 | * The clock needs to be reset after changing it, too... | ||
407 | */ | ||
408 | tmp = ds1307->regs[DS1307_REG_HOUR]; | 382 | tmp = ds1307->regs[DS1307_REG_HOUR]; |
409 | if (tmp & (1 << 6)) { | 383 | switch (ds1307->type) { |
410 | if (tmp & (1 << 5)) | 384 | case ds_1340: |
411 | tmp = BCD2BIN(tmp & 0x1f) + 12; | 385 | case m41t00: |
412 | else | 386 | /* NOTE: ignores century bits; fix before deploying |
413 | tmp = BCD2BIN(tmp); | 387 | * systems that will run through year 2100. |
388 | */ | ||
389 | break; | ||
390 | default: | ||
391 | if (!(tmp & DS1307_BIT_12HR)) | ||
392 | break; | ||
393 | |||
394 | /* Be sure we're in 24 hour mode. Multi-master systems | ||
395 | * take note... | ||
396 | */ | ||
397 | tmp = BCD2BIN(tmp & 0x1f); | ||
398 | if (tmp == 12) | ||
399 | tmp = 0; | ||
400 | if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) | ||
401 | tmp += 12; | ||
414 | i2c_smbus_write_byte_data(client, | 402 | i2c_smbus_write_byte_data(client, |
415 | DS1307_REG_HOUR, | 403 | DS1307_REG_HOUR, |
416 | BIN2BCD(tmp)); | 404 | BIN2BCD(tmp)); |
417 | } | 405 | } |
418 | 406 | ||
419 | /* Tell the I2C layer a new client has arrived */ | ||
420 | if ((err = i2c_attach_client(client))) | ||
421 | goto exit_free; | ||
422 | |||
423 | ds1307->rtc = rtc_device_register(client->name, &client->dev, | 407 | ds1307->rtc = rtc_device_register(client->name, &client->dev, |
424 | &ds13xx_rtc_ops, THIS_MODULE); | 408 | &ds13xx_rtc_ops, THIS_MODULE); |
425 | if (IS_ERR(ds1307->rtc)) { | 409 | if (IS_ERR(ds1307->rtc)) { |
426 | err = PTR_ERR(ds1307->rtc); | 410 | err = PTR_ERR(ds1307->rtc); |
427 | dev_err(&client->dev, | 411 | dev_err(&client->dev, |
428 | "unable to register the class device\n"); | 412 | "unable to register the class device\n"); |
429 | goto exit_detach; | 413 | goto exit_free; |
430 | } | 414 | } |
431 | 415 | ||
432 | return 0; | 416 | return 0; |
433 | 417 | ||
434 | exit_detach: | 418 | exit_bad: |
435 | i2c_detach_client(client); | 419 | dev_dbg(&client->dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", |
420 | "bogus register", | ||
421 | ds1307->regs[0], ds1307->regs[1], | ||
422 | ds1307->regs[2], ds1307->regs[3], | ||
423 | ds1307->regs[4], ds1307->regs[5], | ||
424 | ds1307->regs[6]); | ||
425 | |||
436 | exit_free: | 426 | exit_free: |
437 | kfree(ds1307); | 427 | kfree(ds1307); |
438 | exit: | ||
439 | return err; | 428 | return err; |
440 | } | 429 | } |
441 | 430 | ||
442 | static int __devinit | 431 | static int __devexit ds1307_remove(struct i2c_client *client) |
443 | ds1307_attach_adapter(struct i2c_adapter *adapter) | ||
444 | { | ||
445 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) | ||
446 | return 0; | ||
447 | return i2c_probe(adapter, &addr_data, ds1307_detect); | ||
448 | } | ||
449 | |||
450 | static int __devexit ds1307_detach_client(struct i2c_client *client) | ||
451 | { | 432 | { |
452 | int err; | ||
453 | struct ds1307 *ds1307 = i2c_get_clientdata(client); | 433 | struct ds1307 *ds1307 = i2c_get_clientdata(client); |
454 | 434 | ||
455 | rtc_device_unregister(ds1307->rtc); | 435 | rtc_device_unregister(ds1307->rtc); |
456 | if ((err = i2c_detach_client(client))) | ||
457 | return err; | ||
458 | kfree(ds1307); | 436 | kfree(ds1307); |
459 | return 0; | 437 | return 0; |
460 | } | 438 | } |
461 | 439 | ||
462 | static struct i2c_driver ds1307_driver = { | 440 | static struct i2c_driver ds1307_driver = { |
463 | .driver = { | 441 | .driver = { |
464 | .name = "ds1307", | 442 | .name = "rtc-ds1307", |
465 | .owner = THIS_MODULE, | 443 | .owner = THIS_MODULE, |
466 | }, | 444 | }, |
467 | .attach_adapter = ds1307_attach_adapter, | 445 | .probe = ds1307_probe, |
468 | .detach_client = __devexit_p(ds1307_detach_client), | 446 | .remove = __devexit_p(ds1307_remove), |
469 | }; | 447 | }; |
470 | 448 | ||
471 | static int __init ds1307_init(void) | 449 | static int __init ds1307_init(void) |