diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-20 21:12:06 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-20 21:12:06 +0300 |
commit | f4a2f7866faaf89ea1595b136e01fcb336b46aab (patch) | |
tree | 9215bb0d94c16afd9f4949c356d7e12b7ac03bbd /drivers/rtc/rtc-at91rm9200.c | |
parent | 7703f46f2cafa57b9e5e8f4d6ff3dd8dcf0c1656 (diff) | |
parent | 71ac13457d9d1007effde65b54818106b2c2b525 (diff) | |
download | linux-f4a2f7866faaf89ea1595b136e01fcb336b46aab.tar.xz |
Merge tag 'rtc-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni:
"Subsystem:
- Remove nvram ABI. There was no complaints about the deprecation for
the last 3 years.
- Improve RTC device allocation and registration
- Now available for ARCH=um
Drivers:
- at91rm9200: correction and sam9x60 support
- ds1307: improve ACPI support
- mxc: now DT only
- pcf2127: watchdog support now needs the reset-source property
- pcf8523: set range
- rx6110: i2c support"
* tag 'rtc-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (43 commits)
rtc: pcf2127: only use watchdog when explicitly available
dt-bindings: rtc: add reset-source property
rtc: fix RTC removal
rtc: s3c: Remove dead code related to periodic tick handling
rtc: s3c: Disable all enable (RTC, tick) bits in the probe
rtc: ep93xx: Fix NULL pointer dereference in ep93xx_rtc_read_time
rtc: test: remove debug message
rtc: mxc{,_v2}: enable COMPILE_TEST
rtc: enable RTC framework on ARCH=um
rtc: pcf8523: use BIT
rtc: pcf8523: set range
rtc: pcf8523: switch to devm_rtc_allocate_device
rtc: destroy mutex when releasing the device
rtc: shrink devm_rtc_allocate_device()
rtc: rework rtc_register_device() resource management
rtc: nvmem: emit an error message when nvmem registration fails
rtc: add devm_ prefix to rtc_nvmem_register()
rtc: nvmem: remove nvram ABI
Documentation: list RTC devres helpers in devres.rst
rtc: omap: use devm_pinctrl_register()
...
Diffstat (limited to 'drivers/rtc/rtc-at91rm9200.c')
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 108 |
1 files changed, 103 insertions, 5 deletions
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 5e811e04cb21..fe396d27ebb7 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -36,6 +36,10 @@ #define AT91_RTC_UPDCAL BIT(1) /* Update Request Calendar Register */ #define AT91_RTC_MR 0x04 /* Mode Register */ +#define AT91_RTC_HRMOD BIT(0) /* 12/24 hour mode */ +#define AT91_RTC_NEGPPM BIT(4) /* Negative PPM correction */ +#define AT91_RTC_CORRECTION GENMASK(14, 8) /* Slow clock correction */ +#define AT91_RTC_HIGHPPM BIT(15) /* High PPM correction */ #define AT91_RTC_TIMR 0x08 /* Time Register */ #define AT91_RTC_SEC GENMASK(6, 0) /* Current Second */ @@ -77,6 +81,9 @@ #define AT91_RTC_NVTIMALR BIT(2) /* Non valid Time Alarm */ #define AT91_RTC_NVCALALR BIT(3) /* Non valid Calendar Alarm */ +#define AT91_RTC_CORR_DIVIDEND 3906000 +#define AT91_RTC_CORR_LOW_RATIO 20 + #define at91_rtc_read(field) \ readl_relaxed(at91_rtc_regs + field) #define at91_rtc_write(field, val) \ @@ -84,6 +91,7 @@ struct at91_rtc_config { bool use_shadow_imr; + bool has_correction; }; static const struct at91_rtc_config *at91_rtc_config; @@ -293,6 +301,75 @@ static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } +static int at91_rtc_readoffset(struct device *dev, long *offset) +{ + u32 mr = at91_rtc_read(AT91_RTC_MR); + long val = FIELD_GET(AT91_RTC_CORRECTION, mr); + + if (!val) { + *offset = 0; + return 0; + } + + val++; + + if (!(mr & AT91_RTC_NEGPPM)) + val = -val; + + if (!(mr & AT91_RTC_HIGHPPM)) + val *= AT91_RTC_CORR_LOW_RATIO; + + *offset = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, val); + + return 0; +} + +static int at91_rtc_setoffset(struct device *dev, long offset) +{ + long corr; + u32 mr; + + if (offset > AT91_RTC_CORR_DIVIDEND / 2) + return -ERANGE; + if (offset < -AT91_RTC_CORR_DIVIDEND / 2) + return -ERANGE; + + mr = at91_rtc_read(AT91_RTC_MR); + mr &= ~(AT91_RTC_NEGPPM | AT91_RTC_CORRECTION | AT91_RTC_HIGHPPM); + + if (offset > 0) + mr |= AT91_RTC_NEGPPM; + else + offset = -offset; + + /* offset less than 764 ppb, disable correction*/ + if (offset < 764) { + at91_rtc_write(AT91_RTC_MR, mr & ~AT91_RTC_NEGPPM); + + return 0; + } + + /* + * 29208 ppb is the perfect cutoff between low range and high range + * low range values are never better than high range value after that. + */ + if (offset < 29208) { + corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset * AT91_RTC_CORR_LOW_RATIO); + } else { + corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset); + mr |= AT91_RTC_HIGHPPM; + } + + if (corr > 128) + corr = 128; + + mr |= FIELD_PREP(AT91_RTC_CORRECTION, corr - 1); + + at91_rtc_write(AT91_RTC_MR, mr); + + return 0; +} + /* * IRQ handler for the RTC */ @@ -343,6 +420,10 @@ static const struct at91_rtc_config at91sam9x5_config = { .use_shadow_imr = true, }; +static const struct at91_rtc_config sama5d4_config = { + .has_correction = true, +}; + static const struct of_device_id at91_rtc_dt_ids[] = { { .compatible = "atmel,at91rm9200-rtc", @@ -352,10 +433,13 @@ static const struct of_device_id at91_rtc_dt_ids[] = { .data = &at91sam9x5_config, }, { .compatible = "atmel,sama5d4-rtc", - .data = &at91rm9200_config, + .data = &sama5d4_config, }, { .compatible = "atmel,sama5d2-rtc", - .data = &at91rm9200_config, + .data = &sama5d4_config, + }, { + .compatible = "microchip,sam9x60-rtc", + .data = &sama5d4_config, }, { /* sentinel */ } @@ -370,6 +454,16 @@ static const struct rtc_class_ops at91_rtc_ops = { .alarm_irq_enable = at91_rtc_alarm_irq_enable, }; +static const struct rtc_class_ops sama5d4_rtc_ops = { + .read_time = at91_rtc_readtime, + .set_time = at91_rtc_settime, + .read_alarm = at91_rtc_readalarm, + .set_alarm = at91_rtc_setalarm, + .alarm_irq_enable = at91_rtc_alarm_irq_enable, + .set_offset = at91_rtc_setoffset, + .read_offset = at91_rtc_readoffset, +}; + /* * Initialize and install RTC driver */ @@ -416,7 +510,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) } at91_rtc_write(AT91_RTC_CR, 0); - at91_rtc_write(AT91_RTC_MR, 0); /* 24 hour mode */ + at91_rtc_write(AT91_RTC_MR, at91_rtc_read(AT91_RTC_MR) & ~AT91_RTC_HRMOD); /* Disable all interrupts */ at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM | @@ -437,10 +531,14 @@ static int __init at91_rtc_probe(struct platform_device *pdev) if (!device_can_wakeup(&pdev->dev)) device_init_wakeup(&pdev->dev, 1); - rtc->ops = &at91_rtc_ops; + if (at91_rtc_config->has_correction) + rtc->ops = &sama5d4_rtc_ops; + else + rtc->ops = &at91_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; rtc->range_max = RTC_TIMESTAMP_END_2099; - ret = rtc_register_device(rtc); + ret = devm_rtc_register_device(rtc); if (ret) goto err_clk; |