diff options
author | Greg Kroah-Hartman <gregkh@google.com> | 2018-12-21 14:28:48 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2018-12-21 14:28:48 +0100 |
commit | dfca92bab267c629c7aff059de9217d2fb1ab21e (patch) | |
tree | ed540c9a30e53b5459290ba622d9306be9714be8 /drivers/rtc | |
parent | 34a4d7ffc21bafe8af1e39eae951e3255f2b1c37 (diff) | |
parent | ba0da581b466dcd333eeb326919a698116926258 (diff) |
Merge 4.4.169 into android-4.4
Changes in 4.4.169
lib/interval_tree_test.c: make test options module parameters
lib/interval_tree_test.c: allow full tree search
lib/rbtree_test.c: make input module parameters
lib/rbtree-test: lower default params
lib/interval_tree_test.c: allow users to limit scope of endpoint
timer/debug: Change /proc/timer_list from 0444 to 0400
powerpc/boot: Fix random libfdt related build errors
pinctrl: sunxi: a83t: Fix IRQ offset typo for PH11
aio: fix spectre gadget in lookup_ioctx
MMC: OMAP: fix broken MMC on OMAP15XX/OMAP5910/OMAP310
tracing: Fix memory leak in set_trigger_filter()
tracing: Fix memory leak of instance function hash filters
powerpc/msi: Fix NULL pointer access in teardown code
Revert "drm/rockchip: Allow driver to be shutdown on reboot/kexec"
f2fs: fix a panic caused by NULL flush_cmd_control
mac80211: don't WARN on bad WMM parameters from buggy APs
mac80211: Fix condition validating WMM IE
mac80211_hwsim: fix module init error paths for netlink
scsi: libiscsi: Fix NULL pointer dereference in iscsi_eh_session_reset
scsi: vmw_pscsi: Rearrange code to avoid multiple calls to free_irq during unload
x86/earlyprintk/efi: Fix infinite loop on some screen widths
drm/msm: Grab a vblank reference when waiting for commit_done
ARC: io.h: Implement reads{x}()/writes{x}()
bonding: fix 802.3ad state sent to partner when unbinding slave
SUNRPC: Fix a potential race in xprt_connect()
sbus: char: add of_node_put()
drivers/sbus/char: add of_node_put()
drivers/tty: add missing of_node_put()
ide: pmac: add of_node_put()
clk: mmp: Off by one in mmp_clk_add()
Input: omap-keypad - fix keyboard debounce configuration
libata: whitelist all SAMSUNG MZ7KM* solid-state disks
mv88e6060: disable hardware level MAC learning
ARM: 8814/1: mm: improve/fix ARM v7_dma_inv_range() unaligned address handling
cifs: In Kconfig CONFIG_CIFS_POSIX needs depends on legacy (insecure cifs)
i2c: axxia: properly handle master timeout
i2c: scmi: Fix probe error on devices with an empty SMB0001 ACPI device node
rtc: snvs: add a missing write sync
rtc: snvs: Add timeouts to avoid kernel lockups
ALSA: isa/wavefront: prevent some out of bound writes
Linux 4.4.169
Change-Id: Ic1bde8f64625c7787feb8e7411b15e70a2f76981
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-snvs.c | 104 |
1 files changed, 70 insertions, 34 deletions
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index a161fbf6f172..63ad5b543f14 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -47,49 +47,83 @@ struct snvs_rtc_data { struct clk *clk; }; +/* Read 64 bit timer register, which could be in inconsistent state */ +static u64 rtc_read_lpsrt(struct snvs_rtc_data *data) +{ + u32 msb, lsb; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb); + return (u64)msb << 32 | lsb; +} + +/* Read the secure real time counter, taking care to deal with the cases of the + * counter updating while being read. + */ static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) { u64 read1, read2; - u32 val; + unsigned int timeout = 100; + /* As expected, the registers might update between the read of the LSB + * reg and the MSB reg. It's also possible that one register might be + * in partially modified state as well. + */ + read1 = rtc_read_lpsrt(data); do { - regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); - read1 = val; - read1 <<= 32; - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); - read1 |= val; - - regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); - read2 = val; - read2 <<= 32; - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); - read2 |= val; - } while (read1 != read2); + read2 = read1; + read1 = rtc_read_lpsrt(data); + } while (read1 != read2 && --timeout); + if (!timeout) + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); /* Convert 47-bit counter to 32-bit raw second count */ return (u32) (read1 >> CNTR_TO_SECS_SH); } -static void rtc_write_sync_lp(struct snvs_rtc_data *data) +/* Just read the lsb from the counter, dealing with inconsistent state */ +static int rtc_read_lp_counter_lsb(struct snvs_rtc_data *data, u32 *lsb) { - u32 count1, count2, count3; - int i; - - /* Wait for 3 CKIL cycles */ - for (i = 0; i < 3; i++) { - do { - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); - } while (count1 != count2); - - /* Now wait until counter value changes */ - do { - do { - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); - regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3); - } while (count2 != count3); - } while (count3 == count1); + u32 count1, count2; + unsigned int timeout = 100; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + do { + count2 = count1; + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + } while (count1 != count2 && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); + return -ETIMEDOUT; } + + *lsb = count1; + return 0; +} + +static int rtc_write_sync_lp(struct snvs_rtc_data *data) +{ + u32 count1, count2; + u32 elapsed; + unsigned int timeout = 1000; + int ret; + + ret = rtc_read_lp_counter_lsb(data, &count1); + if (ret) + return ret; + + /* Wait for 3 CKIL cycles, about 61.0-91.5 µs */ + do { + ret = rtc_read_lp_counter_lsb(data, &count2); + if (ret) + return ret; + elapsed = count2 - count1; /* wrap around _is_ handled! */ + } while (elapsed < 3 && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout waiting for LPSRT Counter to change\n"); + return -ETIMEDOUT; + } + return 0; } static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) @@ -173,9 +207,7 @@ static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN), enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0); - rtc_write_sync_lp(data); - - return 0; + return rtc_write_sync_lp(data); } static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -183,10 +215,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct snvs_rtc_data *data = dev_get_drvdata(dev); struct rtc_time *alrm_tm = &alrm->time; unsigned long time; + int ret; rtc_tm_to_time(alrm_tm, &time); regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); + ret = rtc_write_sync_lp(data); + if (ret) + return ret; regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); /* Clear alarm interrupt status bit */ |