<br><br>On Saturday, May 8, 2021, Boris Brezillon <<a href="mailto:boris.brezillon@collabora.com">boris.brezillon@collabora.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Fri, 7 May 2021 15:17:54 +0200<br>
<<a href="mailto:patrice.chotard@foss.st.com">patrice.chotard@foss.st.com</a>> wrote:<br>
<br>
> From: Patrice Chotard <<a href="mailto:patrice.chotard@foss.st.com">patrice.chotard@foss.st.com</a>><br>
> <br>
> With STM32 QSPI, it is possible to poll the status register of the device.<br>
> This could be done to offload the CPU during an operation (erase or<br>
> program a SPI NAND for example).<br>
> <br>
> spi_mem_poll_status API has been added to handle this feature.<br>
> This new function take care of the offload/non-offload cases.<br>
> <br>
> For the non-offload case, use read_poll_timeout() to poll the status in<br>
> order to release CPU during this phase.<br>
> <br>
> Signed-off-by: Patrice Chotard <<a href="mailto:patrice.chotard@foss.st.com">patrice.chotard@foss.st.com</a>><br>
> Signed-off-by: Christophe Kerello <<a href="mailto:christophe.kerello@foss.st.com">christophe.kerello@foss.st.<wbr>com</a>><br>
> ---<br>
> Changes in v2:<br>
>   - Indicates the spi_mem_poll_status() timeout unit<br>
>   - Use 2-byte wide status register<br>
>   - Add spi_mem_supports_op() call in spi_mem_poll_status()<br>
>   - Add completion management in spi_mem_poll_status()<br>
>   - Add offload/non-offload case mangement in spi_mem_poll_status()<br>
>   - Optimize the non-offload case by using read_poll_timeout()<br>
> <br>
>  drivers/spi/spi-mem.c       | 71 ++++++++++++++++++++++++++++++<wbr>+++++++<br>
>  include/linux/spi/spi-mem.h | 10 ++++++<br>
>  2 files changed, 81 insertions(+)<br>
> <br>
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c<br>
> index 1513553e4080..3f29c604df7d 100644<br>
> --- a/drivers/spi/spi-mem.c<br>
> +++ b/drivers/spi/spi-mem.c<br>
> @@ -6,6 +6,7 @@<br>
>   * Author: Boris Brezillon <<a href="mailto:boris.brezillon@bootlin.com">boris.brezillon@bootlin.com</a>><br>
>   */<br>
>  #include <linux/dmaengine.h><br>
> +#include <linux/iopoll.h><br>
>  #include <linux/pm_runtime.h><br>
>  #include <linux/spi/spi.h><br>
>  #include <linux/spi/spi-mem.h><br>
> @@ -743,6 +744,75 @@ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)<br>
>       return container_of(drv, struct spi_mem_driver, spidrv.driver);<br>
>  }<br>
>  <br>
> +/**<br>
> + * spi_mem_finalize_op - report completion of spi_mem_op<br>
> + * @ctlr: the controller reporting completion<br>
> + *<br>
> + * Called by SPI drivers using the spi-mem spi_mem_poll_status()<br>
> + * implementation to notify it that the current spi_mem_op has<br>
> + * finished.<br>
> + */<br>
> +void spi_mem_finalize_op(struct spi_controller *ctlr)<br>
> +{<br>
> +     complete(&ctlr->xfer_<wbr>completion);<br>
> +}<br>
> +EXPORT_SYMBOL_GPL(spi_mem_<wbr>finalize_op);<br>
> +<br>
> +/**<br>
> + * spi_mem_poll_status() - Poll memory device status<br>
> + * @mem: SPI memory device<br>
> + * @op: the memory operation to execute<br>
> + * @mask: status bitmask to ckeck<br>
> + * @match: (status & mask) expected value<br>
> + * @timeout_ms: timeout in milliseconds<br>
> + *<br>
> + * This function send a polling status request to the controller driver<br>
> + *<br>
> + * Return: 0 in case of success, -ETIMEDOUT in case of error,<br>
> + *         -EOPNOTSUPP if not supported.<br>
> + */<br>
> +int spi_mem_poll_status(struct spi_mem *mem,<br>
> +                     const struct spi_mem_op *op,<br>
> +                     u16 mask, u16 match, u16 timeout_ms)<br>
> +{<br>
> +     struct spi_controller *ctlr = mem->spi->controller;<br>
> +     unsigned long ms;<br>
> +     int ret = -EOPNOTSUPP;<br>
> +     int exec_op_ret;<br>
> +     u16 *status;<br>
> +<br>
> +     if (!spi_mem_supports_op(mem, op))<br>
> +             return ret;<br>
<br>
You should only test that in the SW-based polling path. The driver<br>
->poll_status() method is expected to check the operation and<br>
return -EOPNOTSUPP if HW-based polling doesn't work for this op,<br>
no need to check things twice.<br>
<br>
> +<br>
> +     if (ctlr->mem_ops && ctlr->mem_ops->poll_status) {<br>
> +             ret = spi_mem_access_start(mem);<br>
> +             if (ret)<br>
> +                     return ret;<br>
> +<br>
> +             reinit_completion(&ctlr->xfer_<wbr>completion);<br>
> +<br>
> +             ret = ctlr->mem_ops->poll_status(<wbr>mem, op, mask, match,<br>
> +                                              timeout_ms);<br>
> +<br>
> +             ms = wait_for_completion_timeout(&<wbr>ctlr->xfer_completion,<br>
> +                                              msecs_to_jiffies(timeout_ms));<br>
<br>
Why do you need to wait here? I'd expect the poll_status to take care<br>
of this wait.<br>
<br>
> +<br>
> +             spi_mem_access_end(mem);<br>
> +             if (!ms)<br>
> +                     return -ETIMEDOUT;<br>
> +     } else {<br>
> +             status = (u16 *)op-><a href="http://data.buf.in" target="_blank">data.buf.in</a>;<br>
<br>
Hm, I don't think it's safe, for 2 reasons:<br>
<br>
1/ op-><a href="http://data.buf.in" target="_blank">data.buf.in</a> might be a 1byte buffer, but you're doing a 2byte check<br>
2/ data is in big endian in the SPI buffer, which means your check<br>
   won't work on LE architectures.<br>
<br>
You really need a dedicated spi_mem_read_status() function that's passed<br>
an u16 pointer:<br>
<br>
int spi_mem_read_status(struct spi_mem *mem,<br>
                        const struct spi_mem_op *op,<br>
                        u16 *status)<br>
{<br>
        const u8 *bytes = (u8 *)op-><a href="http://data.buf.in" target="_blank">data.buf.in</a>;<br>
        int ret;<br>
<br>
        ret = spi_mem_exec_op(mem, op);<br>
        if (ret)<br>
                return ret;<br>
<br>
        if (op->data.nbytes > 1)<br>
                *status = ((u16)bytes[0] << 8) | bytes[1];</blockquote><div><br></div><div><br></div><div>Don’t we have get_unaligned_be16() with __be16 type for this? Or be16_to_cpup() if it’s aligned.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
        else<br>
                *status = bytes[0];<br>
<br>
        return 0;<br>
}<br>
<br>
> +             ret = read_poll_timeout(spi_mem_<wbr>exec_op, exec_op_ret,<br>
> +                                     ((*status) & mask) == match, 20,<br>
> +                                     timeout_ms * 1000, false, mem, op);<br>
> +             if (exec_op_ret)<br>
> +                     return exec_op_ret;<br>
> +     }<br>
> +<br>
<br>
I would do something like this instead:<br>
<br>
int spi_mem_poll_status(struct spi_mem *mem,<br>
                        const struct spi_mem_op *op,<br>
                        u16 mask, u16 match, u16 timeout_ms)<br>
{<br>
        struct spi_controller *ctlr = mem->spi->controller;<br>
        int ret = -EOPNOTSUPP;<br>
<br>
        if (op->data.nbytes < 1 || op->data.nbytes > 2)<br>
                return -EINVAL;<br>
<br>
        if (ctlr->mem_ops && ctlr->mem_ops->poll_status) {<br>
                ret = spi_mem_access_start(mem);<br>
                if (ret)<br>
                        return ret;<br>
<br>
                ret = ctlr->mem_ops->poll_status(<wbr>mem, op, mask, match,<br>
                                                 timeout_ms);<br>
<br>
                spi_mem_access_end(mem);<br>
        }<br>
<br>
<br>
        if (ret == -EOPNOTSUPP) {<br>
                u16 status;<br>
                int read_status_ret;<br>
<br>
                if (!spi_mem_supports_op(mem, op))<br>
                        return -EOPNOTSUPP;<br>
<br>
                ret = read_poll_timeout(spi_mem_<wbr>read_status, exec_op_ret,<br>
                                        (read_status_ret || ((status & mask) == match), 20,<br>
                                        timeout_ms * 1000, false, mem, op, &status);<br>
<br>
                if (read_status_ret)<br>
                        return read_status_ret;<br>
        }<br>
<br>
        return ret;<br>
}<br>
<br>
> +     return ret;<br>
> +}<br>
> +EXPORT_SYMBOL_GPL(spi_mem_<wbr>poll_status);<br>
> +<br>
>  static int spi_mem_probe(struct spi_device *spi)<br>
>  {<br>
>       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.<wbr>driver);<br>
> @@ -763,6 +833,7 @@ static int spi_mem_probe(struct spi_device *spi)<br>
>       if (IS_ERR_OR_NULL(mem->name))<br>
>               return PTR_ERR_OR_ZERO(mem->name);<br>
>  <br>
> +     init_completion(&ctlr->xfer_<wbr>completion);<br>
>       spi_set_drvdata(spi, mem);<br>
>  <br>
>       return memdrv->probe(mem);<br>
> diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h<br>
> index 2b65c9edc34e..0fbf5d0a3d31 100644<br>
> --- a/include/linux/spi/spi-mem.h<br>
> +++ b/include/linux/spi/spi-mem.h<br>
> @@ -250,6 +250,7 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)<br>
>   *             the currently mapped area), and the caller of<br>
>   *             spi_mem_dirmap_write() is responsible for calling it again in<br>
>   *             this case.<br>
> + * @poll_status: poll memory device status<br>
>   *<br>
>   * This interface should be implemented by SPI controllers providing an<br>
>   * high-level interface to execute SPI memory operation, which is usually the<br>
> @@ -274,6 +275,9 @@ struct spi_controller_mem_ops {<br>
>                              u64 offs, size_t len, void *buf);<br>
>       ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,<br>
>                               u64 offs, size_t len, const void *buf);<br>
> +     int (*poll_status)(struct spi_mem *mem,<br>
> +                        const struct spi_mem_op *op,<br>
> +                        u16 mask, u16 match, unsigned long timeout);<br>
>  };<br>
>  <br>
>  /**<br>
> @@ -369,6 +373,12 @@ devm_spi_mem_dirmap_create(<wbr>struct device *dev, struct spi_mem *mem,<br>
>  void devm_spi_mem_dirmap_destroy(<wbr>struct device *dev,<br>
>                                struct spi_mem_dirmap_desc *desc);<br>
>  <br>
> +void spi_mem_finalize_op(struct spi_controller *ctlr);<br>
> +<br>
> +int spi_mem_poll_status(struct spi_mem *mem,<br>
> +                     const struct spi_mem_op *op,<br>
> +                     u16 mask, u16 match, u16 timeout);<br>
> +<br>
>  int spi_mem_driver_register_with_<wbr>owner(struct spi_mem_driver *drv,<br>
>                                      struct module *owner);<br>
>  <br>
<br>
</blockquote><br><br>-- <br>With Best Regards,<br>Andy Shevchenko<br><br><br>