usb: typec: maxim_contaminant: Implement check_contaminant callback
Maxim TCPC has additional ADCs and low current(1ua) current source to measure the impedance of CC and SBU pins. When tcpm invokes the check_contaminant callback, Maxim TCPC measures the impedance of the CC & SBU pins and when the impedance measured is less than 1MOhm, it is assumed that USB-C port is contaminated. CC comparators are also checked to differentiate between presence of sink and contaminant. Once USB-C is deemed to be contaminated, MAXIM TCPC has additional hardware to disable normal DRP toggling cycle and enable 1ua on CC pins once every 2.4secs/4.8secs. Maxim TCPC interrupts AP once the impedance on the CC pin is above the 1MOhm threshold. The Maxim tcpc driver then signals TCPM_PORT_CLEAN to restart toggling. Renaming tcpci_maxim.c to tcpci_maxim_core.c and moving reg read/write helper functions to the tcpci_maxim.h header file. Signed-off-by: Badhri Jagan Sridharan <badhri@google.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/20230114093246.1933321-3-badhri@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
abc028a270
commit
02b332a063
|
@ -8,3 +8,4 @@ obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
|
|||
obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o
|
||||
obj-$(CONFIG_TYPEC_TCPCI_MT6370) += tcpci_mt6370.o
|
||||
obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o
|
||||
tcpci_maxim-y += tcpci_maxim_core.o maxim_contaminant.o
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2022 Google, Inc
|
||||
*
|
||||
* USB-C module to reduce wakeups due to contaminants.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/usb/tcpci.h>
|
||||
#include <linux/usb/tcpm.h>
|
||||
#include <linux/usb/typec.h>
|
||||
|
||||
#include "tcpci_maxim.h"
|
||||
|
||||
enum fladc_select {
|
||||
CC1_SCALE1 = 1,
|
||||
CC1_SCALE2,
|
||||
CC2_SCALE1,
|
||||
CC2_SCALE2,
|
||||
SBU1,
|
||||
SBU2,
|
||||
};
|
||||
|
||||
#define FLADC_1uA_LSB_MV 25
|
||||
/* High range CC */
|
||||
#define FLADC_CC_HIGH_RANGE_LSB_MV 208
|
||||
/* Low range CC */
|
||||
#define FLADC_CC_LOW_RANGE_LSB_MV 126
|
||||
|
||||
/* 1uA current source */
|
||||
#define FLADC_CC_SCALE1 1
|
||||
/* 5 uA current source */
|
||||
#define FLADC_CC_SCALE2 5
|
||||
|
||||
#define FLADC_1uA_CC_OFFSET_MV 300
|
||||
#define FLADC_CC_HIGH_RANGE_OFFSET_MV 624
|
||||
#define FLADC_CC_LOW_RANGE_OFFSET_MV 378
|
||||
|
||||
#define CONTAMINANT_THRESHOLD_SBU_K 1000
|
||||
#define CONTAMINANT_THRESHOLD_CC_K 1000
|
||||
|
||||
#define READ1_SLEEP_MS 10
|
||||
#define READ2_SLEEP_MS 5
|
||||
|
||||
#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val))
|
||||
|
||||
#define IS_CC_OPEN(cc_status) \
|
||||
(STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \
|
||||
TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \
|
||||
TCPC_CC_STATUS_CC2_MASK << \
|
||||
TCPC_CC_STATUS_CC2_SHIFT, \
|
||||
TCPC_CC_STATE_SRC_OPEN))
|
||||
|
||||
static int max_contaminant_adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
|
||||
bool ua_src, u8 fladc)
|
||||
{
|
||||
/* SBU channels only have 1 scale with 1uA. */
|
||||
if ((ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2 || channel == SBU1 ||
|
||||
channel == SBU2)))
|
||||
/* Mean of range */
|
||||
return FLADC_1uA_CC_OFFSET_MV + (fladc * FLADC_1uA_LSB_MV);
|
||||
else if (!ua_src && (channel == CC1_SCALE1 || channel == CC2_SCALE1))
|
||||
return FLADC_CC_HIGH_RANGE_OFFSET_MV + (fladc * FLADC_CC_HIGH_RANGE_LSB_MV);
|
||||
else if (!ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2))
|
||||
return FLADC_CC_LOW_RANGE_OFFSET_MV + (fladc * FLADC_CC_LOW_RANGE_LSB_MV);
|
||||
|
||||
dev_err_once(chip->dev, "ADC ERROR: SCALE UNKNOWN");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
|
||||
int sleep_msec, bool raw, bool ua_src)
|
||||
{
|
||||
struct regmap *regmap = chip->data.regmap;
|
||||
u8 fladc;
|
||||
int ret;
|
||||
|
||||
/* Channel & scale select */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK,
|
||||
channel << ADC_CHANNEL_OFFSET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable ADC */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, ADCEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(sleep_msec * 1000, (sleep_msec + 1) * 1000);
|
||||
ret = max_tcpci_read8(chip, TCPC_VENDOR_FLADC_STATUS, &fladc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Disable ADC */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!raw)
|
||||
return max_contaminant_adc_to_mv(chip, channel, ua_src, fladc);
|
||||
else
|
||||
return fladc;
|
||||
}
|
||||
|
||||
static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip,
|
||||
enum fladc_select channel, int sleep_msec, bool raw)
|
||||
{
|
||||
struct regmap *regmap = chip->data.regmap;
|
||||
int mv;
|
||||
int ret;
|
||||
|
||||
if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 ||
|
||||
channel == CC2_SCALE2) {
|
||||
/* Enable 1uA current source */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
|
||||
ULTRA_LOW_POWER_MODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable 1uA current source */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* OVP disable */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, CCOVPDIS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, true);
|
||||
if (mv < 0)
|
||||
return ret;
|
||||
|
||||
/* OVP enable */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* returns KOhm as 1uA source is used. */
|
||||
return mv;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, SBUOVPDIS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* SBU switches auto configure when channel is selected. */
|
||||
/* Enable 1ua current source */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, SBURPCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, true);
|
||||
if (mv < 0)
|
||||
return ret;
|
||||
/* Disable current source */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* OVP disable */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *vendor_cc_status2_cc1,
|
||||
u8 *vendor_cc_status2_cc2)
|
||||
{
|
||||
struct regmap *regmap = chip->data.regmap;
|
||||
int ret;
|
||||
|
||||
/* Enable 80uA source */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable comparators */
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, CCCOMPEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Sleep to allow comparators settle */
|
||||
usleep_range(5000, 6000);
|
||||
ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, vendor_cc_status2_cc1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, vendor_cc_status2_cc2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max_contaminant_detect_contaminant(struct max_tcpci_chip *chip)
|
||||
{
|
||||
int cc1_k, cc2_k, sbu1_k, sbu2_k, ret;
|
||||
u8 vendor_cc_status2_cc1 = 0xff, vendor_cc_status2_cc2 = 0xff;
|
||||
u8 role_ctrl = 0, role_ctrl_backup = 0;
|
||||
int inferred_state = NOT_DETECTED;
|
||||
|
||||
ret = max_tcpci_read8(chip, TCPC_ROLE_CTRL, &role_ctrl);
|
||||
if (ret < 0)
|
||||
return NOT_DETECTED;
|
||||
|
||||
role_ctrl_backup = role_ctrl;
|
||||
role_ctrl = 0x0F;
|
||||
ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl);
|
||||
if (ret < 0)
|
||||
return NOT_DETECTED;
|
||||
|
||||
cc1_k = max_contaminant_read_resistance_kohm(chip, CC1_SCALE2, READ1_SLEEP_MS, false);
|
||||
if (cc1_k < 0)
|
||||
goto exit;
|
||||
|
||||
cc2_k = max_contaminant_read_resistance_kohm(chip, CC2_SCALE2, READ2_SLEEP_MS, false);
|
||||
if (cc2_k < 0)
|
||||
goto exit;
|
||||
|
||||
sbu1_k = max_contaminant_read_resistance_kohm(chip, SBU1, READ1_SLEEP_MS, false);
|
||||
if (sbu1_k < 0)
|
||||
goto exit;
|
||||
|
||||
sbu2_k = max_contaminant_read_resistance_kohm(chip, SBU2, READ2_SLEEP_MS, false);
|
||||
if (sbu2_k < 0)
|
||||
goto exit;
|
||||
|
||||
ret = max_contaminant_read_comparators(chip, &vendor_cc_status2_cc1,
|
||||
&vendor_cc_status2_cc2);
|
||||
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if ((!(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1) ||
|
||||
!(CC2_VUFP_RD0P5 & vendor_cc_status2_cc2)) &&
|
||||
!(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1 && CC2_VUFP_RD0P5 & vendor_cc_status2_cc2))
|
||||
inferred_state = SINK;
|
||||
else if ((cc1_k < CONTAMINANT_THRESHOLD_CC_K || cc2_k < CONTAMINANT_THRESHOLD_CC_K) &&
|
||||
(sbu1_k < CONTAMINANT_THRESHOLD_SBU_K || sbu2_k < CONTAMINANT_THRESHOLD_SBU_K))
|
||||
inferred_state = DETECTED;
|
||||
|
||||
if (inferred_state == NOT_DETECTED)
|
||||
max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup);
|
||||
else
|
||||
max_tcpci_write8(chip, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA));
|
||||
|
||||
return inferred_state;
|
||||
exit:
|
||||
max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup);
|
||||
return NOT_DETECTED;
|
||||
}
|
||||
|
||||
static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
|
||||
{
|
||||
struct regmap *regmap = chip->data.regmap;
|
||||
u8 temp;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK
|
||||
| WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT |
|
||||
CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S <<
|
||||
WTRCYCLE_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP, TCPC_ROLE_CTRL_DRP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, CCCONNDRY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL1, &temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
|
||||
ULTRA_LOW_POWER_MODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, &temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable Look4Connection before sending the command */
|
||||
ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT,
|
||||
TCPC_TCPC_CTRL_EN_LK4CONN_ALRT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = max_tcpci_write8(chip, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce)
|
||||
{
|
||||
u8 cc_status, pwr_cntl;
|
||||
int ret;
|
||||
|
||||
ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
ret = max_tcpci_read8(chip, TCPC_POWER_CTRL, &pwr_cntl);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (chip->contaminant_state == NOT_DETECTED || chip->contaminant_state == SINK) {
|
||||
if (!disconnect_while_debounce)
|
||||
msleep(100);
|
||||
|
||||
ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (IS_CC_OPEN(cc_status)) {
|
||||
u8 role_ctrl, role_ctrl_backup;
|
||||
|
||||
ret = max_tcpci_read8(chip, TCPC_ROLE_CTRL, &role_ctrl);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
role_ctrl_backup = role_ctrl;
|
||||
role_ctrl |= 0x0F;
|
||||
role_ctrl &= ~(TCPC_ROLE_CTRL_DRP);
|
||||
ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
chip->contaminant_state = max_contaminant_detect_contaminant(chip);
|
||||
|
||||
ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (chip->contaminant_state == DETECTED) {
|
||||
max_contaminant_enable_dry_detection(chip);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (chip->contaminant_state == DETECTED) {
|
||||
if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) {
|
||||
chip->contaminant_state = max_contaminant_detect_contaminant(chip);
|
||||
if (chip->contaminant_state == DETECTED) {
|
||||
max_contaminant_enable_dry_detection(chip);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("MAXIM TCPC CONTAMINANT Module");
|
||||
MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,89 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2022 Google, Inc
|
||||
*
|
||||
* MAXIM TCPC header file.
|
||||
*/
|
||||
#ifndef TCPCI_MAXIM_H_
|
||||
#define TCPCI_MAXIM_H_
|
||||
|
||||
#define VENDOR_CC_STATUS2 0x85
|
||||
#define CC1_VUFP_RD0P5 BIT(1)
|
||||
#define CC2_VUFP_RD0P5 BIT(5)
|
||||
#define TCPC_VENDOR_FLADC_STATUS 0x89
|
||||
|
||||
#define TCPC_VENDOR_CC_CTRL1 0x8c
|
||||
#define CCCONNDRY BIT(7)
|
||||
#define CCCOMPEN BIT(5)
|
||||
|
||||
#define TCPC_VENDOR_CC_CTRL2 0x8d
|
||||
#define SBUOVPDIS BIT(7)
|
||||
#define CCOVPDIS BIT(6)
|
||||
#define SBURPCTRL BIT(5)
|
||||
#define CCLPMODESEL_MASK GENMASK(4, 3)
|
||||
#define ULTRA_LOW_POWER_MODE BIT(3)
|
||||
#define CCRPCTRL_MASK GENMASK(2, 0)
|
||||
#define UA_1_SRC 1
|
||||
#define UA_80_SRC 3
|
||||
|
||||
#define TCPC_VENDOR_CC_CTRL3 0x8e
|
||||
#define CCWTRDEB_MASK GENMASK(7, 6)
|
||||
#define CCWTRDEB_SHIFT 6
|
||||
#define CCWTRDEB_1MS 1
|
||||
#define CCWTRSEL_MASK GENMASK(5, 3)
|
||||
#define CCWTRSEL_SHIFT 3
|
||||
#define CCWTRSEL_1V 0x4
|
||||
#define CCLADDERDIS BIT(2)
|
||||
#define WTRCYCLE_MASK BIT(0)
|
||||
#define WTRCYCLE_SHIFT 0
|
||||
#define WTRCYCLE_2_4_S 0
|
||||
#define WTRCYCLE_4_8_S 1
|
||||
|
||||
#define TCPC_VENDOR_ADC_CTRL1 0x91
|
||||
#define ADCINSEL_MASK GENMASK(7, 5)
|
||||
#define ADC_CHANNEL_OFFSET 5
|
||||
#define ADCEN BIT(0)
|
||||
|
||||
enum contamiant_state {
|
||||
NOT_DETECTED,
|
||||
DETECTED,
|
||||
SINK,
|
||||
};
|
||||
|
||||
/*
|
||||
* @potential_contaminant:
|
||||
* Last returned result to tcpm indicating whether the TCPM port
|
||||
* has potential contaminant.
|
||||
*/
|
||||
struct max_tcpci_chip {
|
||||
struct tcpci_data data;
|
||||
struct tcpci *tcpci;
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct tcpm_port *port;
|
||||
enum contamiant_state contaminant_state;
|
||||
};
|
||||
|
||||
static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
|
||||
{
|
||||
return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
|
||||
}
|
||||
|
||||
static inline int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
|
||||
{
|
||||
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
|
||||
}
|
||||
|
||||
static inline int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
|
||||
{
|
||||
return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
|
||||
}
|
||||
|
||||
static inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
|
||||
{
|
||||
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
|
||||
}
|
||||
|
||||
bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce);
|
||||
|
||||
#endif // TCPCI_MAXIM_H_
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2020, Google LLC
|
||||
* Copyright (C) 2020 - 2022, Google LLC
|
||||
*
|
||||
* MAXIM TCPCI based TCPC driver
|
||||
*/
|
||||
|
@ -15,6 +15,8 @@
|
|||
#include <linux/usb/tcpm.h>
|
||||
#include <linux/usb/typec.h>
|
||||
|
||||
#include "tcpci_maxim.h"
|
||||
|
||||
#define PD_ACTIVITY_TIMEOUT_MS 10000
|
||||
|
||||
#define TCPC_VENDOR_ALERT 0x80
|
||||
|
@ -39,14 +41,6 @@
|
|||
#define MAX_BUCK_BOOST_SOURCE 0xa
|
||||
#define MAX_BUCK_BOOST_SINK 0x5
|
||||
|
||||
struct max_tcpci_chip {
|
||||
struct tcpci_data data;
|
||||
struct tcpci *tcpci;
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct tcpm_port *port;
|
||||
};
|
||||
|
||||
static const struct regmap_range max_tcpci_tcpci_range[] = {
|
||||
regmap_reg_range(0x00, 0x95)
|
||||
};
|
||||
|
@ -68,26 +62,6 @@ static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata)
|
|||
return container_of(tdata, struct max_tcpci_chip, data);
|
||||
}
|
||||
|
||||
static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
|
||||
{
|
||||
return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
|
||||
}
|
||||
|
||||
static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
|
||||
{
|
||||
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
|
||||
}
|
||||
|
||||
static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
|
||||
{
|
||||
return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
|
||||
}
|
||||
|
||||
static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
|
||||
{
|
||||
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
|
||||
}
|
||||
|
||||
static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
|
||||
{
|
||||
u16 alert_mask = 0;
|
||||
|
@ -348,8 +322,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
|
|||
if (status & TCPC_ALERT_VBUS_DISCNCT)
|
||||
tcpm_vbus_change(chip->port);
|
||||
|
||||
if (status & TCPC_ALERT_CC_STATUS)
|
||||
tcpm_cc_change(chip->port);
|
||||
if (status & TCPC_ALERT_CC_STATUS) {
|
||||
if (chip->contaminant_state == DETECTED || tcpm_port_is_toggling(chip->port)) {
|
||||
if (!max_contaminant_is_contaminant(chip, false))
|
||||
tcpm_port_clean(chip->port);
|
||||
} else {
|
||||
tcpm_cc_change(chip->port);
|
||||
}
|
||||
}
|
||||
|
||||
if (status & TCPC_ALERT_POWER_STATUS)
|
||||
process_power_status(chip);
|
||||
|
@ -438,6 +418,14 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata)
|
||||
{
|
||||
struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
|
||||
|
||||
if (!max_contaminant_is_contaminant(chip, true))
|
||||
tcpm_port_clean(chip->port);
|
||||
}
|
||||
|
||||
static int max_tcpci_probe(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
|
@ -471,6 +459,7 @@ static int max_tcpci_probe(struct i2c_client *client)
|
|||
chip->data.auto_discharge_disconnect = true;
|
||||
chip->data.vbus_vsafe0v = true;
|
||||
chip->data.set_partner_usb_comm_capable = max_tcpci_set_partner_usb_comm_capable;
|
||||
chip->data.check_contaminant = max_tcpci_check_contaminant;
|
||||
|
||||
max_tcpci_init_regs(chip);
|
||||
chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
|
Loading…
Reference in New Issue