| From: Bryan O'Donoghue <pure.logic@nexus-software.ie> |
| Date: Wed, 28 Mar 2018 20:14:05 +0100 |
| Subject: rtc: snvs: Fix usage of snvs_rtc_enable |
| |
| commit 1485991c024603b2fb4ae77beb7a0d741128a48e upstream. |
| |
| commit 179a502f8c46 ("rtc: snvs: add Freescale rtc-snvs driver") introduces |
| the SNVS RTC driver with a function snvs_rtc_enable(). |
| |
| snvs_rtc_enable() can return an error on the enable path however this |
| driver does not currently trap that failure on the probe() path and |
| consequently if enabling the RTC fails we encounter a later error spinning |
| forever in rtc_write_sync_lp(). |
| |
| [ 36.093481] [<c010d630>] (__irq_svc) from [<c0c2e9ec>] (_raw_spin_unlock_irqrestore+0x34/0x44) |
| [ 36.102122] [<c0c2e9ec>] (_raw_spin_unlock_irqrestore) from [<c072e32c>] (regmap_read+0x4c/0x5c) |
| [ 36.110938] [<c072e32c>] (regmap_read) from [<c085d0f4>] (rtc_write_sync_lp+0x6c/0x98) |
| [ 36.118881] [<c085d0f4>] (rtc_write_sync_lp) from [<c085d160>] (snvs_rtc_alarm_irq_enable+0x40/0x4c) |
| [ 36.128041] [<c085d160>] (snvs_rtc_alarm_irq_enable) from [<c08567b4>] (rtc_timer_do_work+0xd8/0x1a8) |
| [ 36.137291] [<c08567b4>] (rtc_timer_do_work) from [<c01441b8>] (process_one_work+0x28c/0x76c) |
| [ 36.145840] [<c01441b8>] (process_one_work) from [<c01446cc>] (worker_thread+0x34/0x58c) |
| [ 36.153961] [<c01446cc>] (worker_thread) from [<c014aee4>] (kthread+0x138/0x150) |
| [ 36.161388] [<c014aee4>] (kthread) from [<c0107e14>] (ret_from_fork+0x14/0x20) |
| [ 36.168635] rcu_sched kthread starved for 2602 jiffies! g496 c495 f0x2 RCU_GP_WAIT_FQS(3) ->state=0x0 ->cpu=0 |
| [ 36.178564] rcu_sched R running task 0 8 2 0x00000000 |
| [ 36.185664] [<c0c288b0>] (__schedule) from [<c0c29134>] (schedule+0x3c/0xa0) |
| [ 36.192739] [<c0c29134>] (schedule) from [<c0c2db80>] (schedule_timeout+0x78/0x4e0) |
| [ 36.200422] [<c0c2db80>] (schedule_timeout) from [<c01a7ab0>] (rcu_gp_kthread+0x648/0x1864) |
| [ 36.208800] [<c01a7ab0>] (rcu_gp_kthread) from [<c014aee4>] (kthread+0x138/0x150) |
| [ 36.216309] [<c014aee4>] (kthread) from [<c0107e14>] (ret_from_fork+0x14/0x20) |
| |
| This patch fixes by parsing the result of rtc_write_sync_lp() and |
| propagating both in the probe and elsewhere. If the RTC doesn't start we |
| don't proceed loading the driver and don't get into this loop mess later |
| on. |
| |
| Fixes: 179a502f8c46 ("rtc: snvs: add Freescale rtc-snvs driver") |
| Signed-off-by: Bryan O'Donoghue <pure.logic@nexus-software.ie> |
| Acked-by: Shawn Guo <shawn.guo@linaro.org> |
| Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> |
| [bwh: Backported to 3.16: |
| - No cleanup is needed on error in snvs_rtc_probe(); just return |
| - Adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/rtc/rtc-snvs.c | 15 +++++++++++---- |
| 1 file changed, 11 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/rtc/rtc-snvs.c |
| +++ b/drivers/rtc/rtc-snvs.c |
| @@ -130,20 +130,23 @@ static int snvs_rtc_set_time(struct devi |
| { |
| struct snvs_rtc_data *data = dev_get_drvdata(dev); |
| unsigned long time; |
| + int ret; |
| |
| rtc_tm_to_time(tm, &time); |
| |
| /* Disable RTC first */ |
| - snvs_rtc_enable(data, false); |
| + ret = snvs_rtc_enable(data, false); |
| + if (ret) |
| + return ret; |
| |
| /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ |
| writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR); |
| writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR); |
| |
| /* Enable RTC again */ |
| - snvs_rtc_enable(data, true); |
| + ret = snvs_rtc_enable(data, true); |
| |
| - return 0; |
| + return ret; |
| } |
| |
| static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| @@ -271,7 +274,11 @@ static int snvs_rtc_probe(struct platfor |
| writel(0xffffffff, data->ioaddr + SNVS_LPSR); |
| |
| /* Enable RTC */ |
| - snvs_rtc_enable(data, true); |
| + ret = snvs_rtc_enable(data, true); |
| + if (ret) { |
| + dev_err(&pdev->dev, "failed to enable rtc %d\n", ret); |
| + return ret; |
| + } |
| |
| device_init_wakeup(&pdev->dev, true); |
| |