/* Copyright (c) 2016-2017, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbpd.h" /* To start USB stack for USB3.1 complaince testing */ static bool usb_compliance_mode; module_param(usb_compliance_mode, bool, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(usb_compliance_mode, "Start USB stack for USB3.1 compliance testing"); static bool disable_usb_pd; module_param(disable_usb_pd, bool, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(disable_usb_pd, "Disable USB PD for USB3.1 compliance testing"); static bool rev3_sink_only; module_param(rev3_sink_only, bool, 0644); MODULE_PARM_DESC(rev3_sink_only, "Enable power delivery rev3.0 sink only mode"); enum usbpd_state { PE_UNKNOWN, PE_ERROR_RECOVERY, PE_SRC_DISABLED, PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES, PE_SRC_SEND_CAPABILITIES_WAIT, /* substate to wait for Request */ PE_SRC_NEGOTIATE_CAPABILITY, PE_SRC_TRANSITION_SUPPLY, PE_SRC_READY, PE_SRC_HARD_RESET, PE_SRC_SOFT_RESET, PE_SRC_SEND_SOFT_RESET, PE_SRC_DISCOVERY, PE_SRC_TRANSITION_TO_DEFAULT, PE_SNK_STARTUP, PE_SNK_DISCOVERY, PE_SNK_WAIT_FOR_CAPABILITIES, PE_SNK_EVALUATE_CAPABILITY, PE_SNK_SELECT_CAPABILITY, PE_SNK_TRANSITION_SINK, PE_SNK_READY, PE_SNK_HARD_RESET, PE_SNK_SOFT_RESET, PE_SNK_SEND_SOFT_RESET, PE_SNK_TRANSITION_TO_DEFAULT, PE_DRS_SEND_DR_SWAP, PE_PRS_SNK_SRC_SEND_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_SOURCE_ON, PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_VCS_WAIT_FOR_VCONN, }; static const char * const usbpd_state_strings[] = { "UNKNOWN", "ERROR_RECOVERY", "SRC_Disabled", "SRC_Startup", "SRC_Send_Capabilities", "SRC_Send_Capabilities (Wait for Request)", "SRC_Negotiate_Capability", "SRC_Transition_Supply", "SRC_Ready", "SRC_Hard_Reset", "SRC_Soft_Reset", "SRC_Send_Soft_Reset", "SRC_Discovery", "SRC_Transition_to_default", "SNK_Startup", "SNK_Discovery", "SNK_Wait_for_Capabilities", "SNK_Evaluate_Capability", "SNK_Select_Capability", "SNK_Transition_Sink", "SNK_Ready", "SNK_Hard_Reset", "SNK_Soft_Reset", "SNK_Send_Soft_Reset", "SNK_Transition_to_default", "DRS_Send_DR_Swap", "PRS_SNK_SRC_Send_Swap", "PRS_SNK_SRC_Transition_to_off", "PRS_SNK_SRC_Source_on", "PRS_SRC_SNK_Send_Swap", "PRS_SRC_SNK_Transition_to_off", "PRS_SRC_SNK_Wait_Source_on", "VCS_Wait_for_VCONN", }; enum usbpd_control_msg_type { MSG_RESERVED = 0, MSG_GOODCRC, MSG_GOTOMIN, MSG_ACCEPT, MSG_REJECT, MSG_PING, MSG_PS_RDY, MSG_GET_SOURCE_CAP, MSG_GET_SINK_CAP, MSG_DR_SWAP, MSG_PR_SWAP, MSG_VCONN_SWAP, MSG_WAIT, MSG_SOFT_RESET, MSG_NOT_SUPPORTED = 0x10, MSG_GET_SOURCE_CAP_EXTENDED, MSG_GET_STATUS, MSG_FR_SWAP, MSG_GET_PPS_STATUS, MSG_GET_COUNTRY_CODES, }; enum usbpd_data_msg_type { MSG_SOURCE_CAPABILITIES = 1, MSG_REQUEST, MSG_BIST, MSG_SINK_CAPABILITIES, MSG_BATTERY_STATUS, MSG_ALERT, MSG_GET_COUNTRY_INFO, MSG_VDM = 0xF, }; enum usbpd_ext_msg_type { MSG_SOURCE_CAPABILITIES_EXTENDED = 1, MSG_STATUS, MSG_GET_BATTERY_CAP, MSG_GET_BATTERY_STATUS, MSG_BATTERY_CAPABILITIES, MSG_GET_MANUFACTURER_INFO, MSG_MANUFACTURER_INFO, MSG_SECURITY_REQUEST, MSG_SECURITY_RESPONSE, MSG_FIRMWARE_UPDATE_REQUEST, MSG_FIRMWARE_UPDATE_RESPONSE, MSG_PPS_STATUS, MSG_COUNTRY_INFO, MSG_COUNTRY_CODES, }; enum vdm_state { VDM_NONE, DISCOVERED_ID, DISCOVERED_SVIDS, DISCOVERED_MODES, MODE_ENTERED, MODE_EXITED, }; static void *usbpd_ipc_log; #define usbpd_dbg(dev, fmt, ...) do { \ ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \ ##__VA_ARGS__); \ dev_dbg(dev, fmt, ##__VA_ARGS__); \ } while (0) #define usbpd_info(dev, fmt, ...) do { \ ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \ ##__VA_ARGS__); \ dev_info(dev, fmt, ##__VA_ARGS__); \ } while (0) #define usbpd_warn(dev, fmt, ...) do { \ ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \ ##__VA_ARGS__); \ dev_warn(dev, fmt, ##__VA_ARGS__); \ } while (0) #define usbpd_err(dev, fmt, ...) do { \ ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \ ##__VA_ARGS__); \ dev_err(dev, fmt, ##__VA_ARGS__); \ } while (0) #define NUM_LOG_PAGES 10 /* Timeouts (in ms) */ #define ERROR_RECOVERY_TIME 25 #define SENDER_RESPONSE_TIME 26 #define SINK_WAIT_CAP_TIME 500 #define PS_TRANSITION_TIME 450 #define SRC_CAP_TIME 120 #define SRC_TRANSITION_TIME 25 #define SRC_RECOVER_TIME 750 #define PS_HARD_RESET_TIME 25 #define PS_SOURCE_ON 400 #define PS_SOURCE_OFF 750 #define FIRST_SOURCE_CAP_TIME 200 #define VDM_BUSY_TIME 50 #define VCONN_ON_TIME 100 /* tPSHardReset + tSafe0V */ #define SNK_HARD_RESET_VBUS_OFF_TIME (35 + 650) /* tSrcRecover + tSrcTurnOn */ #define SNK_HARD_RESET_VBUS_ON_TIME (1000 + 275) #define PD_CAPS_COUNT 50 #define PD_MAX_MSG_ID 7 #define PD_MAX_DATA_OBJ 7 #define PD_SRC_CAP_EXT_DB_LEN 24 #define PD_STATUS_DB_LEN 5 #define PD_BATTERY_CAP_DB_LEN 9 #define PD_MAX_EXT_MSG_LEN 260 #define PD_MAX_EXT_MSG_LEGACY_LEN 26 #define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \ (((type) & 0x1F) | ((dr) << 5) | (rev << 6) | \ ((pr) << 8) | ((id) << 9) | ((cnt) << 12)) #define PD_MSG_HDR_COUNT(hdr) (((hdr) >> 12) & 7) #define PD_MSG_HDR_TYPE(hdr) ((hdr) & 0x1F) #define PD_MSG_HDR_ID(hdr) (((hdr) >> 9) & 7) #define PD_MSG_HDR_REV(hdr) (((hdr) >> 6) & 3) #define PD_MSG_HDR_EXTENDED BIT(15) #define PD_MSG_HDR_IS_EXTENDED(hdr) ((hdr) & PD_MSG_HDR_EXTENDED) #define PD_MSG_EXT_HDR(chunked, num, req, size) \ (((chunked) << 15) | (((num) & 0xF) << 11) | \ ((req) << 10) | ((size) & 0x1FF)) #define PD_MSG_EXT_HDR_IS_CHUNKED(ehdr) ((ehdr) & 0x8000) #define PD_MSG_EXT_HDR_CHUNK_NUM(ehdr) (((ehdr) >> 11) & 0xF) #define PD_MSG_EXT_HDR_REQ_CHUNK(ehdr) ((ehdr) & 0x400) #define PD_MSG_EXT_HDR_DATA_SIZE(ehdr) ((ehdr) & 0x1FF) #define PD_RDO_FIXED(obj, gb, mismatch, usb_comm, no_usb_susp, curr1, curr2) \ (((obj) << 28) | ((gb) << 27) | ((mismatch) << 26) | \ ((usb_comm) << 25) | ((no_usb_susp) << 24) | \ ((curr1) << 10) | (curr2)) #define PD_RDO_AUGMENTED(obj, mismatch, usb_comm, no_usb_susp, volt, curr) \ (((obj) << 28) | ((mismatch) << 26) | ((usb_comm) << 25) | \ ((no_usb_susp) << 24) | ((volt) << 9) | (curr)) #define PD_RDO_OBJ_POS(rdo) ((rdo) >> 28 & 7) #define PD_RDO_GIVEBACK(rdo) ((rdo) >> 27 & 1) #define PD_RDO_MISMATCH(rdo) ((rdo) >> 26 & 1) #define PD_RDO_USB_COMM(rdo) ((rdo) >> 25 & 1) #define PD_RDO_NO_USB_SUSP(rdo) ((rdo) >> 24 & 1) #define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 10 & 0x3FF) #define PD_RDO_FIXED_CURR_MINMAX(rdo) ((rdo) & 0x3FF) #define PD_RDO_PROG_VOLTAGE(rdo) ((rdo) >> 9 & 0x7FF) #define PD_RDO_PROG_CURR(rdo) ((rdo) & 0x7F) #define PD_SRC_PDO_TYPE(pdo) (((pdo) >> 30) & 3) #define PD_SRC_PDO_TYPE_FIXED 0 #define PD_SRC_PDO_TYPE_BATTERY 1 #define PD_SRC_PDO_TYPE_VARIABLE 2 #define PD_SRC_PDO_TYPE_AUGMENTED 3 #define PD_SRC_PDO_FIXED_PR_SWAP(pdo) (((pdo) >> 29) & 1) #define PD_SRC_PDO_FIXED_USB_SUSP(pdo) (((pdo) >> 28) & 1) #define PD_SRC_PDO_FIXED_EXT_POWERED(pdo) (((pdo) >> 27) & 1) #define PD_SRC_PDO_FIXED_USB_COMM(pdo) (((pdo) >> 26) & 1) #define PD_SRC_PDO_FIXED_DR_SWAP(pdo) (((pdo) >> 25) & 1) #define PD_SRC_PDO_FIXED_PEAK_CURR(pdo) (((pdo) >> 20) & 3) #define PD_SRC_PDO_FIXED_VOLTAGE(pdo) (((pdo) >> 10) & 0x3FF) #define PD_SRC_PDO_FIXED_MAX_CURR(pdo) ((pdo) & 0x3FF) #define PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) (((pdo) >> 20) & 0x3FF) #define PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) (((pdo) >> 10) & 0x3FF) #define PD_SRC_PDO_VAR_BATT_MAX(pdo) ((pdo) & 0x3FF) #define PD_APDO_PPS(pdo) (((pdo) >> 28) & 3) #define PD_APDO_MAX_VOLT(pdo) (((pdo) >> 17) & 0xFF) #define PD_APDO_MIN_VOLT(pdo) (((pdo) >> 8) & 0xFF) #define PD_APDO_MAX_CURR(pdo) ((pdo) & 0x7F) /* Vendor Defined Messages */ #define MAX_CRC_RECEIVE_TIME 9 /* ~(2 * tReceive_max(1.1ms) * # retry 4) */ #define MAX_VDM_RESPONSE_TIME 60 /* 2 * tVDMSenderResponse_max(30ms) */ #define MAX_VDM_BUSY_TIME 100 /* 2 * tVDMBusy (50ms) */ #define PD_SNK_PDO_FIXED(prs, hc, uc, usb_comm, drs, volt, curr) \ (((prs) << 29) | ((hc) << 28) | ((uc) << 27) | ((usb_comm) << 26) | \ ((drs) << 25) | ((volt) << 10) | (curr)) /* VDM header is the first 32-bit object following the 16-bit PD header */ #define VDM_HDR_SVID(hdr) ((hdr) >> 16) #define VDM_IS_SVDM(hdr) ((hdr) & 0x8000) #define SVDM_HDR_OBJ_POS(hdr) (((hdr) >> 8) & 0x7) #define SVDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) #define SVDM_HDR_CMD(hdr) ((hdr) & 0x1f) #define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \ (((svid) << 16) | (1 << 15) | ((ver) << 13) \ | ((obj) << 8) | ((cmd_type) << 6) | (cmd)) /* discover id response vdo bit fields */ #define ID_HDR_USB_HOST BIT(31) #define ID_HDR_USB_DEVICE BIT(30) #define ID_HDR_MODAL_OPR BIT(26) #define ID_HDR_PRODUCT_TYPE(n) ((n) >> 27) #define ID_HDR_PRODUCT_PER_MASK (2 << 27) #define ID_HDR_PRODUCT_HUB 1 #define ID_HDR_PRODUCT_PER 2 #define ID_HDR_PRODUCT_AMA 5 #define ID_HDR_VID 0x05c6 /* qcom */ #define PROD_VDO_PID 0x0a00 /* TBD */ static bool check_vsafe0v = true; module_param(check_vsafe0v, bool, S_IRUSR | S_IWUSR); static int min_sink_current = 900; module_param(min_sink_current, int, S_IRUSR | S_IWUSR); static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ static const u32 default_snk_caps[] = { 0x2601912C }; /* VSafe5V @ 3A */ struct vdm_tx { u32 data[PD_MAX_DATA_OBJ]; int size; }; struct rx_msg { u16 hdr; u16 data_len; /* size of payload in bytes */ struct list_head entry; u8 payload[]; }; #define IS_DATA(m, t) ((m) && !PD_MSG_HDR_IS_EXTENDED((m)->hdr) && \ PD_MSG_HDR_COUNT((m)->hdr) && \ (PD_MSG_HDR_TYPE((m)->hdr) == (t))) #define IS_CTRL(m, t) ((m) && !PD_MSG_HDR_COUNT((m)->hdr) && \ (PD_MSG_HDR_TYPE((m)->hdr) == (t))) #define IS_EXT(m, t) ((m) && PD_MSG_HDR_IS_EXTENDED((m)->hdr) && \ (PD_MSG_HDR_TYPE((m)->hdr) == (t))) struct usbpd { struct device dev; struct workqueue_struct *wq; struct work_struct sm_work; struct hrtimer timer; bool sm_queued; struct extcon_dev *extcon; enum usbpd_state current_state; bool hard_reset_recvd; ktime_t hard_reset_recvd_time; struct list_head rx_q; spinlock_t rx_lock; struct rx_msg *rx_ext_msg; u32 received_pdos[PD_MAX_DATA_OBJ]; u32 received_ado; u16 src_cap_id; u8 selected_pdo; u8 requested_pdo; u32 rdo; /* can be either source or sink */ int current_voltage; /* uV */ int requested_voltage; /* uV */ int requested_current; /* mA */ bool pd_connected; bool in_explicit_contract; bool peer_usb_comm; bool peer_pr_swap; bool peer_dr_swap; u32 sink_caps[7]; int num_sink_caps; struct power_supply *usb_psy; struct notifier_block psy_nb; enum power_supply_typec_mode typec_mode; enum power_supply_type psy_type; enum power_supply_typec_power_role forced_pr; bool vbus_present; enum pd_spec_rev spec_rev; enum data_role current_dr; enum power_role current_pr; bool in_pr_swap; bool pd_phy_opened; bool send_request; struct completion is_ready; struct completion tx_chunk_request; u8 next_tx_chunk; struct mutex swap_lock; struct dual_role_phy_instance *dual_role; struct dual_role_phy_desc dr_desc; bool send_pr_swap; bool send_dr_swap; struct regulator *vbus; struct regulator *vconn; bool vbus_enabled; bool vconn_enabled; bool vconn_is_external; u8 tx_msgid; u8 rx_msgid; int caps_count; int hard_reset_count; enum vdm_state vdm_state; u16 *discovered_svids; int num_svids; struct vdm_tx *vdm_tx; struct vdm_tx *vdm_tx_retry; struct list_head svid_handlers; struct list_head instance; /* ext msg support */ bool send_get_src_cap_ext; u8 src_cap_ext_db[PD_SRC_CAP_EXT_DB_LEN]; bool send_get_pps_status; u32 pps_status_db; u8 status_db[PD_STATUS_DB_LEN]; bool send_get_battery_cap; u8 get_battery_cap_db; u8 battery_cap_db[PD_BATTERY_CAP_DB_LEN]; u8 get_battery_status_db; bool send_get_battery_status; u32 battery_sts_dobj; }; static LIST_HEAD(_usbpd); /* useful for debugging */ static const unsigned int usbpd_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, EXTCON_USB_CC, EXTCON_USB_SPEED, EXTCON_USB_TYPEC_MED_HIGH_CURRENT, EXTCON_NONE, }; /* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ static const u32 usbpd_extcon_exclusive[] = {0x3, 0}; enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) { int ret; union power_supply_propval val; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, &val); if (ret) return ORIENTATION_NONE; return val.intval; } EXPORT_SYMBOL(usbpd_get_plug_orientation); static inline void stop_usb_host(struct usbpd *pd) { extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0); } static inline void start_usb_host(struct usbpd *pd, bool ss) { enum plug_orientation cc = usbpd_get_plug_orientation(pd); extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, cc == ORIENTATION_CC2); extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss); extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1); } static inline void stop_usb_peripheral(struct usbpd *pd) { extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0); } static inline void start_usb_peripheral(struct usbpd *pd) { enum plug_orientation cc = usbpd_get_plug_orientation(pd); extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, cc == ORIENTATION_CC2); extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, 1); extcon_set_cable_state_(pd->extcon, EXTCON_USB_TYPEC_MED_HIGH_CURRENT, pd->typec_mode > POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ? 1 : 0); extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1); } static int set_power_role(struct usbpd *pd, enum power_role pr) { union power_supply_propval val = {0}; switch (pr) { case PR_NONE: val.intval = POWER_SUPPLY_TYPEC_PR_NONE; break; case PR_SINK: val.intval = POWER_SUPPLY_TYPEC_PR_SINK; break; case PR_SRC: val.intval = POWER_SUPPLY_TYPEC_PR_SOURCE; break; } return power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); } static struct usbpd_svid_handler *find_svid_handler(struct usbpd *pd, u16 svid) { struct usbpd_svid_handler *handler; list_for_each_entry(handler, &pd->svid_handlers, entry) if (svid == handler->svid) return handler; return NULL; } /* Reset protocol layer */ static inline void pd_reset_protocol(struct usbpd *pd) { /* * first Rx ID should be 0; set this to a sentinel of -1 so that in * phy_msg_received() we can check if we had seen it before. */ pd->rx_msgid = -1; pd->tx_msgid = 0; pd->send_request = false; pd->send_pr_swap = false; pd->send_dr_swap = false; } static int pd_send_msg(struct usbpd *pd, u8 msg_type, const u32 *data, size_t num_data, enum pd_sop_type sop) { int ret; u16 hdr; if (pd->hard_reset_recvd) return -EBUSY; hdr = PD_MSG_HDR(msg_type, pd->current_dr, pd->current_pr, pd->tx_msgid, num_data, pd->spec_rev); ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), sop); if (ret) return ret; pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; return 0; } static int pd_send_ext_msg(struct usbpd *pd, u8 msg_type, const u8 *data, size_t data_len, enum pd_sop_type sop) { int ret; size_t len_remain, chunk_len; u8 chunked_payload[PD_MAX_DATA_OBJ * sizeof(u32)] = {0}; u16 hdr; u16 ext_hdr; u8 num_objs; if (data_len > PD_MAX_EXT_MSG_LEN) { usbpd_warn(&pd->dev, "Extended message length exceeds max, truncating...\n"); data_len = PD_MAX_EXT_MSG_LEN; } pd->next_tx_chunk = 0; len_remain = data_len; do { ext_hdr = PD_MSG_EXT_HDR(1, pd->next_tx_chunk++, 0, data_len); memcpy(chunked_payload, &ext_hdr, sizeof(ext_hdr)); chunk_len = min_t(size_t, len_remain, PD_MAX_EXT_MSG_LEGACY_LEN); memcpy(chunked_payload + sizeof(ext_hdr), data, chunk_len); num_objs = DIV_ROUND_UP(chunk_len + sizeof(u16), sizeof(u32)); len_remain -= chunk_len; reinit_completion(&pd->tx_chunk_request); hdr = PD_MSG_HDR(msg_type, pd->current_dr, pd->current_pr, pd->tx_msgid, num_objs, pd->spec_rev) | PD_MSG_HDR_EXTENDED; ret = pd_phy_write(hdr, chunked_payload, num_objs * sizeof(u32), sop); if (ret) return ret; pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; /* Wait for request chunk */ if (len_remain && !wait_for_completion_timeout(&pd->tx_chunk_request, msecs_to_jiffies(SENDER_RESPONSE_TIME))) { usbpd_err(&pd->dev, "Timed out waiting for chunk request\n"); return -EPROTO; } } while (len_remain); return 0; } static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) { int curr; int max_current; bool mismatch = false; u8 type; u32 pdo = pd->received_pdos[pdo_pos - 1]; type = PD_SRC_PDO_TYPE(pdo); if (type == PD_SRC_PDO_TYPE_FIXED) { curr = max_current = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; /* * Check if the PDO has enough current, otherwise set the * Capability Mismatch flag */ if (curr < min_sink_current) { mismatch = true; max_current = min_sink_current; } pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10, max_current / 10); } else if (type == PD_SRC_PDO_TYPE_AUGMENTED) { if ((uv / 100000) > PD_APDO_MAX_VOLT(pdo) || (uv / 100000) < PD_APDO_MIN_VOLT(pdo) || (ua / 50000) > PD_APDO_MAX_CURR(pdo) || (ua < 0)) { usbpd_err(&pd->dev, "uv (%d) and ua (%d) out of range of APDO\n", uv, ua); return -EINVAL; } curr = ua / 1000; pd->requested_voltage = uv; pd->rdo = PD_RDO_AUGMENTED(pdo_pos, mismatch, 1, 1, uv / 20000, ua / 50000); } else { usbpd_err(&pd->dev, "Only Fixed or Programmable PDOs supported\n"); return -ENOTSUPP; } /* Can't sink more than 5V if VCONN is sourced from the VBUS input */ if (pd->vconn_enabled && !pd->vconn_is_external && pd->requested_voltage > 5000000) return -ENOTSUPP; pd->requested_current = curr; pd->requested_pdo = pdo_pos; return 0; } static int pd_eval_src_caps(struct usbpd *pd) { int i; union power_supply_propval val; u32 first_pdo = pd->received_pdos[0]; if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) { usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo); return -EINVAL; } pd->peer_usb_comm = PD_SRC_PDO_FIXED_USB_COMM(first_pdo); 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); if (pd->spec_rev == USBPD_REV_30 && !rev3_sink_only) { bool pps_found = false; /* downgrade to 2.0 if no PPS */ for (i = 1; i < PD_MAX_DATA_OBJ; i++) { if ((PD_SRC_PDO_TYPE(pd->received_pdos[i]) == PD_SRC_PDO_TYPE_AUGMENTED) && !PD_APDO_PPS(pd->received_pdos[i])) { pps_found = true; break; } } if (!pps_found) pd->spec_rev = USBPD_REV_20; } /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1, 0, 0); return 0; } static void pd_send_hard_reset(struct usbpd *pd) { union power_supply_propval val = {0}; usbpd_dbg(&pd->dev, "send hard reset"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_count++; pd_phy_signal(HARD_RESET_SIG); pd->in_pr_swap = false; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } static void kick_sm(struct usbpd *pd, int ms) { pm_stay_awake(&pd->dev); pd->sm_queued = true; if (ms) hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL); else queue_work(pd->wq, &pd->sm_work); } static void phy_sig_received(struct usbpd *pd, enum pd_sig_type sig) { union power_supply_propval val = {1}; if (sig != HARD_RESET_SIG) { usbpd_err(&pd->dev, "invalid signal (%d) received\n", sig); return; } pd->hard_reset_recvd = true; pd->hard_reset_recvd_time = ktime_get(); usbpd_err(&pd->dev, "hard reset received\n"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); kick_sm(pd, 0); } struct pd_request_chunk { struct work_struct w; struct usbpd *pd; u8 msg_type; u8 chunk_num; enum pd_sop_type sop; }; static void pd_request_chunk_work(struct work_struct *w) { struct pd_request_chunk *req = container_of(w, struct pd_request_chunk, w); struct usbpd *pd = req->pd; unsigned long flags; int ret; u8 payload[4] = {0}; /* ext_hdr + padding */ u16 hdr = PD_MSG_HDR(req->msg_type, pd->current_dr, pd->current_pr, pd->tx_msgid, 1, pd->spec_rev) | PD_MSG_HDR_EXTENDED; *(u16 *)payload = PD_MSG_EXT_HDR(1, req->chunk_num, 1, 0); ret = pd_phy_write(hdr, payload, sizeof(payload), req->sop); if (!ret) { pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; } else { usbpd_err(&pd->dev, "could not send chunk request\n"); /* queue what we have anyway */ spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&pd->rx_ext_msg->entry, &pd->rx_q); spin_unlock_irqrestore(&pd->rx_lock, flags); pd->rx_ext_msg = NULL; } kfree(req); } static struct rx_msg *pd_ext_msg_received(struct usbpd *pd, u16 header, u8 *buf, size_t len, enum pd_sop_type sop) { struct rx_msg *rx_msg; u16 bytes_to_copy; u16 ext_hdr = *(u16 *)buf; u8 chunk_num; if (!PD_MSG_EXT_HDR_IS_CHUNKED(ext_hdr)) { usbpd_err(&pd->dev, "unchunked extended messages unsupported\n"); return NULL; } /* request for next Tx chunk */ if (PD_MSG_EXT_HDR_REQ_CHUNK(ext_hdr)) { if (PD_MSG_EXT_HDR_DATA_SIZE(ext_hdr) || PD_MSG_EXT_HDR_CHUNK_NUM(ext_hdr) != pd->next_tx_chunk) { usbpd_err(&pd->dev, "invalid request chunk ext header 0x%02x\n", ext_hdr); return NULL; } if (!completion_done(&pd->tx_chunk_request)) complete(&pd->tx_chunk_request); return NULL; } chunk_num = PD_MSG_EXT_HDR_CHUNK_NUM(ext_hdr); if (!chunk_num) { /* allocate new message if first chunk */ rx_msg = kzalloc(sizeof(*rx_msg) + PD_MSG_EXT_HDR_DATA_SIZE(ext_hdr), GFP_ATOMIC); if (!rx_msg) return NULL; rx_msg->hdr = header; rx_msg->data_len = PD_MSG_EXT_HDR_DATA_SIZE(ext_hdr); if (rx_msg->data_len > PD_MAX_EXT_MSG_LEN) { usbpd_warn(&pd->dev, "Extended message length exceeds max, truncating...\n"); rx_msg->data_len = PD_MAX_EXT_MSG_LEN; } } else { if (!pd->rx_ext_msg) { usbpd_err(&pd->dev, "missing first rx_ext_msg chunk\n"); return NULL; } rx_msg = pd->rx_ext_msg; } /* * The amount to copy is derived as follows: * * - if extended data_len < 26, then copy data_len bytes * - for chunks 0..N-2, copy 26 bytes * - for the last chunk (N-1), copy the remainder */ bytes_to_copy = min((rx_msg->data_len - chunk_num * PD_MAX_EXT_MSG_LEGACY_LEN), PD_MAX_EXT_MSG_LEGACY_LEN); /* check against received length to avoid overrun */ if (bytes_to_copy > len - sizeof(ext_hdr)) { usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%zu\n", bytes_to_copy, len - sizeof(ext_hdr)); bytes_to_copy = len - sizeof(ext_hdr); } memcpy(rx_msg->payload + chunk_num * PD_MAX_EXT_MSG_LEGACY_LEN, buf + 2, bytes_to_copy); /* request next chunk? */ if ((rx_msg->data_len - chunk_num * PD_MAX_EXT_MSG_LEGACY_LEN) > PD_MAX_EXT_MSG_LEGACY_LEN) { struct pd_request_chunk *req; if (pd->rx_ext_msg && pd->rx_ext_msg != rx_msg) { usbpd_dbg(&pd->dev, "stale previous rx_ext_msg?\n"); kfree(pd->rx_ext_msg); } pd->rx_ext_msg = rx_msg; req = kzalloc(sizeof(*req), GFP_ATOMIC); if (!req) goto queue_rx; /* return what we have anyway */ INIT_WORK(&req->w, pd_request_chunk_work); req->pd = pd; req->msg_type = PD_MSG_HDR_TYPE(header); req->chunk_num = chunk_num + 1; req->sop = sop; queue_work(pd->wq, &req->w); return NULL; } queue_rx: pd->rx_ext_msg = NULL; return rx_msg; /* queue it for usbpd_sm */ } static void phy_msg_received(struct usbpd *pd, enum pd_sop_type sop, u8 *buf, size_t len) { struct rx_msg *rx_msg; unsigned long flags; u16 header; if (sop != SOP_MSG) { usbpd_err(&pd->dev, "invalid msg type (%d) received; only SOP supported\n", sop); return; } if (len < 2) { usbpd_err(&pd->dev, "invalid message received, len=%zd\n", len); return; } header = *((u16 *)buf); buf += sizeof(u16); len -= sizeof(u16); if (len % 4 != 0) { usbpd_err(&pd->dev, "len=%zd not multiple of 4\n", len); return; } /* if MSGID already seen, discard */ if (PD_MSG_HDR_ID(header) == pd->rx_msgid && PD_MSG_HDR_TYPE(header) != MSG_SOFT_RESET) { usbpd_dbg(&pd->dev, "MessageID already seen, discarding\n"); return; } pd->rx_msgid = PD_MSG_HDR_ID(header); /* discard Pings */ if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len) return; /* check header's count field to see if it matches len */ if (PD_MSG_HDR_COUNT(header) != (len / 4)) { usbpd_err(&pd->dev, "header count (%d) mismatch, len=%zd\n", PD_MSG_HDR_COUNT(header), len); return; } /* if spec rev differs (i.e. is older), update PHY */ if (PD_MSG_HDR_REV(header) < pd->spec_rev) pd->spec_rev = PD_MSG_HDR_REV(header); usbpd_dbg(&pd->dev, "received message: type(%d) num_objs(%d)\n", PD_MSG_HDR_TYPE(header), PD_MSG_HDR_COUNT(header)); if (!PD_MSG_HDR_IS_EXTENDED(header)) { rx_msg = kzalloc(sizeof(*rx_msg) + len, GFP_ATOMIC); if (!rx_msg) return; rx_msg->hdr = header; rx_msg->data_len = len; memcpy(rx_msg->payload, buf, len); } else { rx_msg = pd_ext_msg_received(pd, header, buf, len, sop); if (!rx_msg) return; } spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&rx_msg->entry, &pd->rx_q); spin_unlock_irqrestore(&pd->rx_lock, flags); kick_sm(pd, 0); } static void phy_shutdown(struct usbpd *pd) { usbpd_dbg(&pd->dev, "shutdown"); } static enum hrtimer_restart pd_timeout(struct hrtimer *timer) { struct usbpd *pd = container_of(timer, struct usbpd, timer); usbpd_dbg(&pd->dev, "timeout"); queue_work(pd->wq, &pd->sm_work); return HRTIMER_NORESTART; } /* Enters new state and executes actions on entry */ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) { struct pd_phy_params phy_params = { .signal_cb = phy_sig_received, .msg_rx_cb = phy_msg_received, .shutdown_cb = phy_shutdown, .frame_filter_val = FRAME_FILTER_EN_SOP | FRAME_FILTER_EN_HARD_RESET, }; union power_supply_propval val = {0}; unsigned long flags; int ret; if (pd->hard_reset_recvd) /* let usbpd_sm handle it */ return; usbpd_dbg(&pd->dev, "%s -> %s\n", usbpd_state_strings[pd->current_state], usbpd_state_strings[next_state]); pd->current_state = next_state; switch (next_state) { case PE_ERROR_RECOVERY: /* perform hard disconnect/reconnect */ pd->in_pr_swap = false; pd->current_pr = PR_NONE; set_power_role(pd, PR_NONE); pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; kick_sm(pd, 0); break; /* Source states */ case PE_SRC_DISABLED: /* are we still connected? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) { pd->current_pr = PR_NONE; kick_sm(pd, 0); } break; case PE_SRC_STARTUP: if (pd->current_dr == DR_NONE) { pd->current_dr = DR_DFP; /* * Defer starting USB host mode until PE_SRC_READY or * when PE_SRC_SEND_CAPABILITIES fails */ } dual_role_instance_changed(pd->dual_role); /* Set CC back to DRP toggle for the next disconnect */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); /* support only PD 2.0 as a source */ pd->spec_rev = USBPD_REV_20; pd_reset_protocol(pd); if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { pd_phy_close(); pd->pd_phy_opened = false; } phy_params.data_role = pd->current_dr; phy_params.power_role = pd->current_pr; ret = pd_phy_open(&phy_params); if (ret) { WARN_ON_ONCE(1); usbpd_err(&pd->dev, "error opening PD PHY %d\n", ret); pd->current_state = PE_UNKNOWN; return; } pd->pd_phy_opened = true; } if (pd->in_pr_swap) { pd->in_pr_swap = false; val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } /* * A sink might remove its terminations (during some Type-C * compliance tests or a sink attempting to do Try.SRC) * at this point just after we enabled VBUS. Sending PD * messages now would delay detecting the detach beyond the * required timing. Instead, delay sending out the first * source capabilities to allow for the other side to * completely settle CC debounce and allow HW to detect detach * sooner in the meantime. PD spec allows up to * tFirstSourceCap (250ms). */ pd->current_state = PE_SRC_SEND_CAPABILITIES; kick_sm(pd, FIRST_SOURCE_CAP_TIME); break; case PE_SRC_SEND_CAPABILITIES: kick_sm(pd, 0); break; case PE_SRC_NEGOTIATE_CAPABILITY: if (PD_RDO_OBJ_POS(pd->rdo) != 1 || PD_RDO_FIXED_CURR(pd->rdo) > PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) { /* send Reject */ ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Reject\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } usbpd_err(&pd->dev, "Invalid request: %08x\n", pd->rdo); if (pd->in_explicit_contract) usbpd_set_state(pd, PE_SRC_READY); else /* * bypass PE_SRC_Capability_Response and * PE_SRC_Wait_New_Capabilities in this * implementation for simplicity. */ usbpd_set_state(pd, PE_SRC_SEND_CAPABILITIES); break; } /* PE_SRC_TRANSITION_SUPPLY pseudo-state */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } /* tSrcTransition required after ACCEPT */ usleep_range(SRC_TRANSITION_TIME * USEC_PER_MSEC, (SRC_TRANSITION_TIME + 5) * USEC_PER_MSEC); /* * Normally a voltage change should occur within tSrcReady * but since we only support VSafe5V there is nothing more to * prepare from the power supply so send PS_RDY right away. */ ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } usbpd_set_state(pd, PE_SRC_READY); break; case PE_SRC_READY: pd->in_explicit_contract = true; if (pd->vdm_tx) kick_sm(pd, 0); else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE) usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_IDENTITY, SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); complete(&pd->is_ready); dual_role_instance_changed(pd->dual_role); break; case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* are we still connected? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) pd->current_pr = PR_NONE; /* hard reset may sleep; handle it in the workqueue */ kick_sm(pd, 0); break; case PE_SRC_SEND_SOFT_RESET: case PE_SNK_SEND_SOFT_RESET: pd_reset_protocol(pd); ret = pd_send_msg(pd, MSG_SOFT_RESET, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Soft Reset, do Hard Reset\n"); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); break; } /* wait for ACCEPT */ kick_sm(pd, SENDER_RESPONSE_TIME); break; /* Sink states */ case PE_SNK_STARTUP: if (pd->current_dr == DR_NONE || pd->current_dr == DR_UFP) { pd->current_dr = DR_UFP; if (pd->psy_type == POWER_SUPPLY_TYPE_USB || pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP || pd->psy_type == POWER_SUPPLY_TYPE_USB_FLOAT || usb_compliance_mode) start_usb_peripheral(pd); } dual_role_instance_changed(pd->dual_role); ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ALLOWED, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n", ret); break; } if (!val.intval || disable_usb_pd) break; /* * support up to PD 3.0 as a sink; if source is 2.0 * phy_msg_received() will handle the downgrade. */ pd->spec_rev = USBPD_REV_30; pd_reset_protocol(pd); if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { pd_phy_close(); pd->pd_phy_opened = false; } phy_params.data_role = pd->current_dr; phy_params.power_role = pd->current_pr; ret = pd_phy_open(&phy_params); if (ret) { WARN_ON_ONCE(1); usbpd_err(&pd->dev, "error opening PD PHY %d\n", ret); pd->current_state = PE_UNKNOWN; return; } pd->pd_phy_opened = true; } pd->current_voltage = pd->requested_voltage = 5000000; val.intval = pd->requested_voltage; /* set max range to 5V */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val); if (!pd->vbus_present) { pd->current_state = PE_SNK_DISCOVERY; /* max time for hard reset to turn vbus back on */ kick_sm(pd, SNK_HARD_RESET_VBUS_ON_TIME); break; } pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES; /* fall-through */ case PE_SNK_WAIT_FOR_CAPABILITIES: spin_lock_irqsave(&pd->rx_lock, flags); if (list_empty(&pd->rx_q)) kick_sm(pd, SINK_WAIT_CAP_TIME); spin_unlock_irqrestore(&pd->rx_lock, flags); break; case PE_SNK_EVALUATE_CAPABILITY: pd->pd_connected = true; /* we know peer is PD capable */ pd->hard_reset_count = 0; /* evaluate PDOs and select one */ ret = pd_eval_src_caps(pd); if (ret < 0) { usbpd_err(&pd->dev, "Invalid src_caps received. Skipping request\n"); break; } pd->current_state = PE_SNK_SELECT_CAPABILITY; /* fall-through */ case PE_SNK_SELECT_CAPABILITY: ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Request\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } /* wait for ACCEPT */ kick_sm(pd, SENDER_RESPONSE_TIME); break; case PE_SNK_TRANSITION_SINK: /* wait for PS_RDY */ kick_sm(pd, PS_TRANSITION_TIME); break; case PE_SNK_READY: pd->in_explicit_contract = true; if (pd->vdm_tx) kick_sm(pd, 0); else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE) usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_IDENTITY, SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); complete(&pd->is_ready); dual_role_instance_changed(pd->dual_role); break; case PE_SNK_TRANSITION_TO_DEFAULT: if (pd->current_dr != DR_UFP) { stop_usb_host(pd); start_usb_peripheral(pd); pd->current_dr = DR_UFP; pd_phy_update_roles(pd->current_dr, pd->current_pr); } if (pd->vconn_enabled) { regulator_disable(pd->vconn); pd->vconn_enabled = false; } /* max time for hard reset to turn vbus off */ kick_sm(pd, SNK_HARD_RESET_VBUS_OFF_TIME); break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: val.intval = pd->requested_current = 0; /* suspend charging */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); pd->in_explicit_contract = false; /* * need to update PR bit in message header so that * proper GoodCRC is sent when receiving next PS_RDY */ pd_phy_update_roles(pd->current_dr, PR_SRC); /* wait for PS_RDY */ kick_sm(pd, PS_SOURCE_OFF); break; default: usbpd_dbg(&pd->dev, "No action for state %s\n", usbpd_state_strings[pd->current_state]); break; } } int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) { if (find_svid_handler(pd, hdlr->svid)) { usbpd_err(&pd->dev, "SVID 0x%04x already registered\n", hdlr->svid); return -EINVAL; } /* require connect/disconnect callbacks be implemented */ if (!hdlr->connect || !hdlr->disconnect) { usbpd_err(&pd->dev, "SVID 0x%04x connect/disconnect must be non-NULL\n", hdlr->svid); return -EINVAL; } usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid); list_add_tail(&hdlr->entry, &pd->svid_handlers); /* already connected with this SVID discovered? */ if (pd->vdm_state >= DISCOVERED_SVIDS) { int i; for (i = 0; i < pd->num_svids; i++) { if (pd->discovered_svids[i] == hdlr->svid) { hdlr->connect(hdlr); hdlr->discovered = true; break; } } } return 0; } EXPORT_SYMBOL(usbpd_register_svid); void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) { list_del_init(&hdlr->entry); } EXPORT_SYMBOL(usbpd_unregister_svid); int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) { struct vdm_tx *vdm_tx; if (!pd->in_explicit_contract || pd->vdm_tx) return -EBUSY; vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL); if (!vdm_tx) return -ENOMEM; vdm_tx->data[0] = vdm_hdr; if (vdos && num_vdos) memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32)); vdm_tx->size = num_vdos + 1; /* include the header */ /* VDM will get sent in PE_SRC/SNK_READY state handling */ pd->vdm_tx = vdm_tx; /* slight delay before queuing to prioritize handling of incoming VDM */ kick_sm(pd, 2); return 0; } EXPORT_SYMBOL(usbpd_send_vdm); int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, int obj_pos, const u32 *vdos, int num_vdos) { u32 svdm_hdr = SVDM_HDR(svid, 0, obj_pos, cmd_type, cmd); usbpd_dbg(&pd->dev, "VDM tx: svid:%x cmd:%x cmd_type:%x svdm_hdr:%x\n", svid, cmd, cmd_type, svdm_hdr); return usbpd_send_vdm(pd, svdm_hdr, vdos, num_vdos); } EXPORT_SYMBOL(usbpd_send_svdm); static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg) { u32 vdm_hdr = rx_msg->data_len >= sizeof(u32) ? ((u32 *)rx_msg->payload)[0] : 0; u32 *vdos = (u32 *)&rx_msg->payload[sizeof(u32)]; u16 svid = VDM_HDR_SVID(vdm_hdr); u16 *psvid; u8 i, num_vdos = PD_MSG_HDR_COUNT(rx_msg->hdr) - 1; u8 cmd = SVDM_HDR_CMD(vdm_hdr); u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr); bool has_dp = false; struct usbpd_svid_handler *handler; usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n", svid, cmd, cmd_type, vdm_hdr); /* if it's a supported SVID, pass the message to the handler */ handler = find_svid_handler(pd, svid); /* Unstructured VDM */ if (!VDM_IS_SVDM(vdm_hdr)) { if (handler && handler->vdm_received) handler->vdm_received(handler, vdm_hdr, vdos, num_vdos); return; } /* if this interrupts a previous exchange, abort queued response */ if (cmd_type == SVDM_CMD_TYPE_INITIATOR && pd->vdm_tx) { usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n", VDM_HDR_SVID(pd->vdm_tx->data[0])); kfree(pd->vdm_tx); pd->vdm_tx = NULL; } if (handler && handler->svdm_received) { handler->svdm_received(handler, cmd, cmd_type, vdos, num_vdos); return; } /* Standard Discovery or unhandled messages go here */ switch (cmd_type) { case SVDM_CMD_TYPE_INITIATOR: if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) { u32 tx_vdos[3] = { ID_HDR_USB_HOST | ID_HDR_USB_DEVICE | ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID, 0x0, /* TBD: Cert Stat VDO */ (PROD_VDO_PID << 16), /* TBD: Get these from gadget */ }; usbpd_send_svdm(pd, USBPD_SID, cmd, SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3); } else if (cmd != USBPD_SVDM_ATTENTION) { usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK, SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0); } break; case SVDM_CMD_TYPE_RESP_ACK: if (svid != USBPD_SID) { usbpd_err(&pd->dev, "unhandled ACK for SVID:0x%x\n", svid); break; } switch (cmd) { case USBPD_SVDM_DISCOVER_IDENTITY: kfree(pd->vdm_tx_retry); pd->vdm_tx_retry = NULL; pd->vdm_state = DISCOVERED_ID; usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_SVIDS, SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); break; case USBPD_SVDM_DISCOVER_SVIDS: pd->vdm_state = DISCOVERED_SVIDS; kfree(pd->vdm_tx_retry); pd->vdm_tx_retry = NULL; if (!pd->discovered_svids) { pd->num_svids = 2 * num_vdos; pd->discovered_svids = kcalloc(pd->num_svids, sizeof(u16), GFP_KERNEL); if (!pd->discovered_svids) { usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); break; } psvid = pd->discovered_svids; } else { /* handle > 12 SVIDs */ void *ptr; size_t oldsize = pd->num_svids * sizeof(u16); size_t newsize = oldsize + (2 * num_vdos * sizeof(u16)); ptr = krealloc(pd->discovered_svids, newsize, GFP_KERNEL); if (!ptr) { usbpd_err(&pd->dev, "unable to realloc SVIDs\n"); break; } pd->discovered_svids = ptr; psvid = pd->discovered_svids + pd->num_svids; memset(psvid, 0, (2 * num_vdos)); pd->num_svids += 2 * num_vdos; } /* convert 32-bit VDOs to list of 16-bit SVIDs */ for (i = 0; i < num_vdos * 2; i++) { /* * Within each 32-bit VDO, * SVID[i]: upper 16-bits * SVID[i+1]: lower 16-bits * where i is even. */ if (!(i & 1)) svid = vdos[i >> 1] >> 16; else svid = vdos[i >> 1] & 0xFFFF; /* * There are some devices that incorrectly * swap the order of SVIDs within a VDO. So in * case of an odd-number of SVIDs it could end * up with SVID[i] as 0 while SVID[i+1] is * non-zero. Just skip over the zero ones. */ if (svid) { usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n", svid); *psvid++ = svid; } } /* if more than 12 SVIDs, resend the request */ if (num_vdos == 6 && vdos[5] != 0) { usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_SVIDS, SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); break; } /* now that all SVIDs are discovered, notify handlers */ for (i = 0; i < pd->num_svids; i++) { svid = pd->discovered_svids[i]; if (svid) { handler = find_svid_handler(pd, svid); if (handler) { handler->connect(handler); handler->discovered = true; } } if (svid == 0xFF01) has_dp = true; } /* * Finally start USB host now that we have determined * if DisplayPort mode is present or not and limit USB * to HS-only mode if so. */ start_usb_host(pd, !has_dp); break; default: usbpd_dbg(&pd->dev, "unhandled ACK for command:0x%x\n", cmd); break; } break; case SVDM_CMD_TYPE_RESP_NAK: usbpd_info(&pd->dev, "VDM NAK received for SVID:0x%04x command:0x%x\n", svid, cmd); switch (cmd) { case USBPD_SVDM_DISCOVER_IDENTITY: case USBPD_SVDM_DISCOVER_SVIDS: start_usb_host(pd, true); break; default: break; } break; case SVDM_CMD_TYPE_RESP_BUSY: switch (cmd) { case USBPD_SVDM_DISCOVER_IDENTITY: case USBPD_SVDM_DISCOVER_SVIDS: if (!pd->vdm_tx_retry) { usbpd_err(&pd->dev, "Discover command %d VDM was unexpectedly freed\n", cmd); break; } /* wait tVDMBusy, then retry */ pd->vdm_tx = pd->vdm_tx_retry; pd->vdm_tx_retry = NULL; kick_sm(pd, VDM_BUSY_TIME); break; default: break; } break; } } static void handle_vdm_tx(struct usbpd *pd) { int ret; unsigned long flags; /* only send one VDM at a time */ if (pd->vdm_tx) { u32 vdm_hdr = pd->vdm_tx->data[0]; /* bail out and try again later if a message just arrived */ spin_lock_irqsave(&pd->rx_lock, flags); if (!list_empty(&pd->rx_q)) { spin_unlock_irqrestore(&pd->rx_lock, flags); return; } spin_unlock_irqrestore(&pd->rx_lock, flags); ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data, pd->vdm_tx->size, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error (%d) sending VDM command %d\n", ret, SVDM_HDR_CMD(pd->vdm_tx->data[0])); /* retry when hitting PE_SRC/SNK_Ready again */ if (ret != -EBUSY) usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_SOFT_RESET : PE_SNK_SEND_SOFT_RESET); return; } /* * special case: keep initiated Discover ID/SVIDs * around in case we need to re-try when receiving BUSY */ if (VDM_IS_SVDM(vdm_hdr) && SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { if (pd->vdm_tx_retry) { usbpd_dbg(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n", SVDM_HDR_CMD( pd->vdm_tx_retry->data[0])); kfree(pd->vdm_tx_retry); } pd->vdm_tx_retry = pd->vdm_tx; } else { kfree(pd->vdm_tx); } pd->vdm_tx = NULL; } } static void reset_vdm_state(struct usbpd *pd) { struct usbpd_svid_handler *handler; list_for_each_entry(handler, &pd->svid_handlers, entry) { if (handler->discovered) { handler->disconnect(handler); handler->discovered = false; } } pd->vdm_state = VDM_NONE; kfree(pd->vdm_tx_retry); pd->vdm_tx_retry = NULL; kfree(pd->discovered_svids); pd->discovered_svids = NULL; pd->num_svids = 0; kfree(pd->vdm_tx); pd->vdm_tx = NULL; } static void dr_swap(struct usbpd *pd) { reset_vdm_state(pd); if (pd->current_dr == DR_DFP) { stop_usb_host(pd); start_usb_peripheral(pd); pd->current_dr = DR_UFP; } else if (pd->current_dr == DR_UFP) { stop_usb_peripheral(pd); pd->current_dr = DR_DFP; /* don't start USB host until after SVDM discovery */ usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_IDENTITY, SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); } pd_phy_update_roles(pd->current_dr, pd->current_pr); dual_role_instance_changed(pd->dual_role); } static void vconn_swap(struct usbpd *pd) { int ret; if (pd->vconn_enabled) { pd->current_state = PE_VCS_WAIT_FOR_VCONN; kick_sm(pd, VCONN_ON_TIME); } else { ret = regulator_enable(pd->vconn); if (ret) { usbpd_err(&pd->dev, "Unable to enable vconn\n"); return; } pd->vconn_enabled = true; /* * Small delay to ensure Vconn has ramped up. This is well * below tVCONNSourceOn (100ms) so we still send PS_RDY within * the allowed time. */ usleep_range(5000, 10000); ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_SOFT_RESET : PE_SNK_SEND_SOFT_RESET); return; } } } static int enable_vbus(struct usbpd *pd) { union power_supply_propval val = {0}; int count = 100; int ret; if (!check_vsafe0v) goto enable_reg; /* * Check to make sure there's no lingering charge on * VBUS before enabling it as a source. If so poll here * until it goes below VSafe0V (0.8V) before proceeding. */ while (count--) { ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); if (ret || val.intval <= 800000) break; usleep_range(20000, 30000); } if (count < 99) msleep(100); /* need to wait an additional tCCDebounce */ enable_reg: ret = regulator_enable(pd->vbus); if (ret) usbpd_err(&pd->dev, "Unable to enable vbus (%d)\n", ret); else pd->vbus_enabled = true; count = 10; /* * Check to make sure VBUS voltage reaches above Vsafe5Vmin (4.75v) * before proceeding. */ while (count--) { ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); if (ret || val.intval >= 4750000) /*vsafe5Vmin*/ break; usleep_range(10000, 12000); /* Delay between two reads */ } if (ret) msleep(100); /* Delay to wait for VBUS ramp up if read fails */ return ret; } static inline void rx_msg_cleanup(struct usbpd *pd) { struct rx_msg *msg, *tmp; unsigned long flags; spin_lock_irqsave(&pd->rx_lock, flags); list_for_each_entry_safe(msg, tmp, &pd->rx_q, entry) { list_del(&msg->entry); kfree(msg); } spin_unlock_irqrestore(&pd->rx_lock, flags); } /* For PD 3.0, check SinkTxOk before allowing initiating AMS */ static inline bool is_sink_tx_ok(struct usbpd *pd) { if (pd->spec_rev == USBPD_REV_30) return pd->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH; return true; } /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { struct usbpd *pd = container_of(w, struct usbpd, sm_work); union power_supply_propval val = {0}; int ret; struct rx_msg *rx_msg = NULL; unsigned long flags; usbpd_dbg(&pd->dev, "handle state %s\n", usbpd_state_strings[pd->current_state]); hrtimer_cancel(&pd->timer); pd->sm_queued = false; spin_lock_irqsave(&pd->rx_lock, flags); if (!list_empty(&pd->rx_q)) { rx_msg = list_first_entry(&pd->rx_q, struct rx_msg, entry); list_del(&rx_msg->entry); } spin_unlock_irqrestore(&pd->rx_lock, flags); /* Disconnect? */ if (pd->current_pr == PR_NONE) { if (pd->current_state == PE_UNKNOWN) goto sm_done; if (pd->vconn_enabled) { regulator_disable(pd->vconn); pd->vconn_enabled = false; } usbpd_info(&pd->dev, "USB Type-C disconnect\n"); if (pd->pd_phy_opened) { pd_phy_close(); pd->pd_phy_opened = false; } pd->in_pr_swap = false; pd->pd_connected = false; pd->in_explicit_contract = false; pd->hard_reset_recvd = false; pd->caps_count = 0; pd->hard_reset_count = 0; pd->requested_voltage = 0; pd->requested_current = 0; pd->selected_pdo = pd->requested_pdo = 0; memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); 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); if (pd->vbus_enabled) { regulator_disable(pd->vbus); pd->vbus_enabled = false; } if (pd->current_dr == DR_UFP) stop_usb_peripheral(pd); else if (pd->current_dr == DR_DFP) stop_usb_host(pd); pd->current_dr = DR_NONE; reset_vdm_state(pd); if (pd->current_state == PE_ERROR_RECOVERY) /* forced disconnect, wait before resetting to DRP */ usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC, (ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC); val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); /* set due to dual_role class "mode" change */ if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) val.intval = pd->forced_pr; else if (rev3_sink_only) val.intval = POWER_SUPPLY_TYPEC_PR_SINK; else /* Set CC back to DRP toggle */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); pd->forced_pr = POWER_SUPPLY_TYPEC_PR_NONE; pd->current_state = PE_UNKNOWN; kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); dual_role_instance_changed(pd->dual_role); goto sm_done; } /* Hard reset? */ if (pd->hard_reset_recvd) { pd->hard_reset_recvd = false; if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } pd->requested_voltage = 5000000; val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); pd->in_pr_swap = false; val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); pd->in_explicit_contract = false; pd->selected_pdo = pd->requested_pdo = 0; pd->rdo = 0; rx_msg_cleanup(pd); reset_vdm_state(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); if (pd->current_pr == PR_SINK) { usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); } else { s64 delta = ktime_ms_delta(ktime_get(), pd->hard_reset_recvd_time); pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; if (delta >= PS_HARD_RESET_TIME) kick_sm(pd, 0); else kick_sm(pd, PS_HARD_RESET_TIME - (int)delta); } goto sm_done; } /* Soft reset? */ if (IS_CTRL(rx_msg, MSG_SOFT_RESET)) { usbpd_dbg(&pd->dev, "Handle soft reset\n"); if (pd->current_pr == PR_SRC) pd->current_state = PE_SRC_SOFT_RESET; else if (pd->current_pr == PR_SINK) pd->current_state = PE_SNK_SOFT_RESET; } switch (pd->current_state) { case PE_UNKNOWN: val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); if (pd->current_pr == PR_SINK) { usbpd_set_state(pd, PE_SNK_STARTUP); } else if (pd->current_pr == PR_SRC) { if (!pd->vconn_enabled && pd->typec_mode == POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE) { ret = regulator_enable(pd->vconn); if (ret) usbpd_err(&pd->dev, "Unable to enable vconn\n"); else pd->vconn_enabled = true; } enable_vbus(pd); usbpd_set_state(pd, PE_SRC_STARTUP); } 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); if (ret) { pd->caps_count++; if (pd->caps_count == 10 && pd->current_dr == DR_DFP) { /* Likely not PD-capable, start host now */ start_usb_host(pd, true); } else if (pd->caps_count >= PD_CAPS_COUNT) { usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n"); usbpd_set_state(pd, PE_SRC_DISABLED); val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); break; } kick_sm(pd, SRC_CAP_TIME); break; } /* transmit was successful if GoodCRC was received */ pd->caps_count = 0; pd->hard_reset_count = 0; pd->pd_connected = true; /* we know peer is PD capable */ /* wait for REQUEST */ pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; kick_sm(pd, SENDER_RESPONSE_TIME); val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); break; case PE_SRC_SEND_CAPABILITIES_WAIT: if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = *(u32 *)rx_msg->payload; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (rx_msg) { usbpd_err(&pd->dev, "Unexpected message received\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } else { usbpd_set_state(pd, PE_SRC_HARD_RESET); } break; case PE_SRC_READY: if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) { pd->current_state = PE_SRC_SEND_CAPABILITIES; kick_sm(pd, 0); } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, pd->sink_caps, pd->num_sink_caps, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } } else if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = *(u32 *)rx_msg->payload; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SRC_HARD_RESET); break; } ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } dr_swap(pd); } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { /* lock in current mode */ set_power_role(pd, pd->current_pr); /* we'll happily accept Src->Sink requests anytime */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; kick_sm(pd, SRC_TRANSITION_TIME); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } vconn_swap(pd); } else if (IS_DATA(rx_msg, MSG_VDM)) { handle_vdm_rx(pd, rx_msg); } else if (rx_msg && pd->spec_rev == USBPD_REV_30) { /* unhandled messages */ ret = pd_send_msg(pd, MSG_NOT_SUPPORTED, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Not supported\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } break; } else if (pd->send_pr_swap) { pd->send_pr_swap = false; ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending PR Swap\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } pd->current_state = PE_PRS_SRC_SNK_SEND_SWAP; kick_sm(pd, SENDER_RESPONSE_TIME); } else if (pd->send_dr_swap) { pd->send_dr_swap = false; ret = pd_send_msg(pd, MSG_DR_SWAP, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending DR Swap\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } pd->current_state = PE_DRS_SEND_DR_SWAP; kick_sm(pd, SENDER_RESPONSE_TIME); } else { handle_vdm_tx(pd); } break; case PE_SRC_TRANSITION_TO_DEFAULT: if (pd->vconn_enabled) regulator_disable(pd->vconn); pd->vconn_enabled = false; if (pd->vbus_enabled) regulator_disable(pd->vbus); pd->vbus_enabled = false; if (pd->current_dr != DR_DFP) { extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0); pd->current_dr = DR_DFP; pd_phy_update_roles(pd->current_dr, pd->current_pr); } /* PE_UNKNOWN will turn on VBUS and go back to PE_SRC_STARTUP */ pd->current_state = PE_UNKNOWN; kick_sm(pd, SRC_RECOVER_TIME); break; case PE_SRC_HARD_RESET: val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd_send_hard_reset(pd); pd->in_explicit_contract = false; pd->rdo = 0; rx_msg_cleanup(pd); reset_vdm_state(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; kick_sm(pd, PS_HARD_RESET_TIME); break; case PE_SNK_STARTUP: usbpd_set_state(pd, PE_SNK_STARTUP); break; case PE_SNK_DISCOVERY: if (!rx_msg) { if (pd->vbus_present) usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); /* * Handle disconnection in the middle of PR_Swap. * Since in psy_changed() if pd->in_pr_swap is true * we ignore the typec_mode==NONE change since that is * expected to happen. However if the cable really did * get disconnected we need to check for it here after * waiting for VBUS presence times out. */ if (!pd->typec_mode) { pd->current_pr = PR_NONE; kick_sm(pd, 0); } break; } /* else fall-through */ case PE_SNK_WAIT_FOR_CAPABILITIES: pd->in_pr_swap = false; val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); /* save the PDOs so userspace can further evaluate */ memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); memcpy(&pd->received_pdos, rx_msg->payload, min_t(size_t, rx_msg->data_len, sizeof(pd->received_pdos))); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); } else { usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n"); val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); pd_phy_close(); pd->pd_phy_opened = false; } break; case PE_SNK_SELECT_CAPABILITY: if (IS_CTRL(rx_msg, MSG_ACCEPT)) { u32 pdo = pd->received_pdos[pd->requested_pdo - 1]; bool same_pps = (pd->selected_pdo == pd->requested_pdo) && (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_AUGMENTED); usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); /* prepare for voltage increase/decrease */ val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? POWER_SUPPLY_PROP_PD_VOLTAGE_MAX : POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); /* * if changing voltages (not within the same PPS PDO), * we must lower input current to pSnkStdby (2.5W). * Calculate it and set PD_CURRENT_MAX accordingly. */ if (!same_pps && pd->requested_voltage != pd->current_voltage) { int mv = max(pd->requested_voltage, pd->current_voltage) / 1000; val.intval = (2500000 / mv) * 1000; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } else { /* decreasing current? */ ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); if (!ret && pd->requested_current < val.intval) { val.intval = pd->requested_current * 1000; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } } pd->selected_pdo = pd->requested_pdo; } else if (IS_CTRL(rx_msg, MSG_REJECT) || IS_CTRL(rx_msg, MSG_WAIT)) { if (pd->in_explicit_contract) usbpd_set_state(pd, PE_SNK_READY); else usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); } else if (rx_msg) { usbpd_err(&pd->dev, "Invalid response to sink request\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } else { /* timed out; go to hard reset */ usbpd_set_state(pd, PE_SNK_HARD_RESET); } break; case PE_SNK_TRANSITION_SINK: if (IS_CTRL(rx_msg, MSG_PS_RDY)) { val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? POWER_SUPPLY_PROP_PD_VOLTAGE_MIN : POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val); pd->current_voltage = pd->requested_voltage; /* resume charging */ val.intval = pd->requested_current * 1000; /* mA->uA */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); usbpd_set_state(pd, PE_SNK_READY); } else { /* timed out; go to hard reset */ usbpd_set_state(pd, PE_SNK_HARD_RESET); } break; case PE_SNK_READY: if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { /* save the PDOs so userspace can further evaluate */ memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); memcpy(&pd->received_pdos, rx_msg->payload, min_t(size_t, rx_msg->data_len, sizeof(pd->received_pdos))); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, pd->sink_caps, pd->num_sink_caps, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } } else if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP) && pd->spec_rev == USBPD_REV_20) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending SRC CAPs\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SNK_HARD_RESET); break; } ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } dr_swap(pd); } else if (IS_CTRL(rx_msg, MSG_PR_SWAP) && pd->spec_rev == USBPD_REV_20) { /* lock in current mode */ set_power_role(pd, pd->current_pr); /* TODO: should we Reject in certain circumstances? */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } pd->in_pr_swap = true; val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP) && pd->spec_rev == USBPD_REV_20) { /* * if VCONN is connected to VBUS, make sure we are * not in high voltage contract, otherwise reject. */ if (!pd->vconn_is_external && (pd->requested_voltage > 5000000)) { ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Reject\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } break; } ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } vconn_swap(pd); } else if (IS_DATA(rx_msg, MSG_VDM)) { handle_vdm_rx(pd, rx_msg); } else if (pd->send_get_src_cap_ext && is_sink_tx_ok(pd)) { pd->send_get_src_cap_ext = false; ret = pd_send_msg(pd, MSG_GET_SOURCE_CAP_EXTENDED, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending get_src_cap_ext\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } kick_sm(pd, SENDER_RESPONSE_TIME); } else if (rx_msg && IS_EXT(rx_msg, MSG_SOURCE_CAPABILITIES_EXTENDED)) { if (rx_msg->data_len != PD_SRC_CAP_EXT_DB_LEN) { usbpd_err(&pd->dev, "Invalid src cap ext db\n"); break; } memcpy(&pd->src_cap_ext_db, rx_msg->payload, sizeof(pd->src_cap_ext_db)); complete(&pd->is_ready); } else if (pd->send_get_pps_status && is_sink_tx_ok(pd)) { pd->send_get_pps_status = false; ret = pd_send_msg(pd, MSG_GET_PPS_STATUS, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending get_pps_status\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } kick_sm(pd, SENDER_RESPONSE_TIME); } else if (rx_msg && IS_EXT(rx_msg, MSG_PPS_STATUS)) { if (rx_msg->data_len != sizeof(pd->pps_status_db)) { usbpd_err(&pd->dev, "Invalid pps status db\n"); break; } memcpy(&pd->pps_status_db, rx_msg->payload, sizeof(pd->pps_status_db)); complete(&pd->is_ready); } else if (IS_DATA(rx_msg, MSG_ALERT)) { if (rx_msg->data_len != sizeof(pd->received_ado)) { usbpd_err(&pd->dev, "Invalid ado\n"); break; } memcpy(&pd->received_ado, rx_msg->payload, sizeof(pd->received_ado)); ret = pd_send_msg(pd, MSG_GET_STATUS, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending get_status\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } kick_sm(pd, SENDER_RESPONSE_TIME); } else if (rx_msg && IS_EXT(rx_msg, MSG_STATUS)) { if (rx_msg->data_len != PD_STATUS_DB_LEN) { usbpd_err(&pd->dev, "Invalid status db\n"); break; } memcpy(&pd->status_db, rx_msg->payload, sizeof(pd->status_db)); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (pd->send_get_battery_cap && is_sink_tx_ok(pd)) { pd->send_get_battery_cap = false; ret = pd_send_ext_msg(pd, MSG_GET_BATTERY_CAP, &pd->get_battery_cap_db, 1, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending get_battery_cap\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } kick_sm(pd, SENDER_RESPONSE_TIME); } else if (rx_msg && IS_EXT(rx_msg, MSG_BATTERY_CAPABILITIES)) { if (rx_msg->data_len != PD_BATTERY_CAP_DB_LEN) { usbpd_err(&pd->dev, "Invalid battery cap db\n"); break; } memcpy(&pd->battery_cap_db, rx_msg->payload, sizeof(pd->battery_cap_db)); complete(&pd->is_ready); } else if (pd->send_get_battery_status && is_sink_tx_ok(pd)) { pd->send_get_battery_status = false; ret = pd_send_ext_msg(pd, MSG_GET_BATTERY_STATUS, &pd->get_battery_status_db, 1, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending get_battery_status\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } kick_sm(pd, SENDER_RESPONSE_TIME); } else if (rx_msg && IS_EXT(rx_msg, MSG_BATTERY_STATUS)) { if (rx_msg->data_len != sizeof(pd->battery_sts_dobj)) { usbpd_err(&pd->dev, "Invalid bat sts dobj\n"); break; } memcpy(&pd->battery_sts_dobj, rx_msg->payload, sizeof(pd->battery_sts_dobj)); complete(&pd->is_ready); } else if (rx_msg && pd->spec_rev == USBPD_REV_30) { /* unhandled messages */ ret = pd_send_msg(pd, MSG_NOT_SUPPORTED, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Not supported\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } break; } else if (pd->send_request) { pd->send_request = false; usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY); } else if (pd->send_pr_swap && is_sink_tx_ok(pd)) { pd->send_pr_swap = false; ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending PR Swap\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } pd->current_state = PE_PRS_SNK_SRC_SEND_SWAP; kick_sm(pd, SENDER_RESPONSE_TIME); } else if (pd->send_dr_swap && is_sink_tx_ok(pd)) { pd->send_dr_swap = false; ret = pd_send_msg(pd, MSG_DR_SWAP, NULL, 0, SOP_MSG); if (ret) { dev_err(&pd->dev, "Error sending DR Swap\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } pd->current_state = PE_DRS_SEND_DR_SWAP; kick_sm(pd, SENDER_RESPONSE_TIME); } else if (is_sink_tx_ok(pd)) { handle_vdm_tx(pd); } break; case PE_SNK_TRANSITION_TO_DEFAULT: usbpd_set_state(pd, PE_SNK_STARTUP); break; case PE_SRC_SOFT_RESET: case PE_SNK_SOFT_RESET: pd_reset_protocol(pd); ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "%s: Error sending Accept, do Hard Reset\n", usbpd_state_strings[pd->current_state]); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); break; } usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_CAPABILITIES : PE_SNK_WAIT_FOR_CAPABILITIES); break; case PE_SRC_SEND_SOFT_RESET: case PE_SNK_SEND_SOFT_RESET: if (IS_CTRL(rx_msg, MSG_ACCEPT)) { usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_CAPABILITIES : PE_SNK_WAIT_FOR_CAPABILITIES); } else { usbpd_err(&pd->dev, "%s: Did not see Accept, do Hard Reset\n", usbpd_state_strings[pd->current_state]); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); } break; case PE_SNK_HARD_RESET: /* prepare charger for VBUS change */ val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd->requested_voltage = 5000000; if (pd->requested_current) { val.intval = pd->requested_current = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); } val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, &val); pd_send_hard_reset(pd); pd->in_explicit_contract = false; pd->selected_pdo = pd->requested_pdo = 0; pd->rdo = 0; reset_vdm_state(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); break; case PE_DRS_SEND_DR_SWAP: if (IS_CTRL(rx_msg, MSG_ACCEPT)) dr_swap(pd); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_READY : PE_SNK_READY); break; case PE_PRS_SRC_SNK_SEND_SWAP: if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SRC_READY; break; } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; kick_sm(pd, SRC_TRANSITION_TIME); break; case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: pd->in_pr_swap = true; val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); pd->in_explicit_contract = false; if (pd->vbus_enabled) { regulator_disable(pd->vbus); pd->vbus_enabled = false; } /* PE_PRS_SRC_SNK_Assert_Rd */ pd->current_pr = PR_SINK; set_power_role(pd, pd->current_pr); pd_phy_update_roles(pd->current_dr, pd->current_pr); /* allow time for Vbus discharge, must be < tSrcSwapStdby */ msleep(500); ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); usbpd_set_state(pd, PE_ERROR_RECOVERY); break; } pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON; kick_sm(pd, PS_SOURCE_ON); break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: if (IS_CTRL(rx_msg, MSG_PS_RDY)) usbpd_set_state(pd, PE_SNK_STARTUP); else usbpd_set_state(pd, PE_ERROR_RECOVERY); break; case PE_PRS_SNK_SRC_SEND_SWAP: if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SNK_READY; break; } pd->in_pr_swap = true; val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: if (!IS_CTRL(rx_msg, MSG_PS_RDY)) { usbpd_set_state(pd, PE_ERROR_RECOVERY); break; } /* PE_PRS_SNK_SRC_Assert_Rp */ pd->current_pr = PR_SRC; set_power_role(pd, pd->current_pr); pd->current_state = PE_PRS_SNK_SRC_SOURCE_ON; /* fall-through */ case PE_PRS_SNK_SRC_SOURCE_ON: enable_vbus(pd); ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending PS_RDY\n"); usbpd_set_state(pd, PE_ERROR_RECOVERY); break; } usbpd_set_state(pd, PE_SRC_STARTUP); break; case PE_VCS_WAIT_FOR_VCONN: if (IS_CTRL(rx_msg, MSG_PS_RDY)) { /* * hopefully redundant check but in case not enabled * avoids unbalanced regulator disable count */ if (pd->vconn_enabled) regulator_disable(pd->vconn); pd->vconn_enabled = false; pd->current_state = pd->current_pr == PR_SRC ? PE_SRC_READY : PE_SNK_READY; } else { /* timed out; go to hard reset */ usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); } break; default: usbpd_err(&pd->dev, "Unhandled state %s\n", usbpd_state_strings[pd->current_state]); break; } sm_done: kfree(rx_msg); spin_lock_irqsave(&pd->rx_lock, flags); ret = list_empty(&pd->rx_q); spin_unlock_irqrestore(&pd->rx_lock, flags); /* requeue if there are any new/pending RX messages */ if (!ret) kick_sm(pd, 0); if (!pd->sm_queued) pm_relax(&pd->dev); } static inline const char *src_current(enum power_supply_typec_mode typec_mode) { switch (typec_mode) { case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: return "default"; case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: return "medium - 1.5A"; case POWER_SUPPLY_TYPEC_SOURCE_HIGH: return "high - 3.0A"; default: return ""; } } 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; enum power_supply_typec_mode typec_mode; int ret; if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED) return 0; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_MODE, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB TYPEC_MODE: %d\n", ret); return ret; } typec_mode = val.intval; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PE_START, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB PROP_PE_START: %d\n", ret); return ret; } /* Don't proceed if PE_START=0 as other props may still change */ if (!val.intval && !pd->pd_connected && typec_mode != POWER_SUPPLY_TYPEC_NONE) return 0; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PRESENT, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret); return ret; } pd->vbus_present = val.intval; ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_REAL_TYPE, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret); return ret; } pd->psy_type = val.intval; /* * For sink hard reset, state machine needs to know when VBUS changes * - when in PE_SNK_TRANSITION_TO_DEFAULT, notify when VBUS falls * - when in PE_SNK_DISCOVERY, notify when VBUS rises */ if (typec_mode && ((!pd->vbus_present && pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) || (pd->vbus_present && pd->current_state == PE_SNK_DISCOVERY))) { usbpd_dbg(&pd->dev, "hard reset: typec mode:%d present:%d\n", typec_mode, pd->vbus_present); pd->typec_mode = typec_mode; kick_sm(pd, 0); return 0; } if (pd->typec_mode == typec_mode) return 0; pd->typec_mode = typec_mode; usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", typec_mode, pd->vbus_present, pd->psy_type, usbpd_get_plug_orientation(pd)); switch (typec_mode) { /* Disconnect */ case POWER_SUPPLY_TYPEC_NONE: if (pd->in_pr_swap) { usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n"); return 0; } pd->current_pr = PR_NONE; break; /* Sink states */ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: case POWER_SUPPLY_TYPEC_SOURCE_HIGH: usbpd_info(&pd->dev, "Type-C Source (%s) connected\n", src_current(typec_mode)); /* if waiting for SinkTxOk to start an AMS */ if (pd->spec_rev == USBPD_REV_30 && typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH && (pd->send_pr_swap || pd->send_dr_swap || pd->vdm_tx)) break; if (pd->current_pr == PR_SINK) return 0; /* * Unexpected if not in PR swap; need to force disconnect from * source so we can turn off VBUS, Vconn, PD PHY etc. */ if (pd->current_pr == PR_SRC) { usbpd_info(&pd->dev, "Forcing disconnect from source mode\n"); pd->current_pr = PR_NONE; break; } pd->current_pr = PR_SINK; break; /* Source states */ case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE: case POWER_SUPPLY_TYPEC_SINK: usbpd_info(&pd->dev, "Type-C Sink%s connected\n", typec_mode == POWER_SUPPLY_TYPEC_SINK ? "" : " (powered)"); if (pd->current_pr == PR_SRC) return 0; pd->current_pr = PR_SRC; break; case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n"); break; case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n"); break; default: usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode); break; } /* queue state machine due to CC state change */ kick_sm(pd, 0); return 0; } static enum dual_role_property usbpd_dr_properties[] = { DUAL_ROLE_PROP_SUPPORTED_MODES, DUAL_ROLE_PROP_MODE, DUAL_ROLE_PROP_PR, DUAL_ROLE_PROP_DR, }; static int usbpd_dr_get_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, unsigned int *val) { struct usbpd *pd = dual_role_get_drvdata(dual_role); if (!pd) return -ENODEV; switch (prop) { case DUAL_ROLE_PROP_MODE: /* For now associate UFP/DFP with data role only */ if (pd->current_dr == DR_UFP) *val = DUAL_ROLE_PROP_MODE_UFP; else if (pd->current_dr == DR_DFP) *val = DUAL_ROLE_PROP_MODE_DFP; else *val = DUAL_ROLE_PROP_MODE_NONE; break; case DUAL_ROLE_PROP_PR: if (pd->current_pr == PR_SRC) *val = DUAL_ROLE_PROP_PR_SRC; else if (pd->current_pr == PR_SINK) *val = DUAL_ROLE_PROP_PR_SNK; else *val = DUAL_ROLE_PROP_PR_NONE; break; case DUAL_ROLE_PROP_DR: if (pd->current_dr == DR_UFP) *val = DUAL_ROLE_PROP_DR_DEVICE; else if (pd->current_dr == DR_DFP) *val = DUAL_ROLE_PROP_DR_HOST; else *val = DUAL_ROLE_PROP_DR_NONE; break; default: usbpd_warn(&pd->dev, "unsupported property %d\n", prop); return -ENODATA; } return 0; } static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, const unsigned int *val) { struct usbpd *pd = dual_role_get_drvdata(dual_role); bool do_swap = false; int wait_count = 5; if (!pd) return -ENODEV; switch (prop) { case DUAL_ROLE_PROP_MODE: usbpd_dbg(&pd->dev, "Setting mode to %d\n", *val); if (pd->current_state == PE_UNKNOWN) { usbpd_warn(&pd->dev, "No active connection. Don't allow MODE change\n"); return -EAGAIN; } /* * Forces disconnect on CC and re-establishes connection. * This does not use PD-based PR/DR swap */ if (*val == DUAL_ROLE_PROP_MODE_UFP) pd->forced_pr = POWER_SUPPLY_TYPEC_PR_SINK; else if (*val == DUAL_ROLE_PROP_MODE_DFP) pd->forced_pr = POWER_SUPPLY_TYPEC_PR_SOURCE; /* new mode will be applied in disconnect handler */ set_power_role(pd, PR_NONE); /* wait until it takes effect */ while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE && --wait_count) msleep(20); if (!wait_count) { usbpd_err(&pd->dev, "setting mode timed out\n"); return -ETIMEDOUT; } break; case DUAL_ROLE_PROP_DR: usbpd_dbg(&pd->dev, "Setting data_role to %d\n", *val); if (*val == DUAL_ROLE_PROP_DR_HOST) { if (pd->current_dr == DR_UFP) do_swap = true; } else if (*val == DUAL_ROLE_PROP_DR_DEVICE) { if (pd->current_dr == DR_DFP) do_swap = true; } else { usbpd_warn(&pd->dev, "setting data_role to 'none' unsupported\n"); return -ENOTSUPP; } if (do_swap) { if (pd->current_state != PE_SRC_READY && pd->current_state != PE_SNK_READY) { usbpd_err(&pd->dev, "data_role swap not allowed: PD not in Ready state\n"); return -EAGAIN; } if (pd->current_state == PE_SNK_READY && !is_sink_tx_ok(pd)) { usbpd_err(&pd->dev, "Rp indicates SinkTxNG\n"); return -EAGAIN; } mutex_lock(&pd->swap_lock); reinit_completion(&pd->is_ready); pd->send_dr_swap = true; kick_sm(pd, 0); /* wait for operation to complete */ if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(100))) { usbpd_err(&pd->dev, "data_role swap timed out\n"); mutex_unlock(&pd->swap_lock); return -ETIMEDOUT; } mutex_unlock(&pd->swap_lock); if ((*val == DUAL_ROLE_PROP_DR_HOST && pd->current_dr != DR_DFP) || (*val == DUAL_ROLE_PROP_DR_DEVICE && pd->current_dr != DR_UFP)) { usbpd_err(&pd->dev, "incorrect state (%s) after data_role swap\n", pd->current_dr == DR_DFP ? "dfp" : "ufp"); return -EPROTO; } } break; case DUAL_ROLE_PROP_PR: usbpd_dbg(&pd->dev, "Setting power_role to %d\n", *val); if (*val == DUAL_ROLE_PROP_PR_SRC) { if (pd->current_pr == PR_SINK) do_swap = true; } else if (*val == DUAL_ROLE_PROP_PR_SNK) { if (pd->current_pr == PR_SRC) do_swap = true; } else { usbpd_warn(&pd->dev, "setting power_role to 'none' unsupported\n"); return -ENOTSUPP; } if (do_swap) { if (pd->current_state != PE_SRC_READY && pd->current_state != PE_SNK_READY) { usbpd_err(&pd->dev, "power_role swap not allowed: PD not in Ready state\n"); return -EAGAIN; } if (pd->current_state == PE_SNK_READY && !is_sink_tx_ok(pd)) { usbpd_err(&pd->dev, "Rp indicates SinkTxNG\n"); return -EAGAIN; } mutex_lock(&pd->swap_lock); reinit_completion(&pd->is_ready); pd->send_pr_swap = true; kick_sm(pd, 0); /* wait for operation to complete */ if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(2000))) { usbpd_err(&pd->dev, "power_role swap timed out\n"); mutex_unlock(&pd->swap_lock); return -ETIMEDOUT; } mutex_unlock(&pd->swap_lock); if ((*val == DUAL_ROLE_PROP_PR_SRC && pd->current_pr != PR_SRC) || (*val == DUAL_ROLE_PROP_PR_SNK && pd->current_pr != PR_SINK)) { usbpd_err(&pd->dev, "incorrect state (%s) after power_role swap\n", pd->current_pr == PR_SRC ? "source" : "sink"); return -EPROTO; } } break; default: usbpd_warn(&pd->dev, "unsupported property %d\n", prop); return -ENOTSUPP; } return 0; } static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role, enum dual_role_property prop) { struct usbpd *pd = dual_role_get_drvdata(dual_role); switch (prop) { case DUAL_ROLE_PROP_MODE: return 1; case DUAL_ROLE_PROP_DR: case DUAL_ROLE_PROP_PR: if (pd) return pd->current_state == PE_SNK_READY || pd->current_state == PE_SRC_READY; break; default: break; } return 0; } static int usbpd_uevent(struct device *dev, struct kobj_uevent_env *env) { struct usbpd *pd = dev_get_drvdata(dev); int i; add_uevent_var(env, "DATA_ROLE=%s", pd->current_dr == DR_DFP ? "dfp" : "ufp"); if (pd->current_pr == PR_SINK) { add_uevent_var(env, "POWER_ROLE=sink"); add_uevent_var(env, "SRC_CAP_ID=%d", pd->src_cap_id); for (i = 0; i < ARRAY_SIZE(pd->received_pdos); i++) add_uevent_var(env, "PDO%d=%08x", i, pd->received_pdos[i]); add_uevent_var(env, "REQUESTED_PDO=%d", pd->requested_pdo); add_uevent_var(env, "SELECTED_PDO=%d", pd->selected_pdo); } else { add_uevent_var(env, "POWER_ROLE=source"); for (i = 0; i < ARRAY_SIZE(default_src_caps); i++) add_uevent_var(env, "PDO%d=%08x", i, default_src_caps[i]); } add_uevent_var(env, "RDO=%08x", pd->rdo); add_uevent_var(env, "CONTRACT=%s", pd->in_explicit_contract ? "explicit" : "implicit"); add_uevent_var(env, "ALT_MODE=%d", pd->vdm_state == MODE_ENTERED); add_uevent_var(env, "ADO=%08x", pd->received_ado); for (i = 0; i < PD_STATUS_DB_LEN; i++) add_uevent_var(env, "SDB%d=%08x", i, pd->status_db[i]); return 0; } static ssize_t contract_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", pd->in_explicit_contract ? "explicit" : "implicit"); } static DEVICE_ATTR_RO(contract); static ssize_t current_pr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); const char *pr = "none"; if (pd->current_pr == PR_SINK) pr = "sink"; else if (pd->current_pr == PR_SRC) pr = "source"; return snprintf(buf, PAGE_SIZE, "%s\n", pr); } static DEVICE_ATTR_RO(current_pr); static ssize_t initial_pr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); const char *pr = "none"; if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) pr = "sink"; else if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SINK) pr = "source"; return snprintf(buf, PAGE_SIZE, "%s\n", pr); } static DEVICE_ATTR_RO(initial_pr); static ssize_t current_dr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); const char *dr = "none"; if (pd->current_dr == DR_UFP) dr = "ufp"; else if (pd->current_dr == DR_DFP) dr = "dfp"; return snprintf(buf, PAGE_SIZE, "%s\n", dr); } static DEVICE_ATTR_RO(current_dr); static ssize_t initial_dr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); const char *dr = "none"; if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) dr = "ufp"; else if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SINK) dr = "dfp"; return snprintf(buf, PAGE_SIZE, "%s\n", dr); } static DEVICE_ATTR_RO(initial_dr); static ssize_t src_cap_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", pd->src_cap_id); } static DEVICE_ATTR_RO(src_cap_id); /* Dump received source PDOs in human-readable format */ static ssize_t pdo_h_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); int i; ssize_t cnt = 0; for (i = 0; i < ARRAY_SIZE(pd->received_pdos); i++) { u32 pdo = pd->received_pdos[i]; if (pdo == 0) break; cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "PDO %d\n", i + 1); if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_FIXED) { cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "\tFixed supply\n" "\tDual-Role Power:%d\n" "\tUSB Suspend Supported:%d\n" "\tExternally Powered:%d\n" "\tUSB Communications Capable:%d\n" "\tData Role Swap:%d\n" "\tPeak Current:%d\n" "\tVoltage:%d (mV)\n" "\tMax Current:%d (mA)\n", PD_SRC_PDO_FIXED_PR_SWAP(pdo), PD_SRC_PDO_FIXED_USB_SUSP(pdo), PD_SRC_PDO_FIXED_EXT_POWERED(pdo), PD_SRC_PDO_FIXED_USB_COMM(pdo), PD_SRC_PDO_FIXED_DR_SWAP(pdo), PD_SRC_PDO_FIXED_PEAK_CURR(pdo), PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50, PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10); } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_BATTERY) { cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "\tBattery supply\n" "\tMax Voltage:%d (mV)\n" "\tMin Voltage:%d (mV)\n" "\tMax Power:%d (mW)\n", PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50, PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50, PD_SRC_PDO_VAR_BATT_MAX(pdo) * 250); } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_VARIABLE) { cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "\tVariable supply\n" "\tMax Voltage:%d (mV)\n" "\tMin Voltage:%d (mV)\n" "\tMax Current:%d (mA)\n", PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50, PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50, PD_SRC_PDO_VAR_BATT_MAX(pdo) * 10); } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_AUGMENTED) { cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "\tProgrammable Power supply\n" "\tMax Voltage:%d (mV)\n" "\tMin Voltage:%d (mV)\n" "\tMax Current:%d (mA)\n", PD_APDO_MAX_VOLT(pdo) * 100, PD_APDO_MIN_VOLT(pdo) * 100, PD_APDO_MAX_CURR(pdo) * 50); } else { cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "Invalid PDO\n"); } buf[cnt++] = '\n'; } return cnt; } static DEVICE_ATTR_RO(pdo_h); static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr, char *buf); #define PDO_ATTR(n) { \ .attr = { .name = __stringify(pdo##n), .mode = S_IRUGO }, \ .show = pdo_n_show, \ } static struct device_attribute dev_attr_pdos[] = { PDO_ATTR(1), PDO_ATTR(2), PDO_ATTR(3), PDO_ATTR(4), PDO_ATTR(5), PDO_ATTR(6), PDO_ATTR(7), }; static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); int i; for (i = 0; i < ARRAY_SIZE(dev_attr_pdos); i++) if (attr == &dev_attr_pdos[i]) /* dump the PDO as a hex string */ return snprintf(buf, PAGE_SIZE, "%08x\n", pd->received_pdos[i]); usbpd_err(&pd->dev, "Invalid PDO index\n"); return -EINVAL; } static ssize_t select_pdo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct usbpd *pd = dev_get_drvdata(dev); int src_cap_id; int pdo, uv = 0, ua = 0; int ret; mutex_lock(&pd->swap_lock); /* Only allowed if we are already in explicit sink contract */ if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); ret = -EBUSY; goto out; } ret = sscanf(buf, "%d %d %d %d", &src_cap_id, &pdo, &uv, &ua); if (ret != 2 && ret != 4) { usbpd_err(&pd->dev, "select_pdo: Must specify [ ]\n"); ret = -EINVAL; goto out; } if (src_cap_id != pd->src_cap_id) { usbpd_err(&pd->dev, "select_pdo: src_cap_id mismatch. Requested:%d, current:%d\n", src_cap_id, pd->src_cap_id); ret = -EINVAL; goto out; } if (pdo < 1 || pdo > 7) { usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo); ret = -EINVAL; goto out; } ret = pd_select_pdo(pd, pdo, uv, ua); if (ret) goto out; reinit_completion(&pd->is_ready); pd->send_request = true; kick_sm(pd, 0); /* wait for operation to complete */ if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(1000))) { usbpd_err(&pd->dev, "select_pdo: request timed out\n"); ret = -ETIMEDOUT; goto out; } /* determine if request was accepted/rejected */ if (pd->selected_pdo != pd->requested_pdo || pd->current_voltage != pd->requested_voltage) { usbpd_err(&pd->dev, "select_pdo: request rejected\n"); ret = -EINVAL; } out: pd->send_request = false; mutex_unlock(&pd->swap_lock); return ret ? ret : size; } static ssize_t select_pdo_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", pd->selected_pdo); } static DEVICE_ATTR_RW(select_pdo); static ssize_t rdo_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); /* dump the RDO as a hex string */ return snprintf(buf, PAGE_SIZE, "%08x\n", pd->rdo); } static DEVICE_ATTR_RO(rdo); static ssize_t rdo_h_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); int pos = PD_RDO_OBJ_POS(pd->rdo); int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos]); int len; len = scnprintf(buf, PAGE_SIZE, "Request Data Object\n" "\tObj Pos:%d\n" "\tGiveback:%d\n" "\tCapability Mismatch:%d\n" "\tUSB Communications Capable:%d\n" "\tNo USB Suspend:%d\n", PD_RDO_OBJ_POS(pd->rdo), PD_RDO_GIVEBACK(pd->rdo), PD_RDO_MISMATCH(pd->rdo), PD_RDO_USB_COMM(pd->rdo), PD_RDO_NO_USB_SUSP(pd->rdo)); switch (type) { case PD_SRC_PDO_TYPE_FIXED: case PD_SRC_PDO_TYPE_VARIABLE: len += scnprintf(buf + len, PAGE_SIZE - len, "(Fixed/Variable)\n" "\tOperating Current:%d (mA)\n" "\t%s Current:%d (mA)\n", PD_RDO_FIXED_CURR(pd->rdo) * 10, PD_RDO_GIVEBACK(pd->rdo) ? "Min" : "Max", PD_RDO_FIXED_CURR_MINMAX(pd->rdo) * 10); break; case PD_SRC_PDO_TYPE_BATTERY: len += scnprintf(buf + len, PAGE_SIZE - len, "(Battery)\n" "\tOperating Power:%d (mW)\n" "\t%s Power:%d (mW)\n", PD_RDO_FIXED_CURR(pd->rdo) * 250, PD_RDO_GIVEBACK(pd->rdo) ? "Min" : "Max", PD_RDO_FIXED_CURR_MINMAX(pd->rdo) * 250); break; case PD_SRC_PDO_TYPE_AUGMENTED: len += scnprintf(buf + len, PAGE_SIZE - len, "(Programmable)\n" "\tOutput Voltage:%d (mV)\n" "\tOperating Current:%d (mA)\n", PD_RDO_PROG_VOLTAGE(pd->rdo) * 20, PD_RDO_PROG_CURR(pd->rdo) * 50); break; } return len; } static DEVICE_ATTR_RO(rdo_h); static ssize_t hard_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct usbpd *pd = dev_get_drvdata(dev); int val = 0; if (sscanf(buf, "%d\n", &val) != 1) return -EINVAL; if (val) usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); return size; } static DEVICE_ATTR_WO(hard_reset); static int trigger_tx_msg(struct usbpd *pd, bool *msg_tx_flag) { int ret = 0; /* Only allowed if we are already in explicit sink contract */ if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { usbpd_err(&pd->dev, "%s: Cannot send msg\n", __func__); ret = -EBUSY; goto out; } reinit_completion(&pd->is_ready); *msg_tx_flag = true; kick_sm(pd, 0); /* wait for operation to complete */ if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(1000))) { usbpd_err(&pd->dev, "%s: request timed out\n", __func__); ret = -ETIMEDOUT; } out: *msg_tx_flag = false; return ret; } static ssize_t get_src_cap_ext_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, ret, len = 0; struct usbpd *pd = dev_get_drvdata(dev); if (pd->spec_rev == USBPD_REV_20) return -EINVAL; ret = trigger_tx_msg(pd, &pd->send_get_src_cap_ext); if (ret) return ret; for (i = 0; i < PD_SRC_CAP_EXT_DB_LEN; i++) len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", pd->src_cap_ext_db[i]); return len; } static DEVICE_ATTR_RO(get_src_cap_ext); static ssize_t get_pps_status_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; struct usbpd *pd = dev_get_drvdata(dev); if (pd->spec_rev == USBPD_REV_20) return -EINVAL; ret = trigger_tx_msg(pd, &pd->send_get_pps_status); if (ret) return ret; return snprintf(buf, PAGE_SIZE, "%d\n", pd->pps_status_db); } static DEVICE_ATTR_RO(get_pps_status); static ssize_t rx_ado_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); /* dump the ADO as a hex string */ return snprintf(buf, PAGE_SIZE, "%08x\n", pd->received_ado); } static DEVICE_ATTR_RO(rx_ado); static ssize_t get_battery_cap_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct usbpd *pd = dev_get_drvdata(dev); int val, ret; if (pd->spec_rev == USBPD_REV_20 || sscanf(buf, "%d\n", &val) != 1) { pd->get_battery_cap_db = -EINVAL; return -EINVAL; } pd->get_battery_cap_db = val; ret = trigger_tx_msg(pd, &pd->send_get_battery_cap); return ret ? ret : size; } static ssize_t get_battery_cap_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0; struct usbpd *pd = dev_get_drvdata(dev); if (pd->get_battery_cap_db == -EINVAL) return -EINVAL; for (i = 0; i < PD_BATTERY_CAP_DB_LEN; i++) len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", pd->battery_cap_db[i]); return len; } static DEVICE_ATTR_RW(get_battery_cap); static ssize_t get_battery_status_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct usbpd *pd = dev_get_drvdata(dev); int val, ret; if (pd->spec_rev == USBPD_REV_20 || sscanf(buf, "%d\n", &val) != 1) { pd->get_battery_status_db = -EINVAL; return -EINVAL; } pd->get_battery_status_db = val; ret = trigger_tx_msg(pd, &pd->send_get_battery_status); return ret ? ret : size; } static ssize_t get_battery_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); if (pd->get_battery_status_db == -EINVAL) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%d\n", pd->battery_sts_dobj); } static DEVICE_ATTR_RW(get_battery_status); static struct attribute *usbpd_attrs[] = { &dev_attr_contract.attr, &dev_attr_initial_pr.attr, &dev_attr_current_pr.attr, &dev_attr_initial_dr.attr, &dev_attr_current_dr.attr, &dev_attr_src_cap_id.attr, &dev_attr_pdo_h.attr, &dev_attr_pdos[0].attr, &dev_attr_pdos[1].attr, &dev_attr_pdos[2].attr, &dev_attr_pdos[3].attr, &dev_attr_pdos[4].attr, &dev_attr_pdos[5].attr, &dev_attr_pdos[6].attr, &dev_attr_select_pdo.attr, &dev_attr_rdo.attr, &dev_attr_rdo_h.attr, &dev_attr_hard_reset.attr, &dev_attr_get_src_cap_ext.attr, &dev_attr_get_pps_status.attr, &dev_attr_rx_ado.attr, &dev_attr_get_battery_cap.attr, &dev_attr_get_battery_status.attr, NULL, }; ATTRIBUTE_GROUPS(usbpd); static struct class usbpd_class = { .name = "usbpd", .owner = THIS_MODULE, .dev_uevent = usbpd_uevent, .dev_groups = usbpd_groups, }; static int match_usbpd_device(struct device *dev, const void *data) { return dev->parent == data; } static void devm_usbpd_put(struct device *dev, void *res) { struct usbpd **ppd = res; put_device(&(*ppd)->dev); } struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle) { struct usbpd **ptr, *pd = NULL; struct device_node *pd_np; struct platform_device *pdev; struct device *pd_dev; if (!usbpd_class.p) /* usbpd_init() not yet called */ return ERR_PTR(-EAGAIN); if (!dev->of_node) return ERR_PTR(-EINVAL); pd_np = of_parse_phandle(dev->of_node, phandle, 0); if (!pd_np) return ERR_PTR(-ENXIO); pdev = of_find_device_by_node(pd_np); if (!pdev) return ERR_PTR(-ENODEV); pd_dev = class_find_device(&usbpd_class, NULL, &pdev->dev, match_usbpd_device); if (!pd_dev) { platform_device_put(pdev); /* device was found but maybe hadn't probed yet, so defer */ return ERR_PTR(-EPROBE_DEFER); } ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL); if (!ptr) { put_device(pd_dev); platform_device_put(pdev); return ERR_PTR(-ENOMEM); } pd = dev_get_drvdata(pd_dev); if (!pd) return ERR_PTR(-EPROBE_DEFER); *ptr = pd; devres_add(dev, ptr); return pd; } EXPORT_SYMBOL(devm_usbpd_get_by_phandle); static int num_pd_instances; /** * usbpd_create - Create a new instance of USB PD protocol/policy engine * @parent - parent device to associate with * * This creates a new usbpd class device which manages the state of a * USB PD-capable port. The parent device that is passed in should be * associated with the physical device port, e.g. a PD PHY. * * Return: struct usbpd pointer, or an ERR_PTR value */ struct usbpd *usbpd_create(struct device *parent) { int ret; struct usbpd *pd; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); device_initialize(&pd->dev); pd->dev.class = &usbpd_class; pd->dev.parent = parent; dev_set_drvdata(&pd->dev, pd); ret = dev_set_name(&pd->dev, "usbpd%d", num_pd_instances++); if (ret) goto free_pd; ret = device_init_wakeup(&pd->dev, true); if (ret) goto free_pd; ret = device_add(&pd->dev); if (ret) goto free_pd; pd->wq = alloc_ordered_workqueue("usbpd_wq", WQ_FREEZABLE | WQ_HIGHPRI); if (!pd->wq) { ret = -ENOMEM; goto del_pd; } INIT_WORK(&pd->sm_work, usbpd_sm); hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); pd->timer.function = pd_timeout; mutex_init(&pd->swap_lock); pd->usb_psy = power_supply_get_by_name("usb"); if (!pd->usb_psy) { usbpd_dbg(&pd->dev, "Could not get USB power_supply, deferring probe\n"); ret = -EPROBE_DEFER; goto destroy_wq; } /* * associate extcon with the parent dev as it could have a DT * node which will be useful for extcon_get_edev_by_phandle() */ pd->extcon = devm_extcon_dev_allocate(parent, usbpd_extcon_cable); if (IS_ERR(pd->extcon)) { usbpd_err(&pd->dev, "failed to allocate extcon device\n"); ret = PTR_ERR(pd->extcon); goto put_psy; } pd->extcon->mutually_exclusive = usbpd_extcon_exclusive; ret = devm_extcon_dev_register(parent, pd->extcon); if (ret) { usbpd_err(&pd->dev, "failed to register extcon device\n"); goto put_psy; } pd->vbus = devm_regulator_get(parent, "vbus"); if (IS_ERR(pd->vbus)) { ret = PTR_ERR(pd->vbus); goto put_psy; } pd->vconn = devm_regulator_get(parent, "vconn"); if (IS_ERR(pd->vconn)) { ret = PTR_ERR(pd->vconn); goto put_psy; } pd->vconn_is_external = device_property_present(parent, "qcom,vconn-uses-external-source"); pd->num_sink_caps = device_property_read_u32_array(parent, "qcom,default-sink-caps", NULL, 0); if (pd->num_sink_caps > 0) { int i; u32 sink_caps[14]; if (pd->num_sink_caps % 2 || pd->num_sink_caps > 14) { ret = -EINVAL; usbpd_err(&pd->dev, "default-sink-caps must be be specified as voltage/current, max 7 pairs\n"); goto put_psy; } ret = device_property_read_u32_array(parent, "qcom,default-sink-caps", sink_caps, pd->num_sink_caps); if (ret) { usbpd_err(&pd->dev, "Error reading default-sink-caps\n"); goto put_psy; } pd->num_sink_caps /= 2; for (i = 0; i < pd->num_sink_caps; i++) { int v = sink_caps[i * 2] / 50; int c = sink_caps[i * 2 + 1] / 10; pd->sink_caps[i] = PD_SNK_PDO_FIXED(0, 0, 0, 0, 0, v, c); } /* First PDO includes additional capabilities */ pd->sink_caps[0] |= PD_SNK_PDO_FIXED(1, 0, 0, 1, 1, 0, 0); } else { memcpy(pd->sink_caps, default_snk_caps, sizeof(default_snk_caps)); pd->num_sink_caps = ARRAY_SIZE(default_snk_caps); } /* * Register the Android dual-role class (/sys/class/dual_role_usb/). * The first instance should be named "otg_default" as that's what * Android expects. * Note this is different than the /sys/class/usbpd/ created above. */ pd->dr_desc.name = (num_pd_instances == 1) ? "otg_default" : dev_name(&pd->dev); pd->dr_desc.supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP; pd->dr_desc.properties = usbpd_dr_properties; pd->dr_desc.num_properties = ARRAY_SIZE(usbpd_dr_properties); pd->dr_desc.get_property = usbpd_dr_get_property; pd->dr_desc.set_property = usbpd_dr_set_property; pd->dr_desc.property_is_writeable = usbpd_dr_prop_writeable; pd->dual_role = devm_dual_role_instance_register(&pd->dev, &pd->dr_desc); if (IS_ERR(pd->dual_role)) { usbpd_err(&pd->dev, "could not register dual_role instance\n"); goto put_psy; } else { pd->dual_role->drv_data = pd; } pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); spin_lock_init(&pd->rx_lock); INIT_LIST_HEAD(&pd->rx_q); INIT_LIST_HEAD(&pd->svid_handlers); init_completion(&pd->is_ready); init_completion(&pd->tx_chunk_request); pd->psy_nb.notifier_call = psy_changed; ret = power_supply_reg_notifier(&pd->psy_nb); if (ret) goto del_inst; /* force read initial power_supply values */ psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy); return pd; del_inst: list_del(&pd->instance); put_psy: power_supply_put(pd->usb_psy); destroy_wq: destroy_workqueue(pd->wq); del_pd: device_del(&pd->dev); free_pd: num_pd_instances--; kfree(pd); return ERR_PTR(ret); } EXPORT_SYMBOL(usbpd_create); /** * usbpd_destroy - Removes and frees a usbpd instance * @pd: the instance to destroy */ void usbpd_destroy(struct usbpd *pd) { if (!pd) return; list_del(&pd->instance); power_supply_unreg_notifier(&pd->psy_nb); power_supply_put(pd->usb_psy); destroy_workqueue(pd->wq); device_del(&pd->dev); kfree(pd); } EXPORT_SYMBOL(usbpd_destroy); static int __init usbpd_init(void) { usbpd_ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "usb_pd", 0); return class_register(&usbpd_class); } module_init(usbpd_init); static void __exit usbpd_exit(void) { class_unregister(&usbpd_class); } module_exit(usbpd_exit); MODULE_DESCRIPTION("USB Power Delivery Policy Engine"); MODULE_LICENSE("GPL v2");