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:
parent
90530cba96
commit
391008b097
6 changed files with 918 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
28
drivers/usb_c/tcpc/Kconfig.tcpc_rt1715
Normal file
28
drivers/usb_c/tcpc/Kconfig.tcpc_rt1715
Normal 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
726
drivers/usb_c/tcpc/rt1715.c
Normal 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 = ®
|
||||
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)
|
||||
88
drivers/usb_c/tcpc/rt1715.h
Normal file
88
drivers/usb_c/tcpc/rt1715.h
Normal 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_ */
|
||||
74
dts/bindings/tcpc/richtek,rt1715.yaml
Normal file
74
dts/bindings/tcpc/richtek,rt1715.yaml
Normal 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.
|
||||
Loading…
Reference in a new issue