bc12: API and 1st driver implementation.
Add portable-device mode to the Diodes PI3USB9201 USB charging detector. Signed-off-by: Keith Short <keithshort@google.com>
This commit is contained in:
parent
443986159e
commit
e0dd45ba31
|
@ -3,4 +3,5 @@
|
|||
add_subdirectory_ifdef(CONFIG_UDC_DRIVER udc)
|
||||
add_subdirectory_ifdef(CONFIG_UHC_DRIVER uhc)
|
||||
add_subdirectory_ifdef(CONFIG_UVB uvb)
|
||||
add_subdirectory_ifdef(CONFIG_USB_BC12 bc12)
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_DRIVER device)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Copyright (c) 2016 Wind River Systems, Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
source "drivers/usb/bc12/Kconfig"
|
||||
source "drivers/usb/udc/Kconfig"
|
||||
source "drivers/usb/uhc/Kconfig"
|
||||
source "drivers/usb/uvb/Kconfig"
|
||||
|
|
5
drivers/usb/bc12/CMakeLists.txt
Normal file
5
drivers/usb/bc12/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USB_BC12_PI3USB9201 bc12_pi3usb9201.c)
|
17
drivers/usb/bc12/Kconfig
Normal file
17
drivers/usb/bc12/Kconfig
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig USB_BC12
|
||||
bool "USB BC1.2 Drivers"
|
||||
help
|
||||
Enable USB BC1.2 (battery charging detection) drivers.
|
||||
|
||||
if USB_BC12
|
||||
|
||||
module = USB_BC12
|
||||
module-str = usb_bc12
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
source "drivers/usb/bc12/Kconfig.pi3usb9201"
|
||||
|
||||
endif # USB_BC12
|
14
drivers/usb/bc12/Kconfig.pi3usb9201
Normal file
14
drivers/usb/bc12/Kconfig.pi3usb9201
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config USB_BC12_PI3USB9201
|
||||
bool "Diodes PI3USB9201"
|
||||
default y
|
||||
depends on DT_HAS_DIODES_PI3USB9201_ENABLED
|
||||
help
|
||||
This is a Dual-Role USB Charging-Type Detector. It can operate in
|
||||
host or client mode. It supports Battery Charging Specification, rev
|
||||
1.2 (BC1.2) with Standard/Charging/Dedicated downstream port
|
||||
(SDP/CDP/DCP) advertisement when in host mode. In portable device or
|
||||
client mode it starts BC1.2 detection to detect the attached host
|
||||
type. It provides an I2C interface to report detection results.
|
24
drivers/usb/bc12/bc12_handlers.c
Normal file
24
drivers/usb/bc12/bc12_handlers.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/usb/usb_bc12.h>
|
||||
#include <zephyr/syscall_handler.h>
|
||||
|
||||
static inline int z_vrfy_bc12_set_role(const struct device *dev, enum bc12_role role)
|
||||
{
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_BC12(dev, set_role));
|
||||
|
||||
return z_impl_bc12_set_role(dev, role);
|
||||
}
|
||||
|
||||
static inline int z_vrfy_bc12_set_result_cb(const struct device *dev, bc12_callback_t cb,
|
||||
void *user_data)
|
||||
{
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_BC12(dev, set_result_cb));
|
||||
Z_OOPS(Z_SYSCALL_VERIFY_MSG(cb == NULL, "callbacks may not be set from user mode"));
|
||||
|
||||
return z_impl_bc12_set_result_cb(dev, cb, user_data);
|
||||
}
|
432
drivers/usb/bc12/bc12_pi3usb9201.c
Normal file
432
drivers/usb/bc12/bc12_pi3usb9201.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* PI3USB9201 USB BC 1.2 Charger Detector driver. */
|
||||
|
||||
#define DT_DRV_COMPAT diodes_pi3usb9201
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/usb/usb_bc12.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include "bc12_pi3usb9201.h"
|
||||
|
||||
LOG_MODULE_REGISTER(PI3USB9201, CONFIG_USB_BC12_LOG_LEVEL);
|
||||
|
||||
/* Constant configuration data */
|
||||
struct pi3usb9201_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
|
||||
struct gpio_dt_spec intb_gpio;
|
||||
|
||||
enum bc12_type charging_mode;
|
||||
};
|
||||
|
||||
/* Run-time configuration data */
|
||||
struct pi3usb9201_data {
|
||||
const struct device *dev;
|
||||
struct k_work work;
|
||||
enum bc12_type partner_type;
|
||||
struct gpio_callback gpio_cb;
|
||||
|
||||
bc12_callback_t result_cb;
|
||||
void *result_cb_data;
|
||||
};
|
||||
|
||||
enum pi3usb9201_client_sts {
|
||||
CHG_OTHER = 0,
|
||||
CHG_2_4A = 1,
|
||||
CHG_2_0A = 2,
|
||||
CHG_1_0A = 3,
|
||||
CHG_RESERVED = 4,
|
||||
CHG_CDP = 5,
|
||||
CHG_SDP = 6,
|
||||
CHG_DCP = 7,
|
||||
};
|
||||
|
||||
struct bc12_status {
|
||||
enum bc12_type partner_type;
|
||||
int current_limit;
|
||||
};
|
||||
|
||||
/*
|
||||
* The USB Type-C specification limits the maximum amount of current from BC 1.2
|
||||
* suppliers to 1.5A. Technically, proprietary methods are not allowed, but we
|
||||
* will continue to allow those.
|
||||
*/
|
||||
static const struct bc12_status bc12_chg_limits[] = {
|
||||
/* For unknown chargers return Isusp. */
|
||||
[CHG_OTHER] = {BC12_TYPE_PROPRIETARY, BC12_CURR_UA(BC12_CHARGER_MIN_CURR_UA)},
|
||||
[CHG_2_4A] = {BC12_TYPE_PROPRIETARY, BC12_CURR_UA(2400000)},
|
||||
[CHG_2_0A] = {BC12_TYPE_PROPRIETARY, BC12_CURR_UA(2000000)},
|
||||
[CHG_1_0A] = {BC12_TYPE_PROPRIETARY, BC12_CURR_UA(1000000)},
|
||||
[CHG_RESERVED] = {BC12_TYPE_NONE, 0},
|
||||
[CHG_CDP] = {BC12_TYPE_CDP, BC12_CURR_UA(1500000)},
|
||||
/* BC1.2 driver contract specifies to return Isusp for SDP ports. */
|
||||
[CHG_SDP] = {BC12_TYPE_SDP, BC12_CURR_UA(BC12_CHARGER_MIN_CURR_UA)},
|
||||
[CHG_DCP] = {BC12_TYPE_DCP, BC12_CURR_UA(1500000)},
|
||||
};
|
||||
|
||||
static int pi3usb9201_interrupt_enable(const struct device *dev, const bool enable)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
|
||||
/* Clear the interrupt mask bit to enable the interrupt */
|
||||
return i2c_reg_update_byte_dt(&cfg->i2c, PI3USB9201_REG_CTRL_1,
|
||||
PI3USB9201_REG_CTRL_1_INT_MASK,
|
||||
enable ? 0 : PI3USB9201_REG_CTRL_1_INT_MASK);
|
||||
}
|
||||
|
||||
static int pi3usb9201_bc12_detect_ctrl(const struct device *dev, const bool enable)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
|
||||
return i2c_reg_update_byte_dt(&cfg->i2c, PI3USB9201_REG_CTRL_2,
|
||||
PI3USB9201_REG_CTRL_2_START_DET,
|
||||
enable ? PI3USB9201_REG_CTRL_2_START_DET : 0);
|
||||
}
|
||||
|
||||
static int pi3usb9201_bc12_usb_switch(const struct device *dev, bool enable)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
|
||||
/* USB data switch enabled when PI3USB9201_REG_CTRL_2_AUTO_SW is clear */
|
||||
return i2c_reg_update_byte_dt(&cfg->i2c, PI3USB9201_REG_CTRL_2,
|
||||
PI3USB9201_REG_CTRL_2_START_DET,
|
||||
enable ? 0 : PI3USB9201_REG_CTRL_2_AUTO_SW);
|
||||
}
|
||||
|
||||
static int pi3usb9201_set_mode(const struct device *dev, enum pi3usb9201_mode mode)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
|
||||
return i2c_reg_update_byte_dt(&cfg->i2c, PI3USB9201_REG_CTRL_1,
|
||||
PI3USB9201_REG_CTRL_1_MODE_MASK
|
||||
<< PI3USB9201_REG_CTRL_1_MODE_SHIFT,
|
||||
mode << PI3USB9201_REG_CTRL_1_MODE_SHIFT);
|
||||
}
|
||||
|
||||
static int pi3usb9201_get_status(const struct device *dev, uint8_t *const client,
|
||||
uint8_t *const host)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
uint8_t status;
|
||||
int rv;
|
||||
|
||||
rv = i2c_reg_read_byte_dt(&cfg->i2c, PI3USB9201_REG_CLIENT_STS, &status);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (client != NULL) {
|
||||
*client = status;
|
||||
}
|
||||
|
||||
rv = i2c_reg_read_byte_dt(&cfg->i2c, PI3USB9201_REG_HOST_STS, &status);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (host != NULL) {
|
||||
*host = status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pi3usb9201_notify_callback(const struct device *dev,
|
||||
struct bc12_partner_state *const state)
|
||||
{
|
||||
struct pi3usb9201_data *pi3usb9201_data = dev->data;
|
||||
|
||||
if (pi3usb9201_data->result_cb) {
|
||||
pi3usb9201_data->result_cb(dev, state, pi3usb9201_data->result_cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void pi3usb9201_update_charging_partner(const struct device *dev,
|
||||
struct bc12_partner_state *const state)
|
||||
{
|
||||
struct pi3usb9201_data *pi3usb9201_data = dev->data;
|
||||
|
||||
if (state && state->type == pi3usb9201_data->partner_type) {
|
||||
/* No change to the charging partner */
|
||||
return;
|
||||
}
|
||||
|
||||
if (state && state->type != BC12_TYPE_NONE) {
|
||||
/* Now update the current charger type */
|
||||
pi3usb9201_data->partner_type = state->type;
|
||||
pi3usb9201_notify_callback(dev, state);
|
||||
} else {
|
||||
pi3usb9201_data->partner_type = BC12_TYPE_NONE;
|
||||
pi3usb9201_notify_callback(dev, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int pi3usb9201_client_detect_start(const struct device *dev)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/*
|
||||
* Read both status registers to ensure that all interrupt indications
|
||||
* are cleared prior to starting bc1.2 detection.
|
||||
*/
|
||||
pi3usb9201_get_status(dev, NULL, NULL);
|
||||
|
||||
/* Put pi3usb9201 into client mode */
|
||||
rv = pi3usb9201_set_mode(dev, PI3USB9201_CLIENT_MODE);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Have pi3usb9201 start bc1.2 detection */
|
||||
rv = pi3usb9201_bc12_detect_ctrl(dev, true);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Enable interrupt to wake task when detection completes */
|
||||
return pi3usb9201_interrupt_enable(dev, true);
|
||||
}
|
||||
|
||||
static void pi3usb9201_client_detect_finish(const struct device *dev, const int status)
|
||||
{
|
||||
struct bc12_partner_state new_chg;
|
||||
int bit_pos;
|
||||
bool enable_usb_data;
|
||||
|
||||
/* Set charge voltage to 5V */
|
||||
new_chg.voltage_uv = BC12_CHARGER_VOLTAGE_UV;
|
||||
|
||||
/*
|
||||
* Find set bit position. Note that this function is only called if a
|
||||
* bit was set in client_status, so bit_pos won't be negative.
|
||||
*/
|
||||
bit_pos = __builtin_ffs(status) - 1;
|
||||
|
||||
new_chg.current_ua = bc12_chg_limits[bit_pos].current_limit;
|
||||
new_chg.type = bc12_chg_limits[bit_pos].partner_type;
|
||||
|
||||
LOG_DBG("client status = 0x%x, current = %d mA, type = %d",
|
||||
status, new_chg.current_ua, new_chg.type);
|
||||
|
||||
/* bc1.2 is complete and start bit does not auto clear */
|
||||
if (pi3usb9201_bc12_detect_ctrl(dev, false) < 0) {
|
||||
LOG_ERR("failed to clear client detect");
|
||||
}
|
||||
|
||||
/* If DCP mode, disable USB swtich */
|
||||
if (status & BIT(CHG_DCP)) {
|
||||
enable_usb_data = false;
|
||||
} else {
|
||||
enable_usb_data = true;
|
||||
}
|
||||
if (pi3usb9201_bc12_usb_switch(dev, enable_usb_data) < 0) {
|
||||
LOG_ERR("failed to set USB data mode");
|
||||
}
|
||||
|
||||
/* Inform charge manager of new supplier type and current limit */
|
||||
pi3usb9201_update_charging_partner(dev, &new_chg);
|
||||
}
|
||||
|
||||
static int pi3usb9201_disconnect(const struct device *dev)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/* Ensure USB switch auto-on is enabled */
|
||||
rv = pi3usb9201_bc12_usb_switch(dev, true);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Put pi3usb9201 into its power down mode */
|
||||
rv = pi3usb9201_set_mode(dev, PI3USB9201_POWER_DOWN);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* The start bc1.2 bit does not auto clear */
|
||||
rv = pi3usb9201_bc12_detect_ctrl(dev, false);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Mask interrupts until next bc1.2 detection event */
|
||||
rv = pi3usb9201_interrupt_enable(dev, false);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let the application know there's no more charge available for the
|
||||
* supplier type that was most recently detected.
|
||||
*/
|
||||
pi3usb9201_update_charging_partner(dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pi3usb9201_set_portable_device(const struct device *dev)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/* Disable interrupts during mode change */
|
||||
rv = pi3usb9201_interrupt_enable(dev, false);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (pi3usb9201_client_detect_start(dev) < 0) {
|
||||
struct bc12_partner_state new_result;
|
||||
|
||||
/*
|
||||
* VBUS is present, but starting bc1.2 detection failed
|
||||
* for some reason. Set the partner type to unknown limit
|
||||
* current to the minimum allowed for a suspended USB device.
|
||||
*/
|
||||
new_result.voltage_uv = BC12_CHARGER_VOLTAGE_UV;
|
||||
new_result.current_ua = BC12_CHARGER_MIN_CURR_UA;
|
||||
new_result.type = BC12_TYPE_UNKNOWN;
|
||||
/* Save supplier type and notify callbacks */
|
||||
pi3usb9201_update_charging_partner(dev, &new_result);
|
||||
LOG_ERR("bc1.2 detection failed, using defaults");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pi3usb9201_isr_work(struct k_work *item)
|
||||
{
|
||||
struct pi3usb9201_data *pi3usb9201_data = CONTAINER_OF(item, struct pi3usb9201_data, work);
|
||||
const struct device *dev = pi3usb9201_data->dev;
|
||||
uint8_t client;
|
||||
uint8_t host;
|
||||
int rv;
|
||||
|
||||
rv = pi3usb9201_get_status(dev, &client, &host);
|
||||
if (rv < 0) {
|
||||
LOG_ERR("Failed to get host/client status");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client != 0) {
|
||||
/*
|
||||
* Any bit set in client status register indicates that
|
||||
* BC1.2 detection has completed.
|
||||
*/
|
||||
pi3usb9201_client_detect_finish(dev, client);
|
||||
}
|
||||
}
|
||||
|
||||
static void pi3usb9201_gpio_callback(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
struct pi3usb9201_data *pi3usb9201_data = CONTAINER_OF(cb, struct pi3usb9201_data, gpio_cb);
|
||||
|
||||
k_work_submit(&pi3usb9201_data->work);
|
||||
}
|
||||
|
||||
static int pi3usb9201_set_role(const struct device *dev, const enum bc12_role role)
|
||||
{
|
||||
switch (role) {
|
||||
case BC12_DISCONNECTED:
|
||||
return pi3usb9201_disconnect(dev);
|
||||
case BC12_PORTABLE_DEVICE:
|
||||
return pi3usb9201_set_portable_device(dev);
|
||||
|
||||
default:
|
||||
LOG_ERR("unsupported BC12 role: %d", role);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pi3usb9201_set_result_cb(const struct device *dev, bc12_callback_t cb, void *const user_data)
|
||||
{
|
||||
struct pi3usb9201_data *pi3usb9201_data = dev->data;
|
||||
|
||||
pi3usb9201_data->result_cb = cb;
|
||||
pi3usb9201_data->result_cb_data = user_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bc12_driver_api pi3usb9201_driver_api = {
|
||||
.set_role = pi3usb9201_set_role,
|
||||
.set_result_cb = pi3usb9201_set_result_cb,
|
||||
};
|
||||
|
||||
static int pi3usb9201_init(const struct device *dev)
|
||||
{
|
||||
const struct pi3usb9201_config *cfg = dev->config;
|
||||
struct pi3usb9201_data *pi3usb9201_data = dev->data;
|
||||
int rv;
|
||||
|
||||
if (!i2c_is_ready_dt(&cfg->i2c)) {
|
||||
LOG_ERR("Bus device is not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&cfg->intb_gpio)) {
|
||||
LOG_ERR("intb_gpio device is not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pi3usb9201_data->dev = dev;
|
||||
|
||||
/*
|
||||
* Set most recent bc1.2 detection type result to
|
||||
* BC12_TYPE_NONE for the port.
|
||||
*/
|
||||
pi3usb9201_data->partner_type = BC12_TYPE_NONE;
|
||||
|
||||
rv = gpio_pin_configure_dt(&cfg->intb_gpio, GPIO_INPUT);
|
||||
if (rv < 0) {
|
||||
LOG_DBG("Failed to set gpio callback.");
|
||||
return rv;
|
||||
}
|
||||
|
||||
gpio_init_callback(&pi3usb9201_data->gpio_cb, pi3usb9201_gpio_callback,
|
||||
BIT(cfg->intb_gpio.pin));
|
||||
k_work_init(&pi3usb9201_data->work, pi3usb9201_isr_work);
|
||||
|
||||
rv = gpio_add_callback(cfg->intb_gpio.port, &pi3usb9201_data->gpio_cb);
|
||||
if (rv < 0) {
|
||||
LOG_DBG("Failed to set gpio callback.");
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = gpio_pin_interrupt_configure_dt(&cfg->intb_gpio, GPIO_INT_EDGE_FALLING);
|
||||
if (rv < 0) {
|
||||
LOG_DBG("Failed to configure gpio interrupt.");
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* The is no specific initialization required for the pi3usb9201 other
|
||||
* than disabling the interrupt.
|
||||
*/
|
||||
return pi3usb9201_interrupt_enable(dev, false);
|
||||
}
|
||||
|
||||
#define PI2USB9201_DEFINE(inst) \
|
||||
static struct pi3usb9201_data pi3usb9201_data_##inst; \
|
||||
\
|
||||
static const struct pi3usb9201_config pi3usb9201_config_##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.intb_gpio = GPIO_DT_SPEC_INST_GET(inst, intb_gpios), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, pi3usb9201_init, NULL, &pi3usb9201_data_##inst, \
|
||||
&pi3usb9201_config_##inst, POST_KERNEL, \
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, &pi3usb9201_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(PI2USB9201_DEFINE)
|
45
drivers/usb/bc12/bc12_pi3usb9201.h
Normal file
45
drivers/usb/bc12/bc12_pi3usb9201.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* PI3USB9201 USB BC 1.2 Charger Detector driver definitions */
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USB_BC12_PI3USB9201_H
|
||||
#define ZEPHYR_INCLUDE_USB_BC12_PI3USB9201_H
|
||||
|
||||
#define PI3USB9201_REG_CTRL_1 0x0
|
||||
#define PI3USB9201_REG_CTRL_2 0x1
|
||||
#define PI3USB9201_REG_CLIENT_STS 0x2
|
||||
#define PI3USB9201_REG_HOST_STS 0x3
|
||||
|
||||
/* Flags */
|
||||
#define PI3USB9201_ALWAYS_POWERED BIT(0)
|
||||
|
||||
/* Control_1 register bit definitions */
|
||||
#define PI3USB9201_REG_CTRL_1_INT_MASK BIT(0)
|
||||
#define PI3USB9201_REG_CTRL_1_MODE_SHIFT 1
|
||||
#define PI3USB9201_REG_CTRL_1_MODE_MASK BIT_MASK(3)
|
||||
|
||||
/* Control_2 register bit definitions */
|
||||
#define PI3USB9201_REG_CTRL_2_AUTO_SW BIT(1)
|
||||
#define PI3USB9201_REG_CTRL_2_START_DET BIT(3)
|
||||
|
||||
/* Host status register bit definitions */
|
||||
#define PI3USB9201_REG_HOST_STS_BC12_DET BIT(0)
|
||||
#define PI3USB9201_REG_HOST_STS_DEV_PLUG BIT(1)
|
||||
#define PI3USB9201_REG_HOST_STS_DEV_UNPLUG BIT(2)
|
||||
|
||||
enum pi3usb9201_mode {
|
||||
PI3USB9201_POWER_DOWN,
|
||||
PI3USB9201_SDP_HOST_MODE,
|
||||
PI3USB9201_DCP_HOST_MODE,
|
||||
PI3USB9201_CDP_HOST_MODE,
|
||||
PI3USB9201_CLIENT_MODE,
|
||||
PI3USB9201_RESERVED_1,
|
||||
PI3USB9201_RESERVED_2,
|
||||
PI3USB9201_USB_PATH_ON,
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USB_BC12_PI3USB9201_H */
|
15
dts/bindings/usb/diodes,pi3usb9201.yaml
Normal file
15
dts/bindings/usb/diodes,pi3usb9201.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Diodes PI3USB9201, Dual-Role USB Charging-Type Detector
|
||||
|
||||
compatible: "diodes,pi3usb9201"
|
||||
|
||||
include: [i2c-device.yaml, usb-bc12.yaml]
|
||||
|
||||
properties:
|
||||
intb-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
GPIO input connected to the active low interrupt INTB pin on the PI3USB9201.
|
6
dts/bindings/usb/usb-bc12.yaml
Normal file
6
dts/bindings/usb/usb-bc12.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Common fields for USB BC1.2 devices
|
||||
|
||||
include: base.yaml
|
173
include/zephyr/drivers/usb/usb_bc12.h
Normal file
173
include/zephyr/drivers/usb/usb_bc12.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public APIs for the USB BC1.2 battery charging detect drivers.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_USB_USB_BC12_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_USB_USB_BC12_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief BC1.2 driver APIs
|
||||
* @defgroup b12_interface BC1.2 driver APIs
|
||||
* @ingroup io_interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* FIXME - make these Kconfig options */
|
||||
|
||||
/**
|
||||
* @name BC1.2 constants
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** BC1.2 USB charger voltage. */
|
||||
#define BC12_CHARGER_VOLTAGE_UV 5000 * 1000
|
||||
/**
|
||||
* BC1.2 USB charger minimum current. Set to match the Isusp of 2.5 mA parameter.
|
||||
* This is returned by the driver when either BC1.2 detection fails, or the
|
||||
* attached partner is a SDP (standard downstream port).
|
||||
*
|
||||
* The application may increase the current draw after determing the USB device
|
||||
* state of suspended/unconfigured/configured.
|
||||
* Suspended: 2.5 mA
|
||||
* Unconfigured: 100 mA
|
||||
* Configured: 500 mA (USB 2.0)
|
||||
*/
|
||||
#define BC12_CHARGER_MIN_CURR_UA 2500
|
||||
/** BC1.2 USB charger maximum current. */
|
||||
#define BC12_CHARGER_MAX_CURR_UA 1500 * 1000
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @cond INTERNAL_HIDDEN
|
||||
* @brief Helper macro for setting a BC1.2 current limit
|
||||
*
|
||||
* @param val Current limit value, in uA.
|
||||
* @return A valid BC1.2 current limit, in uA, clamped between the BC1.2 minimum
|
||||
* and maximum values.
|
||||
*/
|
||||
#define BC12_CURR_UA(val) CLAMP(val, BC12_CHARGER_MIN_CURR_UA, BC12_CHARGER_MAX_CURR_UA)
|
||||
/** @endcond */
|
||||
|
||||
/** @brief BC1.2 device role. */
|
||||
enum bc12_role {
|
||||
BC12_DISCONNECTED,
|
||||
BC12_PORTABLE_DEVICE,
|
||||
};
|
||||
|
||||
/** @brief BC1.2 charging partner type. */
|
||||
enum bc12_type {
|
||||
/** No partner connected. */
|
||||
BC12_TYPE_NONE,
|
||||
/** Standard Downstream Port */
|
||||
BC12_TYPE_SDP,
|
||||
/** Dedicated Charging Port */
|
||||
BC12_TYPE_DCP,
|
||||
/** Charging Downstream Port */
|
||||
BC12_TYPE_CDP,
|
||||
/** Proprietary charging port */
|
||||
BC12_TYPE_PROPRIETARY,
|
||||
/** Unknown charging port, BC1.2 detection failed. */
|
||||
BC12_TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BC1.2 detected partner state.
|
||||
*
|
||||
* @param type Charging partner type.
|
||||
* @param current_ma Current, in uA, that the charging partner provides.
|
||||
* @param voltage_mv Voltage, in uV, that the charging partner provides.
|
||||
*/
|
||||
struct bc12_partner_state {
|
||||
enum bc12_type type;
|
||||
int current_ua;
|
||||
int voltage_uv;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BC1.2 callback for charger configuration
|
||||
*
|
||||
* @param dev BC1.2 device which is notifying of the new charger state.
|
||||
* @param state Current state of the BC1.2 client, including BC1.2 type
|
||||
* detected, voltage, and current limits.
|
||||
* If NULL, then the partner charger is disconnected or the BC1.2 device is
|
||||
* operating in host mode.
|
||||
* @param user_data Requester supplied data which is passed along to the callback.
|
||||
*/
|
||||
typedef void (*bc12_callback_t)(const struct device *dev, struct bc12_partner_state *state,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* @cond INTERNAL_HIDDEN
|
||||
*
|
||||
* These are for internal use only, so skip these in public documentation.
|
||||
*/
|
||||
__subsystem struct bc12_driver_api {
|
||||
int (*set_role)(const struct device *dev, enum bc12_role role);
|
||||
int (*set_result_cb)(const struct device *dev, bc12_callback_t cb, void *user_data);
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Set the BC1.2 role.
|
||||
*
|
||||
* @param dev Pointer to the device structure for the BC1.2 driver instance.
|
||||
* @param role New role for the BC1.2 device.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO general input/output error.
|
||||
*/
|
||||
__syscall int bc12_set_role(const struct device *dev, enum bc12_role role);
|
||||
|
||||
static inline int z_impl_bc12_set_role(const struct device *dev, enum bc12_role role)
|
||||
{
|
||||
const struct bc12_driver_api *api = (const struct bc12_driver_api *)dev->api;
|
||||
|
||||
return api->set_role(dev, role);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback for BC1.2 results.
|
||||
*
|
||||
* @param dev Pointer to the device structure for the BC1.2 driver instance.
|
||||
* @param cb Function pointer for the result callback.
|
||||
* @param user_data Requester supplied data which is passed along to the callback.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EIO general input/output error.
|
||||
*/
|
||||
__syscall int bc12_set_result_cb(const struct device *dev, bc12_callback_t cb, void *user_data);
|
||||
|
||||
static inline int z_impl_bc12_set_result_cb(const struct device *dev, bc12_callback_t cb,
|
||||
void *user_data)
|
||||
{
|
||||
const struct bc12_driver_api *api = (const struct bc12_driver_api *)dev->api;
|
||||
|
||||
return api->set_result_cb(dev, cb, user_data);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <syscalls/usb_bc12.h>
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_USB_USB_BC12_H_ */
|
Loading…
Reference in a new issue