Zephyr CBI UFSC Configuration

Overview

Newer devices use a unified system called UFSC (Unified Firmware and Second-source Configuration). This system replaces the separate legacy FW_CONFIG and SSFC fields with a single, schema-driven 128-bit (4-DWORD) value stored in the CBI.

The primary goals of UFSC are to:

  • Unify static (non-probeable) and dynamic (probeable) configuration into a single source of truth.
  • Enforce a standardized schema to reduce errors and eliminate board-specific decoding logic.
  • Provide a clear and maintainable way to manage hardware variations.

The 128-bit UFSC field is structured as follows:

  • BITS 0-55, 64-119 (Standardized Firmware Configuration): Contain standardized definitions for common hardware components (e.g., audio codecs, sensors).
  • BITS 56-63 (AP OEM Customization Field): Reserved for OEM/ODM partners to encode board-specific information for the AP use.
  • BITS 120-127 (EC OEM Customization Field): Reserved for OEM/ODM partners to encode board-specific information for the EC use.

Kconfig Options

The CONFIG_CROS_EC_CBI_UFSC_PARSER Kconfig option enables the driver that parses UFSC fields from the devicetree and provides the necessary APIs.

This option is enabled automatically if a devicetree node with the cros-ec,cbi-ufsc compatible is present and enabled. Therefore, you do not need to set this Kconfig option manually in your project's configuration file.

Devicetree Nodes

UFSC is configured using a template-based approach in the devicetree. A project overlay includes standard schema files and then defines the specific values for the hardware present on that device. The schema for these nodes is defined in cros-ec,cbi-ufsc.yaml and cros-ec,cbi-ufsc-value.yaml.

Schema Templates

The structure of the UFSC bitfield is defined in two template files that should be included by a project's overlay:

These files define the field names, start bit, and size for each configuration item.

Example snippet from cbi_ufsc_std_schema.dtsi:

#define UFSC_BIT(dword, bit) ((dword) * 32 + (bit))

/ {
    cbi_ufsc: cbi-ufsc {
        compatible = "cros-ec,cbi-ufsc";

        ufsc_thermal_fan: thermal-fan {
            enum-name = "UFSC_THERMAL_FAN";
            start = <UFSC_BIT(2, 2)>;
            size = <1>;
        };
        ufsc_base_sensor: base-sensor {
            enum-name = "UFSC_BASE_SENSOR";
            start = <UFSC_BIT(2, 6)>;
            size = <3>;
        };
        /* ... other standard fields ... */
    };
};

Project Value Definition

A project defines the possible values for each field by creating child nodes within the corresponding field node. These value definitions are typically placed in a project-specific overlay file (e.g., generated_std_ufsc.dtsi for autogenerated values, or oem_ufsc.dtsi for custom values).

Each value node has the following properties:

  • compatible = “cros-ec,cbi-ufsc-value”: The required compatible string.
  • status = “okay”: This property is mandatory.
  • value: The integer value that will be stored in the bitfield.
  • default: A boolean property indicating this is the default value.

Example project overlay (project.overlay):

/* Include the standard and OEM schema templates */
#include <cros/cbi_ufsc_std_schema.dtsi>
#include <cros/cbi_ufsc_oem_schema.dtsi>

/* Include the generated file that defines the values for this project */
#include "generated_std_ufsc.dtsi"

/* Optionally include and define OEM custom values */
#include "oem_ufsc.dtsi"

Example value definition (generated_std_ufsc.dtsi):

/* This file is typically auto-generated */
&ufsc_thermal_fan {
	ufsc_fan_absent: absent {
		compatible = "cros-ec,cbi-ufsc-value";
		status = "okay";
		value = <0>;
		default;
	};

	ufsc_fan_present: present {
		compatible = "cros-ec,cbi-ufsc-value";
		status = "okay";
		value = <1>;
	};
};

&ufsc_base_sensor {
	ufsc_base_lsm6dso: lsm6dso {
		compatible = "cros-ec,cbi-ufsc-value";
		status = "okay";
		value = <0>;
	};
	ufsc_base_bmi160: bmi160 {
		compatible = "cros-ec,cbi-ufsc-value";
		status = "okay";
		value = <1>;
		default;
	};
};

API Usage

The firmware interacts with UFSC data through a common driver API, abstracting the bit-level details.

The preferred method is to check for a specific value using cros_cbi_ufsc_check_match(). This is less error-prone and similar to the legacy SSFC API.

#include "cros_cbi.h"

if (cros_cbi_ufsc_check_match(
        CBI_UFSC_VALUE_ID(DT_NODELABEL(ufsc_fan_present)))) {
    /* Fan is present */
}

Testing and Debugging

The cbi console command and the ectool cbi host command can be used to read UFSC data.

When setting the UFSC value using ectool, the argument provided must be a byte-ordered hex string using little-endian format (least significant byte first).

For example, if the desired UFSC data consists of the following 32-bit words:

  • DWORD[0]: 0x11223344
  • DWORD[1]: 0x55667788
  • DWORD[2]: 0x99aabbcc
  • DWORD[3]: 0xddeeff00

The command to write this to the UFSC tag (29) is:

ectool cbi set 29 4433221188776655ccbbaa9900ffeedd

To generate a CBI image file containing UFSC data, use the cbi-util tool. There are two ways to provide UFSC data:

  • Hex string format: Use the --ufsc_hex argument to provide a raw hex string. This aligns with the ectool format.
cbi-util create --file cbi.bin --board_version 1 --sku_id 1 --size 256 \
    --ufsc_hex 4433221188776655ccbbaa9900ffeedd
  • Integer list format: Use the --ufsc argument with four comma-separated 32-bit hexadecimal values (DWORD 0 to DWORD 3).
cbi-util create --file cbi.bin --board_version 1 --sku_id 1 --size 256 \
    --ufsc 0x11223344,0x55667788,0x99aabbcc,0xddeeff00