Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Neil Armstrong and committed by
Mark Brown
468a3208 6ff33f39

+28
+28
drivers/spi/spi-omap2-mcspi.c
··· 1217 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 1247 static int omap2_mcspi_transfer_one(struct spi_master *master, 1221 1248 struct spi_device *spi, struct spi_transfer *t) 1222 1249 { ··· 1371 1344 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); 1372 1345 master->setup = omap2_mcspi_setup; 1373 1346 master->auto_runtime_pm = true; 1347 + master->prepare_message = omap2_mcspi_prepare_message; 1374 1348 master->transfer_one = omap2_mcspi_transfer_one; 1375 1349 master->set_cs = omap2_mcspi_set_cs; 1376 1350 master->cleanup = omap2_mcspi_cleanup;