aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/typec/ucsi/ucsi.c')
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c125
1 files changed, 105 insertions, 20 deletions
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 51a570d40a42..f02958927cbd 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -53,7 +53,7 @@ static int ucsi_acknowledge_connector_change(struct ucsi *ucsi)
53 ctrl = UCSI_ACK_CC_CI; 53 ctrl = UCSI_ACK_CC_CI;
54 ctrl |= UCSI_ACK_CONNECTOR_CHANGE; 54 ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
55 55
56 return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); 56 return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
57} 57}
58 58
59static int ucsi_exec_command(struct ucsi *ucsi, u64 command); 59static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
@@ -625,21 +625,113 @@ static void ucsi_handle_connector_change(struct work_struct *work)
625 struct ucsi_connector *con = container_of(work, struct ucsi_connector, 625 struct ucsi_connector *con = container_of(work, struct ucsi_connector,
626 work); 626 work);
627 struct ucsi *ucsi = con->ucsi; 627 struct ucsi *ucsi = con->ucsi;
628 struct ucsi_connector_status pre_ack_status;
629 struct ucsi_connector_status post_ack_status;
628 enum typec_role role; 630 enum typec_role role;
631 u16 inferred_changes;
632 u16 changed_flags;
629 u64 command; 633 u64 command;
630 int ret; 634 int ret;
631 635
632 mutex_lock(&con->lock); 636 mutex_lock(&con->lock);
633 637
638 /*
639 * Some/many PPMs have an issue where all fields in the change bitfield
640 * are cleared when an ACK is send. This will causes any change
641 * between GET_CONNECTOR_STATUS and ACK to be lost.
642 *
643 * We work around this by re-fetching the connector status afterwards.
644 * We then infer any changes that we see have happened but that may not
645 * be represented in the change bitfield.
646 *
647 * Also, even though we don't need to know the currently supported alt
648 * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does
649 * not get stuck in case it assumes we do.
650 * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be
651 * set in the change bitfield.
652 *
653 * We end up with the following actions:
654 * 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes
655 * 2. UCSI_GET_CAM_SUPPORTED, discard result
656 * 3. ACK connector change
657 * 4. UCSI_GET_CONNECTOR_STATUS, store result
658 * 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results
659 * 6. If PPM reported a new change, then restart in order to ACK
660 * 7. Process everything as usual.
661 *
662 * We may end up seeing a change twice, but we can only miss extremely
663 * short transitional changes.
664 */
665
666 /* 1. First UCSI_GET_CONNECTOR_STATUS */
667 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
668 ret = ucsi_send_command(ucsi, command, &pre_ack_status,
669 sizeof(pre_ack_status));
670 if (ret < 0) {
671 dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
672 __func__, ret);
673 goto out_unlock;
674 }
675 con->unprocessed_changes |= pre_ack_status.change;
676
677 /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */
678 command = UCSI_GET_CAM_SUPPORTED;
679 command |= UCSI_CONNECTOR_NUMBER(con->num);
680 ucsi_send_command(con->ucsi, command, NULL, 0);
681
682 /* 3. ACK connector change */
683 clear_bit(EVENT_PENDING, &ucsi->flags);
684 ret = ucsi_acknowledge_connector_change(ucsi);
685 if (ret) {
686 dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
687 goto out_unlock;
688 }
689
690 /* 4. Second UCSI_GET_CONNECTOR_STATUS */
634 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); 691 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
635 ret = ucsi_send_command(ucsi, command, &con->status, 692 ret = ucsi_send_command(ucsi, command, &post_ack_status,
636 sizeof(con->status)); 693 sizeof(post_ack_status));
637 if (ret < 0) { 694 if (ret < 0) {
638 dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", 695 dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
639 __func__, ret); 696 __func__, ret);
640 goto out_unlock; 697 goto out_unlock;
641 } 698 }
642 699
700 /* 5. Inferre any missing changes */
701 changed_flags = pre_ack_status.flags ^ post_ack_status.flags;
702 inferred_changes = 0;
703 if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0)
704 inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE;
705
706 if (changed_flags & UCSI_CONSTAT_CONNECTED)
707 inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE;
708
709 if (changed_flags & UCSI_CONSTAT_PWR_DIR)
710 inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE;
711
712 if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0)
713 inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
714
715 if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0)
716 inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
717
718 /* Mask out anything that was correctly notified in the later call. */
719 inferred_changes &= ~post_ack_status.change;
720 if (inferred_changes)
721 dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n",
722 __func__, inferred_changes);
723
724 con->unprocessed_changes |= inferred_changes;
725
726 /* 6. If PPM reported a new change, then restart in order to ACK */
727 if (post_ack_status.change)
728 goto out_unlock;
729
730 /* 7. Continue as if nothing happened */
731 con->status = post_ack_status;
732 con->status.change = con->unprocessed_changes;
733 con->unprocessed_changes = 0;
734
643 role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); 735 role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
644 736
645 if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || 737 if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
@@ -680,28 +772,19 @@ static void ucsi_handle_connector_change(struct work_struct *work)
680 ucsi_port_psy_changed(con); 772 ucsi_port_psy_changed(con);
681 } 773 }
682 774
683 if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) {
684 /*
685 * We don't need to know the currently supported alt modes here.
686 * Running GET_CAM_SUPPORTED command just to make sure the PPM
687 * does not get stuck in case it assumes we do so.
688 */
689 command = UCSI_GET_CAM_SUPPORTED;
690 command |= UCSI_CONNECTOR_NUMBER(con->num);
691 ucsi_send_command(con->ucsi, command, NULL, 0);
692 }
693
694 if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) 775 if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
695 ucsi_partner_change(con); 776 ucsi_partner_change(con);
696 777
697 ret = ucsi_acknowledge_connector_change(ucsi);
698 if (ret)
699 dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
700
701 trace_ucsi_connector_change(con->num, &con->status); 778 trace_ucsi_connector_change(con->num, &con->status);
702 779
703out_unlock: 780out_unlock:
704 clear_bit(EVENT_PENDING, &ucsi->flags); 781 if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) {
782 schedule_work(&con->work);
783 mutex_unlock(&con->lock);
784 return;
785 }
786
787 clear_bit(EVENT_PROCESSING, &ucsi->flags);
705 mutex_unlock(&con->lock); 788 mutex_unlock(&con->lock);
706} 789}
707 790
@@ -719,7 +802,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num)
719 return; 802 return;
720 } 803 }
721 804
722 if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) 805 set_bit(EVENT_PENDING, &ucsi->flags);
806
807 if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags))
723 schedule_work(&con->work); 808 schedule_work(&con->work);
724} 809}
725EXPORT_SYMBOL_GPL(ucsi_connector_change); 810EXPORT_SYMBOL_GPL(ucsi_connector_change);