drivers: tcpc: Add TCPC driver for RT1715

Add support for RT1715.

Signed-off-by: Jianxiong Gu <jianxiong.gu@outlook.com>
This commit is contained in:
Jianxiong Gu 2024-12-14 20:21:03 +08:00 committed by Benjamin Cabé
parent 90530cba96
commit 391008b097
6 changed files with 918 additions and 0 deletions

View file

@ -7,3 +7,4 @@ zephyr_library_sources_ifdef(CONFIG_USBC_TCPC_STM32 ucpd_stm32.c)
zephyr_library_sources_ifdef(CONFIG_USBC_TCPC_NUMAKER ucpd_numaker.c)
zephyr_library_sources_ifdef(CONFIG_USBC_TCPC_TCPCI tcpci.c)
zephyr_library_sources_ifdef(CONFIG_USBC_TCPC_PS8XXX ps8xxx.c)
zephyr_library_sources_ifdef(CONFIG_USBC_TCPC_RT1715 rt1715.c)

View file

@ -29,6 +29,7 @@ source "drivers/usb_c/tcpc/Kconfig.tcpc_stm32"
source "drivers/usb_c/tcpc/Kconfig.tcpc_numaker"
source "drivers/usb_c/tcpc/Kconfig.tcpc_tcpci"
source "drivers/usb_c/tcpc/Kconfig.tcpc_ps8xxx"
source "drivers/usb_c/tcpc/Kconfig.tcpc_rt1715"
module = USBC
module-str = usbc

View file

@ -0,0 +1,28 @@
# USB-C RT1715 TCPC configuration options
# Copyright (c) 2024 Jianxiong Gu <jianxiong.gu@outlook.com>
# SPDX-License-Identifier: Apache-2.0
config USBC_TCPC_RT1715
bool "USB-C TCPC device controller driver"
select USBC_TCPC_TCPCI
default y
depends on DT_HAS_RICHTEK_RT1715_ENABLED
help
Enable USB-C TCPC support for the Richtek RT1715
if USBC_TCPC_RT1715
config USBC_TCPC_RT1715_INIT_DELAY
int "RT1715 init delay"
default 5
help
Delay between each try of the TCPC initialization
config USBC_TCPC_RT1715_INIT_RETRIES
int "RT1715 init retries"
default 10
help
Number of initialization tries that will be performed
endif

726
drivers/usb_c/tcpc/rt1715.c Normal file
View file

@ -0,0 +1,726 @@
/*
* Copyright (c) 2024 Jianxiong Gu <jianxiong.gu@outlook.com>
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/usb_c/tcpci_priv.h>
#include <zephyr/usb_c/usbc.h>
#include <zephyr/usb_c/tcpci.h>
#include <zephyr/shell/shell.h>
#include "rt1715.h"
#define DT_DRV_COMPAT richtek_rt1715
LOG_MODULE_REGISTER(rt1715, CONFIG_USBC_LOG_LEVEL);
/** Data structure for device instances */
struct rt1715_data {
/** Device structure used to retrieve it in k_work functions */
const struct device *const dev;
/** Delayable work item for chip initialization */
struct k_work_delayable init_dwork;
/** Initialization retries counter */
int init_retries;
/** Boolean value if chip was successfully initialized */
bool initialized;
/** Callback for alert GPIO */
struct gpio_callback alert_cb;
/** Work item for alert handling out of interrupt context */
struct k_work alert_work;
/** Boolean value if there is a message pending */
bool msg_pending;
/** One-slot Rx FIFO */
struct pd_msg rx_msg;
/** Alert handler set by USB-C stack */
tcpc_alert_handler_cb_t alert_handler;
/** Alert handler data set by USB-C stack */
void *alert_handler_data;
/** VCONN discharge callback set by USB-C stack */
tcpc_vconn_discharge_cb_t vconn_discharge_cb;
/** VCONN discharge callback data set by USB-C stack */
tcpc_vconn_control_cb_t vconn_cb;
/** Polarity of CC lines for PD and VCONN */
enum tc_cc_polarity cc_polarity;
/** Boolean value if there was a change on the CC lines since last check */
bool cc_changed;
/** State of CC1 line */
enum tc_cc_voltage_state cc1;
/** State of CC2 line */
enum tc_cc_voltage_state cc2;
/* Flag to receive or ignore SOP Prime messages */
bool rx_sop_prime_enable;
};
/** Configuration structure for device instances */
struct rt1715_cfg {
/** I2C bus and address used for communication */
const struct i2c_dt_spec bus;
/** GPIO specification for alert pin */
const struct gpio_dt_spec alert_gpio;
/** GPIO specification for VCONN power control pin */
const struct gpio_dt_spec vconn_ctrl_gpio;
/** GPIO specification for VCONN discharge control pin */
const struct gpio_dt_spec vconn_disc_gpio;
/** Maximum number of packet retransmissions done by TCPC */
const uint8_t transmit_retries;
};
static int tcpci_init_alert_mask(const struct device *dev)
{
const struct rt1715_cfg *cfg = dev->config;
uint16_t mask = TCPC_REG_ALERT_TX_COMPLETE | TCPC_REG_ALERT_RX_STATUS |
TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS |
TCPC_REG_ALERT_POWER_STATUS | TCPC_REG_ALERT_FAULT |
TCPC_REG_ALERT_RX_BUF_OVF;
return tcpci_tcpm_mask_status_register(&cfg->bus, TCPC_ALERT_STATUS, mask);
}
static int rt1715_tcpc_init(const struct device *dev)
{
struct rt1715_data *data = dev->data;
if (!data->initialized) {
if (data->init_retries > CONFIG_USBC_TCPC_RT1715_INIT_RETRIES) {
LOG_ERR("TCPC was not initialized correctly");
return -EIO;
}
return -EAGAIN;
}
data->rx_sop_prime_enable = false;
data->msg_pending = false;
memset(&data->rx_msg, 0x00, sizeof(data->rx_msg));
LOG_INF("RT1715 TCPC initialized");
return 0;
}
static int rt1715_tcpc_get_cc(const struct device *dev, enum tc_cc_voltage_state *cc1,
enum tc_cc_voltage_state *cc2)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
int ret;
if (!data->initialized) {
return -EIO;
}
if (IS_ENABLED(CONFIG_USBC_CSM_SINK_ONLY) && !data->cc_changed) {
*cc1 = data->cc1;
*cc2 = data->cc2;
return 0;
}
data->cc_changed = false;
ret = tcpci_tcpm_get_cc(&cfg->bus, cc1, cc2);
if (IS_ENABLED(CONFIG_USBC_CSM_SINK_ONLY) || *cc1 != data->cc1 || *cc2 != data->cc2) {
LOG_DBG("CC changed values: %d->%d, %d->%d", data->cc1, *cc1, data->cc2, *cc2);
data->cc1 = *cc1;
data->cc2 = *cc2;
}
return ret;
}
static int rt1715_tcpc_select_rp_value(const struct device *dev, enum tc_rp_value rp)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
data->cc_changed = true;
return tcpci_tcpm_select_rp_value(&cfg->bus, rp);
}
static int rt1715_tcpc_get_rp_value(const struct device *dev, enum tc_rp_value *rp)
{
const struct rt1715_cfg *cfg = dev->config;
return tcpci_tcpm_get_rp_value(&cfg->bus, rp);
}
static int rt1715_tcpc_set_cc(const struct device *dev, enum tc_cc_pull pull)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
if (!data->initialized) {
return -EIO;
}
data->cc_changed = true;
return tcpci_tcpm_set_cc(&cfg->bus, pull);
}
static void rt1715_tcpc_set_vconn_discharge_cb(const struct device *dev,
tcpc_vconn_discharge_cb_t cb)
{
struct rt1715_data *data = dev->data;
data->vconn_discharge_cb = cb;
}
static void rt1715_tcpc_set_vconn_cb(const struct device *dev, tcpc_vconn_control_cb_t vconn_cb)
{
struct rt1715_data *data = dev->data;
data->vconn_cb = vconn_cb;
}
static int rt1715_tcpc_vconn_discharge(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
if (cfg->vconn_disc_gpio.port == NULL) {
/* RT1715 does not have built-in VCONN discharge path */
LOG_ERR("VCONN discharge GPIO is not defined");
return -EIO;
}
return gpio_pin_set_dt(&cfg->vconn_disc_gpio, enable);
}
static int rt1715_tcpc_set_vconn(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
int ret;
if (!data->initialized) {
return -EIO;
}
if (enable == true && cfg->vconn_ctrl_gpio.port != NULL) {
/* Enable external VCONN power supply */
gpio_pin_set_dt(&cfg->vconn_ctrl_gpio, true);
}
data->cc_changed = true;
ret = tcpci_tcpm_set_vconn(&cfg->bus, enable);
if (ret != 0) {
return ret;
}
if (enable == false && cfg->vconn_ctrl_gpio.port != NULL) {
/* Disable external VCONN power supply */
gpio_pin_set_dt(&cfg->vconn_ctrl_gpio, false);
}
if (data->vconn_cb != NULL) {
ret = data->vconn_cb(dev, data->cc_polarity, enable);
}
return ret;
}
static int rt1715_tcpc_set_roles(const struct device *dev, enum tc_power_role power_role,
enum tc_data_role data_role)
{
const struct rt1715_cfg *cfg = dev->config;
return tcpci_tcpm_set_roles(&cfg->bus, PD_REV30, power_role, data_role);
}
static int rt1715_tcpc_get_rx_pending_msg(const struct device *dev, struct pd_msg *msg)
{
struct rt1715_data *data = dev->data;
/* Rx message pending? */
if (!data->msg_pending) {
return -ENODATA;
}
/* Query status only? */
if (msg == NULL) {
return 0;
}
/* Dequeue Rx FIFO */
*msg = data->rx_msg;
data->msg_pending = false;
/* Indicate Rx message returned */
return 1;
}
static int rt1715_tcpc_rx_fifo_enqueue(const struct device *dev)
{
const struct rt1715_cfg *const cfg = dev->config;
struct rt1715_data *data = dev->data;
struct i2c_msg buf[5];
uint8_t reg = TCPC_REG_RX_BUFFER;
uint8_t rxbcnt;
uint8_t rxftype;
uint16_t rxhead;
uint8_t rx_data_size;
struct pd_msg *msg = &data->rx_msg;
int ret = 0;
buf[0].buf = &reg;
buf[0].len = 1;
buf[0].flags = I2C_MSG_WRITE;
buf[1].buf = &rxbcnt;
buf[1].len = 1;
buf[1].flags = I2C_MSG_RESTART | I2C_MSG_READ;
buf[2].buf = &rxftype;
buf[2].len = 1;
buf[2].flags = I2C_MSG_RESTART | I2C_MSG_READ;
buf[3].buf = (uint8_t *)&rxhead;
buf[3].len = 2;
buf[3].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP;
ret = i2c_transfer(cfg->bus.bus, buf, 5, cfg->bus.addr);
if (ret != 0) {
return ret;
}
/* rxbcnt = 1 (frame type) + 2 (Message Header) + Rx data byte count */
if (rxbcnt < 3) {
LOG_ERR("Invalid RXBCNT: %d", rxbcnt);
return -EIO;
}
rx_data_size = rxbcnt - 3;
/* Not support Unchunked Extended Message exceeding PD_CONVERT_PD_HEADER_COUNT_TO_BYTES */
if (rx_data_size > (PD_MAX_EXTENDED_MSG_LEGACY_LEN + 2)) {
LOG_ERR("Not support Unchunked Extended Message exceeding "
"PD_CONVERT_PD_HEADER_COUNT_TO_BYTES: %d",
rx_data_size);
return -EIO;
}
/* Rx frame type */
msg->type = rxftype;
/* Rx header */
msg->header.raw_value = (uint16_t)rxhead;
/* Rx data size */
msg->len = rx_data_size;
/* Rx data */
if (rx_data_size > 0) {
ret = i2c_burst_read_dt(&cfg->bus, TCPC_REG_RX_BUFFER + 4, msg->data, rx_data_size);
if (ret) {
LOG_ERR("Failed to read Rx data: %d", ret);
}
}
return ret;
}
static int rt1715_tcpc_set_rx_enable(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
if (!enable) {
return tcpci_tcpm_set_rx_type(&cfg->bus, 0);
}
if (data->rx_sop_prime_enable) {
return tcpci_tcpm_set_rx_type(&cfg->bus,
TCPC_REG_RX_DETECT_SOP_SOPP_SOPPP_HRST_MASK);
} else {
return tcpci_tcpm_set_rx_type(&cfg->bus, TCPC_REG_RX_DETECT_SOP_HRST_MASK);
}
}
static int rt1715_tcpc_set_cc_polarity(const struct device *dev, enum tc_cc_polarity polarity)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
int ret;
if (!data->initialized) {
return -EIO;
}
ret = tcpci_tcpm_set_cc_polarity(&cfg->bus, polarity);
if (ret != 0) {
return ret;
}
data->cc_changed = true;
data->cc_polarity = polarity;
return 0;
}
static int rt1715_tcpc_transmit_data(const struct device *dev, struct pd_msg *msg)
{
const struct rt1715_cfg *cfg = dev->config;
return tcpci_tcpm_transmit_data(&cfg->bus, msg, cfg->transmit_retries);
}
static int rt1715_tcpc_dump_std_reg(const struct device *dev)
{
const struct rt1715_cfg *cfg = dev->config;
LOG_INF("TCPC %s:%s registers:", cfg->bus.bus->name, dev->name);
return tcpci_tcpm_dump_std_reg(&cfg->bus);
}
void rt1715_tcpc_alert_handler_cb(const struct device *dev, void *data, enum tcpc_alert alert)
{
}
static int rt1715_tcpc_get_status_register(const struct device *dev, enum tcpc_status_reg reg,
uint32_t *status)
{
const struct rt1715_cfg *cfg = dev->config;
if (reg == TCPC_VENDOR_DEFINED_STATUS) {
return tcpci_read_reg8(&cfg->bus, RT1715_REG_RT_INT, (uint8_t *)status);
}
return tcpci_tcpm_get_status_register(&cfg->bus, reg, (uint16_t *)status);
}
static int rt1715_tcpc_clear_status_register(const struct device *dev, enum tcpc_status_reg reg,
uint32_t mask)
{
const struct rt1715_cfg *cfg = dev->config;
if (reg == TCPC_VENDOR_DEFINED_STATUS) {
return tcpci_write_reg8(&cfg->bus, RT1715_REG_RT_INT, (uint8_t)mask);
}
return tcpci_tcpm_clear_status_register(&cfg->bus, reg, (uint16_t)mask);
}
static int rt1715_tcpc_mask_status_register(const struct device *dev, enum tcpc_status_reg reg,
uint32_t mask)
{
const struct rt1715_cfg *cfg = dev->config;
if (reg == TCPC_VENDOR_DEFINED_STATUS) {
return tcpci_write_reg8(&cfg->bus, RT1715_REG_RT_INT_MASK, (uint8_t)mask);
}
return tcpci_tcpm_mask_status_register(&cfg->bus, reg, (uint16_t)mask);
}
static int rt1715_tcpc_set_drp_toggle(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
return tcpci_tcpm_set_drp_toggle(&cfg->bus, enable);
}
static int rt1715_tcpc_get_chip_info(const struct device *dev, struct tcpc_chip_info *chip_info)
{
const struct rt1715_cfg *cfg = dev->config;
if (chip_info == NULL) {
return -EIO;
}
chip_info->fw_version_number = 0;
chip_info->min_req_fw_version_number = 0;
return tcpci_tcpm_get_chip_info(&cfg->bus, chip_info);
}
static int rt1715_tcpc_set_low_power_mode(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
int ret;
ret = tcpci_write_reg8(&cfg->bus, RT1715_REG_SYS_WAKEUP, RT1715_REG_SYS_WAKEUP_EN);
if (ret < 0) {
return ret;
}
return tcpci_update_reg8(&cfg->bus, RT1715_REG_SYS_CTRL_1,
RT1715_REG_SYS_CTRL_1_BMCIO_LP_EN,
enable ? RT1715_REG_SYS_CTRL_1_BMCIO_LP_EN : 0);
}
static int rt1715_tcpc_sop_prime_enable(const struct device *dev, bool enable)
{
struct rt1715_data *data = dev->data;
data->rx_sop_prime_enable = enable;
return 0;
}
static int rt1715_tcpc_set_bist_test_mode(const struct device *dev, bool enable)
{
const struct rt1715_cfg *cfg = dev->config;
return tcpci_tcpm_set_bist_test_mode(&cfg->bus, enable);
}
static int rt1715_tcpc_set_alert_handler_cb(const struct device *dev,
tcpc_alert_handler_cb_t handler, void *handler_data)
{
struct rt1715_data *data = dev->data;
if (data->alert_handler == handler && data->alert_handler_data == handler_data) {
return 0;
}
data->alert_handler = handler;
data->alert_handler_data = handler_data;
return 0;
}
static DEVICE_API(tcpc, rt1715_driver_api) = {
.init = rt1715_tcpc_init,
.get_cc = rt1715_tcpc_get_cc,
.select_rp_value = rt1715_tcpc_select_rp_value,
.get_rp_value = rt1715_tcpc_get_rp_value,
.set_cc = rt1715_tcpc_set_cc,
.set_vconn_discharge_cb = rt1715_tcpc_set_vconn_discharge_cb,
.set_vconn_cb = rt1715_tcpc_set_vconn_cb,
.vconn_discharge = rt1715_tcpc_vconn_discharge,
.set_vconn = rt1715_tcpc_set_vconn,
.set_roles = rt1715_tcpc_set_roles,
.get_rx_pending_msg = rt1715_tcpc_get_rx_pending_msg,
.set_rx_enable = rt1715_tcpc_set_rx_enable,
.set_cc_polarity = rt1715_tcpc_set_cc_polarity,
.transmit_data = rt1715_tcpc_transmit_data,
.dump_std_reg = rt1715_tcpc_dump_std_reg,
.alert_handler_cb = rt1715_tcpc_alert_handler_cb,
.get_status_register = rt1715_tcpc_get_status_register,
.clear_status_register = rt1715_tcpc_clear_status_register,
.mask_status_register = rt1715_tcpc_mask_status_register,
.set_drp_toggle = rt1715_tcpc_set_drp_toggle,
.get_chip_info = rt1715_tcpc_get_chip_info,
.set_low_power_mode = rt1715_tcpc_set_low_power_mode,
.sop_prime_enable = rt1715_tcpc_sop_prime_enable,
.set_bist_test_mode = rt1715_tcpc_set_bist_test_mode,
.set_alert_handler_cb = rt1715_tcpc_set_alert_handler_cb,
};
void rt1715_alert_cb(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins)
{
struct rt1715_data *data = CONTAINER_OF(cb, struct rt1715_data, alert_cb);
k_work_submit(&data->alert_work);
}
void rt1715_alert_work_cb(struct k_work *work)
{
struct rt1715_data *data = CONTAINER_OF(work, struct rt1715_data, alert_work);
const struct device *dev = data->dev;
const struct rt1715_cfg *cfg = dev->config;
uint16_t alert_reg = 0;
uint16_t clear_flags = 0;
if (!data->initialized) {
return;
}
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_ALERT_STATUS, &alert_reg);
while (alert_reg != 0) {
enum tcpc_alert alert_type = tcpci_alert_reg_to_enum(alert_reg);
if (alert_type == TCPC_ALERT_HARD_RESET_RECEIVED) {
LOG_DBG("hard rst received");
tcpci_init_alert_mask(dev);
data->cc_changed = true;
} else if (alert_type == TCPC_ALERT_FAULT_STATUS) {
uint8_t fault;
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_FAULT_STATUS,
(uint16_t *)&fault);
tcpci_tcpm_clear_status_register(&cfg->bus, TCPC_FAULT_STATUS,
(uint16_t)fault);
LOG_DBG("fault: %02x", fault);
} else if (alert_type == TCPC_ALERT_EXTENDED_STATUS) {
uint8_t ext_status;
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_EXTENDED_STATUS,
(uint16_t *)&ext_status);
tcpci_tcpm_clear_status_register(&cfg->bus, TCPC_EXTENDED_STATUS,
(uint16_t)ext_status);
data->cc_changed = true;
LOG_DBG("ext status: %02x", ext_status);
} else if (alert_type == TCPC_ALERT_POWER_STATUS) {
uint8_t pwr_status;
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_POWER_STATUS,
(uint16_t *)&pwr_status);
tcpci_tcpm_clear_status_register(&cfg->bus, TCPC_POWER_STATUS,
(uint16_t)pwr_status);
LOG_DBG("power status: %02x", pwr_status);
} else if (alert_type == TCPC_ALERT_EXTENDED) {
uint8_t alert_status;
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_EXTENDED_ALERT_STATUS,
(uint16_t *)&alert_status);
tcpci_tcpm_clear_status_register(&cfg->bus, TCPC_EXTENDED_ALERT_STATUS,
(uint16_t)alert_status);
LOG_DBG("ext alert: %02x", alert_status);
} else if (alert_type == TCPC_ALERT_MSG_STATUS) {
LOG_DBG("MSG pending");
if (rt1715_tcpc_rx_fifo_enqueue(dev) == 0) {
data->msg_pending = true;
}
} else if (alert_type == TCPC_ALERT_CC_STATUS) {
data->cc_changed = true;
}
if (data->alert_handler != NULL) {
data->alert_handler(dev, data->alert_handler_data, alert_type);
}
clear_flags |= BIT(alert_type);
alert_reg &= ~BIT(alert_type);
}
tcpci_tcpm_clear_status_register(&cfg->bus, TCPC_ALERT_STATUS, clear_flags);
tcpci_tcpm_get_status_register(&cfg->bus, TCPC_ALERT_STATUS, &alert_reg);
/* If alert_reg is not 0 or the interrupt signal is still active */
if ((alert_reg != 0) || gpio_pin_get_dt(&cfg->alert_gpio)) {
k_work_submit(work);
}
}
void rt1715_init_work_cb(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct rt1715_data *data = CONTAINER_OF(dwork, struct rt1715_data, init_dwork);
const struct rt1715_cfg *cfg = data->dev->config;
uint8_t power_reg, lp_reg = 0;
struct tcpc_chip_info chip_info;
int ret;
LOG_INF("Initializing RT1715 chip: %s", data->dev->name);
ret = tcpci_tcpm_get_status_register(&cfg->bus, TCPC_POWER_STATUS, (uint16_t *)&power_reg);
if (ret != 0 || (power_reg & TCPC_REG_POWER_STATUS_UNINIT)) {
data->init_retries++;
if (data->init_retries > CONFIG_USBC_TCPC_RT1715_INIT_RETRIES) {
LOG_ERR("Chip didn't respond");
return;
}
LOG_DBG("Postpone chip initialization %d", data->init_retries);
k_work_schedule(&data->init_dwork, K_MSEC(CONFIG_USBC_TCPC_RT1715_INIT_DELAY));
return;
}
rt1715_tcpc_get_chip_info(data->dev, &chip_info);
LOG_INF("Initialized chip is: %04x:%04x:%04x", chip_info.vendor_id, chip_info.product_id,
chip_info.device_id);
/* Exit shutdown mode & Enable ext messages */
lp_reg = RT1715_REG_LP_CTRL_SHUTDOWN_OFF | RT1715_REG_LP_CTRL_ENEXTMSG;
/* Disable idle mode */
lp_reg &= ~RT1715_REG_LP_CTRL_AUTOIDLE_EN;
tcpci_write_reg8(&cfg->bus, RT1715_REG_LP_CTRL, lp_reg);
/* Initialize alert interrupt */
gpio_pin_configure_dt(&cfg->alert_gpio, GPIO_INPUT);
gpio_init_callback(&data->alert_cb, rt1715_alert_cb, BIT(cfg->alert_gpio.pin));
gpio_add_callback(cfg->alert_gpio.port, &data->alert_cb);
gpio_pin_interrupt_configure_dt(&cfg->alert_gpio, GPIO_INT_EDGE_TO_ACTIVE);
tcpci_init_alert_mask(data->dev);
data->initialized = true;
/* Disable the vconn and open CC lines to reinitialize the communication with partner */
rt1715_tcpc_set_vconn(data->dev, false);
rt1715_tcpc_set_cc(data->dev, TC_CC_OPEN);
/* Check and clear any alert set after initialization */
k_work_submit(&data->alert_work);
}
static int rt1715_dev_init(const struct device *dev)
{
const struct rt1715_cfg *cfg = dev->config;
struct rt1715_data *data = dev->data;
int ret;
if (!device_is_ready(cfg->bus.bus)) {
return -EIO;
}
/* Resets the chip */
ret = tcpci_write_reg8(&cfg->bus, RT1715_REG_SW_RST, RT1715_REG_SW_RST_EN);
if (ret != 0) {
LOG_ERR("Failed to reset chip: %d", ret);
return ret;
}
k_work_init_delayable(&data->init_dwork, rt1715_init_work_cb);
k_work_schedule(&data->init_dwork, K_MSEC(CONFIG_USBC_TCPC_RT1715_INIT_DELAY));
k_work_init(&data->alert_work, rt1715_alert_work_cb);
return 0;
}
#define RT1715_DRIVER_DATA_INIT(node) \
{ \
.dev = DEVICE_DT_GET(node), \
.init_retries = 0, \
.cc_changed = true, \
}
#define VCONN_CTRL_GPIO(node) \
.vconn_ctrl_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(node, vconn_ctrl_gpios), \
(GPIO_DT_SPEC_INST_GET(node, vconn_ctrl_gpios)), ({0}))
#define VCONN_DISC_GPIO(node) \
.vconn_disc_gpio = COND_CODE_1(DT_INST_NODE_HAS_PROP(node, vconn_disc_gpios), \
(GPIO_DT_SPEC_INST_GET(node, vconn_disc_gpios)), ({0}))
#define RT1715_DRIVER_CFG_INIT(node) \
{ \
.bus = I2C_DT_SPEC_GET(node), \
.alert_gpio = GPIO_DT_SPEC_GET(node, irq_gpios), \
.transmit_retries = DT_PROP(node, transmit_retries), \
VCONN_CTRL_GPIO(node), \
VCONN_DISC_GPIO(node), \
}
#define RT1715_DRIVER_INIT(inst) \
static struct rt1715_data drv_data_rt1715##inst = \
RT1715_DRIVER_DATA_INIT(DT_DRV_INST(inst)); \
static struct rt1715_cfg drv_cfg_rt1715##inst = RT1715_DRIVER_CFG_INIT(DT_DRV_INST(inst)); \
DEVICE_DT_INST_DEFINE(inst, &rt1715_dev_init, NULL, &drv_data_rt1715##inst, \
&drv_cfg_rt1715##inst, POST_KERNEL, CONFIG_USBC_TCPC_INIT_PRIORITY, \
&rt1715_driver_api);
DT_INST_FOREACH_STATUS_OKAY(RT1715_DRIVER_INIT)

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2024 Jianxiong Gu <jianxiong.gu@outlook.com>
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_USBC_TCPC_RT1715_H_
#define ZEPHYR_DRIVERS_USBC_TCPC_RT1715_H_
#define RT1715_REG_SYS_CTRL_1 0x90
/** VCONN OVP occurs and discharge path turn-on */
#define RT1715_REG_SYS_CTRL_1_VCONN_DISCHARGE_EN BIT(5)
/** Low power mode Rd or Rp */
#define RT1715_REG_SYS_CTRL_1_BMCIO_LPR_PRD BIT(4)
/** Low power mode enable */
#define RT1715_REG_SYS_CTRL_1_BMCIO_LP_EN BIT(3)
/** BMCIO BandGap enable */
#define RT1715_REG_SYS_CTRL_1_BMCIO_BG_EN BIT(2)
/** VBUS detection enable */
#define RT1715_REG_SYS_CTRL_1_VBUS_DETECT_EN BIT(1)
/** 24M oscillator for BMC communication */
#define RT1715_REG_SYS_CTRL_1_BMCIO_OSC_EN BIT(0)
#define RT1715_REG_OCP 0x93
/** VCONN over-current control selection */
#define RT1715_REG_OCP_BMCIO_VCON_OCP GENMASK(7, 5)
#define RT1715_VCON_OCP_200MA (0 << 5)
#define RT1715_VCON_OCP_300MA (1 << 5)
#define RT1715_VCON_OCP_400MA (2 << 5)
#define RT1715_VCON_OCP_500MA (3 << 5)
#define RT1715_VCON_OCP_600MA (4 << 5)
#define RT1715_REG_RT_ST 0x97
/** If VBUS under 0.8V */
#define RT1715_REG_RT_ST_VBUS_80 BIT(1)
#define RT1715_REG_RT_INT 0x98
/** Ra detach */
#define RT1715_REG_RT_INT_RA_DETACH BIT(5)
/** VBUS under 0.8V */
#define RT1715_REG_RT_INT_VBUS_80 BIT(1)
/** Low power mode exited */
#define RT1715_REG_RT_INT_WAKEUP BIT(0)
#define RT1715_REG_RT_INT_MASK 0x99
#define RT1715_REG_LP_CTRL 0x9B
/** Clock_300K divided from Clock_24M */
#define RT1715_REG_LP_CTRL_CK_300K_SEL BIT(7)
/** Non-Shutdown mode */
#define RT1715_REG_LP_CTRL_SHUTDOWN_OFF BIT(5)
/** Enable PD3.0 Extended message */
#define RT1715_REG_LP_CTRL_ENEXTMSG BIT(4)
/** Auto enter idle mode enable */
#define RT1715_REG_LP_CTRL_AUTOIDLE_EN BIT(3)
/** Enter idle mode timeout time */
#define RT1715_REG_LP_CTRL_AUTOIDLE_TIMEOUT GENMASK(2, 0)
#define RT1715_AUTOIDLE_TIMEOUT_96P0_MS 7
#define RT1715_AUTOIDLE_TIMEOUT_83P2_MS 6
#define RT1715_AUTOIDLE_TIMEOUT_70P4_MS 5
#define RT1715_AUTOIDLE_TIMEOUT_57P6_MS 4
#define RT1715_AUTOIDLE_TIMEOUT_44P8_MS 3
#define RT1715_AUTOIDLE_TIMEOUT_32P0_MS 2
#define RT1715_AUTOIDLE_TIMEOUT_19P2_MS 1
#define RT1715_AUTOIDLE_TIMEOUT_6P4_MS 0
#define RT1715_REG_SYS_WAKEUP 0x9F
/** Wakeup function enable */
#define RT1715_REG_SYS_WAKEUP_EN BIT(7)
#define RT1715_REG_SW_RST 0xA0
/** Write 1 to trigger software reset */
#define RT1715_REG_SW_RST_EN BIT(0)
#define RT1715_REG_DRP_CTRL_1 0xA2
/**
* The period a DRP will complete a Source to Sink and back advertisement.
* (Period = TDRP * 6.4 + 51.2ms)
*/
#define RT1715_REG_DRP_CTRL_1_TDRP GENMASK(3, 0)
#define RT1715_REG_DRP_CTRL_2 0xA3
/**
* The percent of time that a DRP will advertise Source during tDRP.
* (DUTY = (DCSRCDRP[9:0] + 1) / 1024)
*/
#define RT1715_REG_DRP_CTRL_2_DCSRCDRP GENMASK(9, 0)
#endif /* ZEPHYR_DRIVERS_USBC_TCPC_UCPD_NUMAKER_H_ */

View file

@ -0,0 +1,74 @@
# Copyright (c) 2024 Jianxiong Gu <jianxiong.gu@outlook.com>
# SPDX-License-Identifier: Apache-2.0
description: |
The Richtek RT1715 is a Type-C Port Controller (TCPC) chip.
Example:
ports {
#address-cells = <1>;
#size-cells = <0>;
port0: usbc-port@0 {
compatible = "usb-c-connector";
reg = <0>;
tcpc = <&rt1715_tcpc0>;
vbus = <&vbus0>;
power-role = "sink";
sink-pdos = <PDO_FIXED(5000, 100, 0)>;
};
};
vbus0: vbus {
compatible = "zephyr,usb-c-vbus-adc";
status = "okay";
io-channels = <&adc 2>;
output-ohms = <10000>;
full-ohms = <(100000 + 10000)>;
};
i2c1 {
status = "okay";
clock-frequency = <I2C_BITRATE_FAST_PLUS>;
rt1715_tcpc0: rt1715@4e {
compatible = "richtek,rt1715";
reg = <0x4e>;
irq-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
status = "okay";
};
};
compatible: "richtek,rt1715"
include: [base.yaml, pinctrl-device.yaml]
properties:
reg:
required: true
irq-gpios:
type: phandle-array
required: true
description: |
Interrupt GPIO pin connected from the chip.
transmit-retries:
type: int
default: 2
description: |
Maximum number of packet retransmissions done by TCPC. Valid values are <0, 3>.
This value is used to fill the Retry Counter part of the TCPCI Transmit register.
vconn-ctrl-gpios:
type: phandle-array
description: |
GPIO pin for VCONN control. RT1715 does not have built-in VCONN power supply. If the
state of the VCONN power supply need to be toggled, this pin and a switchable power
supply are required.
vconn-disc-gpios:
type: phandle-array
description: |
GPIO pin for VCONN discharge control. RT1715 does not have VCONN discharge capability.
If VCONN discharge is not needed, this pin does not need to be defined in the device
tree. Otherwise, this pin and a discharge path are required.