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> 16ae8a2ca8SHeikki Krogerus #include <linux/usb/tcpm.h> 17ae8a2ca8SHeikki Krogerus #include <linux/usb/typec.h> 18ae8a2ca8SHeikki Krogerus 19ae8a2ca8SHeikki Krogerus #include "tcpci.h" 20ae8a2ca8SHeikki Krogerus 21e4a93780SBadhri Jagan Sridharan #define PD_RETRY_COUNT_DEFAULT 3 22e4a93780SBadhri Jagan Sridharan #define PD_RETRY_COUNT_3_0_OR_HIGHER 2 23e1a97bf8SBadhri Jagan Sridharan #define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 244288debeSBadhri Jagan Sridharan #define VSINKPD_MIN_IR_DROP_MV 750 254288debeSBadhri Jagan Sridharan #define VSRC_NEW_MIN_PERCENT 95 264288debeSBadhri Jagan Sridharan #define VSRC_VALID_MIN_MV 500 274288debeSBadhri Jagan Sridharan #define VPPS_NEW_MIN_PERCENT 95 284288debeSBadhri Jagan Sridharan #define VPPS_VALID_MIN_MV 100 294288debeSBadhri Jagan Sridharan #define VSINKDISCONNECT_PD_MIN_PERCENT 90 30ae8a2ca8SHeikki Krogerus 31aecb1e45SBadhri Jagan Sridharan #define tcpc_presenting_rd(reg, cc) \ 3219c234a1SBadhri Jagan Sridharan (!(TCPC_ROLE_CTRL_DRP & (reg)) && \ 33aecb1e45SBadhri Jagan Sridharan (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \ 34aecb1e45SBadhri Jagan Sridharan (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT))) 3519c234a1SBadhri Jagan Sridharan 36ae8a2ca8SHeikki Krogerus struct tcpci { 37ae8a2ca8SHeikki Krogerus struct device *dev; 38ae8a2ca8SHeikki Krogerus 39ae8a2ca8SHeikki Krogerus struct tcpm_port *port; 40ae8a2ca8SHeikki Krogerus 41ae8a2ca8SHeikki Krogerus struct regmap *regmap; 42ae8a2ca8SHeikki Krogerus 43ae8a2ca8SHeikki Krogerus bool controls_vbus; 44ae8a2ca8SHeikki Krogerus 45ae8a2ca8SHeikki Krogerus struct tcpc_dev tcpc; 46ae8a2ca8SHeikki Krogerus struct tcpci_data *data; 47ae8a2ca8SHeikki Krogerus }; 48ae8a2ca8SHeikki Krogerus 49ae8a2ca8SHeikki Krogerus struct tcpci_chip { 50ae8a2ca8SHeikki Krogerus struct tcpci *tcpci; 51ae8a2ca8SHeikki Krogerus struct tcpci_data data; 52ae8a2ca8SHeikki Krogerus }; 53ae8a2ca8SHeikki Krogerus 5458ea326bSBadhri Jagan Sridharan struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci) 5558ea326bSBadhri Jagan Sridharan { 5658ea326bSBadhri Jagan Sridharan return tcpci->port; 5758ea326bSBadhri Jagan Sridharan } 5858ea326bSBadhri Jagan Sridharan EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port); 5958ea326bSBadhri Jagan Sridharan 60ae8a2ca8SHeikki Krogerus static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) 61ae8a2ca8SHeikki Krogerus { 62ae8a2ca8SHeikki Krogerus return container_of(tcpc, struct tcpci, tcpc); 63ae8a2ca8SHeikki Krogerus } 64ae8a2ca8SHeikki Krogerus 65ae8a2ca8SHeikki Krogerus static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val) 66ae8a2ca8SHeikki Krogerus { 67ae8a2ca8SHeikki Krogerus return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16)); 68ae8a2ca8SHeikki Krogerus } 69ae8a2ca8SHeikki Krogerus 70ae8a2ca8SHeikki Krogerus static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val) 71ae8a2ca8SHeikki Krogerus { 72ae8a2ca8SHeikki Krogerus return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16)); 73ae8a2ca8SHeikki Krogerus } 74ae8a2ca8SHeikki Krogerus 75ae8a2ca8SHeikki Krogerus static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) 76ae8a2ca8SHeikki Krogerus { 77ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 78ae8a2ca8SHeikki Krogerus unsigned int reg; 79ae8a2ca8SHeikki Krogerus int ret; 80ae8a2ca8SHeikki Krogerus 81ae8a2ca8SHeikki Krogerus switch (cc) { 82ae8a2ca8SHeikki Krogerus case TYPEC_CC_RA: 83ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) | 84ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT); 85ae8a2ca8SHeikki Krogerus break; 86ae8a2ca8SHeikki Krogerus case TYPEC_CC_RD: 87ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | 88ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); 89ae8a2ca8SHeikki Krogerus break; 90ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_DEF: 91ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 92ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 93ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_DEF << 94ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 95ae8a2ca8SHeikki Krogerus break; 96ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_1_5: 97ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 98ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 99ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_1_5 << 100ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 101ae8a2ca8SHeikki Krogerus break; 102ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_3_0: 103ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 104ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | 105ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_RP_VAL_3_0 << 106ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 107ae8a2ca8SHeikki Krogerus break; 108ae8a2ca8SHeikki Krogerus case TYPEC_CC_OPEN: 109ae8a2ca8SHeikki Krogerus default: 110ae8a2ca8SHeikki Krogerus reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) | 111ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); 112ae8a2ca8SHeikki Krogerus break; 113ae8a2ca8SHeikki Krogerus } 114ae8a2ca8SHeikki Krogerus 115ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 116ae8a2ca8SHeikki Krogerus if (ret < 0) 117ae8a2ca8SHeikki Krogerus return ret; 118ae8a2ca8SHeikki Krogerus 119ae8a2ca8SHeikki Krogerus return 0; 120ae8a2ca8SHeikki Krogerus } 121ae8a2ca8SHeikki Krogerus 122a0765597SWei Yongjun static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc, 123a0765597SWei Yongjun enum typec_cc_polarity polarity) 1247257fbc7SBadhri Jagan Sridharan { 1257257fbc7SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 1267257fbc7SBadhri Jagan Sridharan unsigned int reg; 1277257fbc7SBadhri Jagan Sridharan int ret; 1287257fbc7SBadhri Jagan Sridharan 1297257fbc7SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); 1307257fbc7SBadhri Jagan Sridharan if (ret < 0) 1317257fbc7SBadhri Jagan Sridharan return ret; 1327257fbc7SBadhri Jagan Sridharan 1337257fbc7SBadhri Jagan Sridharan /* 1347257fbc7SBadhri Jagan Sridharan * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on 1357257fbc7SBadhri Jagan Sridharan * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2. 1367257fbc7SBadhri Jagan Sridharan */ 1377257fbc7SBadhri Jagan Sridharan if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >> 1387257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC2_SHIFT) != 1397257fbc7SBadhri Jagan Sridharan ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >> 1407257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC1_SHIFT)) 1417257fbc7SBadhri Jagan Sridharan return 0; 1427257fbc7SBadhri Jagan Sridharan 1437257fbc7SBadhri Jagan Sridharan return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ? 1447257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT : 1457257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT, 1467257fbc7SBadhri Jagan Sridharan TCPC_ROLE_CTRL_CC_OPEN); 1477257fbc7SBadhri Jagan Sridharan } 1487257fbc7SBadhri Jagan Sridharan 1497893f9e1SHans de Goede static int tcpci_start_toggling(struct tcpc_dev *tcpc, 1507893f9e1SHans de Goede enum typec_port_type port_type, 151ae8a2ca8SHeikki Krogerus enum typec_cc_status cc) 152ae8a2ca8SHeikki Krogerus { 153ae8a2ca8SHeikki Krogerus int ret; 154ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 155ae8a2ca8SHeikki Krogerus unsigned int reg = TCPC_ROLE_CTRL_DRP; 156ae8a2ca8SHeikki Krogerus 1577893f9e1SHans de Goede if (port_type != TYPEC_PORT_DRP) 1587893f9e1SHans de Goede return -EOPNOTSUPP; 1597893f9e1SHans de Goede 160ae8a2ca8SHeikki Krogerus /* Handle vendor drp toggling */ 161ae8a2ca8SHeikki Krogerus if (tcpci->data->start_drp_toggling) { 162ae8a2ca8SHeikki Krogerus ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); 163ae8a2ca8SHeikki Krogerus if (ret < 0) 164ae8a2ca8SHeikki Krogerus return ret; 165ae8a2ca8SHeikki Krogerus } 166ae8a2ca8SHeikki Krogerus 167ae8a2ca8SHeikki Krogerus switch (cc) { 168ae8a2ca8SHeikki Krogerus default: 169ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_DEF: 170ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << 171ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 172ae8a2ca8SHeikki Krogerus break; 173ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_1_5: 174ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << 175ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 176ae8a2ca8SHeikki Krogerus break; 177ae8a2ca8SHeikki Krogerus case TYPEC_CC_RP_3_0: 178ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << 179ae8a2ca8SHeikki Krogerus TCPC_ROLE_CTRL_RP_VAL_SHIFT); 180ae8a2ca8SHeikki Krogerus break; 181ae8a2ca8SHeikki Krogerus } 182ae8a2ca8SHeikki Krogerus 183ae8a2ca8SHeikki Krogerus if (cc == TYPEC_CC_RD) 184ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | 185ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); 186ae8a2ca8SHeikki Krogerus else 187ae8a2ca8SHeikki Krogerus reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | 188ae8a2ca8SHeikki Krogerus (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); 189ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 190ae8a2ca8SHeikki Krogerus if (ret < 0) 191ae8a2ca8SHeikki Krogerus return ret; 192ae8a2ca8SHeikki Krogerus return regmap_write(tcpci->regmap, TCPC_COMMAND, 193ae8a2ca8SHeikki Krogerus TCPC_CMD_LOOK4CONNECTION); 194ae8a2ca8SHeikki Krogerus } 195ae8a2ca8SHeikki Krogerus 196ae8a2ca8SHeikki Krogerus static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) 197ae8a2ca8SHeikki Krogerus { 198ae8a2ca8SHeikki Krogerus switch (cc) { 199ae8a2ca8SHeikki Krogerus case 0x1: 200ae8a2ca8SHeikki Krogerus return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA; 201ae8a2ca8SHeikki Krogerus case 0x2: 202ae8a2ca8SHeikki Krogerus return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD; 203ae8a2ca8SHeikki Krogerus case 0x3: 204ae8a2ca8SHeikki Krogerus if (sink) 205ae8a2ca8SHeikki Krogerus return TYPEC_CC_RP_3_0; 206df561f66SGustavo A. R. Silva fallthrough; 207ae8a2ca8SHeikki Krogerus case 0x0: 208ae8a2ca8SHeikki Krogerus default: 209ae8a2ca8SHeikki Krogerus return TYPEC_CC_OPEN; 210ae8a2ca8SHeikki Krogerus } 211ae8a2ca8SHeikki Krogerus } 212ae8a2ca8SHeikki Krogerus 213ae8a2ca8SHeikki Krogerus static int tcpci_get_cc(struct tcpc_dev *tcpc, 214ae8a2ca8SHeikki Krogerus enum typec_cc_status *cc1, enum typec_cc_status *cc2) 215ae8a2ca8SHeikki Krogerus { 216ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 21719c234a1SBadhri Jagan Sridharan unsigned int reg, role_control; 218ae8a2ca8SHeikki Krogerus int ret; 219ae8a2ca8SHeikki Krogerus 22019c234a1SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &role_control); 22119c234a1SBadhri Jagan Sridharan if (ret < 0) 22219c234a1SBadhri Jagan Sridharan return ret; 22319c234a1SBadhri Jagan Sridharan 224ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, ®); 225ae8a2ca8SHeikki Krogerus if (ret < 0) 226ae8a2ca8SHeikki Krogerus return ret; 227ae8a2ca8SHeikki Krogerus 228ae8a2ca8SHeikki Krogerus *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & 229ae8a2ca8SHeikki Krogerus TCPC_CC_STATUS_CC1_MASK, 23019c234a1SBadhri Jagan Sridharan reg & TCPC_CC_STATUS_TERM || 231aecb1e45SBadhri Jagan Sridharan tcpc_presenting_rd(role_control, CC1)); 232ae8a2ca8SHeikki Krogerus *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & 233ae8a2ca8SHeikki Krogerus TCPC_CC_STATUS_CC2_MASK, 23419c234a1SBadhri Jagan Sridharan reg & TCPC_CC_STATUS_TERM || 235aecb1e45SBadhri Jagan Sridharan tcpc_presenting_rd(role_control, CC2)); 236ae8a2ca8SHeikki Krogerus 237ae8a2ca8SHeikki Krogerus return 0; 238ae8a2ca8SHeikki Krogerus } 239ae8a2ca8SHeikki Krogerus 240ae8a2ca8SHeikki Krogerus static int tcpci_set_polarity(struct tcpc_dev *tcpc, 241ae8a2ca8SHeikki Krogerus enum typec_cc_polarity polarity) 242ae8a2ca8SHeikki Krogerus { 243ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 244ae8a2ca8SHeikki Krogerus unsigned int reg; 245ae8a2ca8SHeikki Krogerus int ret; 24657ce6466SBadhri Jagan Sridharan enum typec_cc_status cc1, cc2; 247ae8a2ca8SHeikki Krogerus 24857ce6466SBadhri Jagan Sridharan /* Obtain Rp setting from role control */ 249ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); 250ae8a2ca8SHeikki Krogerus if (ret < 0) 251ae8a2ca8SHeikki Krogerus return ret; 252ae8a2ca8SHeikki Krogerus 25357ce6466SBadhri Jagan Sridharan ret = tcpci_get_cc(tcpc, &cc1, &cc2); 25457ce6466SBadhri Jagan Sridharan if (ret < 0) 25557ce6466SBadhri Jagan Sridharan return ret; 25657ce6466SBadhri Jagan Sridharan 25757ce6466SBadhri Jagan Sridharan /* 25857ce6466SBadhri Jagan Sridharan * When port has drp toggling enabled, ROLE_CONTROL would only have the initial 25957ce6466SBadhri Jagan Sridharan * terminations for the toggling and does not indicate the final cc 26057ce6466SBadhri Jagan Sridharan * terminations when ConnectionResult is 0 i.e. drp toggling stops and 26157ce6466SBadhri Jagan Sridharan * the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the 26257ce6466SBadhri Jagan Sridharan * terminations seen. The port role is then used to set the cc terminations. 26357ce6466SBadhri Jagan Sridharan */ 26457ce6466SBadhri Jagan Sridharan if (reg & TCPC_ROLE_CTRL_DRP) { 26557ce6466SBadhri Jagan Sridharan /* Disable DRP for the OPEN setting to take effect */ 26657ce6466SBadhri Jagan Sridharan reg = reg & ~TCPC_ROLE_CTRL_DRP; 26757ce6466SBadhri Jagan Sridharan 26857ce6466SBadhri Jagan Sridharan if (polarity == TYPEC_POLARITY_CC2) { 26957ce6466SBadhri Jagan Sridharan reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); 27057ce6466SBadhri Jagan Sridharan /* Local port is source */ 27157ce6466SBadhri Jagan Sridharan if (cc2 == TYPEC_CC_RD) 27257ce6466SBadhri Jagan Sridharan /* Role control would have the Rp setting when DRP was enabled */ 27357ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT; 27457ce6466SBadhri Jagan Sridharan else 27557ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT; 27657ce6466SBadhri Jagan Sridharan } else { 27757ce6466SBadhri Jagan Sridharan reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); 27857ce6466SBadhri Jagan Sridharan /* Local port is source */ 27957ce6466SBadhri Jagan Sridharan if (cc1 == TYPEC_CC_RD) 28057ce6466SBadhri Jagan Sridharan /* Role control would have the Rp setting when DRP was enabled */ 28157ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT; 28257ce6466SBadhri Jagan Sridharan else 28357ce6466SBadhri Jagan Sridharan reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT; 28457ce6466SBadhri Jagan Sridharan } 28557ce6466SBadhri Jagan Sridharan } 28657ce6466SBadhri Jagan Sridharan 287ae8a2ca8SHeikki Krogerus if (polarity == TYPEC_POLARITY_CC2) 288ae8a2ca8SHeikki Krogerus reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT; 289ae8a2ca8SHeikki Krogerus else 290ae8a2ca8SHeikki Krogerus reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT; 291ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); 292ae8a2ca8SHeikki Krogerus if (ret < 0) 293ae8a2ca8SHeikki Krogerus return ret; 294ae8a2ca8SHeikki Krogerus 295ae8a2ca8SHeikki Krogerus return regmap_write(tcpci->regmap, TCPC_TCPC_CTRL, 296ae8a2ca8SHeikki Krogerus (polarity == TYPEC_POLARITY_CC2) ? 297ae8a2ca8SHeikki Krogerus TCPC_TCPC_CTRL_ORIENTATION : 0); 298ae8a2ca8SHeikki Krogerus } 299ae8a2ca8SHeikki Krogerus 300372a3d0bSBadhri Jagan Sridharan static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable) 301372a3d0bSBadhri Jagan Sridharan { 302372a3d0bSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 303372a3d0bSBadhri Jagan Sridharan 304372a3d0bSBadhri Jagan Sridharan if (tcpci->data->set_partner_usb_comm_capable) 305372a3d0bSBadhri Jagan Sridharan tcpci->data->set_partner_usb_comm_capable(tcpci, tcpci->data, capable); 306372a3d0bSBadhri Jagan Sridharan } 307372a3d0bSBadhri Jagan Sridharan 308ae8a2ca8SHeikki Krogerus static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) 309ae8a2ca8SHeikki Krogerus { 310ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 311ae8a2ca8SHeikki Krogerus int ret; 312ae8a2ca8SHeikki Krogerus 313ae8a2ca8SHeikki Krogerus /* Handle vendor set vconn */ 314ae8a2ca8SHeikki Krogerus if (tcpci->data->set_vconn) { 315ae8a2ca8SHeikki Krogerus ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable); 316ae8a2ca8SHeikki Krogerus if (ret < 0) 317ae8a2ca8SHeikki Krogerus return ret; 318ae8a2ca8SHeikki Krogerus } 319ae8a2ca8SHeikki Krogerus 320ae8a2ca8SHeikki Krogerus return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, 321ae8a2ca8SHeikki Krogerus TCPC_POWER_CTRL_VCONN_ENABLE, 322ae8a2ca8SHeikki Krogerus enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); 323ae8a2ca8SHeikki Krogerus } 324ae8a2ca8SHeikki Krogerus 325e1a97bf8SBadhri Jagan Sridharan static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) 326e1a97bf8SBadhri Jagan Sridharan { 327e1a97bf8SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 328e1a97bf8SBadhri Jagan Sridharan int ret; 329e1a97bf8SBadhri Jagan Sridharan 330e1a97bf8SBadhri Jagan Sridharan ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, 331e1a97bf8SBadhri Jagan Sridharan enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0); 332e1a97bf8SBadhri Jagan Sridharan return ret; 333e1a97bf8SBadhri Jagan Sridharan } 334e1a97bf8SBadhri Jagan Sridharan 335e1a97bf8SBadhri Jagan Sridharan static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, 336e1a97bf8SBadhri Jagan Sridharan bool pps_active, u32 requested_vbus_voltage_mv) 337e1a97bf8SBadhri Jagan Sridharan { 338e1a97bf8SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 339e1a97bf8SBadhri Jagan Sridharan unsigned int pwr_ctrl, threshold = 0; 340e1a97bf8SBadhri Jagan Sridharan int ret; 341e1a97bf8SBadhri Jagan Sridharan 342e1a97bf8SBadhri Jagan Sridharan /* 343e1a97bf8SBadhri Jagan Sridharan * Indicates that vbus is going to go away due PR_SWAP, hard reset etc. 344e1a97bf8SBadhri Jagan Sridharan * Do not discharge vbus here. 345e1a97bf8SBadhri Jagan Sridharan */ 346e1a97bf8SBadhri Jagan Sridharan if (requested_vbus_voltage_mv == 0) 347e1a97bf8SBadhri Jagan Sridharan goto write_thresh; 348e1a97bf8SBadhri Jagan Sridharan 349e1a97bf8SBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl); 350e1a97bf8SBadhri Jagan Sridharan if (ret < 0) 351e1a97bf8SBadhri Jagan Sridharan return ret; 352e1a97bf8SBadhri Jagan Sridharan 353e1a97bf8SBadhri Jagan Sridharan if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) { 354e1a97bf8SBadhri Jagan Sridharan /* To prevent disconnect when the source is fast role swap is capable. */ 355e1a97bf8SBadhri Jagan Sridharan threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; 356e1a97bf8SBadhri Jagan Sridharan } else if (mode == TYPEC_PWR_MODE_PD) { 357e1a97bf8SBadhri Jagan Sridharan if (pps_active) 3584288debeSBadhri Jagan Sridharan threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - 3594288debeSBadhri Jagan Sridharan VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) * 3604288debeSBadhri Jagan Sridharan VSINKDISCONNECT_PD_MIN_PERCENT / 100; 361e1a97bf8SBadhri Jagan Sridharan else 3624288debeSBadhri Jagan Sridharan threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - 3634288debeSBadhri Jagan Sridharan VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) * 3644288debeSBadhri Jagan Sridharan VSINKDISCONNECT_PD_MIN_PERCENT / 100; 365e1a97bf8SBadhri Jagan Sridharan } else { 366e1a97bf8SBadhri Jagan Sridharan /* 3.5V for non-pd sink */ 367e1a97bf8SBadhri Jagan Sridharan threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; 368e1a97bf8SBadhri Jagan Sridharan } 369e1a97bf8SBadhri Jagan Sridharan 370e1a97bf8SBadhri Jagan Sridharan threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV; 371e1a97bf8SBadhri Jagan Sridharan 372e1a97bf8SBadhri Jagan Sridharan if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) 373e1a97bf8SBadhri Jagan Sridharan return -EINVAL; 374e1a97bf8SBadhri Jagan Sridharan 375e1a97bf8SBadhri Jagan Sridharan write_thresh: 376e1a97bf8SBadhri Jagan Sridharan return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold); 377e1a97bf8SBadhri Jagan Sridharan } 378e1a97bf8SBadhri Jagan Sridharan 37911121c24SBadhri Jagan Sridharan static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) 38011121c24SBadhri Jagan Sridharan { 38111121c24SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 38211121c24SBadhri Jagan Sridharan int ret; 38311121c24SBadhri Jagan Sridharan 38411121c24SBadhri Jagan Sridharan /* To prevent disconnect during FRS, set disconnect threshold to 3.5V */ 38511121c24SBadhri Jagan Sridharan ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c); 38611121c24SBadhri Jagan Sridharan if (ret < 0) 38711121c24SBadhri Jagan Sridharan return ret; 38811121c24SBadhri Jagan Sridharan 38911121c24SBadhri Jagan Sridharan ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ? 39011121c24SBadhri Jagan Sridharan TCPC_FAST_ROLE_SWAP_EN : 0); 39111121c24SBadhri Jagan Sridharan 39211121c24SBadhri Jagan Sridharan return ret; 39311121c24SBadhri Jagan Sridharan } 39411121c24SBadhri Jagan Sridharan 395a57d253fSBadhri Jagan Sridharan static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) 396a57d253fSBadhri Jagan Sridharan { 397a57d253fSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(dev); 398a57d253fSBadhri Jagan Sridharan 399a57d253fSBadhri Jagan Sridharan if (tcpci->data->frs_sourcing_vbus) 400a57d253fSBadhri Jagan Sridharan tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); 401a57d253fSBadhri Jagan Sridharan } 402a57d253fSBadhri Jagan Sridharan 403c081ac42SBadhri Jagan Sridharan static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) 404c081ac42SBadhri Jagan Sridharan { 405c081ac42SBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 406c081ac42SBadhri Jagan Sridharan 407c081ac42SBadhri Jagan Sridharan return regmap_update_bits(tcpci->regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_BIST_TM, 408c081ac42SBadhri Jagan Sridharan enable ? TCPC_TCPC_CTRL_BIST_TM : 0); 409c081ac42SBadhri Jagan Sridharan } 410c081ac42SBadhri Jagan Sridharan 411ae8a2ca8SHeikki Krogerus static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, 412ae8a2ca8SHeikki Krogerus enum typec_role role, enum typec_data_role data) 413ae8a2ca8SHeikki Krogerus { 414ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 415ae8a2ca8SHeikki Krogerus unsigned int reg; 416ae8a2ca8SHeikki Krogerus int ret; 417ae8a2ca8SHeikki Krogerus 418ae8a2ca8SHeikki Krogerus reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT; 419ae8a2ca8SHeikki Krogerus if (role == TYPEC_SOURCE) 420ae8a2ca8SHeikki Krogerus reg |= TCPC_MSG_HDR_INFO_PWR_ROLE; 421ae8a2ca8SHeikki Krogerus if (data == TYPEC_HOST) 422ae8a2ca8SHeikki Krogerus reg |= TCPC_MSG_HDR_INFO_DATA_ROLE; 423ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg); 424ae8a2ca8SHeikki Krogerus if (ret < 0) 425ae8a2ca8SHeikki Krogerus return ret; 426ae8a2ca8SHeikki Krogerus 427ae8a2ca8SHeikki Krogerus return 0; 428ae8a2ca8SHeikki Krogerus } 429ae8a2ca8SHeikki Krogerus 430ae8a2ca8SHeikki Krogerus static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) 431ae8a2ca8SHeikki Krogerus { 432ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 433ae8a2ca8SHeikki Krogerus unsigned int reg = 0; 434ae8a2ca8SHeikki Krogerus int ret; 435ae8a2ca8SHeikki Krogerus 436ae8a2ca8SHeikki Krogerus if (enable) 437ae8a2ca8SHeikki Krogerus reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; 438ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); 439ae8a2ca8SHeikki Krogerus if (ret < 0) 440ae8a2ca8SHeikki Krogerus return ret; 441ae8a2ca8SHeikki Krogerus 442ae8a2ca8SHeikki Krogerus return 0; 443ae8a2ca8SHeikki Krogerus } 444ae8a2ca8SHeikki Krogerus 445ae8a2ca8SHeikki Krogerus static int tcpci_get_vbus(struct tcpc_dev *tcpc) 446ae8a2ca8SHeikki Krogerus { 447ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 448ae8a2ca8SHeikki Krogerus unsigned int reg; 449ae8a2ca8SHeikki Krogerus int ret; 450ae8a2ca8SHeikki Krogerus 451ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); 452ae8a2ca8SHeikki Krogerus if (ret < 0) 453ae8a2ca8SHeikki Krogerus return ret; 454ae8a2ca8SHeikki Krogerus 455ae8a2ca8SHeikki Krogerus return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); 456ae8a2ca8SHeikki Krogerus } 457ae8a2ca8SHeikki Krogerus 458766c485bSBadhri Jagan Sridharan static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc) 459766c485bSBadhri Jagan Sridharan { 460766c485bSBadhri Jagan Sridharan struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 461766c485bSBadhri Jagan Sridharan unsigned int reg; 462766c485bSBadhri Jagan Sridharan int ret; 463766c485bSBadhri Jagan Sridharan 464766c485bSBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®); 465766c485bSBadhri Jagan Sridharan if (ret < 0) 466766c485bSBadhri Jagan Sridharan return false; 467766c485bSBadhri Jagan Sridharan 468766c485bSBadhri Jagan Sridharan return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V); 469766c485bSBadhri Jagan Sridharan } 470766c485bSBadhri Jagan Sridharan 471ae8a2ca8SHeikki Krogerus static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) 472ae8a2ca8SHeikki Krogerus { 473ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 474ae8a2ca8SHeikki Krogerus int ret; 475ae8a2ca8SHeikki Krogerus 476b9358a06SBadhri Jagan Sridharan if (tcpci->data->set_vbus) { 477b9358a06SBadhri Jagan Sridharan ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink); 478b9358a06SBadhri Jagan Sridharan /* Bypass when ret > 0 */ 479b9358a06SBadhri Jagan Sridharan if (ret != 0) 480b9358a06SBadhri Jagan Sridharan return ret < 0 ? ret : 0; 481b9358a06SBadhri Jagan Sridharan } 482b9358a06SBadhri Jagan Sridharan 483ae8a2ca8SHeikki Krogerus /* Disable both source and sink first before enabling anything */ 484ae8a2ca8SHeikki Krogerus 485ae8a2ca8SHeikki Krogerus if (!source) { 486ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 487ae8a2ca8SHeikki Krogerus TCPC_CMD_DISABLE_SRC_VBUS); 488ae8a2ca8SHeikki Krogerus if (ret < 0) 489ae8a2ca8SHeikki Krogerus return ret; 490ae8a2ca8SHeikki Krogerus } 491ae8a2ca8SHeikki Krogerus 492ae8a2ca8SHeikki Krogerus if (!sink) { 493ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 494ae8a2ca8SHeikki Krogerus TCPC_CMD_DISABLE_SINK_VBUS); 495ae8a2ca8SHeikki Krogerus if (ret < 0) 496ae8a2ca8SHeikki Krogerus return ret; 497ae8a2ca8SHeikki Krogerus } 498ae8a2ca8SHeikki Krogerus 499ae8a2ca8SHeikki Krogerus if (source) { 500ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 501ae8a2ca8SHeikki Krogerus TCPC_CMD_SRC_VBUS_DEFAULT); 502ae8a2ca8SHeikki Krogerus if (ret < 0) 503ae8a2ca8SHeikki Krogerus return ret; 504ae8a2ca8SHeikki Krogerus } 505ae8a2ca8SHeikki Krogerus 506ae8a2ca8SHeikki Krogerus if (sink) { 507ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 508ae8a2ca8SHeikki Krogerus TCPC_CMD_SINK_VBUS); 509ae8a2ca8SHeikki Krogerus if (ret < 0) 510ae8a2ca8SHeikki Krogerus return ret; 511ae8a2ca8SHeikki Krogerus } 512ae8a2ca8SHeikki Krogerus 513ae8a2ca8SHeikki Krogerus return 0; 514ae8a2ca8SHeikki Krogerus } 515ae8a2ca8SHeikki Krogerus 516e4a93780SBadhri Jagan Sridharan static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, 517e4a93780SBadhri Jagan Sridharan const struct pd_message *msg, unsigned int negotiated_rev) 518ae8a2ca8SHeikki Krogerus { 519ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 520ae8a2ca8SHeikki Krogerus u16 header = msg ? le16_to_cpu(msg->header) : 0; 521ae8a2ca8SHeikki Krogerus unsigned int reg, cnt; 522ae8a2ca8SHeikki Krogerus int ret; 523ae8a2ca8SHeikki Krogerus 524ae8a2ca8SHeikki Krogerus cnt = msg ? pd_header_cnt(header) * 4 : 0; 52519b65476SBadhri Jagan Sridharan /** 52619b65476SBadhri Jagan Sridharan * TCPCI spec forbids direct access of TCPC_TX_DATA. 52719b65476SBadhri Jagan Sridharan * But, since some of the chipsets offer this capability, 52819b65476SBadhri Jagan Sridharan * it's fair to support both. 52919b65476SBadhri Jagan Sridharan */ 53019b65476SBadhri Jagan Sridharan if (tcpci->data->TX_BUF_BYTE_x_hidden) { 53119b65476SBadhri Jagan Sridharan u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,}; 53219b65476SBadhri Jagan Sridharan u8 pos = 0; 53319b65476SBadhri Jagan Sridharan 53419b65476SBadhri Jagan Sridharan /* Payload + header + TCPC_TX_BYTE_CNT */ 53519b65476SBadhri Jagan Sridharan buf[pos++] = cnt + 2; 53619b65476SBadhri Jagan Sridharan 53719b65476SBadhri Jagan Sridharan if (msg) 53819b65476SBadhri Jagan Sridharan memcpy(&buf[pos], &msg->header, sizeof(msg->header)); 53919b65476SBadhri Jagan Sridharan 54019b65476SBadhri Jagan Sridharan pos += sizeof(header); 54119b65476SBadhri Jagan Sridharan 54219b65476SBadhri Jagan Sridharan if (cnt > 0) 54319b65476SBadhri Jagan Sridharan memcpy(&buf[pos], msg->payload, cnt); 54419b65476SBadhri Jagan Sridharan 54519b65476SBadhri Jagan Sridharan pos += cnt; 54619b65476SBadhri Jagan Sridharan ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos); 54719b65476SBadhri Jagan Sridharan if (ret < 0) 54819b65476SBadhri Jagan Sridharan return ret; 54919b65476SBadhri Jagan Sridharan } else { 550ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); 551ae8a2ca8SHeikki Krogerus if (ret < 0) 552ae8a2ca8SHeikki Krogerus return ret; 553ae8a2ca8SHeikki Krogerus 554ae8a2ca8SHeikki Krogerus ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); 555ae8a2ca8SHeikki Krogerus if (ret < 0) 556ae8a2ca8SHeikki Krogerus return ret; 557ae8a2ca8SHeikki Krogerus 558ae8a2ca8SHeikki Krogerus if (cnt > 0) { 55919b65476SBadhri Jagan Sridharan ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt); 560ae8a2ca8SHeikki Krogerus if (ret < 0) 561ae8a2ca8SHeikki Krogerus return ret; 562ae8a2ca8SHeikki Krogerus } 56319b65476SBadhri Jagan Sridharan } 564ae8a2ca8SHeikki Krogerus 565e4a93780SBadhri Jagan Sridharan /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ 566e4a93780SBadhri Jagan Sridharan reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) 567e4a93780SBadhri Jagan Sridharan << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); 568ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); 569ae8a2ca8SHeikki Krogerus if (ret < 0) 570ae8a2ca8SHeikki Krogerus return ret; 571ae8a2ca8SHeikki Krogerus 572ae8a2ca8SHeikki Krogerus return 0; 573ae8a2ca8SHeikki Krogerus } 574ae8a2ca8SHeikki Krogerus 575ae8a2ca8SHeikki Krogerus static int tcpci_init(struct tcpc_dev *tcpc) 576ae8a2ca8SHeikki Krogerus { 577ae8a2ca8SHeikki Krogerus struct tcpci *tcpci = tcpc_to_tcpci(tcpc); 578ae8a2ca8SHeikki Krogerus unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */ 579ae8a2ca8SHeikki Krogerus unsigned int reg; 580ae8a2ca8SHeikki Krogerus int ret; 581ae8a2ca8SHeikki Krogerus 582ae8a2ca8SHeikki Krogerus while (time_before_eq(jiffies, timeout)) { 583ae8a2ca8SHeikki Krogerus ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); 584ae8a2ca8SHeikki Krogerus if (ret < 0) 585ae8a2ca8SHeikki Krogerus return ret; 586ae8a2ca8SHeikki Krogerus if (!(reg & TCPC_POWER_STATUS_UNINIT)) 587ae8a2ca8SHeikki Krogerus break; 588ae8a2ca8SHeikki Krogerus usleep_range(10000, 20000); 589ae8a2ca8SHeikki Krogerus } 590ae8a2ca8SHeikki Krogerus if (time_after(jiffies, timeout)) 591ae8a2ca8SHeikki Krogerus return -ETIMEDOUT; 592ae8a2ca8SHeikki Krogerus 593ae8a2ca8SHeikki Krogerus /* Handle vendor init */ 594ae8a2ca8SHeikki Krogerus if (tcpci->data->init) { 595ae8a2ca8SHeikki Krogerus ret = tcpci->data->init(tcpci, tcpci->data); 596ae8a2ca8SHeikki Krogerus if (ret < 0) 597ae8a2ca8SHeikki Krogerus return ret; 598ae8a2ca8SHeikki Krogerus } 599ae8a2ca8SHeikki Krogerus 600ae8a2ca8SHeikki Krogerus /* Clear all events */ 601ae8a2ca8SHeikki Krogerus ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff); 602ae8a2ca8SHeikki Krogerus if (ret < 0) 603ae8a2ca8SHeikki Krogerus return ret; 604ae8a2ca8SHeikki Krogerus 605ae8a2ca8SHeikki Krogerus if (tcpci->controls_vbus) 606ae8a2ca8SHeikki Krogerus reg = TCPC_POWER_STATUS_VBUS_PRES; 607ae8a2ca8SHeikki Krogerus else 608ae8a2ca8SHeikki Krogerus reg = 0; 609ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg); 610ae8a2ca8SHeikki Krogerus if (ret < 0) 611ae8a2ca8SHeikki Krogerus return ret; 612ae8a2ca8SHeikki Krogerus 613ae8a2ca8SHeikki Krogerus /* Enable Vbus detection */ 614ae8a2ca8SHeikki Krogerus ret = regmap_write(tcpci->regmap, TCPC_COMMAND, 615ae8a2ca8SHeikki Krogerus TCPC_CMD_ENABLE_VBUS_DETECT); 616ae8a2ca8SHeikki Krogerus if (ret < 0) 617ae8a2ca8SHeikki Krogerus return ret; 618ae8a2ca8SHeikki Krogerus 619ae8a2ca8SHeikki Krogerus reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED | 620ae8a2ca8SHeikki Krogerus TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS | 621ae8a2ca8SHeikki Krogerus TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; 622ae8a2ca8SHeikki Krogerus if (tcpci->controls_vbus) 623ae8a2ca8SHeikki Krogerus reg |= TCPC_ALERT_POWER_STATUS; 624766c485bSBadhri Jagan Sridharan /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */ 625766c485bSBadhri Jagan Sridharan if (tcpci->data->vbus_vsafe0v) { 626766c485bSBadhri Jagan Sridharan reg |= TCPC_ALERT_EXTENDED_STATUS; 627766c485bSBadhri Jagan Sridharan ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK, 628766c485bSBadhri Jagan Sridharan TCPC_EXTENDED_STATUS_VSAFE0V); 629766c485bSBadhri Jagan Sridharan if (ret < 0) 630766c485bSBadhri Jagan Sridharan return ret; 631766c485bSBadhri Jagan Sridharan } 632ae8a2ca8SHeikki Krogerus return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); 633ae8a2ca8SHeikki Krogerus } 634ae8a2ca8SHeikki Krogerus 635ae8a2ca8SHeikki Krogerus irqreturn_t tcpci_irq(struct tcpci *tcpci) 636ae8a2ca8SHeikki Krogerus { 637ae8a2ca8SHeikki Krogerus u16 status; 638766c485bSBadhri Jagan Sridharan int ret; 639766c485bSBadhri Jagan Sridharan unsigned int raw; 640ae8a2ca8SHeikki Krogerus 641ae8a2ca8SHeikki Krogerus tcpci_read16(tcpci, TCPC_ALERT, &status); 642ae8a2ca8SHeikki Krogerus 643ae8a2ca8SHeikki Krogerus /* 644ae8a2ca8SHeikki Krogerus * Clear alert status for everything except RX_STATUS, which shouldn't 645ae8a2ca8SHeikki Krogerus * be cleared until we have successfully retrieved message. 646ae8a2ca8SHeikki Krogerus */ 647ae8a2ca8SHeikki Krogerus if (status & ~TCPC_ALERT_RX_STATUS) 648ae8a2ca8SHeikki Krogerus tcpci_write16(tcpci, TCPC_ALERT, 649ae8a2ca8SHeikki Krogerus status & ~TCPC_ALERT_RX_STATUS); 650ae8a2ca8SHeikki Krogerus 651ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_CC_STATUS) 652ae8a2ca8SHeikki Krogerus tcpm_cc_change(tcpci->port); 653ae8a2ca8SHeikki Krogerus 654ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_POWER_STATUS) { 655766c485bSBadhri Jagan Sridharan regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw); 656ae8a2ca8SHeikki Krogerus /* 657ae8a2ca8SHeikki Krogerus * If power status mask has been reset, then the TCPC 658ae8a2ca8SHeikki Krogerus * has reset. 659ae8a2ca8SHeikki Krogerus */ 660766c485bSBadhri Jagan Sridharan if (raw == 0xff) 661ae8a2ca8SHeikki Krogerus tcpm_tcpc_reset(tcpci->port); 662ae8a2ca8SHeikki Krogerus else 663ae8a2ca8SHeikki Krogerus tcpm_vbus_change(tcpci->port); 664ae8a2ca8SHeikki Krogerus } 665ae8a2ca8SHeikki Krogerus 666ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_RX_STATUS) { 667ae8a2ca8SHeikki Krogerus struct pd_message msg; 668c215e48eSDouglas Gilbert unsigned int cnt, payload_cnt; 669ae8a2ca8SHeikki Krogerus u16 header; 670ae8a2ca8SHeikki Krogerus 671ae8a2ca8SHeikki Krogerus regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt); 672c215e48eSDouglas Gilbert /* 673c215e48eSDouglas Gilbert * 'cnt' corresponds to READABLE_BYTE_COUNT in section 4.4.14 674c215e48eSDouglas Gilbert * of the TCPCI spec [Rev 2.0 Ver 1.0 October 2017] and is 675c215e48eSDouglas Gilbert * defined in table 4-36 as one greater than the number of 676c215e48eSDouglas Gilbert * bytes received. And that number includes the header. So: 677c215e48eSDouglas Gilbert */ 678c215e48eSDouglas Gilbert if (cnt > 3) 679c215e48eSDouglas Gilbert payload_cnt = cnt - (1 + sizeof(msg.header)); 680c215e48eSDouglas Gilbert else 681c215e48eSDouglas Gilbert payload_cnt = 0; 682ae8a2ca8SHeikki Krogerus 683ae8a2ca8SHeikki Krogerus tcpci_read16(tcpci, TCPC_RX_HDR, &header); 684ae8a2ca8SHeikki Krogerus msg.header = cpu_to_le16(header); 685ae8a2ca8SHeikki Krogerus 686c215e48eSDouglas Gilbert if (WARN_ON(payload_cnt > sizeof(msg.payload))) 687c215e48eSDouglas Gilbert payload_cnt = sizeof(msg.payload); 688ae8a2ca8SHeikki Krogerus 689c215e48eSDouglas Gilbert if (payload_cnt > 0) 690ae8a2ca8SHeikki Krogerus regmap_raw_read(tcpci->regmap, TCPC_RX_DATA, 691c215e48eSDouglas Gilbert &msg.payload, payload_cnt); 692ae8a2ca8SHeikki Krogerus 693ae8a2ca8SHeikki Krogerus /* Read complete, clear RX status alert bit */ 694ae8a2ca8SHeikki Krogerus tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); 695ae8a2ca8SHeikki Krogerus 696ae8a2ca8SHeikki Krogerus tcpm_pd_receive(tcpci->port, &msg); 697ae8a2ca8SHeikki Krogerus } 698ae8a2ca8SHeikki Krogerus 699*05300871SXu Yang if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { 700766c485bSBadhri Jagan Sridharan ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw); 701766c485bSBadhri Jagan Sridharan if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V)) 702766c485bSBadhri Jagan Sridharan tcpm_vbus_change(tcpci->port); 703766c485bSBadhri Jagan Sridharan } 704766c485bSBadhri Jagan Sridharan 705ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_RX_HARD_RST) 706ae8a2ca8SHeikki Krogerus tcpm_pd_hard_reset(tcpci->port); 707ae8a2ca8SHeikki Krogerus 708ae8a2ca8SHeikki Krogerus if (status & TCPC_ALERT_TX_SUCCESS) 709ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS); 710ae8a2ca8SHeikki Krogerus else if (status & TCPC_ALERT_TX_DISCARDED) 711ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED); 712ae8a2ca8SHeikki Krogerus else if (status & TCPC_ALERT_TX_FAILED) 713ae8a2ca8SHeikki Krogerus tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); 714ae8a2ca8SHeikki Krogerus 715ae8a2ca8SHeikki Krogerus return IRQ_HANDLED; 716ae8a2ca8SHeikki Krogerus } 717ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_irq); 718ae8a2ca8SHeikki Krogerus 719ae8a2ca8SHeikki Krogerus static irqreturn_t _tcpci_irq(int irq, void *dev_id) 720ae8a2ca8SHeikki Krogerus { 721ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip = dev_id; 722ae8a2ca8SHeikki Krogerus 723ae8a2ca8SHeikki Krogerus return tcpci_irq(chip->tcpci); 724ae8a2ca8SHeikki Krogerus } 725ae8a2ca8SHeikki Krogerus 726ae8a2ca8SHeikki Krogerus static const struct regmap_config tcpci_regmap_config = { 727ae8a2ca8SHeikki Krogerus .reg_bits = 8, 728ae8a2ca8SHeikki Krogerus .val_bits = 8, 729ae8a2ca8SHeikki Krogerus 730ae8a2ca8SHeikki Krogerus .max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */ 731ae8a2ca8SHeikki Krogerus }; 732ae8a2ca8SHeikki Krogerus 733ae8a2ca8SHeikki Krogerus static int tcpci_parse_config(struct tcpci *tcpci) 734ae8a2ca8SHeikki Krogerus { 735ae8a2ca8SHeikki Krogerus tcpci->controls_vbus = true; /* XXX */ 736ae8a2ca8SHeikki Krogerus 737ae8a2ca8SHeikki Krogerus tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev, 738ae8a2ca8SHeikki Krogerus "connector"); 739ae8a2ca8SHeikki Krogerus if (!tcpci->tcpc.fwnode) { 740ae8a2ca8SHeikki Krogerus dev_err(tcpci->dev, "Can't find connector node.\n"); 741ae8a2ca8SHeikki Krogerus return -EINVAL; 742ae8a2ca8SHeikki Krogerus } 743ae8a2ca8SHeikki Krogerus 744ae8a2ca8SHeikki Krogerus return 0; 745ae8a2ca8SHeikki Krogerus } 746ae8a2ca8SHeikki Krogerus 747ae8a2ca8SHeikki Krogerus struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) 748ae8a2ca8SHeikki Krogerus { 749ae8a2ca8SHeikki Krogerus struct tcpci *tcpci; 750ae8a2ca8SHeikki Krogerus int err; 751ae8a2ca8SHeikki Krogerus 752ae8a2ca8SHeikki Krogerus tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL); 753ae8a2ca8SHeikki Krogerus if (!tcpci) 754ae8a2ca8SHeikki Krogerus return ERR_PTR(-ENOMEM); 755ae8a2ca8SHeikki Krogerus 756ae8a2ca8SHeikki Krogerus tcpci->dev = dev; 757ae8a2ca8SHeikki Krogerus tcpci->data = data; 758ae8a2ca8SHeikki Krogerus tcpci->regmap = data->regmap; 759ae8a2ca8SHeikki Krogerus 760ae8a2ca8SHeikki Krogerus tcpci->tcpc.init = tcpci_init; 761ae8a2ca8SHeikki Krogerus tcpci->tcpc.get_vbus = tcpci_get_vbus; 762ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_vbus = tcpci_set_vbus; 763ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_cc = tcpci_set_cc; 7647257fbc7SBadhri Jagan Sridharan tcpci->tcpc.apply_rc = tcpci_apply_rc; 765ae8a2ca8SHeikki Krogerus tcpci->tcpc.get_cc = tcpci_get_cc; 766ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_polarity = tcpci_set_polarity; 767ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_vconn = tcpci_set_vconn; 7687893f9e1SHans de Goede tcpci->tcpc.start_toggling = tcpci_start_toggling; 769ae8a2ca8SHeikki Krogerus 770ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; 771ae8a2ca8SHeikki Krogerus tcpci->tcpc.set_roles = tcpci_set_roles; 772ae8a2ca8SHeikki Krogerus tcpci->tcpc.pd_transmit = tcpci_pd_transmit; 773c081ac42SBadhri Jagan Sridharan tcpci->tcpc.set_bist_data = tcpci_set_bist_data; 77411121c24SBadhri Jagan Sridharan tcpci->tcpc.enable_frs = tcpci_enable_frs; 775a57d253fSBadhri Jagan Sridharan tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; 776372a3d0bSBadhri Jagan Sridharan tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; 777ae8a2ca8SHeikki Krogerus 778e1a97bf8SBadhri Jagan Sridharan if (tcpci->data->auto_discharge_disconnect) { 779e1a97bf8SBadhri Jagan Sridharan tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; 780e1a97bf8SBadhri Jagan Sridharan tcpci->tcpc.set_auto_vbus_discharge_threshold = 781e1a97bf8SBadhri Jagan Sridharan tcpci_set_auto_vbus_discharge_threshold; 7823b6c3d04SBadhri Jagan Sridharan regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_BLEED_DISCHARGE, 7833b6c3d04SBadhri Jagan Sridharan TCPC_POWER_CTRL_BLEED_DISCHARGE); 784e1a97bf8SBadhri Jagan Sridharan } 785e1a97bf8SBadhri Jagan Sridharan 786766c485bSBadhri Jagan Sridharan if (tcpci->data->vbus_vsafe0v) 787766c485bSBadhri Jagan Sridharan tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v; 788766c485bSBadhri Jagan Sridharan 789ae8a2ca8SHeikki Krogerus err = tcpci_parse_config(tcpci); 790ae8a2ca8SHeikki Krogerus if (err < 0) 791ae8a2ca8SHeikki Krogerus return ERR_PTR(err); 792ae8a2ca8SHeikki Krogerus 793ae8a2ca8SHeikki Krogerus tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); 794ae8a2ca8SHeikki Krogerus if (IS_ERR(tcpci->port)) 795ae8a2ca8SHeikki Krogerus return ERR_CAST(tcpci->port); 796ae8a2ca8SHeikki Krogerus 797ae8a2ca8SHeikki Krogerus return tcpci; 798ae8a2ca8SHeikki Krogerus } 799ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_register_port); 800ae8a2ca8SHeikki Krogerus 801ae8a2ca8SHeikki Krogerus void tcpci_unregister_port(struct tcpci *tcpci) 802ae8a2ca8SHeikki Krogerus { 803ae8a2ca8SHeikki Krogerus tcpm_unregister_port(tcpci->port); 804ae8a2ca8SHeikki Krogerus } 805ae8a2ca8SHeikki Krogerus EXPORT_SYMBOL_GPL(tcpci_unregister_port); 806ae8a2ca8SHeikki Krogerus 807ae8a2ca8SHeikki Krogerus static int tcpci_probe(struct i2c_client *client, 808ae8a2ca8SHeikki Krogerus const struct i2c_device_id *i2c_id) 809ae8a2ca8SHeikki Krogerus { 810ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip; 811ae8a2ca8SHeikki Krogerus int err; 812ae8a2ca8SHeikki Krogerus u16 val = 0; 813ae8a2ca8SHeikki Krogerus 814ae8a2ca8SHeikki Krogerus chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 815ae8a2ca8SHeikki Krogerus if (!chip) 816ae8a2ca8SHeikki Krogerus return -ENOMEM; 817ae8a2ca8SHeikki Krogerus 818ae8a2ca8SHeikki Krogerus chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config); 819ae8a2ca8SHeikki Krogerus if (IS_ERR(chip->data.regmap)) 820ae8a2ca8SHeikki Krogerus return PTR_ERR(chip->data.regmap); 821ae8a2ca8SHeikki Krogerus 822ae8a2ca8SHeikki Krogerus i2c_set_clientdata(client, chip); 823ae8a2ca8SHeikki Krogerus 824ae8a2ca8SHeikki Krogerus /* Disable chip interrupts before requesting irq */ 825ae8a2ca8SHeikki Krogerus err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val, 826ae8a2ca8SHeikki Krogerus sizeof(u16)); 827ae8a2ca8SHeikki Krogerus if (err < 0) 828ae8a2ca8SHeikki Krogerus return err; 829ae8a2ca8SHeikki Krogerus 830ae8a2ca8SHeikki Krogerus chip->tcpci = tcpci_register_port(&client->dev, &chip->data); 831ae8a2ca8SHeikki Krogerus if (IS_ERR(chip->tcpci)) 832ae8a2ca8SHeikki Krogerus return PTR_ERR(chip->tcpci); 833ae8a2ca8SHeikki Krogerus 834ae8a2ca8SHeikki Krogerus err = devm_request_threaded_irq(&client->dev, client->irq, NULL, 835ae8a2ca8SHeikki Krogerus _tcpci_irq, 836ae8a2ca8SHeikki Krogerus IRQF_ONESHOT | IRQF_TRIGGER_LOW, 837ae8a2ca8SHeikki Krogerus dev_name(&client->dev), chip); 838ae8a2ca8SHeikki Krogerus if (err < 0) { 839ae8a2ca8SHeikki Krogerus tcpci_unregister_port(chip->tcpci); 840ae8a2ca8SHeikki Krogerus return err; 841ae8a2ca8SHeikki Krogerus } 842ae8a2ca8SHeikki Krogerus 843ae8a2ca8SHeikki Krogerus return 0; 844ae8a2ca8SHeikki Krogerus } 845ae8a2ca8SHeikki Krogerus 846ae8a2ca8SHeikki Krogerus static int tcpci_remove(struct i2c_client *client) 847ae8a2ca8SHeikki Krogerus { 848ae8a2ca8SHeikki Krogerus struct tcpci_chip *chip = i2c_get_clientdata(client); 8493ba76256SJun Li int err; 8503ba76256SJun Li 8513ba76256SJun Li /* Disable chip interrupts before unregistering port */ 8523ba76256SJun Li err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); 8533ba76256SJun Li if (err < 0) 8543ba76256SJun Li return err; 855ae8a2ca8SHeikki Krogerus 856ae8a2ca8SHeikki Krogerus tcpci_unregister_port(chip->tcpci); 857ae8a2ca8SHeikki Krogerus 858ae8a2ca8SHeikki Krogerus return 0; 859ae8a2ca8SHeikki Krogerus } 860ae8a2ca8SHeikki Krogerus 861ae8a2ca8SHeikki Krogerus static const struct i2c_device_id tcpci_id[] = { 862ae8a2ca8SHeikki Krogerus { "tcpci", 0 }, 863ae8a2ca8SHeikki Krogerus { } 864ae8a2ca8SHeikki Krogerus }; 865ae8a2ca8SHeikki Krogerus MODULE_DEVICE_TABLE(i2c, tcpci_id); 866ae8a2ca8SHeikki Krogerus 867ae8a2ca8SHeikki Krogerus #ifdef CONFIG_OF 868ae8a2ca8SHeikki Krogerus static const struct of_device_id tcpci_of_match[] = { 869ae8a2ca8SHeikki Krogerus { .compatible = "nxp,ptn5110", }, 870ae8a2ca8SHeikki Krogerus {}, 871ae8a2ca8SHeikki Krogerus }; 872ae8a2ca8SHeikki Krogerus MODULE_DEVICE_TABLE(of, tcpci_of_match); 873ae8a2ca8SHeikki Krogerus #endif 874ae8a2ca8SHeikki Krogerus 875ae8a2ca8SHeikki Krogerus static struct i2c_driver tcpci_i2c_driver = { 876ae8a2ca8SHeikki Krogerus .driver = { 877ae8a2ca8SHeikki Krogerus .name = "tcpci", 878ae8a2ca8SHeikki Krogerus .of_match_table = of_match_ptr(tcpci_of_match), 879ae8a2ca8SHeikki Krogerus }, 880ae8a2ca8SHeikki Krogerus .probe = tcpci_probe, 881ae8a2ca8SHeikki Krogerus .remove = tcpci_remove, 882ae8a2ca8SHeikki Krogerus .id_table = tcpci_id, 883ae8a2ca8SHeikki Krogerus }; 884ae8a2ca8SHeikki Krogerus module_i2c_driver(tcpci_i2c_driver); 885ae8a2ca8SHeikki Krogerus 886ae8a2ca8SHeikki Krogerus MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver"); 887ae8a2ca8SHeikki Krogerus MODULE_LICENSE("GPL"); 888