---
# System prepended metadata

title: Access Elkhart Lake GSPI via spidev

---

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)
