2021-03-29 19:55:01

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v5 0/2] Polling for MHI ready

v5:
-Use fsleep in place of udelay or usleep_range to accommodate better delay use
-Drop patch for polling during RDDM panic path as new API cannot be used there

v4:
-Added reviewed-by tag
-Return appropriate error code from mhi_poll_reg_field()
-Fixed bug where mhi_poll_reg_field() returns success if polling times out
-Added an interval_us variable in mhi_ready_state_transition()

v3:
-Removed config changes that crept in in the first patch

v2:
-Addressed review comments
-Introduce new patch for to use controller defined read_reg() for polling
-Add usage in RDDM download panic path as well

Use polling instead of interrupt driven approach to wait for MHI ready state.

In certain devices, it is likely that there is no incoming MHI
interrupt for a transition to MHI READY state. One such example
is the move from Pass Through to an SBL or AMSS execution
environment. In order to facilitate faster bootup times as there
is no need to wait until timeout_ms completes, MHI host can poll
every 25 milliseconds to check if device has entered MHI READY
until a maximum timeout of twice the timeout_ms is reached.

This patch series has been tested on an arm64 device.

Bhaumik Bhatt (2):
bus: mhi: core: Introduce internal register poll helper function
bus: mhi: core: Move to polling method to wait for MHI ready

drivers/bus/mhi/core/internal.h | 3 +++
drivers/bus/mhi/core/main.c | 23 +++++++++++++++++++++++
drivers/bus/mhi/core/pm.c | 32 +++++++++++++++-----------------
3 files changed, 41 insertions(+), 17 deletions(-)

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2021-03-29 19:55:01

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v5 2/2] bus: mhi: core: Move to polling method to wait for MHI ready

In certain devices, it is likely that there is no incoming MHI
interrupt for a transition to MHI READY state. One such example
is the move from Pass Through to an SBL or AMSS execution
environment. In order to facilitate faster bootup times as there
is no need to wait until timeout_ms completes, MHI host can poll
every 25 milliseconds to check if device has entered MHI READY
until a maximum timeout of twice the timeout_ms is reached.

Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/pm.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c
index 681960c..dcc7fe0 100644
--- a/drivers/bus/mhi/core/pm.c
+++ b/drivers/bus/mhi/core/pm.c
@@ -153,34 +153,32 @@ static void mhi_toggle_dev_wake(struct mhi_controller *mhi_cntrl)
/* Handle device ready state transition */
int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
{
- void __iomem *base = mhi_cntrl->regs;
struct mhi_event *mhi_event;
enum mhi_pm_state cur_state;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
- u32 reset = 1, ready = 0;
+ u32 interval_us = 25000; /* poll register field every 25 milliseconds */
int ret, i;

- /* Wait for RESET to be cleared and READY bit to be set by the device */
- wait_event_timeout(mhi_cntrl->state_event,
- MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) ||
- mhi_read_reg_field(mhi_cntrl, base, MHICTRL,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT, &reset) ||
- mhi_read_reg_field(mhi_cntrl, base, MHISTATUS,
- MHISTATUS_READY_MASK,
- MHISTATUS_READY_SHIFT, &ready) ||
- (!reset && ready),
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
-
/* Check if device entered error state */
if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device link is not accessible\n");
return -EIO;
}

- /* Timeout if device did not transition to ready state */
- if (reset || !ready) {
- dev_err(dev, "Device Ready timeout\n");
+ /* Wait for RESET to be cleared and READY bit to be set by the device */
+ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+ MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
+ interval_us);
+ if (ret) {
+ dev_err(dev, "Device failed to clear MHI Reset\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
+ MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT, 1,
+ interval_us);
+ if (ret) {
+ dev_err(dev, "Device failed to enter MHI Ready\n");
return -ETIMEDOUT;
}

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2021-03-29 19:55:01

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v5 1/2] bus: mhi: core: Introduce internal register poll helper function

Introduce helper function to allow MHI core driver to poll for
a value in a register field. This helps reach a common path to
read and poll register values along with a retry time interval.

Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/internal.h | 3 +++
drivers/bus/mhi/core/main.c | 23 +++++++++++++++++++++++
2 files changed, 26 insertions(+)

diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
index 6f80ec3..005286b 100644
--- a/drivers/bus/mhi/core/internal.h
+++ b/drivers/bus/mhi/core/internal.h
@@ -643,6 +643,9 @@ int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 shift, u32 *out);
+int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset, u32 mask,
+ u32 shift, u32 val, u32 delayus);
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val);
void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 4e0131b..6f4b630 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -4,6 +4,7 @@
*
*/

+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
@@ -37,6 +38,28 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
return 0;
}

+int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
+ void __iomem *base, u32 offset,
+ u32 mask, u32 shift, u32 val, u32 delayus)
+{
+ int ret;
+ u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
+
+ while (retry--) {
+ ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, shift,
+ &out);
+ if (ret)
+ return ret;
+
+ if (out == val)
+ return 0;
+
+ fsleep(delayus);
+ }
+
+ return -ENOENT;
+}
+
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val)
{
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2021-03-31 13:05:55

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] bus: mhi: core: Introduce internal register poll helper function

On Mon, Mar 29, 2021 at 12:53:02PM -0700, Bhaumik Bhatt wrote:
> Introduce helper function to allow MHI core driver to poll for
> a value in a register field. This helps reach a common path to
> read and poll register values along with a retry time interval.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> ---
> drivers/bus/mhi/core/internal.h | 3 +++
> drivers/bus/mhi/core/main.c | 23 +++++++++++++++++++++++
> 2 files changed, 26 insertions(+)
>
> diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
> index 6f80ec3..005286b 100644
> --- a/drivers/bus/mhi/core/internal.h
> +++ b/drivers/bus/mhi/core/internal.h
> @@ -643,6 +643,9 @@ int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
> int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
> void __iomem *base, u32 offset, u32 mask,
> u32 shift, u32 *out);
> +int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
> + void __iomem *base, u32 offset, u32 mask,
> + u32 shift, u32 val, u32 delayus);
> void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
> u32 offset, u32 val);
> void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> index 4e0131b..6f4b630 100644
> --- a/drivers/bus/mhi/core/main.c
> +++ b/drivers/bus/mhi/core/main.c
> @@ -4,6 +4,7 @@
> *
> */
>
> +#include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/dma-direction.h>
> #include <linux/dma-mapping.h>
> @@ -37,6 +38,28 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
> return 0;
> }
>
> +int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
> + void __iomem *base, u32 offset,
> + u32 mask, u32 shift, u32 val, u32 delayus)
> +{
> + int ret;
> + u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
> +
> + while (retry--) {
> + ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, shift,
> + &out);
> + if (ret)
> + return ret;
> +
> + if (out == val)
> + return 0;
> +
> + fsleep(delayus);
> + }
> +
> + return -ENOENT;

Maybe I'm too late on this one, but I don't think -ENOENT is the correct
error code here. The error code will be returned only when the reg field
value didn't change as expected, so in that case it should be -EINVAL or
-ETIMEDOUT, no?

Thanks,
Mani

> +}
> +
> void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
> u32 offset, u32 val)
> {
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

2021-03-31 13:06:44

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v5 2/2] bus: mhi: core: Move to polling method to wait for MHI ready

On Mon, Mar 29, 2021 at 12:53:03PM -0700, Bhaumik Bhatt wrote:
> In certain devices, it is likely that there is no incoming MHI
> interrupt for a transition to MHI READY state. One such example
> is the move from Pass Through to an SBL or AMSS execution
> environment. In order to facilitate faster bootup times as there
> is no need to wait until timeout_ms completes, MHI host can poll
> every 25 milliseconds to check if device has entered MHI READY
> until a maximum timeout of twice the timeout_ms is reached.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> ---
> drivers/bus/mhi/core/pm.c | 32 +++++++++++++++-----------------
> 1 file changed, 15 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c
> index 681960c..dcc7fe0 100644
> --- a/drivers/bus/mhi/core/pm.c
> +++ b/drivers/bus/mhi/core/pm.c
> @@ -153,34 +153,32 @@ static void mhi_toggle_dev_wake(struct mhi_controller *mhi_cntrl)
> /* Handle device ready state transition */
> int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
> {
> - void __iomem *base = mhi_cntrl->regs;
> struct mhi_event *mhi_event;
> enum mhi_pm_state cur_state;
> struct device *dev = &mhi_cntrl->mhi_dev->dev;
> - u32 reset = 1, ready = 0;
> + u32 interval_us = 25000; /* poll register field every 25 milliseconds */
> int ret, i;
>
> - /* Wait for RESET to be cleared and READY bit to be set by the device */
> - wait_event_timeout(mhi_cntrl->state_event,
> - MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) ||
> - mhi_read_reg_field(mhi_cntrl, base, MHICTRL,
> - MHICTRL_RESET_MASK,
> - MHICTRL_RESET_SHIFT, &reset) ||
> - mhi_read_reg_field(mhi_cntrl, base, MHISTATUS,
> - MHISTATUS_READY_MASK,
> - MHISTATUS_READY_SHIFT, &ready) ||
> - (!reset && ready),
> - msecs_to_jiffies(mhi_cntrl->timeout_ms));
> -
> /* Check if device entered error state */
> if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
> dev_err(dev, "Device link is not accessible\n");
> return -EIO;
> }
>
> - /* Timeout if device did not transition to ready state */
> - if (reset || !ready) {
> - dev_err(dev, "Device Ready timeout\n");
> + /* Wait for RESET to be cleared and READY bit to be set by the device */
> + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
> + MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
> + interval_us);
> + if (ret) {
> + dev_err(dev, "Device failed to clear MHI Reset\n");
> + return -ETIMEDOUT;

You should return the actual error code since there are couple of error
paths.

Thanks,
Mani

> + }
> +
> + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
> + MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT, 1,
> + interval_us);
> + if (ret) {
> + dev_err(dev, "Device failed to enter MHI Ready\n");
> return -ETIMEDOUT;
> }
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>

2021-03-31 17:11:27

by Bhaumik Bhatt

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] bus: mhi: core: Introduce internal register poll helper function

Hi Mani,

On 2021-03-31 06:03 AM, Manivannan Sadhasivam wrote:
> On Mon, Mar 29, 2021 at 12:53:02PM -0700, Bhaumik Bhatt wrote:
>> Introduce helper function to allow MHI core driver to poll for
>> a value in a register field. This helps reach a common path to
>> read and poll register values along with a retry time interval.
>>
>> Signed-off-by: Bhaumik Bhatt <[email protected]>
>> ---
>> drivers/bus/mhi/core/internal.h | 3 +++
>> drivers/bus/mhi/core/main.c | 23 +++++++++++++++++++++++
>> 2 files changed, 26 insertions(+)
>>
>> diff --git a/drivers/bus/mhi/core/internal.h
>> b/drivers/bus/mhi/core/internal.h
>> index 6f80ec3..005286b 100644
>> --- a/drivers/bus/mhi/core/internal.h
>> +++ b/drivers/bus/mhi/core/internal.h
>> @@ -643,6 +643,9 @@ int __must_check mhi_read_reg(struct
>> mhi_controller *mhi_cntrl,
>> int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
>> void __iomem *base, u32 offset, u32 mask,
>> u32 shift, u32 *out);
>> +int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
>> + void __iomem *base, u32 offset, u32 mask,
>> + u32 shift, u32 val, u32 delayus);
>> void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem
>> *base,
>> u32 offset, u32 val);
>> void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void
>> __iomem *base,
>> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
>> index 4e0131b..6f4b630 100644
>> --- a/drivers/bus/mhi/core/main.c
>> +++ b/drivers/bus/mhi/core/main.c
>> @@ -4,6 +4,7 @@
>> *
>> */
>>
>> +#include <linux/delay.h>
>> #include <linux/device.h>
>> #include <linux/dma-direction.h>
>> #include <linux/dma-mapping.h>
>> @@ -37,6 +38,28 @@ int __must_check mhi_read_reg_field(struct
>> mhi_controller *mhi_cntrl,
>> return 0;
>> }
>>
>> +int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
>> + void __iomem *base, u32 offset,
>> + u32 mask, u32 shift, u32 val, u32 delayus)
>> +{
>> + int ret;
>> + u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
>> +
>> + while (retry--) {
>> + ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, shift,
>> + &out);
>> + if (ret)
>> + return ret;
>> +
>> + if (out == val)
>> + return 0;
>> +
>> + fsleep(delayus);
>> + }
>> +
>> + return -ENOENT;
>
> Maybe I'm too late on this one, but I don't think -ENOENT is the
> correct
> error code here. The error code will be returned only when the reg
> field
> value didn't change as expected, so in that case it should be -EINVAL
> or
> -ETIMEDOUT, no?
>
> Thanks,
> Mani
>

Thanks for pointing that out.

The intention of the error code was despite polling for whatever time
period,
we were unable to see the value changing as expected. I think the
-ETIMEDOUT
error code would be appropriate. Will upload a v6.

>> +}
>> +
>> void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem
>> *base,
>> u32 offset, u32 val)
>> {
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>> Forum,
>> a Linux Foundation Collaborative Project
>>

Thanks,
Bhaumik
---
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum,
a Linux Foundation Collaborative Project

2021-03-31 17:28:14

by Bhaumik Bhatt

[permalink] [raw]
Subject: Re: [PATCH v5 2/2] bus: mhi: core: Move to polling method to wait for MHI ready

On 2021-03-31 06:04 AM, Manivannan Sadhasivam wrote:
> On Mon, Mar 29, 2021 at 12:53:03PM -0700, Bhaumik Bhatt wrote:
>> In certain devices, it is likely that there is no incoming MHI
>> interrupt for a transition to MHI READY state. One such example
>> is the move from Pass Through to an SBL or AMSS execution
>> environment. In order to facilitate faster bootup times as there
>> is no need to wait until timeout_ms completes, MHI host can poll
>> every 25 milliseconds to check if device has entered MHI READY
>> until a maximum timeout of twice the timeout_ms is reached.
>>
>> Signed-off-by: Bhaumik Bhatt <[email protected]>
>> ---
>> drivers/bus/mhi/core/pm.c | 32 +++++++++++++++-----------------
>> 1 file changed, 15 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c
>> index 681960c..dcc7fe0 100644
>> --- a/drivers/bus/mhi/core/pm.c
>> +++ b/drivers/bus/mhi/core/pm.c
>> @@ -153,34 +153,32 @@ static void mhi_toggle_dev_wake(struct
>> mhi_controller *mhi_cntrl)
>> /* Handle device ready state transition */
>> int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
>> {
>> - void __iomem *base = mhi_cntrl->regs;
>> struct mhi_event *mhi_event;
>> enum mhi_pm_state cur_state;
>> struct device *dev = &mhi_cntrl->mhi_dev->dev;
>> - u32 reset = 1, ready = 0;
>> + u32 interval_us = 25000; /* poll register field every 25
>> milliseconds */
>> int ret, i;
>>
>> - /* Wait for RESET to be cleared and READY bit to be set by the
>> device */
>> - wait_event_timeout(mhi_cntrl->state_event,
>> - MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) ||
>> - mhi_read_reg_field(mhi_cntrl, base, MHICTRL,
>> - MHICTRL_RESET_MASK,
>> - MHICTRL_RESET_SHIFT, &reset) ||
>> - mhi_read_reg_field(mhi_cntrl, base, MHISTATUS,
>> - MHISTATUS_READY_MASK,
>> - MHISTATUS_READY_SHIFT, &ready) ||
>> - (!reset && ready),
>> - msecs_to_jiffies(mhi_cntrl->timeout_ms));
>> -
>> /* Check if device entered error state */
>> if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
>> dev_err(dev, "Device link is not accessible\n");
>> return -EIO;
>> }
>>
>> - /* Timeout if device did not transition to ready state */
>> - if (reset || !ready) {
>> - dev_err(dev, "Device Ready timeout\n");
>> + /* Wait for RESET to be cleared and READY bit to be set by the
>> device */
>> + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
>> + MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0,
>> + interval_us);
>> + if (ret) {
>> + dev_err(dev, "Device failed to clear MHI Reset\n");
>> + return -ETIMEDOUT;
>
> You should return the actual error code since there are couple of error
> paths.
>
> Thanks,
> Mani
>
Sure. With the update on patch #1, this will be taken care of properly
as we
return -ETIMEDOUT from the API.
>> + }
>> +
>> + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
>> + MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT, 1,
>> + interval_us);
>> + if (ret) {
>> + dev_err(dev, "Device failed to enter MHI Ready\n");
>> return -ETIMEDOUT;
>> }
>>
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>> Forum,
>> a Linux Foundation Collaborative Project
>>

Thanks,
Bhaumik
---
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum,
a Linux Foundation Collaborative Project