From 67b204258838dd2c940d5b1508318b4890324a06 Mon Sep 17 00:00:00 2001 From: christophe montaud Date: Tue, 29 Jan 2019 17:31:19 +0100 Subject: [PATCH 63/64] ARM stm32mp1 r0 rc4 hotfix w905.2 DRIVERS --- drivers/dma/stm32-dma.c | 70 ++++++++++++++++++++++++++++++++++++--------- drivers/usb/dwc2/platform.c | 38 ++++++++++++++++++------ 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 5abfa4f..dc3ba91 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -1675,37 +1675,81 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } +static bool stm32_dma_is_current_sg(struct stm32_dma_chan *chan) +{ + struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); + struct stm32_dma_sg_req *sg_req; + u32 dma_scr, dma_smar, id; + + id = chan->id; + dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id)); + + if (!(dma_scr & STM32_DMA_SCR_DBM)) + return true; + + sg_req = &chan->desc->sg_req[chan->next_sg]; + + if (dma_scr & STM32_DMA_SCR_CT) { + dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM0AR(id)); + return (dma_smar == sg_req->chan_reg.dma_sm0ar); + } + + dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM1AR(id)); + + return (dma_smar == sg_req->chan_reg.dma_sm1ar); +} + static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, struct stm32_dma_desc *desc, u32 next_sg) { u32 modulo, burst_size; - u32 residue = 0; + u32 residue; + u32 n_sg = next_sg; + struct stm32_dma_sg_req *sg_req = &chan->desc->sg_req[chan->next_sg]; int i; /* Drain case */ if (chan->residue_after_drain) return chan->residue_after_drain; + residue = stm32_dma_get_remaining_bytes(chan); + /* - * In cyclic mode, for the last period, residue = remaining bytes from - * NDTR + * Calculate the residue means compute the descriptors + * information: + * - the sg currently transferred + * - the remaining position in this sg (NDTR). + * + * The issue is that a race condition can occur if DMA is + * running. DMA can have started to transfer the next sg before + * the position in sg is read. In this case the remaing position + * can correspond to the new sg position. + * The strategy implemented in the stm32 driver is to check the + * sg transition. If detected we can not trust the SxNDTR register value + * this register can not be up to date during the transition. + * in this case we can assume that the dma is at the beginning of next + * sg so we calculate the residue in consequence. */ - if (chan->desc->cyclic && next_sg == 0) { - residue = stm32_dma_get_remaining_bytes(chan); - goto end; + + if (!stm32_dma_is_current_sg(chan)) { + n_sg++; + if (n_sg == chan->desc->num_sgs) + n_sg = 0; + residue = sg_dma_len(&sg_req->stm32_sgl_req); } /* - * For all other periods in cyclic mode, and in sg mode, - * residue = remaining bytes from NDTR + remaining periods/sg to be - * transferred + * In cyclic mode, for the last period, residue = remaining bytes + * from NDTR + * else for all other periods in cyclic mode, and in sg mode, + * residue = remaining bytes from NDTR + remaining + * periods/sg to be transferred */ - for (i = next_sg; i < desc->num_sgs; i++) - residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req); - residue += stm32_dma_get_remaining_bytes(chan); + if (!chan->desc->cyclic || n_sg != 0) + for (i = n_sg; i < desc->num_sgs; i++) + residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req); -end: if (!chan->mem_burst) return residue; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b80d046..7e6960c 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -568,16 +568,35 @@ static int __maybe_unused dwc2_suspend(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; + if (dwc2_is_device_mode(dwc2)) + dwc2_hsotg_suspend(dwc2); + if (dwc2->params.activate_stm_id_vb_detection && !dwc2->params.force_b_session_valid) { - u32 ggpio; + u32 ggpio, gotgctl; + int is_host = dwc2_is_host_mode(dwc2); /* * Need to force the mode to the current mode to avoid Mode - * Mismatch Interrupt when ID and VBUS detection will be - * disabled + * Mismatch Interrupt when ID detection will be disabled. */ - dwc2_force_mode(dwc2, dwc2_is_host_mode(dwc2)); + dwc2_force_mode(dwc2, is_host); + + if (!is_host) { + gotgctl = dwc2_readl(dwc2, GOTGCTL); + /* + * We're about to disable Vbus detection hw before low + * power mode entry. Then an undesired disconnect + * interrupt may occur which is racy with low power + * (low-level hw disable). Then check valid session + * to force B-peripheral session value. + */ + if (gotgctl & GOTGCTL_BSESVLD) { + gotgctl |= GOTGCTL_BVALOVAL; + gotgctl |= GOTGCTL_BVALOEN; + dwc2_writel(dwc2, gotgctl, GOTGCTL); + } + } ggpio = dwc2_readl(dwc2, GGPIO); ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; @@ -587,9 +606,6 @@ static int __maybe_unused dwc2_suspend(struct device *dev) regulator_disable(dwc2->usb33d); } - if (dwc2_is_device_mode(dwc2)) - dwc2_hsotg_suspend(dwc2); - if (dwc2->ll_hw_enabled) ret = __dwc2_lowlevel_hw_disable(dwc2); @@ -612,7 +628,7 @@ static int __maybe_unused dwc2_resume(struct device *dev) if (dwc2->params.activate_stm_id_vb_detection && !dwc2->params.force_b_session_valid) { - u32 ggpio; + u32 ggpio, gotgctl; ret = regulator_enable(dwc2->usb33d); if (ret) @@ -625,6 +641,12 @@ static int __maybe_unused dwc2_resume(struct device *dev) /* ID/VBUS detection startup time */ usleep_range(5000, 7000); + + /* Unconditionally clear B-Session Valid override */ + gotgctl = dwc2_readl(dwc2, GOTGCTL); + gotgctl &= ~GOTGCTL_BVALOVAL; + gotgctl &= ~GOTGCTL_BVALOEN; + dwc2_writel(dwc2, gotgctl, GOTGCTL); } if (dwc2->params.force_b_session_valid) { -- 2.7.4