diff options
Diffstat (limited to 'drivers/rtc/rtc-ds3232.c')
| -rw-r--r-- | drivers/rtc/rtc-ds3232.c | 100 | 
1 files changed, 72 insertions, 28 deletions
| diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index b83bb5a527f8..adaf06c41479 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c | |||
| @@ -57,6 +57,7 @@ struct ds3232 { | |||
| 57 | * in the remove function. | 57 | * in the remove function. | 
| 58 | */ | 58 | */ | 
| 59 | struct mutex mutex; | 59 | struct mutex mutex; | 
| 60 | bool suspended; | ||
| 60 | int exiting; | 61 | int exiting; | 
| 61 | }; | 62 | }; | 
| 62 | 63 | ||
| @@ -345,7 +346,15 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id) | |||
| 345 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | 346 | struct ds3232 *ds3232 = i2c_get_clientdata(client); | 
| 346 | 347 | ||
| 347 | disable_irq_nosync(irq); | 348 | disable_irq_nosync(irq); | 
| 348 | schedule_work(&ds3232->work); | 349 | |
| 350 | /* | ||
| 351 | * If rtc as a wakeup source, can't schedule the work | ||
| 352 | * at system resume flow, because at this time the i2c bus | ||
| 353 | * has not been resumed. | ||
| 354 | */ | ||
| 355 | if (!ds3232->suspended) | ||
| 356 | schedule_work(&ds3232->work); | ||
| 357 | |||
| 349 | return IRQ_HANDLED; | 358 | return IRQ_HANDLED; | 
| 350 | } | 359 | } | 
| 351 | 360 | ||
| @@ -363,22 +372,26 @@ static void ds3232_work(struct work_struct *work) | |||
| 363 | 372 | ||
| 364 | if (stat & DS3232_REG_SR_A1F) { | 373 | if (stat & DS3232_REG_SR_A1F) { | 
| 365 | control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | 374 | control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); | 
| 366 | if (control < 0) | 375 | if (control < 0) { | 
| 367 | goto out; | 376 | pr_warn("Read DS3232 Control Register error." | 
| 368 | /* disable alarm1 interrupt */ | 377 | "Disable IRQ%d.\n", client->irq); | 
| 369 | control &= ~(DS3232_REG_CR_A1IE); | 378 | } else { | 
| 370 | i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); | 379 | /* disable alarm1 interrupt */ | 
| 371 | 380 | control &= ~(DS3232_REG_CR_A1IE); | |
| 372 | /* clear the alarm pend flag */ | 381 | i2c_smbus_write_byte_data(client, DS3232_REG_CR, | 
| 373 | stat &= ~DS3232_REG_SR_A1F; | 382 | control); | 
| 374 | i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); | 383 | |
| 375 | 384 | /* clear the alarm pend flag */ | |
| 376 | rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); | 385 | stat &= ~DS3232_REG_SR_A1F; | 
| 386 | i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); | ||
| 387 | |||
| 388 | rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); | ||
| 389 | |||
| 390 | if (!ds3232->exiting) | ||
| 391 | enable_irq(client->irq); | ||
| 392 | } | ||
| 377 | } | 393 | } | 
| 378 | 394 | ||
| 379 | out: | ||
| 380 | if (!ds3232->exiting) | ||
| 381 | enable_irq(client->irq); | ||
| 382 | unlock: | 395 | unlock: | 
| 383 | mutex_unlock(&ds3232->mutex); | 396 | mutex_unlock(&ds3232->mutex); | 
| 384 | } | 397 | } | 
| @@ -411,23 +424,17 @@ static int ds3232_probe(struct i2c_client *client, | |||
| 411 | if (ret) | 424 | if (ret) | 
| 412 | return ret; | 425 | return ret; | 
| 413 | 426 | ||
| 414 | ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, | 427 | if (client->irq > 0) { | 
| 415 | &ds3232_rtc_ops, THIS_MODULE); | 428 | ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, | 
| 416 | if (IS_ERR(ds3232->rtc)) { | 429 | IRQF_SHARED, "ds3232", client); | 
| 417 | dev_err(&client->dev, "unable to register the class device\n"); | ||
| 418 | return PTR_ERR(ds3232->rtc); | ||
| 419 | } | ||
| 420 | |||
| 421 | if (client->irq >= 0) { | ||
| 422 | ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, 0, | ||
| 423 | "ds3232", client); | ||
| 424 | if (ret) { | 430 | if (ret) { | 
| 425 | dev_err(&client->dev, "unable to request IRQ\n"); | 431 | dev_err(&client->dev, "unable to request IRQ\n"); | 
| 426 | return ret; | ||
| 427 | } | 432 | } | 
| 433 | device_init_wakeup(&client->dev, 1); | ||
| 428 | } | 434 | } | 
| 429 | 435 | ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, | |
| 430 | return 0; | 436 | &ds3232_rtc_ops, THIS_MODULE); | 
| 437 | return PTR_ERR_OR_ZERO(ds3232->rtc); | ||
| 431 | } | 438 | } | 
| 432 | 439 | ||
| 433 | static int ds3232_remove(struct i2c_client *client) | 440 | static int ds3232_remove(struct i2c_client *client) | 
| @@ -446,6 +453,42 @@ static int ds3232_remove(struct i2c_client *client) | |||
| 446 | return 0; | 453 | return 0; | 
| 447 | } | 454 | } | 
| 448 | 455 | ||
| 456 | #ifdef CONFIG_PM_SLEEP | ||
| 457 | static int ds3232_suspend(struct device *dev) | ||
| 458 | { | ||
| 459 | struct ds3232 *ds3232 = dev_get_drvdata(dev); | ||
| 460 | struct i2c_client *client = to_i2c_client(dev); | ||
| 461 | |||
| 462 | if (device_can_wakeup(dev)) { | ||
| 463 | ds3232->suspended = true; | ||
| 464 | irq_set_irq_wake(client->irq, 1); | ||
| 465 | } | ||
| 466 | |||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | static int ds3232_resume(struct device *dev) | ||
| 471 | { | ||
| 472 | struct ds3232 *ds3232 = dev_get_drvdata(dev); | ||
| 473 | struct i2c_client *client = to_i2c_client(dev); | ||
| 474 | |||
| 475 | if (ds3232->suspended) { | ||
| 476 | ds3232->suspended = false; | ||
| 477 | |||
| 478 | /* Clear the hardware alarm pend flag */ | ||
| 479 | schedule_work(&ds3232->work); | ||
| 480 | |||
| 481 | irq_set_irq_wake(client->irq, 0); | ||
| 482 | } | ||
| 483 | |||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | #endif | ||
| 487 | |||
| 488 | static const struct dev_pm_ops ds3232_pm_ops = { | ||
| 489 | SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) | ||
| 490 | }; | ||
| 491 | |||
| 449 | static const struct i2c_device_id ds3232_id[] = { | 492 | static const struct i2c_device_id ds3232_id[] = { | 
| 450 | { "ds3232", 0 }, | 493 | { "ds3232", 0 }, | 
| 451 | { } | 494 | { } | 
| @@ -456,6 +499,7 @@ static struct i2c_driver ds3232_driver = { | |||
| 456 | .driver = { | 499 | .driver = { | 
| 457 | .name = "rtc-ds3232", | 500 | .name = "rtc-ds3232", | 
| 458 | .owner = THIS_MODULE, | 501 | .owner = THIS_MODULE, | 
| 502 | .pm = &ds3232_pm_ops, | ||
| 459 | }, | 503 | }, | 
| 460 | .probe = ds3232_probe, | 504 | .probe = ds3232_probe, | 
| 461 | .remove = ds3232_remove, | 505 | .remove = ds3232_remove, | 
