| /* ahci.c: dump AHCI registers */ |
| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include "inteltool.h" |
| |
| static const char *ghc_regs[] = { |
| "CAP", "GHC", "IS", "PI", |
| "VS", "CCC_CTL", "CCC_PORTS", "EM_LOC", |
| "EM_CTL", "CAP2", "BOHC" |
| }; |
| |
| static const char *port_ctl_regs[] = { |
| "PxCLB", "PxCLBU", "PxFB", "PxFBU", |
| "PxIS", "PxIE", "PxCMD", "Reserved", |
| "PxTFD", "PxSIG", "PxSSTS", "PxSCTL", |
| "PxSERR", "PxSACT", "PxCI", "PxSNTF", |
| "PxFBS", "PxDEVSLP", "Reserved" |
| }; |
| |
| static const io_register_t sunrise_ahci_cfg_registers[] = { |
| {0x0, 4, "ID"}, |
| {0x4, 2, "CMD"}, |
| {0x6, 2, "STS"}, |
| {0x8, 1, "RID"}, |
| {0x9, 1, "PI"}, |
| {0xa, 2, "CC"}, |
| {0xc, 1, "CLS"}, |
| {0xd, 1, "MLT"}, |
| {0xe, 1, "HTYPE"}, |
| {0x10, 4, "MXTBA"}, |
| {0x14, 4, "MXPBA"}, |
| {0x20, 4, "AIDPBA"}, |
| {0x24, 4, "ABAR"}, |
| {0x2c, 4, "SS"}, |
| {0x34, 1, "CAP"}, |
| {0x3c, 2, "INTR"}, |
| {0x70, 2, "PID"}, |
| {0x72, 2, "PC"}, |
| {0x74, 2, "PMCS"}, |
| {0x80, 2, "MID"}, |
| {0x82, 2, "MC"}, |
| {0x84, 4, "MA"}, |
| {0x88, 2, "MD"}, |
| {0x90, 4, "MAP"}, |
| {0x94, 4, "PCS"}, |
| {0x9c, 4, "SATAGC"}, |
| {0xa0, 1, "SIRI"}, |
| {0xa4, 4, "SIRD"}, |
| {0xa8, 4, "SATACR0"}, |
| {0xac, 4, "SATACR1"}, |
| {0xc0, 4, "SP"}, |
| {0xd0, 2, "MXID"}, |
| {0xd2, 2, "MXC"}, |
| {0xd4, 4, "MXT"}, |
| {0xd8, 4, "MXP"}, |
| {0xe0, 4, "BFCS"}, |
| {0xe4, 4, "BFTD1"}, |
| {0xe8, 4, "BFTD2"}, |
| }; |
| |
| static const io_register_t sunrise_ahci_sir_registers[] = { |
| {0x80, 4, "SQUELCH"}, |
| {0x90, 4, "SATA_MPHY_PG"}, |
| {0xa4, 4, "OOBRETR"}, |
| }; |
| |
| #define NUM_GHC (sizeof(ghc_regs)/sizeof(ghc_regs[0])) |
| #define NUM_PORTCTL (sizeof(port_ctl_regs)/sizeof(port_ctl_regs[0])) |
| |
| static void print_port(const uint8_t *const mmio, size_t port) |
| { |
| size_t i; |
| printf("\nPort %zu Control Registers:\n", port); |
| const uint8_t *const mmio_port = mmio + 0x100 + port * 0x80; |
| for (i = 0; i < 0x80; i += 4) { |
| if (i / 4 < NUM_PORTCTL) { |
| printf("0x%03zx: 0x%08x (%s)\n", |
| (size_t)(mmio_port - mmio) + i, |
| read32(mmio_port + i), port_ctl_regs[i / 4]); |
| } else if (read32(mmio_port + i)) { |
| printf("0x%03zx: 0x%08x (Reserved)\n", |
| (size_t)(mmio_port - mmio) + i, |
| read32(mmio_port + i)); |
| } |
| } |
| } |
| |
| int print_ahci(struct pci_dev *ahci) |
| { |
| size_t ahci_registers_size = 0, i; |
| size_t ahci_cfg_registers_size = 0; |
| const io_register_t *ahci_cfg_registers; |
| size_t ahci_sir_index_offset = 0, ahci_sir_data_offset; |
| size_t ahci_sir_registers_size = 0; |
| const io_register_t *ahci_sir_registers; |
| |
| if (!ahci) { |
| puts("No SATA device found"); |
| return 0; |
| } |
| printf("\n============= AHCI Registers ==============\n\n"); |
| |
| switch (ahci->device_id) { |
| case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_SATA: |
| case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SATA: |
| ahci_registers_size = 0x800; |
| ahci_sir_index_offset = 0xa0; |
| ahci_cfg_registers = sunrise_ahci_cfg_registers; |
| ahci_cfg_registers_size = ARRAY_SIZE(sunrise_ahci_cfg_registers); |
| ahci_sir_registers = sunrise_ahci_sir_registers; |
| ahci_sir_registers_size = ARRAY_SIZE(sunrise_ahci_sir_registers); |
| break; |
| default: |
| ahci_registers_size = 0x400; |
| } |
| |
| ahci_sir_data_offset = ahci_sir_index_offset + 4; |
| |
| printf("\n============= AHCI Configuration Registers ==============\n\n"); |
| for (i = 0; i < ahci_cfg_registers_size; i++) { |
| switch (ahci_cfg_registers[i].size) { |
| case 4: |
| printf("0x%04x: 0x%08x (%s)\n", |
| ahci_cfg_registers[i].addr, |
| pci_read_long(ahci, ahci_cfg_registers[i].addr), |
| ahci_cfg_registers[i].name); |
| break; |
| case 2: |
| printf("0x%04x: 0x%04x (%s)\n", |
| ahci_cfg_registers[i].addr, |
| pci_read_word(ahci, ahci_cfg_registers[i].addr), |
| ahci_cfg_registers[i].name); |
| break; |
| case 1: |
| printf("0x%04x: 0x%02x (%s)\n", |
| ahci_cfg_registers[i].addr, |
| pci_read_byte(ahci, ahci_cfg_registers[i].addr), |
| ahci_cfg_registers[i].name); |
| break; |
| } |
| } |
| |
| printf("\n============= SATA Initialization Registers ==============\n\n"); |
| for (i = 0; i < ahci_sir_registers_size; i++) { |
| pci_write_byte(ahci, ahci_sir_index_offset, ahci_sir_registers[i].addr); |
| switch (ahci_sir_registers[i].size) { |
| case 4: |
| printf("0x%02x: 0x%08x (%s)\n", |
| ahci_sir_registers[i].addr, |
| pci_read_long(ahci, ahci_sir_data_offset), |
| ahci_sir_registers[i].name); |
| break; |
| case 2: |
| printf("0x%02x: 0x%04x (%s)\n", |
| ahci_sir_registers[i].addr, |
| pci_read_word(ahci, ahci_sir_data_offset), |
| ahci_sir_registers[i].name); |
| break; |
| case 1: |
| printf("0x%02x: 0x%02x (%s)\n", |
| ahci_sir_registers[i].addr, |
| pci_read_byte(ahci, ahci_sir_data_offset), |
| ahci_sir_registers[i].name); |
| break; |
| } |
| } |
| |
| const pciaddr_t ahci_phys = ahci->base_addr[5] & ~0x7ULL; |
| printf("\n============= ABAR ==============\n\n"); |
| printf("ABAR = 0x%08llx (MEM)\n\n", (unsigned long long)ahci_phys); |
| const uint8_t *const mmio = map_physical(ahci_phys, ahci_registers_size); |
| if (mmio == NULL) { |
| perror("Error mapping MMIO"); |
| exit(1); |
| } |
| |
| puts("Generic Host Control Registers:"); |
| for (i = 0; i < 0x100; i += 4) { |
| if (i / 4 < NUM_GHC) { |
| printf("0x%03zx: 0x%08x (%s)\n", |
| i, read32(mmio + i), ghc_regs[i / 4]); |
| } else if (read32(mmio + i)) { |
| printf("0x%03zx: 0x%08x (Reserved)\n", i, |
| read32(mmio + i)); |
| } |
| } |
| |
| const size_t max_ports = (ahci_registers_size - 0x100) / 0x80; |
| for (i = 0; i < max_ports; i++) { |
| if (read32(mmio + 0x0c) & 1 << i) |
| print_port(mmio, i); |
| } |
| |
| puts("\nOther registers:"); |
| for (i = 0x500; i < ahci_registers_size; i += 4) { |
| if (read32(mmio + i)) |
| printf("0x%03zx: 0x%08x\n", i, read32(mmio + i)); |
| } |
| |
| unmap_physical((void *)mmio, ahci_registers_size); |
| return 0; |
| } |