↓ Skip to main content

IT8528E Embedded Controller Firmware Architecture and Code Flow

IT8528E Embedded Controller Firmware Architecture and Code Flow

The ITE IT8528E is a highly integrated embedded controller (EC) used in notebook computer motherboards. Built around an 8032 Twin Turbo 8051 core, it provides host communication, keyboard scanning, serial buses, analog acquisition, timers, GPIO, and power-management functions.

The hardware provides the interfaces, but the firmware determines how they work together. Its initialization routines establish the controller’s operating environment, code banking expands the available firmware space, the main loop coordinates foreground execution, and timer hooks schedule periodic tasks such as power-button detection, battery monitoring, and system-event processing.

This article examines the IT8528E hardware architecture and a representative Keil uVision firmware project, focusing on its directory structure, startup sequence, core and OEM initialization, priority-based service dispatcher, and periodic OEM hooks.

🧠 1. IT8528E Overview and Specifications
#

The IT8528E integrates an 8032-compatible Twin Turbo 8051 core with a range of peripheral interfaces for motherboard management. It supports code banking for firmware stored in external SPI Flash and provides the hardware interfaces needed for communication between the EC, host processor, and onboard devices.

Core specifications
#

Feature Specification
CPU core 8032 Twin Turbo 8051
Instruction cycle One machine cycle per instruction cycle, as specified in the source material
Maximum operating frequency 10 MHz
Code space Code banking with support for up to 4 MB of SPI Flash
Host bus LPC 1.1 with Serial IRQ
SMBus Four-channel Host/Slave interface, up to 400 kHz
GPIO Ports A–J with multifunction pin multiplexing
PWM 8 channels
ADC 12 channels, 10-bit resolution
DAC 4 channels, 8-bit resolution
Package TQFP-128

The controller’s peripherals can be grouped into six functional categories: host-side interfaces, serial communication buses, analog interfaces, timing and control units, human-machine interfaces, and general-purpose I/O and power management.

Together, these modules support firmware functions such as keyboard scanning, smart-battery communication, hardware monitoring, fan-speed control, event processing, and motherboard power sequencing.

For broader context on how embedded controller firmware fits into the platform’s startup environment, see UEFI BIOS Architecture and Boot Flow.

🔌 2. Internal Architecture and Peripheral Modules
#

2.1 Host-side bridging interfaces
#

The host-facing interfaces allow the EC to exchange data with the CPU, BIOS, and supported platform components.

LPC bus

The Low Pin Count (LPC) bus provides the traditional host interface used by the CPU and BIOS to access supported controller registers and Super I/O functions. The supplied material also identifies the I-Bus arbiter as part of this interface architecture.

EC2I bridge

The EC2I bridge allows the EC core to access selected host-side registers, including registers associated with PNPCFG and SWUC. The supplied description identifies register-access coordination, lock protection, and bus arbitration as part of its role.

PECI interface

The Platform Environment Control Interface (PECI) provides access to CPU platform telemetry, including CPU core temperature information where supported. In the firmware project, PECI-related logic is associated with routines such as Init_PECI() and PECI_PowerCtrlCenter().

2.2 Serial communication buses
#

The IT8528E includes several serial interfaces for communication with external devices.

Interface Main purpose
SMBus Communication with smart batteries, power-management ICs, hardware-monitoring sensors, and EEPROMs
SSPI Serial SPI communication, including access to external Flash
UART Debug output and communication with supported external modules
CEC Consumer Electronics Control for compatible HDMI-connected devices

SMBus is particularly important for battery-management and monitoring functions. The supplied firmware includes core-level SMBus support and OEM routines for configuring channels and coordinating transactions.

SPI-related initialization includes functions such as GPIO_HSPI_INIT() and ChangeSPIFlashReadMode(), depending on the selected configuration.

2.3 Analog interfaces: ADC and DAC
#

The analog interfaces allow firmware to acquire sensor signals and produce analog outputs.

ADC — Analog-to-Digital Converter

The controller provides 12 ADC channels with 10-bit resolution according to the supplied specifications. ADC inputs can be used for voltage monitoring and temperature sensing through NTC thermistors.

The firmware examples include two dynamic scanning functions:

ScanADCDyChannel1();
ScanADCDyChannel2();

These calls are associated with 10 ms OEM timer hooks. The first call is commented out in the supplied Event A example, while the second is conditionally compiled in Event B.

DAC — Digital-to-Analog Converter

The controller provides four DAC channels with 8-bit resolution. These channels support analog-output functions where required by the board design and firmware configuration.

2.4 Timers, PWM, watchdog, and counters
#

The controller’s timing and control modules support periodic execution, signal generation, watchdog operations, and pulse measurement.

  • PWM: Provides pulse-width modulation for applications such as fan-speed regulation and LED brightness control.
  • TMR: General-purpose 8-bit timers for timing and pulse-related operations.
  • ETWD: External timer and watchdog functionality used for monitoring and system recovery.
  • GCTRL: General counter functionality, including pulse counting for fan tachometer measurements.

Fan control commonly combines a PWM output with tachometer feedback. The firmware can use the counter and timer functions to measure fan activity and apply the relevant OEM control policy.

2.5 Human-machine and legacy interfaces
#

The IT8528E includes interfaces for keyboard input, pointing devices, infrared reception, and legacy peripheral functions.

Module Description
KBS Internal keyboard-matrix scanning
PS/2-compatible interface External keyboard and mouse support
Parallel port Traditional parallel-port functionality
CIR Consumer infrared reception

The KBS hardware supports matrix scanning, while PS/2-related functions handle compatible external devices and their communication paths.

The project separates these responsibilities across modules such as CORE_SCAN.C, CORE_PS2.C, CORE_XLT.C, and corresponding OEM modules.

2.6 GPIO, interrupts, and power management
#

These modules connect firmware logic to physical board signals and system-state transitions.

  • EGPC: Configurable GPIO for buttons, LEDs, and other control signals.
  • ECPM: EC clock and power management, including clock control and low-power functionality.
  • INT: Interrupt handling and reporting.
  • WUC: Wake-up source management.
  • BRAM: Battery-backed SRAM for retaining supported data across power loss.

GPIO pins may support multiple functions. Their effective role therefore depends on pin multiplexing, register configuration, and the motherboard’s electrical design.

Peripheral summary
#

Category Interfaces and modules Typical use
Host bridge LPC, EC2I, PECI Host communication and CPU telemetry
Serial buses SMBus, SSPI, UART, CEC Battery access, Flash communication, debugging, peripheral control
Analog interface ADC, DAC Voltage and temperature acquisition, analog output
Timer and control PWM, TMR, ETWD, GCTRL Fan control, timing, watchdog, pulse counting
Human-machine interface KBS, PS/2, CIR, parallel port Keyboard, mouse, infrared, legacy peripherals
Basic I/O and power GPIO, INT, ECPM, WUC, BRAM Buttons, LEDs, interrupts, wake-up, power management, retained data

🗂️ 3. Firmware Project Architecture
#

The firmware project is compiled using Keil uVision and separates common controller functionality from motherboard-specific implementations.

The source structure can be understood as three conceptual layers:

  • CORE layer: Shared firmware services, including host communication, timers, GPIO, SMBus, memory management, and service scheduling.
  • OEM layer: Motherboard-specific configuration and behavior, including power sequencing, battery management, fan control, LEDs, and OEM event processing.
  • CHIP layer: Chip-specific configuration, startup support, and low-level initialization.

The supplied directory tree primarily exposes CORE and OEM directories, while chip-specific support is represented by files such as CORE_CHIPREGS.C and STARTUP.A51.

3.1 Directory structure
#

The following tree summarizes the project organization shown in the source material.

KB01_EC/
└── KB01EC/                          # Keil main project
    ├── CORE_COMMON/                 # Resident core layer
    │   ├── CORE_ACPI.C              # ACPI EC interface
    │   ├── CORE_ASM.C               # Assembly helper functions
    │   ├── CORE_BITADDR.C           # Bit-addressable operations
    │   ├── CORE_GPIO.C              # GPIO control and initialization
    │   ├── CORE_HOSTIF.C             # Host interface
    │   ├── CORE_INIT.C              # Core initialization
    │   ├── CORE_IRQ.C                # Interrupt vectors and dispatch
    │   ├── CORE_MAIN.C               # Main loop and service scheduler
    │   ├── CORE_Memory.C             # Memory management
    │   ├── CORE_PORT6064.C           # KBC ports 60/64
    │   ├── CORE_SMBUS.C              # SMBus protocol support
    │   ├── CORE_TIMERS.C             # Timer management
    │   ├── CORE_CHIPREGS.C            # Chip register definitions
    │   ├── CORE_ITEString.C           # String utilities
    │   ├── UART.C                    # Debug serial port
    │   ├── L51_BANK.A51              # Bank-switching assembly
    │   └── STARTUP.A51               # 8051 startup code
    ├── CORE_BANK0/                   # Switchable core bank 0
    │   ├── CORE_FLASH.C              # SPI Flash read/write
    │   ├── CORE_PS2.C                # PS/2 handling
    │   ├── CORE_SCAN.C               # Keyboard-matrix scanning
    │   └── CORE_XLT.C                # Scan-code translation tables
    ├── CORE_BANK1/                   # Additional core bank
    └── OEM/                          # OEM customization
        └── Example/
            ├── OEM_BANK0/
            │   ├── OEM_6064.C
            │   ├── OEM_ACPI.C
            │   ├── OEM_ADC.C
            │   ├── OEM_BATTERY.C      # Battery management
            │   ├── OEM_CIR.C
            │   ├── OEM_DEBUG.C
            │   ├── OEM_ECD.C
            │   ├── OEM_EVENT.C
            │   ├── OEM_FAN.C
            │   ├── OEM_FLASH.C
            │   ├── OEM_GPIO.C         # GPIO configuration
            │   ├── OEM_HOSTIF.C
            │   ├── OEM_HSPL.C
            │   ├── OEM_INIT.C
            │   ├── OEM_IRQ.C
            │   ├── OEM_LCD.C
            │   ├── OEM_LED.C
            │   ├── OEM_LPC.C
            │   ├── OEM_MAILBOX.C
            │   ├── OEM_MAIN.C         # OEM timer hooks
            │   ├── OEM_MEMORY.C
            │   ├── OEM_MMB.C
            │   ├── OEM_PECI.C
            │   ├── OEM_PM3.C
            │   ├── OEM_PORT6064.C
            │   ├── OEM_POWER.C        # Power-sequencing logic
            │   ├── OEM_PS2.C
            │   ├── OEM_SCANTABS.C
            │   ├── OEM_SMBUS.C
            │   ├── OEM_SPI.C
            │   ├── OEM_TIMER.C
            │   └── OEM_VER.C
            ├── OEM_BANK1/
            ├── OEM_BANK2/
            ├── OEM_BANK3/
            └── INCLUDE/
                ├── CORE_INCLUDE.H
                └── OEM_INCLUDE.H

The project directories separate shared infrastructure from functions specific to a motherboard.

Directory or file Responsibility
CORE_COMMON Resident shared code, including main-loop scheduling and core services
CORE_BANK0 Banked core functionality such as Flash access, PS/2 handling, and keyboard scanning
CORE_BANK1 Additional banked core code
OEM_BANK0–OEM_BANK3 Board-specific functionality distributed across OEM banks
INCLUDE Shared header files for CORE and OEM interfaces
STARTUP.A51 Startup and low-level execution setup
L51_BANK.A51 Assembly support for code-bank switching

For example, CORE_SMBUS.C is the starting point for examining shared SMBus services, while OEM_BATTERY.C contains motherboard-specific battery logic in this project. Similarly, CORE_SCAN.C and OEM_SCANTABS.C are relevant when tracing keyboard scanning and scan-code configuration.

3.2 Code banking and memory layout
#

The standard 8051 code-address space is limited to 64 KB. The IT8528 firmware architecture uses code banking to address a larger external SPI Flash image, with support described in the source material for up to 4 MB.

The project divides the EC code-address space into two regions.

Region Address range Size Role
COMMON 0x0000–0x7FFF 32 KB Resident code, including main(), interrupt routines, and core scheduling
BANK 0x8000–0xFFFF 32 KB Switchable window used for banked functions

The COMMON area remains resident, while the banked region maps the selected code bank into the upper portion of the code-address space.

This arrangement allows firmware modules to be distributed across multiple banks without requiring the entire firmware image to be visible simultaneously.

Bank-switching mechanism
#

The source identifies L51_BANK.A51 as the assembly component responsible for bank switching. Its described mechanism uses the BANKSEL register to select a bank and transfers control using a long jump (LJMP).

The supplied material gives 0x2002 as an example address for BANKSEL.

// Conceptual example from the supplied firmware description.

// Initialize the code-banking state.
ResetBANKDATA();

// Calls to banked functions use the project's
// compiler, linker, and assembly bank-switching support.

// The switching mechanism selects the required bank
// through the configured BANKSEL register.

Before a banked function is invoked, the firmware’s generated call sequence and assembly support arrange for the relevant bank to be selected. The exact behavior depends on the compiler and linker configuration as well as the implementation in L51_BANK.A51.

ResetBANKDATA() is called during startup to initialize the bank-switching state to Bank 0. This gives the firmware a known starting point before executing subsequent code.

🚀 4. Main Loop and Initialization Flow
#

The main execution flow starts in main(). The function establishes the initial CPU and banking state, selects an initialization path, configures keyboard scan pins, and enters the continuous polling loop.

The initialization path depends on the return value of Hook_ECReturnMainFuncKeepCondition().

4.1 The main() function
#

The following code preserves the startup branches and main-loop structure shown in the supplied source.

void main(void)
{
    DisableAllInterrupt();
    SP = 0xC0;
    ResetBANKDATA();

    // Branch 1: Exiting Follow Mode or EC Scratch ROM
    if(Hook_ECReturnMainFuncKeepCondition() == 0x33)
    {
        CLEAR_MASK(FBCFG, SSMC);
        _nop_();

        MPRECF = 0x01;
        _nop_(); MPRECF = 0x01;
        _nop_(); MPRECF = 0x01;
        _nop_(); MPRECF = 0x01;
        _nop_(); _nop_();

        WinFlashMark = 0x00;
        Hook_ECExitFollowMode();

        Init_Timers();
        EnableModuleInterrupt();
    }
    // Branch 2: Normal cold reset
    else
    {
        Core_Initialization();
        Oem_Initialization();
        InitEnableInterrupt();

        #ifdef UART_Debug
        Init_UART();
        printf("\n***************************************");
        printf("\nEC Init OK !!!");
        printf("\n***************************************\n");
        #endif
    }

    KSOLGCTRL |= 0x07;
    KSOLGOEN  &= 0xF8;

    while(1)
    {
        if(OEM_SkipMainServiceFunc() == Normal_MainService)
        {
            main_service();
            EnableModuleInterrupt();

            _nop_();
            _nop_();
            _nop_();
            _nop_();
        }

        #if TouchPad_only
        #else
        if(PS2CheckPendingISR() == 0x00)
        {
            ScanAUXDeviceStep();
        }
        #endif

        DebugUart();

        #ifdef SMBusServiceCenterFunc
        if((Service == 0x00) && (Service1 == 0x00)
            && (CheckSMBusNeedService() == SMBus_ClearService))
        #else
        if((Service == 0x00) && (Service1 == 0x00))
        #endif
        {
            PCON = 1;
        }
    }
}

Startup stages
#

1. Establish the initial execution state
#

The function begins by disabling all interrupts, setting the stack pointer to 0xC0, and calling ResetBANKDATA().

These operations establish the initial execution environment before the firmware enters either initialization path.

2. Evaluate the startup pathway
#

The condition

Hook_ECReturnMainFuncKeepCondition() == 0x33

selects the Follow Mode / EC Scratch ROM exit path when it evaluates to true.

In that path, the code clears the SSMC bit in FBCFG, performs repeated writes to MPRECF, clears WinFlashMark, invokes Hook_ECExitFollowMode(), initializes timers, and enables module interrupts.

The repeated MPRECF writes and intervening _nop_() instructions are part of the supplied low-level sequence.

If the condition does not match 0x33, the function follows the normal cold-reset path.

3. Perform normal initialization
#

The normal startup path executes the following functions in order:

Core_Initialization();
Oem_Initialization();
InitEnableInterrupt();

The CORE layer establishes shared controller functionality. The OEM layer then applies board-specific configuration, after which the configured interrupt environment is initialized and enabled.

If UART_Debug is defined, the firmware initializes UART and prints an initialization message.

4. Configure keyboard scan pins
#

After either startup path, the following register operations execute:

KSOLGCTRL |= 0x07;
KSOLGOEN  &= 0xF8;

These operations set the lower three bits in KSOLGCTRL and clear the lower three bits in KSOLGOEN, following the register conventions used by this firmware.

5. Enter the main polling loop
#

The infinite loop repeatedly performs four kinds of work:

  1. Calls OEM_SkipMainServiceFunc() to determine whether the standard service scheduler should run.
  2. Calls main_service() and re-enables module interrupts when the normal scheduling path is selected.
  3. Processes PS/2 auxiliary-device scanning when TouchPad_only is not enabled and no PS/2 ISR is pending.
  4. Calls DebugUart() and checks whether the service flags and any required SMBus work have cleared.

When no relevant service work remains, the firmware writes PCON = 1 to enter the 8051 Idle mode. An interrupt can wake the controller and allow the main loop to resume.

The resulting execution pattern is:

Hardware or host event → pending service state → foreground processing → idle when no eligible work remains.

4.2 Core initialization: Core_Initialization()
#

Core_Initialization() initializes shared EC functions before the firmware enters the OEM initialization stage.

void Core_Initialization(void)
{
    Init_Kbd();         // Keyboard module initialization
    Core_InitSio();     // SIO/Super I/O initialization
    cold_reset();       // Cold-reset processing
    common_reset();     // Common peripheral reset
    Init_Mouse();       // Mouse and touchpad variables
    Init_Timers();      // Timer initialization
    Core_Init_Regs();   // Core register initialization
    Core_Init_SMBus();  // SMBus initialization

    #if !TouchPad_only
    // Delay auxiliary-device scanning
    // SetPS2SkipScanTime(60);
    #endif
}

The function is organized around a sequence of shared initialization operations.

Function Purpose
Init_Kbd() Initializes the keyboard module and KBS-related functionality
Core_InitSio() Initializes shared SIO/Super I/O functionality
cold_reset() Executes the project’s cold-reset processing
common_reset() Performs common peripheral reset operations
Init_Mouse() Initializes mouse and touchpad variables
Init_Timers() Prepares timer functionality
Core_Init_Regs() Loads core register settings, including core_reg_init_table
Core_Init_SMBus() Initializes shared SMBus functionality

The ordering establishes input and communication functionality before later foreground processing depends on those services.

The source includes a commented call to SetPS2SkipScanTime(60), described as a delay for auxiliary-device scanning. Because the call is commented out in the supplied code, it is not executed by this snippet.

4.3 OEM initialization: Oem_Initialization()
#

After shared core initialization, Oem_Initialization() configures platform-specific functionality. Its conditional blocks enable selected features according to the project’s compile-time definitions.

void Oem_Initialization(void)
{
    // Init_GPIO();
    // InitSio();

    #ifdef HSPI
    GPIO_HSPI_INIT();
    #endif

    #ifdef SPIReadMode
    ChangeSPIFlashReadMode(SPIReadMode);
    #endif

    #ifdef HSRSMode
    ChangeHSRSMode(HSRSMode);
    #endif

    #ifdef PLLFrequency
    ChangePLLFrequency(PLLFrequency);
    #endif

    #ifdef PECI_Support
    Init_PECI();
    #endif

    Init_Regs();

    #ifdef SMBusChannel4Support
    InitSMBusChannel4();
    #endif

    Init_SMBus_Regs();

    // #ifdef ADCFuncSupport
    // Init_ADC();
    // #endif

    #if CIRFuncSupport
    Init_CIR();
    #endif

    Init_Cache();
    Init_PS2Port();

    #ifdef SysMemory2ECRam
    InitSysMemory2ECRam();
    #endif

    InitSMBus();
    ResetSCIEvent();
    Init_OEMVariable();

    // Check the extended keyboard-matrix configuration
    if(ExtendScanPin == 0x02)
    {
        if(ExternMatrixGPO)
        {
            ExtendScanPin++;
        }
    }
}

Initialization phases
#

1. Clock and Flash configuration

The first conditional blocks configure selected SPI, reset, and clock features.

  • GPIO_HSPI_INIT() configures pins for the high-speed SPI implementation.
  • ChangeSPIFlashReadMode() applies the configured Flash read mode.
  • ChangeHSRSMode() configures the selected high-speed reset mode.
  • ChangePLLFrequency() applies the specified EC clock configuration.

These calls are conditional on the relevant compile-time definitions.

2. Peripheral initialization

Init_PECI() initializes PECI functionality when PECI_Support is enabled.

InitSMBusChannel4() initializes the fourth SMBus channel when SMBusChannel4Support is enabled. The subsequent calls Init_SMBus_Regs() and InitSMBus() configure additional SMBus state and functionality.

Init_CIR() initializes the consumer infrared receiver when CIRFuncSupport is enabled. Init_PS2Port() initializes the PS/2-related interface.

The Init_ADC() call appears inside a commented-out block in the supplied code. It is not an active initialization call in this example.

3. Register, cache, and variable setup

Init_Regs() applies OEM register configuration, while Init_Cache() initializes the project’s cache functionality.

InitSysMemory2ECRam() is called when SysMemory2ECRam is enabled. The routine is associated with mapping system memory into EC RAM.

ResetSCIEvent() resets the firmware’s SCI-event state, and Init_OEMVariable() initializes OEM-specific variables.

4. Extended keyboard-matrix configuration

The last conditional checks ExtendScanPin and ExternMatrixGPO. If ExtendScanPin equals 0x02 and ExternMatrixGPO is active, ExtendScanPin is incremented.

The supplied description associates this logic with extending the keyboard matrix through additional scan lines, including KSO16 and KSO17.

Initialization function mapping
#

Function or setting Purpose Related module
GPIO_HSPI_INIT() Configure high-speed SPI pins SSPI / GPIO
ChangeSPIFlashReadMode() Configure SPI Flash read mode SSPI
ChangeHSRSMode() Configure high-speed reset behavior Reset configuration
ChangePLLFrequency() Configure the EC clock ECPM
Init_PECI() Initialize PECI telemetry PECI
InitSMBusChannel4() Initialize the fourth SMBus channel SMBus
Init_SMBus_Regs() Configure SMBus registers SMBus
Init_CIR() Initialize infrared reception CIR
Init_PS2Port() Initialize PS/2 functionality PS/2
InitSysMemory2ECRam() Configure system-memory mapping EC RAM
ResetSCIEvent() Reset SCI-event state ACPI event handling
ExtendScanPin handling Configure extended keyboard scanning KBS / GPIO

Compile-time configuration switches
#

Macro or setting Effect
HSPI Enables high-speed SPI pin initialization
SPIReadMode Selects the configured Flash read mode
HSRSMode Enables high-speed reset configuration
PLLFrequency Selects the EC clock configuration
PECI_Support Enables PECI initialization
SMBusChannel4Support Enables initialization of SMBus channel 4
ADCFuncSupport Controls ADC-related functionality where referenced by active code
CIRFuncSupport Enables CIR initialization
SysMemory2ECRam Enables the system-memory-to-EC-RAM mapping path
ExtendScanPin Participates in keyboard-matrix extension configuration

When tracing initialization behavior, both the function body and the active preprocessor definitions must be examined. A function can appear in the source while its call is disabled by comments or compile-time conditions.

4.4 The main_service() priority polling engine
#

main_service() is the central foreground service dispatcher in the supplied firmware. It checks pending service flags in a fixed sequence, clears a flag when its handler is selected, runs the handler, and then restarts the scan from the top.

When SMBusServiceCenterFunc is defined, the loop condition also checks for outstanding SMBus work.

void main_service(void)
{
    #ifdef SMBusServiceCenterFunc
    while((Service != 0x00) || (Service1 != 0x00)
          || (CheckSMBusNeedService() == SMBus_NeedService))
    #else
    while((Service != 0x00) || (Service1 != 0x00))
    #endif
    {
        // 1. Host command/data service
        if(F_Service_PCI)
        {
            F_Service_PCI = 0;
            service_pci1();
            continue;
        }

        // 2. Service unlock
        if(F_Service_UNLOCK)
        {
            F_Service_UNLOCK = 0;
            service_unlock();
            continue;
        }

        // 3. Send byte from KBC
        if(F_Service_SEND)
        {
            F_Service_SEND = 0;
            service_send();
            continue;
        }

        // 4. Send data to PS/2 peripheral
        if(F_Service_Send_PS2)
        {
            F_Service_Send_PS2 = 0;
            service_PS2_data();
            continue;
        }

        // 5. Process incoming PS/2 data
        if(F_Service_PS2)
        {
            F_Service_PS2 = 0;
            service_ps2();
            continue;
        }

        // 6. Process SMBus service
        #ifdef SMBusServiceCenterFunc
        ServiceSMBus();
        #endif

        // 7. One-millisecond service
        if(F_Service_MS_1)
        {
            F_Service_MS_1 = 0;
            service_1mS();
            continue;
        }

        // 8. Secondary host command/data service
        if(F_Service_PCI2)
        {
            F_Service_PCI2 = 0;
            service_pci2();
            continue;
        }

        // 9. Keyboard scanner service
        if(F_Service_KEY)
        {
            F_Service_KEY = 0;
            service_scan();
            continue;
        }

        // OEM high-priority hook
        Hook_main_service_H();

        // 10. Low-level event
        if(F_Service_Low_LV)
        {
            F_Service_Low_LV = 0;
            service_Low_LVEvent();
            continue;
        }

        // 11. Third host command/data service
        if(F_Service_PCI3)
        {
            F_Service_PCI3 = 0;
            service_pci3();
            continue;
        }

        // 12. CIR interrupt service
        if(F_Service_CIR)
        {
            F_Service_CIR = 0;
            service_cir();
            continue;
        }

        // 13. Fourth host command/data service
        if(F_Service_PCI4)
        {
            F_Service_PCI4 = 0;
            service_pci4();
            continue;
        }

        // 14–17. OEM service slots
        if(F_Service_OEM_1)
        {
            F_Service_OEM_1 = 0;
            service_OEM_1();
            continue;
        }

        if(F_Service_OEM_2)
        {
            F_Service_OEM_2 = 0;
            service_OEM_2();
            continue;
        }

        if(F_Service_OEM_3)
        {
            F_Service_OEM_3 = 0;
            service_OEM_3();
            continue;
        }

        if(F_Service_OEM_4)
        {
            F_Service_OEM_4 = 0;
            service_OEM_4();
            continue;
        }

        // OEM low-priority hook
        Hook_main_service_L();
    }
}

Dispatcher execution model
#

Entry condition

The dispatcher continues while Service or Service1 is nonzero. When SMBusServiceCenterFunc is enabled, the condition also considers whether SMBus needs service.

Flag polling and handler execution

The service flags are evaluated sequentially. When a flag is set, the dispatcher clears it and calls the corresponding handler.

Each selected handler is followed by continue, which returns execution to the start of the loop. The next scan therefore begins again with the highest-positioned service condition.

This structure gives earlier checks precedence when multiple services are pending. The source describes the design as flag polling with priority preemption; operationally, the shown loop is a priority-ordered polling dispatcher, and its response time also depends on handler execution and event-arrival timing.

SMBus processing

When SMBusServiceCenterFunc is enabled, ServiceSMBus() runs within the dispatch loop. The outer loop condition checks whether SMBus work remains, keeping that activity part of the foreground service process.

OEM extension points

Two hook functions provide OEM insertion points:

  • Hook_main_service_H() is called after keyboard scanning is checked and before low-level event processing.
  • Hook_main_service_L() runs after the listed OEM service slots have been checked.

The four OEM service flags, F_Service_OEM_1 through F_Service_OEM_4, provide additional locations for custom service handlers.

This arrangement lets OEM code extend the shared scheduler while retaining the standard service order.

Service priority reference
#

Priority Service flag Handler Description
1 F_Service_PCI service_pci1() Primary host command/data service, including KBC traffic
2 F_Service_UNLOCK service_unlock() Auxiliary-device unlock and host IRQ handling
3 F_Service_SEND service_send() Sends data to the host through the KBC path
4 F_Service_Send_PS2 service_PS2_data() Sends data to PS/2 peripherals
5 F_Service_PS2 service_ps2() Processes incoming PS/2 data
6 — ServiceSMBus() SMBus processing, when enabled
7 F_Service_MS_1 service_1mS() One-millisecond service
8 F_Service_PCI2 service_pci2() Secondary host command/data service
9 F_Service_KEY service_scan() Keyboard-matrix scanning
— — Hook_main_service_H() OEM high-priority hook
10 F_Service_Low_LV service_Low_LVEvent() Low-level event processing
11 F_Service_PCI3 service_pci3() Third host command/data service
12 F_Service_CIR service_cir() Consumer infrared processing
13 F_Service_PCI4 service_pci4() Fourth host command/data service
14 F_Service_OEM_1 service_OEM_1() OEM service slot 1
15 F_Service_OEM_2 service_OEM_2() OEM service slot 2
16 F_Service_OEM_3 service_OEM_3() OEM service slot 3
17 F_Service_OEM_4 service_OEM_4() OEM service slot 4
— — Hook_main_service_L() OEM low-priority hook

The table follows the order in the supplied main_service() function. It describes foreground dispatch order rather than the hardware interrupt controller’s priority configuration.

⏱️ 5. OEM Timer Hook Architecture
#

The firmware separates timer management from platform-specific processing. The core timer implementation is identified as CORE_TIMERS.C, while OEM hook functions provide scheduled entry points for application logic.

The supplied material describes timer intervals of 1 ms, 5 ms, 10 ms, 50 ms, 100 ms, 500 ms, 1 second, and 1 minute, along with OEM-customized intervals.

Shorter intervals are used for tasks requiring frequent evaluation, while longer intervals provide additional scheduling levels for work that does not require the same frequency.

5.1 Hook_Timer1msEvent() — one-millisecond hook
#

void Hook_Timer1msEvent(IBYTE EventId)
{
    #if MailBoxRWECram
    HandleMailBox();
    #endif

    EventManager(EventId);

    Oem_SysPowerContrl();

    SMBusCenter();

    ACPILED_ON();
}

The 1 ms hook combines mailbox handling, event processing, power control, SMBus coordination, and LED-related activity.

Mailbox handling

When MailBoxRWECram is enabled, HandleMailBox() processes the associated mailbox functionality.

Event processing

EventManager(EventId) receives the event identifier for the current invocation.

In the supplied description, the event ID cycles through values 0–9, with each 1 ms tick polling one-tenth of the physical events. Under that arrangement, the complete event set is checked over a nominal 10 ms cycle.

Power sequencing

Oem_SysPowerContrl() executes the OEM system power-control logic. Its placement in this hook allows the power-control state machine to be evaluated on the 1 ms schedule.

SMBus coordination

SMBusCenter() coordinates SMBus activity, including smart-battery polling and charger-related operations described in the source material.

LED updates

ACPILED_ON() performs the associated LED update logic.

5.2 Hook_Timer5msEvent() — five-millisecond hook
#

void Hook_Timer5msEvent(void)
{
    #ifdef PECI_Support
    PECI_PowerCtrlCenter();
    #endif

    #ifdef PMC3_Support
    SystemIsS0
    {
        PECI_SendBufferData();
    }
    #endif

    #if ACPI_QEventPending
    Get_PendingECQEvent();
    #endif
}

The 5 ms hook focuses on PECI-related processing and pending ACPI query events.

PECI power-control processing

When PECI_Support is defined, PECI_PowerCtrlCenter() handles the corresponding PECI control workflow. The supplied description associates this processing with CPU telemetry for thermal policy and fan-control decisions.

Conditional PECI buffer transmission

When PMC3_Support is defined, the code invokes PECI_SendBufferData() within the SystemIsS0 condition.

The supplied code expresses SystemIsS0 as a condition surrounding the buffer-send operation. Its exact macro or statement implementation resides elsewhere in the project.

ACPI query-event processing

When ACPI_QEventPending is enabled, Get_PendingECQEvent() retrieves pending EC query events. The supplied description associates this with reporting query-event information to the host through the SCI mechanism.

5.3 Hook_Timer10msEventA() — ten-millisecond Event A
#

void Hook_Timer10msEventA(void)
{
    #if ADCFuncSupport
    // ScanADCDyChannel1();
    #endif

    #if BAT1FuncSupport
    if(BT1_STATUS1 & bat_in)
    {
        if(BAT1_CtrlStep == BATCHECK)
            ChkBatt();
    }

    if(BT1_STATUS1 & bat_in)
    {
        PollingBAT1Data();
    }
    #endif

    #ifdef ECPowerDownModeSupport
    ECPowerDownModeManager();
    #endif
}

Event A primarily handles smart-battery processing and EC power-down management. An ADC scanning call is present but commented out.

Battery-presence check

When BAT1FuncSupport is enabled, the function checks whether BT1_STATUS1 contains the bat_in flag.

Battery identification

If the battery is present and BAT1_CtrlStep equals BATCHECK, the firmware calls ChkBatt().

The supplied description associates this step with retrieving battery identification information, including manufacturer information and design capacity.

Battery telemetry polling

PollingBAT1Data() is called when the battery-presence condition is true. The described polling process obtains dynamic battery information such as voltage, current, and relative state of charge (RSOC).

The source describes this work as being distributed across successive 10 ms intervals to avoid overloading SMBus processing.

EC power-down management

When ECPowerDownModeSupport is enabled, ECPowerDownModeManager() evaluates the configured EC power-down logic.

The ADC call remains commented out in the provided example, so it is not executed by this function as shown.

5.4 Hook_Timer10msEventB() — ten-millisecond Event B
#

void Hook_Timer10msEventB(void)
{
    CheckSBPowerButton();

    #if ADCFuncSupport
    ScanADCDyChannel2();
    #endif
}

Event B complements Event A by handling power-button checking and a second ADC scanning path.

Power-button processing

CheckSBPowerButton() monitors the system power-button state. The supplied description associates the routine with button-transition detection and hardware debounce handling before the resulting state change is handled by Oem_SysPowerContrl().

The source also references an approximately 200 ms PWRBTN# pulse assertion requirement. The specific timing behavior belongs to the platform implementation.

Secondary ADC scan

When ADCFuncSupport is enabled, ScanADCDyChannel2() performs the secondary dynamic ADC scan. Dividing ADC work between Event A and Event B distributes the acquisition tasks across separate timer hooks.

5.5 Timer hierarchy summary
#

Timer interval Main task described in the source Related hook or implementation
1 ms Event polling, power-control evaluation, SMBus coordination, LED updates Hook_Timer1msEvent()
5 ms PECI control, conditional buffer transmission, ACPI query-event processing Hook_Timer5msEvent()
10 ms, Event A Battery identification, battery-data polling, EC power-down management, optional ADC scan Hook_Timer10msEventA()
10 ms, Event B Power-button checking and optional secondary ADC scan Hook_Timer10msEventB()
50 ms Additional periodic work in the timer hierarchy OEM-defined hook
100 ms Additional periodic work in the timer hierarchy OEM-defined hook
500 ms Additional periodic work in the timer hierarchy OEM-defined hook
1 second Longer-interval periodic work OEM-defined hook
1 minute Long-interval maintenance or monitoring work OEM-defined hook
OEM-defined intervals Application-specific periodic processing Project-specific implementation

The timer hierarchy provides multiple scheduling frequencies for different types of work. Power control and event detection use shorter intervals, while tasks such as thermal averaging, fan-speed smoothing, and LED fading can be assigned to longer intervals where the implementation requires them.

🔧 6. Practical Firmware Debugging
#

Understanding the boundaries between CORE, OEM, and hardware functionality makes it easier to identify where a failure originates.

6.1 Trace initialization in execution order
#

When a peripheral or board function is unavailable, start with the initialization sequence.

  1. Confirm which branch of main() executes.
  2. Check whether Core_Initialization() completes.
  3. Determine which OEM initialization calls are enabled by the build configuration.
  4. Inspect the relevant register setup and GPIO multiplexing.
  5. Verify that the associated interrupt or foreground service can run.

Pay particular attention to preprocessor definitions and commented-out calls. Their presence in the source does not mean the associated functionality is active in the compiled firmware.

6.2 Trace events through the service dispatcher
#

For host communication, keyboard scanning, SMBus work, or other delayed operations, follow the entire processing path:

Event source → pending flag or state → main_service() → service handler → resulting hardware or host action.

This makes it possible to distinguish an event that never becomes pending from a handler that is delayed or does not produce the expected result.

6.3 Inspect code-bank placement and switching
#

For faults involving functions in different banks, examine:

  • The code-bank assignment of the affected function.
  • The bank-switching support in L51_BANK.A51.
  • The configured BANKSEL register.
  • The generated call and return sequences.
  • The compiler and linker configuration.
  • The initialization performed by ResetBANKDATA().

The directory structure indicates where modules are organized, but the actual placement of individual functions depends on the firmware build configuration.

6.4 Review timer scheduling and peripheral workloads
#

When periodic processing is delayed, inspect the execution time and scheduling behavior of the relevant handlers.

Useful areas to examine include service-flag management, SMBus transaction progress, ADC scanning, power-state transitions, and the work performed by OEM hooks.

Because the dispatcher checks flags in order and restarts the scan after processing a selected service, the amount of time spent in a handler can influence when other services receive attention.

✅ 7. Conclusion
#

The IT8528E firmware architecture combines integrated EC peripherals with a layered software design. The CORE layer supplies shared initialization, interfaces, timers, and service scheduling, while the OEM layer configures and implements motherboard-specific behavior.

Code banking expands the available firmware space beyond the conventional 8051 code-address window. The main() function establishes the startup state and chooses the initialization path; Core_Initialization() prepares common services; Oem_Initialization() configures platform-specific functionality; and main_service() dispatches pending foreground work according to its defined order.

The OEM timer hooks add periodic processing for event management, power sequencing, PECI, battery monitoring, power-button detection, ADC scanning, and other platform tasks.

Taken together, these components provide the foundation for understanding how the supplied EC firmware initializes the controller, coordinates peripherals, schedules foreground services, and manages recurring system tasks.

Related