Configure I2C Buses

Config options

The I2C options are prefixed with CONFIG_I2C*. Evaluate whether each option is appropriate to add to your board.

A typical EC and board should at a minimum set CONFIG_I2C and CONFIG_I2C_CONTROLLER.

Feature Parameters

The following parameters control the behavior of the I2C library. config.h defines a reasonable default value, but you may need to change the default value for your board.

  • CONFIG_I2C_CHIP_MAX_TRANSFER_SIZE <bytes>
  • CONFIG_I2C_NACK_RETRY_COUNT <count>
  • CONFIG_I2C_EXTRA_PACKET_SIZE <bytes> - Only used on STM32 EC's if CONFIG_HOSTCMD_I2C_ADDR_FLAGS is defined.

GPIOs and Alternate Pins

In the gpio.inc file, you need to define a GPIO for the clock (SCL) and data (SDA) pin used on each active I2C bus. The corresponding GPIOs are then included in the i2c_ports[] array. This permits the I2C library to perform common bus recovery actions using bit-banging without involvement by the EC-specific I2C device driver.

You also need to define the alternate function assignment for all I2C pins using the ALTERNATE() macro. This step can be skipped for any pins that default to I2C functionality.

Note that many I2C buses only support 1.8V operation. This is determined by I2C devices connected to the bus. In this case you need to include GPIO_SEL_1P8V as part of the flags field in both the GPIO() and ALTERNATE() macros. I2C bus 0 in the example below demonstrates configuring the SCL and SDA pins for 1.8V operation.

See the GPIO documentation for additional details on the GPIO macros.

Data Structures

  • const struct i2c_port_t i2c_ports[] - This array should be defined in your baseboard.c or board.c file. This array defines the mapping of internal I2C port numbers used by the I2C library to the physical I2C ports connected to the EC.
  • const unsigned int i2c_port_used = ARRAY_SIZE(i2c_ports) - Defines the number of internal I2C ports accessible by the I2C library.

Tasks

None required by this feature.

Testing and Debugging

Console Commands

  • i2cscan - Provides a quick look of all I2C devices found on all configured buses.
  • i2cxfer - Allows you to read and write individual registers on an I2C device.

For runtime troubleshooting of an I2C device, enable and the I2C tracing module to log all I2C transactions initiated by the EC code.

Example

The image below shows the I2C bus assignment for the Volteer reference board.

I2C Example

The gpio.inc file for Volteer defines both GPIO() and ALTERNATE() entries for all I2C buses used in the design.

/* I2C pins - Alternate function below configures I2C module on these pins */
GPIO(EC_I2C0_SENSOR_SCL,       PIN(B, 5), GPIO_INPUT | GPIO_SEL_1P8V)
GPIO(EC_I2C0_SENSOR_SDA,       PIN(B, 4), GPIO_INPUT | GPIO_SEL_1P8V)
GPIO(EC_I2C1_USB_C0_SCL,       PIN(9, 0), GPIO_INPUT)
GPIO(EC_I2C1_USB_C0_SDA,       PIN(8, 7), GPIO_INPUT)
GPIO(EC_I2C2_USB_C1_SCL,       PIN(9, 2), GPIO_INPUT)
GPIO(EC_I2C2_USB_C1_SDA,       PIN(9, 1), GPIO_INPUT)
GPIO(EC_I2C3_USB_1_MIX_SCL,    PIN(D, 1), GPIO_INPUT)
GPIO(EC_I2C3_USB_1_MIX_SDA,    PIN(D, 0), GPIO_INPUT)
GPIO(EC_I2C5_POWER_SCL,        PIN(3, 3), GPIO_INPUT)
GPIO(EC_I2C5_POWER_SDA,        PIN(3, 6), GPIO_INPUT)
GPIO(EC_I2C7_EEPROM_SCL,       PIN(B, 3), GPIO_INPUT)
GPIO(EC_I2C7_EEPROM_SDA,       PIN(B, 2), GPIO_INPUT)

/* Alternate functions GPIO definitions */
ALTERNATE(PIN_MASK(B, BIT(5) | BIT(4)), 0, MODULE_I2C, (GPIO_INPUT | GPIO_SEL_1P8V)) /* I2C0 */
ALTERNATE(PIN_MASK(9, BIT(0) | BIT(2) | BIT(1)), 0, MODULE_I2C, 0)                   /* I2C1 SCL / I2C2 */
ALTERNATE(PIN_MASK(8, BIT(7)), 0, MODULE_I2C, 0)                                     /* I2C1 SDA */
ALTERNATE(PIN_MASK(D, BIT(1) | BIT(0)), 0, MODULE_I2C, 0)                            /* I2C3 */
ALTERNATE(PIN_MASK(3, BIT(3) | BIT(6)), 0, MODULE_I2C, 0)                            /* I2C5 */
ALTERNATE(PIN_MASK(B, BIT(3) | BIT(2)), 0, MODULE_I2C, 0)                            /* I2C7 */

The i2c_ports[] array requires the .port field to be assigned to an EC chipset specific enumeration. For the NPCx7 I2C bus names are defined in ./chip/npcx/registers.h. The Volteer baseboard.h file creates a mapping from the schematic net name to the NPCx7 I2C bus enumeration.

#define CONFIG_I2C
#define I2C_PORT_SENSOR     NPCX_I2C_PORT0_0
#define I2C_PORT_USB_C0     NPCX_I2C_PORT1_0
#define I2C_PORT_USB_C1     NPCX_I2C_PORT2_0
#define I2C_PORT_USB_1_MIX  NPCX_I2C_PORT3_0
#define I2C_PORT_POWER      NPCX_I2C_PORT5_0
#define I2C_PORT_EEPROM     NPCX_I2C_PORT7_0

The last piece for I2C configuration is to create the i2c_ports[] array using the macros and enumerations added to baseboard.h and gpio.inc.

/* I2C port map configuration */
const struct i2c_port_t i2c_ports[] = {
    {
        .name = "sensor",
        .port = I2C_PORT_SENSOR,
        .kbps = 400,
        .scl = GPIO_EC_I2C0_SENSOR_SCL,
        .sda = GPIO_EC_I2C0_SENSOR_SDA,
        .flags = 0,
    },
    {
        .name = "usb_c0",
        .port = I2C_PORT_USB_C0,
        /*
         * I2C buses used for PD communication must be set for 400 kbps
         * or greater. Set to the maximum speed supported by all devices.
         */
        .kbps = 1000,
        .scl = GPIO_EC_I2C1_USB_C0_SCL,
        .sda = GPIO_EC_I2C1_USB_C0_SDA,
    },
    {
        .name = "usb_c1",
        .port = I2C_PORT_USB_C1,
        /*
         * I2C buses used for PD communication must be set for 400 kbps
         * or greater. Set to the maximum speed supported by all devices.
         */
        .scl = GPIO_EC_I2C2_USB_C1_SCL,
        .sda = GPIO_EC_I2C2_USB_C1_SDA,
    },
    {
        .name = "usb_1_mix",
        .port = I2C_PORT_USB_1_MIX,
        .kbps = 100,
        .scl = GPIO_EC_I2C3_USB_1_MIX_SCL,
        .sda = GPIO_EC_I2C3_USB_1_MIX_SDA,
    },
    {
        .name = "power",
        .port = I2C_PORT_POWER,
        .kbps = 100,
        .scl = GPIO_EC_I2C5_POWER_SCL,
        .sda = GPIO_EC_I2C5_POWER_SDA,
    },
    {
        .name = "eeprom",
        .port = I2C_PORT_EEPROM,
        .kbps = 400,
        .scl = GPIO_EC_I2C7_EEPROM_SCL,
        .sda = GPIO_EC_I2C7_EEPROM_SDA,
    },
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);

The .flags field is optional when using the default I2C bus setup. See ./include/i2c.h for the full list of supported flags.

The flag I2C_PORT_FLAG_DYNAMIC_SPEED allows the I2C bus frequency to be changed at runtime. The typical use case is to set the I2C bus frequency to different speeds based on the BOARD_VERSION in CBI. For example board version 1 supports 100 kbps operation but board version 2 and greater supports 400 kbps operation. I2C_PORT_FLAG_DYNAMIC_SPEED is not used to change the I2C bus frequency on the fly depending on the addressed slave device.

An example of changing the I2C bus frequency from the Kodama board is shown below.

static void board_i2c_init(void)
{
    if (board_get_version() < 2)
        i2c_set_freq(1,  I2C_FREQ_100KHZ);
}
DECLARE_HOOK(HOOK_INIT, board_i2c_init, HOOK_PRIO_INIT_I2C);