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)