diff options
author | Neil Armstrong <narmstrong@baylibre.com> | 2015-10-09 09:47:41 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-10-22 10:26:22 -0400 |
commit | 468a32082b04c7febccfcd55b06ecbc438fcddcc (patch) | |
tree | bc5227c0d5c385e296503562068ac5d162524d0b | |
parent | 6ff33f3902c3b1c5d0db6b1e2c70b6d76fba357f (diff) |
spi: omap2-mcspi: disable other channels CHCONF_FORCE in prepare_message
Since the "Switch driver to use transfer_one" change, the cs_change
behavior has changed and a channel chip select can still be
asserted when changing channel from a previous last transfer in a
message having the cs_change attribute.
Since there is no sense having multiple chip select being asserted at the
same time, disable all the remaining forced chip selects in a the
prepare_message called right before a spi_transfer_one_message call.
It ignores the current channel configuration in order to keep the
possibility to leave the chip select asserted between messages.
It fixes this bug on a DM8168 SoC ES2.1 Soc and an OMAP4 ES2.1 SoC.
It was hanging all the other channels transfers when a CHCONF_FORCE
is present on the wrong channel.
Fixes: b28cb9414db9 ("spi: omap2-mcspi: Switch driver to use transfer_one")
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Michael Welling <mwelling@ieee.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3d09e0b69b73..1f8903d356e5 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c | |||
@@ -1217,6 +1217,33 @@ out: | |||
1217 | return status; | 1217 | return status; |
1218 | } | 1218 | } |
1219 | 1219 | ||
1220 | static int omap2_mcspi_prepare_message(struct spi_master *master, | ||
1221 | struct spi_message *msg) | ||
1222 | { | ||
1223 | struct omap2_mcspi *mcspi = spi_master_get_devdata(master); | ||
1224 | struct omap2_mcspi_regs *ctx = &mcspi->ctx; | ||
1225 | struct omap2_mcspi_cs *cs; | ||
1226 | |||
1227 | /* Only a single channel can have the FORCE bit enabled | ||
1228 | * in its chconf0 register. | ||
1229 | * Scan all channels and disable them except the current one. | ||
1230 | * A FORCE can remain from a last transfer having cs_change enabled | ||
1231 | */ | ||
1232 | list_for_each_entry(cs, &ctx->cs, node) { | ||
1233 | if (msg->spi->controller_state == cs) | ||
1234 | continue; | ||
1235 | |||
1236 | if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE)) { | ||
1237 | cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE; | ||
1238 | writel_relaxed(cs->chconf0, | ||
1239 | cs->base + OMAP2_MCSPI_CHCONF0); | ||
1240 | readl_relaxed(cs->base + OMAP2_MCSPI_CHCONF0); | ||
1241 | } | ||
1242 | } | ||
1243 | |||
1244 | return 0; | ||
1245 | } | ||
1246 | |||
1220 | static int omap2_mcspi_transfer_one(struct spi_master *master, | 1247 | static int omap2_mcspi_transfer_one(struct spi_master *master, |
1221 | struct spi_device *spi, struct spi_transfer *t) | 1248 | struct spi_device *spi, struct spi_transfer *t) |
1222 | { | 1249 | { |
@@ -1344,6 +1371,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) | |||
1344 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); | 1371 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); |
1345 | master->setup = omap2_mcspi_setup; | 1372 | master->setup = omap2_mcspi_setup; |
1346 | master->auto_runtime_pm = true; | 1373 | master->auto_runtime_pm = true; |
1374 | master->prepare_message = omap2_mcspi_prepare_message; | ||
1347 | master->transfer_one = omap2_mcspi_transfer_one; | 1375 | master->transfer_one = omap2_mcspi_transfer_one; |
1348 | master->set_cs = omap2_mcspi_set_cs; | 1376 | master->set_cs = omap2_mcspi_set_cs; |
1349 | master->cleanup = omap2_mcspi_cleanup; | 1377 | master->cleanup = omap2_mcspi_cleanup; |