1ae8a2ca8SHeikki Krogerus // SPDX-License-Identifier: GPL-2.0+ 2ae8a2ca8SHeikki Krogerus /* 3ae8a2ca8SHeikki Krogerus * Copyright 2015-2017 Google, Inc 4ae8a2ca8SHeikki Krogerus * 5ae8a2ca8SHeikki Krogerus * USB Type-C Port Controller Interface. 6ae8a2ca8SHeikki Krogerus */ 7ae8a2ca8SHeikki Krogerus 8ae8a2ca8SHeikki Krogerus #include <linux/delay.h> 9ae8a2ca8SHeikki Krogerus #include <linux/kernel.h> 10ae8a2ca8SHeikki Krogerus #include <linux/module.h> 11ae8a2ca8SHeikki Krogerus #include <linux/i2c.h> 12ae8a2ca8SHeikki Krogerus #include <linux/interrupt.h> 13ae8a2ca8SHeikki Krogerus #include <linux/property.h> 14ae8a2ca8SHeikki Krogerus #include <linux/regmap.h> 15ae8a2ca8SHeikki Krogerus #include <linux/usb/pd.h> 167963d4d7SXin Ji #include <linux/usb/tcpci.h> 17ae8a2ca8SHeikki Krogerus #include <linux/usb/tcpm.h> 18ae8a2ca8SHeikki Krogerus #include <linux/usb/typec.h> 19ae8a2ca8SHeikki Krogerus 20e4a93780SBadhri Jagan Sridharan #define PD_RETRY_COUNT_DEFAULT 3 21e4a93780SBadhri Jagan Sridharan #define PD_RETRY_COUNT_3_0_OR_HIGHER 2 22e1a97bf8SBadhri Jagan Sridharan #define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 234288debeSBadhri Jagan Sridharan #define VSINKPD_MIN_IR_DROP_MV 750 244288debeSBadhri Jagan Sridharan #define VSRC_NEW_MIN_PERCENT 95 254288debeSBadhri Jagan Sridharan #define VSRC_VALID_MIN_MV 500 264288debeSBadhri Jagan Sridharan #define VPPS_NEW_MIN_PERCENT 95 274288debeSBadhri Jagan Sridharan #define VPPS_VALID_MIN_MV 100 284288debeSBadhri Jagan Sridharan #define VSINKDISCONNECT_PD_MIN_PERCENT 90 29ae8a2ca8SHeikki Krogerus 30ae8a2ca8SHeikki Krogerus struct tcpci { 31ae8a2ca8SHeikki Krogerus struct device *dev; 32ae8a2ca8SHeikki Krogerus 33ae8a2ca8SHeikki Krogerus struct tcpm_port *port; 34ae8a2ca8SHeikki Krogerus 35ae8a2ca8SHeikki Krogerus struct regmap *regmap; 36ccb0beb4SXu Yang unsigned int alert_mask; 37ae8a2ca8SHeikki Krogerus 38ae8a2ca8SHeikki Krogerus bool controls_vbus; 39ae8a2ca8SHeikki Krogerus 40ae8a2ca8SHeikki Krogerus struct tcpc_dev tcpc; 41ae8a2ca8SHeikki Krogerus struct tcpci_data *data; 42ae8a2ca8SHeikki Krogerus }; 43ae8a2ca8SHeikki Krogerus 44ae8a2ca8SHeikki Krogerus struct tcpci_chip { 45ae8a2ca8SHeikki Krogerus struct tcpci *tcpci; 46ae8a2ca8SHeikki Krogerus struct tcpci_data data; 47ae8a2ca8SHeikki Krogerus }; 48ae8a2ca8SHeikki Krogerus 4958ea326bSBadhri Jagan Sridharan struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci) 5058ea326bSBadhri Jagan Sridharan { 5158ea326bSBadhri Jagan Sridharan return tcpci->port; 5258ea326bSBadhri Jagan Sridharan } 5358ea326bSBadhri Jagan Sridharan EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port); 5458ea326bSBadhri Jagan Sridharan 55ae8a2ca8SHeikki Krogerus static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) 56ae8a2ca8SHeikki Krogerus { 57ae8a2ca8SHeikki Krogerus return container_of(tcpc, struct tcpci, tcpc); 58ae8a2ca8SHeikki Krogerus } 59ae8a2ca8SHeikki Krogerus 60ae8a2ca8SHeikki Krogerus static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val) 61ae8a2ca8SHeikki Krogerus { 62ae8a2ca8SHeikki Krogerus return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16)); 63ae8a2ca8SHeikki Krogerus } 64ae8a2ca8SHeikki Krogerus 65ae8a2ca8SHeikki Krogerus static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val) 66ae8a2ca8SHeikki Krogerus { 67ae8a2ca8SHeikki Krogerus return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16)); 68ae8a2ca8SHeikki Krogerus } 69ae8a2ca8SHeikki Krogerus 70ae8a2ca8SHeikki Krogerus static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) 71ae8a2ca8SHeikki Krogerus { 72ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 735638b0dfSXu Yang bool vconn_pres; 745638b0dfSXu Yang enum typec_cc_polarity polarity = TYPEC_POLARITY_CC1; 75ae8a2ca8SHeikki Krogerus unsigned int reg; 76ae8a2ca8SHeikki Krogerus int ret; 77ae8a2ca8SHeikki Krogerus 785638b0dfSXu Yang ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); 795638b0dfSXu Yang if (ret < 0) 805638b0dfSXu Yang return ret; 815638b0dfSXu Yang 825638b0dfSXu Yang vconn_pres = !!(reg & TCPC_POWER_STATUS_VCONN_PRES); 835638b0dfSXu Yang if (vconn_pres) { 845638b0dfSXu Yang ret = regmap_read(tcpci->regmap, TCPC_TCPC_CTRL, ®); 855638b0dfSXu Yang if (ret < 0) 865638b0dfSXu Yang return ret; 875638b0dfSXu Yang 885638b0dfSXu Yang if (reg & TCPC_TCPC_CTRL_ORIENTATION) 895638b0dfSXu Yang polarity = TYPEC_POLARITY_CC2; 905638b0dfSXu Yang } 915638b0dfSXu Yang 92ae8a2ca8SHeikki Krogerus switch (cc) { 93ae8a2ca8SHeikki Krogerus case TYPEC_CC_RA: 94ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) | 95ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT); 96ae8a2ca8SHeikki Krogerus break; 97ae8a2ca8SHeikki Krogerus case TYPEC_CC_RD: 98ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | 99ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); 100ae8a2ca8SHeikki Krogerus break; 101ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_DEF: 102ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 103ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 104ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_DEF << 105ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 106ae8a2ca8SHeikki Krogerus break; 107ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_1_5: 108ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 109ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 110ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_1_5 << 111ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 112ae8a2ca8SHeikki Krogerus break; 113ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_3_0: 114ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 115ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 116ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_3_0 << 117ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 118ae8a2ca8SHeikki Krogerus break; 119ae8a2ca8SHeikki Krogerus case TYPEC_CC_OPEN: 120ae8a2ca8SHeikki Krogerus default: 121ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) | 122ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); 123ae8a2ca8SHeikki Krogerus break; 124ae8a2ca8SHeikki Krogerus } 125ae8a2ca8SHeikki Krogerus 1265638b0dfSXu Yang if (vconn_pres) { 1275638b0dfSXu Yang if (polarity == TYPEC_POLARITY_CC2) { 1285638b0dfSXu Yang reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); 1295638b0dfSXu Yang reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT); 1305638b0dfSXu Yang } else { 1315638b0dfSXu Yang reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); 1325638b0dfSXu Yang reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); 1335638b0dfSXu Yang } 1345638b0dfSXu Yang } 1355638b0dfSXu Yang 136ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 137ae8a2ca8SHeikki Krogerus if (ret < 0) 138ae8a2ca8SHeikki Krogerus return ret; 139ae8a2ca8SHeikki Krogerus 140ae8a2ca8SHeikki Krogerus return 0; 141ae8a2ca8SHeikki Krogerus } 142ae8a2ca8SHeikki Krogerus 143a0765597SWei Yongjun static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc, 144a0765597SWei Yongjun enum typec_cc_polarity polarity) 1457257fbc7SBadhri Jagan Sridharan { 1467257fbc7SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 1477257fbc7SBadhri Jagan Sridharan unsigned int reg; 1487257fbc7SBadhri Jagan Sridharan int ret; 1497257fbc7SBadhri Jagan Sridharan 1507257fbc7SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); 1517257fbc7SBadhri Jagan Sridharan if (ret < 0) 1527257fbc7SBadhri Jagan Sridharan return ret; 1537257fbc7SBadhri Jagan Sridharan 1547257fbc7SBadhri Jagan Sridharan /* 1557257fbc7SBadhri Jagan Sridharan * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on 1567257fbc7SBadhri Jagan Sridharan * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2. 1577257fbc7SBadhri Jagan Sridharan */ 1587257fbc7SBadhri Jagan Sridharan if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >> 1597257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC2_SHIFT) != 1607257fbc7SBadhri Jagan Sridharan ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >> 1617257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC1_SHIFT)) 1627257fbc7SBadhri Jagan Sridharan return 0; 1637257fbc7SBadhri Jagan Sridharan 1647257fbc7SBadhri Jagan Sridharan return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ? 1657257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT : 1667257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT, 1677257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC_OPEN); 1687257fbc7SBadhri Jagan Sridharan } 1697257fbc7SBadhri Jagan Sridharan 1707893f9e1SHans de Goede static int tcpci_start_toggling(struct tcpc_dev *tcpc, 1717893f9e1SHans de Goede enum typec_port_type port_type, 172ae8a2ca8SHeikki Krogerus enum typec_cc_status cc) 173ae8a2ca8SHeikki Krogerus { 174ae8a2ca8SHeikki Krogerus int ret; 175ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 176ae8a2ca8SHeikki Krogerus unsigned int reg = TCPC_ROLE_CTRL_DRP; 177ae8a2ca8SHeikki Krogerus 1787893f9e1SHans de Goede if (port_type != TYPEC_PORT_DRP) 1797893f9e1SHans de Goede return -EOPNOTSUPP; 1807893f9e1SHans de Goede 181ae8a2ca8SHeikki Krogerus /* Handle vendor drp toggling */ 182ae8a2ca8SHeikki Krogerus if (tcpci->data->start_drp_toggling) { 183ae8a2ca8SHeikki Krogerus ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); 184ae8a2ca8SHeikki Krogerus if (ret < 0) 185ae8a2ca8SHeikki Krogerus return ret; 186ae8a2ca8SHeikki Krogerus } 187ae8a2ca8SHeikki Krogerus 188ae8a2ca8SHeikki Krogerus switch (cc) { 189ae8a2ca8SHeikki Krogerus default: 190ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_DEF: 191ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << 192ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 193ae8a2ca8SHeikki Krogerus break; 194ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_1_5: 195ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << 196ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 197ae8a2ca8SHeikki Krogerus break; 198ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_3_0: 199ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << 200ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 201ae8a2ca8SHeikki Krogerus break; 202ae8a2ca8SHeikki Krogerus } 203ae8a2ca8SHeikki Krogerus 204ae8a2ca8SHeikki Krogerus if (cc == TYPEC_CC_RD) 205ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | 206ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); 207ae8a2ca8SHeikki Krogerus else 208ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 209ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); 210ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 211ae8a2ca8SHeikki Krogerus if (ret < 0) 212ae8a2ca8SHeikki Krogerus return ret; 213ae8a2ca8SHeikki Krogerus return regmap_write(tcpci->regmap, TCPC_COMMAND, 214ae8a2ca8SHeikki Krogerus TCPC_CMD_LOOK4CONNECTION); 215ae8a2ca8SHeikki Krogerus } 216ae8a2ca8SHeikki Krogerus 217ae8a2ca8SHeikki Krogerus static int tcpci_get_cc(struct tcpc_dev *tcpc, 218ae8a2ca8SHeikki Krogerus enum typec_cc_status *cc1, enum typec_cc_status *cc2) 219ae8a2ca8SHeikki Krogerus { 220ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 22119c234a1SBadhri Jagan Sridharan unsigned int reg, role_control; 222ae8a2ca8SHeikki Krogerus int ret; 223ae8a2ca8SHeikki Krogerus 22419c234a1SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &role_control); 22519c234a1SBadhri Jagan Sridharan if (ret < 0) 22619c234a1SBadhri Jagan Sridharan return ret; 22719c234a1SBadhri Jagan Sridharan 228ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, ®); 229ae8a2ca8SHeikki Krogerus if (ret < 0) 230ae8a2ca8SHeikki Krogerus return ret; 231ae8a2ca8SHeikki Krogerus 232ae8a2ca8SHeikki Krogerus *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & 233ae8a2ca8SHeikki Krogerus TCPC_CC_STATUS_CC1_MASK, 23419c234a1SBadhri Jagan Sridharan reg & TCPC_CC_STATUS_TERM || 235aecb1e45SBadhri Jagan Sridharan tcpc_presenting_rd(role_control, CC1)); 236ae8a2ca8SHeikki Krogerus *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & 237ae8a2ca8SHeikki Krogerus TCPC_CC_STATUS_CC2_MASK, 23819c234a1SBadhri Jagan Sridharan reg & TCPC_CC_STATUS_TERM || 239aecb1e45SBadhri Jagan Sridharan tcpc_presenting_rd(role_control, CC2)); 240ae8a2ca8SHeikki Krogerus 241ae8a2ca8SHeikki Krogerus return 0; 242ae8a2ca8SHeikki Krogerus } 243ae8a2ca8SHeikki Krogerus 244ae8a2ca8SHeikki Krogerus static int tcpci_set_polarity(struct tcpc_dev *tcpc, 245ae8a2ca8SHeikki Krogerus enum typec_cc_polarity polarity) 246ae8a2ca8SHeikki Krogerus { 247ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 248ae8a2ca8SHeikki Krogerus unsigned int reg; 249ae8a2ca8SHeikki Krogerus int ret; 25057ce6466SBadhri Jagan Sridharan enum typec_cc_status cc1, cc2; 251ae8a2ca8SHeikki Krogerus 25257ce6466SBadhri Jagan Sridharan /* Obtain Rp setting from role control */ 253ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); 254ae8a2ca8SHeikki Krogerus if (ret < 0) 255ae8a2ca8SHeikki Krogerus return ret; 256ae8a2ca8SHeikki Krogerus 25757ce6466SBadhri Jagan Sridharan ret = tcpci_get_cc(tcpc, &cc1, &cc2); 25857ce6466SBadhri Jagan Sridharan if (ret < 0) 25957ce6466SBadhri Jagan Sridharan return ret; 26057ce6466SBadhri Jagan Sridharan 26157ce6466SBadhri Jagan Sridharan /* 26257ce6466SBadhri Jagan Sridharan * When port has drp toggling enabled, ROLE_CONTROL would only have the initial 26357ce6466SBadhri Jagan Sridharan * terminations for the toggling and does not indicate the final cc 26457ce6466SBadhri Jagan Sridharan * terminations when ConnectionResult is 0 i.e. drp toggling stops and 265b53908f9SXu Yang * the connection is resolved. Infer port role from TCPC_CC_STATUS based on the 26657ce6466SBadhri Jagan Sridharan * terminations seen. The port role is then used to set the cc terminations. 26757ce6466SBadhri Jagan Sridharan */ 26857ce6466SBadhri Jagan Sridharan if (reg & TCPC_ROLE_CTRL_DRP) { 26957ce6466SBadhri Jagan Sridharan /* Disable DRP for the OPEN setting to take effect */ 27057ce6466SBadhri Jagan Sridharan reg = reg & ~TCPC_ROLE_CTRL_DRP; 27157ce6466SBadhri Jagan Sridharan 27257ce6466SBadhri Jagan Sridharan if (polarity == TYPEC_POLARITY_CC2) { 27357ce6466SBadhri Jagan Sridharan reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); 27457ce6466SBadhri Jagan Sridharan /* Local port is source */ 27557ce6466SBadhri Jagan Sridharan if (cc2 == TYPEC_CC_RD) 27657ce6466SBadhri Jagan Sridharan /* Role control would have the Rp setting when DRP was enabled */ 27757ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT; 27857ce6466SBadhri Jagan Sridharan else 27957ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT; 28057ce6466SBadhri Jagan Sridharan } else { 28157ce6466SBadhri Jagan Sridharan reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); 28257ce6466SBadhri Jagan Sridharan /* Local port is source */ 28357ce6466SBadhri Jagan Sridharan if (cc1 == TYPEC_CC_RD) 28457ce6466SBadhri Jagan Sridharan /* Role control would have the Rp setting when DRP was enabled */ 28557ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT; 28657ce6466SBadhri Jagan Sridharan else 28757ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT; 28857ce6466SBadhri Jagan Sridharan } 28957ce6466SBadhri Jagan Sridharan } 29057ce6466SBadhri Jagan Sridharan 291ae8a2ca8SHeikki Krogerus if (polarity == TYPEC_POLARITY_CC2) 292ae8a2ca8SHeikki Krogerus reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT; 293ae8a2ca8SHeikki Krogerus else 294ae8a2ca8SHeikki Krogerus reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT; 295ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 296ae8a2ca8SHeikki Krogerus if (ret < 0) 297ae8a2ca8SHeikki Krogerus return ret; 298ae8a2ca8SHeikki Krogerus 299ae8a2ca8SHeikki Krogerus return regmap_write(tcpci->regmap, TCPC_TCPC_CTRL, 300ae8a2ca8SHeikki Krogerus (polarity == TYPEC_POLARITY_CC2) ? 301ae8a2ca8SHeikki Krogerus TCPC_TCPC_CTRL_ORIENTATION : 0); 302ae8a2ca8SHeikki Krogerus } 303ae8a2ca8SHeikki Krogerus 304372a3d0bSBadhri Jagan Sridharan static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable) 305372a3d0bSBadhri Jagan Sridharan { 306372a3d0bSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 307372a3d0bSBadhri Jagan Sridharan 308372a3d0bSBadhri Jagan Sridharan if (tcpci->data->set_partner_usb_comm_capable) 309372a3d0bSBadhri Jagan Sridharan tcpci->data->set_partner_usb_comm_capable(tcpci, tcpci->data, capable); 310372a3d0bSBadhri Jagan Sridharan } 311372a3d0bSBadhri Jagan Sridharan 312ae8a2ca8SHeikki Krogerus static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) 313ae8a2ca8SHeikki Krogerus { 314ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 315ae8a2ca8SHeikki Krogerus int ret; 316ae8a2ca8SHeikki Krogerus 317ae8a2ca8SHeikki Krogerus /* Handle vendor set vconn */ 318ae8a2ca8SHeikki Krogerus if (tcpci->data->set_vconn) { 319ae8a2ca8SHeikki Krogerus ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable); 320ae8a2ca8SHeikki Krogerus if (ret < 0) 321ae8a2ca8SHeikki Krogerus return ret; 322ae8a2ca8SHeikki Krogerus } 323ae8a2ca8SHeikki Krogerus 324ae8a2ca8SHeikki Krogerus return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, 325ae8a2ca8SHeikki Krogerus TCPC_POWER_CTRL_VCONN_ENABLE, 326ae8a2ca8SHeikki Krogerus enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); 327ae8a2ca8SHeikki Krogerus } 328ae8a2ca8SHeikki Krogerus 329e1a97bf8SBadhri Jagan Sridharan static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) 330e1a97bf8SBadhri Jagan Sridharan { 331e1a97bf8SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 332e1a97bf8SBadhri Jagan Sridharan int ret; 333e1a97bf8SBadhri Jagan Sridharan 334e1a97bf8SBadhri Jagan Sridharan ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, 335e1a97bf8SBadhri Jagan Sridharan enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0); 336e1a97bf8SBadhri Jagan Sridharan return ret; 337e1a97bf8SBadhri Jagan Sridharan } 338e1a97bf8SBadhri Jagan Sridharan 339e1a97bf8SBadhri Jagan Sridharan static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, 340e1a97bf8SBadhri Jagan Sridharan bool pps_active, u32 requested_vbus_voltage_mv) 341e1a97bf8SBadhri Jagan Sridharan { 342e1a97bf8SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 343e1a97bf8SBadhri Jagan Sridharan unsigned int pwr_ctrl, threshold = 0; 344e1a97bf8SBadhri Jagan Sridharan int ret; 345e1a97bf8SBadhri Jagan Sridharan 346e1a97bf8SBadhri Jagan Sridharan /* 347e1a97bf8SBadhri Jagan Sridharan * Indicates that vbus is going to go away due PR_SWAP, hard reset etc. 348e1a97bf8SBadhri Jagan Sridharan * Do not discharge vbus here. 349e1a97bf8SBadhri Jagan Sridharan */ 350e1a97bf8SBadhri Jagan Sridharan if (requested_vbus_voltage_mv == 0) 351e1a97bf8SBadhri Jagan Sridharan goto write_thresh; 352e1a97bf8SBadhri Jagan Sridharan 353e1a97bf8SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl); 354e1a97bf8SBadhri Jagan Sridharan if (ret < 0) 355e1a97bf8SBadhri Jagan Sridharan return ret; 356e1a97bf8SBadhri Jagan Sridharan 357e1a97bf8SBadhri Jagan Sridharan if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) { 358e1a97bf8SBadhri Jagan Sridharan /* To prevent disconnect when the source is fast role swap is capable. */ 359e1a97bf8SBadhri Jagan Sridharan threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; 360e1a97bf8SBadhri Jagan Sridharan } else if (mode == TYPEC_PWR_MODE_PD) { 361e1a97bf8SBadhri Jagan Sridharan if (pps_active) 3624288debeSBadhri Jagan Sridharan threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - 3634288debeSBadhri Jagan Sridharan VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) * 3644288debeSBadhri Jagan Sridharan VSINKDISCONNECT_PD_MIN_PERCENT / 100; 365e1a97bf8SBadhri Jagan Sridharan else 3664288debeSBadhri Jagan Sridharan threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - 3674288debeSBadhri Jagan Sridharan VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) * 3684288debeSBadhri Jagan Sridharan VSINKDISCONNECT_PD_MIN_PERCENT / 100; 369e1a97bf8SBadhri Jagan Sridharan } else { 370e1a97bf8SBadhri Jagan Sridharan /* 3.5V for non-pd sink */ 371e1a97bf8SBadhri Jagan Sridharan threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; 372e1a97bf8SBadhri Jagan Sridharan } 373e1a97bf8SBadhri Jagan Sridharan 374e1a97bf8SBadhri Jagan Sridharan threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV; 375e1a97bf8SBadhri Jagan Sridharan 376e1a97bf8SBadhri Jagan Sridharan if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) 377e1a97bf8SBadhri Jagan Sridharan return -EINVAL; 378e1a97bf8SBadhri Jagan Sridharan 379e1a97bf8SBadhri Jagan Sridharan write_thresh: 380e1a97bf8SBadhri Jagan Sridharan return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold); 381e1a97bf8SBadhri Jagan Sridharan } 382e1a97bf8SBadhri Jagan Sridharan 38311121c24SBadhri Jagan Sridharan static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) 38411121c24SBadhri Jagan Sridharan { 38511121c24SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 38611121c24SBadhri Jagan Sridharan int ret; 38711121c24SBadhri Jagan Sridharan 38811121c24SBadhri Jagan Sridharan /* To prevent disconnect during FRS, set disconnect threshold to 3.5V */ 38911121c24SBadhri Jagan Sridharan ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c); 39011121c24SBadhri Jagan Sridharan if (ret < 0) 39111121c24SBadhri Jagan Sridharan return ret; 39211121c24SBadhri Jagan Sridharan 39311121c24SBadhri Jagan Sridharan ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ? 39411121c24SBadhri Jagan Sridharan TCPC_FAST_ROLE_SWAP_EN : 0); 39511121c24SBadhri Jagan Sridharan 39611121c24SBadhri Jagan Sridharan return ret; 39711121c24SBadhri Jagan Sridharan } 39811121c24SBadhri Jagan Sridharan 399a57d253fSBadhri Jagan Sridharan static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) 400a57d253fSBadhri Jagan Sridharan { 401a57d253fSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 402a57d253fSBadhri Jagan Sridharan 403a57d253fSBadhri Jagan Sridharan if (tcpci->data->frs_sourcing_vbus) 404a57d253fSBadhri Jagan Sridharan tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); 405a57d253fSBadhri Jagan Sridharan } 406a57d253fSBadhri Jagan Sridharan 407abc028a2SBadhri Jagan Sridharan static void tcpci_check_contaminant(struct tcpc_dev *dev) 408abc028a2SBadhri Jagan Sridharan { 409abc028a2SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 410abc028a2SBadhri Jagan Sridharan 411abc028a2SBadhri Jagan Sridharan if (tcpci->data->check_contaminant) 412abc028a2SBadhri Jagan Sridharan tcpci->data->check_contaminant(tcpci, tcpci->data); 413abc028a2SBadhri Jagan Sridharan } 414abc028a2SBadhri Jagan Sridharan 415c081ac42SBadhri Jagan Sridharan static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) 416c081ac42SBadhri Jagan Sridharan { 417c081ac42SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 418c081ac42SBadhri Jagan Sridharan 419c081ac42SBadhri Jagan Sridharan return regmap_update_bits(tcpci->regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_BIST_TM, 420c081ac42SBadhri Jagan Sridharan enable ? TCPC_TCPC_CTRL_BIST_TM : 0); 421c081ac42SBadhri Jagan Sridharan } 422c081ac42SBadhri Jagan Sridharan 423ae8a2ca8SHeikki Krogerus static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, 424ae8a2ca8SHeikki Krogerus enum typec_role role, enum typec_data_role data) 425ae8a2ca8SHeikki Krogerus { 426ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 427ae8a2ca8SHeikki Krogerus unsigned int reg; 428ae8a2ca8SHeikki Krogerus int ret; 429ae8a2ca8SHeikki Krogerus 430ae8a2ca8SHeikki Krogerus reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT; 431ae8a2ca8SHeikki Krogerus if (role == TYPEC_SOURCE) 432ae8a2ca8SHeikki Krogerus reg |= TCPC_MSG_HDR_INFO_PWR_ROLE; 433ae8a2ca8SHeikki Krogerus if (data == TYPEC_HOST) 434ae8a2ca8SHeikki Krogerus reg |= TCPC_MSG_HDR_INFO_DATA_ROLE; 435ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg); 436ae8a2ca8SHeikki Krogerus if (ret < 0) 437ae8a2ca8SHeikki Krogerus return ret; 438ae8a2ca8SHeikki Krogerus 439ae8a2ca8SHeikki Krogerus return 0; 440ae8a2ca8SHeikki Krogerus } 441ae8a2ca8SHeikki Krogerus 442ae8a2ca8SHeikki Krogerus static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) 443ae8a2ca8SHeikki Krogerus { 444ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 445ae8a2ca8SHeikki Krogerus unsigned int reg = 0; 446ae8a2ca8SHeikki Krogerus int ret; 447ae8a2ca8SHeikki Krogerus 448ae8a2ca8SHeikki Krogerus if (enable) 449ae8a2ca8SHeikki Krogerus reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; 450ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); 451ae8a2ca8SHeikki Krogerus if (ret < 0) 452ae8a2ca8SHeikki Krogerus return ret; 453ae8a2ca8SHeikki Krogerus 454ae8a2ca8SHeikki Krogerus return 0; 455ae8a2ca8SHeikki Krogerus } 456ae8a2ca8SHeikki Krogerus 457ae8a2ca8SHeikki Krogerus static int tcpci_get_vbus(struct tcpc_dev *tcpc) 458ae8a2ca8SHeikki Krogerus { 459ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 460ae8a2ca8SHeikki Krogerus unsigned int reg; 461ae8a2ca8SHeikki Krogerus int ret; 462ae8a2ca8SHeikki Krogerus 463ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); 464ae8a2ca8SHeikki Krogerus if (ret < 0) 465ae8a2ca8SHeikki Krogerus return ret; 466ae8a2ca8SHeikki Krogerus 467ae8a2ca8SHeikki Krogerus return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); 468ae8a2ca8SHeikki Krogerus } 469ae8a2ca8SHeikki Krogerus 470766c485bSBadhri Jagan Sridharan static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc) 471766c485bSBadhri Jagan Sridharan { 472766c485bSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 473766c485bSBadhri Jagan Sridharan unsigned int reg; 474766c485bSBadhri Jagan Sridharan int ret; 475766c485bSBadhri Jagan Sridharan 476766c485bSBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®); 477766c485bSBadhri Jagan Sridharan if (ret < 0) 478766c485bSBadhri Jagan Sridharan return false; 479766c485bSBadhri Jagan Sridharan 480766c485bSBadhri Jagan Sridharan return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V); 481766c485bSBadhri Jagan Sridharan } 482766c485bSBadhri Jagan Sridharan 483ae8a2ca8SHeikki Krogerus static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) 484ae8a2ca8SHeikki Krogerus { 485ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 486ae8a2ca8SHeikki Krogerus int ret; 487ae8a2ca8SHeikki Krogerus 488b9358a06SBadhri Jagan Sridharan if (tcpci->data->set_vbus) { 489b9358a06SBadhri Jagan Sridharan ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink); 490b9358a06SBadhri Jagan Sridharan /* Bypass when ret > 0 */ 491b9358a06SBadhri Jagan Sridharan if (ret != 0) 492b9358a06SBadhri Jagan Sridharan return ret < 0 ? ret : 0; 493b9358a06SBadhri Jagan Sridharan } 494b9358a06SBadhri Jagan Sridharan 495ae8a2ca8SHeikki Krogerus /* Disable both source and sink first before enabling anything */ 496ae8a2ca8SHeikki Krogerus 497ae8a2ca8SHeikki Krogerus if (!source) { 498ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 499ae8a2ca8SHeikki Krogerus TCPC_CMD_DISABLE_SRC_VBUS); 500ae8a2ca8SHeikki Krogerus if (ret < 0) 501ae8a2ca8SHeikki Krogerus return ret; 502ae8a2ca8SHeikki Krogerus } 503ae8a2ca8SHeikki Krogerus 504ae8a2ca8SHeikki Krogerus if (!sink) { 505ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 506ae8a2ca8SHeikki Krogerus TCPC_CMD_DISABLE_SINK_VBUS); 507ae8a2ca8SHeikki Krogerus if (ret < 0) 508ae8a2ca8SHeikki Krogerus return ret; 509ae8a2ca8SHeikki Krogerus } 510ae8a2ca8SHeikki Krogerus 511ae8a2ca8SHeikki Krogerus if (source) { 512ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 513ae8a2ca8SHeikki Krogerus TCPC_CMD_SRC_VBUS_DEFAULT); 514ae8a2ca8SHeikki Krogerus if (ret < 0) 515ae8a2ca8SHeikki Krogerus return ret; 516ae8a2ca8SHeikki Krogerus } 517ae8a2ca8SHeikki Krogerus 518ae8a2ca8SHeikki Krogerus if (sink) { 519ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 520ae8a2ca8SHeikki Krogerus TCPC_CMD_SINK_VBUS); 521ae8a2ca8SHeikki Krogerus if (ret < 0) 522ae8a2ca8SHeikki Krogerus return ret; 523ae8a2ca8SHeikki Krogerus } 524ae8a2ca8SHeikki Krogerus 525ae8a2ca8SHeikki Krogerus return 0; 526ae8a2ca8SHeikki Krogerus } 527ae8a2ca8SHeikki Krogerus 528e4a93780SBadhri Jagan Sridharan static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, 529e4a93780SBadhri Jagan Sridharan const struct pd_message *msg, unsigned int negotiated_rev) 530ae8a2ca8SHeikki Krogerus { 531ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 532ae8a2ca8SHeikki Krogerus u16 header = msg ? le16_to_cpu(msg->header) : 0; 533ae8a2ca8SHeikki Krogerus unsigned int reg, cnt; 534ae8a2ca8SHeikki Krogerus int ret; 535ae8a2ca8SHeikki Krogerus 536ae8a2ca8SHeikki Krogerus cnt = msg ? pd_header_cnt(header) * 4 : 0; 53719b65476SBadhri Jagan Sridharan /** 53819b65476SBadhri Jagan Sridharan * TCPCI spec forbids direct access of TCPC_TX_DATA. 53919b65476SBadhri Jagan Sridharan * But, since some of the chipsets offer this capability, 54019b65476SBadhri Jagan Sridharan * it's fair to support both. 54119b65476SBadhri Jagan Sridharan */ 54219b65476SBadhri Jagan Sridharan if (tcpci->data->TX_BUF_BYTE_x_hidden) { 54319b65476SBadhri Jagan Sridharan u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,}; 54419b65476SBadhri Jagan Sridharan u8 pos = 0; 54519b65476SBadhri Jagan Sridharan 54619b65476SBadhri Jagan Sridharan /* Payload + header + TCPC_TX_BYTE_CNT */ 54719b65476SBadhri Jagan Sridharan buf[pos++] = cnt + 2; 54819b65476SBadhri Jagan Sridharan 54919b65476SBadhri Jagan Sridharan if (msg) 55019b65476SBadhri Jagan Sridharan memcpy(&buf[pos], &msg->header, sizeof(msg->header)); 55119b65476SBadhri Jagan Sridharan 55219b65476SBadhri Jagan Sridharan pos += sizeof(header); 55319b65476SBadhri Jagan Sridharan 55419b65476SBadhri Jagan Sridharan if (cnt > 0) 55519b65476SBadhri Jagan Sridharan memcpy(&buf[pos], msg->payload, cnt); 55619b65476SBadhri Jagan Sridharan 55719b65476SBadhri Jagan Sridharan pos += cnt; 55819b65476SBadhri Jagan Sridharan ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos); 55919b65476SBadhri Jagan Sridharan if (ret < 0) 56019b65476SBadhri Jagan Sridharan return ret; 56119b65476SBadhri Jagan Sridharan } else { 562ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); 563ae8a2ca8SHeikki Krogerus if (ret < 0) 564ae8a2ca8SHeikki Krogerus return ret; 565ae8a2ca8SHeikki Krogerus 566ae8a2ca8SHeikki Krogerus ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); 567ae8a2ca8SHeikki Krogerus if (ret < 0) 568ae8a2ca8SHeikki Krogerus return ret; 569ae8a2ca8SHeikki Krogerus 570ae8a2ca8SHeikki Krogerus if (cnt > 0) { 57119b65476SBadhri Jagan Sridharan ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt); 572ae8a2ca8SHeikki Krogerus if (ret < 0) 573ae8a2ca8SHeikki Krogerus return ret; 574ae8a2ca8SHeikki Krogerus } 57519b65476SBadhri Jagan Sridharan } 576ae8a2ca8SHeikki Krogerus 577e4a93780SBadhri Jagan Sridharan /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ 578e4a93780SBadhri Jagan Sridharan reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) 579e4a93780SBadhri Jagan Sridharan << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); 580ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); 581ae8a2ca8SHeikki Krogerus if (ret < 0) 582ae8a2ca8SHeikki Krogerus return ret; 583ae8a2ca8SHeikki Krogerus 584ae8a2ca8SHeikki Krogerus return 0; 585ae8a2ca8SHeikki Krogerus } 586ae8a2ca8SHeikki Krogerus 587ae8a2ca8SHeikki Krogerus static int tcpci_init(struct tcpc_dev *tcpc) 588ae8a2ca8SHeikki Krogerus { 589ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 590ae8a2ca8SHeikki Krogerus unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */ 591ae8a2ca8SHeikki Krogerus unsigned int reg; 592ae8a2ca8SHeikki Krogerus int ret; 593ae8a2ca8SHeikki Krogerus 594ae8a2ca8SHeikki Krogerus while (time_before_eq(jiffies, timeout)) { 595ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); 596ae8a2ca8SHeikki Krogerus if (ret < 0) 597ae8a2ca8SHeikki Krogerus return ret; 598ae8a2ca8SHeikki Krogerus if (!(reg & TCPC_POWER_STATUS_UNINIT)) 599ae8a2ca8SHeikki Krogerus break; 600ae8a2ca8SHeikki Krogerus usleep_range(10000, 20000); 601ae8a2ca8SHeikki Krogerus } 602ae8a2ca8SHeikki Krogerus if (time_after(jiffies, timeout)) 603ae8a2ca8SHeikki Krogerus return -ETIMEDOUT; 604ae8a2ca8SHeikki Krogerus 605ae8a2ca8SHeikki Krogerus /* Handle vendor init */ 606ae8a2ca8SHeikki Krogerus if (tcpci->data->init) { 607ae8a2ca8SHeikki Krogerus ret = tcpci->data->init(tcpci, tcpci->data); 608ae8a2ca8SHeikki Krogerus if (ret < 0) 609ae8a2ca8SHeikki Krogerus return ret; 610ae8a2ca8SHeikki Krogerus } 611ae8a2ca8SHeikki Krogerus 612ae8a2ca8SHeikki Krogerus /* Clear all events */ 613ae8a2ca8SHeikki Krogerus ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff); 614ae8a2ca8SHeikki Krogerus if (ret < 0) 615ae8a2ca8SHeikki Krogerus return ret; 616ae8a2ca8SHeikki Krogerus 617ae8a2ca8SHeikki Krogerus if (tcpci->controls_vbus) 618ae8a2ca8SHeikki Krogerus reg = TCPC_POWER_STATUS_VBUS_PRES; 619ae8a2ca8SHeikki Krogerus else 620ae8a2ca8SHeikki Krogerus reg = 0; 621ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg); 622ae8a2ca8SHeikki Krogerus if (ret < 0) 623ae8a2ca8SHeikki Krogerus return ret; 624ae8a2ca8SHeikki Krogerus 625ae8a2ca8SHeikki Krogerus /* Enable Vbus detection */ 626ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 627ae8a2ca8SHeikki Krogerus TCPC_CMD_ENABLE_VBUS_DETECT); 628ae8a2ca8SHeikki Krogerus if (ret < 0) 629ae8a2ca8SHeikki Krogerus return ret; 630ae8a2ca8SHeikki Krogerus 631ae8a2ca8SHeikki Krogerus reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED | 632ae8a2ca8SHeikki Krogerus TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS | 633ae8a2ca8SHeikki Krogerus TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; 634ae8a2ca8SHeikki Krogerus if (tcpci->controls_vbus) 635ae8a2ca8SHeikki Krogerus reg |= TCPC_ALERT_POWER_STATUS; 636766c485bSBadhri Jagan Sridharan /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */ 637766c485bSBadhri Jagan Sridharan if (tcpci->data->vbus_vsafe0v) { 638766c485bSBadhri Jagan Sridharan reg |= TCPC_ALERT_EXTENDED_STATUS; 639766c485bSBadhri Jagan Sridharan ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK, 640766c485bSBadhri Jagan Sridharan TCPC_EXTENDED_STATUS_VSAFE0V); 641766c485bSBadhri Jagan Sridharan if (ret < 0) 642766c485bSBadhri Jagan Sridharan return ret; 643766c485bSBadhri Jagan Sridharan } 644ccb0beb4SXu Yang 645ccb0beb4SXu Yang tcpci->alert_mask = reg; 646ccb0beb4SXu Yang 647ae8a2ca8SHeikki Krogerus return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); 648ae8a2ca8SHeikki Krogerus } 649ae8a2ca8SHeikki Krogerus 650ae8a2ca8SHeikki Krogerus irqreturn_t tcpci_irq(struct tcpci *tcpci) 651ae8a2ca8SHeikki Krogerus { 652ae8a2ca8SHeikki Krogerus u16 status; 653766c485bSBadhri Jagan Sridharan int ret; 654766c485bSBadhri Jagan Sridharan unsigned int raw; 655ae8a2ca8SHeikki Krogerus 656ae8a2ca8SHeikki Krogerus tcpci_read16(tcpci, TCPC_ALERT, &status); 657ae8a2ca8SHeikki Krogerus 658ae8a2ca8SHeikki Krogerus /* 659ae8a2ca8SHeikki Krogerus * Clear alert status for everything except RX_STATUS, which shouldn't 660ae8a2ca8SHeikki Krogerus * be cleared until we have successfully retrieved message. 661ae8a2ca8SHeikki Krogerus */ 662ae8a2ca8SHeikki Krogerus if (status & ~TCPC_ALERT_RX_STATUS) 663ae8a2ca8SHeikki Krogerus tcpci_write16(tcpci, TCPC_ALERT, 664ae8a2ca8SHeikki Krogerus status & ~TCPC_ALERT_RX_STATUS); 665ae8a2ca8SHeikki Krogerus 666ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_CC_STATUS) 667ae8a2ca8SHeikki Krogerus tcpm_cc_change(tcpci->port); 668ae8a2ca8SHeikki Krogerus 669ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_POWER_STATUS) { 670766c485bSBadhri Jagan Sridharan regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw); 671ae8a2ca8SHeikki Krogerus /* 672ae8a2ca8SHeikki Krogerus * If power status mask has been reset, then the TCPC 673ae8a2ca8SHeikki Krogerus * has reset. 674ae8a2ca8SHeikki Krogerus */ 675766c485bSBadhri Jagan Sridharan if (raw == 0xff) 676ae8a2ca8SHeikki Krogerus tcpm_tcpc_reset(tcpci->port); 677ae8a2ca8SHeikki Krogerus else 678ae8a2ca8SHeikki Krogerus tcpm_vbus_change(tcpci->port); 679ae8a2ca8SHeikki Krogerus } 680ae8a2ca8SHeikki Krogerus 681ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_RX_STATUS) { 682ae8a2ca8SHeikki Krogerus struct pd_message msg; 683c215e48eSDouglas Gilbert unsigned int cnt, payload_cnt; 684ae8a2ca8SHeikki Krogerus u16 header; 685ae8a2ca8SHeikki Krogerus 686ae8a2ca8SHeikki Krogerus regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt); 687c215e48eSDouglas Gilbert /* 688c215e48eSDouglas Gilbert * 'cnt' corresponds to READABLE_BYTE_COUNT in section 4.4.14 689c215e48eSDouglas Gilbert * of the TCPCI spec [Rev 2.0 Ver 1.0 October 2017] and is 690c215e48eSDouglas Gilbert * defined in table 4-36 as one greater than the number of 691c215e48eSDouglas Gilbert * bytes received. And that number includes the header. So: 692c215e48eSDouglas Gilbert */ 693c215e48eSDouglas Gilbert if (cnt > 3) 694c215e48eSDouglas Gilbert payload_cnt = cnt - (1 + sizeof(msg.header)); 695c215e48eSDouglas Gilbert else 696c215e48eSDouglas Gilbert payload_cnt = 0; 697ae8a2ca8SHeikki Krogerus 698ae8a2ca8SHeikki Krogerus tcpci_read16(tcpci, TCPC_RX_HDR, &header); 699ae8a2ca8SHeikki Krogerus msg.header = cpu_to_le16(header); 700ae8a2ca8SHeikki Krogerus 701c215e48eSDouglas Gilbert if (WARN_ON(payload_cnt > sizeof(msg.payload))) 702c215e48eSDouglas Gilbert payload_cnt = sizeof(msg.payload); 703ae8a2ca8SHeikki Krogerus 704c215e48eSDouglas Gilbert if (payload_cnt > 0) 705ae8a2ca8SHeikki Krogerus regmap_raw_read(tcpci->regmap, TCPC_RX_DATA, 706c215e48eSDouglas Gilbert &msg.payload, payload_cnt); 707ae8a2ca8SHeikki Krogerus 708ae8a2ca8SHeikki Krogerus /* Read complete, clear RX status alert bit */ 709ae8a2ca8SHeikki Krogerus tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); 710ae8a2ca8SHeikki Krogerus 711ae8a2ca8SHeikki Krogerus tcpm_pd_receive(tcpci->port, &msg); 712ae8a2ca8SHeikki Krogerus } 713ae8a2ca8SHeikki Krogerus 71405300871SXu Yang if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { 715766c485bSBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw); 716766c485bSBadhri Jagan Sridharan if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V)) 717766c485bSBadhri Jagan Sridharan tcpm_vbus_change(tcpci->port); 718766c485bSBadhri Jagan Sridharan } 719766c485bSBadhri Jagan Sridharan 720ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_RX_HARD_RST) 721ae8a2ca8SHeikki Krogerus tcpm_pd_hard_reset(tcpci->port); 722ae8a2ca8SHeikki Krogerus 723ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_TX_SUCCESS) 724ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS); 725ae8a2ca8SHeikki Krogerus else if (status & TCPC_ALERT_TX_DISCARDED) 726ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED); 727ae8a2ca8SHeikki Krogerus else if (status & TCPC_ALERT_TX_FAILED) 728ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); 729ae8a2ca8SHeikki Krogerus 730ccb0beb4SXu Yang return IRQ_RETVAL(status & tcpci->alert_mask); 731ae8a2ca8SHeikki Krogerus } 732ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_irq); 733ae8a2ca8SHeikki Krogerus 734ae8a2ca8SHeikki Krogerus static irqreturn_t _tcpci_irq(int irq, void *dev_id) 735ae8a2ca8SHeikki Krogerus { 736ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip = dev_id; 737ae8a2ca8SHeikki Krogerus 738ae8a2ca8SHeikki Krogerus return tcpci_irq(chip->tcpci); 739ae8a2ca8SHeikki Krogerus } 740ae8a2ca8SHeikki Krogerus 741ae8a2ca8SHeikki Krogerus static const struct regmap_config tcpci_regmap_config = { 742ae8a2ca8SHeikki Krogerus .reg_bits = 8, 743ae8a2ca8SHeikki Krogerus .val_bits = 8, 744ae8a2ca8SHeikki Krogerus 745ae8a2ca8SHeikki Krogerus .max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */ 746ae8a2ca8SHeikki Krogerus }; 747ae8a2ca8SHeikki Krogerus 748ae8a2ca8SHeikki Krogerus static int tcpci_parse_config(struct tcpci *tcpci) 749ae8a2ca8SHeikki Krogerus { 750ae8a2ca8SHeikki Krogerus tcpci->controls_vbus = true; /* XXX */ 751ae8a2ca8SHeikki Krogerus 752ae8a2ca8SHeikki Krogerus tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev, 753ae8a2ca8SHeikki Krogerus "connector"); 754ae8a2ca8SHeikki Krogerus if (!tcpci->tcpc.fwnode) { 755ae8a2ca8SHeikki Krogerus dev_err(tcpci->dev, "Can't find connector node.\n"); 756ae8a2ca8SHeikki Krogerus return -EINVAL; 757ae8a2ca8SHeikki Krogerus } 758ae8a2ca8SHeikki Krogerus 759ae8a2ca8SHeikki Krogerus return 0; 760ae8a2ca8SHeikki Krogerus } 761ae8a2ca8SHeikki Krogerus 762ae8a2ca8SHeikki Krogerus struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) 763ae8a2ca8SHeikki Krogerus { 764ae8a2ca8SHeikki Krogerus struct tcpci *tcpci; 765ae8a2ca8SHeikki Krogerus int err; 766ae8a2ca8SHeikki Krogerus 767ae8a2ca8SHeikki Krogerus tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL); 768ae8a2ca8SHeikki Krogerus if (!tcpci) 769ae8a2ca8SHeikki Krogerus return ERR_PTR(-ENOMEM); 770ae8a2ca8SHeikki Krogerus 771ae8a2ca8SHeikki Krogerus tcpci->dev = dev; 772ae8a2ca8SHeikki Krogerus tcpci->data = data; 773ae8a2ca8SHeikki Krogerus tcpci->regmap = data->regmap; 774ae8a2ca8SHeikki Krogerus 775ae8a2ca8SHeikki Krogerus tcpci->tcpc.init = tcpci_init; 776ae8a2ca8SHeikki Krogerus tcpci->tcpc.get_vbus = tcpci_get_vbus; 777ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_vbus = tcpci_set_vbus; 778ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_cc = tcpci_set_cc; 7797257fbc7SBadhri Jagan Sridharan tcpci->tcpc.apply_rc = tcpci_apply_rc; 780ae8a2ca8SHeikki Krogerus tcpci->tcpc.get_cc = tcpci_get_cc; 781ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_polarity = tcpci_set_polarity; 782ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_vconn = tcpci_set_vconn; 7837893f9e1SHans de Goede tcpci->tcpc.start_toggling = tcpci_start_toggling; 784ae8a2ca8SHeikki Krogerus 785ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; 786ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_roles = tcpci_set_roles; 787ae8a2ca8SHeikki Krogerus tcpci->tcpc.pd_transmit = tcpci_pd_transmit; 788c081ac42SBadhri Jagan Sridharan tcpci->tcpc.set_bist_data = tcpci_set_bist_data; 78911121c24SBadhri Jagan Sridharan tcpci->tcpc.enable_frs = tcpci_enable_frs; 790a57d253fSBadhri Jagan Sridharan tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; 791372a3d0bSBadhri Jagan Sridharan tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; 792ae8a2ca8SHeikki Krogerus 793abc028a2SBadhri Jagan Sridharan if (tcpci->data->check_contaminant) 794abc028a2SBadhri Jagan Sridharan tcpci->tcpc.check_contaminant = tcpci_check_contaminant; 795abc028a2SBadhri Jagan Sridharan 796e1a97bf8SBadhri Jagan Sridharan if (tcpci->data->auto_discharge_disconnect) { 797e1a97bf8SBadhri Jagan Sridharan tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; 798e1a97bf8SBadhri Jagan Sridharan tcpci->tcpc.set_auto_vbus_discharge_threshold = 799e1a97bf8SBadhri Jagan Sridharan tcpci_set_auto_vbus_discharge_threshold; 8003b6c3d04SBadhri Jagan Sridharan regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_BLEED_DISCHARGE, 8013b6c3d04SBadhri Jagan Sridharan TCPC_POWER_CTRL_BLEED_DISCHARGE); 802e1a97bf8SBadhri Jagan Sridharan } 803e1a97bf8SBadhri Jagan Sridharan 804766c485bSBadhri Jagan Sridharan if (tcpci->data->vbus_vsafe0v) 805766c485bSBadhri Jagan Sridharan tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v; 806766c485bSBadhri Jagan Sridharan 807ae8a2ca8SHeikki Krogerus err = tcpci_parse_config(tcpci); 808ae8a2ca8SHeikki Krogerus if (err < 0) 809ae8a2ca8SHeikki Krogerus return ERR_PTR(err); 810ae8a2ca8SHeikki Krogerus 811ae8a2ca8SHeikki Krogerus tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); 8120384e87eSYang Yingliang if (IS_ERR(tcpci->port)) { 8130384e87eSYang Yingliang fwnode_handle_put(tcpci->tcpc.fwnode); 814ae8a2ca8SHeikki Krogerus return ERR_CAST(tcpci->port); 8150384e87eSYang Yingliang } 816ae8a2ca8SHeikki Krogerus 817ae8a2ca8SHeikki Krogerus return tcpci; 818ae8a2ca8SHeikki Krogerus } 819ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_register_port); 820ae8a2ca8SHeikki Krogerus 821ae8a2ca8SHeikki Krogerus void tcpci_unregister_port(struct tcpci *tcpci) 822ae8a2ca8SHeikki Krogerus { 823ae8a2ca8SHeikki Krogerus tcpm_unregister_port(tcpci->port); 8240384e87eSYang Yingliang fwnode_handle_put(tcpci->tcpc.fwnode); 825ae8a2ca8SHeikki Krogerus } 826ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_unregister_port); 827ae8a2ca8SHeikki Krogerus 828bdd0400dSUwe Kleine-König static int tcpci_probe(struct i2c_client *client) 829ae8a2ca8SHeikki Krogerus { 830ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip; 831ae8a2ca8SHeikki Krogerus int err; 832ae8a2ca8SHeikki Krogerus u16 val = 0; 833ae8a2ca8SHeikki Krogerus 834ae8a2ca8SHeikki Krogerus chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 835ae8a2ca8SHeikki Krogerus if (!chip) 836ae8a2ca8SHeikki Krogerus return -ENOMEM; 837ae8a2ca8SHeikki Krogerus 838ae8a2ca8SHeikki Krogerus chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config); 839ae8a2ca8SHeikki Krogerus if (IS_ERR(chip->data.regmap)) 840ae8a2ca8SHeikki Krogerus return PTR_ERR(chip->data.regmap); 841ae8a2ca8SHeikki Krogerus 842ae8a2ca8SHeikki Krogerus i2c_set_clientdata(client, chip); 843ae8a2ca8SHeikki Krogerus 844ae8a2ca8SHeikki Krogerus /* Disable chip interrupts before requesting irq */ 845ae8a2ca8SHeikki Krogerus err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val, 846ae8a2ca8SHeikki Krogerus sizeof(u16)); 847ae8a2ca8SHeikki Krogerus if (err < 0) 848ae8a2ca8SHeikki Krogerus return err; 849ae8a2ca8SHeikki Krogerus 850ae8a2ca8SHeikki Krogerus chip->tcpci = tcpci_register_port(&client->dev, &chip->data); 851ae8a2ca8SHeikki Krogerus if (IS_ERR(chip->tcpci)) 852ae8a2ca8SHeikki Krogerus return PTR_ERR(chip->tcpci); 853ae8a2ca8SHeikki Krogerus 854ae8a2ca8SHeikki Krogerus err = devm_request_threaded_irq(&client->dev, client->irq, NULL, 855ae8a2ca8SHeikki Krogerus _tcpci_irq, 856ccb0beb4SXu Yang IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW, 857ae8a2ca8SHeikki Krogerus dev_name(&client->dev), chip); 858ae8a2ca8SHeikki Krogerus if (err < 0) { 859ae8a2ca8SHeikki Krogerus tcpci_unregister_port(chip->tcpci); 860ae8a2ca8SHeikki Krogerus return err; 861ae8a2ca8SHeikki Krogerus } 862ae8a2ca8SHeikki Krogerus 863ae8a2ca8SHeikki Krogerus return 0; 864ae8a2ca8SHeikki Krogerus } 865ae8a2ca8SHeikki Krogerus 866ed5c2f5fSUwe Kleine-König static void tcpci_remove(struct i2c_client *client) 867ae8a2ca8SHeikki Krogerus { 868ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip = i2c_get_clientdata(client); 8693ba76256SJun Li int err; 8703ba76256SJun Li 8713ba76256SJun Li /* Disable chip interrupts before unregistering port */ 8723ba76256SJun Li err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); 8733ba76256SJun Li if (err < 0) 874bbc126aeSUwe Kleine-König dev_warn(&client->dev, "Failed to disable irqs (%pe)\n", ERR_PTR(err)); 875ae8a2ca8SHeikki Krogerus 876ae8a2ca8SHeikki Krogerus tcpci_unregister_port(chip->tcpci); 877ae8a2ca8SHeikki Krogerus } 878ae8a2ca8SHeikki Krogerus 879ae8a2ca8SHeikki Krogerus static const struct i2c_device_id tcpci_id[] = { 880ae8a2ca8SHeikki Krogerus { "tcpci", 0 }, 881ae8a2ca8SHeikki Krogerus { } 882ae8a2ca8SHeikki Krogerus }; 883ae8a2ca8SHeikki Krogerus MODULE_DEVICE_TABLE(i2c, tcpci_id); 884ae8a2ca8SHeikki Krogerus 885ae8a2ca8SHeikki Krogerus #ifdef CONFIG_OF 886ae8a2ca8SHeikki Krogerus static const struct of_device_id tcpci_of_match[] = { 887ae8a2ca8SHeikki Krogerus { .compatible = "nxp,ptn5110", }, 888ae8a2ca8SHeikki Krogerus {}, 889ae8a2ca8SHeikki Krogerus }; 890ae8a2ca8SHeikki Krogerus MODULE_DEVICE_TABLE(of, tcpci_of_match); 891ae8a2ca8SHeikki Krogerus #endif 892ae8a2ca8SHeikki Krogerus 893ae8a2ca8SHeikki Krogerus static struct i2c_driver tcpci_i2c_driver = { 894ae8a2ca8SHeikki Krogerus .driver = { 895ae8a2ca8SHeikki Krogerus .name = "tcpci", 896ae8a2ca8SHeikki Krogerus .of_match_table = of_match_ptr(tcpci_of_match), 897ae8a2ca8SHeikki Krogerus }, 898*7126a2aeSUwe Kleine-König .probe = tcpci_probe, 899ae8a2ca8SHeikki Krogerus .remove = tcpci_remove, 900ae8a2ca8SHeikki Krogerus .id_table = tcpci_id, 901ae8a2ca8SHeikki Krogerus }; 902ae8a2ca8SHeikki Krogerus module_i2c_driver(tcpci_i2c_driver); 903ae8a2ca8SHeikki Krogerus 904ae8a2ca8SHeikki Krogerus MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver"); 905ae8a2ca8SHeikki Krogerus MODULE_LICENSE("GPL"); 906