This article describes how to port OpenBMC to a new machine, including changes to OpenBMC layers, the Linux kernel, and several related components such as hwmon sensor, LED, and inventory.

It also includes information about how to modify the code in the local system to build OpenBMC with local changes without changing the recipe.

Porting to a new machine

To port OpenBMC to a new machine, complete the following steps:

  1. Add the layer of the new machine in meta-openbmc.
  2. Make the kernel changes for the new machine such as configurations and dts.
  3. Add the Workbook of the new machine.
  4. Make the required changes that are specific to the new machine, for example, to the hwmon sensor and LED.

Adding the machine layer

Let us take an example of adding machine machine-name-2 in the manufacture manufacturer-1 repository.

  1. To create the layer, you can either reuse the existing repository meta-manufacturer-1, or create a new repository.
  2. Create a meta-machine-name-2 directory in the meta_manufacturer-1 repository for the machine.
  3. Update the configuration files with the machine name machine-name-2 instead of manufacturer-1.
  4. Create a conf directory in meta_manufacturer-1, following meta-ibm/conf.
  5. After you complete these steps, the directory tree is similar to the following structure:

meta-manufacturer-1/
├── conf
│   └── layer.conf
└── meta-machine-name-2
    ├── conf
    │   ├── bblayers.conf.sample
    │   ├── conf-notes.txt
    │   ├── layer.conf
    │   ├── local.conf.sample
    │   └── machine
    │       └── machine-name-2.conf
    ├── recipes-kernel
    │   └── linux
    │       ├── linux-obmc
    │       │   └── machine-name-2.cfg  # Machine specific kernel configs
    │       └── linux-obmc_%.bbappend
    └── recipes-phosphor
        ...
        ├── images
        │   └── obmc-phosphor-image.bbappend  # Machine specfic apps/services to include
        └── workbook
            ├── machine-name-2-config
            │   └── Machine-name-2.py  # Machine specific workbook (see below)
            └── machine-name-2-config.bb

The above directory tree creates a new layer in the machine.


TEMPLATECONF=meta-manufacturer-1/meta-machine-name-2/conf . oe-init-build-env
bitbake obmc-phosphor-image

Making Kernel changes

This section describes how you can make changes to the Kernel to port the OpenBMC to a new machine. The device tree is in https://github.com/openbmc/linux/tree/dev-4.13/arch/arm/boot/dts. For examples, see aspeed-bmc-opp-romulus.dts or a similar machine. Complete the following steps to make Kernel changes:

  1. Add the new machine device tree:
    • Describe the GPIOs, for example, LED, FSI, and gpio-keys. You can obtain this information from the schematic.
    • Describe the i2c buses and devices, which usually include various hwmon sensors.
    • Describe the other devices, such as uarts and mac.
    • Usually the flash layout does not need a change. Include the openbmc-flash-layout.dtsi file.
  2. Modify the Makefile to build the device tree.

Notes:

  • In dev-4.10, there is common and machine-specific initialization code in arch/arm/mach-aspeed/aspeed.c which is used to do common initializations and perform specific settings in each machine.
  • With dev-4.13, most of the initializations are done with the upstreamed clock and reset driver.
  • If the machine requires specific settings such as uart routing, send an mail to the mailing list for discussion.

Adding the Workbook

In legacy OpenBMC, the workbook describes the machine’s services, sensors, and FRUs. It is a python configuration and is used by other services in skeleton. In the latest OpenBMC release, phosphor-xxx services are used and thus, skeleton is deprecated. However, workbook is required to make the build.

An example is meta-quanta, that defines its own configuration in the OpenBMC tree, so that it does not rely on the skeleton repository.

For OpenPOWER machines, several configurations are still used in this configuration file. For example, in Romulus.py, the configuration details are as follows:


GPIO_CONFIG['BMC_POWER_UP'] = \
        {'gpio_pin': 'D1', 'direction': 'out'}
GPIO_CONFIG['SYS_PWROK_BUFF'] = \
        {'gpio_pin': 'D2', 'direction': 'in'}

GPIO_CONFIGS = {
    'power_config' : {
        'power_good_in' : 'SYS_PWROK_BUFF',
        'power_up_outs' : [
            ('BMC_POWER_UP', True),
        ],
        'reset_outs' : [
        ],
    },
}

The PowerUp and PowerOK GPIOs are needed for the build to power on the chassis and check the power state.

Making other miscellaneous changes

Different machines have different devices, such as hwmon sensors, LEDs, and fans.

OpenBMC is designed to be configurable. Hence, you can describe such devices in different configuration files in the machine layer.

Hwmon sensors

Hwmon sensors include sensors on board, such as temperature sensors, fans, and OCC sensors. The path and name in the configuration files must match the devices in device tree.

See the Sensor Support for OpenBMC repository for more details.

Here, Romulus is taken as as example. The configuration files are in the meta-romulus/recipes-phosphor/sensors repository, which includes sensors on board and sensors of OCC, where on-board sensors are through i2c, and OCC sensors are through FSI.

  • w83773g@4c.conf defines the w83773 temperature sensor containing 3 temperatures:
    
    LABEL_temp1 = "outlet"
    ...
    LABEL_temp2 = "inlet_cpu"
    ...
    LABEL_temp3 = "inlet_io"
    
    

    This device is defined in its device tree as w83773g@4c. When the BMC starts, the udev rule starts phosphor-hwmon and it creates temperature sensors on the following DBus objects based on the sysfs attributes.

    
    /xyz/openbmc_project/sensors/temperature/outlet
    /xyz/openbmc_project/sensors/temperature/inlet_cpu
    /xyz/openbmc_project/sensors/temperature/inlet_io
    
    
  • pwm-tacho-controller@1e786000.conf defines the fans and the configuration is similar as above. The difference is that it creates fan_tach sensors.
  • occ-hwmon.1.conf defines the OCC hwmon sensor for the master CPU. This configuration is different and phosphor-hwmon is instructed to read the label instead of directly getting the index of the sensor, because CPU cores and DIMMs could be dynamic. For example, CPU cores could be disabled and DIMMs could be pulled out.
    
    MODE_temp1 = "label"
    MODE_temp2 = "label"
    ...
    MODE_temp31 = "label"
    MODE_temp32 = "label"
    LABEL_temp91 = "p0_core0_temp"
    LABEL_temp92 = "p0_core1_temp"
    ...
    LABEL_temp33 = "dimm6_temp"
    LABEL_temp34 = "dimm7_temp"
    LABEL_power2 = "p0_power"
    ...
    
    
    • MODE_temp* = "label" indicates that if tempX is seen, the label is read, which is the sensor ID.
    • LABEL_temp* = "xxx" indicates the sensor name for the corresponding sensor ID.

      For example, if the value of temp1_input is 37000 and the value of temp1_label is 91 in sysfs, phosphor-hwmon knows temp1_input is for sensor ID 91, which is p0_core0_temp. Hence, it creates /xyz/openbmc_project/sensors/temperature/p0_core0_temp with a sensor value of 37000.

    • The power sensors do not need to read the label since all powers are available on a system.

LEDs

Several parts are involved for LEDs.

  1. In kernel dts, LEDs are described. For example, romulus dts describes three LEDs, fault, identify, and power.
    
    leds {
        compatible = "gpio-leds";
    
        fault {
          gpios = <&gpio ASPEED_GPIO(N, 2) GPIO_ACTIVE_LOW>;
        };
    
        identify {
          gpios = <&gpio ASPEED_GPIO(N, 4) GPIO_ACTIVE_HIGH>;
        };
    
        power {
          gpios = <&gpio ASPEED_GPIO(R, 5) GPIO_ACTIVE_LOW>;
        };
      };
    
    
  2. In the machine layer, LEDs are configured through yaml files to describe how it functions. For example,
    
    bmc_booted:
        power:
            Action: 'Blink'
            DutyOn: 50
            Period: 1000
            Priority: 'On'
    power_on:
        power:
            Action: 'On'
            DutyOn: 50
            Period: 0
            Priority: 'On'
    
    

    It tells the LED manager to set the power LED to blink when BMC is ready and booted, and set it on when host is powered on.

  3. At runtime, the LED manager automatically set LEDs to on/off/blink based on the above yaml configuration file.
  4. LEDs can be accessed manually through /xyz/openbmc_project/led/. For example,
    • Get identify LED state:
      
      curl -b cjar -k https://$bmc/xyz/openbmc_project/led/physical/identify
      
    • Set identify LED to blink:
      
      jar -k -X PUT -H "Content-Type: application/json" -d '{"data": "xyz.openbmc_project.Led.Physical.Action.Blink" }' https://$bmc/xyz/openbmc_project/led/physical/identify/attr/State
      
  5. Note: This yaml configuration can be automatically generated by phosphor-mrw-tools from its MRW. See the example of Witherspoon.

Inventories and other sensors

Inventories, other sensors (for example, CPU/DIMM temperature), and FRUs are defined in IPMI’s yaml configuration files.

For example, meta-romulus/recipes-phosphor/ipmi

  • romulus-ipmi-inventory-map defines regular inventories, such as CPU, memory, and motherboard.
  • phosphor-ipmi-fru-properties defines extra properties of the inventories.
  • phosphor-ipmi-sensor-inventory defines the sensors from IPMI.
  • romulus-ipmi-inventory-sel defines inventories used for IPMI SEL.

Inventory map and fru-properties are similar in different systems. Refer to this example and make one for your system.

For ipmi-sensor-inventory, the sensors from IPMI are different in different systems. Hence, you must define your own sensors. For example:


0x08:
  sensorType: 0x07
  path: /org/open_power/control/occ0
  ...
0x1e:
  sensorType: 0x0C
  path: /system/chassis/motherboard/dimm0
  ...
0x22:
  sensorType: 0x07
  path: /system/chassis/motherboard/cpu0/core0

The first value 0x08, 0x1e, and 0x22 are the sensor IDs of IPMI, which is defined in MRW. You should follow the system’s MRW to define the above configuration.

Note: The yaml configuration files can be automatically generated by phosphor-mrw-tools from its MRW, see example of Witherspoon.

Fans

phosphor-fan-presence manages all the services about fan:

  • phosphor-fan-presence checks if a fan is present, creates the fan DBus objects in the inventory, and updates the Present property.
  • phosphor-fan-monitor checks if a fan is functional and updates the Functional property of the fan Dbus object.
  • phosphor-fan-control controls the fan speed by setting the fan speed target based on conditions, for example, temperatures.
  • phosphor-cooling-type checks if the system is air-cooled or water-cooled by setting properties of the /xyz/openbmc_project/inventory/system/chassis object.

All the above services are configurable, for example, by using the yaml configuration file. Hence, the machine specific configurations are written when porting OpenBMC to a new machine.

Taking Romulus as example, it is air-cooled and has 3 fans without GPIO presence detection.

Fan presence

Romulus has no GPIO detection for fan. Hence, it checks for the fan tachometer sensor:


- name: fan0
  path: /system/chassis/motherboard/fan0
  methods:
    - type: tach
      sensors:
        - fan0

The yaml configuration file indicates the following:

  • It creates the /system/chassis/motherboard/fan0 object in inventory.
  • It checks that the fan0 tachometer sensor (/sensors/fan_tach/fan0) sets Present as the property on the fan0 object.
Fan monitor

Romulus fans uses pwm to control the fan speed, where pwm ranges from 0 to 255, and the fan speed ranges from 0 to about 7000. Hence, it needs a factor and offset to map the pwm to fan speed:


- inventory: /system/chassis/motherboard/fan0
    allowed_out_of_range_time: 30
    deviation: 15
    num_sensors_nonfunc_for_fan_nonfunc: 1
    sensors:
      - name: fan0
        has_target: true
        target_interface: xyz.openbmc_project.Control.FanPwm
        factor: 21
        offset: 1600

The yaml config indicates the following:

  1. FanPwm is used as the target interface of the tachometer sensor;
  2. The expected fan speed is calculated with the formula, target * 21 + 1600
  3. The deviation is 15%. Hence, if the fan speed is out of the expected range for more than 30 seconds, fan0 shall be set as non-functional.
Fan control

The fan control service requires 4 yaml configuration files:

  • zone-condition defines the cooling zone conditions. Romulus is always air-cooled. Hence, this configuration involves defining a air_cooled_chassis condition based on the cooling type property.
    
    - name: air_cooled_chassis
     type: getProperty
     properties:
       - property: WaterCooled
         interface: xyz.openbmc_project.Inventory.Decorator.CoolingType
         path: /xyz/openbmc_project/inventory/system/chassis
         type: bool
         value: false
    
    
  • zone-config defines the cooling zones. Romulus has only one zone:
    
    zones:
     - zone: 0
       full_speed: 255
       default_floor: 195
       increase_delay: 5
       decrease_interval: 30
    

    It defines the zone full speed and default floor speed for the fans. In this example, the fan pwm is set to 255 if it is in full speed, and set to 195 if fans are in default floor speed.

  • fan-config defines which fans are controlled in which zone and which target interface shall be used, for example, the following yaml config defines that the fan0 is controlled in zone0 and it would use the FanPwm interface.
    
    - inventory: /system/chassis/motherboard/fan0
     cooling_zone: 0
     sensors:
       - fan0
     target_interface: xyz.openbmc_project.Control.FanPwm
     ...
    
  • events-config defines the various events and its handlers. For example, which fan targets would be set in which temperature. This config is described with examples in the example event yaml. Romulus example:
    
    - name: set_air_cooled_speed_boundaries_based_on_ambient
       groups:
           - name: zone0_ambient
             interface: xyz.openbmc_project.Sensor.Value
             property:
                 name: Value
                 type: int64_t
       matches:
           - name: propertiesChanged
       actions:
           - name: set_floor_from_average_sensor_value
             map:
                 value:
                     - 27000: 85
                     - 32000: 112
                     - 37000: 126
                     - 40000: 141
    
                 type: std::map<int64_t, uint64_t>
           - name: set_ceiling_from_average_sensor_value
             map:
                 value:
                     - 25000: 175
                     - 27000: 255
                type: std::map<int64_t, uint64_t>
    
    

The above yaml config defines the fan floor and ceiling speed in zone0_ambient‘s different temperatures. For example,

  • When the temperature is lower than 27 degrees Celsius, the floor speed (pwm) is set to 85.
  • When temperature is between 27 and 32 degrees Celsius, the floor speed (pwm) is set to 112.

With above configs, phosphor-fan runs the fan presence/monitor/control logic as configured specifically for the machine.

Note: Romulus fans are simple. For a more complicated example, refer to Witherspoon fan configurations.. The following are the additional functions of Witherspoon fan configuration:

  • It checks the GPIO for fan presnece.
  • It checks the GPIO to determine if the system is air or water cooled.
  • It has more sensors and more events in fan control.

GPIOs

This section mainly focuses on the GPIOs that are monitored in the device tree. For example,

  • A GPIO might represent a signal of host checkstop
  • A GPIO might represent a button press
  • A GPIO might represent if a device is attached or not

They are categorized as phosphor-gpio-presence for checking presences of a device, and phosphor-gpio-monitor for monitoring a GPIO.

GPIOs in the device tree

All the GPIOs to be monitored are described in the device tree. For example,


 gpio-keys {
    compatible = "gpio-keys";
    checkstop {
      label = "checkstop";
      gpios = <&gpio ASPEED_GPIO(J, 2) GPIO_ACTIVE_LOW>;
      linux,code = ;
    };
    id-button {
      label = "id-button";
      gpios = <&gpio ASPEED_GPIO(Q, 7) GPIO_ACTIVE_LOW>;
      linux,code = ;
    };
  };

The following code describes two GPIO keys, one for checkstop and the other for id-button, where the key code is calculated from aspeed-gpio.h:


#define ASPEED_GPIO_PORT_A 0
#define ASPEED_GPIO_PORT_B 1
...
#define ASPEED_GPIO_PORT_Y 24
#define ASPEED_GPIO_PORT_Z 25
#define ASPEED_GPIO_PORT_AA 26
...

#define ASPEED_GPIO(port, offset) \
  ((ASPEED_GPIO_PORT_##port * 8) + offset)

GPIO Presence

Witherspoon and Zaius have examples for gpio presence.

  • Witherspoon:
    
    INVENTORY=/system/chassis/motherboard/powersupply0
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=104
    NAME=powersupply0
    DRIVERS=/sys/bus/i2c/drivers/ibm-cffps,3-0069
    
    

    It checks GPIO key 104 for powersupply0‘s presence, creates the inventory object and bind or unbind the driver.

  • Zaius:
    
    INVENTORY=/system/chassis/pcie_card_e2b
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=39
    NAME=pcie_card_e2b
    
    

    It checks GPIO key 39 for pcie_card_e2b‘s presence, and creates the inventory object.

GPIO monitor

Typical usage of GPIO monitor is to monitor the checkstop event from host, or button presses.

  • checkstop monitor is a common service for OpenPOWER machines.
    
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=74
    POLARITY=1
    TARGET=obmc-host-crash@0.target
    

    By default, it monitors GPIO key 74, and if it is triggered, it tells systemd to start obmc-host-crash@0.target. For systems using a different GPIO pin for checkstop, it simply overrides the default one by specifying its own config file in meta-machine layer. For example, Zaius’s checkstop config.

    Note: When the key is pressed, phospohr-gpio-monitor starts the target unit and exits.

  • id-button monitor is an example service on Romulus to monitor ID button press.
    
    DEVPATH=/dev/input/by-path/platform-gpio-keys-event
    KEY=135
    POLARITY=1
    TARGET=id-button-pressed.service
    EXTRA_ARGS=--continue
    

    It monitors GPIO key 135 for the button press and starts id-button-pressed.service, that handles the event by setting the identify LED group’s Assert property.

    Note: It has an extra argument --continue that tells phosphor-gpio-monitor to not exit and continue running when the key is pressed.

Join The Discussion

Your email address will not be published. Required fields are marked *