.. | .. |
---|
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 | |
---|