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