/*++ Copyright (c) 1989-2001 Microsoft Corporation Module Name: drvslw.c Abstract: This module implements code to perform Drive/Slew Pad compensation, so we can bring the memory clock up to fullspeed early during boot. --*/ #include "bldr32.h" #include #include "romdata.h" #include // // To enable cache checking code using PMC, define CHECKCACHE // #undef CHECKCACHE // // Location of the ROM base (mapped in BldrStartup) and the datatbl offset. // #define BLDR_ROM_BASE 0xFFC00000 #define BLDR_ROM_DATA_OFFSET 0x0000007C // // Macros used by calibration code // #define TOFIXEDPOINT(d) ((d) << 20) #define SLW_VALUE(r,f) SlwValue(r,f) #define FROMFIXEDPOINT(d) FromFixedPoint(d); #define NV_PBUS_FBIO_CALEN_VAL 0x000f0f0f #define NV_PBUS_FBIO_CALSEL_VCCQ 0x00040004 #define NV_PBUS_FBIO_CALSEL_VCC 0x00040000 #define DRF_MASK(drf) (0xFFFFFFFF>>(31-((1?drf) % 32)+((0?drf) % 32))) // // PMC event to use for cache checking code // #define PMC_EVENT 0x65 // Number of bus burst read transactions // // LDT flow control enable bits // #define LDT_FLOW_CONTROL_BITS 0x88000000 // // LDT registers // #define CR_CPU_LDT_1 0x80000064 #define CR_CPU_LDT_3_USER_CNFG 0x8000006C #define MCP_LEG_CFG_21 0x80000854 #define CR_CPU_MEMTOP 0x80000084 #define CR_CPU_MEMTOP_LIMIT_64MB 0x03FFFFFF #define CR_CPU_MEMTOP_LIMIT_128MB 0x07FFFFFF #define CR_CPU_GRAP 0x80000080 // // Memory test results // #define MCP_USBA_CFG_15 0x8000103C #define MCP_USBB_CFG_15 0x8000183C typedef struct _REGVALUEPAIR { ULONG Register; ULONG Value; } REGVALUEPAIR, *PREGVALUEPAIR; FORCEINLINE ULONG REG_RD32( VOID* Ptr, ULONG Addr ) { return *((volatile ULONG*)((UCHAR*)(Ptr) + (Addr))); } FORCEINLINE VOID REG_WR32( VOID* Ptr, ULONG Addr, ULONG Val) { *((volatile ULONG*)((UCHAR*)(Ptr) + (Addr))) = (ULONG)(Val); } #ifdef CHECKCACHE FORCEINLINE VOID SetPMCEvent( ULONG Event ) { Event |= ((1 << 22) | (1 << 17) | (1 << 18) | (1 << 20)); __asm { mov ecx, 0x186 mov eax, Event xor edx, edx wrmsr ; ; mov eax, cr4 ; _emit 0Fh _emit 20h _emit 0E0h or eax, 100h ; ; mov cr4, eax ; _emit 0Fh _emit 22h _emit 0E0h } } FORCEINLINE ULONG ReadPMCLow( VOID ) { ULONG LowPart; __asm { xor edx, edx xor ecx, ecx _emit 0fh _emit 33h mov LowPart, eax } return LowPart; }; #endif VOID BldrDelay( ULONG nsec ) /*++ Routine Description: This is a busy wait loop for the drive/slew calibration only. Its not very accurate, since the 733MHz cycle time (1.364ns) is rounded to 1ns and also doesn't account for the instructions that make-up the loop. The idle times being passed in are currently 10us, so delaying a little longer in this routine shouldn't be noticeable. Arguments: Return Value: None. --*/ { ULONG tick_curr_lo, tick_curr_hi; ULONG tick_end_lo, tick_end_hi; // // Read start counters // __asm { rdtsc mov tick_curr_lo, eax mov tick_curr_hi, edx } tick_end_hi = tick_curr_hi; tick_end_lo = tick_curr_lo + nsec; // // Handle rollover // if (tick_end_lo < tick_curr_lo) { tick_end_hi++; } // // Do nothing loop for specified time // do { __asm { rdtsc mov tick_curr_lo, eax mov tick_curr_hi, edx } } while ((tick_curr_hi <= tick_end_hi) && (tick_curr_lo < tick_end_lo)); } static _declspec(noinline) ULONG SlwValue( ULONG r, ULONG f ) { return ((r << 28) | (f << 24) | (r << 20) | (f << 16) | (r << 12) | (f << 8) | (r << 4) | f); } static _declspec(noinline) ULONG FromFixedPoint( ULONG d ) { return ((((d) + (0x1 << 19)) >> 20) & 0xf); } FORCEINLINE ULONG ReadPCIConfig( ULONG Reg ) { _outpd(PCI_TYPE1_ADDR_PORT, Reg); return _inpd(PCI_TYPE1_DATA_PORT); } VOID BldrSetDrvSlwFromROMData( PVOID RegisterBase ) { ULONG *offsetp; PROM_DATATBL pROMData; PDRVSLWCALPARAMS pDrvSlwCalParams; PDRVSLWPADPARAMS pPadParams, pDrvSlwPadParam_lo, pDrvSlwPadParam_hi; ULONG adr_drv_fall, adr_drv_rise, adr_drv, clk_drv_fall, clk_drv_rise, clk_drv, dat_drv_fall, dat_drv_rise, dat_drv, dqs_drv_fall, dqs_drv_rise, dqs_drv; ULONG adr_slw_fall, adr_slw_rise, adr_slw, clk_slw_fall, clk_slw_rise, clk_slw, dat_slw_fall, dat_slw_rise, dat_slw, dqs_slw_fall, dqs_slw_rise, dqs_slw; ULONG dqs_ib_del, data_ib_del, clk_ic_del, fbio_dly; ULONG vccq_a, vccq_b, vccq_c, vccq_d, avg_vccq; ULONG vcc_a, vcc_b, vcc_c, vcc_d, avg_vcc; ULONG ratio_a, ratio_b; REGVALUEPAIR RegValuePair[9]; ULONG Pass; ULONG i; #ifdef CHECKCACHE ULONG BeginHits; ULONG EndHits; #endif ULONG StrapEMRS; // // Find start of ROM datatbl containing drive/slew data // offsetp = (ULONG *)(BLDR_ROM_BASE + BLDR_ROM_DATA_OFFSET); pROMData = (PROM_DATATBL)(BLDR_ROM_BASE + *offsetp); pDrvSlwCalParams = &pROMData->calParams; // // Check to see if its Samsung or a Micron memory bits 19:18 // StrapEMRS = REG_RD32(RegisterBase, NV_PEXTDEV_BOOT_0) & 0x0000C0000; if (StrapEMRS == 0) { pPadParams = &(pROMData->MicronParams[0]); BldrPrint(("Bldr: Memory type: Micron")); } else { pPadParams = &(pROMData->SamsungParams[0]); BldrPrint(("Bldr: Memory type: Samsung")); } #ifdef BLDRSPEW // // Dump out memory sizes etc // if (ReadPCIConfig(CR_CPU_MEMTOP) == CR_CPU_MEMTOP_LIMIT_64MB) { BldrPrint(("Bldr: Memory size: 64MB")); } else { BldrPrint(("Bldr: Memory size: 128MB")); } BldrPrint(("Bldr: Memory test results: 4MB/2MB=%x, Pass/Fail=%x", ReadPCIConfig(MCP_USBA_CFG_15) & 0xff, ReadPCIConfig(MCP_USBB_CFG_15) & 0xff)); BldrPrint(("Bldr: NV_PBUS_FBIO_DLY=%x", REG_RD32(RegisterBase, NV_PBUS_FBIO_DLY))); BldrPrint(("Bldr: PadParams=%x", pPadParams)); BldrPrint(("Bldr: DriveSlewCalibration started")); #endif // // Read the calibration for outbound/inbound // REG_WR32(RegisterBase, NV_PBUS_FBIO_CALEN, NV_PBUS_FBIO_CALEN_VAL); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCCQ); BldrDelay(10*1000); // 10 usecs vccq_a = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCCQ+1); BldrDelay(10*1000); // 10 usecs vccq_b = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCCQ+2); BldrDelay(10*1000); // 10 usecs vccq_c = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCCQ+3); BldrDelay(10*1000); // 10 usecs vccq_d = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); avg_vccq = (vccq_a + vccq_b + vccq_c + vccq_d) / 4; BldrPrint(("Bldr: DriveSlewCalibration VCCQ_A=0x%x", vccq_a)); BldrPrint(("Bldr: DriveSlewCalibration VCCQ_B=0x%x", vccq_b)); BldrPrint(("Bldr: DriveSlewCalibration VCCQ_C=0x%x", vccq_c)); BldrPrint(("Bldr: DriveSlewCalibration VCCQ_D=0x%x", vccq_d)); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCC); BldrDelay(10*1000); // 10 usecs vcc_a = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCC+1); BldrDelay(10*1000); // 10 usecs vcc_b = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCC+2); BldrDelay(10*1000); // 10 usecs vcc_c = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALSEL, NV_PBUS_FBIO_CALSEL_VCC+3); BldrDelay(10*1000); // 10 usecs vcc_d = REG_RD32(RegisterBase, NV_PBUS_FBIO_CALCNT); avg_vcc = (vcc_a + vcc_b + vcc_c + vcc_d) / 4; BldrPrint(("Bldr: DriveSlewCalibration VCC_A=0x%x", vcc_a)); BldrPrint(("Bldr: DriveSlewCalibration VCC_B=0x%x", vcc_b)); BldrPrint(("Bldr: DriveSlewCalibration VCC_C=0x%x", vcc_c)); BldrPrint(("Bldr: DriveSlewCalibration VCC_D=0x%x", vcc_d)); REG_WR32(RegisterBase, NV_PBUS_FBIO_CALEN, 0); // // Determine outbound process params and interp ratio_a/ratio_b // if (avg_vccq <= pDrvSlwCalParams->SlowCountAvg) { BldrPrint(("Bldr: Outbound interp slow ext -> slow avg")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwExtSlow]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwAvgSlow]; ratio_a = TOFIXEDPOINT(avg_vccq - pDrvSlwCalParams->SlowCountExt) / (pDrvSlwCalParams->SlowCountAvg - pDrvSlwCalParams->SlowCountExt); if (ratio_a & 0x80000000) ratio_a = 0; } else if (avg_vccq >= pDrvSlwCalParams->FastCountAvg) { BldrPrint(("Bldr: Outbound interp fast avg -> fast ext")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwAvgFast]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwExtFast]; ratio_a = TOFIXEDPOINT(avg_vccq - pDrvSlwCalParams->FastCountAvg) / (pDrvSlwCalParams->FastCountExt - pDrvSlwCalParams->FastCountAvg); if (ratio_a > TOFIXEDPOINT(1)) ratio_a = 1; } else if (avg_vccq < pDrvSlwCalParams->TypiCount) { BldrPrint(("Bldr: Outbound interp slow avg -> typical")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwAvgSlow]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwTypical]; ratio_a = TOFIXEDPOINT(avg_vccq - pDrvSlwCalParams->SlowCountAvg) / (pDrvSlwCalParams->TypiCount - pDrvSlwCalParams->SlowCountAvg); } else { BldrPrint(("Bldr: Outbound interp typical -> fast avg")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwTypical]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwAvgFast]; ratio_a = TOFIXEDPOINT(avg_vccq - pDrvSlwCalParams->TypiCount) / (pDrvSlwCalParams->FastCountAvg - pDrvSlwCalParams->TypiCount); } ratio_b = TOFIXEDPOINT(1) - ratio_a; adr_drv_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->AdrDrvFall * ratio_b) + (pDrvSlwPadParam_hi->AdrDrvFall * ratio_a)); adr_drv_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->AdrDrvRise * ratio_b) + (pDrvSlwPadParam_hi->AdrDrvRise * ratio_a)); adr_slw_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->AdrSlwFall * ratio_b) + (pDrvSlwPadParam_hi->AdrSlwFall * ratio_a)); adr_slw_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->AdrSlwRise * ratio_b) + (pDrvSlwPadParam_hi->AdrSlwRise * ratio_a)); clk_drv_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->ClkDrvFall * ratio_b) + (pDrvSlwPadParam_hi->ClkDrvFall * ratio_a)); clk_drv_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->ClkDrvRise * ratio_b) + (pDrvSlwPadParam_hi->ClkDrvRise * ratio_a)); clk_slw_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->ClkSlwFall * ratio_b) + (pDrvSlwPadParam_hi->ClkSlwFall * ratio_a)); clk_slw_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->ClkSlwRise * ratio_b) + (pDrvSlwPadParam_hi->ClkSlwRise * ratio_a)); dat_drv_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DatDrvFall * ratio_b) + (pDrvSlwPadParam_hi->DatDrvFall * ratio_a)); dat_drv_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DatDrvRise * ratio_b) + (pDrvSlwPadParam_hi->DatDrvRise * ratio_a)); dat_slw_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DatSlwFall * ratio_b) + (pDrvSlwPadParam_hi->DatSlwFall * ratio_a)); dat_slw_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DatSlwRise * ratio_b) + (pDrvSlwPadParam_hi->DatSlwRise * ratio_a)); dqs_drv_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DqsDrvFall * ratio_b) + (pDrvSlwPadParam_hi->DqsDrvFall * ratio_a)); dqs_drv_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DqsDrvRise * ratio_b) + (pDrvSlwPadParam_hi->DqsDrvRise * ratio_a)); dqs_slw_fall = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DqsSlwFall * ratio_b) + (pDrvSlwPadParam_hi->DqsSlwFall * ratio_a)); dqs_slw_rise = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DqsSlwRise * ratio_b) + (pDrvSlwPadParam_hi->DqsSlwRise * ratio_a)); // // Determine inbound process params and interp ratio_a/ratio_b // if (avg_vcc <= pDrvSlwCalParams->SlowCountBAvg) { BldrPrint(("Bldr: Inbound interp slow ext -> slow avg")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwExtSlow]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwAvgSlow]; ratio_a = TOFIXEDPOINT(avg_vcc - pDrvSlwCalParams->SlowCountBExt) / (pDrvSlwCalParams->SlowCountBAvg - pDrvSlwCalParams->SlowCountBExt); if (ratio_a & 0x80000000) { ratio_a = 0; } } else if (avg_vcc >= pDrvSlwCalParams->FastCountBAvg) { BldrPrint(("Bldr: Inbound interp fast avg -> fast ext")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwAvgFast]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwExtFast]; ratio_a = TOFIXEDPOINT(avg_vcc - pDrvSlwCalParams->FastCountBAvg) / (pDrvSlwCalParams->FastCountBExt - pDrvSlwCalParams->FastCountBAvg); if (ratio_a > TOFIXEDPOINT(1)) { ratio_a = 1; } } else if (avg_vcc < pDrvSlwCalParams->TypiCountB) { BldrPrint(("Bldr: Inbound interp slow avg -> typical")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwAvgSlow]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwTypical]; ratio_a = TOFIXEDPOINT(avg_vcc - pDrvSlwCalParams->SlowCountBAvg) / (pDrvSlwCalParams->TypiCountB - pDrvSlwCalParams->SlowCountBAvg); } else { BldrPrint(("Bldr: Inbound interp typical -> fast avg")); pDrvSlwPadParam_lo = &pPadParams[DrvSlwTypical]; pDrvSlwPadParam_hi = &pPadParams[DrvSlwAvgFast]; ratio_a = TOFIXEDPOINT(avg_vcc - pDrvSlwCalParams->TypiCountB) / (pDrvSlwCalParams->FastCountBAvg - pDrvSlwCalParams->TypiCountB); } ratio_b = TOFIXEDPOINT(1) - ratio_a; dqs_ib_del = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DqsInbDely * ratio_b) + (pDrvSlwPadParam_hi->DqsInbDely * ratio_a)); data_ib_del = FROMFIXEDPOINT((pDrvSlwPadParam_lo->DataInbDely * ratio_b) + (pDrvSlwPadParam_hi->DataInbDely * ratio_a)); clk_ic_del = FROMFIXEDPOINT((pDrvSlwPadParam_lo->ClkIcDely * ratio_b) + (pDrvSlwPadParam_hi->ClkIcDely * ratio_a)); // // NOTE: We must write all 9 register values without accessing memory // Therefore, all code and data must be present in the cache // // // Clear the cache // __asm wbinvd; // // Save the final register values and registers in an array // adr_drv = SLW_VALUE(adr_drv_rise, adr_drv_fall); RegValuePair[0].Register = NV_PBUS_FBIO_ADRDRV; RegValuePair[0].Value = adr_drv; adr_slw = SLW_VALUE(adr_slw_rise, adr_slw_fall); RegValuePair[1].Register = NV_PBUS_FBIO_ADRSLW; RegValuePair[1].Value = adr_slw; clk_drv = SLW_VALUE(clk_drv_rise, clk_drv_fall); RegValuePair[2].Register = NV_PBUS_FBIO_CLKDRV; RegValuePair[2].Value = clk_drv; clk_slw = SLW_VALUE(clk_slw_rise, clk_slw_fall); RegValuePair[3].Register = NV_PBUS_FBIO_CLKSLW; RegValuePair[3].Value = clk_slw; dat_drv = SLW_VALUE(dat_drv_rise, dat_drv_fall); RegValuePair[4].Register = NV_PBUS_FBIO_DATDRV; RegValuePair[4].Value = dat_drv; dat_slw = SLW_VALUE(dat_slw_rise, dat_slw_fall); RegValuePair[5].Register = NV_PBUS_FBIO_DATSLW; RegValuePair[5].Value = dat_slw; dqs_drv = SLW_VALUE(dqs_drv_rise, dqs_drv_fall); RegValuePair[6].Register = NV_PBUS_FBIO_DQSDRV; RegValuePair[6].Value = dqs_drv; dqs_slw = SLW_VALUE(dqs_slw_rise, dqs_slw_fall); RegValuePair[7].Register = NV_PBUS_FBIO_DQSSLW; RegValuePair[7].Value = dqs_slw; fbio_dly = (dqs_ib_del << 29) | (clk_ic_del << 27) | (data_ib_del << 24) | (dqs_ib_del << 21) | (clk_ic_del << 19) | (data_ib_del << 16) | (dqs_ib_del << 13) | (clk_ic_del << 11) | (data_ib_del << 8) | (dqs_ib_del << 5) | (clk_ic_del << 3) | data_ib_del; RegValuePair[8].Register = NV_PBUS_FBIO_DLY; RegValuePair[8].Value = fbio_dly; // // Read the array byte by byte to ensure it is in cache. // for (i = 0; i < 9; i++) { if (RegValuePair[i].Register == 0 || RegValuePair[i].Value == 0) { RegValuePair[i].Register = 0; } } #ifdef CHECKCACHE SetPMCEvent(PMC_EVENT); #endif // // First pass is a NOP and used to ensure the code is in cache // Second pass actually writes the register values out // for (Pass = 0; Pass < 2; Pass++) { #ifdef CHECKCACHE BeginHits = ReadPMCLow(); #endif for (i = 0; i < 9; i++) { if (Pass == 1) { REG_WR32(RegisterBase, RegValuePair[i].Register, RegValuePair[i].Value); } } #ifdef CHECKCACHE EndHits = ReadPMCLow(); #endif } #ifdef CHECKCACHE BldrPrint(("Bldr: Mem reads during register writes=%d (must be zero) (%d,%d)", (EndHits - BeginHits), EndHits, BeginHits)); #endif #ifdef BLDRSPEW for (i = 0; i < 9; i++) { BldrPrint(("Bldr: DriveSlewCalibration register %x=%x", RegValuePair[i].Register, RegValuePair[i].Value)); } BldrPrint(("Bldr: DriveSlewCalibration setup finished")); #endif } VOID BldrSetupLDTBus( VOID ) { ULONG ldt_data; // // NOTE: Since we have to reset the LDT bus as part of this initialization, we // can't run this code out of the ROM (while resetting the bus, we won't be // able to execute the code to reenable it), so it has to be in memory. // BldrPrint(("Bldr: Enabling LDT DWORD flow control")); // // Enable DWORD flow control in the MCP // _outpd(PCI_TYPE1_ADDR_PORT, MCP_LEG_CFG_21); ldt_data = _inpd(PCI_TYPE1_DATA_PORT); ldt_data |= LDT_FLOW_CONTROL_BITS; _outpd(PCI_TYPE1_DATA_PORT, ldt_data); // // Enable DWORD flow control in the NV2A // _outpd(PCI_TYPE1_ADDR_PORT, CR_CPU_LDT_1); ldt_data = _inpd(PCI_TYPE1_DATA_PORT); ldt_data |= LDT_FLOW_CONTROL_BITS; _outpd(PCI_TYPE1_DATA_PORT, ldt_data); // // Force a reset of the LDT link. This is done by toggling the // WARM_RESET bit (bit0), where a 0 initiates the reset and a 1 // completes it // _outpd(PCI_TYPE1_ADDR_PORT, CR_CPU_LDT_3_USER_CNFG); ldt_data = _inpd(PCI_TYPE1_DATA_PORT); _outpd(PCI_TYPE1_DATA_PORT, (ldt_data & 0xFFFFFFFE)); _outpd(PCI_TYPE1_DATA_PORT, ldt_data); // // Allow the LDT->1GB transactions by ensuring that the GART is visible to LDT. // Due to the fact there is a shadow copy of this register, we must do this write // even though the power on value is already 0x100 // _outpd(PCI_TYPE1_ADDR_PORT, CR_CPU_GRAP); _outpd(PCI_TYPE1_DATA_PORT, 0x100); }