CPC GPIO Expander#
Enables the expansion of general purpose I/O's on a host system via a Silicon Labs microcontroller through the Co-Processor Communication (CPC) protocol.
Table of Contents#
Overview
Diagram
Architecture
Features
Installation
Components
Bootstrap
Usage
Secondary Application
Host Application
Interaction
Modification
API Reference
Configuration
Limitations
Interrupts
Considerations
Set value feedback
Initialization sequence
Troubleshooting
Overview#
The CPC GPIO Expander enables a CPC host (primary device) to discover and control GPIO pins on a Silicon Labs microcontroller (secondary device) through the Co-Processor Communication (CPC) protocol. The secondary device exposes GPIO pins as a CPC endpoint that can be accessed by any host implementing the CPC protocol.
For Linux-based hosts, the system consists of three main components:
CPC GPIO Secondary: Firmware running on the Silicon Labs microcontroller that exposes GPIO pins via CPC endpoints
CPC GPIO Driver: Linux kernel module that presents the remote GPIOs as standard
/dev/gpiochip*devicesCPC GPIO Bridge: Userspace daemon that bridges communication between the kernel driver and the CPC daemon
Data transfers between the host and the secondary device are handled through CPC endpoints, ensuring error-free, ordered packet delivery over a secure serial link (UART or SPI).
Diagram#
Architecture#
The CPC GPIO Expander follows a layered architecture:
┌─────────────────────────────────────────────────────────┐
│ Linux Userspace Applications │
│ (libgpiod, Python gpiod, Rust gpiod, etc.) │
└────────────────────┬──────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────┐
│ Linux GPIO Subsystem (/dev/gpiochip*) │
└────────────────────┬──────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────┐
│ CPC GPIO Kernel Driver │
│ (Generic Netlink Interface) │
└────────────────────┬──────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────┐
│ CPC GPIO Bridge (Userspace) │
│ (Rust daemon, Generic Netlink) │
└────────────────────┬──────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────┐
│ CPC Daemon (cpcd) │
│ (Serial/UART or SPI link) │
└────────────────────┬──────────────────────────────────┘
│
┌────────────────────▼──────────────────────────────────┐
│ Silicon Labs Microcontroller │
│ (CPC GPIO Secondary Firmware) │
│ GPIO Hardware Pins │
└────────────────────────────────────────────────────────┘
The communication flow (for Linux-based hosts):
Linux applications use standard GPIO APIs (libgpiod, etc.)
The Linux GPIO subsystem routes requests to the CPC GPIO driver
The driver communicates with the bridge via Generic Netlink
The bridge translates requests to CPC protocol messages
The CPC daemon handles serial communication with the secondary
The secondary firmware processes GPIO operations on the hardware
For other host platforms, the host must implement the CPC protocol to communicate with the secondary device's GPIO endpoint.
Features#
Native implementation using the Linux GPIO kernel/userspace subsystem
Compatible with 3rd party libraries (C, Rust, Python and more)
Supported operations:
Get a value
Set a value
Input configurations
Bias: No-Pull
Bias: Pull-Up
Bias: Pull-Down
Output configurations
Drive: Open-Drain, Bias: No-Pull
Drive: Open-Drain, Bias: Pull-Up
Drive: Open-Source, Bias: No-Pull
Drive: Open-Source, Bias: Pull-Down
Drive: Push-Pull, Bias: No-Pull
Installation#
Components#
The following components must be installed:
CPC GPIO Secondary (see device/README.md)
CPC GPIO Driver (see driver/README.md)
CPC GPIO Bridge (see bridge/README.md)
Bootstrap#
The recommended order to launch everything is:
Load the CPC GPIO Driver
Flash the CPC GPIO Secondary
Run the CPC Daemon
Run the CPC GPIO Bridge
Usage#
Secondary Application#
The CPC GPIO Expander component integrates with the Silicon Labs platform's service initialization system (sl_main). The initialization and processing of GPIO commands are handled automatically through event handlers - you do not need to manually call the CPC GPIO functions.
Automatic Initialization#
The component automatically registers event handlers that are called by the platform:
**
service_initevent**: Automatically callssl_cpc_gpio_expander_init()during system initialization**
service_process_actionevent**: Automatically callssl_cpc_gpio_expander_process_action()during the main loop
These events are fired by sl_main_init() and sl_main_process_action() respectively.
Processing Actions#
The method for processing incoming GPIO commands depends on whether a kernel/RTOS is present:
Bare-metal Applications (No Kernel/RTOS):
In bare-metal applications, sl_main_process_action() must be called in the main loop. This automatically calls sl_cpc_gpio_expander_process_action() through the event handler system. The function:
Reads incoming commands from the CPC endpoint
Executes GPIO operations (get/set value, configure, set direction)
Sends responses back to the host
Kernel/RTOS Applications:
When using a kernel or RTOS (e.g., FreeRTOS, Micrium OS), the processing is handled automatically by a background task created during initialization. The service_process_action event handler is not used in RTOS applications.
Sample Code#
Bare-metal Application:
#include "sl_main_init.h"
#include "sl_main_process_action.h"
int main(void)
{
// Initialize Silicon Labs device, system, service(s) and protocol stack(s).
// This automatically calls sl_cpc_gpio_expander_init() via service_init event
sl_main_init();
// User provided code
app_init();
while (1) {
// Silicon Labs components process action routine
// This automatically calls sl_cpc_gpio_expander_process_action()
// via service_process_action event
sl_main_process_action();
// User provided code. Application process.
app_process_action();
}
}
Kernel/RTOS Application:
#include "sl_main_init.h"
int main(void)
{
// Initialize Silicon Labs device, system, service(s) and protocol stack(s).
// This automatically calls sl_cpc_gpio_expander_init() via service_init event
// In RTOS applications, a background task is automatically created to handle
// GPIO command processing - no manual calls needed
sl_main_init();
// User provided code
app_init();
while (1) {
// User provided code. Application process.
app_process_action();
}
}
Note: You do not need to include
sl_cpc_gpio_expander.hor manually callsl_cpc_gpio_expander_init()orsl_cpc_gpio_expander_process_action(). These are handled automatically by the platform's service system through event handlers registered by the component.
Host Application#
For Linux-based hosts, the CPC Secondary appears as a standard GPIO chip (/dev/gpiochip*). Applications can interact with it using any standard GPIO library or tool. For other host platforms, the host must implement the CPC protocol to communicate with the GPIO endpoint.
Using libgpiod (C)#
#include <gpiod.h>
int main(void)
{
struct gpiod_chip *chip;
struct gpiod_line *line;
int value;
// Open the GPIO chip
chip = gpiod_chip_open_by_name("cpc-gpio");
if (!chip) {
perror("Failed to open GPIO chip");
return -1;
}
// Get a GPIO line
line = gpiod_chip_get_line(chip, 0);
if (!line) {
perror("Failed to get GPIO line");
gpiod_chip_close(chip);
return -1;
}
// Read the value
value = gpiod_line_get_value(line);
printf("GPIO 0 value: %d\n", value);
// Set the value
gpiod_line_set_value(line, 1);
gpiod_chip_close(chip);
return 0;
}
Using Python gpiod#
import gpiod
# Open the GPIO chip
chip = gpiod.Chip("cpc-gpio")
# Get a GPIO line
line = chip.get_line(0)
# Read the value
value = line.get_value()
print(f"GPIO 0 value: {value}")
# Set the value
line.set_value(1)
Using Command Line Tools#
# List available GPIO chips
gpiodetect
# Read GPIO value
gpioset cpc-gpio 0=1
# Set GPIO value
gpioget cpc-gpio 0
Interaction#
The CPC Secondary is seen as a GPIO chip (/dev/gpiochip*).
To get started, you can use userspace tools such as gpiod to interact with the GPIO's.
If you wish to do this programatically, there are 3rd party libraries available as highlighted in the Features section.
Modification#
Should you want to expose more (or fewer) GPIO's, you must reflash the CPC GPIO Secondary and run the CPC GPIO Bridge again.
There should never be a need to unload/load the CPC GPIO Driver as it handles deinitialization/initialization dynamically.
API Reference#
The CPC GPIO Expander provides APIs for the secondary (microcontroller) side:
Secondary API#
The secondary API is defined in sl_cpc_gpio_expander.h:
**
sl_cpc_gpio_expander_init()**: Initialize the CPC GPIO endpoint on the secondary device**
sl_cpc_gpio_expander_process_action()**: Process incoming GPIO commands from the host
See the API documentation for detailed function descriptions and parameters.
Host API#
For Linux-based hosts, the host interacts with GPIOs through the standard Linux GPIO subsystem:
/dev/gpiochip*device fileslibgpiod library functions
Standard GPIO sysfs (if enabled)
For other host platforms, the host must implement the CPC protocol to communicate with the GPIO endpoint. The protocol is defined by the command types and packet structures used in the secondary implementation.
Configuration#
Secondary Configuration#
The secondary firmware can be configured through component configuration files:
**
sl_cpc_gpio_expander_config.h**: Main configuration fileGPIO endpoint settings
Buffer sizes
Timeout values
**
sl_cpc_gpio_expander_gpio_inst_config.h**: GPIO instance configurationGPIO pin definitions
Pin names
Default configurations
**
sl_cpc_gpio_expander_kernel_config.h**: Kernel/RTOS configurationTask priorities
Stack sizes
Delay settings
Driver Configuration (Linux hosts only)#
The Linux kernel driver is configured through module parameters and device tree (if applicable). See the driver README for details.
Bridge Configuration#
The bridge can be configured through command-line arguments and environment variables. See the bridge README for details.
Limitations#
Interrupts#
Interrupts are currently not implemented. One must use polling mechanisms as a workaround.
Considerations#
Set value feedback#
Set value operations don't propagate an error to the caller, however, they are logged by the CPC GPIO Driver/Bridge. It is therefore recommended to follow up a Set value operation with a Get value operation to be sure the value is set.
This is normally not a problem unless the CPC Daemon/GPIO Bridge process is terminated and/or the CPC GPIO Secondary has restarted.
Initialization sequence#
Users must release previously held CPC GPIO's back to the kernel before starting the CPC GPIO Bridge again. Not releasing GPIO's will prevent the CPC GPIO Driver from deinitializing the current set of GPIO's and thus prevent the CPC GPIO Bridge from progressing in its initialization sequence.
This may come off as a surprise compared to sysfs but this is by design with the new GPIO subsystem.
Troubleshooting#
GPIO Chip Not Appearing#
If the GPIO chip doesn't appear in /dev/gpiochip*:
Check driver status: Verify the CPC GPIO driver is loaded
lsmod | grep cpc_gpioCheck bridge status: Ensure the CPC GPIO bridge is running
ps aux | grep cpc-gpio-bridgeCheck CPC daemon: Verify the CPC daemon is running and connected
systemctl status cpcdCheck logs: Review kernel and bridge logs for errors
dmesg | grep cpc-gpio journalctl -u cpc-gpio-bridge
GPIO Operations Failing#
If GPIO read/write operations fail:
Verify connection: Check that the secondary device is powered and connected
Check endpoint: Ensure the GPIO endpoint is properly initialized on the secondary
Review timeout settings: Increase timeout values if operations are timing out
Check permissions: Ensure the user has access to
/dev/gpiochip*devices
Bridge Not Starting#
If the bridge fails to start:
Check dependencies: Ensure CPC daemon is running first
Verify configuration: Check bridge configuration files and command-line arguments
Review logs: Check system logs for initialization errors
Port conflicts: Ensure no other process is using the required ports
Secondary Not Responding#
If the secondary device doesn't respond to GPIO commands:
Verify firmware: Ensure the correct CPC GPIO secondary firmware is flashed
Check initialization: Verify
sl_cpc_gpio_expander_init()is called during startupProcess actions: Ensure
sl_cpc_gpio_expander_process_action()is called regularlyEndpoint status: Check that the CPC endpoint is open and connected