kernel: backport fixes for realtek r8152
authorMarty Jones <mj8263788@gmail.com>
Tue, 28 Nov 2023 18:27:18 +0000 (13:27 -0500)
committerChristian Lamparter <chunkeey@gmail.com>
Sat, 2 Dec 2023 20:57:38 +0000 (21:57 +0100)
Fixes issues with RTL8156 2.5G USB adapters

- # ethtool eth1
Settings for eth1:
        Supported ports: [ ]
        Supported link modes:   Not reported
        Supported pause frame use: No
        Supports auto-negotiation: No
        Supported FEC modes: Not reported
        Advertised link modes:  Not reported
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Advertised FEC modes: Not reported
        Speed: 2500Mb/s
        Duplex: Half
        Port: Twisted Pair
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: off
        MDI-X: Unknown
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: yes
- #

- r8152: break the loop when the budget is exhausted
- r8152: Block future register access if register access fails
- r8152: Rename RTL8152_UNPLUG to RTL8152_INACCESSIBLE
- r8152: add vendor/device ID pair for D-Link DUB-E250
- r8152: try to use a normal budget
- r8152: set bp in bulk
- r8152: adjust generic_ocp_write function
- r8152: fix the autosuspend doesn't work
- r8152: Add __GFP_NOWARN to big allocations
- r8152: reduce the control transfer of rtl8152_get_version()
- r8152: remove rtl_vendor_mode function
- r8152: avoid to change cfg for all devices
- r8152: add USB device driver for config selection
- r8152: use napi_gro_frags
- cdc_ether: no need to blacklist any r8152 devices
- cdc_ether: add u-blox 0x1313 composition

Build system: x86_64
Build-tested: bcm2711, rockchip, x86/64
Run-tested: bcm2711/RPi4B, rockchip/nanopi r2s, x86/64

Signed-off-by: Marty Jones <mj8263788@gmail.com>
32 files changed:
target/linux/generic/backport-5.15/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-09-r8152-set-bp-in-bulk.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-09-r8152-set-bp-in-bulk.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/795-v6.7-16-r8152-use-napi_gro_frags.patch [new file with mode: 0644]
target/linux/generic/hack-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch
target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch

diff --git a/target/linux/generic/backport-5.15/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch b/target/linux/generic/backport-5.15/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch
new file mode 100644 (file)
index 0000000..38ddcb5
--- /dev/null
@@ -0,0 +1,229 @@
+From ec51fbd1b8a2bca2948dede99c14ec63dc57ff6b Mon Sep 17 00:00:00 2001
+From: Bjørn Mork <bjorn@mork.no>
+Date: Fri, 6 Jan 2023 17:07:38 +0100
+Subject: [PATCH] r8152: add USB device driver for config selection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Subclassing the generic USB device driver to override the
+default configuration selection regardless of matching interface
+drivers.
+
+The r815x family devices expose a vendor specific function which
+the r8152 interface driver wants to handle.  This is the preferred
+device mode. Additionally one or more USB class functions are
+usually supported for hosts lacking a vendor specific driver. The
+choice is USB configuration based, with one alternate function per
+configuration.
+
+Example device with both NCM and ECM alternate cfgs:
+
+T:  Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  4 Spd=5000 MxCh= 0
+D:  Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs=  3
+P:  Vendor=0bda ProdID=8156 Rev=31.00
+S:  Manufacturer=Realtek
+S:  Product=USB 10/100/1G/2.5G LAN
+S:  SerialNumber=001000001
+C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=256mA
+I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=00 Driver=r8152
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=83(I) Atr=03(Int.) MxPS=   2 Ivl=128ms
+C:  #Ifs= 2 Cfg#= 2 Atr=a0 MxPwr=256mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=
+E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=
+I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+C:  #Ifs= 2 Cfg#= 3 Atr=a0 MxPwr=256mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=06 Prot=00 Driver=
+E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=00 Driver=
+I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+
+A problem with this is that Linux will prefer class functions over
+vendor specific functions. Using the above example, Linux defaults
+to cfg #2, running the device in a sub-optimal NCM mode.
+
+Previously we've attempted to work around the problem by
+blacklisting the devices in the ECM class driver "cdc_ether", and
+matching on the ECM class function in the vendor specific interface
+driver. The latter has been used to switch back to the vendor
+specific configuration when the driver is probed for a class
+function.
+
+This workaround has several issues;
+- class driver blacklists is additional maintanence cruft in an
+  unrelated driver
+- class driver blacklists prevents users from optionally running
+  the devices in class mode
+- each device needs double match entries in the vendor driver
+- the initial probing as a class function slows down device
+  discovery
+
+Now these issues have become even worse with the introduction of
+firmware supporting both NCM and ECM, where NCM ends up as the
+default mode in Linux. To use the same workaround, we now have
+to blacklist the devices in to two different class drivers and
+add yet another match entry to the vendor specific driver.
+
+This patch implements an alternative workaround strategy -
+independent of the interface drivers.  It avoids adding a
+blacklist to the cdc_ncm driver and will let us remove the
+existing blacklist from the cdc_ether driver.
+
+As an additional bonus, removing the blacklists allow users to
+select one of the other device modes if wanted.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 113 ++++++++++++++++++++++++++++------------
+ 1 file changed, 81 insertions(+), 32 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9625,6 +9625,9 @@ static int rtl8152_probe(struct usb_inte
+       if (version == RTL_VER_UNKNOWN)
+               return -ENODEV;
++      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
++              return -ENODEV;
++
+       if (!rtl_vendor_mode(intf))
+               return -ENODEV;
+@@ -9834,43 +9837,35 @@ static void rtl8152_disconnect(struct us
+       }
+ }
+-#define REALTEK_USB_DEVICE(vend, prod)        { \
+-      USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC), \
+-}, \
+-{ \
+-      USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_COMM, \
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), \
+-}
+ /* table of devices that work with this driver */
+ static const struct usb_device_id rtl8152_table[] = {
+       /* Realtek */
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8053),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8155),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8156),
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8050) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8053) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8152) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8153) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) },
+       /* Microsoft */
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0c5e),
+-      REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x304f),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3054),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3062),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3069),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3082),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7205),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x720c),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7214),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x721e),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0xa387),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041),
+-      REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff),
+-      REALTEK_USB_DEVICE(VENDOR_ID_TPLINK,  0x0601),
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) },
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6) },
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927) },
++      { USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x304f) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3054) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3062) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3069) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3082) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x7205) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x720c) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x7214) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x721e) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0xa387) },
++      { USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041) },
++      { USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff) },
++      { USB_DEVICE(VENDOR_ID_TPLINK,  0x0601) },
+       {}
+ };
+@@ -9890,7 +9885,61 @@ static struct usb_driver rtl8152_driver
+       .disable_hub_initiated_lpm = 1,
+ };
+-module_usb_driver(rtl8152_driver);
++static int rtl8152_cfgselector_probe(struct usb_device *udev)
++{
++      struct usb_host_config *c;
++      int i, num_configs;
++
++      /* The vendor mode is not always config #1, so to find it out. */
++      c = udev->config;
++      num_configs = udev->descriptor.bNumConfigurations;
++      for (i = 0; i < num_configs; (i++, c++)) {
++              struct usb_interface_descriptor *desc = NULL;
++
++              if (!c->desc.bNumInterfaces)
++                      continue;
++              desc = &c->intf_cache[0]->altsetting->desc;
++              if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
++                      break;
++      }
++
++      if (i == num_configs)
++              return -ENODEV;
++
++      if (usb_set_configuration(udev, c->desc.bConfigurationValue)) {
++              dev_err(&udev->dev, "Failed to set configuration %d\n",
++                      c->desc.bConfigurationValue);
++              return -ENODEV;
++      }
++
++      return 0;
++}
++
++static struct usb_device_driver rtl8152_cfgselector_driver = {
++      .name =         MODULENAME "-cfgselector",
++      .probe =        rtl8152_cfgselector_probe,
++      .id_table =     rtl8152_table,
++      .generic_subclass = 1,
++};
++
++static int __init rtl8152_driver_init(void)
++{
++      int ret;
++
++      ret = usb_register_device_driver(&rtl8152_cfgselector_driver, THIS_MODULE);
++      if (ret)
++              return ret;
++      return usb_register(&rtl8152_driver);
++}
++
++static void __exit rtl8152_driver_exit(void)
++{
++      usb_deregister(&rtl8152_driver);
++      usb_deregister_device_driver(&rtl8152_cfgselector_driver);
++}
++
++module_init(rtl8152_driver_init);
++module_exit(rtl8152_driver_exit);
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/target/linux/generic/backport-5.15/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch b/target/linux/generic/backport-5.15/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch
new file mode 100644 (file)
index 0000000..2b3272c
--- /dev/null
@@ -0,0 +1,158 @@
+From 69649ef8405320f81497f4757faac8234f61b167 Mon Sep 17 00:00:00 2001
+From: Bjørn Mork <bjorn@mork.no>
+Date: Fri, 6 Jan 2023 17:07:39 +0100
+Subject: [PATCH] cdc_ether: no need to blacklist any r8152 devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The r8152 driver does not need this anymore.
+
+Dropping blacklist entries adds optional support for these
+devices in ECM mode.
+
+The 8153 devices are handled by the r8153_ecm driver when
+in ECM mode, and must still be blacklisted here.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/cdc_ether.c | 114 ------------------------------------
+ 1 file changed, 114 deletions(-)
+
+--- a/drivers/net/usb/cdc_ether.c
++++ b/drivers/net/usb/cdc_ether.c
+@@ -767,13 +767,6 @@ static const struct usb_device_id produc
+       .driver_info = 0,
+ },
+-/* Realtek RTL8152 Based USB 2.0 Ethernet Adapters */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+ /* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */
+ {
+       USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM,
+@@ -781,119 +774,12 @@ static const struct usb_device_id       produc
+       .driver_info = 0,
+ },
+-/* Samsung USB Ethernet Adapters */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, 0xa101, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-#if IS_ENABLED(CONFIG_USB_RTL8152)
+-/* Linksys USB3GIGV1 Ethernet Adapter */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LINKSYS_VENDOR_ID, 0x0041, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-#endif
+-
+-/* Lenovo ThinkPad OneLink+ Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3054, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad USB-C Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3062, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad Thunderbolt 3 Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3069, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad Thunderbolt 3 Dock Gen 2 (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3082, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo USB C to Ethernet Adapter (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x720c, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo USB-C Travel Hub (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7214, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+ /* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */
+ {
+       USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x721e, USB_CLASS_COMM,
+                       USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+ },
+-
+-/* ThinkPad USB-C Dock Gen 2 (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0xa387, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* NVIDIA Tegra USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(NVIDIA_VENDOR_ID, 0x09ff, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface 2 dock (based on Realtek RTL8152) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07ab, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07c6, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153B) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x0927, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* TP-LINK UE300 USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, 0x0601, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+ /* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
+ {
diff --git a/target/linux/generic/backport-5.15/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch b/target/linux/generic/backport-5.15/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch
new file mode 100644 (file)
index 0000000..8bbf0be
--- /dev/null
@@ -0,0 +1,64 @@
+From 0d4cda805a183bbe523f2407edb5c14ade50b841 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 17 Jan 2023 11:03:44 +0800
+Subject: [PATCH] r8152: avoid to change cfg for all devices
+
+The rtl8152_cfgselector_probe() should set the USB configuration to the
+vendor mode only for the devices which the driver (r8152) supports.
+Otherwise, no driver would be used for such devices.
+
+Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9531,9 +9531,8 @@ static int rtl_fw_init(struct r8152 *tp)
+       return 0;
+ }
+-u8 rtl8152_get_version(struct usb_interface *intf)
++static u8 __rtl_get_hw_ver(struct usb_device *udev)
+ {
+-      struct usb_device *udev = interface_to_usbdev(intf);
+       u32 ocp_data = 0;
+       __le32 *tmp;
+       u8 version;
+@@ -9603,10 +9602,19 @@ u8 rtl8152_get_version(struct usb_interf
+               break;
+       default:
+               version = RTL_VER_UNKNOWN;
+-              dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data);
++              dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data);
+               break;
+       }
++      return version;
++}
++
++u8 rtl8152_get_version(struct usb_interface *intf)
++{
++      u8 version;
++
++      version = __rtl_get_hw_ver(interface_to_usbdev(intf));
++
+       dev_dbg(&intf->dev, "Detected version 0x%04x\n", version);
+       return version;
+@@ -9890,6 +9898,12 @@ static int rtl8152_cfgselector_probe(str
+       struct usb_host_config *c;
+       int i, num_configs;
++      /* Switch the device to vendor mode, if and only if the vendor mode
++       * driver supports it.
++       */
++      if (__rtl_get_hw_ver(udev) == RTL_VER_UNKNOWN)
++              return 0;
++
+       /* The vendor mode is not always config #1, so to find it out. */
+       c = udev->config;
+       num_configs = udev->descriptor.bNumConfigurations;
diff --git a/target/linux/generic/backport-5.15/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch b/target/linux/generic/backport-5.15/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch
new file mode 100644 (file)
index 0000000..c9bd0df
--- /dev/null
@@ -0,0 +1,71 @@
+From 95a4c1d617b92cdc4522297741b56e8f6cd01a1e Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Thu, 19 Jan 2023 15:40:42 +0800
+Subject: [PATCH] r8152: remove rtl_vendor_mode function
+
+After commit ec51fbd1b8a2 ("r8152: add USB device driver for
+config selection"), the code about changing USB configuration
+in rtl_vendor_mode() wouldn't be run anymore. Therefore, the
+function could be removed.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 39 +--------------------------------------
+ 1 file changed, 1 insertion(+), 38 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -8267,43 +8267,6 @@ static bool rtl_check_vendor_ok(struct u
+       return true;
+ }
+-static bool rtl_vendor_mode(struct usb_interface *intf)
+-{
+-      struct usb_host_interface *alt = intf->cur_altsetting;
+-      struct usb_device *udev;
+-      struct usb_host_config *c;
+-      int i, num_configs;
+-
+-      if (alt->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC)
+-              return rtl_check_vendor_ok(intf);
+-
+-      /* The vendor mode is not always config #1, so to find it out. */
+-      udev = interface_to_usbdev(intf);
+-      c = udev->config;
+-      num_configs = udev->descriptor.bNumConfigurations;
+-      if (num_configs < 2)
+-              return false;
+-
+-      for (i = 0; i < num_configs; (i++, c++)) {
+-              struct usb_interface_descriptor *desc = NULL;
+-
+-              if (c->desc.bNumInterfaces > 0)
+-                      desc = &c->intf_cache[0]->altsetting->desc;
+-              else
+-                      continue;
+-
+-              if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+-                      usb_driver_set_configuration(udev, c->desc.bConfigurationValue);
+-                      break;
+-              }
+-      }
+-
+-      if (i == num_configs)
+-              dev_err(&intf->dev, "Unexpected Device\n");
+-
+-      return false;
+-}
+-
+ static int rtl8152_pre_reset(struct usb_interface *intf)
+ {
+       struct r8152 *tp = usb_get_intfdata(intf);
+@@ -9636,7 +9599,7 @@ static int rtl8152_probe(struct usb_inte
+       if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+               return -ENODEV;
+-      if (!rtl_vendor_mode(intf))
++      if (!rtl_check_vendor_ok(intf))
+               return -ENODEV;
+       usb_reset_device(udev);
diff --git a/target/linux/generic/backport-5.15/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch b/target/linux/generic/backport-5.15/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch
new file mode 100644 (file)
index 0000000..7d1053a
--- /dev/null
@@ -0,0 +1,46 @@
+From 02767440e1dda9861a11ca1dbe0f19a760b1d5c2 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Thu, 19 Jan 2023 15:40:43 +0800
+Subject: [PATCH] r8152: reduce the control transfer of rtl8152_get_version()
+
+Reduce the control transfer by moving calling rtl8152_get_version() in
+rtl8152_probe(). This could prevent from calling rtl8152_get_version()
+for unnecessary situations. For example, after setting config #2 for the
+device, there are two interfaces and rtl8152_probe() may be called
+twice. However, we don't need to call rtl8152_get_version() for this
+situation.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9588,20 +9588,21 @@ static int rtl8152_probe(struct usb_inte
+                        const struct usb_device_id *id)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+-      u8 version = rtl8152_get_version(intf);
+       struct r8152 *tp;
+       struct net_device *netdev;
++      u8 version;
+       int ret;
+-      if (version == RTL_VER_UNKNOWN)
+-              return -ENODEV;
+-
+       if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+               return -ENODEV;
+       if (!rtl_check_vendor_ok(intf))
+               return -ENODEV;
++      version = rtl8152_get_version(intf);
++      if (version == RTL_VER_UNKNOWN)
++              return -ENODEV;
++
+       usb_reset_device(udev);
+       netdev = alloc_etherdev(sizeof(struct r8152));
+       if (!netdev) {
diff --git a/target/linux/generic/backport-5.15/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch b/target/linux/generic/backport-5.15/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch
new file mode 100644 (file)
index 0000000..5d1dfe3
--- /dev/null
@@ -0,0 +1,55 @@
+From 5cc33f139e11b893ff6dc60d8a0ae865a65521ac Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Thu, 6 Apr 2023 17:14:26 -0700
+Subject: [PATCH] r8152: Add __GFP_NOWARN to big allocations
+
+When memory is a little tight on my system, it's pretty easy to see
+warnings that look like this.
+
+  ksoftirqd/0: page allocation failure: order:3, mode:0x40a20(GFP_ATOMIC|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0
+  ...
+  Call trace:
+   dump_backtrace+0x0/0x1e8
+   show_stack+0x20/0x2c
+   dump_stack_lvl+0x60/0x78
+   dump_stack+0x18/0x38
+   warn_alloc+0x104/0x174
+   __alloc_pages+0x588/0x67c
+   alloc_rx_agg+0xa0/0x190 [r8152 ...]
+   r8152_poll+0x270/0x760 [r8152 ...]
+   __napi_poll+0x44/0x1ec
+   net_rx_action+0x100/0x300
+   __do_softirq+0xec/0x38c
+   run_ksoftirqd+0x38/0xec
+   smpboot_thread_fn+0xb8/0x248
+   kthread+0x134/0x154
+   ret_from_fork+0x10/0x20
+
+On a fragmented system it's normal that order 3 allocations will
+sometimes fail, especially atomic ones. The driver handles these
+failures fine and the WARN just creates spam in the logs for this
+case. The __GFP_NOWARN flag is exactly for this situation, so add it
+to the allocation.
+
+NOTE: my testing is on a 5.15 system, but there should be no reason
+that this would be fundamentally different on a mainline kernel.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Acked-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230406171411.1.I84dbef45786af440fd269b71e9436a96a8e7a152@changeid
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -1944,7 +1944,7 @@ static struct rx_agg *alloc_rx_agg(struc
+       if (!rx_agg)
+               return NULL;
+-      rx_agg->page = alloc_pages(mflags | __GFP_COMP, order);
++      rx_agg->page = alloc_pages(mflags | __GFP_COMP | __GFP_NOWARN, order);
+       if (!rx_agg->page)
+               goto free_rx;
diff --git a/target/linux/generic/backport-5.15/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch b/target/linux/generic/backport-5.15/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch
new file mode 100644 (file)
index 0000000..df881e2
--- /dev/null
@@ -0,0 +1,24 @@
+From 0fbd79c01a9a657348f7032df70c57a406468c86 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 2 May 2023 11:36:27 +0800
+Subject: [PATCH] r8152: fix the autosuspend doesn't work
+
+Set supports_autosuspend = 1 for the rtl8152_cfgselector_driver.
+
+Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9898,6 +9898,7 @@ static struct usb_device_driver rtl8152_
+       .probe =        rtl8152_cfgselector_probe,
+       .id_table =     rtl8152_table,
+       .generic_subclass = 1,
++      .supports_autosuspend = 1,
+ };
+ static int __init rtl8152_driver_init(void)
diff --git a/target/linux/generic/backport-5.15/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch b/target/linux/generic/backport-5.15/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch
new file mode 100644 (file)
index 0000000..0da1a5b
--- /dev/null
@@ -0,0 +1,70 @@
+From 57df0fb9d511f91202114813e90128d65c0589f0 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 26 Jul 2023 11:08:07 +0800
+Subject: [PATCH] r8152: adjust generic_ocp_write function
+
+Reduce the control transfer if all bytes of first or the last DWORD are
+written.
+
+The original method is to split the control transfer into three parts
+(the first DWORD, middle continuous data, and the last DWORD). However,
+they could be combined if whole bytes of the first DWORD or last DWORD
+are written. That is, the first DWORD or the last DWORD could be combined
+with the middle continuous data, if the byte_en is 0xff.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230726030808.9093-418-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 29 ++++++++++++++++++-----------
+ 1 file changed, 18 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -1310,16 +1310,24 @@ static int generic_ocp_write(struct r815
+       byteen_end = byteen & BYTE_EN_END_MASK;
+       byen = byteen_start | (byteen_start << 4);
+-      ret = set_registers(tp, index, type | byen, 4, data);
+-      if (ret < 0)
+-              goto error1;
+-
+-      index += 4;
+-      data += 4;
+-      size -= 4;
+-      if (size) {
++      /* Split the first DWORD if the byte_en is not 0xff */
++      if (byen != BYTE_EN_DWORD) {
++              ret = set_registers(tp, index, type | byen, 4, data);
++              if (ret < 0)
++                      goto error1;
++
++              index += 4;
++              data += 4;
+               size -= 4;
++      }
++
++      if (size) {
++              byen = byteen_end | (byteen_end >> 4);
++
++              /* Split the last DWORD if the byte_en is not 0xff */
++              if (byen != BYTE_EN_DWORD)
++                      size -= 4;
+               while (size) {
+                       if (size > limit) {
+@@ -1346,10 +1354,9 @@ static int generic_ocp_write(struct r815
+                       }
+               }
+-              byen = byteen_end | (byteen_end >> 4);
+-              ret = set_registers(tp, index, type | byen, 4, data);
+-              if (ret < 0)
+-                      goto error1;
++              /* Set the last DWORD */
++              if (byen != BYTE_EN_DWORD)
++                      ret = set_registers(tp, index, type | byen, 4, data);
+       }
+ error1:
diff --git a/target/linux/generic/backport-5.15/795-v6.6-09-r8152-set-bp-in-bulk.patch b/target/linux/generic/backport-5.15/795-v6.6-09-r8152-set-bp-in-bulk.patch
new file mode 100644 (file)
index 0000000..cfc31da
--- /dev/null
@@ -0,0 +1,129 @@
+From e5c266a61186b462c388c53a3564c375e72f2244 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 26 Jul 2023 11:08:08 +0800
+Subject: [PATCH] r8152: set bp in bulk
+
+PLA_BP_0 ~ PLA_BP_15 (0xfc28 ~ 0xfc46) are continuous registers, so we
+could combine the control transfers into one control transfer.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230726030808.9093-419-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 75 ++++++++++++++---------------------------
+ 1 file changed, 25 insertions(+), 50 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -3977,29 +3977,10 @@ static void rtl_reset_bmu(struct r8152 *
+ /* Clear the bp to stop the firmware before loading a new one */
+ static void rtl_clear_bp(struct r8152 *tp, u16 type)
+ {
+-      switch (tp->version) {
+-      case RTL_VER_01:
+-      case RTL_VER_02:
+-      case RTL_VER_07:
+-              break;
+-      case RTL_VER_03:
+-      case RTL_VER_04:
+-      case RTL_VER_05:
+-      case RTL_VER_06:
+-              ocp_write_byte(tp, type, PLA_BP_EN, 0);
+-              break;
+-      case RTL_VER_14:
+-              ocp_write_word(tp, type, USB_BP2_EN, 0);
++      u16 bp[16] = {0};
++      u16 bp_num;
+-              ocp_write_word(tp, type, USB_BP_8, 0);
+-              ocp_write_word(tp, type, USB_BP_9, 0);
+-              ocp_write_word(tp, type, USB_BP_10, 0);
+-              ocp_write_word(tp, type, USB_BP_11, 0);
+-              ocp_write_word(tp, type, USB_BP_12, 0);
+-              ocp_write_word(tp, type, USB_BP_13, 0);
+-              ocp_write_word(tp, type, USB_BP_14, 0);
+-              ocp_write_word(tp, type, USB_BP_15, 0);
+-              break;
++      switch (tp->version) {
+       case RTL_VER_08:
+       case RTL_VER_09:
+       case RTL_VER_10:
+@@ -4007,32 +3988,31 @@ static void rtl_clear_bp(struct r8152 *t
+       case RTL_VER_12:
+       case RTL_VER_13:
+       case RTL_VER_15:
+-      default:
+               if (type == MCU_TYPE_USB) {
+                       ocp_write_word(tp, MCU_TYPE_USB, USB_BP2_EN, 0);
+-
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_8, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_9, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_10, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_11, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_12, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_13, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_14, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_15, 0);
+-              } else {
+-                      ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
++                      bp_num = 16;
++                      break;
+               }
++              fallthrough;
++      case RTL_VER_03:
++      case RTL_VER_04:
++      case RTL_VER_05:
++      case RTL_VER_06:
++              ocp_write_byte(tp, type, PLA_BP_EN, 0);
++              fallthrough;
++      case RTL_VER_01:
++      case RTL_VER_02:
++      case RTL_VER_07:
++              bp_num = 8;
++              break;
++      case RTL_VER_14:
++      default:
++              ocp_write_word(tp, type, USB_BP2_EN, 0);
++              bp_num = 16;
+               break;
+       }
+-      ocp_write_word(tp, type, PLA_BP_0, 0);
+-      ocp_write_word(tp, type, PLA_BP_1, 0);
+-      ocp_write_word(tp, type, PLA_BP_2, 0);
+-      ocp_write_word(tp, type, PLA_BP_3, 0);
+-      ocp_write_word(tp, type, PLA_BP_4, 0);
+-      ocp_write_word(tp, type, PLA_BP_5, 0);
+-      ocp_write_word(tp, type, PLA_BP_6, 0);
+-      ocp_write_word(tp, type, PLA_BP_7, 0);
++      generic_ocp_write(tp, PLA_BP_0, BYTE_EN_DWORD, bp_num << 1, bp, type);
+       /* wait 3 ms to make sure the firmware is stopped */
+       usleep_range(3000, 6000);
+@@ -5009,10 +4989,9 @@ static void rtl8152_fw_phy_nc_apply(stru
+ static void rtl8152_fw_mac_apply(struct r8152 *tp, struct fw_mac *mac)
+ {
+-      u16 bp_en_addr, bp_index, type, bp_num, fw_ver_reg;
++      u16 bp_en_addr, type, fw_ver_reg;
+       u32 length;
+       u8 *data;
+-      int i;
+       switch (__le32_to_cpu(mac->blk_hdr.type)) {
+       case RTL_FW_PLA:
+@@ -5054,12 +5033,8 @@ static void rtl8152_fw_mac_apply(struct
+       ocp_write_word(tp, type, __le16_to_cpu(mac->bp_ba_addr),
+                      __le16_to_cpu(mac->bp_ba_value));
+-      bp_index = __le16_to_cpu(mac->bp_start);
+-      bp_num = __le16_to_cpu(mac->bp_num);
+-      for (i = 0; i < bp_num; i++) {
+-              ocp_write_word(tp, type, bp_index, __le16_to_cpu(mac->bp[i]));
+-              bp_index += 2;
+-      }
++      generic_ocp_write(tp, __le16_to_cpu(mac->bp_start), BYTE_EN_DWORD,
++                        __le16_to_cpu(mac->bp_num) << 1, mac->bp, type);
+       bp_en_addr = __le16_to_cpu(mac->bp_en_addr);
+       if (bp_en_addr)
diff --git a/target/linux/generic/backport-5.15/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch b/target/linux/generic/backport-5.15/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch
new file mode 100644 (file)
index 0000000..4d1b177
--- /dev/null
@@ -0,0 +1,39 @@
+From 72f93a3136ee18fd59fa6579f84c07e93424681e Mon Sep 17 00:00:00 2001
+From: Antonio Napolitano <anton@polit.no>
+Date: Sat, 26 Aug 2023 01:05:50 +0200
+Subject: [PATCH] r8152: add vendor/device ID pair for D-Link DUB-E250
+
+The D-Link DUB-E250 is an RTL8156 based 2.5G Ethernet controller.
+
+Add the vendor and product ID values to the driver. This makes Ethernet
+work with the adapter.
+
+Signed-off-by: Antonio Napolitano <anton@polit.no>
+Link: https://lore.kernel.org/r/CV200KJEEUPC.WPKAHXCQJ05I@mercurius
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c   | 1 +
+ include/linux/usb/r8152.h | 1 +
+ 2 files changed, 2 insertions(+)
+
+
+--- a/include/linux/usb/r8152.h
++++ b/include/linux/usb/r8152.h
+@@ -29,6 +29,7 @@
+ #define VENDOR_ID_LINKSYS             0x13b1
+ #define VENDOR_ID_NVIDIA              0x0955
+ #define VENDOR_ID_TPLINK              0x2357
++#define VENDOR_ID_DLINK                       0x2001
+ #if IS_REACHABLE(CONFIG_USB_RTL8152)
+ extern u8 rtl8152_get_version(struct usb_interface *intf);
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9820,6 +9820,7 @@ static const struct usb_device_id rtl815
+       { USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041) },
+       { USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff) },
+       { USB_DEVICE(VENDOR_ID_TPLINK,  0x0601) },
++      { USB_DEVICE(VENDOR_ID_DLINK,   0xb301) },
+       {}
+ };
diff --git a/target/linux/generic/backport-5.15/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch b/target/linux/generic/backport-5.15/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch
new file mode 100644 (file)
index 0000000..4f0e0e1
--- /dev/null
@@ -0,0 +1,447 @@
+From 715f67f33af45ce2cc3a5b1ef133cc8c8e7787b0 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Fri, 20 Oct 2023 14:06:58 -0700
+Subject: [PATCH] r8152: Rename RTL8152_UNPLUG to RTL8152_INACCESSIBLE
+
+Whenever the RTL8152_UNPLUG is set that just tells the driver that all
+accesses will fail and we should just immediately bail. A future patch
+will use this same concept at a time when the driver hasn't actually
+been unplugged but is about to be reset. Rename the flag in
+preparation for the future patch.
+
+This is a no-op change and just a search and replace.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Grant Grundler <grundler@chromium.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 96 ++++++++++++++++++++---------------------
+ 1 file changed, 48 insertions(+), 48 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -763,7 +763,7 @@ enum rtl_register_content {
+ /* rtl8152 flags */
+ enum rtl8152_flags {
+-      RTL8152_UNPLUG = 0,
++      RTL8152_INACCESSIBLE = 0,
+       RTL8152_SET_RX_MODE,
+       WORK_ENABLE,
+       RTL8152_LINK_CHG,
+@@ -1241,7 +1241,7 @@ int set_registers(struct r8152 *tp, u16
+ static void rtl_set_unplug(struct r8152 *tp)
+ {
+       if (tp->udev->state == USB_STATE_NOTATTACHED) {
+-              set_bit(RTL8152_UNPLUG, &tp->flags);
++              set_bit(RTL8152_INACCESSIBLE, &tp->flags);
+               smp_mb__after_atomic();
+       }
+ }
+@@ -1252,7 +1252,7 @@ static int generic_ocp_read(struct r8152
+       u16 limit = 64;
+       int ret = 0;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       /* both size and indix must be 4 bytes align */
+@@ -1296,7 +1296,7 @@ static int generic_ocp_write(struct r815
+       u16 byteen_start, byteen_end, byen;
+       u16 limit = 512;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       /* both size and indix must be 4 bytes align */
+@@ -1533,7 +1533,7 @@ static int read_mii_word(struct net_devi
+       struct r8152 *tp = netdev_priv(netdev);
+       int ret;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       if (phy_id != R8152_PHY_ID)
+@@ -1549,7 +1549,7 @@ void write_mii_word(struct net_device *n
+ {
+       struct r8152 *tp = netdev_priv(netdev);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (phy_id != R8152_PHY_ID)
+@@ -1754,7 +1754,7 @@ static void read_bulk_callback(struct ur
+       if (!tp)
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+@@ -1846,7 +1846,7 @@ static void write_bulk_callback(struct u
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!skb_queue_empty(&tp->tx_queue))
+@@ -1867,7 +1867,7 @@ static void intr_callback(struct urb *ur
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       switch (status) {
+@@ -2611,7 +2611,7 @@ static void bottom_half(struct tasklet_s
+ {
+       struct r8152 *tp = from_tasklet(tp, t, tx_tl);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+@@ -2654,7 +2654,7 @@ int r8152_submit_rx(struct r8152 *tp, st
+       int ret;
+       /* The rx would be stopped, so skip submitting */
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags) ||
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags) ||
+           !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev))
+               return 0;
+@@ -3050,7 +3050,7 @@ static int rtl_enable(struct r8152 *tp)
+ static int rtl8152_enable(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -3137,7 +3137,7 @@ static int rtl8153_enable(struct r8152 *
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -3169,7 +3169,7 @@ static void rtl_disable(struct r8152 *tp
+       u32 ocp_data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -3623,7 +3623,7 @@ static u16 r8153_phy_status(struct r8152
+               }
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -3655,7 +3655,7 @@ static void r8153b_ups_en(struct r8152 *
+                       int i;
+                       for (i = 0; i < 500; i++) {
+-                              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++                              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                                       return;
+                               if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+                                   AUTOLOAD_DONE)
+@@ -3697,7 +3697,7 @@ static void r8153c_ups_en(struct r8152 *
+                       int i;
+                       for (i = 0; i < 500; i++) {
+-                              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++                              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                                       return;
+                               if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+                                   AUTOLOAD_DONE)
+@@ -4042,8 +4042,8 @@ static int rtl_phy_patch_request(struct
+       for (i = 0; wait && i < 5000; i++) {
+               u32 ocp_data;
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
+-                      break;
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
++                      return -ENODEV;
+               usleep_range(1000, 2000);
+               ocp_data = ocp_reg_read(tp, OCP_PHY_PATCH_STAT);
+@@ -6001,7 +6001,7 @@ static int rtl8156_enable(struct r8152 *
+       u32 ocp_data;
+       u16 speed;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       r8156_fc_parameter(tp);
+@@ -6059,7 +6059,7 @@ static int rtl8156b_enable(struct r8152
+       u32 ocp_data;
+       u16 speed;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -6245,7 +6245,7 @@ out:
+ static void rtl8152_up(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8152_aldps_en(tp, false);
+@@ -6255,7 +6255,7 @@ static void rtl8152_up(struct r8152 *tp)
+ static void rtl8152_down(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6270,7 +6270,7 @@ static void rtl8153_up(struct r8152 *tp)
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_u1u2en(tp, false);
+@@ -6310,7 +6310,7 @@ static void rtl8153_down(struct r8152 *t
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6331,7 +6331,7 @@ static void rtl8153b_up(struct r8152 *tp
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6355,7 +6355,7 @@ static void rtl8153b_down(struct r8152 *
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6392,7 +6392,7 @@ static void rtl8153c_up(struct r8152 *tp
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6473,7 +6473,7 @@ static void rtl8156_up(struct r8152 *tp)
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6546,7 +6546,7 @@ static void rtl8156_down(struct r8152 *t
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6684,7 +6684,7 @@ static void rtl_work_func_t(struct work_
+       /* If the device is unplugged or !netif_running(), the workqueue
+        * doesn't need to wake the device, and could return directly.
+        */
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags) || !netif_running(tp->netdev))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags) || !netif_running(tp->netdev))
+               return;
+       if (usb_autopm_get_interface(tp->intf) < 0)
+@@ -6723,7 +6723,7 @@ static void rtl_hw_phy_work_func_t(struc
+ {
+       struct r8152 *tp = container_of(work, struct r8152, hw_phy_work.work);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (usb_autopm_get_interface(tp->intf) < 0)
+@@ -6850,7 +6850,7 @@ static int rtl8152_close(struct net_devi
+       netif_stop_queue(netdev);
+       res = usb_autopm_get_interface(tp->intf);
+-      if (res < 0 || test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (res < 0 || test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               rtl_stop_rx(tp);
+       } else {
+@@ -6883,7 +6883,7 @@ static void r8152b_init(struct r8152 *tp
+       u32 ocp_data;
+       u16 data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       data = r8152_mdio_read(tp, MII_BMCR);
+@@ -6927,7 +6927,7 @@ static void r8153_init(struct r8152 *tp)
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_u1u2en(tp, false);
+@@ -6938,7 +6938,7 @@ static void r8153_init(struct r8152 *tp)
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -7067,7 +7067,7 @@ static void r8153b_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -7078,7 +7078,7 @@ static void r8153b_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -7149,7 +7149,7 @@ static void r8153c_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -7169,7 +7169,7 @@ static void r8153c_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -7998,7 +7998,7 @@ static void r8156_init(struct r8152 *tp)
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+@@ -8019,7 +8019,7 @@ static void r8156_init(struct r8152 *tp)
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -8094,7 +8094,7 @@ static void r8156b_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+@@ -8128,7 +8128,7 @@ static void r8156b_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -9153,7 +9153,7 @@ static int rtl8152_ioctl(struct net_devi
+       struct mii_ioctl_data *data = if_mii(rq);
+       int res;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       res = usb_autopm_get_interface(tp->intf);
+@@ -9255,7 +9255,7 @@ static const struct net_device_ops rtl81
+ static void rtl8152_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (tp->version != RTL_VER_01)
+@@ -9264,7 +9264,7 @@ static void rtl8152_unload(struct r8152
+ static void rtl8153_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_power_cut_en(tp, false);
+@@ -9272,7 +9272,7 @@ static void rtl8153_unload(struct r8152
+ static void rtl8153b_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_power_cut_en(tp, false);
diff --git a/target/linux/generic/backport-5.15/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch b/target/linux/generic/backport-5.15/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch
new file mode 100644 (file)
index 0000000..0ce8206
--- /dev/null
@@ -0,0 +1,398 @@
+From d9962b0d42029bcb40fe3c38bce06d1870fa4df4 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Fri, 20 Oct 2023 14:06:59 -0700
+Subject: [PATCH] r8152: Block future register access if register access fails
+
+Even though the functions to read/write registers can fail, most of
+the places in the r8152 driver that read/write register values don't
+check error codes. The lack of error code checking is problematic in
+at least two ways.
+
+The first problem is that the r8152 driver often uses code patterns
+similar to this:
+  x = read_register()
+  x = x | SOME_BIT;
+  write_register(x);
+
+...with the above pattern, if the read_register() fails and returns
+garbage then we'll end up trying to write modified garbage back to the
+Realtek adapter. If the write_register() succeeds that's bad. Note
+that as of commit f53a7ad18959 ("r8152: Set memory to all 0xFFs on
+failed reg reads") the "garbage" returned by read_register() will at
+least be consistent garbage, but it is still garbage.
+
+It turns out that this problem is very serious. Writing garbage to
+some of the hardware registers on the Ethernet adapter can put the
+adapter in such a bad state that it needs to be power cycled (fully
+unplugged and plugged in again) before it can enumerate again.
+
+The second problem is that the r8152 driver generally has functions
+that are long sequences of register writes. Assuming everything will
+be OK if a random register write fails in the middle isn't a great
+assumption.
+
+One might wonder if the above two problems are real. You could ask if
+we would really have a successful write after a failed read. It turns
+out that the answer appears to be "yes, this can happen". In fact,
+we've seen at least two distinct failure modes where this happens.
+
+On a sc7180-trogdor Chromebook if you drop into kdb for a while and
+then resume, you can see:
+1. We get a "Tx timeout"
+2. The "Tx timeout" queues up a USB reset.
+3. In rtl8152_pre_reset() we try to reinit the hardware.
+4. The first several (2-9) register accesses fail with a timeout, then
+   things recover.
+
+The above test case was actually fixed by the patch ("r8152: Increase
+USB control msg timeout to 5000ms as per spec") but at least shows
+that we really can see successful calls after failed ones.
+
+On a different (AMD) based Chromebook with a particular adapter, we
+found that during reboot tests we'd also sometimes get a transitory
+failure. In this case we saw -EPIPE being returned sometimes. Retrying
+worked, but retrying is not always safe for all register accesses
+since reading/writing some registers might have side effects (like
+registers that clear on read).
+
+Let's fully lock out all register access if a register access fails.
+When we do this, we'll try to queue up a USB reset and try to unlock
+register access after the reset. This is slightly tricker than it
+sounds since the r8152 driver has an optimized reset sequence that
+only works reliably after probe happens. In order to handle this, we
+avoid the optimized reset if probe didn't finish. Instead, we simply
+retry the probe routine in this case.
+
+When locking out access, we'll use the existing infrastructure that
+the driver was using when it detected we were unplugged. This keeps us
+from getting stuck in delay loops in some parts of the driver.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Grant Grundler <grundler@chromium.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 207 ++++++++++++++++++++++++++++++++++------
+ 1 file changed, 176 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -772,6 +772,9 @@ enum rtl8152_flags {
+       SCHEDULE_TASKLET,
+       GREEN_ETHERNET,
+       RX_EPROTO,
++      IN_PRE_RESET,
++      PROBED_WITH_NO_ERRORS,
++      PROBE_SHOULD_RETRY,
+ };
+ #define DEVICE_ID_THINKPAD_ONELINK_PLUS_DOCK          0x3054
+@@ -949,6 +952,8 @@ struct r8152 {
+       u8 version;
+       u8 duplex;
+       u8 autoneg;
++
++      unsigned int reg_access_reset_count;
+ };
+ /**
+@@ -1196,6 +1201,96 @@ static unsigned int agg_buf_sz = 16384;
+ #define RTL_LIMITED_TSO_SIZE  (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
++/* If register access fails then we block access and issue a reset. If this
++ * happens too many times in a row without a successful access then we stop
++ * trying to reset and just leave access blocked.
++ */
++#define REGISTER_ACCESS_MAX_RESETS    3
++
++static void rtl_set_inaccessible(struct r8152 *tp)
++{
++      set_bit(RTL8152_INACCESSIBLE, &tp->flags);
++      smp_mb__after_atomic();
++}
++
++static void rtl_set_accessible(struct r8152 *tp)
++{
++      clear_bit(RTL8152_INACCESSIBLE, &tp->flags);
++      smp_mb__after_atomic();
++}
++
++static
++int r8152_control_msg(struct r8152 *tp, unsigned int pipe, __u8 request,
++                    __u8 requesttype, __u16 value, __u16 index, void *data,
++                    __u16 size, const char *msg_tag)
++{
++      struct usb_device *udev = tp->udev;
++      int ret;
++
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
++              return -ENODEV;
++
++      ret = usb_control_msg(udev, pipe, request, requesttype,
++                            value, index, data, size,
++                            USB_CTRL_GET_TIMEOUT);
++
++      /* No need to issue a reset to report an error if the USB device got
++       * unplugged; just return immediately.
++       */
++      if (ret == -ENODEV)
++              return ret;
++
++      /* If the write was successful then we're done */
++      if (ret >= 0) {
++              tp->reg_access_reset_count = 0;
++              return ret;
++      }
++
++      dev_err(&udev->dev,
++              "Failed to %s %d bytes at %#06x/%#06x (%d)\n",
++              msg_tag, size, value, index, ret);
++
++      /* Block all future register access until we reset. Much of the code
++       * in the driver doesn't check for errors. Notably, many parts of the
++       * driver do a read/modify/write of a register value without
++       * confirming that the read succeeded. Writing back modified garbage
++       * like this can fully wedge the adapter, requiring a power cycle.
++       */
++      rtl_set_inaccessible(tp);
++
++      /* If probe hasn't yet finished, then we'll request a retry of the
++       * whole probe routine if we get any control transfer errors. We
++       * never have to clear this bit since we free/reallocate the whole "tp"
++       * structure if we retry probe.
++       */
++      if (!test_bit(PROBED_WITH_NO_ERRORS, &tp->flags)) {
++              set_bit(PROBE_SHOULD_RETRY, &tp->flags);
++              return ret;
++      }
++
++      /* Failing to access registers in pre-reset is not surprising since we
++       * wouldn't be resetting if things were behaving normally. The register
++       * access we do in pre-reset isn't truly mandatory--we're just reusing
++       * the disable() function and trying to be nice by powering the
++       * adapter down before resetting it. Thus, if we're in pre-reset,
++       * we'll return right away and not try to queue up yet another reset.
++       * We know the post-reset is already coming.
++       */
++      if (test_bit(IN_PRE_RESET, &tp->flags))
++              return ret;
++
++      if (tp->reg_access_reset_count < REGISTER_ACCESS_MAX_RESETS) {
++              usb_queue_reset_device(tp->intf);
++              tp->reg_access_reset_count++;
++      } else if (tp->reg_access_reset_count == REGISTER_ACCESS_MAX_RESETS) {
++              dev_err(&udev->dev,
++                      "Tried to reset %d times; giving up.\n",
++                      REGISTER_ACCESS_MAX_RESETS);
++      }
++
++      return ret;
++}
++
+ static
+ int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
+ {
+@@ -1206,9 +1301,10 @@ int get_registers(struct r8152 *tp, u16
+       if (!tmp)
+               return -ENOMEM;
+-      ret = usb_control_msg(tp->udev, tp->pipe_ctrl_in,
+-                            RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+-                            value, index, tmp, size, USB_CTRL_GET_TIMEOUT);
++      ret = r8152_control_msg(tp, tp->pipe_ctrl_in,
++                              RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
++                              value, index, tmp, size, "read");
++
+       if (ret < 0)
+               memset(data, 0xff, size);
+       else
+@@ -1229,9 +1325,9 @@ int set_registers(struct r8152 *tp, u16
+       if (!tmp)
+               return -ENOMEM;
+-      ret = usb_control_msg(tp->udev, tp->pipe_ctrl_out,
+-                            RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
+-                            value, index, tmp, size, USB_CTRL_SET_TIMEOUT);
++      ret = r8152_control_msg(tp, tp->pipe_ctrl_out,
++                              RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
++                              value, index, tmp, size, "write");
+       kfree(tmp);
+@@ -1240,10 +1336,8 @@ int set_registers(struct r8152 *tp, u16
+ static void rtl_set_unplug(struct r8152 *tp)
+ {
+-      if (tp->udev->state == USB_STATE_NOTATTACHED) {
+-              set_bit(RTL8152_INACCESSIBLE, &tp->flags);
+-              smp_mb__after_atomic();
+-      }
++      if (tp->udev->state == USB_STATE_NOTATTACHED)
++              rtl_set_inaccessible(tp);
+ }
+ static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
+@@ -8254,7 +8348,7 @@ static int rtl8152_pre_reset(struct usb_
+       struct r8152 *tp = usb_get_intfdata(intf);
+       struct net_device *netdev;
+-      if (!tp)
++      if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
+               return 0;
+       netdev = tp->netdev;
+@@ -8269,7 +8363,9 @@ static int rtl8152_pre_reset(struct usb_
+       napi_disable(&tp->napi);
+       if (netif_carrier_ok(netdev)) {
+               mutex_lock(&tp->control);
++              set_bit(IN_PRE_RESET, &tp->flags);
+               tp->rtl_ops.disable(tp);
++              clear_bit(IN_PRE_RESET, &tp->flags);
+               mutex_unlock(&tp->control);
+       }
+@@ -8282,9 +8378,11 @@ static int rtl8152_post_reset(struct usb
+       struct net_device *netdev;
+       struct sockaddr sa;
+-      if (!tp)
++      if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
+               return 0;
++      rtl_set_accessible(tp);
++
+       /* reset the MAC address in case of policy change */
+       if (determine_ethernet_addr(tp, &sa) >= 0) {
+               rtnl_lock();
+@@ -9482,17 +9580,29 @@ static u8 __rtl_get_hw_ver(struct usb_de
+       __le32 *tmp;
+       u8 version;
+       int ret;
++      int i;
+       tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return 0;
+-      ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+-                            RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+-                            PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
+-                            USB_CTRL_GET_TIMEOUT);
+-      if (ret > 0)
+-              ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
++      /* Retry up to 3 times in case there is a transitory error. We do this
++       * since retrying a read of the version is always safe and this
++       * function doesn't take advantage of r8152_control_msg().
++       */
++      for (i = 0; i < 3; i++) {
++              ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
++                                    RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
++                                    PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
++                                    USB_CTRL_GET_TIMEOUT);
++              if (ret > 0) {
++                      ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
++                      break;
++              }
++      }
++
++      if (i != 0 && ret > 0)
++              dev_warn(&udev->dev, "Needed %d retries to read version\n", i);
+       kfree(tmp);
+@@ -9566,25 +9676,14 @@ u8 rtl8152_get_version(struct usb_interf
+ }
+ EXPORT_SYMBOL_GPL(rtl8152_get_version);
+-static int rtl8152_probe(struct usb_interface *intf,
+-                       const struct usb_device_id *id)
++static int rtl8152_probe_once(struct usb_interface *intf,
++                            const struct usb_device_id *id, u8 version)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct r8152 *tp;
+       struct net_device *netdev;
+-      u8 version;
+       int ret;
+-      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+-              return -ENODEV;
+-
+-      if (!rtl_check_vendor_ok(intf))
+-              return -ENODEV;
+-
+-      version = rtl8152_get_version(intf);
+-      if (version == RTL_VER_UNKNOWN)
+-              return -ENODEV;
+-
+       usb_reset_device(udev);
+       netdev = alloc_etherdev(sizeof(struct r8152));
+       if (!netdev) {
+@@ -9757,10 +9856,20 @@ static int rtl8152_probe(struct usb_inte
+       else
+               device_set_wakeup_enable(&udev->dev, false);
++      /* If we saw a control transfer error while probing then we may
++       * want to try probe() again. Consider this an error.
++       */
++      if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
++              goto out2;
++
++      set_bit(PROBED_WITH_NO_ERRORS, &tp->flags);
+       netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION);
+       return 0;
++out2:
++      unregister_netdev(netdev);
++
+ out1:
+       tasklet_kill(&tp->tx_tl);
+       cancel_delayed_work_sync(&tp->hw_phy_work);
+@@ -9769,10 +9878,46 @@ out1:
+       rtl8152_release_firmware(tp);
+       usb_set_intfdata(intf, NULL);
+ out:
++      if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
++              ret = -EAGAIN;
++
+       free_netdev(netdev);
+       return ret;
+ }
++#define RTL8152_PROBE_TRIES   3
++
++static int rtl8152_probe(struct usb_interface *intf,
++                       const struct usb_device_id *id)
++{
++      u8 version;
++      int ret;
++      int i;
++
++      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
++              return -ENODEV;
++
++      if (!rtl_check_vendor_ok(intf))
++              return -ENODEV;
++
++      version = rtl8152_get_version(intf);
++      if (version == RTL_VER_UNKNOWN)
++              return -ENODEV;
++
++      for (i = 0; i < RTL8152_PROBE_TRIES; i++) {
++              ret = rtl8152_probe_once(intf, id, version);
++              if (ret != -EAGAIN)
++                      break;
++      }
++      if (ret == -EAGAIN) {
++              dev_err(&intf->dev,
++                      "r8152 failed probe after %d tries; giving up\n", i);
++              return -ENODEV;
++      }
++
++      return ret;
++}
++
+ static void rtl8152_disconnect(struct usb_interface *intf)
+ {
+       struct r8152 *tp = usb_get_intfdata(intf);
diff --git a/target/linux/generic/backport-5.15/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch b/target/linux/generic/backport-5.15/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch
new file mode 100644 (file)
index 0000000..42ca9e2
--- /dev/null
@@ -0,0 +1,83 @@
+From 66eee612a1ba39f9a76a9ace4a34d012044767fb Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 26 Sep 2023 19:17:13 +0800
+Subject: [PATCH] r8152: break the loop when the budget is exhausted
+
+[ Upstream commit 2cf51f931797d9a47e75d999d0993a68cbd2a560 ]
+
+A bulk transfer of the USB may contain many packets. And, the total
+number of the packets in the bulk transfer may be more than budget.
+
+Originally, only budget packets would be handled by napi_gro_receive(),
+and the other packets would be queued in the driver for next schedule.
+
+This patch would break the loop about getting next bulk transfer, when
+the budget is exhausted. That is, only the current bulk transfer would
+be handled, and the other bulk transfers would be queued for next
+schedule. Besides, the packets which are more than the budget in the
+current bulk trasnfer would be still queued in the driver, as the
+original method.
+
+In addition, a bulk transfer wouldn't contain more than 400 packets, so
+the check of queue length is unnecessary. Therefore, I replace it with
+WARN_ON_ONCE().
+
+Fixes: cf74eb5a5bc8 ("eth: r8152: try to use a normal budget")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230926111714.9448-433-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/usb/r8152.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2539,7 +2539,7 @@ static int rx_bottom(struct r8152 *tp, i
+               }
+       }
+-      if (list_empty(&tp->rx_done))
++      if (list_empty(&tp->rx_done) || work_done >= budget)
+               goto out1;
+       clear_bit(RX_EPROTO, &tp->flags);
+@@ -2555,6 +2555,15 @@ static int rx_bottom(struct r8152 *tp, i
+               struct urb *urb;
+               u8 *rx_data;
++              /* A bulk transfer of USB may contain may packets, so the
++               * total packets may more than the budget. Deal with all
++               * packets in current bulk transfer, and stop to handle the
++               * next bulk transfer until next schedule, if budget is
++               * exhausted.
++               */
++              if (work_done >= budget)
++                      break;
++
+               list_del_init(cursor);
+               agg = list_entry(cursor, struct rx_agg, list);
+@@ -2574,9 +2583,7 @@ static int rx_bottom(struct r8152 *tp, i
+                       unsigned int pkt_len, rx_frag_head_sz;
+                       struct sk_buff *skb;
+-                      /* limit the skb numbers for rx_queue */
+-                      if (unlikely(skb_queue_len(&tp->rx_queue) >= 1000))
+-                              break;
++                      WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
+                       pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
+                       if (pkt_len < ETH_ZLEN)
+@@ -2654,9 +2661,10 @@ submit:
+               }
+       }
++      /* Splice the remained list back to rx_done for next schedule */
+       if (!list_empty(&rx_queue)) {
+               spin_lock_irqsave(&tp->rx_lock, flags);
+-              list_splice_tail(&rx_queue, &tp->rx_done);
++              list_splice(&rx_queue, &tp->rx_done);
+               spin_unlock_irqrestore(&tp->rx_lock, flags);
+       }
diff --git a/target/linux/generic/backport-5.15/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch b/target/linux/generic/backport-5.15/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch
new file mode 100644 (file)
index 0000000..d578d01
--- /dev/null
@@ -0,0 +1,47 @@
+From 1b0fce8c8e69485e49a7d34aac3d4c2a2aa15d62 Mon Sep 17 00:00:00 2001
+From: Davide Tronchin <davide.tronchin.94@gmail.com>
+Date: Thu, 29 Jun 2023 12:37:36 +0200
+Subject: [PATCH] net: usb: cdc_ether: add u-blox 0x1313 composition.
+
+Add CDC-ECM support for LARA-R6 01B.
+
+The new LARA-R6 product variant identified by the "01B" string can be
+configured (by AT interface) in three different USB modes:
+* Default mode (Vendor ID: 0x1546 Product ID: 0x1311) with 4 serial
+interfaces
+* RmNet mode (Vendor ID: 0x1546 Product ID: 0x1312) with 4 serial
+interfaces and 1 RmNet virtual network interface
+* CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1313) with 4 serial
+interface and 1 CDC-ECM virtual network interface
+The first 4 interfaces of all the 3 configurations (default, RmNet, ECM)
+are the same.
+
+In CDC-ECM mode LARA-R6 01B exposes the following interfaces:
+If 0: Diagnostic
+If 1: AT parser
+If 2: AT parser
+If 3: AT parset/alternative functions
+If 4: CDC-ECM interface
+
+Signed-off-by: Davide Tronchin <davide.tronchin.94@gmail.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/cdc_ether.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/usb/cdc_ether.c
++++ b/drivers/net/usb/cdc_ether.c
+@@ -878,6 +878,12 @@ static const struct usb_device_id produc
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = (unsigned long)&wwan_info,
+ }, {
++      /* U-blox LARA-R6 01B */
++      USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1313, USB_CLASS_COMM,
++                                    USB_CDC_SUBCLASS_ETHERNET,
++                                    USB_CDC_PROTO_NONE),
++      .driver_info = (unsigned long)&wwan_info,
++}, {
+       /* ZTE modules */
+       USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
diff --git a/target/linux/generic/backport-6.1/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch b/target/linux/generic/backport-6.1/795-v6.3-01-r8152-add-USB-device-driver-for-config-selection.patch
new file mode 100644 (file)
index 0000000..605faee
--- /dev/null
@@ -0,0 +1,229 @@
+From ec51fbd1b8a2bca2948dede99c14ec63dc57ff6b Mon Sep 17 00:00:00 2001
+From: Bjørn Mork <bjorn@mork.no>
+Date: Fri, 6 Jan 2023 17:07:38 +0100
+Subject: [PATCH] r8152: add USB device driver for config selection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Subclassing the generic USB device driver to override the
+default configuration selection regardless of matching interface
+drivers.
+
+The r815x family devices expose a vendor specific function which
+the r8152 interface driver wants to handle.  This is the preferred
+device mode. Additionally one or more USB class functions are
+usually supported for hosts lacking a vendor specific driver. The
+choice is USB configuration based, with one alternate function per
+configuration.
+
+Example device with both NCM and ECM alternate cfgs:
+
+T:  Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  4 Spd=5000 MxCh= 0
+D:  Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs=  3
+P:  Vendor=0bda ProdID=8156 Rev=31.00
+S:  Manufacturer=Realtek
+S:  Product=USB 10/100/1G/2.5G LAN
+S:  SerialNumber=001000001
+C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=256mA
+I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=00 Driver=r8152
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=83(I) Atr=03(Int.) MxPS=   2 Ivl=128ms
+C:  #Ifs= 2 Cfg#= 2 Atr=a0 MxPwr=256mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=
+E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=
+I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+C:  #Ifs= 2 Cfg#= 3 Atr=a0 MxPwr=256mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=06 Prot=00 Driver=
+E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=128ms
+I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=00 Driver=
+I:  If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=
+E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
+
+A problem with this is that Linux will prefer class functions over
+vendor specific functions. Using the above example, Linux defaults
+to cfg #2, running the device in a sub-optimal NCM mode.
+
+Previously we've attempted to work around the problem by
+blacklisting the devices in the ECM class driver "cdc_ether", and
+matching on the ECM class function in the vendor specific interface
+driver. The latter has been used to switch back to the vendor
+specific configuration when the driver is probed for a class
+function.
+
+This workaround has several issues;
+- class driver blacklists is additional maintanence cruft in an
+  unrelated driver
+- class driver blacklists prevents users from optionally running
+  the devices in class mode
+- each device needs double match entries in the vendor driver
+- the initial probing as a class function slows down device
+  discovery
+
+Now these issues have become even worse with the introduction of
+firmware supporting both NCM and ECM, where NCM ends up as the
+default mode in Linux. To use the same workaround, we now have
+to blacklist the devices in to two different class drivers and
+add yet another match entry to the vendor specific driver.
+
+This patch implements an alternative workaround strategy -
+independent of the interface drivers.  It avoids adding a
+blacklist to the cdc_ncm driver and will let us remove the
+existing blacklist from the cdc_ether driver.
+
+As an additional bonus, removing the blacklists allow users to
+select one of the other device modes if wanted.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 113 ++++++++++++++++++++++++++++------------
+ 1 file changed, 81 insertions(+), 32 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9661,6 +9661,9 @@ static int rtl8152_probe(struct usb_inte
+       if (version == RTL_VER_UNKNOWN)
+               return -ENODEV;
++      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
++              return -ENODEV;
++
+       if (!rtl_vendor_mode(intf))
+               return -ENODEV;
+@@ -9861,43 +9864,35 @@ static void rtl8152_disconnect(struct us
+       }
+ }
+-#define REALTEK_USB_DEVICE(vend, prod)        { \
+-      USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC), \
+-}, \
+-{ \
+-      USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_COMM, \
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), \
+-}
+ /* table of devices that work with this driver */
+ static const struct usb_device_id rtl8152_table[] = {
+       /* Realtek */
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8053),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8155),
+-      REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8156),
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8050) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8053) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8152) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8153) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) },
++      { USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) },
+       /* Microsoft */
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927),
+-      REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0c5e),
+-      REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x304f),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3054),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3062),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3069),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x3082),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7205),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x720c),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7214),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x721e),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0xa387),
+-      REALTEK_USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041),
+-      REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff),
+-      REALTEK_USB_DEVICE(VENDOR_ID_TPLINK,  0x0601),
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) },
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6) },
++      { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927) },
++      { USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x304f) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3054) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3062) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3069) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x3082) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x7205) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x720c) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x7214) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0x721e) },
++      { USB_DEVICE(VENDOR_ID_LENOVO,  0xa387) },
++      { USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041) },
++      { USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff) },
++      { USB_DEVICE(VENDOR_ID_TPLINK,  0x0601) },
+       {}
+ };
+@@ -9917,7 +9912,61 @@ static struct usb_driver rtl8152_driver
+       .disable_hub_initiated_lpm = 1,
+ };
+-module_usb_driver(rtl8152_driver);
++static int rtl8152_cfgselector_probe(struct usb_device *udev)
++{
++      struct usb_host_config *c;
++      int i, num_configs;
++
++      /* The vendor mode is not always config #1, so to find it out. */
++      c = udev->config;
++      num_configs = udev->descriptor.bNumConfigurations;
++      for (i = 0; i < num_configs; (i++, c++)) {
++              struct usb_interface_descriptor *desc = NULL;
++
++              if (!c->desc.bNumInterfaces)
++                      continue;
++              desc = &c->intf_cache[0]->altsetting->desc;
++              if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
++                      break;
++      }
++
++      if (i == num_configs)
++              return -ENODEV;
++
++      if (usb_set_configuration(udev, c->desc.bConfigurationValue)) {
++              dev_err(&udev->dev, "Failed to set configuration %d\n",
++                      c->desc.bConfigurationValue);
++              return -ENODEV;
++      }
++
++      return 0;
++}
++
++static struct usb_device_driver rtl8152_cfgselector_driver = {
++      .name =         MODULENAME "-cfgselector",
++      .probe =        rtl8152_cfgselector_probe,
++      .id_table =     rtl8152_table,
++      .generic_subclass = 1,
++};
++
++static int __init rtl8152_driver_init(void)
++{
++      int ret;
++
++      ret = usb_register_device_driver(&rtl8152_cfgselector_driver, THIS_MODULE);
++      if (ret)
++              return ret;
++      return usb_register(&rtl8152_driver);
++}
++
++static void __exit rtl8152_driver_exit(void)
++{
++      usb_deregister(&rtl8152_driver);
++      usb_deregister_device_driver(&rtl8152_cfgselector_driver);
++}
++
++module_init(rtl8152_driver_init);
++module_exit(rtl8152_driver_exit);
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/target/linux/generic/backport-6.1/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch b/target/linux/generic/backport-6.1/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch
new file mode 100644 (file)
index 0000000..17131c1
--- /dev/null
@@ -0,0 +1,158 @@
+From 69649ef8405320f81497f4757faac8234f61b167 Mon Sep 17 00:00:00 2001
+From: Bjørn Mork <bjorn@mork.no>
+Date: Fri, 6 Jan 2023 17:07:39 +0100
+Subject: [PATCH] cdc_ether: no need to blacklist any r8152 devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The r8152 driver does not need this anymore.
+
+Dropping blacklist entries adds optional support for these
+devices in ECM mode.
+
+The 8153 devices are handled by the r8153_ecm driver when
+in ECM mode, and must still be blacklisted here.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/cdc_ether.c | 114 ------------------------------------
+ 1 file changed, 114 deletions(-)
+
+--- a/drivers/net/usb/cdc_ether.c
++++ b/drivers/net/usb/cdc_ether.c
+@@ -768,13 +768,6 @@ static const struct usb_device_id produc
+       .driver_info = 0,
+ },
+-/* Realtek RTL8152 Based USB 2.0 Ethernet Adapters */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+ /* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */
+ {
+       USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM,
+@@ -782,119 +775,12 @@ static const struct usb_device_id       produc
+       .driver_info = 0,
+ },
+-/* Samsung USB Ethernet Adapters */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, 0xa101, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-#if IS_ENABLED(CONFIG_USB_RTL8152)
+-/* Linksys USB3GIGV1 Ethernet Adapter */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LINKSYS_VENDOR_ID, 0x0041, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-#endif
+-
+-/* Lenovo ThinkPad OneLink+ Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3054, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad USB-C Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3062, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad Thunderbolt 3 Dock (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3069, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* ThinkPad Thunderbolt 3 Dock Gen 2 (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3082, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo USB C to Ethernet Adapter (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x720c, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Lenovo USB-C Travel Hub (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7214, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+ /* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */
+ {
+       USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x721e, USB_CLASS_COMM,
+                       USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+ },
+-
+-/* ThinkPad USB-C Dock Gen 2 (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0xa387, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* NVIDIA Tegra USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(NVIDIA_VENDOR_ID, 0x09ff, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface 2 dock (based on Realtek RTL8152) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07ab, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07c6, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153B) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x0927, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+-
+-/* TP-LINK UE300 USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+-{
+-      USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, 0x0601, USB_CLASS_COMM,
+-                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+-      .driver_info = 0,
+-},
+ /* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
+ {
diff --git a/target/linux/generic/backport-6.1/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch b/target/linux/generic/backport-6.1/795-v6.3-03-r8152-avoid-to-change-cfg-for-all-devices.patch
new file mode 100644 (file)
index 0000000..a5b01f7
--- /dev/null
@@ -0,0 +1,64 @@
+From 0d4cda805a183bbe523f2407edb5c14ade50b841 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 17 Jan 2023 11:03:44 +0800
+Subject: [PATCH] r8152: avoid to change cfg for all devices
+
+The rtl8152_cfgselector_probe() should set the USB configuration to the
+vendor mode only for the devices which the driver (r8152) supports.
+Otherwise, no driver would be used for such devices.
+
+Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9542,9 +9542,8 @@ static int rtl_fw_init(struct r8152 *tp)
+       return 0;
+ }
+-u8 rtl8152_get_version(struct usb_interface *intf)
++static u8 __rtl_get_hw_ver(struct usb_device *udev)
+ {
+-      struct usb_device *udev = interface_to_usbdev(intf);
+       u32 ocp_data = 0;
+       __le32 *tmp;
+       u8 version;
+@@ -9614,10 +9613,19 @@ u8 rtl8152_get_version(struct usb_interf
+               break;
+       default:
+               version = RTL_VER_UNKNOWN;
+-              dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data);
++              dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data);
+               break;
+       }
++      return version;
++}
++
++u8 rtl8152_get_version(struct usb_interface *intf)
++{
++      u8 version;
++
++      version = __rtl_get_hw_ver(interface_to_usbdev(intf));
++
+       dev_dbg(&intf->dev, "Detected version 0x%04x\n", version);
+       return version;
+@@ -9917,6 +9925,12 @@ static int rtl8152_cfgselector_probe(str
+       struct usb_host_config *c;
+       int i, num_configs;
++      /* Switch the device to vendor mode, if and only if the vendor mode
++       * driver supports it.
++       */
++      if (__rtl_get_hw_ver(udev) == RTL_VER_UNKNOWN)
++              return 0;
++
+       /* The vendor mode is not always config #1, so to find it out. */
+       c = udev->config;
+       num_configs = udev->descriptor.bNumConfigurations;
diff --git a/target/linux/generic/backport-6.1/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch b/target/linux/generic/backport-6.1/795-v6.3-04-r8152-remove-rtl_vendor_mode-function.patch
new file mode 100644 (file)
index 0000000..f977508
--- /dev/null
@@ -0,0 +1,71 @@
+From 95a4c1d617b92cdc4522297741b56e8f6cd01a1e Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Thu, 19 Jan 2023 15:40:42 +0800
+Subject: [PATCH] r8152: remove rtl_vendor_mode function
+
+After commit ec51fbd1b8a2 ("r8152: add USB device driver for
+config selection"), the code about changing USB configuration
+in rtl_vendor_mode() wouldn't be run anymore. Therefore, the
+function could be removed.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 39 +--------------------------------------
+ 1 file changed, 1 insertion(+), 38 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -8274,43 +8274,6 @@ static bool rtl_check_vendor_ok(struct u
+       return true;
+ }
+-static bool rtl_vendor_mode(struct usb_interface *intf)
+-{
+-      struct usb_host_interface *alt = intf->cur_altsetting;
+-      struct usb_device *udev;
+-      struct usb_host_config *c;
+-      int i, num_configs;
+-
+-      if (alt->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC)
+-              return rtl_check_vendor_ok(intf);
+-
+-      /* The vendor mode is not always config #1, so to find it out. */
+-      udev = interface_to_usbdev(intf);
+-      c = udev->config;
+-      num_configs = udev->descriptor.bNumConfigurations;
+-      if (num_configs < 2)
+-              return false;
+-
+-      for (i = 0; i < num_configs; (i++, c++)) {
+-              struct usb_interface_descriptor *desc = NULL;
+-
+-              if (c->desc.bNumInterfaces > 0)
+-                      desc = &c->intf_cache[0]->altsetting->desc;
+-              else
+-                      continue;
+-
+-              if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+-                      usb_driver_set_configuration(udev, c->desc.bConfigurationValue);
+-                      break;
+-              }
+-      }
+-
+-      if (i == num_configs)
+-              dev_err(&intf->dev, "Unexpected Device\n");
+-
+-      return false;
+-}
+-
+ static int rtl8152_pre_reset(struct usb_interface *intf)
+ {
+       struct r8152 *tp = usb_get_intfdata(intf);
+@@ -9672,7 +9635,7 @@ static int rtl8152_probe(struct usb_inte
+       if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+               return -ENODEV;
+-      if (!rtl_vendor_mode(intf))
++      if (!rtl_check_vendor_ok(intf))
+               return -ENODEV;
+       usb_reset_device(udev);
diff --git a/target/linux/generic/backport-6.1/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch b/target/linux/generic/backport-6.1/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch
new file mode 100644 (file)
index 0000000..421f2c7
--- /dev/null
@@ -0,0 +1,46 @@
+From 02767440e1dda9861a11ca1dbe0f19a760b1d5c2 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Thu, 19 Jan 2023 15:40:43 +0800
+Subject: [PATCH] r8152: reduce the control transfer of rtl8152_get_version()
+
+Reduce the control transfer by moving calling rtl8152_get_version() in
+rtl8152_probe(). This could prevent from calling rtl8152_get_version()
+for unnecessary situations. For example, after setting config #2 for the
+device, there are two interfaces and rtl8152_probe() may be called
+twice. However, we don't need to call rtl8152_get_version() for this
+situation.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9624,20 +9624,21 @@ static int rtl8152_probe(struct usb_inte
+                        const struct usb_device_id *id)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+-      u8 version = rtl8152_get_version(intf);
+       struct r8152 *tp;
+       struct net_device *netdev;
++      u8 version;
+       int ret;
+-      if (version == RTL_VER_UNKNOWN)
+-              return -ENODEV;
+-
+       if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+               return -ENODEV;
+       if (!rtl_check_vendor_ok(intf))
+               return -ENODEV;
++      version = rtl8152_get_version(intf);
++      if (version == RTL_VER_UNKNOWN)
++              return -ENODEV;
++
+       usb_reset_device(udev);
+       netdev = alloc_etherdev(sizeof(struct r8152));
+       if (!netdev) {
diff --git a/target/linux/generic/backport-6.1/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch b/target/linux/generic/backport-6.1/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch
new file mode 100644 (file)
index 0000000..cdabca3
--- /dev/null
@@ -0,0 +1,55 @@
+From 5cc33f139e11b893ff6dc60d8a0ae865a65521ac Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Thu, 6 Apr 2023 17:14:26 -0700
+Subject: [PATCH] r8152: Add __GFP_NOWARN to big allocations
+
+When memory is a little tight on my system, it's pretty easy to see
+warnings that look like this.
+
+  ksoftirqd/0: page allocation failure: order:3, mode:0x40a20(GFP_ATOMIC|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0
+  ...
+  Call trace:
+   dump_backtrace+0x0/0x1e8
+   show_stack+0x20/0x2c
+   dump_stack_lvl+0x60/0x78
+   dump_stack+0x18/0x38
+   warn_alloc+0x104/0x174
+   __alloc_pages+0x588/0x67c
+   alloc_rx_agg+0xa0/0x190 [r8152 ...]
+   r8152_poll+0x270/0x760 [r8152 ...]
+   __napi_poll+0x44/0x1ec
+   net_rx_action+0x100/0x300
+   __do_softirq+0xec/0x38c
+   run_ksoftirqd+0x38/0xec
+   smpboot_thread_fn+0xb8/0x248
+   kthread+0x134/0x154
+   ret_from_fork+0x10/0x20
+
+On a fragmented system it's normal that order 3 allocations will
+sometimes fail, especially atomic ones. The driver handles these
+failures fine and the WARN just creates spam in the logs for this
+case. The __GFP_NOWARN flag is exactly for this situation, so add it
+to the allocation.
+
+NOTE: my testing is on a 5.15 system, but there should be no reason
+that this would be fundamentally different on a mainline kernel.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Acked-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230406171411.1.I84dbef45786af440fd269b71e9436a96a8e7a152@changeid
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -1947,7 +1947,7 @@ static struct rx_agg *alloc_rx_agg(struc
+       if (!rx_agg)
+               return NULL;
+-      rx_agg->page = alloc_pages(mflags | __GFP_COMP, order);
++      rx_agg->page = alloc_pages(mflags | __GFP_COMP | __GFP_NOWARN, order);
+       if (!rx_agg->page)
+               goto free_rx;
diff --git a/target/linux/generic/backport-6.1/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch b/target/linux/generic/backport-6.1/795-v6.4-07-r8152-fix-the-autosuspend-doesn-t-work.patch
new file mode 100644 (file)
index 0000000..b4d5b8b
--- /dev/null
@@ -0,0 +1,24 @@
+From 0fbd79c01a9a657348f7032df70c57a406468c86 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 2 May 2023 11:36:27 +0800
+Subject: [PATCH] r8152: fix the autosuspend doesn't work
+
+Set supports_autosuspend = 1 for the rtl8152_cfgselector_driver.
+
+Fixes: ec51fbd1b8a2 ("r8152: add USB device driver for config selection")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9925,6 +9925,7 @@ static struct usb_device_driver rtl8152_
+       .probe =        rtl8152_cfgselector_probe,
+       .id_table =     rtl8152_table,
+       .generic_subclass = 1,
++      .supports_autosuspend = 1,
+ };
+ static int __init rtl8152_driver_init(void)
diff --git a/target/linux/generic/backport-6.1/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch b/target/linux/generic/backport-6.1/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch
new file mode 100644 (file)
index 0000000..3ba79d6
--- /dev/null
@@ -0,0 +1,70 @@
+From 57df0fb9d511f91202114813e90128d65c0589f0 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 26 Jul 2023 11:08:07 +0800
+Subject: [PATCH] r8152: adjust generic_ocp_write function
+
+Reduce the control transfer if all bytes of first or the last DWORD are
+written.
+
+The original method is to split the control transfer into three parts
+(the first DWORD, middle continuous data, and the last DWORD). However,
+they could be combined if whole bytes of the first DWORD or last DWORD
+are written. That is, the first DWORD or the last DWORD could be combined
+with the middle continuous data, if the byte_en is 0xff.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230726030808.9093-418-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 29 ++++++++++++++++++-----------
+ 1 file changed, 18 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -1313,16 +1313,24 @@ static int generic_ocp_write(struct r815
+       byteen_end = byteen & BYTE_EN_END_MASK;
+       byen = byteen_start | (byteen_start << 4);
+-      ret = set_registers(tp, index, type | byen, 4, data);
+-      if (ret < 0)
+-              goto error1;
+-
+-      index += 4;
+-      data += 4;
+-      size -= 4;
+-      if (size) {
++      /* Split the first DWORD if the byte_en is not 0xff */
++      if (byen != BYTE_EN_DWORD) {
++              ret = set_registers(tp, index, type | byen, 4, data);
++              if (ret < 0)
++                      goto error1;
++
++              index += 4;
++              data += 4;
+               size -= 4;
++      }
++
++      if (size) {
++              byen = byteen_end | (byteen_end >> 4);
++
++              /* Split the last DWORD if the byte_en is not 0xff */
++              if (byen != BYTE_EN_DWORD)
++                      size -= 4;
+               while (size) {
+                       if (size > limit) {
+@@ -1349,10 +1357,9 @@ static int generic_ocp_write(struct r815
+                       }
+               }
+-              byen = byteen_end | (byteen_end >> 4);
+-              ret = set_registers(tp, index, type | byen, 4, data);
+-              if (ret < 0)
+-                      goto error1;
++              /* Set the last DWORD */
++              if (byen != BYTE_EN_DWORD)
++                      ret = set_registers(tp, index, type | byen, 4, data);
+       }
+ error1:
diff --git a/target/linux/generic/backport-6.1/795-v6.6-09-r8152-set-bp-in-bulk.patch b/target/linux/generic/backport-6.1/795-v6.6-09-r8152-set-bp-in-bulk.patch
new file mode 100644 (file)
index 0000000..485a005
--- /dev/null
@@ -0,0 +1,129 @@
+From e5c266a61186b462c388c53a3564c375e72f2244 Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Wed, 26 Jul 2023 11:08:08 +0800
+Subject: [PATCH] r8152: set bp in bulk
+
+PLA_BP_0 ~ PLA_BP_15 (0xfc28 ~ 0xfc46) are continuous registers, so we
+could combine the control transfers into one control transfer.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230726030808.9093-419-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 75 ++++++++++++++---------------------------
+ 1 file changed, 25 insertions(+), 50 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -3984,29 +3984,10 @@ static void rtl_reset_bmu(struct r8152 *
+ /* Clear the bp to stop the firmware before loading a new one */
+ static void rtl_clear_bp(struct r8152 *tp, u16 type)
+ {
+-      switch (tp->version) {
+-      case RTL_VER_01:
+-      case RTL_VER_02:
+-      case RTL_VER_07:
+-              break;
+-      case RTL_VER_03:
+-      case RTL_VER_04:
+-      case RTL_VER_05:
+-      case RTL_VER_06:
+-              ocp_write_byte(tp, type, PLA_BP_EN, 0);
+-              break;
+-      case RTL_VER_14:
+-              ocp_write_word(tp, type, USB_BP2_EN, 0);
++      u16 bp[16] = {0};
++      u16 bp_num;
+-              ocp_write_word(tp, type, USB_BP_8, 0);
+-              ocp_write_word(tp, type, USB_BP_9, 0);
+-              ocp_write_word(tp, type, USB_BP_10, 0);
+-              ocp_write_word(tp, type, USB_BP_11, 0);
+-              ocp_write_word(tp, type, USB_BP_12, 0);
+-              ocp_write_word(tp, type, USB_BP_13, 0);
+-              ocp_write_word(tp, type, USB_BP_14, 0);
+-              ocp_write_word(tp, type, USB_BP_15, 0);
+-              break;
++      switch (tp->version) {
+       case RTL_VER_08:
+       case RTL_VER_09:
+       case RTL_VER_10:
+@@ -4014,32 +3995,31 @@ static void rtl_clear_bp(struct r8152 *t
+       case RTL_VER_12:
+       case RTL_VER_13:
+       case RTL_VER_15:
+-      default:
+               if (type == MCU_TYPE_USB) {
+                       ocp_write_word(tp, MCU_TYPE_USB, USB_BP2_EN, 0);
+-
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_8, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_9, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_10, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_11, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_12, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_13, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_14, 0);
+-                      ocp_write_word(tp, MCU_TYPE_USB, USB_BP_15, 0);
+-              } else {
+-                      ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
++                      bp_num = 16;
++                      break;
+               }
++              fallthrough;
++      case RTL_VER_03:
++      case RTL_VER_04:
++      case RTL_VER_05:
++      case RTL_VER_06:
++              ocp_write_byte(tp, type, PLA_BP_EN, 0);
++              fallthrough;
++      case RTL_VER_01:
++      case RTL_VER_02:
++      case RTL_VER_07:
++              bp_num = 8;
++              break;
++      case RTL_VER_14:
++      default:
++              ocp_write_word(tp, type, USB_BP2_EN, 0);
++              bp_num = 16;
+               break;
+       }
+-      ocp_write_word(tp, type, PLA_BP_0, 0);
+-      ocp_write_word(tp, type, PLA_BP_1, 0);
+-      ocp_write_word(tp, type, PLA_BP_2, 0);
+-      ocp_write_word(tp, type, PLA_BP_3, 0);
+-      ocp_write_word(tp, type, PLA_BP_4, 0);
+-      ocp_write_word(tp, type, PLA_BP_5, 0);
+-      ocp_write_word(tp, type, PLA_BP_6, 0);
+-      ocp_write_word(tp, type, PLA_BP_7, 0);
++      generic_ocp_write(tp, PLA_BP_0, BYTE_EN_DWORD, bp_num << 1, bp, type);
+       /* wait 3 ms to make sure the firmware is stopped */
+       usleep_range(3000, 6000);
+@@ -5016,10 +4996,9 @@ static void rtl8152_fw_phy_nc_apply(stru
+ static void rtl8152_fw_mac_apply(struct r8152 *tp, struct fw_mac *mac)
+ {
+-      u16 bp_en_addr, bp_index, type, bp_num, fw_ver_reg;
++      u16 bp_en_addr, type, fw_ver_reg;
+       u32 length;
+       u8 *data;
+-      int i;
+       switch (__le32_to_cpu(mac->blk_hdr.type)) {
+       case RTL_FW_PLA:
+@@ -5061,12 +5040,8 @@ static void rtl8152_fw_mac_apply(struct
+       ocp_write_word(tp, type, __le16_to_cpu(mac->bp_ba_addr),
+                      __le16_to_cpu(mac->bp_ba_value));
+-      bp_index = __le16_to_cpu(mac->bp_start);
+-      bp_num = __le16_to_cpu(mac->bp_num);
+-      for (i = 0; i < bp_num; i++) {
+-              ocp_write_word(tp, type, bp_index, __le16_to_cpu(mac->bp[i]));
+-              bp_index += 2;
+-      }
++      generic_ocp_write(tp, __le16_to_cpu(mac->bp_start), BYTE_EN_DWORD,
++                        __le16_to_cpu(mac->bp_num) << 1, mac->bp, type);
+       bp_en_addr = __le16_to_cpu(mac->bp_en_addr);
+       if (bp_en_addr)
diff --git a/target/linux/generic/backport-6.1/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch b/target/linux/generic/backport-6.1/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch
new file mode 100644 (file)
index 0000000..864671b
--- /dev/null
@@ -0,0 +1,39 @@
+From cf74eb5a5bc867258e7d0b0d1c3c4a60e1e3de2f Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Mon, 14 Aug 2023 08:35:21 -0700
+Subject: [PATCH] eth: r8152: try to use a normal budget
+
+Mario reports that loading r8152 on his system leads to a:
+
+  netif_napi_add_weight() called with weight 256
+
+warning getting printed. We don't have any solid data
+on why such high budget was chosen, and it may cause
+stalls in processing other softirqs and rt threads.
+So try to switch back to the default (64) weight.
+
+If this slows down someone's system we should investigate
+which part of stopping starting the NAPI poll in this
+driver are expensive.
+
+Reported-by: Mario Limonciello <mario.limonciello@amd.com>
+Link: https://lore.kernel.org/all/0bfd445a-81f7-f702-08b0-bd5a72095e49@amd.com/
+Acked-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230814153521.2697982-1-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9770,8 +9770,7 @@ static int rtl8152_probe(struct usb_inte
+       usb_set_intfdata(intf, tp);
+-      netif_napi_add_weight(netdev, &tp->napi, r8152_poll,
+-                            tp->support_2500full ? 256 : 64);
++      netif_napi_add(netdev, &tp->napi, r8152_poll);
+       ret = register_netdev(netdev);
+       if (ret != 0) {
diff --git a/target/linux/generic/backport-6.1/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch b/target/linux/generic/backport-6.1/795-v6.6-11-r8152-add-vendor-device-ID-pair-for-D-Link-DUB-E250.patch
new file mode 100644 (file)
index 0000000..ab6563d
--- /dev/null
@@ -0,0 +1,39 @@
+From 72f93a3136ee18fd59fa6579f84c07e93424681e Mon Sep 17 00:00:00 2001
+From: Antonio Napolitano <anton@polit.no>
+Date: Sat, 26 Aug 2023 01:05:50 +0200
+Subject: [PATCH] r8152: add vendor/device ID pair for D-Link DUB-E250
+
+The D-Link DUB-E250 is an RTL8156 based 2.5G Ethernet controller.
+
+Add the vendor and product ID values to the driver. This makes Ethernet
+work with the adapter.
+
+Signed-off-by: Antonio Napolitano <anton@polit.no>
+Link: https://lore.kernel.org/r/CV200KJEEUPC.WPKAHXCQJ05I@mercurius
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c   | 1 +
+ include/linux/usb/r8152.h | 1 +
+ 2 files changed, 2 insertions(+)
+
+
+--- a/include/linux/usb/r8152.h
++++ b/include/linux/usb/r8152.h
+@@ -29,6 +29,7 @@
+ #define VENDOR_ID_LINKSYS             0x13b1
+ #define VENDOR_ID_NVIDIA              0x0955
+ #define VENDOR_ID_TPLINK              0x2357
++#define VENDOR_ID_DLINK                       0x2001
+ #if IS_REACHABLE(CONFIG_USB_RTL8152)
+ extern u8 rtl8152_get_version(struct usb_interface *intf);
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -9846,6 +9846,7 @@ static const struct usb_device_id rtl815
+       { USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041) },
+       { USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff) },
+       { USB_DEVICE(VENDOR_ID_TPLINK,  0x0601) },
++      { USB_DEVICE(VENDOR_ID_DLINK,   0xb301) },
+       {}
+ };
diff --git a/target/linux/generic/backport-6.1/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch b/target/linux/generic/backport-6.1/795-v6.6-12-r8152-Rename-RTL8152_UNPLUG-to-RTL8152_INACCESSIBLE.patch
new file mode 100644 (file)
index 0000000..480a602
--- /dev/null
@@ -0,0 +1,447 @@
+From 715f67f33af45ce2cc3a5b1ef133cc8c8e7787b0 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Fri, 20 Oct 2023 14:06:58 -0700
+Subject: [PATCH] r8152: Rename RTL8152_UNPLUG to RTL8152_INACCESSIBLE
+
+Whenever the RTL8152_UNPLUG is set that just tells the driver that all
+accesses will fail and we should just immediately bail. A future patch
+will use this same concept at a time when the driver hasn't actually
+been unplugged but is about to be reset. Rename the flag in
+preparation for the future patch.
+
+This is a no-op change and just a search and replace.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Grant Grundler <grundler@chromium.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 96 ++++++++++++++++++++---------------------
+ 1 file changed, 48 insertions(+), 48 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -763,7 +763,7 @@ enum rtl_register_content {
+ /* rtl8152 flags */
+ enum rtl8152_flags {
+-      RTL8152_UNPLUG = 0,
++      RTL8152_INACCESSIBLE = 0,
+       RTL8152_SET_RX_MODE,
+       WORK_ENABLE,
+       RTL8152_LINK_CHG,
+@@ -1244,7 +1244,7 @@ int set_registers(struct r8152 *tp, u16
+ static void rtl_set_unplug(struct r8152 *tp)
+ {
+       if (tp->udev->state == USB_STATE_NOTATTACHED) {
+-              set_bit(RTL8152_UNPLUG, &tp->flags);
++              set_bit(RTL8152_INACCESSIBLE, &tp->flags);
+               smp_mb__after_atomic();
+       }
+ }
+@@ -1255,7 +1255,7 @@ static int generic_ocp_read(struct r8152
+       u16 limit = 64;
+       int ret = 0;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       /* both size and indix must be 4 bytes align */
+@@ -1299,7 +1299,7 @@ static int generic_ocp_write(struct r815
+       u16 byteen_start, byteen_end, byen;
+       u16 limit = 512;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       /* both size and indix must be 4 bytes align */
+@@ -1536,7 +1536,7 @@ static int read_mii_word(struct net_devi
+       struct r8152 *tp = netdev_priv(netdev);
+       int ret;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       if (phy_id != R8152_PHY_ID)
+@@ -1552,7 +1552,7 @@ void write_mii_word(struct net_device *n
+ {
+       struct r8152 *tp = netdev_priv(netdev);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (phy_id != R8152_PHY_ID)
+@@ -1757,7 +1757,7 @@ static void read_bulk_callback(struct ur
+       if (!tp)
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+@@ -1849,7 +1849,7 @@ static void write_bulk_callback(struct u
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!skb_queue_empty(&tp->tx_queue))
+@@ -1870,7 +1870,7 @@ static void intr_callback(struct urb *ur
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+               return;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       switch (status) {
+@@ -2614,7 +2614,7 @@ static void bottom_half(struct tasklet_s
+ {
+       struct r8152 *tp = from_tasklet(tp, t, tx_tl);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (!test_bit(WORK_ENABLE, &tp->flags))
+@@ -2657,7 +2657,7 @@ int r8152_submit_rx(struct r8152 *tp, st
+       int ret;
+       /* The rx would be stopped, so skip submitting */
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags) ||
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags) ||
+           !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev))
+               return 0;
+@@ -3057,7 +3057,7 @@ static int rtl_enable(struct r8152 *tp)
+ static int rtl8152_enable(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -3144,7 +3144,7 @@ static int rtl8153_enable(struct r8152 *
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -3176,7 +3176,7 @@ static void rtl_disable(struct r8152 *tp
+       u32 ocp_data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -3630,7 +3630,7 @@ static u16 r8153_phy_status(struct r8152
+               }
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -3662,7 +3662,7 @@ static void r8153b_ups_en(struct r8152 *
+                       int i;
+                       for (i = 0; i < 500; i++) {
+-                              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++                              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                                       return;
+                               if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+                                   AUTOLOAD_DONE)
+@@ -3704,7 +3704,7 @@ static void r8153c_ups_en(struct r8152 *
+                       int i;
+                       for (i = 0; i < 500; i++) {
+-                              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++                              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                                       return;
+                               if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+                                   AUTOLOAD_DONE)
+@@ -4049,8 +4049,8 @@ static int rtl_phy_patch_request(struct
+       for (i = 0; wait && i < 5000; i++) {
+               u32 ocp_data;
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
+-                      break;
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
++                      return -ENODEV;
+               usleep_range(1000, 2000);
+               ocp_data = ocp_reg_read(tp, OCP_PHY_PATCH_STAT);
+@@ -6008,7 +6008,7 @@ static int rtl8156_enable(struct r8152 *
+       u32 ocp_data;
+       u16 speed;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       r8156_fc_parameter(tp);
+@@ -6066,7 +6066,7 @@ static int rtl8156b_enable(struct r8152
+       u32 ocp_data;
+       u16 speed;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       set_tx_qlen(tp);
+@@ -6252,7 +6252,7 @@ out:
+ static void rtl8152_up(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8152_aldps_en(tp, false);
+@@ -6262,7 +6262,7 @@ static void rtl8152_up(struct r8152 *tp)
+ static void rtl8152_down(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6277,7 +6277,7 @@ static void rtl8153_up(struct r8152 *tp)
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_u1u2en(tp, false);
+@@ -6317,7 +6317,7 @@ static void rtl8153_down(struct r8152 *t
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6338,7 +6338,7 @@ static void rtl8153b_up(struct r8152 *tp
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6362,7 +6362,7 @@ static void rtl8153b_down(struct r8152 *
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6399,7 +6399,7 @@ static void rtl8153c_up(struct r8152 *tp
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6480,7 +6480,7 @@ static void rtl8156_up(struct r8152 *tp)
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -6553,7 +6553,7 @@ static void rtl8156_down(struct r8152 *t
+ {
+       u32 ocp_data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               return;
+       }
+@@ -6691,7 +6691,7 @@ static void rtl_work_func_t(struct work_
+       /* If the device is unplugged or !netif_running(), the workqueue
+        * doesn't need to wake the device, and could return directly.
+        */
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags) || !netif_running(tp->netdev))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags) || !netif_running(tp->netdev))
+               return;
+       if (usb_autopm_get_interface(tp->intf) < 0)
+@@ -6730,7 +6730,7 @@ static void rtl_hw_phy_work_func_t(struc
+ {
+       struct r8152 *tp = container_of(work, struct r8152, hw_phy_work.work);
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (usb_autopm_get_interface(tp->intf) < 0)
+@@ -6857,7 +6857,7 @@ static int rtl8152_close(struct net_devi
+       netif_stop_queue(netdev);
+       res = usb_autopm_get_interface(tp->intf);
+-      if (res < 0 || test_bit(RTL8152_UNPLUG, &tp->flags)) {
++      if (res < 0 || test_bit(RTL8152_INACCESSIBLE, &tp->flags)) {
+               rtl_drop_queued_tx(tp);
+               rtl_stop_rx(tp);
+       } else {
+@@ -6890,7 +6890,7 @@ static void r8152b_init(struct r8152 *tp
+       u32 ocp_data;
+       u16 data;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       data = r8152_mdio_read(tp, MII_BMCR);
+@@ -6934,7 +6934,7 @@ static void r8153_init(struct r8152 *tp)
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_u1u2en(tp, false);
+@@ -6945,7 +6945,7 @@ static void r8153_init(struct r8152 *tp)
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -7074,7 +7074,7 @@ static void r8153b_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -7085,7 +7085,7 @@ static void r8153b_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       break;
+       }
+@@ -7156,7 +7156,7 @@ static void r8153c_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_u1u2en(tp, false);
+@@ -7176,7 +7176,7 @@ static void r8153c_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -8005,7 +8005,7 @@ static void r8156_init(struct r8152 *tp)
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+@@ -8026,7 +8026,7 @@ static void r8156_init(struct r8152 *tp)
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -8101,7 +8101,7 @@ static void r8156b_init(struct r8152 *tp
+       u16 data;
+       int i;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+@@ -8135,7 +8135,7 @@ static void r8156b_init(struct r8152 *tp
+                       break;
+               msleep(20);
+-              if (test_bit(RTL8152_UNPLUG, &tp->flags))
++              if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+                       return;
+       }
+@@ -9164,7 +9164,7 @@ static int rtl8152_ioctl(struct net_devi
+       struct mii_ioctl_data *data = if_mii(rq);
+       int res;
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return -ENODEV;
+       res = usb_autopm_get_interface(tp->intf);
+@@ -9266,7 +9266,7 @@ static const struct net_device_ops rtl81
+ static void rtl8152_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       if (tp->version != RTL_VER_01)
+@@ -9275,7 +9275,7 @@ static void rtl8152_unload(struct r8152
+ static void rtl8153_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153_power_cut_en(tp, false);
+@@ -9283,7 +9283,7 @@ static void rtl8153_unload(struct r8152
+ static void rtl8153b_unload(struct r8152 *tp)
+ {
+-      if (test_bit(RTL8152_UNPLUG, &tp->flags))
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+               return;
+       r8153b_power_cut_en(tp, false);
diff --git a/target/linux/generic/backport-6.1/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch b/target/linux/generic/backport-6.1/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch
new file mode 100644 (file)
index 0000000..2cbe365
--- /dev/null
@@ -0,0 +1,398 @@
+From d9962b0d42029bcb40fe3c38bce06d1870fa4df4 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Fri, 20 Oct 2023 14:06:59 -0700
+Subject: [PATCH] r8152: Block future register access if register access fails
+
+Even though the functions to read/write registers can fail, most of
+the places in the r8152 driver that read/write register values don't
+check error codes. The lack of error code checking is problematic in
+at least two ways.
+
+The first problem is that the r8152 driver often uses code patterns
+similar to this:
+  x = read_register()
+  x = x | SOME_BIT;
+  write_register(x);
+
+...with the above pattern, if the read_register() fails and returns
+garbage then we'll end up trying to write modified garbage back to the
+Realtek adapter. If the write_register() succeeds that's bad. Note
+that as of commit f53a7ad18959 ("r8152: Set memory to all 0xFFs on
+failed reg reads") the "garbage" returned by read_register() will at
+least be consistent garbage, but it is still garbage.
+
+It turns out that this problem is very serious. Writing garbage to
+some of the hardware registers on the Ethernet adapter can put the
+adapter in such a bad state that it needs to be power cycled (fully
+unplugged and plugged in again) before it can enumerate again.
+
+The second problem is that the r8152 driver generally has functions
+that are long sequences of register writes. Assuming everything will
+be OK if a random register write fails in the middle isn't a great
+assumption.
+
+One might wonder if the above two problems are real. You could ask if
+we would really have a successful write after a failed read. It turns
+out that the answer appears to be "yes, this can happen". In fact,
+we've seen at least two distinct failure modes where this happens.
+
+On a sc7180-trogdor Chromebook if you drop into kdb for a while and
+then resume, you can see:
+1. We get a "Tx timeout"
+2. The "Tx timeout" queues up a USB reset.
+3. In rtl8152_pre_reset() we try to reinit the hardware.
+4. The first several (2-9) register accesses fail with a timeout, then
+   things recover.
+
+The above test case was actually fixed by the patch ("r8152: Increase
+USB control msg timeout to 5000ms as per spec") but at least shows
+that we really can see successful calls after failed ones.
+
+On a different (AMD) based Chromebook with a particular adapter, we
+found that during reboot tests we'd also sometimes get a transitory
+failure. In this case we saw -EPIPE being returned sometimes. Retrying
+worked, but retrying is not always safe for all register accesses
+since reading/writing some registers might have side effects (like
+registers that clear on read).
+
+Let's fully lock out all register access if a register access fails.
+When we do this, we'll try to queue up a USB reset and try to unlock
+register access after the reset. This is slightly tricker than it
+sounds since the r8152 driver has an optimized reset sequence that
+only works reliably after probe happens. In order to handle this, we
+avoid the optimized reset if probe didn't finish. Instead, we simply
+retry the probe routine in this case.
+
+When locking out access, we'll use the existing infrastructure that
+the driver was using when it detected we were unplugged. This keeps us
+from getting stuck in delay loops in some parts of the driver.
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Grant Grundler <grundler@chromium.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/r8152.c | 207 ++++++++++++++++++++++++++++++++++------
+ 1 file changed, 176 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -772,6 +772,9 @@ enum rtl8152_flags {
+       SCHEDULE_TASKLET,
+       GREEN_ETHERNET,
+       RX_EPROTO,
++      IN_PRE_RESET,
++      PROBED_WITH_NO_ERRORS,
++      PROBE_SHOULD_RETRY,
+ };
+ #define DEVICE_ID_LENOVO_USB_C_TRAVEL_HUB             0x721e
+@@ -952,6 +955,8 @@ struct r8152 {
+       u8 version;
+       u8 duplex;
+       u8 autoneg;
++
++      unsigned int reg_access_reset_count;
+ };
+ /**
+@@ -1199,6 +1204,96 @@ static unsigned int agg_buf_sz = 16384;
+ #define RTL_LIMITED_TSO_SIZE  (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
++/* If register access fails then we block access and issue a reset. If this
++ * happens too many times in a row without a successful access then we stop
++ * trying to reset and just leave access blocked.
++ */
++#define REGISTER_ACCESS_MAX_RESETS    3
++
++static void rtl_set_inaccessible(struct r8152 *tp)
++{
++      set_bit(RTL8152_INACCESSIBLE, &tp->flags);
++      smp_mb__after_atomic();
++}
++
++static void rtl_set_accessible(struct r8152 *tp)
++{
++      clear_bit(RTL8152_INACCESSIBLE, &tp->flags);
++      smp_mb__after_atomic();
++}
++
++static
++int r8152_control_msg(struct r8152 *tp, unsigned int pipe, __u8 request,
++                    __u8 requesttype, __u16 value, __u16 index, void *data,
++                    __u16 size, const char *msg_tag)
++{
++      struct usb_device *udev = tp->udev;
++      int ret;
++
++      if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
++              return -ENODEV;
++
++      ret = usb_control_msg(udev, pipe, request, requesttype,
++                            value, index, data, size,
++                            USB_CTRL_GET_TIMEOUT);
++
++      /* No need to issue a reset to report an error if the USB device got
++       * unplugged; just return immediately.
++       */
++      if (ret == -ENODEV)
++              return ret;
++
++      /* If the write was successful then we're done */
++      if (ret >= 0) {
++              tp->reg_access_reset_count = 0;
++              return ret;
++      }
++
++      dev_err(&udev->dev,
++              "Failed to %s %d bytes at %#06x/%#06x (%d)\n",
++              msg_tag, size, value, index, ret);
++
++      /* Block all future register access until we reset. Much of the code
++       * in the driver doesn't check for errors. Notably, many parts of the
++       * driver do a read/modify/write of a register value without
++       * confirming that the read succeeded. Writing back modified garbage
++       * like this can fully wedge the adapter, requiring a power cycle.
++       */
++      rtl_set_inaccessible(tp);
++
++      /* If probe hasn't yet finished, then we'll request a retry of the
++       * whole probe routine if we get any control transfer errors. We
++       * never have to clear this bit since we free/reallocate the whole "tp"
++       * structure if we retry probe.
++       */
++      if (!test_bit(PROBED_WITH_NO_ERRORS, &tp->flags)) {
++              set_bit(PROBE_SHOULD_RETRY, &tp->flags);
++              return ret;
++      }
++
++      /* Failing to access registers in pre-reset is not surprising since we
++       * wouldn't be resetting if things were behaving normally. The register
++       * access we do in pre-reset isn't truly mandatory--we're just reusing
++       * the disable() function and trying to be nice by powering the
++       * adapter down before resetting it. Thus, if we're in pre-reset,
++       * we'll return right away and not try to queue up yet another reset.
++       * We know the post-reset is already coming.
++       */
++      if (test_bit(IN_PRE_RESET, &tp->flags))
++              return ret;
++
++      if (tp->reg_access_reset_count < REGISTER_ACCESS_MAX_RESETS) {
++              usb_queue_reset_device(tp->intf);
++              tp->reg_access_reset_count++;
++      } else if (tp->reg_access_reset_count == REGISTER_ACCESS_MAX_RESETS) {
++              dev_err(&udev->dev,
++                      "Tried to reset %d times; giving up.\n",
++                      REGISTER_ACCESS_MAX_RESETS);
++      }
++
++      return ret;
++}
++
+ static
+ int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
+ {
+@@ -1209,9 +1304,10 @@ int get_registers(struct r8152 *tp, u16
+       if (!tmp)
+               return -ENOMEM;
+-      ret = usb_control_msg(tp->udev, tp->pipe_ctrl_in,
+-                            RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+-                            value, index, tmp, size, USB_CTRL_GET_TIMEOUT);
++      ret = r8152_control_msg(tp, tp->pipe_ctrl_in,
++                              RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
++                              value, index, tmp, size, "read");
++
+       if (ret < 0)
+               memset(data, 0xff, size);
+       else
+@@ -1232,9 +1328,9 @@ int set_registers(struct r8152 *tp, u16
+       if (!tmp)
+               return -ENOMEM;
+-      ret = usb_control_msg(tp->udev, tp->pipe_ctrl_out,
+-                            RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
+-                            value, index, tmp, size, USB_CTRL_SET_TIMEOUT);
++      ret = r8152_control_msg(tp, tp->pipe_ctrl_out,
++                              RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
++                              value, index, tmp, size, "write");
+       kfree(tmp);
+@@ -1243,10 +1339,8 @@ int set_registers(struct r8152 *tp, u16
+ static void rtl_set_unplug(struct r8152 *tp)
+ {
+-      if (tp->udev->state == USB_STATE_NOTATTACHED) {
+-              set_bit(RTL8152_INACCESSIBLE, &tp->flags);
+-              smp_mb__after_atomic();
+-      }
++      if (tp->udev->state == USB_STATE_NOTATTACHED)
++              rtl_set_inaccessible(tp);
+ }
+ static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
+@@ -8261,7 +8355,7 @@ static int rtl8152_pre_reset(struct usb_
+       struct r8152 *tp = usb_get_intfdata(intf);
+       struct net_device *netdev;
+-      if (!tp)
++      if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
+               return 0;
+       netdev = tp->netdev;
+@@ -8276,7 +8370,9 @@ static int rtl8152_pre_reset(struct usb_
+       napi_disable(&tp->napi);
+       if (netif_carrier_ok(netdev)) {
+               mutex_lock(&tp->control);
++              set_bit(IN_PRE_RESET, &tp->flags);
+               tp->rtl_ops.disable(tp);
++              clear_bit(IN_PRE_RESET, &tp->flags);
+               mutex_unlock(&tp->control);
+       }
+@@ -8289,9 +8385,11 @@ static int rtl8152_post_reset(struct usb
+       struct net_device *netdev;
+       struct sockaddr sa;
+-      if (!tp)
++      if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
+               return 0;
++      rtl_set_accessible(tp);
++
+       /* reset the MAC address in case of policy change */
+       if (determine_ethernet_addr(tp, &sa) >= 0) {
+               rtnl_lock();
+@@ -9493,17 +9591,29 @@ static u8 __rtl_get_hw_ver(struct usb_de
+       __le32 *tmp;
+       u8 version;
+       int ret;
++      int i;
+       tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return 0;
+-      ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+-                            RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+-                            PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
+-                            USB_CTRL_GET_TIMEOUT);
+-      if (ret > 0)
+-              ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
++      /* Retry up to 3 times in case there is a transitory error. We do this
++       * since retrying a read of the version is always safe and this
++       * function doesn't take advantage of r8152_control_msg().
++       */
++      for (i = 0; i < 3; i++) {
++              ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
++                                    RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
++                                    PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
++                                    USB_CTRL_GET_TIMEOUT);
++              if (ret > 0) {
++                      ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
++                      break;
++              }
++      }
++
++      if (i != 0 && ret > 0)
++              dev_warn(&udev->dev, "Needed %d retries to read version\n", i);
+       kfree(tmp);
+@@ -9602,25 +9712,14 @@ static bool rtl8152_supports_lenovo_macp
+       return 0;
+ }
+-static int rtl8152_probe(struct usb_interface *intf,
+-                       const struct usb_device_id *id)
++static int rtl8152_probe_once(struct usb_interface *intf,
++                            const struct usb_device_id *id, u8 version)
+ {
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct r8152 *tp;
+       struct net_device *netdev;
+-      u8 version;
+       int ret;
+-      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+-              return -ENODEV;
+-
+-      if (!rtl_check_vendor_ok(intf))
+-              return -ENODEV;
+-
+-      version = rtl8152_get_version(intf);
+-      if (version == RTL_VER_UNKNOWN)
+-              return -ENODEV;
+-
+       usb_reset_device(udev);
+       netdev = alloc_etherdev(sizeof(struct r8152));
+       if (!netdev) {
+@@ -9783,10 +9882,20 @@ static int rtl8152_probe(struct usb_inte
+       else
+               device_set_wakeup_enable(&udev->dev, false);
++      /* If we saw a control transfer error while probing then we may
++       * want to try probe() again. Consider this an error.
++       */
++      if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
++              goto out2;
++
++      set_bit(PROBED_WITH_NO_ERRORS, &tp->flags);
+       netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION);
+       return 0;
++out2:
++      unregister_netdev(netdev);
++
+ out1:
+       tasklet_kill(&tp->tx_tl);
+       cancel_delayed_work_sync(&tp->hw_phy_work);
+@@ -9795,10 +9904,46 @@ out1:
+       rtl8152_release_firmware(tp);
+       usb_set_intfdata(intf, NULL);
+ out:
++      if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
++              ret = -EAGAIN;
++
+       free_netdev(netdev);
+       return ret;
+ }
++#define RTL8152_PROBE_TRIES   3
++
++static int rtl8152_probe(struct usb_interface *intf,
++                       const struct usb_device_id *id)
++{
++      u8 version;
++      int ret;
++      int i;
++
++      if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
++              return -ENODEV;
++
++      if (!rtl_check_vendor_ok(intf))
++              return -ENODEV;
++
++      version = rtl8152_get_version(intf);
++      if (version == RTL_VER_UNKNOWN)
++              return -ENODEV;
++
++      for (i = 0; i < RTL8152_PROBE_TRIES; i++) {
++              ret = rtl8152_probe_once(intf, id, version);
++              if (ret != -EAGAIN)
++                      break;
++      }
++      if (ret == -EAGAIN) {
++              dev_err(&intf->dev,
++                      "r8152 failed probe after %d tries; giving up\n", i);
++              return -ENODEV;
++      }
++
++      return ret;
++}
++
+ static void rtl8152_disconnect(struct usb_interface *intf)
+ {
+       struct r8152 *tp = usb_get_intfdata(intf);
diff --git a/target/linux/generic/backport-6.1/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch b/target/linux/generic/backport-6.1/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch
new file mode 100644 (file)
index 0000000..7bbd1be
--- /dev/null
@@ -0,0 +1,83 @@
+From 66eee612a1ba39f9a76a9ace4a34d012044767fb Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 26 Sep 2023 19:17:13 +0800
+Subject: [PATCH] r8152: break the loop when the budget is exhausted
+
+[ Upstream commit 2cf51f931797d9a47e75d999d0993a68cbd2a560 ]
+
+A bulk transfer of the USB may contain many packets. And, the total
+number of the packets in the bulk transfer may be more than budget.
+
+Originally, only budget packets would be handled by napi_gro_receive(),
+and the other packets would be queued in the driver for next schedule.
+
+This patch would break the loop about getting next bulk transfer, when
+the budget is exhausted. That is, only the current bulk transfer would
+be handled, and the other bulk transfers would be queued for next
+schedule. Besides, the packets which are more than the budget in the
+current bulk trasnfer would be still queued in the driver, as the
+original method.
+
+In addition, a bulk transfer wouldn't contain more than 400 packets, so
+the check of queue length is unnecessary. Therefore, I replace it with
+WARN_ON_ONCE().
+
+Fixes: cf74eb5a5bc8 ("eth: r8152: try to use a normal budget")
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230926111714.9448-433-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/usb/r8152.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2542,7 +2542,7 @@ static int rx_bottom(struct r8152 *tp, i
+               }
+       }
+-      if (list_empty(&tp->rx_done))
++      if (list_empty(&tp->rx_done) || work_done >= budget)
+               goto out1;
+       clear_bit(RX_EPROTO, &tp->flags);
+@@ -2558,6 +2558,15 @@ static int rx_bottom(struct r8152 *tp, i
+               struct urb *urb;
+               u8 *rx_data;
++              /* A bulk transfer of USB may contain may packets, so the
++               * total packets may more than the budget. Deal with all
++               * packets in current bulk transfer, and stop to handle the
++               * next bulk transfer until next schedule, if budget is
++               * exhausted.
++               */
++              if (work_done >= budget)
++                      break;
++
+               list_del_init(cursor);
+               agg = list_entry(cursor, struct rx_agg, list);
+@@ -2577,9 +2586,7 @@ static int rx_bottom(struct r8152 *tp, i
+                       unsigned int pkt_len, rx_frag_head_sz;
+                       struct sk_buff *skb;
+-                      /* limit the skb numbers for rx_queue */
+-                      if (unlikely(skb_queue_len(&tp->rx_queue) >= 1000))
+-                              break;
++                      WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
+                       pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
+                       if (pkt_len < ETH_ZLEN)
+@@ -2657,9 +2664,10 @@ submit:
+               }
+       }
++      /* Splice the remained list back to rx_done for next schedule */
+       if (!list_empty(&rx_queue)) {
+               spin_lock_irqsave(&tp->rx_lock, flags);
+-              list_splice_tail(&rx_queue, &tp->rx_done);
++              list_splice(&rx_queue, &tp->rx_done);
+               spin_unlock_irqrestore(&tp->rx_lock, flags);
+       }
diff --git a/target/linux/generic/backport-6.1/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch b/target/linux/generic/backport-6.1/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch
new file mode 100644 (file)
index 0000000..c858152
--- /dev/null
@@ -0,0 +1,47 @@
+From 1b0fce8c8e69485e49a7d34aac3d4c2a2aa15d62 Mon Sep 17 00:00:00 2001
+From: Davide Tronchin <davide.tronchin.94@gmail.com>
+Date: Thu, 29 Jun 2023 12:37:36 +0200
+Subject: [PATCH] net: usb: cdc_ether: add u-blox 0x1313 composition.
+
+Add CDC-ECM support for LARA-R6 01B.
+
+The new LARA-R6 product variant identified by the "01B" string can be
+configured (by AT interface) in three different USB modes:
+* Default mode (Vendor ID: 0x1546 Product ID: 0x1311) with 4 serial
+interfaces
+* RmNet mode (Vendor ID: 0x1546 Product ID: 0x1312) with 4 serial
+interfaces and 1 RmNet virtual network interface
+* CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1313) with 4 serial
+interface and 1 CDC-ECM virtual network interface
+The first 4 interfaces of all the 3 configurations (default, RmNet, ECM)
+are the same.
+
+In CDC-ECM mode LARA-R6 01B exposes the following interfaces:
+If 0: Diagnostic
+If 1: AT parser
+If 2: AT parser
+If 3: AT parset/alternative functions
+If 4: CDC-ECM interface
+
+Signed-off-by: Davide Tronchin <davide.tronchin.94@gmail.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/cdc_ether.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/usb/cdc_ether.c
++++ b/drivers/net/usb/cdc_ether.c
+@@ -879,6 +879,12 @@ static const struct usb_device_id produc
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = (unsigned long)&wwan_info,
+ }, {
++      /* U-blox LARA-R6 01B */
++      USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1313, USB_CLASS_COMM,
++                                    USB_CDC_SUBCLASS_ETHERNET,
++                                    USB_CDC_PROTO_NONE),
++      .driver_info = (unsigned long)&wwan_info,
++}, {
+       /* ZTE modules */
+       USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
diff --git a/target/linux/generic/backport-6.1/795-v6.7-16-r8152-use-napi_gro_frags.patch b/target/linux/generic/backport-6.1/795-v6.7-16-r8152-use-napi_gro_frags.patch
new file mode 100644 (file)
index 0000000..3c9680a
--- /dev/null
@@ -0,0 +1,122 @@
+From 788d30daa8f97f06166b6a63f0e51f2a4c2f036a Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 26 Sep 2023 19:17:14 +0800
+Subject: [PATCH] r8152: use napi_gro_frags
+
+Use napi_gro_frags() for the skb of fragments when the work_done is less
+than budget.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230926111714.9448-434-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 67 ++++++++++++++++++++++++++++++-----------
+ 1 file changed, 50 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2583,8 +2583,9 @@ static int rx_bottom(struct r8152 *tp, i
+               while (urb->actual_length > len_used) {
+                       struct net_device *netdev = tp->netdev;
+                       struct net_device_stats *stats = &netdev->stats;
+-                      unsigned int pkt_len, rx_frag_head_sz;
++                      unsigned int pkt_len, rx_frag_head_sz, len;
+                       struct sk_buff *skb;
++                      bool use_frags;
+                       WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
+@@ -2597,45 +2598,77 @@ static int rx_bottom(struct r8152 *tp, i
+                               break;
+                       pkt_len -= ETH_FCS_LEN;
++                      len = pkt_len;
+                       rx_data += sizeof(struct rx_desc);
+-                      if (!agg_free || tp->rx_copybreak > pkt_len)
+-                              rx_frag_head_sz = pkt_len;
++                      if (!agg_free || tp->rx_copybreak > len)
++                              use_frags = false;
+                       else
+-                              rx_frag_head_sz = tp->rx_copybreak;
++                              use_frags = true;
++
++                      if (use_frags) {
++                              /* If the budget is exhausted, the packet
++                               * would be queued in the driver. That is,
++                               * napi_gro_frags() wouldn't be called, so
++                               * we couldn't use napi_get_frags().
++                               */
++                              if (work_done >= budget) {
++                                      rx_frag_head_sz = tp->rx_copybreak;
++                                      skb = napi_alloc_skb(napi,
++                                                           rx_frag_head_sz);
++                              } else {
++                                      rx_frag_head_sz = 0;
++                                      skb = napi_get_frags(napi);
++                              }
++                      } else {
++                              rx_frag_head_sz = 0;
++                              skb = napi_alloc_skb(napi, len);
++                      }
+-                      skb = napi_alloc_skb(napi, rx_frag_head_sz);
+                       if (!skb) {
+                               stats->rx_dropped++;
+                               goto find_next_rx;
+                       }
+                       skb->ip_summed = r8152_rx_csum(tp, rx_desc);
+-                      memcpy(skb->data, rx_data, rx_frag_head_sz);
+-                      skb_put(skb, rx_frag_head_sz);
+-                      pkt_len -= rx_frag_head_sz;
+-                      rx_data += rx_frag_head_sz;
+-                      if (pkt_len) {
++                      rtl_rx_vlan_tag(rx_desc, skb);
++
++                      if (use_frags) {
++                              if (rx_frag_head_sz) {
++                                      memcpy(skb->data, rx_data,
++                                             rx_frag_head_sz);
++                                      skb_put(skb, rx_frag_head_sz);
++                                      len -= rx_frag_head_sz;
++                                      rx_data += rx_frag_head_sz;
++                                      skb->protocol = eth_type_trans(skb,
++                                                                     netdev);
++                              }
++
+                               skb_add_rx_frag(skb, 0, agg->page,
+                                               agg_offset(agg, rx_data),
+-                                              pkt_len,
+-                                              SKB_DATA_ALIGN(pkt_len));
++                                              len, SKB_DATA_ALIGN(len));
+                               get_page(agg->page);
++                      } else {
++                              memcpy(skb->data, rx_data, len);
++                              skb_put(skb, len);
++                              skb->protocol = eth_type_trans(skb, netdev);
+                       }
+-                      skb->protocol = eth_type_trans(skb, netdev);
+-                      rtl_rx_vlan_tag(rx_desc, skb);
+                       if (work_done < budget) {
++                              if (use_frags)
++                                      napi_gro_frags(napi);
++                              else
++                                      napi_gro_receive(napi, skb);
++
+                               work_done++;
+                               stats->rx_packets++;
+-                              stats->rx_bytes += skb->len;
+-                              napi_gro_receive(napi, skb);
++                              stats->rx_bytes += pkt_len;
+                       } else {
+                               __skb_queue_tail(&tp->rx_queue, skb);
+                       }
+ find_next_rx:
+-                      rx_data = rx_agg_align(rx_data + pkt_len + ETH_FCS_LEN);
++                      rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
+                       rx_desc = (struct rx_desc *)rx_data;
+                       len_used = agg_offset(agg, rx_data);
+                       len_used += sizeof(struct rx_desc);
index 96525089ef5a9e5adc9fa79acbdec5b16221a228..26c5af8510e53c6fa546310945d50446e3b404cc 100644 (file)
@@ -22,7 +22,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  #include <linux/crc32.h>
  #include <linux/if_vlan.h>
  #include <linux/uaccess.h>
-@@ -6896,6 +6897,22 @@ static void rtl_tally_reset(struct r8152
+@@ -6980,6 +6981,22 @@ static void rtl_tally_reset(struct r8152
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
  }
  
@@ -45,7 +45,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  static void r8152b_init(struct r8152 *tp)
  {
        u32 ocp_data;
-@@ -6937,6 +6954,8 @@ static void r8152b_init(struct r8152 *tp
+@@ -7021,6 +7038,8 @@ static void r8152b_init(struct r8152 *tp
        ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
        ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
        ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
@@ -54,7 +54,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  }
  
  static void r8153_init(struct r8152 *tp)
-@@ -7077,6 +7096,8 @@ static void r8153_init(struct r8152 *tp)
+@@ -7161,6 +7180,8 @@ static void r8153_init(struct r8152 *tp)
                tp->coalesce = COALESCE_SLOW;
                break;
        }
@@ -63,7 +63,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  }
  
  static void r8153b_init(struct r8152 *tp)
-@@ -7159,6 +7180,8 @@ static void r8153b_init(struct r8152 *tp
+@@ -7243,6 +7264,8 @@ static void r8153b_init(struct r8152 *tp
        rtl_tally_reset(tp);
  
        tp->coalesce = 15000;   /* 15 us */
index 7b7fcca11f422bc055faad0468d568ffc95b8dfc..c842639792f12057a7c7012f80c1ddab5d2b8a6e 100644 (file)
@@ -22,7 +22,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  #include <linux/crc32.h>
  #include <linux/if_vlan.h>
  #include <linux/uaccess.h>
-@@ -6903,6 +6904,22 @@ static void rtl_tally_reset(struct r8152
+@@ -7020,6 +7021,22 @@ static void rtl_tally_reset(struct r8152
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
  }
  
@@ -45,7 +45,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  static void r8152b_init(struct r8152 *tp)
  {
        u32 ocp_data;
-@@ -6944,6 +6961,8 @@ static void r8152b_init(struct r8152 *tp
+@@ -7061,6 +7078,8 @@ static void r8152b_init(struct r8152 *tp
        ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
        ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
        ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
@@ -54,7 +54,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  }
  
  static void r8153_init(struct r8152 *tp)
-@@ -7084,6 +7103,8 @@ static void r8153_init(struct r8152 *tp)
+@@ -7201,6 +7220,8 @@ static void r8153_init(struct r8152 *tp)
                tp->coalesce = COALESCE_SLOW;
                break;
        }
@@ -63,7 +63,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
  }
  
  static void r8153b_init(struct r8152 *tp)
-@@ -7166,6 +7187,8 @@ static void r8153b_init(struct r8152 *tp
+@@ -7283,6 +7304,8 @@ static void r8153b_init(struct r8152 *tp
        rtl_tally_reset(tp);
  
        tp->coalesce = 15000;   /* 15 us */