Loading drivers/usb/pd/policy_engine.c +112 −86 Original line number Diff line number Diff line Loading @@ -299,6 +299,7 @@ struct usbpd { enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; bool vbus_present; bool pd_allowed; enum data_role current_dr; enum power_role current_pr; Loading Loading @@ -459,6 +460,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) { union power_supply_propval val; u32 first_pdo = src_caps[0]; /* save the PDOs so userspace can further evaluate */ Loading @@ -474,6 +476,10 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) pd->peer_pr_swap = PD_SRC_PDO_FIXED_PR_SWAP(first_pdo); pd->peer_dr_swap = PD_SRC_PDO_FIXED_DR_SWAP(first_pdo); val.intval = PD_SRC_PDO_FIXED_USB_SUSP(first_pdo); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1); Loading Loading @@ -625,7 +631,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SRC_STARTUP: if (pd->current_dr == DR_NONE) { pd->current_dr = DR_DFP; /* Defer starting USB host mode until after PD */ /* * Defer starting USB host mode until PE_SRC_READY or * when PE_SRC_SEND_CAPABILITIES fails */ } /* Set CC back to DRP toggle for the next disconnect */ Loading Loading @@ -660,7 +669,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_state = PE_SRC_SEND_CAPABILITIES; if (pd->in_pr_swap) { pd->in_pr_swap = false; kick_sm(pd, SWAP_SOURCE_START_TIME); break; } Loading Loading @@ -820,6 +828,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } if (!pd->pd_allowed) break; /* Reset protocol layer */ pd->tx_msgid = 0; pd->rx_msgid = -1; Loading Loading @@ -847,7 +858,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->pd_phy_opened = true; } pd->in_pr_swap = false; pd->current_voltage = 5000000; pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES; Loading Loading @@ -1373,11 +1383,11 @@ static void usbpd_sm(struct work_struct *w) ctrl_recvd = pd->rx_msg_type; /* Disconnect? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) { if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) { if (pd->current_state == PE_UNKNOWN) goto sm_done; usbpd_info(&pd->dev, "USB PD disconnect\n"); usbpd_info(&pd->dev, "USB Type-C disconnect\n"); if (pd->pd_phy_opened) { pd_phy_close(); Loading @@ -1399,6 +1409,10 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); Loading Loading @@ -1484,6 +1498,10 @@ static void usbpd_sm(struct work_struct *w) } break; case PE_SRC_STARTUP: usbpd_set_state(pd, PE_SRC_STARTUP); break; case PE_SRC_SEND_CAPABILITIES: ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading Loading @@ -1621,6 +1639,10 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); break; case PE_SNK_STARTUP: usbpd_set_state(pd, PE_SNK_STARTUP); break; case PE_SNK_WAIT_FOR_CAPABILITIES: if (data_recvd == MSG_SOURCE_CAPABILITIES) { val.intval = 0; Loading Loading @@ -1668,9 +1690,14 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); val.intval = 0; /* suspend charging */ /* * disable charging; technically we are allowed to * charge up to pSnkStdby (2.5 W) during this * transition, but disable it just for simplicity. */ val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); pd->selected_pdo = pd->requested_pdo; usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); Loading Loading @@ -1701,7 +1728,7 @@ static void usbpd_sm(struct work_struct *w) /* resume charging */ val.intval = pd->requested_current * 1000; /* mA->uA */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); usbpd_set_state(pd, PE_SNK_READY); } else { Loading Loading @@ -1853,7 +1880,7 @@ static void usbpd_sm(struct work_struct *w) if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } val.intval = pd->requested_voltage; Loading Loading @@ -2008,9 +2035,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) { struct usbpd *pd = container_of(nb, struct usbpd, psy_nb); union power_supply_propval val; bool pd_allowed; enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; bool do_work = false; int ret; if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED) Loading @@ -2024,7 +2050,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } pd_allowed = val.intval; if (pd->pd_allowed != val.intval) do_work = true; pd->pd_allowed = val.intval; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PRESENT, &val); Loading @@ -2044,38 +2072,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) typec_mode = val.intval; /* * Don't proceed if cable is connected but PD_ALLOWED is false. * It means the PMIC may still be in the middle of performing * charger type detection. */ if (!pd_allowed && typec_mode != POWER_SUPPLY_TYPEC_NONE) return 0; /* * Workaround for PMIC HW bug. * * During hard reset or PR swap (sink to source) when VBUS goes to 0 * the CC logic will report this as a disconnection. In those cases it * can be ignored, however the downside is that pd->hard_reset can be * momentarily true even when a non-PD capable source is attached, and * can't be distinguished from a physical disconnect. In that case, * allow for the common case of disconnecting from an SDP. * * The less common case is a PD-capable SDP which will result in a * hard reset getting treated like a disconnect. We can live with this * until the HW bug is fixed: in which disconnection won't be reported * on VBUS loss alone unless pullup is also removed from CC. */ if (typec_mode == POWER_SUPPLY_TYPEC_NONE && (pd->in_pr_swap || (pd->psy_type != POWER_SUPPLY_TYPE_USB && pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT))) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to %s\n", pd->in_pr_swap ? "PR swap" : "hard reset"); return 0; } ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPE, &val); if (ret) { Loading @@ -2083,23 +2079,52 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } psy_type = val.intval; if (pd->psy_type != val.intval) do_work = true; pd->psy_type = val.intval; usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", typec_mode, pd->vbus_present, psy_type, typec_mode, pd->vbus_present, pd->psy_type, usbpd_get_plug_orientation(pd)); /* any change? */ if (pd->typec_mode == typec_mode && pd->psy_type == psy_type) return 0; if (pd->typec_mode != typec_mode) { pd->typec_mode = typec_mode; pd->psy_type = psy_type; do_work = true; switch (typec_mode) { /* Disconnect */ case POWER_SUPPLY_TYPEC_NONE: kick_sm(pd, 0); if (pd->in_pr_swap) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n"); do_work = false; } /* * Workaround for PMIC HW bug. * * During hard reset when VBUS goes to 0 the CC logic * will report this as a disconnection. In those cases * it can be ignored, however the downside is that * pd->hard_reset can be momentarily true even when a * non-PD capable source is attached, and can't be * distinguished from a physical disconnect. In that * case, allow for the common case of disconnecting * from an SDP. * * The less common case is a PD-capable SDP which will * result in a hard reset getting treated like a * disconnect. We can live with this until the HW bug * is fixed: in which disconnection won't be reported * on VBUS loss alone unless pullup is also removed * from CC. */ if (pd->psy_type != POWER_SUPPLY_TYPE_USB && pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n"); do_work = false; } break; /* Sink states */ Loading @@ -2108,11 +2133,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) case POWER_SUPPLY_TYPEC_SOURCE_HIGH: usbpd_info(&pd->dev, "Type-C Source (%s) connected\n", src_current(typec_mode)); if (pd->current_pr != PR_SINK || pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) { pd->current_pr = PR_SINK; kick_sm(pd, 0); } pd->in_pr_swap = false; break; /* Source states */ Loading @@ -2121,10 +2143,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Sink%s connected\n", typec_mode == POWER_SUPPLY_TYPEC_SINK ? "" : " (powered)"); if (pd->current_pr != PR_SRC) { pd->current_pr = PR_SRC; kick_sm(pd, 0); } pd->in_pr_swap = false; break; case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: Loading @@ -2134,9 +2154,15 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); break; default: usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode); usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode); break; } } /* only queue state machine if CC state or PD_ALLOWED changes */ if (do_work) kick_sm(pd, 0); return 0; } Loading Loading
drivers/usb/pd/policy_engine.c +112 −86 Original line number Diff line number Diff line Loading @@ -299,6 +299,7 @@ struct usbpd { enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; bool vbus_present; bool pd_allowed; enum data_role current_dr; enum power_role current_pr; Loading Loading @@ -459,6 +460,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) { union power_supply_propval val; u32 first_pdo = src_caps[0]; /* save the PDOs so userspace can further evaluate */ Loading @@ -474,6 +476,10 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) pd->peer_pr_swap = PD_SRC_PDO_FIXED_PR_SWAP(first_pdo); pd->peer_dr_swap = PD_SRC_PDO_FIXED_DR_SWAP(first_pdo); val.intval = PD_SRC_PDO_FIXED_USB_SUSP(first_pdo); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1); Loading Loading @@ -625,7 +631,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SRC_STARTUP: if (pd->current_dr == DR_NONE) { pd->current_dr = DR_DFP; /* Defer starting USB host mode until after PD */ /* * Defer starting USB host mode until PE_SRC_READY or * when PE_SRC_SEND_CAPABILITIES fails */ } /* Set CC back to DRP toggle for the next disconnect */ Loading Loading @@ -660,7 +669,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_state = PE_SRC_SEND_CAPABILITIES; if (pd->in_pr_swap) { pd->in_pr_swap = false; kick_sm(pd, SWAP_SOURCE_START_TIME); break; } Loading Loading @@ -820,6 +828,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } if (!pd->pd_allowed) break; /* Reset protocol layer */ pd->tx_msgid = 0; pd->rx_msgid = -1; Loading Loading @@ -847,7 +858,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->pd_phy_opened = true; } pd->in_pr_swap = false; pd->current_voltage = 5000000; pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES; Loading Loading @@ -1373,11 +1383,11 @@ static void usbpd_sm(struct work_struct *w) ctrl_recvd = pd->rx_msg_type; /* Disconnect? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) { if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) { if (pd->current_state == PE_UNKNOWN) goto sm_done; usbpd_info(&pd->dev, "USB PD disconnect\n"); usbpd_info(&pd->dev, "USB Type-C disconnect\n"); if (pd->pd_phy_opened) { pd_phy_close(); Loading @@ -1399,6 +1409,10 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); Loading Loading @@ -1484,6 +1498,10 @@ static void usbpd_sm(struct work_struct *w) } break; case PE_SRC_STARTUP: usbpd_set_state(pd, PE_SRC_STARTUP); break; case PE_SRC_SEND_CAPABILITIES: ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading Loading @@ -1621,6 +1639,10 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); break; case PE_SNK_STARTUP: usbpd_set_state(pd, PE_SNK_STARTUP); break; case PE_SNK_WAIT_FOR_CAPABILITIES: if (data_recvd == MSG_SOURCE_CAPABILITIES) { val.intval = 0; Loading Loading @@ -1668,9 +1690,14 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); val.intval = 0; /* suspend charging */ /* * disable charging; technically we are allowed to * charge up to pSnkStdby (2.5 W) during this * transition, but disable it just for simplicity. */ val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); pd->selected_pdo = pd->requested_pdo; usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); Loading Loading @@ -1701,7 +1728,7 @@ static void usbpd_sm(struct work_struct *w) /* resume charging */ val.intval = pd->requested_current * 1000; /* mA->uA */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); usbpd_set_state(pd, PE_SNK_READY); } else { Loading Loading @@ -1853,7 +1880,7 @@ static void usbpd_sm(struct work_struct *w) if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } val.intval = pd->requested_voltage; Loading Loading @@ -2008,9 +2035,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) { struct usbpd *pd = container_of(nb, struct usbpd, psy_nb); union power_supply_propval val; bool pd_allowed; enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; bool do_work = false; int ret; if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED) Loading @@ -2024,7 +2050,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } pd_allowed = val.intval; if (pd->pd_allowed != val.intval) do_work = true; pd->pd_allowed = val.intval; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PRESENT, &val); Loading @@ -2044,38 +2072,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) typec_mode = val.intval; /* * Don't proceed if cable is connected but PD_ALLOWED is false. * It means the PMIC may still be in the middle of performing * charger type detection. */ if (!pd_allowed && typec_mode != POWER_SUPPLY_TYPEC_NONE) return 0; /* * Workaround for PMIC HW bug. * * During hard reset or PR swap (sink to source) when VBUS goes to 0 * the CC logic will report this as a disconnection. In those cases it * can be ignored, however the downside is that pd->hard_reset can be * momentarily true even when a non-PD capable source is attached, and * can't be distinguished from a physical disconnect. In that case, * allow for the common case of disconnecting from an SDP. * * The less common case is a PD-capable SDP which will result in a * hard reset getting treated like a disconnect. We can live with this * until the HW bug is fixed: in which disconnection won't be reported * on VBUS loss alone unless pullup is also removed from CC. */ if (typec_mode == POWER_SUPPLY_TYPEC_NONE && (pd->in_pr_swap || (pd->psy_type != POWER_SUPPLY_TYPE_USB && pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT))) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to %s\n", pd->in_pr_swap ? "PR swap" : "hard reset"); return 0; } ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPE, &val); if (ret) { Loading @@ -2083,23 +2079,52 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) return ret; } psy_type = val.intval; if (pd->psy_type != val.intval) do_work = true; pd->psy_type = val.intval; usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", typec_mode, pd->vbus_present, psy_type, typec_mode, pd->vbus_present, pd->psy_type, usbpd_get_plug_orientation(pd)); /* any change? */ if (pd->typec_mode == typec_mode && pd->psy_type == psy_type) return 0; if (pd->typec_mode != typec_mode) { pd->typec_mode = typec_mode; pd->psy_type = psy_type; do_work = true; switch (typec_mode) { /* Disconnect */ case POWER_SUPPLY_TYPEC_NONE: kick_sm(pd, 0); if (pd->in_pr_swap) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n"); do_work = false; } /* * Workaround for PMIC HW bug. * * During hard reset when VBUS goes to 0 the CC logic * will report this as a disconnection. In those cases * it can be ignored, however the downside is that * pd->hard_reset can be momentarily true even when a * non-PD capable source is attached, and can't be * distinguished from a physical disconnect. In that * case, allow for the common case of disconnecting * from an SDP. * * The less common case is a PD-capable SDP which will * result in a hard reset getting treated like a * disconnect. We can live with this until the HW bug * is fixed: in which disconnection won't be reported * on VBUS loss alone unless pullup is also removed * from CC. */ if (pd->psy_type != POWER_SUPPLY_TYPE_USB && pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n"); do_work = false; } break; /* Sink states */ Loading @@ -2108,11 +2133,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) case POWER_SUPPLY_TYPEC_SOURCE_HIGH: usbpd_info(&pd->dev, "Type-C Source (%s) connected\n", src_current(typec_mode)); if (pd->current_pr != PR_SINK || pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) { pd->current_pr = PR_SINK; kick_sm(pd, 0); } pd->in_pr_swap = false; break; /* Source states */ Loading @@ -2121,10 +2143,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Sink%s connected\n", typec_mode == POWER_SUPPLY_TYPEC_SINK ? "" : " (powered)"); if (pd->current_pr != PR_SRC) { pd->current_pr = PR_SRC; kick_sm(pd, 0); } pd->in_pr_swap = false; break; case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: Loading @@ -2134,9 +2154,15 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); break; default: usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode); usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode); break; } } /* only queue state machine if CC state or PD_ALLOWED changes */ if (do_work) kick_sm(pd, 0); return 0; } Loading