Commit c955a0cc authored by Patrice Chotard's avatar Patrice Chotard Committed by Mark Brown

spi: spi-mem: add automatic poll status functions

With STM32 QSPI, it is possible to poll the status register of the device.
This could be done to offload the CPU during an operation (erase or
program a SPI NAND for example).

spi_mem_poll_status API has been added to handle this feature.
This new function take care of the offload/non-offload cases.

For the non-offload case, use read_poll_timeout() to poll the status in
order to release CPU during this phase.
For example, previously, when erasing large area, in non-offload case,
CPU load can reach ~50%, now it decrease to ~35%.
Signed-off-by: default avatarPatrice Chotard <patrice.chotard@foss.st.com>
Signed-off-by: default avatarChristophe Kerello <christophe.kerello@foss.st.com>
Reviewed-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Link: https://lore.kernel.org/r/20210518162754.15940-2-patrice.chotard@foss.st.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6efb943b
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Author: Boris Brezillon <boris.brezillon@bootlin.com> * Author: Boris Brezillon <boris.brezillon@bootlin.com>
*/ */
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/iopoll.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h> #include <linux/spi/spi-mem.h>
...@@ -743,6 +744,91 @@ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) ...@@ -743,6 +744,91 @@ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
return container_of(drv, struct spi_mem_driver, spidrv.driver); return container_of(drv, struct spi_mem_driver, spidrv.driver);
} }
static int spi_mem_read_status(struct spi_mem *mem,
const struct spi_mem_op *op,
u16 *status)
{
const u8 *bytes = (u8 *)op->data.buf.in;
int ret;
ret = spi_mem_exec_op(mem, op);
if (ret)
return ret;
if (op->data.nbytes > 1)
*status = ((u16)bytes[0] << 8) | bytes[1];
else
*status = bytes[0];
return 0;
}
/**
* spi_mem_poll_status() - Poll memory device status
* @mem: SPI memory device
* @op: the memory operation to execute
* @mask: status bitmask to ckeck
* @match: (status & mask) expected value
* @initial_delay_us: delay in us before starting to poll
* @polling_delay_us: time to sleep between reads in us
* @timeout_ms: timeout in milliseconds
*
* This function polls a status register and returns when
* (status & mask) == match or when the timeout has expired.
*
* Return: 0 in case of success, -ETIMEDOUT in case of error,
* -EOPNOTSUPP if not supported.
*/
int spi_mem_poll_status(struct spi_mem *mem,
const struct spi_mem_op *op,
u16 mask, u16 match,
unsigned long initial_delay_us,
unsigned long polling_delay_us,
u16 timeout_ms)
{
struct spi_controller *ctlr = mem->spi->controller;
int ret = -EOPNOTSUPP;
int read_status_ret;
u16 status;
if (op->data.nbytes < 1 || op->data.nbytes > 2 ||
op->data.dir != SPI_MEM_DATA_IN)
return -EINVAL;
if (ctlr->mem_ops && ctlr->mem_ops->poll_status) {
ret = spi_mem_access_start(mem);
if (ret)
return ret;
ret = ctlr->mem_ops->poll_status(mem, op, mask, match,
initial_delay_us, polling_delay_us,
timeout_ms);
spi_mem_access_end(mem);
}
if (ret == -EOPNOTSUPP) {
if (!spi_mem_supports_op(mem, op))
return ret;
if (initial_delay_us < 10)
udelay(initial_delay_us);
else
usleep_range((initial_delay_us >> 2) + 1,
initial_delay_us);
ret = read_poll_timeout(spi_mem_read_status, read_status_ret,
(read_status_ret || ((status) & mask) == match),
polling_delay_us, timeout_ms * 1000, false, mem,
op, &status);
if (read_status_ret)
return read_status_ret;
}
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_poll_status);
static int spi_mem_probe(struct spi_device *spi) static int spi_mem_probe(struct spi_device *spi)
{ {
struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
......
...@@ -250,6 +250,9 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) ...@@ -250,6 +250,9 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* the currently mapped area), and the caller of * the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in * spi_mem_dirmap_write() is responsible for calling it again in
* this case. * this case.
* @poll_status: poll memory device status until (status & mask) == match or
* when the timeout has expired. It fills the data buffer with
* the last status value.
* *
* This interface should be implemented by SPI controllers providing an * This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the * high-level interface to execute SPI memory operation, which is usually the
...@@ -274,6 +277,12 @@ struct spi_controller_mem_ops { ...@@ -274,6 +277,12 @@ struct spi_controller_mem_ops {
u64 offs, size_t len, void *buf); u64 offs, size_t len, void *buf);
ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc, ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf); u64 offs, size_t len, const void *buf);
int (*poll_status)(struct spi_mem *mem,
const struct spi_mem_op *op,
u16 mask, u16 match,
unsigned long initial_delay_us,
unsigned long polling_rate_us,
unsigned long timeout_ms);
}; };
/** /**
...@@ -369,6 +378,13 @@ devm_spi_mem_dirmap_create(struct device *dev, struct spi_mem *mem, ...@@ -369,6 +378,13 @@ devm_spi_mem_dirmap_create(struct device *dev, struct spi_mem *mem,
void devm_spi_mem_dirmap_destroy(struct device *dev, void devm_spi_mem_dirmap_destroy(struct device *dev,
struct spi_mem_dirmap_desc *desc); struct spi_mem_dirmap_desc *desc);
int spi_mem_poll_status(struct spi_mem *mem,
const struct spi_mem_op *op,
u16 mask, u16 match,
unsigned long initial_delay_us,
unsigned long polling_delay_us,
u16 timeout_ms);
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner); struct module *owner);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment