This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

U-Boot SPI access asserts multiple active-low chip-selects on the same bus

In our design we have two SPI devices on the same bus, each with an active-low chip-select. The SPI flash for booting is McSPI0 chip-select 0 (spi0_cs0) and an LCD driver chip is McSPI0 chip-select 1 (spi0_cs1). When either of these devices are accessed by U-Boot, the active-low chip-select is enabled for both devices at the same time. That's not right, of course.

The problem is in the U-Boot omap3_spi.c driver. Every time a single SPI device is selected with spi_claim_bus(), the entire SPI peripheral is reset with spi_reset(). This causes the EPOL bit for all of the SPI peripheral's chip-selects to be reset in their corresponding MCSPI_CHxCONF register. When EPOL is reset (to zero), the chip-select polarity is set for "held high during the active state". This configures them all as active-high chip-selects. Next, the spi_claim_bus() function will configure only the chip-select for the selected SPI device as active-low (by setting the EPOL bit in the appropriate MCSPI_CHxCONF register).

When the SPI access occurs, the chip-select for the SPI device being accessed will be properly asserted low (because it is correctly configured as active-low), but the other chip-selects from the same SPI bus are also being held asserted low (because they are incorrectly configured as active-high).

This issue occurs with U-Boot 2014.07, but I think with later versions as well, as the code looks the same.

Has this issue been encountered before?

What is the best way to solve this issue?

  • Hi,

    I will ask the software team to comment. From hardware perspective a possibility will be to pinmux the unwanted CS signal to GPIO (with the necessary level) until it's needed.
  • I've bumped into this before. I can share some experience on it but unfortunately I don't have a patch!

    You can *nearly* solve this issue by adding a few lines to the end of that spi_reset routine. After the SPI module is reset, it will be in slave mode and no longer driving the chip select lines. So you should likely have some external pullups. When you get a little further in the code the modulctrl register will be programmed such that the McSPI becomes a master, and that's the point in time where it will begin driving the chip selects based on their respsective EPOL configuration. So immediately following the reset (i.e. before returning from the function) you should configure both chconf registers. That way, when you get to the code that writes to modulctrl, the polarities will be configured correctly.

    There is still one hiccup in this solution. Namely, when the reset occurs, I believe you're going to see a short glitch on the chip select lines during the time where the module is resetting. It's like the polarities revert to their default value slightly before it goes back to slave mode. So the result is 10s of ns where both chip selects drive low. For many devices that's not an issue as there are no clocks during that time. Assuming there's no issue, I think my suggestion above (configuring both chconf registers) should be sufficient. Otherwise you'll need to adjust the pin muxing surrounding the reset to "hide" the glitch.
  • Thanks Brad.

    Your suggestion is good. I have tested a similar approach. In spi_claim_bus() I know the sense of the chip-select being accessed, so I am configuring all of the chip-selects on that SPI bus with the same polarity. This makes it a requirement that all chip-selects on a particular SPI bus must use the same polarity. With your suggestion to configure the chip-select polarity in the spi_reset() function, then it would be a requirement that all chip-selects on every SPI bus must be the same polarity. Neither solution is general-purpose unfortunately, they both impose a requirement on the design.

    With your suggestion, I don't think the momentary glitch in the chip-select when the SPI peripheral is reset is going to be a problem for our SPI devices. Without any changes to U-Boot (and with my approach above), the chip-select goes active between the SPI peripheral being configured as master by spi_reset() and when the chip-select is properly configured as active-low by spi_claim_bus(). This has been happening all along and I haven't seen any issues.

    Here's my change in spi_claim_bus(). It would be essentially the same code in spi_reset(), except that you'd pick a default chip-select polarity to use everywhere.

    --- a/drivers/spi/omap3_spi.c	2015-12-11 15:51:18.637339799 -0800
    +++ b/drivers/spi/omap3_spi.c	2015-12-14 09:22:12.501792789 -0800
    @@ -164,6 +164,7 @@ int spi_claim_bus(struct spi_slave *slav
     {
     	struct omap3_spi_slave *ds = to_omap3_spi(slave);
     	unsigned int conf, div = 0;
    +	int cs;
     
     	/* McSPI global module configuration */
     
    @@ -187,6 +188,70 @@ int spi_claim_bus(struct spi_slave *slav
     	} else
     		div = 0xC;
     
    +	/* Set chipselect polarity. Assume that all chipselects on this bus
    +	 * use the same polarity. Chipselect will be managed with FORCE.
    +	 */
    +	if (ds->regs == (struct mcspi *)OMAP3_MCSPI1_BASE)
    +	{
    +		// MCSPI1 has 4 chipselects.
    +		for (cs = 0; cs < 4; cs++)
    +		{
    +			conf = readl(&ds->regs->channel[cs].chconf);
    +			if (!(ds->mode & SPI_CS_HIGH))
    +				conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
    +			else
    +				conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
    +			writel(conf, &ds->regs->channel[cs].chconf);
    +			/* Flash post writes to make immediate effect */
    +			readl(&ds->regs->channel[cs].chconf);
    +		}
    +	}
    +#ifdef OMAP3_MCSPI2_BASE
    +	else if (ds->regs == (struct mcspi *)OMAP3_MCSPI2_BASE)
    +	{
    +		// MCSPI2 has 2 chipselects.
    +		for (cs = 0; cs < 2; cs++)
    +		{
    +			conf = readl(&ds->regs->channel[cs].chconf);
    +			if (!(ds->mode & SPI_CS_HIGH))
    +				conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
    +			else
    +				conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
    +			writel(conf, &ds->regs->channel[cs].chconf);
    +			/* Flash post writes to make immediate effect */
    +			readl(&ds->regs->channel[cs].chconf);
    +		}
    +	}
    +#endif	// OMAP3_MCSPI2_BASE
    +#ifdef OMAP3_MCSPI3_BASE
    +	else if (ds->regs == (struct mcspi *)OMAP3_MCSPI3_BASE)
    +	{
    +		// MCSPI3 has 1 chipselect.
    +		conf = readl(&ds->regs->channel[0].chconf);
    +		if (!(ds->mode & SPI_CS_HIGH))
    +			conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
    +		else
    +			conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
    +		writel(conf, &ds->regs->channel[0].chconf);
    +		/* Flash post writes to make immediate effect */
    +		readl(&ds->regs->channel[0].chconf);
    +	}
    +#endif	// OMAP3_MCSPI3_BASE
    +#ifdef OMAP3_MCSPI4_BASE
    +	else if (ds->regs == (struct mcspi *)OMAP3_MCSPI4_BASE)
    +	{
    +		// MCSPI4 has 1 chipselect.
    +		conf = readl(&ds->regs->channel[0].chconf);
    +		if (!(ds->mode & SPI_CS_HIGH))
    +			conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
    +		else
    +			conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
    +		writel(conf, &ds->regs->channel[0].chconf);
    +		/* Flash post writes to make immediate effect */
    +		readl(&ds->regs->channel[0].chconf);
    +	}
    +#endif	// OMAP3_MCSPI4_BASE
    +
     	conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
     
     	/* standard 4-wire master mode:	SCK, MOSI/out, MISO/in, nCS
    @@ -208,12 +273,6 @@ int spi_claim_bus(struct spi_slave *slav
     	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
     	conf |= (ds->slave.wordlen - 1) << 7;
     
    -	/* set chipselect polarity; manage with FORCE */
    -	if (!(ds->mode & SPI_CS_HIGH))
    -		conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
    -	else
    -		conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
    -
     	/* set clock divisor */
     	conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
     	conf |= div << 2;
    

    Thanks, Gregg. 

  • Thanks for sharing. Good point regarding multiple SPI instances being affected by the reset solution.