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

USB: Calculate USB 3.0 exit latencies for LPM.

There are several different exit latencies associated with coming out of
the U1 or U2 lower power link state.

Device Exit Latency (DEL) is the maximum time it takes for the USB
device to bring its upstream link into U0. That can be found in the
SuperSpeed Extended Capabilities BOS descriptor for the device. The
time it takes for a particular link in the tree to exit to U0 is the
maximum of either the parent hub's U1/U2 DEL, or the child's U1/U2 DEL.

Hubs introduce a further delay that effects how long it takes a child
device to transition to U0. When a USB 3.0 hub receives a header
packet, it takes some time to decode that header and figure out which
downstream port the packet was destined for. If the port is not in U0,
this hub header decode latency will cause an additional delay for
bringing the child device to U0. This Hub Header Decode Latency is
found in the USB 3.0 hub descriptor.

We can use DEL and the header decode latency, along with additional
latencies imposed by each additional hub tier, to figure out the exit
latencies for both host-initiated and device-initiated exit to U0.

The Max Exit Latency (MEL) is the worst-case time it will take for a
host-initiated exit to U0, based on whether U1 or U2 link states are
enabled. The ping or packet must traverse the path to the device, and
each hub along the way incurs the hub header decode latency in order to
figure out which device the transfer was bound for. We say worst-case,
because some hubs may not be in the lowest link state that is enabled.
See the examples in section C.2.2.1.

Note that "HSD" is a "host specific delay" that the power appendix
architect has not been able to tell me how to calculate. There's no way
to get HSD from the xHCI registers either, so I'm simply ignoring it.

The Path Exit Latency (PEL) is the worst-case time it will take for a
device-initiate exit to U0 to place all the links from the device to the
host into U0.

The System Exit Latency (SEL) is another device-initiated exit latency.
SEL is useful for USB 3.0 devices that need to send data to the host at
specific intervals. The device may send an NRDY to indicate it isn't
ready to send data, then put its link into a lower power state. If it
needs to have that data transmitted at a specific time, it can use SEL
to back calculate when it will need to bring the link back into U0 to
meet its deadlines.

SEL is the worst-case time from the device-initiated exit to U0, to when
the device will receive a packet from the host controller. It includes
PEL, the time it takes for an ERDY to get to the host, a host-specific
delay for the host to process that ERDY, and the time it takes for the
packet to traverse the path to the device. See Figure C-2 in the USB
3.0 bus specification.

Note: I have not been able to get good answers about what the
host-specific delay to process the ERDY should be. The Intel HW
developers say it will be specific to the platform the xHCI host is
integrated into, and they say it's negligible. Ignore this too.

Separate from these four exit latencies are the U1/U2 timeout values we
program into the parent hubs. These timeouts tell the hub to attempt to
place the device into a lower power link state after the link has been
idle for that amount of time.

Create two arrays (one for U1 and one for U2) to store mel, pel, sel,
and the timeout values. Store the exit latency values in nanosecond
units, since that's the smallest units used (DEL is in us, but the Hub
Header Decode Latency is in ns).

If a USB 3.0 device doesn't have a SuperSpeed Extended Capabilities BOS
descriptor, it's highly unlikely it will be able to handle LPM requests
properly. So it's best to disable LPM for devices that don't have this
descriptor, and any children beneath it, if it's a USB 3.0 hub. Warn
users when that happens, since it means they have a non-compliant USB
3.0 device or hub.

This patch assumes a simplified design where links deep in the tree will
not have U1 or U2 enabled unless all their parent links have the
corresponding LPM state enabled. Eventually, we might want to allow a
different policy, and we can revisit this patch when that happens.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>

+247 -1
+210 -1
drivers/usb/core/hub.c
··· 189 189 return 1; 190 190 return 0; 191 191 } 192 + 193 + /* All USB 3.0 must support LPM, but we need their max exit latency 194 + * information from the SuperSpeed Extended Capabilities BOS descriptor. 195 + */ 196 + if (!udev->bos->ss_cap) { 197 + dev_warn(&udev->dev, "No LPM exit latency info found. " 198 + "Power management will be impacted.\n"); 199 + return 0; 200 + } 201 + if (udev->parent->lpm_capable) 202 + return 1; 203 + 204 + dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. " 205 + "Power management will be impacted.\n"); 192 206 return 0; 207 + } 208 + 209 + /* 210 + * Set the Maximum Exit Latency (MEL) for the host to initiate a transition from 211 + * either U1 or U2. 212 + */ 213 + static void usb_set_lpm_mel(struct usb_device *udev, 214 + struct usb3_lpm_parameters *udev_lpm_params, 215 + unsigned int udev_exit_latency, 216 + struct usb_hub *hub, 217 + struct usb3_lpm_parameters *hub_lpm_params, 218 + unsigned int hub_exit_latency) 219 + { 220 + unsigned int total_mel; 221 + unsigned int device_mel; 222 + unsigned int hub_mel; 223 + 224 + /* 225 + * Calculate the time it takes to transition all links from the roothub 226 + * to the parent hub into U0. The parent hub must then decode the 227 + * packet (hub header decode latency) to figure out which port it was 228 + * bound for. 229 + * 230 + * The Hub Header decode latency is expressed in 0.1us intervals (0x1 231 + * means 0.1us). Multiply that by 100 to get nanoseconds. 232 + */ 233 + total_mel = hub_lpm_params->mel + 234 + (hub->descriptor->u.ss.bHubHdrDecLat * 100); 235 + 236 + /* 237 + * How long will it take to transition the downstream hub's port into 238 + * U0? The greater of either the hub exit latency or the device exit 239 + * latency. 240 + * 241 + * The BOS U1/U2 exit latencies are expressed in 1us intervals. 242 + * Multiply that by 1000 to get nanoseconds. 243 + */ 244 + device_mel = udev_exit_latency * 1000; 245 + hub_mel = hub_exit_latency * 1000; 246 + if (device_mel > hub_mel) 247 + total_mel += device_mel; 248 + else 249 + total_mel += hub_mel; 250 + 251 + udev_lpm_params->mel = total_mel; 252 + } 253 + 254 + /* 255 + * Set the maximum Device to Host Exit Latency (PEL) for the device to initiate 256 + * a transition from either U1 or U2. 257 + */ 258 + static void usb_set_lpm_pel(struct usb_device *udev, 259 + struct usb3_lpm_parameters *udev_lpm_params, 260 + unsigned int udev_exit_latency, 261 + struct usb_hub *hub, 262 + struct usb3_lpm_parameters *hub_lpm_params, 263 + unsigned int hub_exit_latency, 264 + unsigned int port_to_port_exit_latency) 265 + { 266 + unsigned int first_link_pel; 267 + unsigned int hub_pel; 268 + 269 + /* 270 + * First, the device sends an LFPS to transition the link between the 271 + * device and the parent hub into U0. The exit latency is the bigger of 272 + * the device exit latency or the hub exit latency. 273 + */ 274 + if (udev_exit_latency > hub_exit_latency) 275 + first_link_pel = udev_exit_latency * 1000; 276 + else 277 + first_link_pel = hub_exit_latency * 1000; 278 + 279 + /* 280 + * When the hub starts to receive the LFPS, there is a slight delay for 281 + * it to figure out that one of the ports is sending an LFPS. Then it 282 + * will forward the LFPS to its upstream link. The exit latency is the 283 + * delay, plus the PEL that we calculated for this hub. 284 + */ 285 + hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel; 286 + 287 + /* 288 + * According to figure C-7 in the USB 3.0 spec, the PEL for this device 289 + * is the greater of the two exit latencies. 290 + */ 291 + if (first_link_pel > hub_pel) 292 + udev_lpm_params->pel = first_link_pel; 293 + else 294 + udev_lpm_params->pel = hub_pel; 295 + } 296 + 297 + /* 298 + * Set the System Exit Latency (SEL) to indicate the total worst-case time from 299 + * when a device initiates a transition to U0, until when it will receive the 300 + * first packet from the host controller. 301 + * 302 + * Section C.1.5.1 describes the four components to this: 303 + * - t1: device PEL 304 + * - t2: time for the ERDY to make it from the device to the host. 305 + * - t3: a host-specific delay to process the ERDY. 306 + * - t4: time for the packet to make it from the host to the device. 307 + * 308 + * t3 is specific to both the xHCI host and the platform the host is integrated 309 + * into. The Intel HW folks have said it's negligible, FIXME if a different 310 + * vendor says otherwise. 311 + */ 312 + static void usb_set_lpm_sel(struct usb_device *udev, 313 + struct usb3_lpm_parameters *udev_lpm_params) 314 + { 315 + struct usb_device *parent; 316 + unsigned int num_hubs; 317 + unsigned int total_sel; 318 + 319 + /* t1 = device PEL */ 320 + total_sel = udev_lpm_params->pel; 321 + /* How many external hubs are in between the device & the root port. */ 322 + for (parent = udev->parent, num_hubs = 0; parent->parent; 323 + parent = parent->parent) 324 + num_hubs++; 325 + /* t2 = 2.1us + 250ns * (num_hubs - 1) */ 326 + if (num_hubs > 0) 327 + total_sel += 2100 + 250 * (num_hubs - 1); 328 + 329 + /* t4 = 250ns * num_hubs */ 330 + total_sel += 250 * num_hubs; 331 + 332 + udev_lpm_params->sel = total_sel; 333 + } 334 + 335 + static void usb_set_lpm_parameters(struct usb_device *udev) 336 + { 337 + struct usb_hub *hub; 338 + unsigned int port_to_port_delay; 339 + unsigned int udev_u1_del; 340 + unsigned int udev_u2_del; 341 + unsigned int hub_u1_del; 342 + unsigned int hub_u2_del; 343 + 344 + if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER) 345 + return; 346 + 347 + hub = hdev_to_hub(udev->parent); 348 + /* It doesn't take time to transition the roothub into U0, since it 349 + * doesn't have an upstream link. 350 + */ 351 + if (!hub) 352 + return; 353 + 354 + udev_u1_del = udev->bos->ss_cap->bU1devExitLat; 355 + udev_u2_del = udev->bos->ss_cap->bU2DevExitLat; 356 + hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat; 357 + hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat; 358 + 359 + usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del, 360 + hub, &udev->parent->u1_params, hub_u1_del); 361 + 362 + usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del, 363 + hub, &udev->parent->u2_params, hub_u2_del); 364 + 365 + /* 366 + * Appendix C, section C.2.2.2, says that there is a slight delay from 367 + * when the parent hub notices the downstream port is trying to 368 + * transition to U0 to when the hub initiates a U0 transition on its 369 + * upstream port. The section says the delays are tPort2PortU1EL and 370 + * tPort2PortU2EL, but it doesn't define what they are. 371 + * 372 + * The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking 373 + * about the same delays. Use the maximum delay calculations from those 374 + * sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For 375 + * U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I 376 + * assume the device exit latencies they are talking about are the hub 377 + * exit latencies. 378 + * 379 + * What do we do if the U2 exit latency is less than the U1 exit 380 + * latency? It's possible, although not likely... 381 + */ 382 + port_to_port_delay = 1; 383 + 384 + usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del, 385 + hub, &udev->parent->u1_params, hub_u1_del, 386 + port_to_port_delay); 387 + 388 + if (hub_u2_del > hub_u1_del) 389 + port_to_port_delay = 1 + hub_u2_del - hub_u1_del; 390 + else 391 + port_to_port_delay = 1 + hub_u1_del; 392 + 393 + usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del, 394 + hub, &udev->parent->u2_params, hub_u2_del, 395 + port_to_port_delay); 396 + 397 + /* Now that we've got PEL, calculate SEL. */ 398 + usb_set_lpm_sel(udev, &udev->u1_params); 399 + usb_set_lpm_sel(udev, &udev->u2_params); 193 400 } 194 401 195 402 /* USB 2.0 spec Section 11.24.4.5 */ ··· 3433 3226 3434 3227 if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { 3435 3228 retval = usb_get_bos_descriptor(udev); 3436 - if (!retval) 3229 + if (!retval) { 3437 3230 udev->lpm_capable = usb_device_supports_lpm(udev); 3231 + usb_set_lpm_parameters(udev); 3232 + } 3438 3233 } 3439 3234 3440 3235 retval = 0;
+37
include/linux/usb.h
··· 378 378 USB_DEVICE_FIXED, 379 379 }; 380 380 381 + /* 382 + * USB 3.0 Link Power Management (LPM) parameters. 383 + * 384 + * PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit. 385 + * MEL is the USB 3.0 Link PM latency for host-initiated LPM exit. 386 + * All three are stored in nanoseconds. 387 + */ 388 + struct usb3_lpm_parameters { 389 + /* 390 + * Maximum exit latency (MEL) for the host to send a packet to the 391 + * device (either a Ping for isoc endpoints, or a data packet for 392 + * interrupt endpoints), the hubs to decode the packet, and for all hubs 393 + * in the path to transition the links to U0. 394 + */ 395 + unsigned int mel; 396 + /* 397 + * Maximum exit latency for a device-initiated LPM transition to bring 398 + * all links into U0. Abbreviated as "PEL" in section 9.4.12 of the USB 399 + * 3.0 spec, with no explanation of what "P" stands for. "Path"? 400 + */ 401 + unsigned int pel; 402 + 403 + /* 404 + * The System Exit Latency (SEL) includes PEL, and three other 405 + * latencies. After a device initiates a U0 transition, it will take 406 + * some time from when the device sends the ERDY to when it will finally 407 + * receive the data packet. Basically, SEL should be the worse-case 408 + * latency from when a device starts initiating a U0 transition to when 409 + * it will get data. 410 + */ 411 + unsigned int sel; 412 + }; 413 + 381 414 /** 382 415 * struct usb_device - kernel's representation of a USB device 383 416 * @devnum: device number; address on a USB bus ··· 468 435 * specific data for the device. 469 436 * @slot_id: Slot ID assigned by xHCI 470 437 * @removable: Device can be physically removed from this port 438 + * @u1_params: exit latencies for U1 (USB 3.0 LPM). 439 + * @u2_params: exit latencies for U2 (USB 3.0 LPM). 471 440 * 472 441 * Notes: 473 442 * Usbcore drivers should not set usbdev->state directly. Instead use ··· 542 507 struct wusb_dev *wusb_dev; 543 508 int slot_id; 544 509 enum usb_device_removable removable; 510 + struct usb3_lpm_parameters u1_params; 511 + struct usb3_lpm_parameters u2_params; 545 512 }; 546 513 #define to_usb_device(d) container_of(d, struct usb_device, dev) 547 514