]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
patches for 4.9
authorSasha Levin <sashal@kernel.org>
Thu, 20 Dec 2018 02:32:11 +0000 (21:32 -0500)
committerSasha Levin <sashal@kernel.org>
Thu, 20 Dec 2018 02:32:11 +0000 (21:32 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.9/rtc-snvs-add-a-missing-write-sync.patch [new file with mode: 0644]
queue-4.9/rtc-snvs-add-timeouts-to-avoid-kernel-lockups.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/rtc-snvs-add-a-missing-write-sync.patch b/queue-4.9/rtc-snvs-add-a-missing-write-sync.patch
new file mode 100644 (file)
index 0000000..5bb75e6
--- /dev/null
@@ -0,0 +1,33 @@
+From a263a4d94c1de650f1dce24d43da71ad8602f212 Mon Sep 17 00:00:00 2001
+From: Guy Shapiro <guy.shapiro@mobi-wize.com>
+Date: Sun, 29 Jan 2017 11:57:19 +0200
+Subject: rtc: snvs: add a missing write sync
+
+[ Upstream commit 7bb633b1a9812a6b9f3e49d0cf17f60a633914e5 ]
+
+The clear of the LPTA_EN flag should be synced before writing to the
+alarm register. Omitting this synchronization creates a race when
+trying to change existing alarm.
+
+Signed-off-by: Guy Shapiro <guy.shapiro@mobi-wize.com>
+Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/rtc/rtc-snvs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
+index 3e8fd33c2576..9af591d5223c 100644
+--- a/drivers/rtc/rtc-snvs.c
++++ b/drivers/rtc/rtc-snvs.c
+@@ -187,6 +187,7 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+       rtc_tm_to_time(alrm_tm, &time);
+       regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
++      rtc_write_sync_lp(data);
+       regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
+       /* Clear alarm interrupt status bit */
+-- 
+2.19.1
+
diff --git a/queue-4.9/rtc-snvs-add-timeouts-to-avoid-kernel-lockups.patch b/queue-4.9/rtc-snvs-add-timeouts-to-avoid-kernel-lockups.patch
new file mode 100644 (file)
index 0000000..9c3a023
--- /dev/null
@@ -0,0 +1,196 @@
+From 47c3f039c63962d8a01ae90bd7faf9d7f750fcfd Mon Sep 17 00:00:00 2001
+From: Trent Piepho <tpiepho@impinj.com>
+Date: Wed, 16 May 2018 16:45:51 -0700
+Subject: rtc: snvs: Add timeouts to avoid kernel lockups
+
+[ Upstream commit cd7f3a249dbed2858e6c2f30e5be7f1f7a709ee2 ]
+
+In order to read correctly from asynchronously updated RTC registers,
+it's necessary to read repeatedly until their values do not change from
+read to read.  It's also necessary to wait for three RTC clock ticks for
+certain operations.  There are no timeouts in this code and these
+operations could possibly loop forever.
+
+To avoid kernel hangs, put in timeouts.
+
+The iMX7d can be configured to stop the SRTC on a tamper event, which
+will lockup the kernel inside this driver as described above.
+
+These hangs can happen when running under qemu, which doesn't emulate
+the SNVS RTC, though currently the driver will refuse to load on qemu
+due to a timeout in the driver probe method.
+
+It could also happen if the SRTC block where somehow placed into reset
+or the slow speed clock that drives the SRTC counter (but not the CPU)
+were to stop.
+
+The symptoms on a two core iMX7d are a work queue hang on
+rtc_timer_do_work(), which eventually blocks a systemd fsnotify
+operation that triggers a work queue flush, causing systemd to hang and
+thus causing all services that should be started by systemd, like a
+console getty, to fail to start or stop.
+
+Also optimize the wait code to wait less.  It only needs to wait for the
+clock to advance three ticks, not to see it change three times.
+
+Cc: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+Cc: Alessandro Zummo <a.zummo@towertech.it>
+Cc: Fabio Estevam <fabio.estevam@nxp.com>
+Cc: Shawn Guo <shawn.guo@linaro.org>
+Cc: Bryan O'Donoghue <pure.logic@nexus-software.ie>
+Signed-off-by: Trent Piepho <tpiepho@impinj.com>
+Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/rtc/rtc-snvs.c | 105 +++++++++++++++++++++++++++--------------
+ 1 file changed, 70 insertions(+), 35 deletions(-)
+
+diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
+index 9af591d5223c..71eee39520f0 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,11 +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);
+-      rtc_write_sync_lp(data);
++      ret = rtc_write_sync_lp(data);
++      if (ret)
++              return ret;
+       regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
+       /* Clear alarm interrupt status bit */
+-- 
+2.19.1
+
index 69ddb1cf774735a1271c45070296956ee5263687..70b55d160e7edf1be284c47718605934de387ab9 100644 (file)
@@ -57,3 +57,5 @@ vhost-vsock-fix-reset-orphans-race-with-close-timeou.patch
 i2c-axxia-properly-handle-master-timeout.patch
 i2c-scmi-fix-probe-error-on-devices-with-an-empty-sm.patch
 nvmet-rdma-fix-response-use-after-free.patch
+rtc-snvs-add-a-missing-write-sync.patch
+rtc-snvs-add-timeouts-to-avoid-kernel-lockups.patch