PSE Configuration (Ubuntu)
Applicable to: K400 series systems
Installation (Intel IoT)
Ubuntu Desktop 20.04 and 22.04 for Intel IoT platforms are versions of Ubuntu that are validated to run a 5.13-intel and 5.15-intel kernel version. This version of Ubuntu is available from Canonical at https://ubuntu.com/download/iot/intel-iotg, and can be installed like any other Ubuntu release.
Scroll down to the "Select Processor" section, and select the "Intel® Atom® X6000E Series Elkhart Lake" option. Ubuntu 20.04 and 22.04 are supported. Other versions may have compatibility issues when trying to utilize the PSE functionality.
IMPORTANT In some releases of Ubuntu, the 'pinctrl_elkhartlake' driver will timeout
during startup and crash during shutdown. You can utilize the method below to
address the issue, by disabling this driver module.
One method is to add "modprobe.blacklist=pinctrl_elkhartlake" to the kernel command
line.PSE Driver Installation
Several system hardware interfaces, including CAN, DIO, Serial/COM and ignition sensing are managed through the system’s Programmable Services Engine (PSE).
The Elkhart Lake PSE can be managed over ISHTP/HECI from the host processor: This driver creates and manages a pse character device, which allows userspace applications to read and write data over the underlying ISHTP bus driver.
You can download the driver files and installation bundle here: PSE HECI Driver v1.0.0.
Or download and extract them from the command line:
$ wget https://static.onlogic.com/resources/firmware/utilities/pse_heci_v100.zip && unzip pse_heci_v100.zip -d pse_heci && cd pse_heciNOTE This driver was written for the 5.13.0-intel kernel. Running it against any other kernel version may require modification. At a minimum, the system requires an ISHTP bus device with UUID bb579a2e-cc54-4450-b1d0-5e7520dcad25, and the bundled additional source headers (intel-ish-hid-5.13) will need to be updated.Quickstart
To build and install the pse kernel module automatically, simply run:
This will check if the pre-compiled kernel modules are compatible with your system, and either install them or compile new ones in-place. If all goes well, you can move on to Using the PSE.
Building
It is also possible to manually build and install the PSE kernel module, which may be required if you intend to run a signed kernel.
Build Preparation
Install the prerequisites for building kernel modules:
Download the kernel headers for the current kernel:
Build
You should now be able to build with:
This will generate the kernel modules files, which can be loaded and checked with:
Install
Permanently installing the PSE driver can be done by copying it to your modules directory:
After this, the PSE module should be loaded and ready for use on each boot. You can confirm this with:
Using the PSE
The programmable services engine consumes commands in the form of packed header and body data structures. The complexity of these structures can be completely ignored by using the pre-compiled command-line application. See the Hardware Control Application (HWC) download within the system product documentation.
However, for tight application integration, it is possible to interface with the PSE directly from your software stack by reading and writing to the PSE character file (/dev/pse). A complete example that establishes a connection to the PSE and reads the firmware version information is included in the examples directory, and can be compiled with:
All source code referenced in the instructions below is included in full context in the examples directory. Additional example code is provided for using the CAN and DIO peripherals, as well as sample code for configuring the system’s automotive features. You can build all targets with:
1. Establish Connection
Before communication with the PSE can begin, the host client must establish a connection with the firmware client. This is performed by sending the ‘client connection’ IOCTL to the device file:

Once the connection has been established and returned, it is possible to send commands to the PSE.
2. Send a Command
Commands sent to the programmable services engine can either be header-only ‘short’ commands, or complete header + body ‘long’ commands.
The header portion of the command is as 48-bit wide structure that is transmitted as raw data to the PSE:

The command header can be easily described as a packed struct in C:
The possible valid system commands are enumerated by the heci_command_id_t, and include:
System Info
0x01
Report the system firmware version number
Digital IO
0x02
Set and read the system’s digital IO and LEDs
UART
0x03
Send data over an attached UART. Allows control of automotive features
CAN Bus
0x04
Send and receive CAN messages
PWM
0x05
Manage the Pulse Width Modulation of a DIO configured as PWM
I2C
0x06
Send/receive I2C data (not recommended for end-user configuration)
QEP
0x07
Configure a group of DIOs set as a Quadrature Encoder Peripheral
The argument packing depends on this command, and is described by the *_command_t structures.
UART, I2C, PWM, and QEP all use a generic ‘operation + device’ scheme, while DIO and CAN have separate formats:

The ‘body’ portion of each command is described by another structure:
The actual data format depends on the command sent; the can_send example function in examples/can.c shows one method for populating message data.
Once a message’s header and optional body portions are prepared, sending the command is as simple as writing to the open PSE device:

3. Read the Response
Any message sent to the PSE will receive a response from the embedded controller. The response message format is identical to the transmit format, and will always include the status of the last command received.
Some commands (like reading a CAN message), will result in additional data being returned, as indicated by the has_next flag. Application code may check this flag to determine if it should continue reading data:

4. Minimal Example
In the provided sample code, the files examples/pse.c and examples/pse.h provide some helpful abstraction to simplify this process. For instance, reading the PSE firmware version can be performed with the following code:
Last updated
