Skip to content

Commit

Permalink
spi: Avoid setting the chip select if we don't need to
Browse files Browse the repository at this point in the history
On some SPI controllers (like spi-geni-qcom) setting the chip select
is a heavy operation.  For instance on spi-geni-qcom, with the current
code, is was measured as taking upwards of 20 us.  Even on SPI
controllers that aren't as heavy, setting the chip select is at least
something like a MMIO operation over some peripheral bus which isn't
as fast as a RAM access.

While it would be good to find ways to mitigate problems like this in
the drivers for those SPI controllers, it can also be noted that the
SPI framework could also help out.  Specifically, in some situations,
we can see the SPI framework calling the driver's set_cs() with the
same parameter several times in a row.  This is specifically observed
when looking at the way the Chrome OS EC SPI driver (cros_ec_spi)
works but other drivers likely trip it to some extent.

Let's solve this by caching the chip select state in the core and only
calling into the controller if there was a change.  We check not only
the "enable" state but also the chip select mode (active high or
active low) since controllers may care about both the mode and the
enable flag in their callback.

Signed-off-by: Douglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20200629164103.1.Ied8e8ad8bbb2df7f947e3bc5ea1c315e041785a2@changeid
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
Douglas Anderson authored and Mark Brown committed Jul 1, 2020
1 parent dd67de8 commit d40f0b6
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 0 deletions.
11 changes: 11 additions & 0 deletions drivers/spi/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,17 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
{
bool enable1 = enable;

/*
* Avoid calling into the driver (or doing delays) if the chip select
* isn't actually changing from the last time this was called.
*/
if ((spi->controller->last_cs_enable == enable) &&
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
return;

spi->controller->last_cs_enable = enable;
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;

if (!spi->controller->set_cs_timing) {
if (enable1)
spi_delay_exec(&spi->controller->cs_setup, NULL);
Expand Down
4 changes: 4 additions & 0 deletions include/linux/spi/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @cur_msg_prepared: spi_prepare_message was called for the currently
* in-flight message
* @cur_msg_mapped: message has been mapped for DMA
* @last_cs_enable: was enable true on the last call to set_cs.
* @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
* @xfer_completion: used by core transfer_one_message()
* @busy: message pump is busy
* @running: message pump is running
Expand Down Expand Up @@ -604,6 +606,8 @@ struct spi_controller {
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
bool last_cs_enable;
bool last_cs_mode_high;
bool fallback;
struct completion xfer_completion;
size_t max_dma_len;
Expand Down

0 comments on commit d40f0b6

Please sign in to comment.