/* SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include static void dmi_print_link_status(int loglevel) { const uint16_t dmilsts = dmibar_read16(DMILSTS); printk(loglevel, "DMI: Running at Gen%u x%u\n", dmilsts & 0xf, dmilsts >> 4 & 0x1f); } #define RETRAIN (1 << 5) #define LTRN (1 << 11) static void dmi_setup_physical_layer(void) { /* Program DMI AFE settings, which are needed for DMI to work */ peg_dmi_recipe(false, 0); /* Additional DMI programming steps */ dmibar_setbits32(0x258, 1 << 29); dmibar_clrsetbits32(0x208, 0x7ff, 0x6b5); dmibar_clrsetbits32(0x22c, 0xffff, 0x2020); /* Write SA reference code version */ dmibar_write32(0x71c, 0x0000000f); dmibar_write32(0x720, 0x01060200); /* We also have to bring up the PCH side of the DMI link */ pch_dmi_setup_physical_layer(); /* Write-once settings */ dmibar_clrsetbits32(DMILCAP, 0x3f00f, 2 << 0); printk(BIOS_DEBUG, "Retraining DMI at Gen2 speeds...\n"); dmi_print_link_status(BIOS_DEBUG); /* Retrain link */ dmibar_setbits16(DMILCTL, RETRAIN); do {} while (dmibar_read16(DMILSTS) & LTRN); dmi_print_link_status(BIOS_DEBUG); /* Retrain link again for DMI Gen2 speeds */ dmibar_setbits16(DMILCTL, RETRAIN); do {} while (dmibar_read16(DMILSTS) & LTRN); dmi_print_link_status(BIOS_INFO); } #define VC_ACTIVE (1U << 31) #define VCNEGPND (1 << 1) #define DMI_VC_CFG(vcid, tcmap) (VC_ACTIVE | ((vcid) << 24) | (tcmap)) static void dmi_tc_vc_mapping(void) { printk(BIOS_DEBUG, "Programming SA DMI VC/TC mappings...\n"); if (CONFIG(INTEL_LYNXPOINT_LP)) dmibar_setbits8(0xa78, 1 << 1); /* Each TC is mapped to one and only one VC */ const u32 vc0 = DMI_VC_CFG(0, (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 0)); const u32 vc1 = DMI_VC_CFG(1, (1 << 1)); const u32 vcp = DMI_VC_CFG(2, (1 << 2)); const u32 vcm = DMI_VC_CFG(7, (1 << 7)); dmibar_write32(DMIVC0RCTL, vc0); dmibar_write32(DMIVC1RCTL, vc1); dmibar_write32(DMIVCPRCTL, vcp); dmibar_write32(DMIVCMRCTL, vcm); /* Set Extended VC Count (EVCC) to 1 if VC1 is active */ dmibar_clrsetbits8(DMIPVCCAP1, 7, !!(vc1 & VC_ACTIVE)); /* * We also have to program the PCH side of the DMI link. Since both ends * must use the same Virtual Channel settings, we pass them as arguments. */ pch_dmi_tc_vc_mapping(vc0, vc1, vcp, vcm); printk(BIOS_DEBUG, "Waiting for SA DMI VC negotiation... "); do {} while (dmibar_read16(DMIVC0RSTS) & VCNEGPND); do {} while (dmibar_read16(DMIVC1RSTS) & VCNEGPND); do {} while (dmibar_read16(DMIVCPRSTS) & VCNEGPND); do {} while (dmibar_read16(DMIVCMRSTS) & VCNEGPND); printk(BIOS_DEBUG, "done!\n"); } void dmi_early_init(void) { dmi_setup_physical_layer(); dmi_tc_vc_mapping(); }