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/rtc-s5m.c | |
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/rtc-s5m.c')
-rw-r--r-- | drivers/rtc/rtc-s5m.c | 101 |
1 files changed, 84 insertions, 17 deletions
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"); |