Pi4 expander improvements

This commit is contained in:
Ken Van Hoeylandt 2026-02-28 17:00:49 +01:00
parent 24a38f7aaf
commit dfb0db86c5
10 changed files with 317 additions and 163 deletions

View File

@ -7,6 +7,7 @@
#include <Tactility/hal/Configuration.h> #include <Tactility/hal/Configuration.h>
#include <Tactility/hal/i2c/I2c.h> #include <Tactility/hal/i2c/I2c.h>
#include <drivers/pi4ioe5v6408.h> #include <drivers/pi4ioe5v6408.h>
#include <tactility/drivers/gpio_controller.h>
using namespace tt::hal; using namespace tt::hal;
@ -19,50 +20,110 @@ static DeviceVector createDevices() {
}; };
} }
static error_t initPower(::Device* io_expander0, ::Device* io_expander1) { /*
PI4IOE5V6408-0 (0x43)
- Bit 0: RF internal/external switch
- Bit 1: Speaker enable
- Bit 2: External 5V bus enable
- Bit 3: /
- Bit 4: LCD reset
- Bit 5: Touch reset
- Bit 6: Camera reset
- Bit 7: Headphone detect
*/
constexpr auto GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL = 0;
constexpr auto GPIO_EXP0_PIN_SPEAKER_ENABLE = 1;
constexpr auto GPIO_EXP0_PIN_EXTERNAL_5V_BUS_ENABLE = 2;
constexpr auto GPIO_EXP0_PIN_LCD_RESET = 4;
constexpr auto GPIO_EXP0_PIN_TOUCH_RESET = 5;
constexpr auto GPIO_EXP0_PIN_CAMERA_RESET = 6;
constexpr auto GPIO_EXP0_PIN_HEADPHONE_DETECT = 7;
/*
PI4IOE5V6408-1 (0x44)
- Bit 0: C6 WLAN enable
- Bit 1: /
- Bit 2: /
- Bit 3: USB-A 5V enable
- Bit 4: Device power: PWROFF_PLUSE
- Bit 5: IP2326: nCHG_QC_EN
- Bit 6: IP2326: CHG_STAT_LED
- Bit 7: IP2326: CHG_EN
*/
constexpr auto GPIO_EXP1_PIN_C6_WLAN_ENABLE = 0;
constexpr auto GPIO_EXP1_PIN_USB_A_5V_ENABLE = 3;
constexpr auto GPIO_EXP1_PIN_DEVICE_POWER = 4;
constexpr auto GPIO_EXP1_PIN_IP2326_NCHG_QC_EN = 5;
constexpr auto GPIO_EXP1_PIN_IP2326_CHG_STAT_LED = 6;
constexpr auto GPIO_EXP1_PIN_IP2326_CHG_EN = 7;
static void initExpander0(::Device* io_expander0) {
constexpr TickType_t i2c_timeout = pdMS_TO_TICKS(10); constexpr TickType_t i2c_timeout = pdMS_TO_TICKS(10);
/* auto* rf_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL, GPIO_OWNER_GPIO);
PI4IOE5V6408-0 (0x43) auto* speaker_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_SPEAKER_ENABLE, GPIO_OWNER_GPIO);
- Bit 0: RF internal/external switch auto* external_5v_bus_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_EXTERNAL_5V_BUS_ENABLE, GPIO_OWNER_GPIO);
- Bit 1: Speaker enable auto* lcd_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_LCD_RESET, GPIO_OWNER_GPIO);
- Bit 2: External 5V bus enable auto* touch_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_TOUCH_RESET, GPIO_OWNER_GPIO);
- Bit 3: / auto* camera_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_CAMERA_RESET, GPIO_OWNER_GPIO);
- Bit 4: LCD reset auto* headphone_detect_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_HEADPHONE_DETECT, GPIO_OWNER_GPIO);
- Bit 5: Touch reset
- Bit 6: Camera reset
- Bit 7: Headphone detect
*/
check(pi4ioe5v6408_set_direction(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE); gpio_descriptor_set_flags(rf_pin, GPIO_FLAG_DIRECTION_OUTPUT);
check(pi4ioe5v6408_set_output_level(io_expander0, 0b01000110, i2c_timeout) == ERROR_NONE); gpio_descriptor_set_flags(speaker_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
check(pi4ioe5v6408_set_output_high_impedance(io_expander0, 0b00000000, i2c_timeout) == ERROR_NONE); gpio_descriptor_set_flags(external_5v_bus_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
check(pi4ioe5v6408_set_pull_select(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE); gpio_descriptor_set_flags(lcd_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
check(pi4ioe5v6408_set_pull_enable(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE); gpio_descriptor_set_flags(touch_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(camera_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(headphone_detect_pin, GPIO_FLAG_DIRECTION_INPUT);
gpio_descriptor_set_level(rf_pin, false);
gpio_descriptor_set_level(speaker_enable_pin, false);
gpio_descriptor_set_level(external_5v_bus_enable_pin, true);
gpio_descriptor_set_level(lcd_reset_pin, false);
gpio_descriptor_set_level(touch_reset_pin, false);
gpio_descriptor_set_level(camera_reset_pin, true);
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
check(pi4ioe5v6408_set_output_level(io_expander0, 0b01110110, i2c_timeout) == ERROR_NONE); // Enable touch and lcd, but not the camera
gpio_descriptor_set_level(lcd_reset_pin, true);
gpio_descriptor_set_level(touch_reset_pin, true);
/* gpio_descriptor_release(rf_pin);
PI4IOE5V6408-1 (0x44) gpio_descriptor_release(speaker_enable_pin);
- Bit 0: C6 WLAN enable gpio_descriptor_release(external_5v_bus_enable_pin);
- Bit 1: / gpio_descriptor_release(lcd_reset_pin);
- Bit 2: / gpio_descriptor_release(touch_reset_pin);
- Bit 3: USB-A 5V enable gpio_descriptor_release(camera_reset_pin);
- Bit 4: Device power: PWROFF_PLUSE gpio_descriptor_release(headphone_detect_pin);
- Bit 5: IP2326: nCHG_QC_EN }
- Bit 6: IP2326: CHG_STAT_LED
- Bit 7: IP2326: CHG_EN
*/
check(pi4ioe5v6408_set_direction(io_expander1, 0b10111001, i2c_timeout) == ERROR_NONE); static void initExpander1(::Device* io_expander1) {
check(pi4ioe5v6408_set_output_high_impedance(io_expander1, 0b00000110, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_select(io_expander1, 0b10111001, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_enable(io_expander1, 0b11111001, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_input_default_level(io_expander1, 0b01000000, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_interrupt_mask(io_expander1, 0b10111111, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_output_level(io_expander1, 0b10001001, i2c_timeout) == ERROR_NONE);
return ERROR_NONE; auto* c6_wlan_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_C6_WLAN_ENABLE, GPIO_OWNER_GPIO);
auto* usb_a_5v_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_USB_A_5V_ENABLE, GPIO_OWNER_GPIO);
auto* device_power_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_DEVICE_POWER, GPIO_OWNER_GPIO);
auto* ip2326_ncharge_qc_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_NCHG_QC_EN, GPIO_OWNER_GPIO);
auto* ip2326_charge_state_led_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_STAT_LED, GPIO_OWNER_GPIO);
auto* ip2326_charge_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_EN, GPIO_OWNER_GPIO);
gpio_descriptor_set_flags(c6_wlan_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(usb_a_5v_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(device_power_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_ncharge_qc_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_charge_state_led_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_charge_enable_pin, GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP);
gpio_descriptor_set_level(c6_wlan_enable_pin, true);
gpio_descriptor_set_level(usb_a_5v_enable_pin, true);
gpio_descriptor_set_level(device_power_pin, false);
gpio_descriptor_set_level(ip2326_ncharge_qc_enable_pin, false);
gpio_descriptor_set_level(ip2326_charge_state_led_pin, false);
gpio_descriptor_release(c6_wlan_enable_pin);
gpio_descriptor_release(usb_a_5v_enable_pin);
gpio_descriptor_release(device_power_pin);
gpio_descriptor_release(ip2326_ncharge_qc_enable_pin);
gpio_descriptor_release(ip2326_charge_state_led_pin);
gpio_descriptor_release(ip2326_charge_enable_pin);
} }
static error_t initSound(::Device* i2c_controller, ::Device* io_expander0 = nullptr) { static error_t initSound(::Device* i2c_controller, ::Device* io_expander0 = nullptr) {
@ -113,13 +174,11 @@ static error_t initSound(::Device* i2c_controller, ::Device* io_expander0 = null
return error; return error;
} }
uint8_t output_level = 0; auto* speaker_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_SPEAKER_ENABLE, GPIO_OWNER_GPIO);
if (pi4ioe5v6408_get_output_level(io_expander0, &output_level, pdMS_TO_TICKS(100)) != ERROR_NONE) { check(speaker_enable_pin, "Failed to acquire speaker enable pin");
LOG_E(TAG, "Failed to read power level: %s", error_to_string(error)); error = gpio_descriptor_set_level(speaker_enable_pin, true);
return ERROR_RESOURCE; gpio_descriptor_release(speaker_enable_pin);
} if (error != ERROR_NONE) {
if (pi4ioe5v6408_set_output_level(io_expander0, output_level | 0b00000010, pdMS_TO_TICKS(100)) != ERROR_NONE) {
LOG_E(TAG, "Failed to enable amplifier: %s", error_to_string(error)); LOG_E(TAG, "Failed to enable amplifier: %s", error_to_string(error));
return ERROR_RESOURCE; return ERROR_RESOURCE;
} }
@ -132,10 +191,12 @@ static bool initBoot() {
check(i2c0, "i2c0 not found"); check(i2c0, "i2c0 not found");
auto* io_expander0 = device_find_by_name("io_expander0"); auto* io_expander0 = device_find_by_name("io_expander0");
check(io_expander0, "io_expander0 not found");
auto* io_expander1 = device_find_by_name("io_expander1"); auto* io_expander1 = device_find_by_name("io_expander1");
check(i2c0, "i2c0 not found"); check(io_expander1, "io_expander1 not found");
initPower(io_expander0, io_expander1); initExpander0(io_expander0);
initExpander1(io_expander1);
error_t error = initSound(i2c0, io_expander0); error_t error = initSound(i2c0, io_expander0);
if (error != ERROR_NONE) { if (error != ERROR_NONE) {

View File

@ -2,40 +2,16 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <tactility/error.h>
#include <tactility/freertos/freertos.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct Device;
struct Pi4ioe5v6408Config { struct Pi4ioe5v6408Config {
/** Address on bus */ /** Address on bus */
uint8_t address; uint8_t address;
}; };
error_t pi4ioe5v6408_set_direction(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_output_level(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_output_level(struct Device* device, uint8_t* bits, TickType_t timeout);
error_t pi4ioe5v6408_set_output_high_impedance(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_input_default_level(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_pull_enable(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_pull_select(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_input_level(struct Device* device, uint8_t* bits, TickType_t timeout);
error_t pi4ioe5v6408_set_interrupt_mask(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_interrupt_level(struct Device* device, uint8_t* bits, TickType_t timeout);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -6,7 +6,6 @@
extern "C" { extern "C" {
extern Driver pi4ioe5v6408_driver; extern Driver pi4ioe5v6408_driver;
extern const ModuleSymbol pi4ioe5v6408_module_symbols[];
static error_t start() { static error_t start() {
/* We crash when construct fails, because if a single driver fails to construct, /* We crash when construct fails, because if a single driver fails to construct,
@ -26,7 +25,7 @@ Module pi4ioe5v6408_module = {
.name = "pi4ioe5v6408", .name = "pi4ioe5v6408",
.start = start, .start = start,
.stop = stop, .stop = stop,
.symbols = pi4ioe5v6408_module_symbols, .symbols = nullptr,
.internal = nullptr .internal = nullptr
}; };

View File

@ -3,6 +3,8 @@
#include <pi4ioe5v6408_module.h> #include <pi4ioe5v6408_module.h>
#include <tactility/device.h> #include <tactility/device.h>
#include <tactility/driver.h> #include <tactility/driver.h>
#include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/gpio_descriptor.h>
#include <tactility/drivers/i2c_controller.h> #include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h> #include <tactility/log.h>
@ -27,72 +29,181 @@ static error_t start(Device* device) {
return ERROR_RESOURCE; return ERROR_RESOURCE;
} }
LOG_I(TAG, "Started PI4IOE5V6408 device %s", device->name); LOG_I(TAG, "Started PI4IOE5V6408 device %s", device->name);
return ERROR_NONE;
return gpio_controller_init_descriptors(device, 8, nullptr);
} }
static error_t stop(Device* device) { static error_t stop(Device* device) {
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
return ERROR_NONE; return ERROR_NONE;
} }
extern "C" { extern "C" {
error_t pi4ioe5v6408_set_direction(Device* device, uint8_t bits, TickType_t timeout) {
static error_t set_level(GpioDescriptor* descriptor, bool high) {
auto* device = descriptor->controller;
auto* parent = device_get_parent(device); auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_DIRECTION, bits, timeout); auto address = GET_CONFIG(device)->address;
uint8_t bit = 1 << descriptor->pin;
if (high) {
return i2c_controller_register8_set_bits(parent, address, PI4_REGISTER_OUTPUT_LEVEL, bit, portMAX_DELAY);
} else {
return i2c_controller_register8_reset_bits(parent, address, PI4_REGISTER_OUTPUT_LEVEL, bit, portMAX_DELAY);
}
} }
error_t pi4ioe5v6408_set_output_level(Device* device, uint8_t bits, TickType_t timeout) { static error_t get_level(GpioDescriptor* descriptor, bool* high) {
auto* device = descriptor->controller;
auto* parent = device_get_parent(device); auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_LEVEL, bits, timeout); auto address = GET_CONFIG(device)->address;
uint8_t bits;
error_t err = i2c_controller_register8_get(parent, address, PI4_REGISTER_INPUT_LEVEL, &bits, portMAX_DELAY);
if (err != ERROR_NONE) {
return err;
}
*high = (bits & (1 << descriptor->pin)) != 0;
return ERROR_NONE;
} }
error_t pi4ioe5v6408_get_output_level(Device* device, uint8_t* bits, TickType_t timeout) { static error_t set_flags(GpioDescriptor* descriptor, gpio_flags_t flags) {
auto* device = descriptor->controller;
auto* parent = device_get_parent(device); auto* parent = device_get_parent(device);
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_LEVEL, bits, timeout); auto address = GET_CONFIG(device)->address;
uint8_t bit = 1 << descriptor->pin;
error_t err;
// Direction
if (flags & GPIO_FLAG_DIRECTION_OUTPUT) {
err = i2c_controller_register8_set_bits(parent, address, PI4_REGISTER_DIRECTION, bit, portMAX_DELAY);
} else {
err = i2c_controller_register8_reset_bits(parent, address, PI4_REGISTER_DIRECTION, bit, portMAX_DELAY);
}
if (err != ERROR_NONE) {
return err;
}
// High Impedance
if (flags & GPIO_FLAG_HIGH_IMPEDANCE) {
err = i2c_controller_register8_set_bits(parent, address, PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE, bit, portMAX_DELAY);
} else {
err = i2c_controller_register8_reset_bits(parent, address, PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE, bit, portMAX_DELAY);
}
if (err != ERROR_NONE) {
return err;
}
// Pull-up/down
if (flags & (GPIO_FLAG_PULL_UP | GPIO_FLAG_PULL_DOWN)) {
// Set pull up or pull down
if (flags & GPIO_FLAG_PULL_UP) {
err = i2c_controller_register8_set_bits(parent, address, PI4_REGISTER_PULL_SELECT, bit, portMAX_DELAY);
} else {
err = i2c_controller_register8_reset_bits(parent, address, PI4_REGISTER_PULL_SELECT, bit, portMAX_DELAY);
}
if (err != ERROR_NONE) {
return err;
}
// Enable pull-up/down
err = i2c_controller_register8_set_bits(parent, address, PI4_REGISTER_PULL_ENABLE, bit, portMAX_DELAY);
} else {
// Disable pull-up/down
err = i2c_controller_register8_reset_bits(parent, address, PI4_REGISTER_PULL_ENABLE, bit, portMAX_DELAY);
}
return err;
} }
error_t pi4ioe5v6408_set_output_high_impedance(struct Device* device, uint8_t bits, TickType_t timeout) { static error_t get_flags(GpioDescriptor* descriptor, gpio_flags_t* flags) {
auto* device = descriptor->controller;
auto* parent = device_get_parent(device); auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE, bits, timeout); auto address = GET_CONFIG(device)->address;
uint8_t bit = 1 << descriptor->pin;
uint8_t val;
error_t err;
gpio_flags_t f = GPIO_FLAG_NONE;
// Direction
err = i2c_controller_register8_get(parent, address, PI4_REGISTER_DIRECTION, &val, portMAX_DELAY);
if (err != ERROR_NONE) return err;
if (val & bit) {
f |= GPIO_FLAG_DIRECTION_OUTPUT;
} else {
f |= GPIO_FLAG_DIRECTION_INPUT;
}
// Pull-up/down
err = i2c_controller_register8_get(parent, address, PI4_REGISTER_PULL_ENABLE, &val, portMAX_DELAY);
if (err != ERROR_NONE) return err;
if (val & bit) {
err = i2c_controller_register8_get(parent, address, PI4_REGISTER_PULL_SELECT, &val, portMAX_DELAY);
if (err != ERROR_NONE) return err;
if (val & bit) {
f |= GPIO_FLAG_PULL_UP;
} else {
f |= GPIO_FLAG_PULL_DOWN;
}
}
// High Impedance
err = i2c_controller_register8_get(parent, address, PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE, &val, portMAX_DELAY);
if (err != ERROR_NONE) return err;
if (val & bit) {
f |= GPIO_FLAG_HIGH_IMPEDANCE;
}
*flags = f;
return ERROR_NONE;
} }
error_t pi4ioe5v6408_set_input_default_level(struct Device* device, uint8_t bits, TickType_t timeout) { static error_t get_native_pin_number(GpioDescriptor* descriptor, void* pin_number) {
auto* parent = device_get_parent(device); return ERROR_NOT_SUPPORTED;
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_INPUT_DEFAULT_LEVEL, bits, timeout);
} }
error_t pi4ioe5v6408_set_pull_enable(struct Device* device, uint8_t bits, TickType_t timeout) { static error_t add_callback(GpioDescriptor* descriptor, void (*callback)(void*), void* arg) {
auto* parent = device_get_parent(device); return ERROR_NOT_SUPPORTED;
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_PULL_ENABLE, bits, timeout);
} }
error_t pi4ioe5v6408_set_pull_select(struct Device* device, uint8_t bits, TickType_t timeout) { static error_t remove_callback(GpioDescriptor* descriptor) {
auto* parent = device_get_parent(device); return ERROR_NOT_SUPPORTED;
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_PULL_SELECT, bits, timeout);
} }
error_t pi4ioe5v6408_get_input_level(struct Device* device, uint8_t* bits, TickType_t timeout) { static error_t enable_interrupt(GpioDescriptor* descriptor) {
auto* parent = device_get_parent(device); return ERROR_NOT_SUPPORTED;
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_INPUT_LEVEL, bits, timeout);
} }
error_t pi4ioe5v6408_set_interrupt_mask(struct Device* device, uint8_t bits, TickType_t timeout) { static error_t disable_interrupt(GpioDescriptor* descriptor) {
auto* parent = device_get_parent(device); return ERROR_NOT_SUPPORTED;
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_INTERRUPT_MASK, bits, timeout);
} }
error_t pi4ioe5v6408_get_interrupt_level(struct Device* device, uint8_t* bits, TickType_t timeout) { const static GpioControllerApi pi4_gpio_api = {
auto* parent = device_get_parent(device); .set_level = set_level,
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_INTERRUPT_LEVEL, bits, timeout); .get_level = get_level,
} .set_flags = set_flags,
.get_flags = get_flags,
.get_native_pin_number = get_native_pin_number,
.add_callback = add_callback,
.remove_callback = remove_callback,
.enable_interrupt = enable_interrupt,
.disable_interrupt = disable_interrupt
};
Driver pi4ioe5v6408_driver = { Driver pi4ioe5v6408_driver = {
.name = "pi4ioe5v6408", .name = "pi4ioe5v6408",
.compatible = (const char*[]) { "diodes,pi4ioe5v6408", nullptr}, .compatible = (const char*[]) { "diodes,pi4ioe5v6408", nullptr},
.start_device = start, .start_device = start,
.stop_device = stop, .stop_device = stop,
.api = nullptr, .api = static_cast<const void*>(&pi4_gpio_api),
.device_type = nullptr, .device_type = &GPIO_CONTROLLER_TYPE,
.owner = &pi4ioe5v6408_module, .owner = &pi4ioe5v6408_module,
.internal = nullptr .internal = nullptr
}; };

View File

@ -1,15 +0,0 @@
#include <drivers/pi4ioe5v6408.h>
#include <tactility/module.h>
const struct ModuleSymbol pi4ioe5v6408_module_symbols[] = {
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_direction),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_output_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_output_high_impedance),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_input_default_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_pull_enable),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_pull_select),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_get_input_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_interrupt_mask),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_get_interrupt_level),
MODULE_SYMBOL_TERMINATOR
};

View File

@ -17,7 +17,7 @@ struct Esp32GpioInternal {
}; };
#define GET_CONFIG(device) ((struct Esp32GpioConfig*)device->config) #define GET_CONFIG(device) ((struct Esp32GpioConfig*)device->config)
#define GET_INTERNAL(device) ((struct Esp32GpioInternal*)device->internal) #define GET_INTERNAL_FROM_DESCRIPTOR(gpio_descriptor) ((struct Esp32GpioInternal*)gpio_descriptor->controller_context)
extern "C" { extern "C" {
@ -102,18 +102,18 @@ static error_t get_native_pin_number(GpioDescriptor* descriptor, void* pin_numbe
return ERROR_NONE; return ERROR_NONE;
} }
static error_t add_callback(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg) { static error_t add_callback(GpioDescriptor* descriptor, void (*callback)(void*), void* arg) {
auto esp_error = gpio_isr_handler_add(static_cast<gpio_num_t>(descriptor->pin), callback, arg); auto esp_error = gpio_isr_handler_add(static_cast<gpio_num_t>(descriptor->pin), callback, arg);
return esp_err_to_error(esp_error); return esp_err_to_error(esp_error);
} }
static error_t remove_callback(struct GpioDescriptor* descriptor) { static error_t remove_callback(GpioDescriptor* descriptor) {
auto esp_error = gpio_isr_handler_remove(static_cast<gpio_num_t>(descriptor->pin)); auto esp_error = gpio_isr_handler_remove(static_cast<gpio_num_t>(descriptor->pin));
return esp_err_to_error(esp_error); return esp_err_to_error(esp_error);
} }
static error_t enable_interrupt(struct GpioDescriptor* descriptor) { static error_t enable_interrupt(GpioDescriptor* descriptor) {
auto* internal = GET_INTERNAL(descriptor->controller); auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
if (internal->isr_service_ref_count == 0) { if (internal->isr_service_ref_count == 0) {
auto esp_error = gpio_install_isr_service(0); auto esp_error = gpio_install_isr_service(0);
if (esp_error != ESP_OK && esp_error != ESP_ERR_INVALID_STATE) { if (esp_error != ESP_OK && esp_error != ESP_ERR_INVALID_STATE) {
@ -127,8 +127,8 @@ static error_t enable_interrupt(struct GpioDescriptor* descriptor) {
return esp_err_to_error(esp_error); return esp_err_to_error(esp_error);
} }
static error_t disable_interrupt(struct GpioDescriptor* descriptor) { static error_t disable_interrupt(GpioDescriptor* descriptor) {
auto* internal = GET_INTERNAL(descriptor->controller); auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin)); auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin));
if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) { if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) {
internal->isr_service_ref_count--; internal->isr_service_ref_count--;
@ -142,18 +142,18 @@ static error_t disable_interrupt(struct GpioDescriptor* descriptor) {
static error_t start(Device* device) { static error_t start(Device* device) {
ESP_LOGI(TAG, "start %s", device->name); ESP_LOGI(TAG, "start %s", device->name);
const Esp32GpioConfig* config = GET_CONFIG(device); const Esp32GpioConfig* config = GET_CONFIG(device);
device_set_driver_data(device, new Esp32GpioInternal()); auto* internal = new Esp32GpioInternal();
return gpio_controller_init_descriptors(device, config->gpioCount, nullptr); return gpio_controller_init_descriptors(device, config->gpioCount, internal);
} }
static error_t stop(Device* device) { static error_t stop(Device* device) {
ESP_LOGI(TAG, "stop %s", device->name); ESP_LOGI(TAG, "stop %s", device->name);
auto* internal = GET_INTERNAL(device); auto* internal = static_cast<Esp32GpioInternal*>(gpio_controller_get_controller_context(device));
if (internal->isr_service_ref_count > 0) { if (internal->isr_service_ref_count > 0) {
gpio_uninstall_isr_service(); gpio_uninstall_isr_service();
} }
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
delete internal; delete internal;
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
return ERROR_NONE; return ERROR_NONE;
} }
@ -169,14 +169,14 @@ const static GpioControllerApi esp32_gpio_api = {
.disable_interrupt = disable_interrupt .disable_interrupt = disable_interrupt
}; };
extern struct Module platform_esp32_module; extern Module platform_esp32_module;
Driver esp32_gpio_driver = { Driver esp32_gpio_driver = {
.name = "esp32_gpio", .name = "esp32_gpio",
.compatible = (const char*[]) { "espressif,esp32-gpio", nullptr }, .compatible = (const char*[]) { "espressif,esp32-gpio", nullptr },
.start_device = start, .start_device = start,
.stop_device = stop, .stop_device = stop,
.api = (void*)&esp32_gpio_api, .api = static_cast<const void*>(&esp32_gpio_api),
.device_type = &GPIO_CONTROLLER_TYPE, .device_type = &GPIO_CONTROLLER_TYPE,
.owner = &platform_esp32_module, .owner = &platform_esp32_module,
.internal = nullptr .internal = nullptr

View File

@ -7,7 +7,6 @@ extern "C" {
#endif #endif
#include <tactility/device.h> #include <tactility/device.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#define GPIO_FLAGS_MASK 0x1f #define GPIO_FLAGS_MASK 0x1f
@ -27,6 +26,7 @@ extern "C" {
#define GPIO_FLAG_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5] #define GPIO_FLAG_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5]
#define GPIO_FLAG_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_FLAG_INTERRUPT_BITMASK) >> 5) #define GPIO_FLAG_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_FLAG_INTERRUPT_BITMASK) >> 5)
#define GPIO_FLAG_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5)) #define GPIO_FLAG_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5))
#define GPIO_FLAG_HIGH_IMPEDANCE (1 << 8)
typedef enum { typedef enum {
GPIO_INTERRUPT_DISABLE = 0, GPIO_INTERRUPT_DISABLE = 0,

View File

@ -201,6 +201,15 @@ error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_cou
*/ */
error_t gpio_controller_deinit_descriptors(struct Device* device); error_t gpio_controller_deinit_descriptors(struct Device* device);
/**
* Unlike other drivers, a GPIO controller's internal data is created and set by gpio_controller_init_descriptors()
* This means that the specific controller implementation cannot set the device's driver data, as it's already set by the GPIO controller base coded.
* When calling init descriptors, the caller can pass a controller_context, which is an optional pointer that holds the implementation's internal data.
* @param device the GPIO controller device
* @return ERROR_NONE if successful
*/
void* gpio_controller_get_controller_context(struct Device* device);
extern const struct DeviceType GPIO_CONTROLLER_TYPE; extern const struct DeviceType GPIO_CONTROLLER_TYPE;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -11,6 +11,11 @@ struct GpioDescriptor {
gpio_pin_t pin; gpio_pin_t pin;
/** @brief Current owner */ /** @brief Current owner */
enum GpioOwnerType owner_type; enum GpioOwnerType owner_type;
/** @brief Implementation-specific context (e.g. from esp32 controller internally) */ /**
* @brief Implementation-specific context (e.g. from esp32 controller internally)
* Unlike other drivers, a GPIO controller's internal data is created and set by gpio_controller_init_descriptors()
* This means that the specific controller implementation cannot set the device's driver data, as it's already set by the GPIO controller base coded.
* When calling init descriptors, the caller can pass a controller_context, which is an optional pointer that holds the implementation's internal data.
*/
void* controller_context; void* controller_context;
}; };

View File

@ -14,22 +14,25 @@
extern "C" { extern "C" {
struct GpioControllerData { struct GpioControllerData {
struct Mutex mutex {}; Mutex mutex {};
uint32_t pin_count; uint32_t pin_count;
struct GpioDescriptor* descriptors = nullptr; GpioDescriptor* descriptors = nullptr;
void* controller_context;
explicit GpioControllerData(uint32_t pin_count) : pin_count(pin_count) { explicit GpioControllerData(
uint32_t pin_count, void* controller_context
) : pin_count(pin_count), controller_context(controller_context) {
mutex_construct(&mutex); mutex_construct(&mutex);
} }
error_t init_descriptors(Device* device, void* controller_context) { error_t init_descriptors(Device* device) {
descriptors = (struct GpioDescriptor*)calloc(pin_count, sizeof(struct GpioDescriptor)); descriptors = static_cast<GpioDescriptor*>(calloc(pin_count, sizeof(GpioDescriptor)));
if (!descriptors) return ERROR_OUT_OF_MEMORY; if (!descriptors) return ERROR_OUT_OF_MEMORY;
for (uint32_t i = 0; i < pin_count; ++i) { for (uint32_t i = 0; i < pin_count; ++i) {
descriptors[i].controller = device; descriptors[i].controller = device;
descriptors[i].pin = (gpio_pin_t)i; descriptors[i].pin = static_cast<gpio_pin_t>(i);
descriptors[i].owner_type = GPIO_OWNER_NONE; descriptors[i].owner_type = GPIO_OWNER_NONE;
descriptors[i].controller_context = controller_context; descriptors[i].controller_context = this->controller_context;
} }
return ERROR_NONE; return ERROR_NONE;
} }
@ -42,14 +45,14 @@ struct GpioControllerData {
} }
}; };
struct GpioDescriptor* gpio_descriptor_acquire( GpioDescriptor* gpio_descriptor_acquire(
struct Device* controller, Device* controller,
gpio_pin_t pin_number, gpio_pin_t pin_number,
enum GpioOwnerType owner GpioOwnerType owner
) { ) {
check(owner != GPIO_OWNER_NONE); check(owner != GPIO_OWNER_NONE);
auto* data = (struct GpioControllerData*)device_get_driver_data(controller); auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(controller));
mutex_lock(&data->mutex); mutex_lock(&data->mutex);
if (pin_number >= data->pin_count) { if (pin_number >= data->pin_count) {
@ -57,7 +60,7 @@ struct GpioDescriptor* gpio_descriptor_acquire(
return nullptr; return nullptr;
} }
struct GpioDescriptor* desc = &data->descriptors[pin_number]; GpioDescriptor* desc = &data->descriptors[pin_number];
if (desc->owner_type != GPIO_OWNER_NONE) { if (desc->owner_type != GPIO_OWNER_NONE) {
mutex_unlock(&data->mutex); mutex_unlock(&data->mutex);
return nullptr; return nullptr;
@ -69,22 +72,22 @@ struct GpioDescriptor* gpio_descriptor_acquire(
return desc; return desc;
} }
error_t gpio_descriptor_release(struct GpioDescriptor* descriptor) { error_t gpio_descriptor_release(GpioDescriptor* descriptor) {
descriptor->owner_type = GPIO_OWNER_NONE; descriptor->owner_type = GPIO_OWNER_NONE;
return ERROR_NONE; return ERROR_NONE;
} }
error_t gpio_controller_get_pin_count(struct Device* device, uint32_t* count) { error_t gpio_controller_get_pin_count(Device* device, uint32_t* count) {
auto* data = (struct GpioControllerData*)device_get_driver_data(device); auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
*count = data->pin_count; *count = data->pin_count;
return ERROR_NONE; return ERROR_NONE;
} }
error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_count, void* controller_context) { error_t gpio_controller_init_descriptors(Device* device, uint32_t pin_count, void* controller_context) {
auto* data = new(std::nothrow) GpioControllerData(pin_count); auto* data = new(std::nothrow) GpioControllerData(pin_count, controller_context);
if (!data) return ERROR_OUT_OF_MEMORY; if (!data) return ERROR_OUT_OF_MEMORY;
if (data->init_descriptors(device, controller_context) != ERROR_NONE) { if (data->init_descriptors(device) != ERROR_NONE) {
delete data; delete data;
return ERROR_OUT_OF_MEMORY; return ERROR_OUT_OF_MEMORY;
} }
@ -93,77 +96,82 @@ error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_cou
return ERROR_NONE; return ERROR_NONE;
} }
error_t gpio_controller_deinit_descriptors(struct Device* device) { error_t gpio_controller_deinit_descriptors(Device* device) {
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device)); auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
delete data; delete data;
device_set_driver_data(device, nullptr); device_set_driver_data(device, nullptr);
return ERROR_NONE; return ERROR_NONE;
} }
error_t gpio_descriptor_set_level(struct GpioDescriptor* descriptor, bool high) { void* gpio_controller_get_controller_context(Device* device) {
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
return data->controller_context;
}
error_t gpio_descriptor_set_level(GpioDescriptor* descriptor, bool high) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->set_level(descriptor, high); return GPIO_INTERNAL_API(driver)->set_level(descriptor, high);
} }
error_t gpio_descriptor_get_level(struct GpioDescriptor* descriptor, bool* high) { error_t gpio_descriptor_get_level(GpioDescriptor* descriptor, bool* high) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_level(descriptor, high); return GPIO_INTERNAL_API(driver)->get_level(descriptor, high);
} }
error_t gpio_descriptor_set_flags(struct GpioDescriptor* descriptor, gpio_flags_t flags) { error_t gpio_descriptor_set_flags(GpioDescriptor* descriptor, gpio_flags_t flags) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->set_flags(descriptor, flags); return GPIO_INTERNAL_API(driver)->set_flags(descriptor, flags);
} }
error_t gpio_descriptor_get_flags(struct GpioDescriptor* descriptor, gpio_flags_t* flags) { error_t gpio_descriptor_get_flags(GpioDescriptor* descriptor, gpio_flags_t* flags) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_flags(descriptor, flags); return GPIO_INTERNAL_API(driver)->get_flags(descriptor, flags);
} }
error_t gpio_descriptor_get_pin_number(struct GpioDescriptor* descriptor, gpio_pin_t* pin) { error_t gpio_descriptor_get_pin_number(GpioDescriptor* descriptor, gpio_pin_t* pin) {
*pin = descriptor->pin; *pin = descriptor->pin;
return ERROR_NONE; return ERROR_NONE;
} }
error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, void* pin_number) { error_t gpio_descriptor_get_native_pin_number(GpioDescriptor* descriptor, void* pin_number) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_native_pin_number(descriptor, pin_number); return GPIO_INTERNAL_API(driver)->get_native_pin_number(descriptor, pin_number);
} }
error_t gpio_descriptor_add_callback(struct GpioDescriptor* descriptor, void (*callback)(void*), void* arg) { error_t gpio_descriptor_add_callback(GpioDescriptor* descriptor, void (*callback)(void*), void* arg) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
auto* api = GPIO_INTERNAL_API(driver); auto* api = GPIO_INTERNAL_API(driver);
if (!api->add_callback) return ERROR_NOT_SUPPORTED; if (!api->add_callback) return ERROR_NOT_SUPPORTED;
return api->add_callback(descriptor, callback, arg); return api->add_callback(descriptor, callback, arg);
} }
error_t gpio_descriptor_remove_callback(struct GpioDescriptor* descriptor) { error_t gpio_descriptor_remove_callback(GpioDescriptor* descriptor) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
auto* api = GPIO_INTERNAL_API(driver); auto* api = GPIO_INTERNAL_API(driver);
if (!api->remove_callback) return ERROR_NOT_SUPPORTED; if (!api->remove_callback) return ERROR_NOT_SUPPORTED;
return api->remove_callback(descriptor); return api->remove_callback(descriptor);
} }
error_t gpio_descriptor_enable_interrupt(struct GpioDescriptor* descriptor) { error_t gpio_descriptor_enable_interrupt(GpioDescriptor* descriptor) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
auto* api = GPIO_INTERNAL_API(driver); auto* api = GPIO_INTERNAL_API(driver);
if (!api->enable_interrupt) return ERROR_NOT_SUPPORTED; if (!api->enable_interrupt) return ERROR_NOT_SUPPORTED;
return api->enable_interrupt(descriptor); return api->enable_interrupt(descriptor);
} }
error_t gpio_descriptor_disable_interrupt(struct GpioDescriptor* descriptor) { error_t gpio_descriptor_disable_interrupt(GpioDescriptor* descriptor) {
const auto* driver = device_get_driver(descriptor->controller); const auto* driver = device_get_driver(descriptor->controller);
auto* api = GPIO_INTERNAL_API(driver); auto* api = GPIO_INTERNAL_API(driver);
if (!api->disable_interrupt) return ERROR_NOT_SUPPORTED; if (!api->disable_interrupt) return ERROR_NOT_SUPPORTED;
return api->disable_interrupt(descriptor); return api->disable_interrupt(descriptor);
} }
error_t gpio_descriptor_get_owner_type(struct GpioDescriptor* descriptor, GpioOwnerType* owner_type) { error_t gpio_descriptor_get_owner_type(GpioDescriptor* descriptor, GpioOwnerType* owner_type) {
*owner_type = descriptor->owner_type; *owner_type = descriptor->owner_type;
return ERROR_NONE; return ERROR_NONE;
} }
const struct DeviceType GPIO_CONTROLLER_TYPE { const DeviceType GPIO_CONTROLLER_TYPE {
.name = "gpio-controller" .name = "gpio-controller"
}; };