Access Elkhart Lake GSPI via spidev === ### 1. Compile spidev.dsl ```bash= cat spidev.dsl DefinitionBlock ("spidev.aml", "SSDT", 5, "", "SPIDEV", 1) { External (\_SB.PC00.SPI0, DeviceObj) Scope (\_SB.PC00.SPI0) { Device (TP0) { Name (_HID, "SPT0001") Name (_DDN, "SPI test device") Name (_CRS, ResourceTemplate () { SpiSerialBus ( 0, // Chip select PolarityLow, // Chip select is active low FourWireMode, // Full duplex 8, // Bits per word is 8 (byte) ControllerInitiated, // Don't care 10000000, // 10 MHz ClockPolarityLow, // SPI mode 0 ClockPhaseFirst, // SPI mode 0 "\\_SB.PC00.SPI0", // SPI host controller 0 // Must be 0 ) }) } } } ``` compile ```bash= iasl spidev.dsl Intel ACPI Component Architecture ASL+ Optimizing Compiler/Disassembler version 20190509 Copyright (c) 2000 - 2019 Intel Corporation ASL Input: spidev.dsl - 989 bytes 5 keywords 29 source lines AML Output: spidev.aml - 166 bytes 0 opcodes 5 named objects Compilation successful. 0 Errors, 0 Warnings, 0 Remarks, 0 Optimizations ``` ### 2. make a directory and copy spidev.aml (source code spidev.dsl) ```bash= mkdir -p kernel/firmware/acpi cp spidev.aml kernel/firmware/acpi ``` ### 3. make a modified initrd ```bash= find kernel | cpio -H newc --create > initrd_mod; cat /boot/initrd.img >> initrd_mod sudo cp initrd_mod /boot ``` ### 4. Reboot and update BIOS menu Change SPI CS to low active ``` Chipset -> SerialIo Configuration -> Serial IO SPI0 Settings -> Chipselect 0 polarity to [Active Low] (change to GPIO, change to low active, change back to native) ``` ### 5. Temporary update Grub2 parameter At GRUB2 menu, press ā€œeā€ to edit first menu item Move to last line, change initrd.xxxx to initrd_mod and press ā€œF10ā€ to boot ### 6. Check dmesg ```bash= dmesg | grep -i spi [ 0.017106] ACPI: SSDT ACPI table found in initrd [kernel/firmware/acpi/spidev.aml][0xa6] [ 0.017332] ACPI: Table Upgrade: install [SSDT- - SPIDEV] [ 0.017334] ACPI: SSDT 0x00000000753C2000 0000A6 (v05 SPIDEV 00000001 INTL 20190509) [ 1.440097] spidev spi-SPT0001:00: do not use this driver in production systems! ``` ### 7. Give permission and test (source code mram.c modified from spidev-test.c) ```bash= sudo chmod a+rw /dev/spidev0.1 ./mram -C -D /dev/spidev0.1 -s 10000000 ./mram -C -D /dev/spidev0.1 -s 20000000 ./mram -C -D /dev/spidev0.1 -s 25000000 ``` example code ```c= #define CMD_READ 0x03 #define CMD_WRITE 0x02 #define MRAM_SIZE 0x8000 #define BUF_SIZE 0x100 static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) { int i = 0; const unsigned char *address = src; const unsigned char *line = address; unsigned char c; printf("%s | ", prefix); while (length-- > 0) { printf("%02X ", *address++); if (!(++i % line_size) || (length == 0 && i % line_size)) { if (length == 0) { while (i++ % line_size) printf("__ "); } printf(" | "); /* right close */ while (line < address) { c = *line++; printf("%c", (c < 33 || c == 255) ? 0x2E : c); } printf("\n"); if (length > 0) printf("%s | ", prefix); } } } int transfer(int fd, const uint8_t *tx_buf, unsigned long tx_len, uint8_t *rx_buf, unsigned long rx_len) { long ret, i = 1; struct spi_ioc_transfer x[2]; memset(x, 0, sizeof(x)); x[0].tx_buf = (unsigned long)tx_buf; x[0].len = tx_len; if (rx_buf) { x[1].rx_buf = (unsigned long)rx_buf; x[1].len = rx_len; i++; } ret = ioctl(fd, SPI_IOC_MESSAGE(i), x); if(ret != tx_len + rx_len) { printf("spi transfer error: %ld\n", ret); return -1; } if(tx_buf) hex_dump(tx_buf, tx_len, 32, "TX"); if(rx_buf) hex_dump(rx_buf, rx_len, 32, "RX"); return 0; } int rdid(int fd) { uint8_t tx = 0x9F, rx[4]; return transfer(fd, &tx, 1, rx, 4); } int ruid(int fd) { uint8_t tx = 0x4C, rx[8]; return transfer(fd, &tx, 1, rx, 8); } int rdsn(int fd) { uint8_t tx = 0xC3, rx[8]; return transfer(fd, &tx, 1, rx, 8); } #define BYTES_TO_WRITE (1024 * 1) int mram_read(int fd, int addr, int len) { uint8_t tx[4]; uint8_t rx[BYTES_TO_WRITE] = {0}; tx[0] = CMD_READ; tx[1] = (addr >> 16) & 0xFF; tx[2] = (addr >> 8) & 0xFF; tx[3] = addr & 0xFF; printf("\nread %d bytes\n", len ); return transfer(fd, tx, 4, rx, len); } int mram_write(int fd, int addr, char *buf, int len) { uint8_t tx[BYTES_TO_WRITE + 4] = {0}; int i, j = 0; for (i = 0; i < len; i++) { tx[i + 4] = buf[i]; } tx[0] = CMD_WRITE; tx[1] = (addr >> 16) & 0xFF; tx[2] = (addr >> 8) & 0xFF; tx[3] = addr & 0xFF; printf("\nwrite %d bytes\n", len ); return transfer(fd, tx, len + 4, NULL, 0); } ``` ### 8. (optional) write spi-nor flash like driver [reference patch](https://lists.infradead.org/pipermail/linux-mtd/2017-January/071674.html)