Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: typec: tcpm: add support for Sink Cap Extended msg response

Add support for responding to Sink Cap Extended msg request. To achieve
this, include parsing support for DT properties related to Sink Cap
Extended. The request for Sink Cap Ext is a control message while the
response is an extended message (chunked). As the Sink Caps Extended
Data Block size (24 Byte) is less than MaxExtendedMsgChunkLen (26 Byte),
a single chunk is sufficient to complete this AMS.

Supporting sink cap extended messages while responding to a
Get_Sink_Caps_Extended request when port is in Sink role is required in
order to be compliant with at least USB PD Rev3.1 Ver1.8.

Signed-off-by: Amit Sunil Dhamne <amitsd@google.com>
Reviewed-by: Badhri Jagan Sridharan <badhri@google.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://patch.msgid.link/20260223-skedb-v2-2-60675765bc7e@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Amit Sunil Dhamne and committed by
Greg Kroah-Hartman
b558a9cc ef22555f

+332 -3
+252 -1
drivers/usb/typec/tcpm/tcpm.c
··· 12 12 #include <linux/jiffies.h> 13 13 #include <linux/kernel.h> 14 14 #include <linux/kthread.h> 15 + #include <linux/minmax.h> 15 16 #include <linux/module.h> 16 17 #include <linux/mutex.h> 17 18 #include <linux/power_supply.h> ··· 189 188 S(STRUCTURED_VDMS), \ 190 189 S(COUNTRY_INFO), \ 191 190 S(COUNTRY_CODES), \ 192 - S(REVISION_INFORMATION) 191 + S(REVISION_INFORMATION), \ 192 + S(GETTING_SINK_EXTENDED_CAPABILITIES) 193 193 194 194 #define GENERATE_ENUM(e) e 195 195 #define GENERATE_STRING(s) #s ··· 231 229 PD_MSG_DATA_SINK_CAP, 232 230 PD_MSG_DATA_SOURCE_CAP, 233 231 PD_MSG_DATA_REV, 232 + PD_MSG_EXT_SINK_CAP_EXT 234 233 }; 235 234 236 235 enum adev_actions { ··· 338 335 u32 ps_src_off_time; 339 336 u32 cc_debounce_time; 340 337 u32 snk_bc12_cmpletion_time; 338 + }; 339 + 340 + /* Convert microwatt to watt */ 341 + #define UW_TO_W(pow) ((pow) / 1000000) 342 + 343 + /* 344 + * struct pd_identifier - Contains info about PD identifiers 345 + * @vid: Vendor ID (assigned by USB-IF) 346 + * @pid: Product ID (assigned by manufacturer) 347 + * @xid: Value assigned by USB-IF for product 348 + */ 349 + struct pd_identifier { 350 + u16 vid; 351 + u16 pid; 352 + u32 xid; 353 + }; 354 + 355 + /* 356 + * struct sink_caps_ext_data - Sink extended capability data 357 + * @load_step: Indicates the load step slew rate. Value of 0 indicates 150mA/us 358 + * & 1 indicates 500 mA/us 359 + * @load_char: Snk overload characteristics 360 + * @compliance: Types of sources the sink has been tested & certified on 361 + * @modes: Charging caps & power sources supported 362 + * @spr_min_pdp: Sink Minimum PDP for SPR mode (in Watts) 363 + * @spr_op_pdp: Sink Operational PDP for SPR mode (in Watts) 364 + * @spr_max_pdp: Sink Maximum PDP for SPR mode (in Watts) 365 + */ 366 + struct sink_caps_ext_data { 367 + u8 load_step; 368 + u16 load_char; 369 + u8 compliance; 370 + u8 modes; 371 + u8 spr_min_pdp; 372 + u8 spr_op_pdp; 373 + u8 spr_max_pdp; 341 374 }; 342 375 343 376 struct tcpm_port { ··· 624 585 625 586 /* Indicates maximum (revision, version) supported */ 626 587 struct pd_revision_info pd_rev; 588 + 589 + struct pd_identifier pd_ident; 590 + struct sink_caps_ext_data sink_caps_ext; 627 591 #ifdef CONFIG_DEBUG_FS 628 592 struct dentry *dentry; 629 593 struct mutex logbuffer_lock; /* log buffer access lock */ ··· 1406 1364 nr_pdo); 1407 1365 } 1408 1366 1367 + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); 1368 + } 1369 + 1370 + static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) 1371 + { 1372 + u16 operating_snk_watt = port->operating_snk_mw / 1000; 1373 + struct sink_caps_ext_data *data = &port->sink_caps_ext; 1374 + struct pd_identifier *pd_ident = &port->pd_ident; 1375 + struct sink_caps_ext_msg skedb = {0}; 1376 + struct pd_message msg; 1377 + u8 data_obj_cnt; 1378 + 1379 + if (!port->self_powered) 1380 + data->spr_op_pdp = operating_snk_watt; 1381 + 1382 + /* 1383 + * SPR Sink Minimum PDP indicates the minimum power required to operate 1384 + * a sink device in its lowest level of functionality without requiring 1385 + * power from the battery. We can use the operating_snk_watt value to 1386 + * populate it, as operating_snk_watt indicates device's min operating 1387 + * power. 1388 + */ 1389 + data->spr_min_pdp = operating_snk_watt; 1390 + 1391 + if (data->spr_op_pdp < data->spr_min_pdp || 1392 + data->spr_max_pdp < data->spr_op_pdp) { 1393 + tcpm_log(port, 1394 + "Invalid PDP values, Min PDP:%u, Op PDP:%u, Max PDP:%u", 1395 + data->spr_min_pdp, data->spr_op_pdp, data->spr_max_pdp); 1396 + return -EOPNOTSUPP; 1397 + } 1398 + 1399 + memset(&msg, 0, sizeof(msg)); 1400 + skedb.vid = cpu_to_le16(pd_ident->vid); 1401 + skedb.pid = cpu_to_le16(pd_ident->pid); 1402 + skedb.xid = cpu_to_le32(pd_ident->xid); 1403 + skedb.skedb_ver = SKEDB_VER_1_0; 1404 + skedb.load_step = data->load_step; 1405 + skedb.load_char = cpu_to_le16(data->load_char); 1406 + skedb.compliance = data->compliance; 1407 + skedb.modes = data->modes; 1408 + skedb.spr_min_pdp = data->spr_min_pdp; 1409 + skedb.spr_op_pdp = data->spr_op_pdp; 1410 + skedb.spr_max_pdp = data->spr_max_pdp; 1411 + memcpy(msg.ext_msg.data, &skedb, sizeof(skedb)); 1412 + msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(skedb), 1413 + 0, /* Denotes if request chunk */ 1414 + 0, /* Chunk Number */ 1415 + 1 /* Chunked */); 1416 + 1417 + data_obj_cnt = count_chunked_data_objs(sizeof(skedb)); 1418 + msg.header = cpu_to_le16(PD_HEADER(PD_EXT_SINK_CAP_EXT, 1419 + port->pwr_role, 1420 + port->data_role, 1421 + port->negotiated_rev, 1422 + port->message_id, 1423 + data_obj_cnt, 1424 + 1 /* Denotes if ext header */)); 1409 1425 return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); 1410 1426 } 1411 1427 ··· 3746 3646 PD_MSG_CTRL_NOT_SUPP, 3747 3647 NONE_AMS); 3748 3648 break; 3649 + case PD_CTRL_GET_SINK_CAP_EXT: 3650 + /* This is an unsupported message if port type is SRC */ 3651 + if (port->negotiated_rev >= PD_REV30 && 3652 + port->port_type != TYPEC_PORT_SRC) 3653 + tcpm_pd_handle_msg(port, PD_MSG_EXT_SINK_CAP_EXT, 3654 + GETTING_SINK_EXTENDED_CAPABILITIES); 3655 + else 3656 + tcpm_pd_handle_msg(port, 3657 + port->negotiated_rev < PD_REV30 ? 3658 + PD_MSG_CTRL_REJECT : 3659 + PD_MSG_CTRL_NOT_SUPP, 3660 + NONE_AMS); 3661 + break; 3749 3662 default: 3750 3663 tcpm_pd_handle_msg(port, 3751 3664 port->negotiated_rev < PD_REV30 ? ··· 4008 3895 if (ret) 4009 3896 tcpm_log(port, 4010 3897 "Unable to send revision msg, ret=%d", 3898 + ret); 3899 + tcpm_ams_finish(port); 3900 + break; 3901 + case PD_MSG_EXT_SINK_CAP_EXT: 3902 + ret = tcpm_pd_send_sink_cap_ext(port); 3903 + if (ret == -EOPNOTSUPP) 3904 + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); 3905 + else if (ret < 0) 3906 + tcpm_log(port, 3907 + "Unable to transmit sink cap extended, ret=%d", 4011 3908 ret); 4012 3909 tcpm_ams_finish(port); 4013 3910 break; ··· 7405 7282 port->timings.snk_bc12_cmpletion_time = val; 7406 7283 } 7407 7284 7285 + static void tcpm_fw_get_pd_ident(struct tcpm_port *port) 7286 + { 7287 + struct pd_identifier *pd_ident = &port->pd_ident; 7288 + u32 *vdo; 7289 + 7290 + /* First 3 vdo values contain info regarding USB PID, VID & XID */ 7291 + if (port->nr_snk_vdo >= 3) 7292 + vdo = port->snk_vdo; 7293 + else if (port->nr_snk_vdo_v1 >= 3) 7294 + vdo = port->snk_vdo_v1; 7295 + else 7296 + return; 7297 + 7298 + pd_ident->vid = PD_IDH_VID(vdo[0]); 7299 + pd_ident->pid = PD_PRODUCT_PID(vdo[2]); 7300 + pd_ident->xid = PD_CSTAT_XID(vdo[1]); 7301 + tcpm_log(port, "vid:%#x pid:%#x xid:%#x", 7302 + pd_ident->vid, pd_ident->pid, pd_ident->xid); 7303 + } 7304 + 7305 + static void tcpm_parse_snk_pdos(struct tcpm_port *port) 7306 + { 7307 + struct sink_caps_ext_data *caps = &port->sink_caps_ext; 7308 + u32 max_mv, max_ma; 7309 + u8 avs_tier1_pdp, avs_tier2_pdp; 7310 + int i, pdo_itr; 7311 + u32 *snk_pdos; 7312 + 7313 + for (i = 0; i < port->pd_count; ++i) { 7314 + snk_pdos = port->pd_list[i]->sink_desc.pdo; 7315 + for (pdo_itr = 0; pdo_itr < PDO_MAX_OBJECTS && snk_pdos[pdo_itr]; 7316 + ++pdo_itr) { 7317 + u32 pdo = snk_pdos[pdo_itr]; 7318 + u8 curr_snk_pdp = 0; 7319 + 7320 + switch (pdo_type(pdo)) { 7321 + case PDO_TYPE_FIXED: 7322 + max_mv = pdo_fixed_voltage(pdo); 7323 + max_ma = pdo_fixed_current(pdo); 7324 + curr_snk_pdp = UW_TO_W(max_mv * max_ma); 7325 + break; 7326 + case PDO_TYPE_BATT: 7327 + curr_snk_pdp = UW_TO_W(pdo_max_power(pdo)); 7328 + break; 7329 + case PDO_TYPE_VAR: 7330 + max_mv = pdo_max_voltage(pdo); 7331 + max_ma = pdo_max_current(pdo); 7332 + curr_snk_pdp = UW_TO_W(max_mv * max_ma); 7333 + break; 7334 + case PDO_TYPE_APDO: 7335 + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { 7336 + max_mv = pdo_pps_apdo_max_voltage(pdo); 7337 + max_ma = pdo_pps_apdo_max_current(pdo); 7338 + curr_snk_pdp = UW_TO_W(max_mv * max_ma); 7339 + caps->modes |= SINK_MODE_PPS; 7340 + } else if (pdo_apdo_type(pdo) == 7341 + APDO_TYPE_SPR_AVS) { 7342 + avs_tier1_pdp = UW_TO_W(SPR_AVS_TIER1_MAX_VOLT_MV 7343 + * pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo)); 7344 + avs_tier2_pdp = UW_TO_W(SPR_AVS_TIER2_MAX_VOLT_MV 7345 + * pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo)); 7346 + curr_snk_pdp = max(avs_tier1_pdp, avs_tier2_pdp); 7347 + caps->modes |= SINK_MODE_AVS; 7348 + } 7349 + break; 7350 + default: 7351 + tcpm_log(port, "Invalid source PDO type, ignoring"); 7352 + continue; 7353 + } 7354 + 7355 + caps->spr_max_pdp = max(caps->spr_max_pdp, 7356 + curr_snk_pdp); 7357 + } 7358 + } 7359 + } 7360 + 7361 + static void tcpm_fw_get_sink_caps_ext(struct tcpm_port *port, 7362 + struct fwnode_handle *fwnode) 7363 + { 7364 + struct sink_caps_ext_data *caps = &port->sink_caps_ext; 7365 + int ret; 7366 + u32 val; 7367 + 7368 + /* 7369 + * Load step represents the change in current per usec that a given 7370 + * source can tolerate while maintaining Vbus within the vSrcValid 7371 + * range. For a sink this represents the "preferred" load-step value. It 7372 + * can only have 2 values (150 mA/usec or 500 mA/usec) with 150 mA/usec 7373 + * being the default. 7374 + */ 7375 + ret = fwnode_property_read_u32(fwnode, "sink-load-step", &val); 7376 + if (!ret) 7377 + caps->load_step = val == 500 ? 1 : 0; 7378 + 7379 + fwnode_property_read_u16(fwnode, "sink-load-characteristics", 7380 + &caps->load_char); 7381 + fwnode_property_read_u8(fwnode, "sink-compliance", &caps->compliance); 7382 + caps->modes = SINK_MODE_VBUS; 7383 + 7384 + /* 7385 + * As per "6.5.13.14" SPR Sink Operational PDP definition, for battery 7386 + * powered devices, this value will correspond to the PDP of the 7387 + * charging adapter either shipped or recommended for use with it. For 7388 + * batteryless sink devices SPR Operational PDP indicates the power 7389 + * required to operate all the device's functional modes. Hence, this 7390 + * value may be considered equal to port's operating_snk_mw. As 7391 + * operating_sink_mw can change as per the pd set used thus, OP PDP 7392 + * is determined when populating Sink Caps Extended Data Block. 7393 + */ 7394 + if (port->self_powered) { 7395 + fwnode_property_read_u32(fwnode, "charging-adapter-pdp-milliwatt", 7396 + &val); 7397 + caps->spr_op_pdp = (u8)(val / 1000); 7398 + caps->modes |= SINK_MODE_BATT; 7399 + } 7400 + 7401 + tcpm_parse_snk_pdos(port); 7402 + tcpm_log(port, 7403 + "load-step:%#x load-char:%#x compl:%#x op-pdp:%#x max-pdp:%#x", 7404 + caps->load_step, caps->load_char, caps->compliance, 7405 + caps->spr_op_pdp, caps->spr_max_pdp); 7406 + } 7407 + 7408 7408 static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) 7409 7409 { 7410 7410 struct fwnode_handle *capabilities, *caps = NULL; ··· 7701 7455 } 7702 7456 } 7703 7457 7458 + if (port->port_type != TYPEC_PORT_SRC) 7459 + tcpm_fw_get_sink_caps_ext(port, fwnode); 7460 + 7704 7461 put_caps: 7705 7462 if (caps != fwnode) 7706 7463 fwnode_handle_put(caps); ··· 7745 7496 if (ret < 0) 7746 7497 return ret; 7747 7498 } 7499 + 7500 + tcpm_fw_get_pd_ident(port); 7748 7501 7749 7502 return 0; 7750 7503 }
+80 -2
include/linux/usb/pd.h
··· 34 34 PD_CTRL_FR_SWAP = 19, 35 35 PD_CTRL_GET_PPS_STATUS = 20, 36 36 PD_CTRL_GET_COUNTRY_CODES = 21, 37 - /* 22-23 Reserved */ 37 + PD_CTRL_GET_SINK_CAP_EXT = 22, 38 + /* 23 Reserved */ 38 39 PD_CTRL_GET_REVISION = 24, 39 40 /* 25-31 Reserved */ 40 41 }; ··· 73 72 PD_EXT_PPS_STATUS = 12, 74 73 PD_EXT_COUNTRY_INFO = 13, 75 74 PD_EXT_COUNTRY_CODES = 14, 76 - /* 15-31 Reserved */ 75 + PD_EXT_SINK_CAP_EXT = 15, 76 + /* 16-31 Reserved */ 77 77 }; 78 78 79 79 #define PD_REV10 0x0 ··· 207 205 }; 208 206 } __packed; 209 207 208 + /* 209 + * count_chunked_data_objs - Helper to calculate number of Data Objects on a 4 210 + * byte boundary. 211 + * @size: Size of data block for extended message. Should *not* include extended 212 + * header size. 213 + */ 214 + static inline u8 count_chunked_data_objs(u32 size) 215 + { 216 + size += offsetof(struct pd_chunked_ext_message_data, data); 217 + return ((size / 4) + (size % 4 ? 1 : 0)); 218 + } 219 + 220 + /* Sink Caps Extended Data Block Version */ 221 + #define SKEDB_VER_1_0 1 222 + 223 + /* Sink Caps Extended Sink Modes */ 224 + #define SINK_MODE_PPS BIT(0) 225 + #define SINK_MODE_VBUS BIT(1) 226 + #define SINK_MODE_AC_SUPPLY BIT(2) 227 + #define SINK_MODE_BATT BIT(3) 228 + #define SINK_MODE_BATT_UL BIT(4) /* Unlimited battery power supply */ 229 + #define SINK_MODE_AVS BIT(5) 230 + 231 + /** 232 + * struct sink_caps_ext_msg - Sink extended capability PD message 233 + * @vid: Vendor ID 234 + * @pid: Product ID 235 + * @xid: Value assigned by USB-IF for product 236 + * @fw: Firmware version 237 + * @hw: Hardware version 238 + * @skedb_ver: Sink Caps Extended Data Block (SKEDB) Version 239 + * @load_step: Indicates the load step slew rate. 240 + * @load_char: Sink overload characteristics 241 + * @compliance: Types of sources the sink has been tested & certified on 242 + * @touch_temp: Indicates the IEC standard to which the touch temperature 243 + * conforms to (if applicable). 244 + * @batt_info: Indicates number batteries and hot swappable ports 245 + * @modes: Charging caps & power sources supported 246 + * @spr_min_pdp: Sink Minimum PDP for SPR mode 247 + * @spr_op_pdp: Sink Operational PDP for SPR mode 248 + * @spr_max_pdp: Sink Maximum PDP for SPR mode 249 + * @epr_min_pdp: Sink Minimum PDP for EPR mode 250 + * @epr_op_pdp: Sink Operational PDP for EPR mode 251 + * @epr_max_pdp: Sink Maximum PDP for EPR mode 252 + */ 253 + struct sink_caps_ext_msg { 254 + __le16 vid; 255 + __le16 pid; 256 + __le32 xid; 257 + u8 fw; 258 + u8 hw; 259 + u8 skedb_ver; 260 + u8 load_step; 261 + __le16 load_char; 262 + u8 compliance; 263 + u8 touch_temp; 264 + u8 batt_info; 265 + u8 modes; 266 + u8 spr_min_pdp; 267 + u8 spr_op_pdp; 268 + u8 spr_max_pdp; 269 + u8 epr_min_pdp; 270 + u8 epr_op_pdp; 271 + u8 epr_max_pdp; 272 + } __packed; 273 + 210 274 /* PDO: Power Data Object */ 211 275 #define PDO_MAX_OBJECTS 7 212 276 ··· 397 329 #define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR GENMASK(19, 10) /* 10mA unit */ 398 330 #define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ 399 331 332 + /* SPR AVS has two different current ranges 9V - 15V, 15V - 20V */ 333 + #define SPR_AVS_TIER1_MIN_VOLT_MV 9000 334 + #define SPR_AVS_TIER1_MAX_VOLT_MV 15000 335 + #define SPR_AVS_TIER2_MAX_VOLT_MV 20000 336 + 400 337 static inline enum pd_pdo_type pdo_type(u32 pdo) 401 338 { 402 339 return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; ··· 410 337 static inline unsigned int pdo_fixed_voltage(u32 pdo) 411 338 { 412 339 return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; 340 + } 341 + 342 + static inline unsigned int pdo_fixed_current(u32 pdo) 343 + { 344 + return ((pdo >> PDO_FIXED_CURR_SHIFT) & PDO_CURR_MASK) * 10; 413 345 } 414 346 415 347 static inline unsigned int pdo_min_voltage(u32 pdo)