ctrl = UCSI_ACK_CC_CI;
        ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
 
-       return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+       return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
 }
 
 static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
        struct ucsi_connector *con = container_of(work, struct ucsi_connector,
                                                  work);
        struct ucsi *ucsi = con->ucsi;
+       struct ucsi_connector_status pre_ack_status;
+       struct ucsi_connector_status post_ack_status;
        enum typec_role role;
+       u16 inferred_changes;
+       u16 changed_flags;
        u64 command;
        int ret;
 
        mutex_lock(&con->lock);
 
+       /*
+        * Some/many PPMs have an issue where all fields in the change bitfield
+        * are cleared when an ACK is send. This will causes any change
+        * between GET_CONNECTOR_STATUS and ACK to be lost.
+        *
+        * We work around this by re-fetching the connector status afterwards.
+        * We then infer any changes that we see have happened but that may not
+        * be represented in the change bitfield.
+        *
+        * Also, even though we don't need to know the currently supported alt
+        * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does
+        * not get stuck in case it assumes we do.
+        * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be
+        * set in the change bitfield.
+        *
+        * We end up with the following actions:
+        *  1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes
+        *  2. UCSI_GET_CAM_SUPPORTED, discard result
+        *  3. ACK connector change
+        *  4. UCSI_GET_CONNECTOR_STATUS, store result
+        *  5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results
+        *  6. If PPM reported a new change, then restart in order to ACK
+        *  7. Process everything as usual.
+        *
+        * We may end up seeing a change twice, but we can only miss extremely
+        * short transitional changes.
+        */
+
+       /* 1. First UCSI_GET_CONNECTOR_STATUS */
+       command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
+       ret = ucsi_send_command(ucsi, command, &pre_ack_status,
+                               sizeof(pre_ack_status));
+       if (ret < 0) {
+               dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
+                       __func__, ret);
+               goto out_unlock;
+       }
+       con->unprocessed_changes |= pre_ack_status.change;
+
+       /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */
+       command = UCSI_GET_CAM_SUPPORTED;
+       command |= UCSI_CONNECTOR_NUMBER(con->num);
+       ucsi_send_command(con->ucsi, command, NULL, 0);
+
+       /* 3. ACK connector change */
+       clear_bit(EVENT_PENDING, &ucsi->flags);
+       ret = ucsi_acknowledge_connector_change(ucsi);
+       if (ret) {
+               dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
+               goto out_unlock;
+       }
+
+       /* 4. Second UCSI_GET_CONNECTOR_STATUS */
        command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
-       ret = ucsi_send_command(ucsi, command, &con->status,
-                               sizeof(con->status));
+       ret = ucsi_send_command(ucsi, command, &post_ack_status,
+                               sizeof(post_ack_status));
        if (ret < 0) {
                dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
                        __func__, ret);
                goto out_unlock;
        }
 
+       /* 5. Inferre any missing changes */
+       changed_flags = pre_ack_status.flags ^ post_ack_status.flags;
+       inferred_changes = 0;
+       if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0)
+               inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE;
+
+       if (changed_flags & UCSI_CONSTAT_CONNECTED)
+               inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE;
+
+       if (changed_flags & UCSI_CONSTAT_PWR_DIR)
+               inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE;
+
+       if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0)
+               inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
+
+       if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0)
+               inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
+
+       /* Mask out anything that was correctly notified in the later call. */
+       inferred_changes &= ~post_ack_status.change;
+       if (inferred_changes)
+               dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n",
+                       __func__, inferred_changes);
+
+       con->unprocessed_changes |= inferred_changes;
+
+       /* 6. If PPM reported a new change, then restart in order to ACK */
+       if (post_ack_status.change)
+               goto out_unlock;
+
+       /* 7. Continue as if nothing happened */
+       con->status = post_ack_status;
+       con->status.change = con->unprocessed_changes;
+       con->unprocessed_changes = 0;
+
        role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
 
        if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
                        ucsi_unregister_partner(con);
        }
 
-       if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) {
-               /*
-                * We don't need to know the currently supported alt modes here.
-                * Running GET_CAM_SUPPORTED command just to make sure the PPM
-                * does not get stuck in case it assumes we do so.
-                */
-               command = UCSI_GET_CAM_SUPPORTED;
-               command |= UCSI_CONNECTOR_NUMBER(con->num);
-               ucsi_send_command(con->ucsi, command, NULL, 0);
-       }
-
        if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
                ucsi_partner_change(con);
 
-       ret = ucsi_acknowledge_connector_change(ucsi);
-       if (ret)
-               dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
-
        trace_ucsi_connector_change(con->num, &con->status);
 
 out_unlock:
-       clear_bit(EVENT_PENDING, &ucsi->flags);
+       if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) {
+               schedule_work(&con->work);
+               mutex_unlock(&con->lock);
+               return;
+       }
+
+       clear_bit(EVENT_PROCESSING, &ucsi->flags);
        mutex_unlock(&con->lock);
 }
 
                return;
        }
 
-       if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
+       set_bit(EVENT_PENDING, &ucsi->flags);
+
+       if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags))
                schedule_work(&con->work);
 }
 EXPORT_SYMBOL_GPL(ucsi_connector_change);