With spi40 herein, I believe we can lock and hold the SPI3 clock at 45 MHz, and successfully request 40 MHz via spidev ioctls. But the transfer still returns all FF, so the remaining problem does not appear to be the userspace speed request path. The problem may be in MB1 pinmux configuration for the SPI2 pads, which are currently configured as rsvd1 and tristated rather than assigned to SPI2.
Experimental thor-spi2-pinmux.patch to attempt and resolve that is below.
cd ./jp7/Linux_for_Tegra/source/kernel/kernel-noble/tools/spi
Create spi40.c
#include <errno.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
static void dump_buf(const char *label, const uint8_t *buf, size_t len)
{
size_t i;
printf("%s", label);
for (i = 0; i < len; i++) {
printf("%02X ", buf[i]);
}
printf(" |");
for (i = 0; i < len; i++) {
unsigned char c = buf[i];
putchar((c >= 32 && c <= 126) ? c : '.');
}
printf("|\n");
}
int main(int argc, char **argv)
{
const char *dev = "/dev/spidev2.0";
int fd;
uint8_t mode = SPI_MODE_0;
uint8_t bits = 8;
uint32_t req_speed = 40000000;
uint32_t rd_speed = 0;
uint8_t tx[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
uint8_t rx[8] = { 0 };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = sizeof(tx),
.speed_hz = 40000000,
.delay_usecs = 0,
.bits_per_word = 8,
.cs_change = 0,
.tx_nbits = 0,
.rx_nbits = 0,
.word_delay_usecs = 0,
.pad = 0,
};
if (argc > 1) {
dev = argv[1];
}
fd = open(dev, O_RDWR);
if (fd < 0) {
fprintf(stderr, "open(%s) failed: %s\n", dev, strerror(errno));
return 1;
}
if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
fprintf(stderr, "SPI_IOC_WR_MODE failed: %s\n", strerror(errno));
close(fd);
return 1;
}
if (ioctl(fd, SPI_IOC_RD_MODE, &mode) == -1) {
fprintf(stderr, "SPI_IOC_RD_MODE failed: %s\n", strerror(errno));
close(fd);
return 1;
}
if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) {
fprintf(stderr, "SPI_IOC_WR_BITS_PER_WORD failed: %s\n", strerror(errno));
close(fd);
return 1;
}
if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1) {
fprintf(stderr, "SPI_IOC_RD_BITS_PER_WORD failed: %s\n", strerror(errno));
close(fd);
return 1;
}
if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &req_speed) == -1) {
fprintf(stderr, "SPI_IOC_WR_MAX_SPEED_HZ failed: %s\n", strerror(errno));
close(fd);
return 1;
}
if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &rd_speed) == -1) {
fprintf(stderr, "SPI_IOC_RD_MAX_SPEED_HZ failed: %s\n", strerror(errno));
close(fd);
return 1;
}
printf("device: %s\n", dev);
printf("mode: %u\n", mode);
printf("bits per word: %u\n", bits);
printf("requested max speed: %u Hz\n", req_speed);
printf("readback max speed: %u Hz\n", rd_speed);
printf("transfer speed_hz: %u Hz\n", tr.speed_hz);
dump_buf("TX: ", tx, sizeof(tx));
if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 1) {
fprintf(stderr, "SPI_IOC_MESSAGE failed: %s\n", strerror(errno));
close(fd);
return 1;
}
dump_buf("RX: ", rx, sizeof(rx));
close(fd);
return 0;
}
You can test with:
cd <jp7>/Linux_for_Tegra/source/kernel/kernel-noble/tools/spi$
sudo sh -c '
echo 1 > /sys/kernel/debug/bpmp/debug/clk/spi3/mrq_rate_locked
echo 45000000 > /sys/kernel/debug/bpmp/debug/clk/spi3/rate
cat /sys/kernel/debug/bpmp/debug/clk/spi3/rate
'
sudo ./spi40 /dev/spidev2.0
sudo cat /sys/kernel/debug/bpmp/debug/clk/spi3/rate
thor-spi2-pinmux.patch
cat > /tmp/thor-spi2-pinmux.patch <<'EOF'
diff --git a/bootloader/tegra264-mb1-bct-pinmux-p3834-xxxx-p4071-0008.dtsi b/bootloader/tegra264-mb1-bct-pinmux-p3834-xxxx-p4071-0008.dtsi
--- a/bootloader/tegra264-mb1-bct-pinmux-p3834-xxxx-p4071-0008.dtsi
+++ b/bootloader/tegra264-mb1-bct-pinmux-p3834-xxxx-p4071-0008.dtsi
@@ -1519,42 +1519,42 @@
spi2_sck_pcc7 {
nvidia,pins = "spi2_sck_pcc7";
- nvidia,function = "rsvd1";
+ nvidia,function = "spi2_sck";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
- nvidia,tristate = <TEGRA_PIN_ENABLE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,drv-type = <TEGRA_PIN_1X_DRIVER>;
nvidia,e-io-od = <TEGRA_PIN_DISABLE>;
nvidia,e-lpbk = <TEGRA_PIN_DISABLE>;
};
spi2_miso_pdd0 {
nvidia,pins = "spi2_miso_pdd0";
- nvidia,function = "rsvd1";
+ nvidia,function = "spi2_din";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
- nvidia,tristate = <TEGRA_PIN_ENABLE>;
- nvidia,enable-input = <TEGRA_PIN_DISABLE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
+ nvidia,enable-input = <TEGRA_PIN_ENABLE>;
nvidia,drv-type = <TEGRA_PIN_1X_DRIVER>;
nvidia,e-io-od = <TEGRA_PIN_DISABLE>;
nvidia,e-lpbk = <TEGRA_PIN_DISABLE>;
};
spi2_mosi_pdd1 {
nvidia,pins = "spi2_mosi_pdd1";
- nvidia,function = "rsvd1";
+ nvidia,function = "spi2_dout";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
- nvidia,tristate = <TEGRA_PIN_ENABLE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,drv-type = <TEGRA_PIN_1X_DRIVER>;
nvidia,e-io-od = <TEGRA_PIN_DISABLE>;
nvidia,e-lpbk = <TEGRA_PIN_DISABLE>;
};
spi2_cs0_n_pdd2 {
nvidia,pins = "spi2_cs0_n_pdd2";
- nvidia,function = "rsvd1";
+ nvidia,function = "spi2_cs0";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
- nvidia,tristate = <TEGRA_PIN_ENABLE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,drv-type = <TEGRA_PIN_1X_DRIVER>;
nvidia,e-io-od = <TEGRA_PIN_DISABLE>;
nvidia,e-lpbk = <TEGRA_PIN_DISABLE>;
};
EOF