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.

SK-AM62: Long Word Delays on MCSPI transfers

Part Number: SK-AM62
Other Parts Discussed in Thread: SYSCONFIG

I'm currently working with the MCSPI peripheral on the AM62 SK board. I have to transfer image-like data and need appropriate bandwidth. However, my effective bandwidth is far below what I would expect. When I look at the signal using an oscilloscope I can see the cause: after each word sent the SPI controller makes a break of about 1.1 us while the actual word is sent with 0,3 us. This means that I'm effectively only using ~22% of my bandwidth. When I increase the SPI clock to 50 MHz, one word gets sent in ~0,16 us while the total "cycle time" (word + delay) still takes ~1,4 us, which reduces the effective bandwidth even to 11%.

Now I want to reduce this word delay and my question is if this delay can be configured somehow? Below I will give more details on my setup. Maybe I'm having a mistake in somewhere?

------------------- Details -------------------

I'm using the MCSPI with Cortex-A53 from Linux, so MCSPI should be controlled by the "drivers/spi/spi-omap2-mcspi.c" driver. I tested it with the kernel of TI's Linux Processor SDK (version 08.03.00.19). I configured SPI to operate in mode 3 and with 8 Bits per word. Below I provided a Sysconfig snippet to show which pin-muxing configuration I'm using. Also I attach a snippet of the devicetrees showing how I register my test module to the kernel:

/**
 * These arguments were used when this file was generated. They will be automatically applied on subsequent loads
 * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
 * @cliArgs --device "AM62x" --package "ALW" --part "Default"
 * @versions {"tool":"1.12.1+2446"}
 */
scripting.excludeFromBuild("AM62x_pinmux.h");
scripting.excludeFromBuild("AM62x_pinmux_data.c");

/**
 * These are the peripherals and settings in this configuration
 */
const iSPI1       = scripting.addPeripheral("SPI");
iSPI1.$name       = "IIR_SPI";
iSPI1.$assign     = "SPI2";
iSPI1.CLK.$assign = "ball.A20";
iSPI1.CS0.$assign = "ball.E19";
iSPI1.CS1.$used   = false;
iSPI1.CS2.$used   = false;
iSPI1.CS3.$used   = false;
iSPI1.D0.$assign  = "ball.B19";
iSPI1.D1.$assign  = "ball.A19";

&main_pmx0 {
	uec_spi2_pins_default: uec-spi2-pins-default {
		pinctrl-single,pins = <
			AM62X_IOPAD(0x01b0, PIN_INPUT, 1) /* (A20) MCASP0_ACLKR.SPI2_CLK */
			AM62X_IOPAD(0x01ac, PIN_INPUT, 1) /* (E19) MCASP0_AFSR.SPI2_CS0 */
			AM62X_IOPAD(0x0194, PIN_INPUT, 1) /* (B19) MCASP0_AXR3.SPI2_D0 */
			AM62X_IOPAD(0x0198, PIN_INPUT, 1) /* (A19) MCASP0_AXR2.SPI2_D1 */
		>;
	};
};

...

&main_spi2 {
	status = "ok";
	pinctrl-names = "default";
	pinctrl-0 = <&uec_spi2_pins_default>;
	ti,pindir-d0-out-d1-in = <1>;
	spitest@0 {
		compatible = "test,device";
		reg = <0>;
		spi-max-frequency = <50000000>;
	};
};

The SPI core API of Linux foresees some functions to control the inter-word delay and other timing parameters (which doesn't mean that all drivers are implementing it). I tested all of them, but the actual signal kept unchanged. Below I've attached a simple kernel module that can be used to reproduce the issue. The module can be built with the kernel contained in Processor SDK.

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/spi/spi.h>

struct spitest_data
{
    struct spi_device *spi;
    u32 len;
};

int spitest_read_sync(struct spitest_data *spitest)
{
    int status;
    u8 *rx_buf;
    u8 *tx_buf;
    u32 len_aligned;
    struct spi_transfer *xfers;
    struct spi_message msg;

    struct spi_device *spi = spitest->spi;

    rx_buf = kzalloc(spitest->len, GFP_KERNEL);
    tx_buf = kzalloc(spitest->len, GFP_KERNEL);

    xfers = kcalloc(1, sizeof(struct spi_transfer), GFP_KERNEL);
    if (xfers == NULL)
        return -ENOMEM;

    xfers[0].rx_buf = rx_buf;
    xfers[0].tx_buf = tx_buf;

    xfers[0].len = spitest->len;
    xfers[0].speed_hz = spi->max_speed_hz;
    xfers[0].bits_per_word = spi->bits_per_word; // == 8 bpw
    
    xfers[0].word_delay = spi->word_delay; // == 0
    xfers[0].cs_change_delay = spi->word_delay; // == 0
    xfers[0].delay = spi->word_delay; // == 0

    spi_message_init(&msg);
    spi_message_add_tail(&xfers[0], &msg);

    status = spi_sync(spi, &msg);

    kfree(rx_buf);
    kfree(tx_buf);
    kfree(xfers);

    return status;
}

/*----------------------------------------------------------------------------*/

static int spitest_probe(struct spi_device *spi)
{
    int status;
    struct spitest_data *spitest;

    spitest = kzalloc(sizeof(*spitest), GFP_KERNEL);
    if (!spitest) return -ENOMEM;

    spitest->spi = spi;
    spitest->len = 4096; // arbitrarily chosen

    spitest->spi->max_speed_hz = 25000000;
    spitest->spi->chip_select = 0;
    spitest->spi->bits_per_word = 8;
    spitest->spi->mode = SPI_MODE_3;
    spitest->spi->word_delay.unit = SPI_DELAY_UNIT_USECS;
    spitest->spi->word_delay.value = 0;

    // setting all delays to see if something changes
    spitest->spi->cs_setup.unit = SPI_DELAY_UNIT_USECS;
    spitest->spi->cs_setup.value = 0;
    spitest->spi->cs_hold.unit = SPI_DELAY_UNIT_USECS;
    spitest->spi->cs_hold.value = 0;
    spitest->spi->cs_inactive.unit = SPI_DELAY_UNIT_USECS;
    spitest->spi->cs_inactive.value = 0;

    status = spi_setup(spitest->spi);

    spi_set_drvdata(spi, spitest);

    // make test transaction
    status = spitest_read_sync(spitest);

    return status;
}

static void spitest_remove(struct spi_device *spi)
{
    struct spitest_data *spitest;
    spitest = spi_get_drvdata(spi);
    kfree(spitest);
}

/*----------------------------------------------------------------------------*/

static const struct spi_device_id spitest_spi_ids[] = {
    { .name = "device" },
    {},
};
MODULE_DEVICE_TABLE(spi, spitest_spi_ids);

static const struct of_device_id spitest_dt_ids[] = {
    { .compatible = "test,dvice" },
    {}
};

static struct spi_driver spitest_spi_driver = {
    .driver = {
        .name = "spitest",
        .of_match_table = spitest_dt_ids
    },
    .id_table = spitest_spi_ids,
    .probe = spitest_probe,
    .remove = spitest_remove
};
module_spi_driver(spitest_spi_driver); // macro replacing module init/exit

MODULE_LICENSE("GPL");

Thanks in advance,

Claudio