diff options
| author | Krzysztof Kozlowski <k.kozlowski@samsung.com> | 2014-06-10 18:18:46 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-10 18:34:47 -0400 |
| commit | 0c5deb1ea92fefa6ee69e2da8b887723bdecf3d9 (patch) | |
| tree | 1fb14a31b5a4696a2c7ae16fa9c1e2abc5c010b9 /drivers/rtc | |
| parent | f8b23bbdad5dfb50af101a56c58ad4e83510a9a6 (diff) | |
rtc: s5m: add support for S2MPS14 RTC
Add support for S2MPS14 to the rtc-s5m driver. Differences in S2MPS14
(in comparison to S5M8767):
- Layout of registers
- Lack of century support for time and alarms (7 registers used for
storing time/alarm)
- Two buffer control registers: WUDR and RUDR
- No register for enabling writing time
- RTC interrupts are reported in main PMIC I2C device
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Sangbeom Kim <sbkim73@samsung.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/rtc/rtc-s5m.c | 101 |
2 files changed, 86 insertions, 19 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 71988b69eca6..0754f5c7cb3b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -530,11 +530,11 @@ config RTC_DRV_RV3029C2 | |||
| 530 | will be called rtc-rv3029c2. | 530 | will be called rtc-rv3029c2. |
| 531 | 531 | ||
| 532 | config RTC_DRV_S5M | 532 | config RTC_DRV_S5M |
| 533 | tristate "Samsung S5M series" | 533 | tristate "Samsung S2M/S5M series" |
| 534 | depends on MFD_SEC_CORE | 534 | depends on MFD_SEC_CORE |
| 535 | help | 535 | help |
| 536 | If you say yes here you will get support for the | 536 | If you say yes here you will get support for the |
| 537 | RTC of Samsung S5M PMIC series. | 537 | RTC of Samsung S2MPS14 and S5M PMIC series. |
| 538 | 538 | ||
| 539 | This driver can also be built as a module. If so, the module | 539 | This driver can also be built as a module. If so, the module |
| 540 | will be called rtc-s5m. | 540 | will be called rtc-s5m. |
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 59860128a221..76287ebd0e35 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c | |||
| @@ -17,16 +17,14 @@ | |||
| 17 | 17 | ||
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | #include <linux/i2c.h> | 19 | #include <linux/i2c.h> |
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/bcd.h> | 20 | #include <linux/bcd.h> |
| 22 | #include <linux/bitops.h> | ||
| 23 | #include <linux/regmap.h> | 21 | #include <linux/regmap.h> |
| 24 | #include <linux/rtc.h> | 22 | #include <linux/rtc.h> |
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
| 27 | #include <linux/mfd/samsung/core.h> | 24 | #include <linux/mfd/samsung/core.h> |
| 28 | #include <linux/mfd/samsung/irq.h> | 25 | #include <linux/mfd/samsung/irq.h> |
| 29 | #include <linux/mfd/samsung/rtc.h> | 26 | #include <linux/mfd/samsung/rtc.h> |
| 27 | #include <linux/mfd/samsung/s2mps14.h> | ||
| 30 | 28 | ||
| 31 | /* | 29 | /* |
| 32 | * Maximum number of retries for checking changes in UDR field | 30 | * Maximum number of retries for checking changes in UDR field |
| @@ -74,6 +72,21 @@ static const struct s5m_rtc_reg_config s5m_rtc_regs = { | |||
| 74 | .rtc_udr_mask = S5M_RTC_UDR_MASK, | 72 | .rtc_udr_mask = S5M_RTC_UDR_MASK, |
| 75 | }; | 73 | }; |
| 76 | 74 | ||
| 75 | /* | ||
| 76 | * Register map for S2MPS14. | ||
| 77 | * It may be also suitable for S2MPS11 but this was not tested. | ||
| 78 | */ | ||
| 79 | static const struct s5m_rtc_reg_config s2mps_rtc_regs = { | ||
| 80 | .regs_count = 7, | ||
| 81 | .time = S2MPS_RTC_SEC, | ||
| 82 | .ctrl = S2MPS_RTC_CTRL, | ||
| 83 | .alarm0 = S2MPS_ALARM0_SEC, | ||
| 84 | .alarm1 = S2MPS_ALARM1_SEC, | ||
| 85 | .smpl_wtsr = S2MPS_WTSR_SMPL_CNTL, | ||
| 86 | .rtc_udr_update = S2MPS_RTC_UDR_CON, | ||
| 87 | .rtc_udr_mask = S2MPS_RTC_WUDR_MASK, | ||
| 88 | }; | ||
| 89 | |||
| 77 | struct s5m_rtc_info { | 90 | struct s5m_rtc_info { |
| 78 | struct device *dev; | 91 | struct device *dev; |
| 79 | struct i2c_client *i2c; | 92 | struct i2c_client *i2c; |
| @@ -178,6 +191,11 @@ static inline int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info, | |||
| 178 | ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val); | 191 | ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val); |
| 179 | val &= S5M_ALARM0_STATUS; | 192 | val &= S5M_ALARM0_STATUS; |
| 180 | break; | 193 | break; |
| 194 | case S2MPS14X: | ||
| 195 | ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2, | ||
| 196 | &val); | ||
| 197 | val &= S2MPS_ALARM0_STATUS; | ||
| 198 | break; | ||
| 181 | default: | 199 | default: |
| 182 | return -EINVAL; | 200 | return -EINVAL; |
| 183 | } | 201 | } |
| @@ -203,8 +221,9 @@ static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info) | |||
| 203 | return ret; | 221 | return ret; |
| 204 | } | 222 | } |
| 205 | 223 | ||
| 206 | data |= S5M_RTC_TIME_EN_MASK; | ||
| 207 | data |= info->regs->rtc_udr_mask; | 224 | data |= info->regs->rtc_udr_mask; |
| 225 | if (info->device_type == S5M8763X || info->device_type == S5M8767X) | ||
| 226 | data |= S5M_RTC_TIME_EN_MASK; | ||
| 208 | 227 | ||
| 209 | ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data); | 228 | ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data); |
| 210 | if (ret < 0) { | 229 | if (ret < 0) { |
| @@ -229,8 +248,18 @@ static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info) | |||
| 229 | return ret; | 248 | return ret; |
| 230 | } | 249 | } |
| 231 | 250 | ||
| 232 | data &= ~S5M_RTC_TIME_EN_MASK; | ||
| 233 | data |= info->regs->rtc_udr_mask; | 251 | data |= info->regs->rtc_udr_mask; |
| 252 | switch (info->device_type) { | ||
| 253 | case S5M8763X: | ||
| 254 | case S5M8767X: | ||
| 255 | data &= ~S5M_RTC_TIME_EN_MASK; | ||
| 256 | break; | ||
| 257 | case S2MPS14X: | ||
| 258 | data |= S2MPS_RTC_RUDR_MASK; | ||
| 259 | break; | ||
| 260 | default: | ||
| 261 | return -EINVAL; | ||
| 262 | } | ||
| 234 | 263 | ||
| 235 | ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data); | 264 | ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data); |
| 236 | if (ret < 0) { | 265 | if (ret < 0) { |
| @@ -282,6 +311,17 @@ static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
| 282 | u8 data[info->regs->regs_count]; | 311 | u8 data[info->regs->regs_count]; |
| 283 | int ret; | 312 | int ret; |
| 284 | 313 | ||
| 314 | if (info->device_type == S2MPS14X) { | ||
| 315 | ret = regmap_update_bits(info->regmap, | ||
| 316 | info->regs->rtc_udr_update, | ||
| 317 | S2MPS_RTC_RUDR_MASK, S2MPS_RTC_RUDR_MASK); | ||
| 318 | if (ret) { | ||
| 319 | dev_err(dev, | ||
| 320 | "Failed to prepare registers for time reading: %d\n", | ||
| 321 | ret); | ||
| 322 | return ret; | ||
| 323 | } | ||
| 324 | } | ||
| 285 | ret = regmap_bulk_read(info->regmap, info->regs->time, data, | 325 | ret = regmap_bulk_read(info->regmap, info->regs->time, data, |
| 286 | info->regs->regs_count); | 326 | info->regs->regs_count); |
| 287 | if (ret < 0) | 327 | if (ret < 0) |
| @@ -293,6 +333,7 @@ static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
| 293 | break; | 333 | break; |
| 294 | 334 | ||
| 295 | case S5M8767X: | 335 | case S5M8767X: |
| 336 | case S2MPS14X: | ||
| 296 | s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode); | 337 | s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode); |
| 297 | break; | 338 | break; |
| 298 | 339 | ||
| @@ -318,6 +359,7 @@ static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
| 318 | s5m8763_tm_to_data(tm, data); | 359 | s5m8763_tm_to_data(tm, data); |
| 319 | break; | 360 | break; |
| 320 | case S5M8767X: | 361 | case S5M8767X: |
| 362 | case S2MPS14X: | ||
| 321 | ret = s5m8767_tm_to_data(tm, data); | 363 | ret = s5m8767_tm_to_data(tm, data); |
| 322 | break; | 364 | break; |
| 323 | default: | 365 | default: |
| @@ -364,6 +406,7 @@ static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
| 364 | break; | 406 | break; |
| 365 | 407 | ||
| 366 | case S5M8767X: | 408 | case S5M8767X: |
| 409 | case S2MPS14X: | ||
| 367 | s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); | 410 | s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); |
| 368 | alrm->enabled = 0; | 411 | alrm->enabled = 0; |
| 369 | for (i = 0; i < info->regs->regs_count; i++) { | 412 | for (i = 0; i < info->regs->regs_count; i++) { |
| @@ -411,6 +454,7 @@ static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) | |||
| 411 | break; | 454 | break; |
| 412 | 455 | ||
| 413 | case S5M8767X: | 456 | case S5M8767X: |
| 457 | case S2MPS14X: | ||
| 414 | for (i = 0; i < info->regs->regs_count; i++) | 458 | for (i = 0; i < info->regs->regs_count; i++) |
| 415 | data[i] &= ~ALARM_ENABLE_MASK; | 459 | data[i] &= ~ALARM_ENABLE_MASK; |
| 416 | 460 | ||
| @@ -454,6 +498,7 @@ static int s5m_rtc_start_alarm(struct s5m_rtc_info *info) | |||
| 454 | break; | 498 | break; |
| 455 | 499 | ||
| 456 | case S5M8767X: | 500 | case S5M8767X: |
| 501 | case S2MPS14X: | ||
| 457 | data[RTC_SEC] |= ALARM_ENABLE_MASK; | 502 | data[RTC_SEC] |= ALARM_ENABLE_MASK; |
| 458 | data[RTC_MIN] |= ALARM_ENABLE_MASK; | 503 | data[RTC_MIN] |= ALARM_ENABLE_MASK; |
| 459 | data[RTC_HOUR] |= ALARM_ENABLE_MASK; | 504 | data[RTC_HOUR] |= ALARM_ENABLE_MASK; |
| @@ -492,6 +537,7 @@ static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
| 492 | break; | 537 | break; |
| 493 | 538 | ||
| 494 | case S5M8767X: | 539 | case S5M8767X: |
| 540 | case S2MPS14X: | ||
| 495 | s5m8767_tm_to_data(&alrm->time, data); | 541 | s5m8767_tm_to_data(&alrm->time, data); |
| 496 | break; | 542 | break; |
| 497 | 543 | ||
| @@ -578,19 +624,33 @@ static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) | |||
| 578 | u8 data[2]; | 624 | u8 data[2]; |
| 579 | int ret; | 625 | int ret; |
| 580 | 626 | ||
| 581 | /* UDR update time. Default of 7.32 ms is too long. */ | 627 | switch (info->device_type) { |
| 582 | ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON, | 628 | case S5M8763X: |
| 583 | S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US); | 629 | case S5M8767X: |
| 584 | if (ret < 0) | 630 | /* UDR update time. Default of 7.32 ms is too long. */ |
| 585 | dev_err(info->dev, "%s: fail to change UDR time: %d\n", | 631 | ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON, |
| 586 | __func__, ret); | 632 | S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US); |
| 633 | if (ret < 0) | ||
| 634 | dev_err(info->dev, "%s: fail to change UDR time: %d\n", | ||
| 635 | __func__, ret); | ||
| 587 | 636 | ||
| 588 | /* Set RTC control register : Binary mode, 24hour mode */ | 637 | /* Set RTC control register : Binary mode, 24hour mode */ |
| 589 | data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); | 638 | data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); |
| 590 | data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); | 639 | data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); |
| 640 | |||
| 641 | ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2); | ||
| 642 | break; | ||
| 643 | |||
| 644 | case S2MPS14X: | ||
| 645 | data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); | ||
| 646 | ret = regmap_write(info->regmap, info->regs->ctrl, data[0]); | ||
| 647 | break; | ||
| 648 | |||
| 649 | default: | ||
| 650 | return -EINVAL; | ||
| 651 | } | ||
| 591 | 652 | ||
| 592 | info->rtc_24hr_mode = 1; | 653 | info->rtc_24hr_mode = 1; |
| 593 | ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2); | ||
| 594 | if (ret < 0) { | 654 | if (ret < 0) { |
| 595 | dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", | 655 | dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", |
| 596 | __func__, ret); | 656 | __func__, ret); |
| @@ -620,6 +680,7 @@ static int s5m_rtc_probe(struct platform_device *pdev) | |||
| 620 | switch (pdata->device_type) { | 680 | switch (pdata->device_type) { |
| 621 | case S2MPS14X: | 681 | case S2MPS14X: |
| 622 | regmap_cfg = &s2mps14_rtc_regmap_config; | 682 | regmap_cfg = &s2mps14_rtc_regmap_config; |
| 683 | info->regs = &s2mps_rtc_regs; | ||
| 623 | break; | 684 | break; |
| 624 | case S5M8763X: | 685 | case S5M8763X: |
| 625 | regmap_cfg = &s5m_rtc_regmap_config; | 686 | regmap_cfg = &s5m_rtc_regmap_config; |
| @@ -654,6 +715,11 @@ static int s5m_rtc_probe(struct platform_device *pdev) | |||
| 654 | info->wtsr_smpl = s5m87xx->wtsr_smpl; | 715 | info->wtsr_smpl = s5m87xx->wtsr_smpl; |
| 655 | 716 | ||
| 656 | switch (pdata->device_type) { | 717 | switch (pdata->device_type) { |
| 718 | case S2MPS14X: | ||
| 719 | info->irq = regmap_irq_get_virq(s5m87xx->irq_data, | ||
| 720 | S2MPS14_IRQ_RTCA0); | ||
| 721 | break; | ||
| 722 | |||
| 657 | case S5M8763X: | 723 | case S5M8763X: |
| 658 | info->irq = regmap_irq_get_virq(s5m87xx->irq_data, | 724 | info->irq = regmap_irq_get_virq(s5m87xx->irq_data, |
| 659 | S5M8763_IRQ_ALARM0); | 725 | S5M8763_IRQ_ALARM0); |
| @@ -768,7 +834,8 @@ static int s5m_rtc_suspend(struct device *dev) | |||
| 768 | static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); | 834 | static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); |
| 769 | 835 | ||
| 770 | static const struct platform_device_id s5m_rtc_id[] = { | 836 | static const struct platform_device_id s5m_rtc_id[] = { |
| 771 | { "s5m-rtc", 0 }, | 837 | { "s5m-rtc", S5M8767X }, |
| 838 | { "s2mps14-rtc", S2MPS14X }, | ||
| 772 | }; | 839 | }; |
| 773 | 840 | ||
| 774 | static struct platform_driver s5m_rtc_driver = { | 841 | static struct platform_driver s5m_rtc_driver = { |
| @@ -787,6 +854,6 @@ module_platform_driver(s5m_rtc_driver); | |||
| 787 | 854 | ||
| 788 | /* Module information */ | 855 | /* Module information */ |
| 789 | MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); | 856 | MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); |
| 790 | MODULE_DESCRIPTION("Samsung S5M RTC driver"); | 857 | MODULE_DESCRIPTION("Samsung S5M/S2MPS14 RTC driver"); |
| 791 | MODULE_LICENSE("GPL"); | 858 | MODULE_LICENSE("GPL"); |
| 792 | MODULE_ALIAS("platform:s5m-rtc"); | 859 | MODULE_ALIAS("platform:s5m-rtc"); |
