bc12: Add charging mode role information to callback

Update the result callback routine to also provide information about the
charging mode role. This permits tests to validate plug/unplug events
detected by the BC1.2 device operating in charging mode.

Signed-off-by: Keith Short <keithshort@google.com>
This commit is contained in:
Keith Short 2022-12-28 11:00:15 -07:00 committed by Carles Cufí
parent 824d021377
commit 86cb55943d
3 changed files with 101 additions and 41 deletions

View file

@ -30,7 +30,7 @@ struct pi3usb9201_config {
struct pi3usb9201_data { struct pi3usb9201_data {
const struct device *dev; const struct device *dev;
struct k_work work; struct k_work work;
enum bc12_type partner_type; struct bc12_partner_state partner_state;
struct gpio_callback gpio_cb; struct gpio_callback gpio_cb;
bc12_callback_t result_cb; bc12_callback_t result_cb;
@ -148,22 +148,56 @@ static void pi3usb9201_notify_callback(const struct device *dev,
} }
} }
static bool pi3usb9201_partner_has_changed(const struct device *dev,
struct bc12_partner_state *state)
{
struct pi3usb9201_data *pi3usb9201_data = dev->data;
/* Always notify when clearing out partner state */
if (!state) {
return true;
}
if (state->bc12_role != pi3usb9201_data->partner_state.bc12_role) {
return true;
}
if (state->bc12_role == BC12_PORTABLE_DEVICE &&
pi3usb9201_data->partner_state.type != state->type) {
return true;
}
if (state->bc12_role == BC12_CHARGING_PORT &&
pi3usb9201_data->partner_state.pd_partner_connected != state->pd_partner_connected) {
return true;
}
return false;
}
/**
* @brief Notify the application about changes to the BC1.2 partner.
*
* @param dev BC1.2 device instance
* @param state New partner state information. Set to NULL to indicate no
* partner is connected.
*/
static void pi3usb9201_update_charging_partner(const struct device *dev, static void pi3usb9201_update_charging_partner(const struct device *dev,
struct bc12_partner_state *const state) struct bc12_partner_state *const state)
{ {
struct pi3usb9201_data *pi3usb9201_data = dev->data; struct pi3usb9201_data *pi3usb9201_data = dev->data;
if (state && state->type == pi3usb9201_data->partner_type) { if (!pi3usb9201_partner_has_changed(dev, state)) {
/* No change to the charging partner */ /* No change to the partner */
return; return;
} }
if (state && state->type != BC12_TYPE_NONE) { if (state) {
/* Now update the current charger type */ /* Now update callback with the new partner type. */
pi3usb9201_data->partner_type = state->type; pi3usb9201_data->partner_state = *state;
pi3usb9201_notify_callback(dev, state); pi3usb9201_notify_callback(dev, state);
} else { } else {
pi3usb9201_data->partner_type = BC12_TYPE_NONE; pi3usb9201_data->partner_state.bc12_role = BC12_DISCONNECTED;
pi3usb9201_notify_callback(dev, NULL); pi3usb9201_notify_callback(dev, NULL);
} }
} }
@ -196,12 +230,14 @@ static int pi3usb9201_client_detect_start(const struct device *dev)
static void pi3usb9201_client_detect_finish(const struct device *dev, const int status) static void pi3usb9201_client_detect_finish(const struct device *dev, const int status)
{ {
struct bc12_partner_state new_chg; struct bc12_partner_state new_partner_state;
int bit_pos; int bit_pos;
bool enable_usb_data; bool enable_usb_data;
new_partner_state.bc12_role = BC12_PORTABLE_DEVICE;
/* Set charge voltage to 5V */ /* Set charge voltage to 5V */
new_chg.voltage_uv = BC12_CHARGER_VOLTAGE_UV; new_partner_state.voltage_uv = BC12_CHARGER_VOLTAGE_UV;
/* /*
* Find set bit position. Note that this function is only called if a * Find set bit position. Note that this function is only called if a
@ -209,11 +245,11 @@ static void pi3usb9201_client_detect_finish(const struct device *dev, const int
*/ */
bit_pos = __builtin_ffs(status) - 1; bit_pos = __builtin_ffs(status) - 1;
new_chg.current_ua = bc12_chg_limits[bit_pos].current_limit; new_partner_state.current_ua = bc12_chg_limits[bit_pos].current_limit;
new_chg.type = bc12_chg_limits[bit_pos].partner_type; new_partner_state.type = bc12_chg_limits[bit_pos].partner_type;
LOG_DBG("client status = 0x%x, current = %d mA, type = %d", LOG_DBG("client status = 0x%x, current = %d mA, type = %d",
status, new_chg.current_ua, new_chg.type); status, new_partner_state.current_ua, new_partner_state.type);
/* bc1.2 is complete and start bit does not auto clear */ /* bc1.2 is complete and start bit does not auto clear */
if (pi3usb9201_bc12_detect_ctrl(dev, false) < 0) { if (pi3usb9201_bc12_detect_ctrl(dev, false) < 0) {
@ -231,7 +267,7 @@ static void pi3usb9201_client_detect_finish(const struct device *dev, const int
} }
/* Inform charge manager of new supplier type and current limit */ /* Inform charge manager of new supplier type and current limit */
pi3usb9201_update_charging_partner(dev, &new_chg); pi3usb9201_update_charging_partner(dev, &new_partner_state);
} }
static int pi3usb9201_disconnect(const struct device *dev) static int pi3usb9201_disconnect(const struct device *dev)
@ -282,18 +318,19 @@ static int pi3usb9201_set_portable_device(const struct device *dev)
} }
if (pi3usb9201_client_detect_start(dev) < 0) { if (pi3usb9201_client_detect_start(dev) < 0) {
struct bc12_partner_state new_result; struct bc12_partner_state partner_state;
/* /*
* VBUS is present, but starting bc1.2 detection failed * VBUS is present, but starting bc1.2 detection failed
* for some reason. Set the partner type to unknown limit * for some reason. Set the partner type to unknown limit
* current to the minimum allowed for a suspended USB device. * current to the minimum allowed for a suspended USB device.
*/ */
new_result.voltage_uv = BC12_CHARGER_VOLTAGE_UV; partner_state.bc12_role = BC12_PORTABLE_DEVICE;
new_result.current_ua = BC12_CHARGER_MIN_CURR_UA; partner_state.voltage_uv = BC12_CHARGER_VOLTAGE_UV;
new_result.type = BC12_TYPE_UNKNOWN; partner_state.current_ua = BC12_CHARGER_MIN_CURR_UA;
partner_state.type = BC12_TYPE_UNKNOWN;
/* Save supplier type and notify callbacks */ /* Save supplier type and notify callbacks */
pi3usb9201_update_charging_partner(dev, &new_result); pi3usb9201_update_charging_partner(dev, &partner_state);
LOG_ERR("bc1.2 detection failed, using defaults"); LOG_ERR("bc1.2 detection failed, using defaults");
return -EIO; return -EIO;
@ -384,9 +421,9 @@ static int pi3usb9201_init(const struct device *dev)
/* /*
* Set most recent bc1.2 detection type result to * Set most recent bc1.2 detection type result to
* BC12_TYPE_NONE for the port. * BC12_DISCONNECTED for the port.
*/ */
pi3usb9201_data->partner_type = BC12_TYPE_NONE; pi3usb9201_data->partner_state.bc12_role = BC12_DISCONNECTED;
rv = gpio_pin_configure_dt(&cfg->intb_gpio, GPIO_INPUT); rv = gpio_pin_configure_dt(&cfg->intb_gpio, GPIO_INPUT);
if (rv < 0) { if (rv < 0) {
@ -417,16 +454,16 @@ static int pi3usb9201_init(const struct device *dev)
return pi3usb9201_interrupt_enable(dev, false); return pi3usb9201_interrupt_enable(dev, false);
} }
#define PI2USB9201_DEFINE(inst) \ #define PI2USB9201_DEFINE(inst) \
static struct pi3usb9201_data pi3usb9201_data_##inst; \ static struct pi3usb9201_data pi3usb9201_data_##inst; \
\ \
static const struct pi3usb9201_config pi3usb9201_config_##inst = { \ static const struct pi3usb9201_config pi3usb9201_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \ .i2c = I2C_DT_SPEC_INST_GET(inst), \
.intb_gpio = GPIO_DT_SPEC_INST_GET(inst, intb_gpios), \ .intb_gpio = GPIO_DT_SPEC_INST_GET(inst, intb_gpios), \
}; \ }; \
\ \
DEVICE_DT_INST_DEFINE(inst, pi3usb9201_init, NULL, &pi3usb9201_data_##inst, \ DEVICE_DT_INST_DEFINE(inst, pi3usb9201_init, NULL, &pi3usb9201_data_##inst, \
&pi3usb9201_config_##inst, POST_KERNEL, \ &pi3usb9201_config_##inst, POST_KERNEL, \
CONFIG_APPLICATION_INIT_PRIORITY, &pi3usb9201_driver_api); CONFIG_APPLICATION_INIT_PRIORITY, &pi3usb9201_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PI2USB9201_DEFINE) DT_INST_FOREACH_STATUS_OKAY(PI2USB9201_DEFINE)

View file

@ -65,6 +65,7 @@ extern "C" {
enum bc12_role { enum bc12_role {
BC12_DISCONNECTED, BC12_DISCONNECTED,
BC12_PORTABLE_DEVICE, BC12_PORTABLE_DEVICE,
BC12_CHARGING_PORT,
}; };
/** @brief BC1.2 charging partner type. */ /** @brief BC1.2 charging partner type. */
@ -81,19 +82,34 @@ enum bc12_type {
BC12_TYPE_PROPRIETARY, BC12_TYPE_PROPRIETARY,
/** Unknown charging port, BC1.2 detection failed. */ /** Unknown charging port, BC1.2 detection failed. */
BC12_TYPE_UNKNOWN, BC12_TYPE_UNKNOWN,
/** Count of valid BC12 types. */
BC12_TYPE_COUNT,
}; };
/** /**
* @brief BC1.2 detected partner state. * @brief BC1.2 detected partner state.
* *
* @param type Charging partner type. * @param bc12_role Current role of the BC1.2 device.
* @param current_ma Current, in uA, that the charging partner provides. * @param type Charging partner type. Valid when bc12_role is BC12_PORTABLE_DEVICE.
* @param voltage_mv Voltage, in uV, that the charging partner provides. * @param current_ma Current, in uA, that the charging partner provides. Valid when bc12_role is
* BC12_PORTABLE_DEVICE.
* @param voltage_mv Voltage, in uV, that the charging partner provides. Valid when bc12_role is
* BC12_PORTABLE_DEVICE.
* @param pd_partner_connected True if a PD partner is currently connected. Valid when bc12_role is
* BC12_CHARGING_PORT.
*/ */
struct bc12_partner_state { struct bc12_partner_state {
enum bc12_type type; enum bc12_role bc12_role;
int current_ua; union {
int voltage_uv; struct {
enum bc12_type type;
int current_ua;
int voltage_uv;
};
struct {
bool pd_partner_connected;
};
};
}; };
/** /**

View file

@ -38,8 +38,13 @@ static void bc12_test_result_cb(const struct device *dev, struct bc12_partner_st
fixture->callback_count++; fixture->callback_count++;
if (state) { if (state) {
LOG_INF("callback: type %d, voltage %d, current %d", state->type, state->voltage_uv, if (state->bc12_role == BC12_PORTABLE_DEVICE) {
state->current_ua); LOG_INF("charging partner: type %d, voltage %d, current %d", state->type,
state->voltage_uv, state->current_ua);
} else if (state->bc12_role == BC12_CHARGING_PORT) {
LOG_INF("portable device partner: connected %d",
state->pd_partner_connected);
}
fixture->partner_state = *state; fixture->partner_state = *state;
} else { } else {
LOG_INF("callback: partner disconnect"); LOG_INF("callback: partner disconnect");
@ -80,6 +85,7 @@ ZTEST_USER_F(bc12_pd_mode, test_bc12_sdp_charging_partner)
* for SDP ports or when BC1.2 detection fails. * for SDP ports or when BC1.2 detection fails.
*/ */
zassert_equal(fixture->callback_count, 1); zassert_equal(fixture->callback_count, 1);
zassert_equal(fixture->partner_state.bc12_role, BC12_PORTABLE_DEVICE);
zassert_equal(fixture->partner_state.type, BC12_TYPE_SDP); zassert_equal(fixture->partner_state.type, BC12_TYPE_SDP);
zassert_equal(fixture->partner_state.current_ua, 2500); zassert_equal(fixture->partner_state.current_ua, 2500);
zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000); zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000);
@ -109,6 +115,7 @@ ZTEST_USER_F(bc12_pd_mode, test_bc12_cdp_charging_partner)
k_sleep(K_MSEC(100)); k_sleep(K_MSEC(100));
zassert_equal(fixture->callback_count, 1); zassert_equal(fixture->callback_count, 1);
zassert_equal(fixture->partner_state.bc12_role, BC12_PORTABLE_DEVICE);
zassert_equal(fixture->partner_state.type, BC12_TYPE_CDP); zassert_equal(fixture->partner_state.type, BC12_TYPE_CDP);
zassert_equal(fixture->partner_state.current_ua, 1500 * 1000); zassert_equal(fixture->partner_state.current_ua, 1500 * 1000);
zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000); zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000);
@ -139,6 +146,7 @@ ZTEST_USER_F(bc12_pd_mode, test_bc12_sdp_to_dcp_charging_partner)
k_sleep(K_MSEC(100)); k_sleep(K_MSEC(100));
zassert_equal(fixture->callback_count, 1); zassert_equal(fixture->callback_count, 1);
zassert_equal(fixture->partner_state.bc12_role, BC12_PORTABLE_DEVICE);
zassert_equal(fixture->partner_state.type, BC12_TYPE_SDP); zassert_equal(fixture->partner_state.type, BC12_TYPE_SDP);
zassert_equal(fixture->partner_state.current_ua, 2500); zassert_equal(fixture->partner_state.current_ua, 2500);
zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000); zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000);
@ -154,6 +162,7 @@ ZTEST_USER_F(bc12_pd_mode, test_bc12_sdp_to_dcp_charging_partner)
/* The BC1.2 driver should invoke the callback once to report the new state. */ /* The BC1.2 driver should invoke the callback once to report the new state. */
zassert_equal(fixture->callback_count, 1); zassert_equal(fixture->callback_count, 1);
zassert_equal(fixture->partner_state.bc12_role, BC12_PORTABLE_DEVICE);
zassert_equal(fixture->partner_state.type, BC12_TYPE_DCP); zassert_equal(fixture->partner_state.type, BC12_TYPE_DCP);
zassert_equal(fixture->partner_state.current_ua, 1500 * 1000); zassert_equal(fixture->partner_state.current_ua, 1500 * 1000);
zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000); zassert_equal(fixture->partner_state.voltage_uv, 5000 * 1000);
@ -165,9 +174,7 @@ static void bc12_before(void *data)
fixture->callback_count = 0; fixture->callback_count = 0;
fixture->disconnect_detected = 0; fixture->disconnect_detected = 0;
fixture->partner_state.type = BC12_TYPE_NONE; memset(&fixture->partner_state, 0, sizeof(struct bc12_partner_state));
fixture->partner_state.current_ua = 0;
fixture->partner_state.voltage_uv = 0;
bc12_set_result_cb(fixture->bc12_dev, &bc12_test_result_cb, fixture); bc12_set_result_cb(fixture->bc12_dev, &bc12_test_result_cb, fixture);
} }