| .. | .. |
|---|
| 15 | 15 | #include <linux/usb/pd.h> |
|---|
| 16 | 16 | #include <linux/usb/tcpm.h> |
|---|
| 17 | 17 | #include <linux/usb/typec.h> |
|---|
| 18 | +#include <trace/hooks/typec.h> |
|---|
| 18 | 19 | |
|---|
| 19 | 20 | #include "tcpci.h" |
|---|
| 20 | 21 | |
|---|
| 21 | | -#define PD_RETRY_COUNT 3 |
|---|
| 22 | +#define PD_RETRY_COUNT_DEFAULT 3 |
|---|
| 23 | +#define PD_RETRY_COUNT_3_0_OR_HIGHER 2 |
|---|
| 24 | +#define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 |
|---|
| 25 | +#define VSINKPD_MIN_IR_DROP_MV 750 |
|---|
| 26 | +#define VSRC_NEW_MIN_PERCENT 95 |
|---|
| 27 | +#define VSRC_VALID_MIN_MV 500 |
|---|
| 28 | +#define VPPS_NEW_MIN_PERCENT 95 |
|---|
| 29 | +#define VPPS_VALID_MIN_MV 100 |
|---|
| 30 | +#define VSINKDISCONNECT_PD_MIN_PERCENT 90 |
|---|
| 31 | + |
|---|
| 32 | +#define tcpc_presenting_rd(reg, cc) \ |
|---|
| 33 | + (!(TCPC_ROLE_CTRL_DRP & (reg)) && \ |
|---|
| 34 | + (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \ |
|---|
| 35 | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT))) |
|---|
| 22 | 36 | |
|---|
| 23 | 37 | #define tcpc_presenting_cc1_rd(reg) \ |
|---|
| 24 | 38 | (!(TCPC_ROLE_CTRL_DRP & (reg)) && \ |
|---|
| .. | .. |
|---|
| 47 | 61 | struct tcpci_data data; |
|---|
| 48 | 62 | }; |
|---|
| 49 | 63 | |
|---|
| 64 | +struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci) |
|---|
| 65 | +{ |
|---|
| 66 | + return tcpci->port; |
|---|
| 67 | +} |
|---|
| 68 | +EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port); |
|---|
| 69 | + |
|---|
| 50 | 70 | static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) |
|---|
| 51 | 71 | { |
|---|
| 52 | 72 | return container_of(tcpc, struct tcpci, tcpc); |
|---|
| .. | .. |
|---|
| 65 | 85 | static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) |
|---|
| 66 | 86 | { |
|---|
| 67 | 87 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 88 | + bool vconn_pres; |
|---|
| 89 | + enum typec_cc_polarity polarity = TYPEC_POLARITY_CC1; |
|---|
| 68 | 90 | unsigned int reg; |
|---|
| 69 | 91 | int ret; |
|---|
| 92 | + |
|---|
| 93 | + ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); |
|---|
| 94 | + if (ret < 0) |
|---|
| 95 | + return ret; |
|---|
| 96 | + |
|---|
| 97 | + vconn_pres = !!(reg & TCPC_POWER_STATUS_VCONN_PRES); |
|---|
| 98 | + if (vconn_pres) { |
|---|
| 99 | + ret = regmap_read(tcpci->regmap, TCPC_TCPC_CTRL, ®); |
|---|
| 100 | + if (ret < 0) |
|---|
| 101 | + return ret; |
|---|
| 102 | + |
|---|
| 103 | + if (reg & TCPC_TCPC_CTRL_ORIENTATION) |
|---|
| 104 | + polarity = TYPEC_POLARITY_CC2; |
|---|
| 105 | + } |
|---|
| 70 | 106 | |
|---|
| 71 | 107 | switch (cc) { |
|---|
| 72 | 108 | case TYPEC_CC_RA: |
|---|
| .. | .. |
|---|
| 102 | 138 | break; |
|---|
| 103 | 139 | } |
|---|
| 104 | 140 | |
|---|
| 141 | + if (vconn_pres) { |
|---|
| 142 | + if (polarity == TYPEC_POLARITY_CC2) { |
|---|
| 143 | + reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); |
|---|
| 144 | + reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT); |
|---|
| 145 | + } else { |
|---|
| 146 | + reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); |
|---|
| 147 | + reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); |
|---|
| 148 | + } |
|---|
| 149 | + } |
|---|
| 150 | + |
|---|
| 105 | 151 | ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); |
|---|
| 106 | 152 | if (ret < 0) |
|---|
| 107 | 153 | return ret; |
|---|
| 108 | 154 | |
|---|
| 109 | 155 | return 0; |
|---|
| 156 | +} |
|---|
| 157 | + |
|---|
| 158 | +static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc, |
|---|
| 159 | + enum typec_cc_polarity polarity) |
|---|
| 160 | +{ |
|---|
| 161 | + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 162 | + unsigned int reg; |
|---|
| 163 | + int ret; |
|---|
| 164 | + |
|---|
| 165 | + ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); |
|---|
| 166 | + if (ret < 0) |
|---|
| 167 | + return ret; |
|---|
| 168 | + |
|---|
| 169 | + /* |
|---|
| 170 | + * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on |
|---|
| 171 | + * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2. |
|---|
| 172 | + */ |
|---|
| 173 | + if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >> |
|---|
| 174 | + TCPC_ROLE_CTRL_CC2_SHIFT) != |
|---|
| 175 | + ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >> |
|---|
| 176 | + TCPC_ROLE_CTRL_CC1_SHIFT)) |
|---|
| 177 | + return 0; |
|---|
| 178 | + |
|---|
| 179 | + return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ? |
|---|
| 180 | + TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT : |
|---|
| 181 | + TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT, |
|---|
| 182 | + TCPC_ROLE_CTRL_CC_OPEN); |
|---|
| 110 | 183 | } |
|---|
| 111 | 184 | |
|---|
| 112 | 185 | static int tcpci_start_toggling(struct tcpc_dev *tcpc, |
|---|
| .. | .. |
|---|
| 116 | 189 | int ret; |
|---|
| 117 | 190 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 118 | 191 | unsigned int reg = TCPC_ROLE_CTRL_DRP; |
|---|
| 192 | + int override_toggling = 0; |
|---|
| 119 | 193 | |
|---|
| 120 | 194 | if (port_type != TYPEC_PORT_DRP) |
|---|
| 121 | 195 | return -EOPNOTSUPP; |
|---|
| 122 | 196 | |
|---|
| 123 | 197 | /* Handle vendor drp toggling */ |
|---|
| 124 | 198 | if (tcpci->data->start_drp_toggling) { |
|---|
| 199 | + trace_android_vh_typec_tcpci_override_toggling(tcpci, tcpci->data, |
|---|
| 200 | + &override_toggling); |
|---|
| 125 | 201 | ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); |
|---|
| 126 | | - if (ret < 0) |
|---|
| 202 | + if (ret < 0 || override_toggling) |
|---|
| 127 | 203 | return ret; |
|---|
| 128 | 204 | } |
|---|
| 129 | 205 | |
|---|
| .. | .. |
|---|
| 166 | 242 | case 0x3: |
|---|
| 167 | 243 | if (sink) |
|---|
| 168 | 244 | return TYPEC_CC_RP_3_0; |
|---|
| 169 | | - /* fall through */ |
|---|
| 245 | + fallthrough; |
|---|
| 170 | 246 | case 0x0: |
|---|
| 171 | 247 | default: |
|---|
| 172 | 248 | return TYPEC_CC_OPEN; |
|---|
| .. | .. |
|---|
| 191 | 267 | *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & |
|---|
| 192 | 268 | TCPC_CC_STATUS_CC1_MASK, |
|---|
| 193 | 269 | reg & TCPC_CC_STATUS_TERM || |
|---|
| 194 | | - tcpc_presenting_cc1_rd(role_control)); |
|---|
| 270 | + tcpc_presenting_rd(role_control, CC1)); |
|---|
| 195 | 271 | *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & |
|---|
| 196 | 272 | TCPC_CC_STATUS_CC2_MASK, |
|---|
| 197 | 273 | reg & TCPC_CC_STATUS_TERM || |
|---|
| 198 | | - tcpc_presenting_cc2_rd(role_control)); |
|---|
| 274 | + tcpc_presenting_rd(role_control, CC2)); |
|---|
| 199 | 275 | |
|---|
| 200 | 276 | return 0; |
|---|
| 201 | 277 | } |
|---|
| .. | .. |
|---|
| 260 | 336 | TCPC_TCPC_CTRL_ORIENTATION : 0); |
|---|
| 261 | 337 | } |
|---|
| 262 | 338 | |
|---|
| 339 | +static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable) |
|---|
| 340 | +{ |
|---|
| 341 | + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 342 | + |
|---|
| 343 | + if (tcpci->data->set_partner_usb_comm_capable) |
|---|
| 344 | + tcpci->data->set_partner_usb_comm_capable(tcpci, tcpci->data, capable); |
|---|
| 345 | +} |
|---|
| 346 | + |
|---|
| 263 | 347 | static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) |
|---|
| 264 | 348 | { |
|---|
| 265 | 349 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| .. | .. |
|---|
| 275 | 359 | return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, |
|---|
| 276 | 360 | TCPC_POWER_CTRL_VCONN_ENABLE, |
|---|
| 277 | 361 | enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); |
|---|
| 362 | +} |
|---|
| 363 | + |
|---|
| 364 | +static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) |
|---|
| 365 | +{ |
|---|
| 366 | + struct tcpci *tcpci = tcpc_to_tcpci(dev); |
|---|
| 367 | + int ret; |
|---|
| 368 | + |
|---|
| 369 | + ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, |
|---|
| 370 | + enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0); |
|---|
| 371 | + return ret; |
|---|
| 372 | +} |
|---|
| 373 | + |
|---|
| 374 | +static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, |
|---|
| 375 | + bool pps_active, u32 requested_vbus_voltage_mv) |
|---|
| 376 | +{ |
|---|
| 377 | + struct tcpci *tcpci = tcpc_to_tcpci(dev); |
|---|
| 378 | + unsigned int pwr_ctrl, threshold = 0; |
|---|
| 379 | + int ret; |
|---|
| 380 | + |
|---|
| 381 | + /* |
|---|
| 382 | + * Indicates that vbus is going to go away due PR_SWAP, hard reset etc. |
|---|
| 383 | + * Do not discharge vbus here. |
|---|
| 384 | + */ |
|---|
| 385 | + if (requested_vbus_voltage_mv == 0) |
|---|
| 386 | + goto write_thresh; |
|---|
| 387 | + |
|---|
| 388 | + ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl); |
|---|
| 389 | + if (ret < 0) |
|---|
| 390 | + return ret; |
|---|
| 391 | + |
|---|
| 392 | + if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) { |
|---|
| 393 | + /* To prevent disconnect when the source is fast role swap is capable. */ |
|---|
| 394 | + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; |
|---|
| 395 | + } else if (mode == TYPEC_PWR_MODE_PD) { |
|---|
| 396 | + if (pps_active) |
|---|
| 397 | + threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - |
|---|
| 398 | + VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) * |
|---|
| 399 | + VSINKDISCONNECT_PD_MIN_PERCENT / 100; |
|---|
| 400 | + else |
|---|
| 401 | + threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - |
|---|
| 402 | + VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) * |
|---|
| 403 | + VSINKDISCONNECT_PD_MIN_PERCENT / 100; |
|---|
| 404 | + } else { |
|---|
| 405 | + /* 3.5V for non-pd sink */ |
|---|
| 406 | + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; |
|---|
| 407 | + } |
|---|
| 408 | + |
|---|
| 409 | + threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV; |
|---|
| 410 | + |
|---|
| 411 | + if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) |
|---|
| 412 | + return -EINVAL; |
|---|
| 413 | + |
|---|
| 414 | +write_thresh: |
|---|
| 415 | + return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold); |
|---|
| 416 | +} |
|---|
| 417 | + |
|---|
| 418 | +static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) |
|---|
| 419 | +{ |
|---|
| 420 | + struct tcpci *tcpci = tcpc_to_tcpci(dev); |
|---|
| 421 | + int ret; |
|---|
| 422 | + |
|---|
| 423 | + /* To prevent disconnect during FRS, set disconnect threshold to 3.5V */ |
|---|
| 424 | + ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c); |
|---|
| 425 | + if (ret < 0) |
|---|
| 426 | + return ret; |
|---|
| 427 | + |
|---|
| 428 | + ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ? |
|---|
| 429 | + TCPC_FAST_ROLE_SWAP_EN : 0); |
|---|
| 430 | + |
|---|
| 431 | + return ret; |
|---|
| 432 | +} |
|---|
| 433 | + |
|---|
| 434 | +static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) |
|---|
| 435 | +{ |
|---|
| 436 | + struct tcpci *tcpci = tcpc_to_tcpci(dev); |
|---|
| 437 | + |
|---|
| 438 | + if (tcpci->data->frs_sourcing_vbus) |
|---|
| 439 | + tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); |
|---|
| 440 | +} |
|---|
| 441 | + |
|---|
| 442 | +static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) |
|---|
| 443 | +{ |
|---|
| 444 | + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 445 | + |
|---|
| 446 | + return regmap_update_bits(tcpci->regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_BIST_TM, |
|---|
| 447 | + enable ? TCPC_TCPC_CTRL_BIST_TM : 0); |
|---|
| 278 | 448 | } |
|---|
| 279 | 449 | |
|---|
| 280 | 450 | static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, |
|---|
| .. | .. |
|---|
| 315 | 485 | { |
|---|
| 316 | 486 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 317 | 487 | unsigned int reg; |
|---|
| 318 | | - int ret; |
|---|
| 488 | + int ret, vbus, bypass = 0; |
|---|
| 489 | + |
|---|
| 490 | + trace_android_rvh_typec_tcpci_get_vbus(tcpci, tcpci->data, &vbus, &bypass); |
|---|
| 491 | + if (bypass) |
|---|
| 492 | + return vbus; |
|---|
| 319 | 493 | |
|---|
| 320 | 494 | ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); |
|---|
| 321 | 495 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 324 | 498 | return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); |
|---|
| 325 | 499 | } |
|---|
| 326 | 500 | |
|---|
| 501 | +static int tcpci_check_contaminant(struct tcpc_dev *tcpc) |
|---|
| 502 | +{ |
|---|
| 503 | + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 504 | + int ret = 0; |
|---|
| 505 | + |
|---|
| 506 | + trace_android_rvh_typec_tcpci_chk_contaminant(tcpci, tcpci->data, &ret); |
|---|
| 507 | + return ret; |
|---|
| 508 | +} |
|---|
| 509 | + |
|---|
| 510 | +static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc) |
|---|
| 511 | +{ |
|---|
| 512 | + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 513 | + unsigned int reg; |
|---|
| 514 | + int ret; |
|---|
| 515 | + |
|---|
| 516 | + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®); |
|---|
| 517 | + if (ret < 0) |
|---|
| 518 | + return false; |
|---|
| 519 | + |
|---|
| 520 | + return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V); |
|---|
| 521 | +} |
|---|
| 522 | + |
|---|
| 327 | 523 | static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) |
|---|
| 328 | 524 | { |
|---|
| 329 | 525 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 330 | 526 | int ret; |
|---|
| 527 | + |
|---|
| 528 | + if (tcpci->data->set_vbus) { |
|---|
| 529 | + ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink); |
|---|
| 530 | + /* Bypass when ret > 0 */ |
|---|
| 531 | + if (ret != 0) |
|---|
| 532 | + return ret < 0 ? ret : 0; |
|---|
| 533 | + } |
|---|
| 331 | 534 | |
|---|
| 332 | 535 | /* Disable both source and sink first before enabling anything */ |
|---|
| 333 | 536 | |
|---|
| .. | .. |
|---|
| 362 | 565 | return 0; |
|---|
| 363 | 566 | } |
|---|
| 364 | 567 | |
|---|
| 365 | | -static int tcpci_pd_transmit(struct tcpc_dev *tcpc, |
|---|
| 366 | | - enum tcpm_transmit_type type, |
|---|
| 367 | | - const struct pd_message *msg) |
|---|
| 568 | +static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, |
|---|
| 569 | + const struct pd_message *msg, unsigned int negotiated_rev) |
|---|
| 368 | 570 | { |
|---|
| 369 | 571 | struct tcpci *tcpci = tcpc_to_tcpci(tcpc); |
|---|
| 370 | 572 | u16 header = msg ? le16_to_cpu(msg->header) : 0; |
|---|
| .. | .. |
|---|
| 372 | 574 | int ret; |
|---|
| 373 | 575 | |
|---|
| 374 | 576 | cnt = msg ? pd_header_cnt(header) * 4 : 0; |
|---|
| 375 | | - ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); |
|---|
| 376 | | - if (ret < 0) |
|---|
| 377 | | - return ret; |
|---|
| 577 | + /** |
|---|
| 578 | + * TCPCI spec forbids direct access of TCPC_TX_DATA. |
|---|
| 579 | + * But, since some of the chipsets offer this capability, |
|---|
| 580 | + * it's fair to support both. |
|---|
| 581 | + */ |
|---|
| 582 | + if (tcpci->data->TX_BUF_BYTE_x_hidden) { |
|---|
| 583 | + u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,}; |
|---|
| 584 | + u8 pos = 0; |
|---|
| 378 | 585 | |
|---|
| 379 | | - ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); |
|---|
| 380 | | - if (ret < 0) |
|---|
| 381 | | - return ret; |
|---|
| 586 | + /* Payload + header + TCPC_TX_BYTE_CNT */ |
|---|
| 587 | + buf[pos++] = cnt + 2; |
|---|
| 382 | 588 | |
|---|
| 383 | | - if (cnt > 0) { |
|---|
| 384 | | - ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, |
|---|
| 385 | | - &msg->payload, cnt); |
|---|
| 589 | + if (msg) |
|---|
| 590 | + memcpy(&buf[pos], &msg->header, sizeof(msg->header)); |
|---|
| 591 | + |
|---|
| 592 | + pos += sizeof(header); |
|---|
| 593 | + |
|---|
| 594 | + if (cnt > 0) |
|---|
| 595 | + memcpy(&buf[pos], msg->payload, cnt); |
|---|
| 596 | + |
|---|
| 597 | + pos += cnt; |
|---|
| 598 | + ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos); |
|---|
| 386 | 599 | if (ret < 0) |
|---|
| 387 | 600 | return ret; |
|---|
| 601 | + } else { |
|---|
| 602 | + ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); |
|---|
| 603 | + if (ret < 0) |
|---|
| 604 | + return ret; |
|---|
| 605 | + |
|---|
| 606 | + ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); |
|---|
| 607 | + if (ret < 0) |
|---|
| 608 | + return ret; |
|---|
| 609 | + |
|---|
| 610 | + if (cnt > 0) { |
|---|
| 611 | + ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt); |
|---|
| 612 | + if (ret < 0) |
|---|
| 613 | + return ret; |
|---|
| 614 | + } |
|---|
| 388 | 615 | } |
|---|
| 389 | 616 | |
|---|
| 390 | | - reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | |
|---|
| 391 | | - (type << TCPC_TRANSMIT_TYPE_SHIFT); |
|---|
| 617 | + /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ |
|---|
| 618 | + reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) |
|---|
| 619 | + << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); |
|---|
| 392 | 620 | ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); |
|---|
| 393 | 621 | if (ret < 0) |
|---|
| 394 | 622 | return ret; |
|---|
| .. | .. |
|---|
| 413 | 641 | } |
|---|
| 414 | 642 | if (time_after(jiffies, timeout)) |
|---|
| 415 | 643 | return -ETIMEDOUT; |
|---|
| 644 | + |
|---|
| 645 | + ret = tcpci_write16(tcpci, TCPC_FAULT_STATUS, TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT); |
|---|
| 646 | + if (ret < 0) |
|---|
| 647 | + return ret; |
|---|
| 416 | 648 | |
|---|
| 417 | 649 | /* Handle vendor init */ |
|---|
| 418 | 650 | if (tcpci->data->init) { |
|---|
| .. | .. |
|---|
| 445 | 677 | TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; |
|---|
| 446 | 678 | if (tcpci->controls_vbus) |
|---|
| 447 | 679 | reg |= TCPC_ALERT_POWER_STATUS; |
|---|
| 680 | + /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */ |
|---|
| 681 | + if (tcpci->data->vbus_vsafe0v) { |
|---|
| 682 | + reg |= TCPC_ALERT_EXTENDED_STATUS; |
|---|
| 683 | + ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK, |
|---|
| 684 | + TCPC_EXTENDED_STATUS_VSAFE0V); |
|---|
| 685 | + if (ret < 0) |
|---|
| 686 | + return ret; |
|---|
| 687 | + } |
|---|
| 448 | 688 | return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); |
|---|
| 449 | 689 | } |
|---|
| 450 | 690 | |
|---|
| 451 | 691 | irqreturn_t tcpci_irq(struct tcpci *tcpci) |
|---|
| 452 | 692 | { |
|---|
| 453 | 693 | u16 status; |
|---|
| 694 | + int ret; |
|---|
| 695 | + unsigned int raw; |
|---|
| 454 | 696 | |
|---|
| 455 | 697 | tcpci_read16(tcpci, TCPC_ALERT, &status); |
|---|
| 456 | 698 | |
|---|
| .. | .. |
|---|
| 466 | 708 | tcpm_cc_change(tcpci->port); |
|---|
| 467 | 709 | |
|---|
| 468 | 710 | if (status & TCPC_ALERT_POWER_STATUS) { |
|---|
| 469 | | - unsigned int reg; |
|---|
| 470 | | - |
|---|
| 471 | | - regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®); |
|---|
| 472 | | - |
|---|
| 711 | + regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw); |
|---|
| 473 | 712 | /* |
|---|
| 474 | 713 | * If power status mask has been reset, then the TCPC |
|---|
| 475 | 714 | * has reset. |
|---|
| 476 | 715 | */ |
|---|
| 477 | | - if (reg == 0xff) |
|---|
| 716 | + if (raw == 0xff) |
|---|
| 478 | 717 | tcpm_tcpc_reset(tcpci->port); |
|---|
| 479 | 718 | else |
|---|
| 480 | 719 | tcpm_vbus_change(tcpci->port); |
|---|
| .. | .. |
|---|
| 511 | 750 | tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); |
|---|
| 512 | 751 | |
|---|
| 513 | 752 | tcpm_pd_receive(tcpci->port, &msg); |
|---|
| 753 | + } |
|---|
| 754 | + |
|---|
| 755 | + if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { |
|---|
| 756 | + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw); |
|---|
| 757 | + if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V)) |
|---|
| 758 | + tcpm_vbus_change(tcpci->port); |
|---|
| 514 | 759 | } |
|---|
| 515 | 760 | |
|---|
| 516 | 761 | if (status & TCPC_ALERT_RX_HARD_RST) |
|---|
| .. | .. |
|---|
| 572 | 817 | tcpci->tcpc.get_vbus = tcpci_get_vbus; |
|---|
| 573 | 818 | tcpci->tcpc.set_vbus = tcpci_set_vbus; |
|---|
| 574 | 819 | tcpci->tcpc.set_cc = tcpci_set_cc; |
|---|
| 820 | + tcpci->tcpc.apply_rc = tcpci_apply_rc; |
|---|
| 575 | 821 | tcpci->tcpc.get_cc = tcpci_get_cc; |
|---|
| 576 | 822 | tcpci->tcpc.set_polarity = tcpci_set_polarity; |
|---|
| 577 | 823 | tcpci->tcpc.set_vconn = tcpci_set_vconn; |
|---|
| .. | .. |
|---|
| 580 | 826 | tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; |
|---|
| 581 | 827 | tcpci->tcpc.set_roles = tcpci_set_roles; |
|---|
| 582 | 828 | tcpci->tcpc.pd_transmit = tcpci_pd_transmit; |
|---|
| 829 | + tcpci->tcpc.set_bist_data = tcpci_set_bist_data; |
|---|
| 830 | + tcpci->tcpc.enable_frs = tcpci_enable_frs; |
|---|
| 831 | + tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; |
|---|
| 832 | + tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; |
|---|
| 833 | + tcpci->tcpc.check_contaminant = tcpci_check_contaminant; |
|---|
| 834 | + |
|---|
| 835 | + if (tcpci->data->auto_discharge_disconnect) { |
|---|
| 836 | + tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; |
|---|
| 837 | + tcpci->tcpc.set_auto_vbus_discharge_threshold = |
|---|
| 838 | + tcpci_set_auto_vbus_discharge_threshold; |
|---|
| 839 | + regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_BLEED_DISCHARGE, |
|---|
| 840 | + TCPC_POWER_CTRL_BLEED_DISCHARGE); |
|---|
| 841 | + } |
|---|
| 842 | + |
|---|
| 843 | + if (tcpci->data->vbus_vsafe0v) |
|---|
| 844 | + tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v; |
|---|
| 583 | 845 | |
|---|
| 584 | 846 | err = tcpci_parse_config(tcpci); |
|---|
| 585 | 847 | if (err < 0) |
|---|
| 586 | 848 | return ERR_PTR(err); |
|---|
| 587 | 849 | |
|---|
| 588 | 850 | tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); |
|---|
| 589 | | - if (IS_ERR(tcpci->port)) |
|---|
| 851 | + if (IS_ERR(tcpci->port)) { |
|---|
| 852 | + fwnode_handle_put(tcpci->tcpc.fwnode); |
|---|
| 590 | 853 | return ERR_CAST(tcpci->port); |
|---|
| 854 | + } |
|---|
| 591 | 855 | |
|---|
| 592 | 856 | return tcpci; |
|---|
| 593 | 857 | } |
|---|
| .. | .. |
|---|
| 596 | 860 | void tcpci_unregister_port(struct tcpci *tcpci) |
|---|
| 597 | 861 | { |
|---|
| 598 | 862 | tcpm_unregister_port(tcpci->port); |
|---|
| 863 | + fwnode_handle_put(tcpci->tcpc.fwnode); |
|---|
| 599 | 864 | } |
|---|
| 600 | 865 | EXPORT_SYMBOL_GPL(tcpci_unregister_port); |
|---|
| 601 | 866 | |
|---|
| .. | .. |
|---|
| 646 | 911 | /* Disable chip interrupts before unregistering port */ |
|---|
| 647 | 912 | err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); |
|---|
| 648 | 913 | if (err < 0) |
|---|
| 649 | | - return err; |
|---|
| 914 | + dev_warn(&client->dev, "Failed to disable irqs (%pe)\n", ERR_PTR(err)); |
|---|
| 650 | 915 | |
|---|
| 651 | 916 | tcpci_unregister_port(chip->tcpci); |
|---|
| 652 | 917 | |
|---|