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

usb: phy: add usb phy notify port status API

In Realtek SoC, the parameter of usb phy is designed to can dynamic
tuning base on port status. Therefore, add a notify callback of phy
driver when usb port status change.

The Realtek phy driver is designed to dynamically adjust disconnection
level and calibrate phy parameters. When the device connected bit changes
and when the disconnected bit changes, do port status change notification:

Check if portstatus is USB_PORT_STAT_CONNECTION and portchange is
USB_PORT_STAT_C_CONNECTION.
1. The device is connected, the driver lowers the disconnection level and
calibrates the phy parameters.
2. The device disconnects, the driver increases the disconnect level and
calibrates the phy parameters.

When controller to notify connect that device is already ready. If we
adjust the disconnection level in notify_connect, the disconnect may have
been triggered at this stage. So we need to change that as early as
possible. The status change of connection is before port reset.
Therefore, we add an api to notify phy the port status changes. In this
stage, the device is not port enable, and it will not trigger
disconnection.

Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
Link: https://lore.kernel.org/r/20230725033318.8361-1-stanley_chang@realtek.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Stanley Chang and committed by
Greg Kroah-Hartman
a08799cf 3609699c

+36
+23
drivers/usb/core/hub.c
··· 614 614 ret = 0; 615 615 } 616 616 mutex_unlock(&hub->status_mutex); 617 + 618 + /* 619 + * There is no need to lock status_mutex here, because status_mutex 620 + * protects hub->status, and the phy driver only checks the port 621 + * status without changing the status. 622 + */ 623 + if (!ret) { 624 + struct usb_device *hdev = hub->hdev; 625 + 626 + /* 627 + * Only roothub will be notified of port state changes, 628 + * since the USB PHY only cares about changes at the next 629 + * level. 630 + */ 631 + if (is_root_hub(hdev)) { 632 + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); 633 + 634 + if (hcd->usb_phy) 635 + usb_phy_notify_port_status(hcd->usb_phy, 636 + port1 - 1, *status, *change); 637 + } 638 + } 639 + 617 640 return ret; 618 641 } 619 642
+13
include/linux/usb/phy.h
··· 144 144 */ 145 145 int (*set_wakeup)(struct usb_phy *x, bool enabled); 146 146 147 + /* notify phy port status change */ 148 + int (*notify_port_status)(struct usb_phy *x, int port, 149 + u16 portstatus, u16 portchange); 150 + 147 151 /* notify phy connect status change */ 148 152 int (*notify_connect)(struct usb_phy *x, 149 153 enum usb_device_speed speed); ··· 316 312 { 317 313 if (x && x->set_wakeup) 318 314 return x->set_wakeup(x, enabled); 315 + else 316 + return 0; 317 + } 318 + 319 + static inline int 320 + usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus, u16 portchange) 321 + { 322 + if (x && x->notify_port_status) 323 + return x->notify_port_status(x, port, portstatus, portchange); 319 324 else 320 325 return 0; 321 326 }