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.

AM4378: U-boot SPI probe issue

Part Number: AM4378
Other Parts Discussed in Thread: AM4372, SYSCONFIG

We have a custom made board running an am437x device. We have two peripherals connected to SPI0 on CS0 and CS3.

They are connected as follows:


mux.c:

static struct module_pin_mux spi_flash_pin_mux[] = {
{OFFSET(spi0_d0), (MODE(0) | RXACTIVE | PULLUDDIS)}, /* SPI0_D0 */
{OFFSET(spi0_d1), (MODE(0) | PULLUDDIS)}, /* SPI0_D1 */
{OFFSET(spi0_cs0), (MODE(0) | PULLUP_EN | PULLUDEN)}, /* SPI0_CS0 */
{OFFSET(cam1_hd), (MODE(2) | PULLUP_EN) | PULLUDEN}, /* SPI0_CS3 */
{OFFSET(spi0_sclk), (MODE(0) | RXACTIVE | PULLUDDIS)}, /* SPI0_SCLK */
{-1},
};


dts:

flash_memory_spi_pins_default: flash_memory_spi_pins_default {

pinctrl-single,pins = <

AM4372_IOPAD(0x950, PIN_INPUT | MUX_MODE0) /* (P23) spi0_sclk.spi0_sclk */

AM4372_IOPAD(0x954, PIN_INPUT | MUX_MODE0) /* (T22) spi0_d0.spi0_d0 */

AM4372_IOPAD(0x958, PIN_OUTPUT | MUX_MODE0) /* (T21) spi0_d1.spi0_d1 */

AM4372_IOPAD(0x95c, PIN_OUTPUT_PULLUP | MUX_MODE0) /* (T20) spi0_cs0.spi0_cs0 */

AM4372_IOPAD(0x9d4, PIN_OUTPUT_PULLUP | MUX_MODE2) /* (AD25) cam1_hd.gpio4[9] */

>;

};

&spi0 {

u-boot,dm-spl;

pinctrl-names = "default";

pinctrl-0 = <&flash_memory_spi_pins_default>;

status = "okay";

ti,spi-num-cs = <4>;

flash: spi_flash@0 {

#address-cells = <1>;

#size-cells = <1>;

u-boot,dm-spl;

u-boot,dm-pre-reloc;

compatible = "jedec,spi-nor";

reg = <0>; /* Chip select 0 */

spi-max-frequency = <24000000>;

};

dev_xxx@3 {

compatible = "dev_xxx-spi";

reg = <3>;

spi-max-frequency = <400000>;

};


};

U-boot version:

U-Boot 2019.10-rc1-g773766f6f1-dirty (May 05 2020 - 12:47:27 +0300)

arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0

GNU ld (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 2.32.0.20190321

Problem is that when SPI-memory is probed (CS0) it also pulls the chip select for CS3 so that both peripherals gets activated at the same time.

I can go around this by changing the order the peripherals are probed and hacking the SPI driver. This, however, means that my hack will need to be applied to any future updates we make to U-boot.


The reason it fails for us is that the polarity register for CS3 of SPI0 (MCSPI0->MCSPI_CH3CONF->EPOL) defaults to active high after MCSPI software reset(we need the opposite). For it to work the MCSPI_CH(X)CONF register for each CS used has to be initialized with the correct values prior to trying to access any of the peripherals. But the driver does not seem to operate like this. Instead, it only updates the MCSPI_CH(X)CONF of the peripheral that is currently being accessed. So for us it works like this:

Step 1) probe cs0 - This correctly configures cs0 but operation fails because cs3 is configured wrong.

Step 2) probe cs3 - This correctly configures cs3 and because cs0 is still configured correctly the peripheral is successfully probed

Step 3) probe cs0 - Because both cs0 and cs3 are now correctly configured also this peripheral works.

In reality, because of a bug (?), step 2 has to executed twice for it to work.

The reason that step 2 has to rpeated twice is that in:

spi-uclass.c

the function:

int dm_spi_claim_bus(struct udevice *dev)

(indirectly) calls:

static int omap3_spi_set_mode(struct udevice *dev, uint mode)

before it calls:

static int omap3_spi_claim_bus(struct udevice *dev)

The omap3_spi_claim_bus is where the "to-be-used" cs is updated in the private data structure used by the SPI driver.

The omap3_spi_set_mode is the function that actually updates the chip-select related register values.

Since omap3_spi_set_mode is called before omap3_spi_claim_bus the actual updating of the register is not performed until the next call to dm_spi_claim_bus. Probably not as intended?


The above also applies to:
static int omap3_spi_set_speed(struct udevice *dev, unsigned int speed)
which is also called before omap3_spi_claim_bus resulting in the speed being updated for the previously used chip-select.


This can be mitigated with a small hack:

static int omap3_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct omap3_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);

priv->cs = slave_plat->cs;
priv->freq = slave_plat->max_hz;

_omap3_spi_claim_bus(priv);
_omap3_spi_set_mode(priv); <-- Add this here
_omap3_spi_set_speed(priv); <-- Add this here

return 0;
}

static int omap3_spi_set_speed(struct udevice *dev, unsigned int speed)
{

struct omap3_spi_priv *priv = dev_get_priv(dev);

priv->freq = speed;
// _omap3_spi_set_speed(priv); <-- Comment out

return 0;
}

static int omap3_spi_set_mode(struct udevice *dev, uint mode)
{
struct omap3_spi_priv *priv = dev_get_priv(dev);

priv->mode = mode;

// _omap3_spi_set_mode(priv); <-- Comment out

return 0;
}

So, with a little hacking and creative probing I can get our device to work properly. The question is, should this work out of the box without any hacking?

Is TI u-boot SPI driver intended to support multiple peripherals(multiple chip-selects) on the same bus?

If so, did I configure it wrong or is the driver broken?

If possible I would prefer a solution where I do not have to patch the low-level driver.

  • Mathias,

    thanks for the detailed investigation, the details are very helpful. It does sound like a bug to me, and it looks like the same issue was reported not too long ago: https://e2e.ti.com/support/processors/f/791/p/882656/3289493#3289493

    Mathias Agren1 said:

    If so, did I configure it wrong or is the driver broken?

    If possible I would prefer a solution where I do not have to patch the low-level driver.

    I'm going to file a bug against the associated modules/driver to get this addressed in a future SDK release. Also I'm going to leave this thread open here for a little bit longer so I can add any outcome from an initial discussion with the respective driver owner/developer FYI.

    Regards, Andreas

  • Andreas Dannenberg said:
    I'm going to file a bug against the associated modules/driver to get this addressed in a future SDK release. Also I'm going to leave this thread open here for a little bit longer so I can add any outcome from an initial discussion with the respective driver owner/developer FYI.

    Mathias,

    seems like our driver domain expert has already identified a fix for this. We do not have hardware readily accessible to test it, so it would be great if you could help us to validate the fix. Please stay tuned.

    Regards, Andreas

  • Hi Andreas

    Thanks for the input.

    I'll be happy to help out with the testing on my hardware.

    /Mathias

  • Hi Mathias,

    thanks for your willingness to help. I'm still waiting on the developer to provide the patch for testing, will update this thread as soon as I have it.

    Regards, Andreas

  • Mathias,

    please find attached the proposed solution from our driver expert for you to try out.

    In this context I also want to pass along the following information: The patch should avoid having to run step 2 twice. But you would still have to probe CS3 first and CS0  later from u-boot prompt. This is because CS3 uses CS Polarity active High (which will be default state of line on POR) and driver won't know this info until probed. Alternatively you could have an inverter on CS3 line so that McSPI can use CS Polarity of active low.

    Please let us know if this improves things on your end.

    Thanks, Andreas

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/0001_2D00_spi_2D00_omap3_5F00_spi_2D00_Fix_2D00_speed_2D00_and_2D00_mode_2D00_selection.patch

  • Hi Andreas

    Sorry for the delay. I missed you post.

    There still seems to be some issues with the driver. In my case, see image below, CS3 is active when only CS0 should be.

    Only after I probe CS3 is both CS configured correctly.

    I have not dug very deep into this but I think the reason for this is, when the parent spi driver (omap3_spi) is probed it runs:

    writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &regs->sysconfig);

    in:

    static void spi_reset(struct mcspi *regs) 

    this sets OMAP3_MCSPI_CHCONF_EPOL registers for all channels to its default value. For some reason this seems to be active high for CS3 which is the opposite of what we need. 

    Since the spi-flash driver, using CS0, only updates channel config for CS0, CS3 channel config will be wrong when spi-flash driver is invoked.

    I think, for this to really work the parent driver (omap3_spi) needs to crawl the spi node of the device tree and configure the CS settings for all child nodes after executing writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &regs->sysconfig);

    Best Regards

    Mathias

     

  • Mathias,

    Mathias Agren1 said:
    There still seems to be some issues with the driver. In my case, see image below, CS3 is active when only CS0 should be.

    thanks for giving this a shot. In my earlier post I had mentioned that "But you would still have to probe CS3 first and CS0  later from u-boot prompt". Can you confirm you have followed this sequence when you took that first scope capture?

    Regards, Andreas

  • Hi Andreas

    Sorry, I totally missed you writing about the ordering. I have done some more testing. I can confirm that your patch works as long as CS3 is probed before CS0. The problem here is that CS0 is the SPI-memory which also holds the environment, thus CS0 is probed before you can use the command line (as far as I know at least).

    My solution  is to probe CS3 from board_init()

     

    static void probe_and_setup_SPI0CS3()
    {
    #define CS3 3

    struct udevice *bus;
    struct udevice *dev;
    int ret;
    int busnum = 0;

    ret = uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus);

    if(!ret){
        ret = spi_find_chip_select(bus, CS3, &dev);
    if(!ret){
        device_probe(dev);
    }else{
        printf("probeAndSetupSPI0 spi_find_chip_select failed!\n");
    }
    }else{
        printf("probeAndSetupSPI0 uclass_get_device_by_seq failed!\n");
    }

    }

    Not sure if this is the best/correct way but it seems to work.

    Best Regards

    Mathias 

  • HI Mathias,

    thanks for the update. Yes what you are doing looks good. Alternatively you might be able to use spi_find_bus_and_cs() in combination with probe calls to the bus and SPI device it returns to simplify the code a little but at the end it'd be the same principle.

    Let me close loop with the developer so we can finalize the patch and also contribute it to our SDK and upstream.

    Regards, Andreas