diff mbox series

[net-next] r8152: support request_firmware for RTL8153

Message ID 1394712342-15778-329-Taiwan-albertk@realtek.com (mailing list archive)
State Mainlined
Commit 9370f2d05a2a150b0aa719a3070b26d478180df3
Headers show
Series [net-next] r8152: support request_firmware for RTL8153 | expand

Commit Message

Hayes Wang Oct. 16, 2019, 3:02 a.m. UTC
This patch supports loading additional firmware file through
request_firmware().

A firmware file may include a header followed by several blocks
which have different types of firmware. Currently, the supported
types are RTL_FW_END, RTL_FW_PLA, and RTL_FW_USB.

The firmware is used to fix some compatible or hardware issues. For
example, the device couldn't be found after rebooting several times.

The supported chips are
	RTL_VER_04 (rtl8153a-2.fw)
	RTL_VER_05 (rtl8153a-3.fw)
	RTL_VER_06 (rtl8153a-4.fw)
	RTL_VER_09 (rtl8153b-2.fw)

Signed-off-by: Hayes Wang <hayeswang@realtek.com>
Reviewed-by: Prashant Malani <pmalani@chromium.org>
---
 drivers/net/usb/r8152.c | 768 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 756 insertions(+), 12 deletions(-)

Comments

David Miller Oct. 16, 2019, 6:31 p.m. UTC | #1
From: Hayes Wang <hayeswang@realtek.com>
Date: Wed, 16 Oct 2019 11:02:42 +0800

> This patch supports loading additional firmware file through
> request_firmware().
> 
> A firmware file may include a header followed by several blocks
> which have different types of firmware. Currently, the supported
> types are RTL_FW_END, RTL_FW_PLA, and RTL_FW_USB.
> 
> The firmware is used to fix some compatible or hardware issues. For
> example, the device couldn't be found after rebooting several times.
> 
> The supported chips are
> 	RTL_VER_04 (rtl8153a-2.fw)
> 	RTL_VER_05 (rtl8153a-3.fw)
> 	RTL_VER_06 (rtl8153a-4.fw)
> 	RTL_VER_09 (rtl8153b-2.fw)
> 
> Signed-off-by: Hayes Wang <hayeswang@realtek.com>
> Reviewed-by: Prashant Malani <pmalani@chromium.org>

Applied, thank you.
Marek Szyprowski Oct. 23, 2019, 9:16 a.m. UTC | #2
Hi Hayes,

On 16.10.2019 05:02, Hayes Wang wrote:
> This patch supports loading additional firmware file through
> request_firmware().
>
> A firmware file may include a header followed by several blocks
> which have different types of firmware. Currently, the supported
> types are RTL_FW_END, RTL_FW_PLA, and RTL_FW_USB.
>
> The firmware is used to fix some compatible or hardware issues. For
> example, the device couldn't be found after rebooting several times.
>
> The supported chips are
> 	RTL_VER_04 (rtl8153a-2.fw)
> 	RTL_VER_05 (rtl8153a-3.fw)
> 	RTL_VER_06 (rtl8153a-4.fw)
> 	RTL_VER_09 (rtl8153b-2.fw)
>
> Signed-off-by: Hayes Wang <hayeswang@realtek.com>
> Reviewed-by: Prashant Malani <pmalani@chromium.org>

This patch (which landed in linux-next last days) causes a following 
kernel oops on the ARM 32bit Exynos5422 SoC based Odroid XU4 board:

usb 6-1: new SuperSpeed Gen 1 USB device number 2 using xhci-hcd
usb 6-1: New USB device found, idVendor=0bda, idProduct=8153, 
bcdDevice=30.00
usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=6
usb 6-1: Product: USB 10/100/1000 LAN
usb 6-1: Manufacturer: Realtek
usb 6-1: SerialNumber: 000001000000
usb 6-1: reset SuperSpeed Gen 1 USB device number 2 using xhci-hcd
r8152 6-1:1.0: Direct firmware load for rtl_nic/rtl8153a-3.fw failed 
with error -2
r8152 6-1:1.0: unable to load firmware patch rtl_nic/rtl8153a-3.fw (-2)
8<--- cut here ---
Unable to handle kernel NULL pointer dereference at virtual address 00000002
pgd = (ptrval)
[00000002] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 0 PID: 131 Comm: kworker/0:2 Not tainted 
5.4.0-rc1-00539-g9370f2d05a2a #6788
Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
Workqueue: events_long rtl_hw_phy_work_func_t
PC is at rtl8152_apply_firmware+0x14/0x464
LR is at r8153_hw_phy_cfg+0x24/0x17c
pc : [<c064f4e4>]    lr : [<c064fa18>]    psr: a0000013
sp : e75c9e60  ip : 60000013  fp : c11b7614
r10: e883b91c  r9 : 00000000  r8 : fffffffe
r7 : e883b640  r6 : fffffffe  r5 : fffffffe  r4 : e883b640
r3 : 736cfe7c  r2 : 736cfe7c  r1 : 000052f8  r0 : e883b640
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 6640006a  DAC: 00000051
Process kworker/0:2 (pid: 131, stack limit = 0x(ptrval))
Stack: (0xe75c9e60 to 0xe75ca000)
...
[<c064f4e4>] (rtl8152_apply_firmware) from [<c064fa18>] 
(r8153_hw_phy_cfg+0x24/0x17c)
[<c064fa18>] (r8153_hw_phy_cfg) from [<c064e784>] 
(rtl_hw_phy_work_func_t+0x220/0x3e4)
[<c064e784>] (rtl_hw_phy_work_func_t) from [<c0148a74>] 
(process_one_work+0x22c/0x7c8)
[<c0148a74>] (process_one_work) from [<c0149054>] (worker_thread+0x44/0x520)
[<c0149054>] (worker_thread) from [<c0150548>] (kthread+0x130/0x164)
[<c0150548>] (kthread) from [<c01010b4>] (ret_from_fork+0x14/0x20)
Exception stack(0xe75c9fb0 to 0xe75c9ff8)
...
---[ end trace 0995684a5160c8b5 ]---

I don't have firmware files for that chip, but the driver shouldn't 
crash in such case.


> ---
>   drivers/net/usb/r8152.c | 768 +++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 756 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
> index 54a83f734ede..55d0bcb00aef 100644
> --- a/drivers/net/usb/r8152.c
> +++ b/drivers/net/usb/r8152.c
> @@ -24,9 +24,11 @@
>   #include <linux/suspend.h>
>   #include <linux/atomic.h>
>   #include <linux/acpi.h>
> +#include <linux/firmware.h>
> +#include <crypto/hash.h>
>   
>   /* Information for net-next */
> -#define NETNEXT_VERSION		"10"
> +#define NETNEXT_VERSION		"11"
>   
>   /* Information for net */
>   #define NET_VERSION		"10"
> @@ -54,8 +56,11 @@
>   #define PLA_BDC_CR		0xd1a0
>   #define PLA_TEREDO_TIMER	0xd2cc
>   #define PLA_REALWOW_TIMER	0xd2e8
> +#define PLA_UPHY_TIMER		0xd388
>   #define PLA_SUSPEND_FLAG	0xd38a
>   #define PLA_INDICATE_FALG	0xd38c
> +#define PLA_MACDBG_PRE		0xd38c	/* RTL_VER_04 only */
> +#define PLA_MACDBG_POST		0xd38e	/* RTL_VER_04 only */
>   #define PLA_EXTRA_STATUS	0xd398
>   #define PLA_EFUSE_DATA		0xdd00
>   #define PLA_EFUSE_CMD		0xdd02
> @@ -110,7 +115,12 @@
>   #define USB_CONNECT_TIMER	0xcbf8
>   #define USB_MSC_TIMER		0xcbfc
>   #define USB_BURST_SIZE		0xcfc0
> +#define USB_FW_FIX_EN0		0xcfca
> +#define USB_FW_FIX_EN1		0xcfcc
>   #define USB_LPM_CONFIG		0xcfd8
> +#define USB_CSTMR		0xcfef	/* RTL8153A */
> +#define USB_FW_CTRL		0xd334	/* RTL8153B */
> +#define USB_FC_TIMER		0xd340
>   #define USB_USB_CTRL		0xd406
>   #define USB_PHY_CTRL		0xd408
>   #define USB_TX_AGG		0xd40a
> @@ -126,6 +136,7 @@
>   #define USB_LPM_CTRL		0xd41a
>   #define USB_BMU_RESET		0xd4b0
>   #define USB_U1U2_TIMER		0xd4da
> +#define USB_FW_TASK		0xd4e8	/* RTL8153B */
>   #define USB_UPS_CTRL		0xd800
>   #define USB_POWER_CUT		0xd80a
>   #define USB_MISC_0		0xd81a
> @@ -133,18 +144,19 @@
>   #define USB_AFE_CTRL2		0xd824
>   #define USB_UPS_CFG		0xd842
>   #define USB_UPS_FLAGS		0xd848
> +#define USB_WDT1_CTRL		0xe404
>   #define USB_WDT11_CTRL		0xe43c
> -#define USB_BP_BA		0xfc26
> -#define USB_BP_0		0xfc28
> -#define USB_BP_1		0xfc2a
> -#define USB_BP_2		0xfc2c
> -#define USB_BP_3		0xfc2e
> -#define USB_BP_4		0xfc30
> -#define USB_BP_5		0xfc32
> -#define USB_BP_6		0xfc34
> -#define USB_BP_7		0xfc36
> -#define USB_BP_EN		0xfc38
> -#define USB_BP_8		0xfc38
> +#define USB_BP_BA		PLA_BP_BA
> +#define USB_BP_0		PLA_BP_0
> +#define USB_BP_1		PLA_BP_1
> +#define USB_BP_2		PLA_BP_2
> +#define USB_BP_3		PLA_BP_3
> +#define USB_BP_4		PLA_BP_4
> +#define USB_BP_5		PLA_BP_5
> +#define USB_BP_6		PLA_BP_6
> +#define USB_BP_7		PLA_BP_7
> +#define USB_BP_EN		PLA_BP_EN	/* RTL8153A */
> +#define USB_BP_8		0xfc38		/* RTL8153B */
>   #define USB_BP_9		0xfc3a
>   #define USB_BP_10		0xfc3c
>   #define USB_BP_11		0xfc3e
> @@ -346,7 +358,12 @@
>   /* PLA_INDICATE_FALG */
>   #define UPCOMING_RUNTIME_D3	BIT(0)
>   
> +/* PLA_MACDBG_PRE and PLA_MACDBG_POST */
> +#define DEBUG_OE		BIT(0)
> +#define DEBUG_LTSSM		0x0082
> +
>   /* PLA_EXTRA_STATUS */
> +#define U3P3_CHECK_EN		BIT(7)	/* RTL_VER_05 only */
>   #define LINK_CHANGE_FLAG	BIT(8)
>   
>   /* USB_USB2PHY */
> @@ -368,6 +385,12 @@
>   #define STAT_SPEED_HIGH		0x0000
>   #define STAT_SPEED_FULL		0x0002
>   
> +/* USB_FW_FIX_EN0 */
> +#define FW_FIX_SUSPEND		BIT(14)
> +
> +/* USB_FW_FIX_EN1 */
> +#define FW_IP_RESET_EN		BIT(9)
> +
>   /* USB_LPM_CONFIG */
>   #define LPM_U1U2_EN		BIT(0)
>   
> @@ -392,12 +415,24 @@
>   #define OWN_UPDATE		BIT(0)
>   #define OWN_CLEAR		BIT(1)
>   
> +/* USB_FW_TASK */
> +#define FC_PATCH_TASK		BIT(1)
> +
>   /* USB_UPS_CTRL */
>   #define POWER_CUT		0x0100
>   
>   /* USB_PM_CTRL_STATUS */
>   #define RESUME_INDICATE		0x0001
>   
> +/* USB_CSTMR */
> +#define FORCE_SUPER		BIT(0)
> +
> +/* USB_FW_CTRL */
> +#define FLOW_CTRL_PATCH_OPT	BIT(1)
> +
> +/* USB_FC_TIMER */
> +#define CTRL_TIMER_EN		BIT(15)
> +
>   /* USB_USB_CTRL */
>   #define RX_AGG_DISABLE		0x0010
>   #define RX_ZERO_EN		0x0080
> @@ -419,6 +454,9 @@
>   #define COALESCE_HIGH		250000U
>   #define COALESCE_SLOW		524280U
>   
> +/* USB_WDT1_CTRL */
> +#define WTD1_EN			BIT(0)
> +
>   /* USB_WDT11_CTRL */
>   #define TIMER11_EN		0x0001
>   
> @@ -570,6 +608,8 @@ enum spd_duplex {
>   #define EFUSE			0xcfdb
>   #define PASS_THRU_MASK		0x1
>   
> +#define BP4_SUPER_ONLY		0x1578	/* RTL_VER_04 only */
> +
>   enum rtl_register_content {
>   	_1000bps	= 0x10,
>   	_100bps		= 0x08,
> @@ -766,6 +806,19 @@ struct r8152 {
>   		u32 ctap_short_off:1;
>   	} ups_info;
>   
> +#define RTL_VER_SIZE		32
> +
> +	struct rtl_fw {
> +		const char *fw_name;
> +		const struct firmware *fw;
> +
> +		char version[RTL_VER_SIZE];
> +		int (*pre_fw)(struct r8152 *tp);
> +		int (*post_fw)(struct r8152 *tp);
> +
> +		bool retry;
> +	} rtl_fw;
> +
>   	atomic_t rx_count;
>   
>   	bool eee_en;
> @@ -788,6 +841,76 @@ struct r8152 {
>   	u8 autoneg;
>   };
>   
> +/**
> + * struct fw_block - block type and total length
> + * @type: type of the current block, such as RTL_FW_END, RTL_FW_PLA,
> + *	RTL_FW_USB and so on.
> + * @length: total length of the current block.
> + */
> +struct fw_block {
> +	__le32 type;
> +	__le32 length;
> +} __packed;
> +
> +/**
> + * struct fw_header - header of the firmware file
> + * @checksum: checksum of sha256 which is calculated from the whole file
> + *	except the checksum field of the file. That is, calculate sha256
> + *	from the version field to the end of the file.
> + * @version: version of this firmware.
> + * @blocks: the first firmware block of the file
> + */
> +struct fw_header {
> +	u8 checksum[32];
> +	char version[RTL_VER_SIZE];
> +	struct fw_block blocks[0];
> +} __packed;
> +
> +/**
> + * struct fw_type_1 - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
> + *	The layout of the firmware block is:
> + *	<struct fw_type_1> + <info> + <firmware data>.
> + * @fw_offset: offset of the firmware binary data. The start address of
> + *	the data would be the address of struct fw_type_1 + @fw_offset.
> + * @fw_reg: the register to load the firmware. Depends on chip.
> + * @bp_ba_addr: the register to write break point base address. Depends on
> + *	chip.
> + * @bp_ba_value: break point base address. Depends on chip.
> + * @bp_en_addr: the register to write break point enabled mask. Depends
> + *	on chip.
> + * @bp_en_value: break point enabled mask. Depends on the firmware.
> + * @bp_start: the start register of break points. Depends on chip.
> + * @bp_num: the break point number which needs to be set for this firmware.
> + *	Depends on the firmware.
> + * @bp: break points. Depends on firmware.
> + * @fw_ver_reg: the register to store the fw version.
> + * @fw_ver_data: the firmware version of the current type.
> + * @info: additional information for debugging, and is followed by the
> + *	binary data of firmware.
> + */
> +struct fw_type_1 {
> +	struct fw_block blk_hdr;
> +	__le16 fw_offset;
> +	__le16 fw_reg;
> +	__le16 bp_ba_addr;
> +	__le16 bp_ba_value;
> +	__le16 bp_en_addr;
> +	__le16 bp_en_value;
> +	__le16 bp_start;
> +	__le16 bp_num;
> +	__le16 bp[16]; /* any value determined by firmware */
> +	__le32 reserved;
> +	__le16 fw_ver_reg;
> +	u8 fw_ver_data;
> +	char info[0];
> +} __packed;
> +
> +enum rtl_fw_type {
> +	RTL_FW_END = 0,
> +	RTL_FW_PLA,
> +	RTL_FW_USB,
> +};
> +
>   enum rtl_version {
>   	RTL_VER_UNKNOWN = 0,
>   	RTL_VER_01,
> @@ -3226,6 +3349,435 @@ static void rtl_reset_bmu(struct r8152 *tp)
>   	ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
>   }
>   
> +/* 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_08:
> +	case RTL_VER_09:
> +	default:
> +		if (type == MCU_TYPE_USB) {
> +			ocp_write_byte(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);
> +		}
> +		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);
> +
> +	/* wait 3 ms to make sure the firmware is stopped */
> +	usleep_range(3000, 6000);
> +	ocp_write_word(tp, type, PLA_BP_BA, 0);
> +}
> +
> +static bool rtl8152_is_fw_type1_ok(struct r8152 *tp, struct fw_type_1 *type1)
> +{
> +	u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start;
> +	bool rc = false;
> +	u32 length, type;
> +	int i, max_bp;
> +
> +	type = __le32_to_cpu(type1->blk_hdr.type);
> +	if (type == RTL_FW_PLA) {
> +		switch (tp->version) {
> +		case RTL_VER_01:
> +		case RTL_VER_02:
> +		case RTL_VER_07:
> +			fw_reg = 0xf800;
> +			bp_ba_addr = PLA_BP_BA;
> +			bp_en_addr = 0;
> +			bp_start = PLA_BP_0;
> +			max_bp = 8;
> +			break;
> +		case RTL_VER_03:
> +		case RTL_VER_04:
> +		case RTL_VER_05:
> +		case RTL_VER_06:
> +		case RTL_VER_08:
> +		case RTL_VER_09:
> +			fw_reg = 0xf800;
> +			bp_ba_addr = PLA_BP_BA;
> +			bp_en_addr = PLA_BP_EN;
> +			bp_start = PLA_BP_0;
> +			max_bp = 8;
> +			break;
> +		default:
> +			goto out;
> +		}
> +	} else if (type == RTL_FW_USB) {
> +		switch (tp->version) {
> +		case RTL_VER_03:
> +		case RTL_VER_04:
> +		case RTL_VER_05:
> +		case RTL_VER_06:
> +			fw_reg = 0xf800;
> +			bp_ba_addr = USB_BP_BA;
> +			bp_en_addr = USB_BP_EN;
> +			bp_start = USB_BP_0;
> +			max_bp = 8;
> +			break;
> +		case RTL_VER_08:
> +		case RTL_VER_09:
> +			fw_reg = 0xe600;
> +			bp_ba_addr = USB_BP_BA;
> +			bp_en_addr = USB_BP2_EN;
> +			bp_start = USB_BP_0;
> +			max_bp = 16;
> +			break;
> +		case RTL_VER_01:
> +		case RTL_VER_02:
> +		case RTL_VER_07:
> +		default:
> +			goto out;
> +		}
> +	} else {
> +		goto out;
> +	}
> +
> +	length = __le32_to_cpu(type1->blk_hdr.length);
> +	if (length < __le16_to_cpu(type1->fw_offset)) {
> +		dev_err(&tp->intf->dev, "invalid fw_offset\n");
> +		goto out;
> +	}
> +
> +	length -= __le16_to_cpu(type1->fw_offset);
> +	if (length < 4 || (length & 3)) {
> +		dev_err(&tp->intf->dev, "invalid block length\n");
> +		goto out;
> +	}
> +
> +	if (__le16_to_cpu(type1->fw_reg) != fw_reg) {
> +		dev_err(&tp->intf->dev, "invalid register to load firmware\n");
> +		goto out;
> +	}
> +
> +	if (__le16_to_cpu(type1->bp_ba_addr) != bp_ba_addr) {
> +		dev_err(&tp->intf->dev, "invalid base address register\n");
> +		goto out;
> +	}
> +
> +	if (__le16_to_cpu(type1->bp_en_addr) != bp_en_addr) {
> +		dev_err(&tp->intf->dev, "invalid enabled mask register\n");
> +		goto out;
> +	}
> +
> +	if (__le16_to_cpu(type1->bp_start) != bp_start) {
> +		dev_err(&tp->intf->dev,
> +			"invalid start register of break point\n");
> +		goto out;
> +	}
> +
> +	if (__le16_to_cpu(type1->bp_num) > max_bp) {
> +		dev_err(&tp->intf->dev, "invalid break point number\n");
> +		goto out;
> +	}
> +
> +	for (i = __le16_to_cpu(type1->bp_num); i < max_bp; i++) {
> +		if (type1->bp[i]) {
> +			dev_err(&tp->intf->dev, "unused bp%u is not zero\n", i);
> +			goto out;
> +		}
> +	}
> +
> +	rc = true;
> +out:
> +	return rc;
> +}
> +
> +/* Verify the checksum for the firmware file. It is calculated from the version
> + * field to the end of the file. Compare the result with the checksum field to
> + * make sure the file is correct.
> + */
> +static long rtl8152_fw_verify_checksum(struct r8152 *tp,
> +				       struct fw_header *fw_hdr, size_t size)
> +{
> +	unsigned char checksum[sizeof(fw_hdr->checksum)];
> +	struct crypto_shash *alg;
> +	struct shash_desc *sdesc;
> +	size_t len;
> +	long rc;
> +
> +	alg = crypto_alloc_shash("sha256", 0, 0);
> +	if (IS_ERR(alg)) {
> +		rc = PTR_ERR(alg);
> +		goto out;
> +	}
> +
> +	if (crypto_shash_digestsize(alg) != sizeof(fw_hdr->checksum)) {
> +		rc = -EFAULT;
> +		dev_err(&tp->intf->dev, "digestsize incorrect (%u)\n",
> +			crypto_shash_digestsize(alg));
> +		goto free_shash;
> +	}
> +
> +	len = sizeof(*sdesc) + crypto_shash_descsize(alg);
> +	sdesc = kmalloc(len, GFP_KERNEL);
> +	if (!sdesc) {
> +		rc = -ENOMEM;
> +		goto free_shash;
> +	}
> +	sdesc->tfm = alg;
> +
> +	len = size - sizeof(fw_hdr->checksum);
> +	rc = crypto_shash_digest(sdesc, fw_hdr->version, len, checksum);
> +	kfree(sdesc);
> +	if (rc)
> +		goto free_shash;
> +
> +	if (memcmp(fw_hdr->checksum, checksum, sizeof(fw_hdr->checksum))) {
> +		dev_err(&tp->intf->dev, "checksum fail\n");
> +		rc = -EFAULT;
> +	}
> +
> +free_shash:
> +	crypto_free_shash(alg);
> +out:
> +	return rc;
> +}
> +
> +static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw)
> +{
> +	const struct firmware *fw = rtl_fw->fw;
> +	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
> +	struct fw_type_1 *pla = NULL, *usb = NULL;
> +	long ret = -EFAULT;
> +	int i;
> +
> +	if (fw->size < sizeof(*fw_hdr)) {
> +		dev_err(&tp->intf->dev, "file too small\n");
> +		goto fail;
> +	}
> +
> +	ret = rtl8152_fw_verify_checksum(tp, fw_hdr, fw->size);
> +	if (ret)
> +		goto fail;
> +
> +	ret = -EFAULT;
> +
> +	for (i = sizeof(*fw_hdr); i < fw->size;) {
> +		struct fw_block *block = (struct fw_block *)&fw->data[i];
> +		u32 type;
> +
> +		if ((i + sizeof(*block)) > fw->size)
> +			goto fail;
> +
> +		type = __le32_to_cpu(block->type);
> +		switch (type) {
> +		case RTL_FW_END:
> +			if (__le32_to_cpu(block->length) != sizeof(*block))
> +				goto fail;
> +			goto success;
> +		case RTL_FW_PLA:
> +			if (pla) {
> +				dev_err(&tp->intf->dev,
> +					"multiple PLA firmware encountered");
> +				goto fail;
> +			}
> +
> +			pla = (struct fw_type_1 *)block;
> +			if (!rtl8152_is_fw_type1_ok(tp, pla)) {
> +				dev_err(&tp->intf->dev,
> +					"load PLA firmware failed\n");
> +				goto fail;
> +			}
> +			break;
> +		case RTL_FW_USB:
> +			if (usb) {
> +				dev_err(&tp->intf->dev,
> +					"multiple USB firmware encountered");
> +				goto fail;
> +			}
> +
> +			usb = (struct fw_type_1 *)block;
> +			if (!rtl8152_is_fw_type1_ok(tp, usb)) {
> +				dev_err(&tp->intf->dev,
> +					"load USB firmware failed\n");
> +				goto fail;
> +			}
> +			break;
> +		default:
> +			dev_warn(&tp->intf->dev, "Unknown type %u is found\n",
> +				 type);
> +			break;
> +		}
> +
> +		/* next block */
> +		i += ALIGN(__le32_to_cpu(block->length), 8);
> +	}
> +
> +success:
> +	return 0;
> +fail:
> +	return ret;
> +}
> +
> +static void rtl8152_fw_type_1_apply(struct r8152 *tp, struct fw_type_1 *type1)
> +{
> +	u16 bp_en_addr, bp_index, type, bp_num, fw_ver_reg;
> +	u32 length;
> +	u8 *data;
> +	int i;
> +
> +	switch (__le32_to_cpu(type1->blk_hdr.type)) {
> +	case RTL_FW_PLA:
> +		type = MCU_TYPE_PLA;
> +		break;
> +	case RTL_FW_USB:
> +		type = MCU_TYPE_USB;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	rtl_clear_bp(tp, type);
> +
> +	/* Enable backup/restore of MACDBG. This is required after clearing PLA
> +	 * break points and before applying the PLA firmware.
> +	 */
> +	if (tp->version == RTL_VER_04 && type == MCU_TYPE_PLA &&
> +	    !(ocp_read_word(tp, MCU_TYPE_PLA, PLA_MACDBG_POST) & DEBUG_OE)) {
> +		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MACDBG_PRE, DEBUG_LTSSM);
> +		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MACDBG_POST, DEBUG_LTSSM);
> +	}
> +
> +	length = __le32_to_cpu(type1->blk_hdr.length);
> +	length -= __le16_to_cpu(type1->fw_offset);
> +
> +	data = (u8 *)type1;
> +	data += __le16_to_cpu(type1->fw_offset);
> +
> +	generic_ocp_write(tp, __le16_to_cpu(type1->fw_reg), 0xff, length, data,
> +			  type);
> +
> +	ocp_write_word(tp, type, __le16_to_cpu(type1->bp_ba_addr),
> +		       __le16_to_cpu(type1->bp_ba_value));
> +
> +	bp_index = __le16_to_cpu(type1->bp_start);
> +	bp_num = __le16_to_cpu(type1->bp_num);
> +	for (i = 0; i < bp_num; i++) {
> +		ocp_write_word(tp, type, bp_index, __le16_to_cpu(type1->bp[i]));
> +		bp_index += 2;
> +	}
> +
> +	bp_en_addr = __le16_to_cpu(type1->bp_en_addr);
> +	if (bp_en_addr)
> +		ocp_write_word(tp, type, bp_en_addr,
> +			       __le16_to_cpu(type1->bp_en_value));
> +
> +	fw_ver_reg = __le16_to_cpu(type1->fw_ver_reg);
> +	if (fw_ver_reg)
> +		ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg,
> +			       type1->fw_ver_data);
> +
> +	dev_dbg(&tp->intf->dev, "successfully applied %s\n", type1->info);
> +}
> +
> +static void rtl8152_apply_firmware(struct r8152 *tp)
> +{
> +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> +	const struct firmware *fw = rtl_fw->fw;
> +	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
> +	int i;
> +
> +	if (IS_ERR_OR_NULL(rtl_fw->fw))
> +		return;
> +
> +	if (rtl_fw->pre_fw)
> +		rtl_fw->pre_fw(tp);
> +
> +	for (i = offsetof(struct fw_header, blocks); i < fw->size;) {
> +		struct fw_block *block = (struct fw_block *)&fw->data[i];
> +
> +		switch (__le32_to_cpu(block->type)) {
> +		case RTL_FW_END:
> +			goto post_fw;
> +		case RTL_FW_PLA:
> +		case RTL_FW_USB:
> +			rtl8152_fw_type_1_apply(tp, (struct fw_type_1 *)block);
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		i += ALIGN(__le32_to_cpu(block->length), 8);
> +	}
> +
> +post_fw:
> +	if (rtl_fw->post_fw)
> +		rtl_fw->post_fw(tp);
> +
> +	strscpy(rtl_fw->version, fw_hdr->version, RTL_VER_SIZE);
> +	dev_info(&tp->intf->dev, "load %s successfully\n", rtl_fw->version);
> +}
> +
> +static void rtl8152_release_firmware(struct r8152 *tp)
> +{
> +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> +
> +	if (!IS_ERR_OR_NULL(rtl_fw->fw)) {
> +		release_firmware(rtl_fw->fw);
> +		rtl_fw->fw = NULL;
> +	}
> +}
> +
> +static int rtl8152_request_firmware(struct r8152 *tp)
> +{
> +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> +	long rc;
> +
> +	if (rtl_fw->fw || !rtl_fw->fw_name) {
> +		dev_info(&tp->intf->dev, "skip request firmware\n");
> +		rc = 0;
> +		goto result;
> +	}
> +
> +	rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, &tp->intf->dev);
> +	if (rc < 0)
> +		goto result;
> +
> +	rc = rtl8152_check_firmware(tp, rtl_fw);
> +	if (rc < 0)
> +		release_firmware(rtl_fw->fw);
> +
> +result:
> +	if (rc) {
> +		rtl_fw->fw = ERR_PTR(rc);
> +
> +		dev_warn(&tp->intf->dev,
> +			 "unable to load firmware patch %s (%ld)\n",
> +			 rtl_fw->fw_name, rc);
> +	}
> +
> +	return rc;
> +}
> +
>   static void r8152_aldps_en(struct r8152 *tp, bool enable)
>   {
>   	if (enable) {
> @@ -3370,6 +3922,7 @@ static void rtl8152_disable(struct r8152 *tp)
>   
>   static void r8152b_hw_phy_cfg(struct r8152 *tp)
>   {
> +	rtl8152_apply_firmware(tp);
>   	rtl_eee_enable(tp, tp->eee_en);
>   	r8152_aldps_en(tp, true);
>   	r8152b_enable_fc(tp);
> @@ -3524,6 +4077,126 @@ static int r8153_patch_request(struct r8152 *tp, bool request)
>   	}
>   }
>   
> +static int r8153_pre_firmware_1(struct r8152 *tp)
> +{
> +	int i;
> +
> +	/* Wait till the WTD timer is ready. It would take at most 104 ms. */
> +	for (i = 0; i < 104; i++) {
> +		u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_WDT1_CTRL);
> +
> +		if (!(ocp_data & WTD1_EN))
> +			break;
> +		usleep_range(1000, 2000);
> +	}
> +
> +	return 0;
> +}
> +
> +static int r8153_post_firmware_1(struct r8152 *tp)
> +{
> +	/* set USB_BP_4 to support USB_SPEED_SUPER only */
> +	if (ocp_read_byte(tp, MCU_TYPE_USB, USB_CSTMR) & FORCE_SUPER)
> +		ocp_write_word(tp, MCU_TYPE_USB, USB_BP_4, BP4_SUPER_ONLY);
> +
> +	/* reset UPHY timer to 36 ms */
> +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_UPHY_TIMER, 36000 / 16);
> +
> +	return 0;
> +}
> +
> +static int r8153_pre_firmware_2(struct r8152 *tp)
> +{
> +	u32 ocp_data;
> +
> +	r8153_pre_firmware_1(tp);
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0);
> +	ocp_data &= ~FW_FIX_SUSPEND;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0, ocp_data);
> +
> +	return 0;
> +}
> +
> +static int r8153_post_firmware_2(struct r8152 *tp)
> +{
> +	u32 ocp_data;
> +
> +	/* enable bp0 if support USB_SPEED_SUPER only */
> +	if (ocp_read_byte(tp, MCU_TYPE_USB, USB_CSTMR) & FORCE_SUPER) {
> +		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BP_EN);
> +		ocp_data |= BIT(0);
> +		ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, ocp_data);
> +	}
> +
> +	/* reset UPHY timer to 36 ms */
> +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_UPHY_TIMER, 36000 / 16);
> +
> +	/* enable U3P3 check, set the counter to 4 */
> +	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, U3P3_CHECK_EN | 4);
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0);
> +	ocp_data |= FW_FIX_SUSPEND;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0, ocp_data);
> +
> +	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY);
> +	ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND;
> +	ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data);
> +
> +	return 0;
> +}
> +
> +static int r8153_post_firmware_3(struct r8152 *tp)
> +{
> +	u32 ocp_data;
> +
> +	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY);
> +	ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND;
> +	ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data);
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
> +	ocp_data |= FW_IP_RESET_EN;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
> +
> +	return 0;
> +}
> +
> +static int r8153b_pre_firmware_1(struct r8152 *tp)
> +{
> +	/* enable fc timer and set timer to 1 second. */
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
> +		       CTRL_TIMER_EN | (1000 / 8));
> +
> +	return 0;
> +}
> +
> +static int r8153b_post_firmware_1(struct r8152 *tp)
> +{
> +	u32 ocp_data;
> +
> +	/* enable bp0 for RTL8153-BND */
> +	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1);
> +	if (ocp_data & BND_MASK) {
> +		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BP_EN);
> +		ocp_data |= BIT(0);
> +		ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, ocp_data);
> +	}
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
> +	ocp_data |= FLOW_CTRL_PATCH_OPT;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
> +	ocp_data |= FC_PATCH_TASK;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
> +
> +	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
> +	ocp_data |= FW_IP_RESET_EN;
> +	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
> +
> +	return 0;
> +}
> +
>   static void r8153_aldps_en(struct r8152 *tp, bool enable)
>   {
>   	u16 data;
> @@ -3558,6 +4231,8 @@ static void r8153_hw_phy_cfg(struct r8152 *tp)
>   	/* disable EEE before updating the PHY parameters */
>   	rtl_eee_enable(tp, false);
>   
> +	rtl8152_apply_firmware(tp);
> +
>   	if (tp->version == RTL_VER_03) {
>   		data = ocp_reg_read(tp, OCP_EEE_CFG);
>   		data &= ~CTAP_SHORT_EN;
> @@ -3630,6 +4305,8 @@ static void r8153b_hw_phy_cfg(struct r8152 *tp)
>   	/* disable EEE before updating the PHY parameters */
>   	rtl_eee_enable(tp, false);
>   
> +	rtl8152_apply_firmware(tp);
> +
>   	r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
>   
>   	data = sram_read(tp, SRAM_GREEN_CFG);
> @@ -4156,11 +4833,22 @@ static void rtl_hw_phy_work_func_t(struct work_struct *work)
>   
>   	mutex_lock(&tp->control);
>   
> +	if (rtl8152_request_firmware(tp) == -ENODEV && tp->rtl_fw.retry) {
> +		tp->rtl_fw.retry = false;
> +		tp->rtl_fw.fw = NULL;
> +
> +		/* Delay execution in case request_firmware() is not ready yet.
> +		 */
> +		queue_delayed_work(system_long_wq, &tp->hw_phy_work, HZ * 10);
> +		goto ignore_once;
> +	}
> +
>   	tp->rtl_ops.hw_phy_cfg(tp);
>   
>   	rtl8152_set_speed(tp, tp->autoneg, tp->speed, tp->duplex,
>   			  tp->advertising);
>   
> +ignore_once:
>   	mutex_unlock(&tp->control);
>   
>   	usb_autopm_put_interface(tp->intf);
> @@ -4198,6 +4886,11 @@ static int rtl8152_open(struct net_device *netdev)
>   	struct r8152 *tp = netdev_priv(netdev);
>   	int res = 0;
>   
> +	if (work_busy(&tp->hw_phy_work.work) & WORK_BUSY_PENDING) {
> +		cancel_delayed_work_sync(&tp->hw_phy_work);
> +		rtl_hw_phy_work_func_t(&tp->hw_phy_work.work);
> +	}
> +
>   	res = alloc_all_mem(tp);
>   	if (res)
>   		goto out;
> @@ -4844,6 +5537,9 @@ static void rtl8152_get_drvinfo(struct net_device *netdev,
>   	strlcpy(info->driver, MODULENAME, sizeof(info->driver));
>   	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
>   	usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info));
> +	if (!IS_ERR_OR_NULL(tp->rtl_fw.fw))
> +		strlcpy(info->fw_version, tp->rtl_fw.version,
> +			sizeof(info->fw_version));
>   }
>   
>   static
> @@ -5468,6 +6164,47 @@ static int rtl_ops_init(struct r8152 *tp)
>   	return ret;
>   }
>   
> +#define FIRMWARE_8153A_2	"rtl_nic/rtl8153a-2.fw"
> +#define FIRMWARE_8153A_3	"rtl_nic/rtl8153a-3.fw"
> +#define FIRMWARE_8153A_4	"rtl_nic/rtl8153a-4.fw"
> +#define FIRMWARE_8153B_2	"rtl_nic/rtl8153b-2.fw"
> +
> +MODULE_FIRMWARE(FIRMWARE_8153A_2);
> +MODULE_FIRMWARE(FIRMWARE_8153A_3);
> +MODULE_FIRMWARE(FIRMWARE_8153A_4);
> +MODULE_FIRMWARE(FIRMWARE_8153B_2);
> +
> +static int rtl_fw_init(struct r8152 *tp)
> +{
> +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> +
> +	switch (tp->version) {
> +	case RTL_VER_04:
> +		rtl_fw->fw_name		= FIRMWARE_8153A_2;
> +		rtl_fw->pre_fw		= r8153_pre_firmware_1;
> +		rtl_fw->post_fw		= r8153_post_firmware_1;
> +		break;
> +	case RTL_VER_05:
> +		rtl_fw->fw_name		= FIRMWARE_8153A_3;
> +		rtl_fw->pre_fw		= r8153_pre_firmware_2;
> +		rtl_fw->post_fw		= r8153_post_firmware_2;
> +		break;
> +	case RTL_VER_06:
> +		rtl_fw->fw_name		= FIRMWARE_8153A_4;
> +		rtl_fw->post_fw		= r8153_post_firmware_3;
> +		break;
> +	case RTL_VER_09:
> +		rtl_fw->fw_name		= FIRMWARE_8153B_2;
> +		rtl_fw->pre_fw		= r8153b_pre_firmware_1;
> +		rtl_fw->post_fw		= r8153b_post_firmware_1;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
>   static u8 rtl_get_version(struct usb_interface *intf)
>   {
>   	struct usb_device *udev = interface_to_usbdev(intf);
> @@ -5575,6 +6312,8 @@ static int rtl8152_probe(struct usb_interface *intf,
>   	if (ret)
>   		goto out;
>   
> +	rtl_fw_init(tp);
> +
>   	mutex_init(&tp->control);
>   	INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t);
>   	INIT_DELAYED_WORK(&tp->hw_phy_work, rtl_hw_phy_work_func_t);
> @@ -5646,6 +6385,10 @@ static int rtl8152_probe(struct usb_interface *intf,
>   	intf->needs_remote_wakeup = 1;
>   
>   	tp->rtl_ops.init(tp);
> +#if IS_BUILTIN(CONFIG_USB_RTL8152)
> +	/* Retry in case request_firmware() is not ready yet. */
> +	tp->rtl_fw.retry = true;
> +#endif
>   	queue_delayed_work(system_long_wq, &tp->hw_phy_work, 0);
>   	set_ethernet_addr(tp);
>   
> @@ -5691,6 +6434,7 @@ static void rtl8152_disconnect(struct usb_interface *intf)
>   		tasklet_kill(&tp->tx_tl);
>   		cancel_delayed_work_sync(&tp->hw_phy_work);
>   		tp->rtl_ops.unload(tp);
> +		rtl8152_release_firmware(tp);
>   		free_netdev(tp->netdev);
>   	}
>   }

Best regards
Hayes Wang Oct. 23, 2019, 9:48 a.m. UTC | #3
Marek Szyprowski [mailto:m.szyprowski@samsung.com]
> Sent: Wednesday, October 23, 2019 5:17 PM
> Subject: Re: [PATCH net-next] r8152: support request_firmware for RTL8153
> 
> Hi Hayes,
> 
> On 16.10.2019 05:02, Hayes Wang wrote:
> > This patch supports loading additional firmware file through
> > request_firmware().
> >
> > A firmware file may include a header followed by several blocks
> > which have different types of firmware. Currently, the supported
> > types are RTL_FW_END, RTL_FW_PLA, and RTL_FW_USB.
> >
> > The firmware is used to fix some compatible or hardware issues. For
> > example, the device couldn't be found after rebooting several times.
> >
> > The supported chips are
> > 	RTL_VER_04 (rtl8153a-2.fw)
> > 	RTL_VER_05 (rtl8153a-3.fw)
> > 	RTL_VER_06 (rtl8153a-4.fw)
> > 	RTL_VER_09 (rtl8153b-2.fw)
> >
> > Signed-off-by: Hayes Wang <hayeswang@realtek.com>
> > Reviewed-by: Prashant Malani <pmalani@chromium.org>
> 
> This patch (which landed in linux-next last days) causes a following
> kernel oops on the ARM 32bit Exynos5422 SoC based Odroid XU4 board:

Please try the following patch.

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index d3c30ccc8577..283b35a76cf0 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -4000,8 +4000,8 @@ static void rtl8152_fw_mac_apply(struct r8152 *tp, struct fw_mac *mac)
 static void rtl8152_apply_firmware(struct r8152 *tp)
 {
 	struct rtl_fw *rtl_fw = &tp->rtl_fw;
-	const struct firmware *fw = rtl_fw->fw;
-	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
+	const struct firmware *fw;
+	struct fw_header *fw_hdr;
 	struct fw_phy_patch_key *key;
 	u16 key_addr = 0;
 	int i;
@@ -4009,6 +4009,9 @@ static void rtl8152_apply_firmware(struct r8152 *tp)
 	if (IS_ERR_OR_NULL(rtl_fw->fw))
 		return;
 
+	fw = rtl_fw->fw;
+	fw_hdr = (struct fw_header *)fw->data;
+
 	if (rtl_fw->pre_fw)
 		rtl_fw->pre_fw(tp);

> > +static void rtl8152_apply_firmware(struct r8152 *tp)
> > +{
> > +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> > +	const struct firmware *fw = rtl_fw->fw;
> > +	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
> > +	int i;
> > +
> > +	if (IS_ERR_OR_NULL(rtl_fw->fw))
> > +		return;
> > +
> > +	if (rtl_fw->pre_fw)
> > +		rtl_fw->pre_fw(tp);
> > +

Best Regards,
Hayes
Marek Szyprowski Oct. 23, 2019, 10:22 a.m. UTC | #4
Hi Hayes,

On 23.10.2019 11:48, Hayes Wang wrote:
> Marek Szyprowski [mailto:m.szyprowski@samsung.com]
>> Sent: Wednesday, October 23, 2019 5:17 PM
>> Subject: Re: [PATCH net-next] r8152: support request_firmware for RTL8153
>>
>> Hi Hayes,
>>
>> On 16.10.2019 05:02, Hayes Wang wrote:
>>> This patch supports loading additional firmware file through
>>> request_firmware().
>>>
>>> A firmware file may include a header followed by several blocks
>>> which have different types of firmware. Currently, the supported
>>> types are RTL_FW_END, RTL_FW_PLA, and RTL_FW_USB.
>>>
>>> The firmware is used to fix some compatible or hardware issues. For
>>> example, the device couldn't be found after rebooting several times.
>>>
>>> The supported chips are
>>> 	RTL_VER_04 (rtl8153a-2.fw)
>>> 	RTL_VER_05 (rtl8153a-3.fw)
>>> 	RTL_VER_06 (rtl8153a-4.fw)
>>> 	RTL_VER_09 (rtl8153b-2.fw)
>>>
>>> Signed-off-by: Hayes Wang <hayeswang@realtek.com>
>>> Reviewed-by: Prashant Malani <pmalani@chromium.org>
>> This patch (which landed in linux-next last days) causes a following
>> kernel oops on the ARM 32bit Exynos5422 SoC based Odroid XU4 board:
> Please try the following patch.

Yes, this fixes the issue. I've applied those changes manually on top of 
Linux next-20191022, due to some differences in the context. When you 
prepare a final patch, feel free to add:

Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Fixes: 9370f2d05a2a ("r8152: support request_firmware for RTL8153")
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>


> diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
> index d3c30ccc8577..283b35a76cf0 100644
> --- a/drivers/net/usb/r8152.c
> +++ b/drivers/net/usb/r8152.c
> @@ -4000,8 +4000,8 @@ static void rtl8152_fw_mac_apply(struct r8152 *tp, struct fw_mac *mac)
>   static void rtl8152_apply_firmware(struct r8152 *tp)
>   {
>   	struct rtl_fw *rtl_fw = &tp->rtl_fw;
> -	const struct firmware *fw = rtl_fw->fw;
> -	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
> +	const struct firmware *fw;
> +	struct fw_header *fw_hdr;
>   	struct fw_phy_patch_key *key;
>   	u16 key_addr = 0;
>   	int i;
> @@ -4009,6 +4009,9 @@ static void rtl8152_apply_firmware(struct r8152 *tp)
>   	if (IS_ERR_OR_NULL(rtl_fw->fw))
>   		return;
>   
> +	fw = rtl_fw->fw;
> +	fw_hdr = (struct fw_header *)fw->data;
> +
>   	if (rtl_fw->pre_fw)
>   		rtl_fw->pre_fw(tp);
>
>>> +static void rtl8152_apply_firmware(struct r8152 *tp)
>>> +{
>>> +	struct rtl_fw *rtl_fw = &tp->rtl_fw;
>>> +	const struct firmware *fw = rtl_fw->fw;
>>> +	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
>>> +	int i;
>>> +
>>> +	if (IS_ERR_OR_NULL(rtl_fw->fw))
>>> +		return;
>>> +
>>> +	if (rtl_fw->pre_fw)
>>> +		rtl_fw->pre_fw(tp);
>>> +
>>>
Best regards
Hayes Wang Oct. 23, 2019, 11:52 a.m. UTC | #5
Marek Szyprowski [mailto:m.szyprowski@samsung.com]
> Sent: Wednesday, October 23, 2019 6:23 PM
[...]
> >> This patch (which landed in linux-next last days) causes a following
> >> kernel oops on the ARM 32bit Exynos5422 SoC based Odroid XU4 board:
> > Please try the following patch.
> 
> Yes, this fixes the issue. I've applied those changes manually on top of
> Linux next-20191022, due to some differences in the context. When you
> prepare a final patch, feel free to add:
> 
> Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Fixes: 9370f2d05a2a ("r8152: support request_firmware for RTL8153")
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>

Thanks

Best Regards,
Hayes
diff mbox series

Patch

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 54a83f734ede..55d0bcb00aef 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -24,9 +24,11 @@ 
 #include <linux/suspend.h>
 #include <linux/atomic.h>
 #include <linux/acpi.h>
+#include <linux/firmware.h>
+#include <crypto/hash.h>
 
 /* Information for net-next */
-#define NETNEXT_VERSION		"10"
+#define NETNEXT_VERSION		"11"
 
 /* Information for net */
 #define NET_VERSION		"10"
@@ -54,8 +56,11 @@ 
 #define PLA_BDC_CR		0xd1a0
 #define PLA_TEREDO_TIMER	0xd2cc
 #define PLA_REALWOW_TIMER	0xd2e8
+#define PLA_UPHY_TIMER		0xd388
 #define PLA_SUSPEND_FLAG	0xd38a
 #define PLA_INDICATE_FALG	0xd38c
+#define PLA_MACDBG_PRE		0xd38c	/* RTL_VER_04 only */
+#define PLA_MACDBG_POST		0xd38e	/* RTL_VER_04 only */
 #define PLA_EXTRA_STATUS	0xd398
 #define PLA_EFUSE_DATA		0xdd00
 #define PLA_EFUSE_CMD		0xdd02
@@ -110,7 +115,12 @@ 
 #define USB_CONNECT_TIMER	0xcbf8
 #define USB_MSC_TIMER		0xcbfc
 #define USB_BURST_SIZE		0xcfc0
+#define USB_FW_FIX_EN0		0xcfca
+#define USB_FW_FIX_EN1		0xcfcc
 #define USB_LPM_CONFIG		0xcfd8
+#define USB_CSTMR		0xcfef	/* RTL8153A */
+#define USB_FW_CTRL		0xd334	/* RTL8153B */
+#define USB_FC_TIMER		0xd340
 #define USB_USB_CTRL		0xd406
 #define USB_PHY_CTRL		0xd408
 #define USB_TX_AGG		0xd40a
@@ -126,6 +136,7 @@ 
 #define USB_LPM_CTRL		0xd41a
 #define USB_BMU_RESET		0xd4b0
 #define USB_U1U2_TIMER		0xd4da
+#define USB_FW_TASK		0xd4e8	/* RTL8153B */
 #define USB_UPS_CTRL		0xd800
 #define USB_POWER_CUT		0xd80a
 #define USB_MISC_0		0xd81a
@@ -133,18 +144,19 @@ 
 #define USB_AFE_CTRL2		0xd824
 #define USB_UPS_CFG		0xd842
 #define USB_UPS_FLAGS		0xd848
+#define USB_WDT1_CTRL		0xe404
 #define USB_WDT11_CTRL		0xe43c
-#define USB_BP_BA		0xfc26
-#define USB_BP_0		0xfc28
-#define USB_BP_1		0xfc2a
-#define USB_BP_2		0xfc2c
-#define USB_BP_3		0xfc2e
-#define USB_BP_4		0xfc30
-#define USB_BP_5		0xfc32
-#define USB_BP_6		0xfc34
-#define USB_BP_7		0xfc36
-#define USB_BP_EN		0xfc38
-#define USB_BP_8		0xfc38
+#define USB_BP_BA		PLA_BP_BA
+#define USB_BP_0		PLA_BP_0
+#define USB_BP_1		PLA_BP_1
+#define USB_BP_2		PLA_BP_2
+#define USB_BP_3		PLA_BP_3
+#define USB_BP_4		PLA_BP_4
+#define USB_BP_5		PLA_BP_5
+#define USB_BP_6		PLA_BP_6
+#define USB_BP_7		PLA_BP_7
+#define USB_BP_EN		PLA_BP_EN	/* RTL8153A */
+#define USB_BP_8		0xfc38		/* RTL8153B */
 #define USB_BP_9		0xfc3a
 #define USB_BP_10		0xfc3c
 #define USB_BP_11		0xfc3e
@@ -346,7 +358,12 @@ 
 /* PLA_INDICATE_FALG */
 #define UPCOMING_RUNTIME_D3	BIT(0)
 
+/* PLA_MACDBG_PRE and PLA_MACDBG_POST */
+#define DEBUG_OE		BIT(0)
+#define DEBUG_LTSSM		0x0082
+
 /* PLA_EXTRA_STATUS */
+#define U3P3_CHECK_EN		BIT(7)	/* RTL_VER_05 only */
 #define LINK_CHANGE_FLAG	BIT(8)
 
 /* USB_USB2PHY */
@@ -368,6 +385,12 @@ 
 #define STAT_SPEED_HIGH		0x0000
 #define STAT_SPEED_FULL		0x0002
 
+/* USB_FW_FIX_EN0 */
+#define FW_FIX_SUSPEND		BIT(14)
+
+/* USB_FW_FIX_EN1 */
+#define FW_IP_RESET_EN		BIT(9)
+
 /* USB_LPM_CONFIG */
 #define LPM_U1U2_EN		BIT(0)
 
@@ -392,12 +415,24 @@ 
 #define OWN_UPDATE		BIT(0)
 #define OWN_CLEAR		BIT(1)
 
+/* USB_FW_TASK */
+#define FC_PATCH_TASK		BIT(1)
+
 /* USB_UPS_CTRL */
 #define POWER_CUT		0x0100
 
 /* USB_PM_CTRL_STATUS */
 #define RESUME_INDICATE		0x0001
 
+/* USB_CSTMR */
+#define FORCE_SUPER		BIT(0)
+
+/* USB_FW_CTRL */
+#define FLOW_CTRL_PATCH_OPT	BIT(1)
+
+/* USB_FC_TIMER */
+#define CTRL_TIMER_EN		BIT(15)
+
 /* USB_USB_CTRL */
 #define RX_AGG_DISABLE		0x0010
 #define RX_ZERO_EN		0x0080
@@ -419,6 +454,9 @@ 
 #define COALESCE_HIGH		250000U
 #define COALESCE_SLOW		524280U
 
+/* USB_WDT1_CTRL */
+#define WTD1_EN			BIT(0)
+
 /* USB_WDT11_CTRL */
 #define TIMER11_EN		0x0001
 
@@ -570,6 +608,8 @@  enum spd_duplex {
 #define EFUSE			0xcfdb
 #define PASS_THRU_MASK		0x1
 
+#define BP4_SUPER_ONLY		0x1578	/* RTL_VER_04 only */
+
 enum rtl_register_content {
 	_1000bps	= 0x10,
 	_100bps		= 0x08,
@@ -766,6 +806,19 @@  struct r8152 {
 		u32 ctap_short_off:1;
 	} ups_info;
 
+#define RTL_VER_SIZE		32
+
+	struct rtl_fw {
+		const char *fw_name;
+		const struct firmware *fw;
+
+		char version[RTL_VER_SIZE];
+		int (*pre_fw)(struct r8152 *tp);
+		int (*post_fw)(struct r8152 *tp);
+
+		bool retry;
+	} rtl_fw;
+
 	atomic_t rx_count;
 
 	bool eee_en;
@@ -788,6 +841,76 @@  struct r8152 {
 	u8 autoneg;
 };
 
+/**
+ * struct fw_block - block type and total length
+ * @type: type of the current block, such as RTL_FW_END, RTL_FW_PLA,
+ *	RTL_FW_USB and so on.
+ * @length: total length of the current block.
+ */
+struct fw_block {
+	__le32 type;
+	__le32 length;
+} __packed;
+
+/**
+ * struct fw_header - header of the firmware file
+ * @checksum: checksum of sha256 which is calculated from the whole file
+ *	except the checksum field of the file. That is, calculate sha256
+ *	from the version field to the end of the file.
+ * @version: version of this firmware.
+ * @blocks: the first firmware block of the file
+ */
+struct fw_header {
+	u8 checksum[32];
+	char version[RTL_VER_SIZE];
+	struct fw_block blocks[0];
+} __packed;
+
+/**
+ * struct fw_type_1 - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
+ *	The layout of the firmware block is:
+ *	<struct fw_type_1> + <info> + <firmware data>.
+ * @fw_offset: offset of the firmware binary data. The start address of
+ *	the data would be the address of struct fw_type_1 + @fw_offset.
+ * @fw_reg: the register to load the firmware. Depends on chip.
+ * @bp_ba_addr: the register to write break point base address. Depends on
+ *	chip.
+ * @bp_ba_value: break point base address. Depends on chip.
+ * @bp_en_addr: the register to write break point enabled mask. Depends
+ *	on chip.
+ * @bp_en_value: break point enabled mask. Depends on the firmware.
+ * @bp_start: the start register of break points. Depends on chip.
+ * @bp_num: the break point number which needs to be set for this firmware.
+ *	Depends on the firmware.
+ * @bp: break points. Depends on firmware.
+ * @fw_ver_reg: the register to store the fw version.
+ * @fw_ver_data: the firmware version of the current type.
+ * @info: additional information for debugging, and is followed by the
+ *	binary data of firmware.
+ */
+struct fw_type_1 {
+	struct fw_block blk_hdr;
+	__le16 fw_offset;
+	__le16 fw_reg;
+	__le16 bp_ba_addr;
+	__le16 bp_ba_value;
+	__le16 bp_en_addr;
+	__le16 bp_en_value;
+	__le16 bp_start;
+	__le16 bp_num;
+	__le16 bp[16]; /* any value determined by firmware */
+	__le32 reserved;
+	__le16 fw_ver_reg;
+	u8 fw_ver_data;
+	char info[0];
+} __packed;
+
+enum rtl_fw_type {
+	RTL_FW_END = 0,
+	RTL_FW_PLA,
+	RTL_FW_USB,
+};
+
 enum rtl_version {
 	RTL_VER_UNKNOWN = 0,
 	RTL_VER_01,
@@ -3226,6 +3349,435 @@  static void rtl_reset_bmu(struct r8152 *tp)
 	ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
 }
 
+/* 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_08:
+	case RTL_VER_09:
+	default:
+		if (type == MCU_TYPE_USB) {
+			ocp_write_byte(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);
+		}
+		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);
+
+	/* wait 3 ms to make sure the firmware is stopped */
+	usleep_range(3000, 6000);
+	ocp_write_word(tp, type, PLA_BP_BA, 0);
+}
+
+static bool rtl8152_is_fw_type1_ok(struct r8152 *tp, struct fw_type_1 *type1)
+{
+	u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start;
+	bool rc = false;
+	u32 length, type;
+	int i, max_bp;
+
+	type = __le32_to_cpu(type1->blk_hdr.type);
+	if (type == RTL_FW_PLA) {
+		switch (tp->version) {
+		case RTL_VER_01:
+		case RTL_VER_02:
+		case RTL_VER_07:
+			fw_reg = 0xf800;
+			bp_ba_addr = PLA_BP_BA;
+			bp_en_addr = 0;
+			bp_start = PLA_BP_0;
+			max_bp = 8;
+			break;
+		case RTL_VER_03:
+		case RTL_VER_04:
+		case RTL_VER_05:
+		case RTL_VER_06:
+		case RTL_VER_08:
+		case RTL_VER_09:
+			fw_reg = 0xf800;
+			bp_ba_addr = PLA_BP_BA;
+			bp_en_addr = PLA_BP_EN;
+			bp_start = PLA_BP_0;
+			max_bp = 8;
+			break;
+		default:
+			goto out;
+		}
+	} else if (type == RTL_FW_USB) {
+		switch (tp->version) {
+		case RTL_VER_03:
+		case RTL_VER_04:
+		case RTL_VER_05:
+		case RTL_VER_06:
+			fw_reg = 0xf800;
+			bp_ba_addr = USB_BP_BA;
+			bp_en_addr = USB_BP_EN;
+			bp_start = USB_BP_0;
+			max_bp = 8;
+			break;
+		case RTL_VER_08:
+		case RTL_VER_09:
+			fw_reg = 0xe600;
+			bp_ba_addr = USB_BP_BA;
+			bp_en_addr = USB_BP2_EN;
+			bp_start = USB_BP_0;
+			max_bp = 16;
+			break;
+		case RTL_VER_01:
+		case RTL_VER_02:
+		case RTL_VER_07:
+		default:
+			goto out;
+		}
+	} else {
+		goto out;
+	}
+
+	length = __le32_to_cpu(type1->blk_hdr.length);
+	if (length < __le16_to_cpu(type1->fw_offset)) {
+		dev_err(&tp->intf->dev, "invalid fw_offset\n");
+		goto out;
+	}
+
+	length -= __le16_to_cpu(type1->fw_offset);
+	if (length < 4 || (length & 3)) {
+		dev_err(&tp->intf->dev, "invalid block length\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(type1->fw_reg) != fw_reg) {
+		dev_err(&tp->intf->dev, "invalid register to load firmware\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(type1->bp_ba_addr) != bp_ba_addr) {
+		dev_err(&tp->intf->dev, "invalid base address register\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(type1->bp_en_addr) != bp_en_addr) {
+		dev_err(&tp->intf->dev, "invalid enabled mask register\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(type1->bp_start) != bp_start) {
+		dev_err(&tp->intf->dev,
+			"invalid start register of break point\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(type1->bp_num) > max_bp) {
+		dev_err(&tp->intf->dev, "invalid break point number\n");
+		goto out;
+	}
+
+	for (i = __le16_to_cpu(type1->bp_num); i < max_bp; i++) {
+		if (type1->bp[i]) {
+			dev_err(&tp->intf->dev, "unused bp%u is not zero\n", i);
+			goto out;
+		}
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
+/* Verify the checksum for the firmware file. It is calculated from the version
+ * field to the end of the file. Compare the result with the checksum field to
+ * make sure the file is correct.
+ */
+static long rtl8152_fw_verify_checksum(struct r8152 *tp,
+				       struct fw_header *fw_hdr, size_t size)
+{
+	unsigned char checksum[sizeof(fw_hdr->checksum)];
+	struct crypto_shash *alg;
+	struct shash_desc *sdesc;
+	size_t len;
+	long rc;
+
+	alg = crypto_alloc_shash("sha256", 0, 0);
+	if (IS_ERR(alg)) {
+		rc = PTR_ERR(alg);
+		goto out;
+	}
+
+	if (crypto_shash_digestsize(alg) != sizeof(fw_hdr->checksum)) {
+		rc = -EFAULT;
+		dev_err(&tp->intf->dev, "digestsize incorrect (%u)\n",
+			crypto_shash_digestsize(alg));
+		goto free_shash;
+	}
+
+	len = sizeof(*sdesc) + crypto_shash_descsize(alg);
+	sdesc = kmalloc(len, GFP_KERNEL);
+	if (!sdesc) {
+		rc = -ENOMEM;
+		goto free_shash;
+	}
+	sdesc->tfm = alg;
+
+	len = size - sizeof(fw_hdr->checksum);
+	rc = crypto_shash_digest(sdesc, fw_hdr->version, len, checksum);
+	kfree(sdesc);
+	if (rc)
+		goto free_shash;
+
+	if (memcmp(fw_hdr->checksum, checksum, sizeof(fw_hdr->checksum))) {
+		dev_err(&tp->intf->dev, "checksum fail\n");
+		rc = -EFAULT;
+	}
+
+free_shash:
+	crypto_free_shash(alg);
+out:
+	return rc;
+}
+
+static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw)
+{
+	const struct firmware *fw = rtl_fw->fw;
+	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
+	struct fw_type_1 *pla = NULL, *usb = NULL;
+	long ret = -EFAULT;
+	int i;
+
+	if (fw->size < sizeof(*fw_hdr)) {
+		dev_err(&tp->intf->dev, "file too small\n");
+		goto fail;
+	}
+
+	ret = rtl8152_fw_verify_checksum(tp, fw_hdr, fw->size);
+	if (ret)
+		goto fail;
+
+	ret = -EFAULT;
+
+	for (i = sizeof(*fw_hdr); i < fw->size;) {
+		struct fw_block *block = (struct fw_block *)&fw->data[i];
+		u32 type;
+
+		if ((i + sizeof(*block)) > fw->size)
+			goto fail;
+
+		type = __le32_to_cpu(block->type);
+		switch (type) {
+		case RTL_FW_END:
+			if (__le32_to_cpu(block->length) != sizeof(*block))
+				goto fail;
+			goto success;
+		case RTL_FW_PLA:
+			if (pla) {
+				dev_err(&tp->intf->dev,
+					"multiple PLA firmware encountered");
+				goto fail;
+			}
+
+			pla = (struct fw_type_1 *)block;
+			if (!rtl8152_is_fw_type1_ok(tp, pla)) {
+				dev_err(&tp->intf->dev,
+					"load PLA firmware failed\n");
+				goto fail;
+			}
+			break;
+		case RTL_FW_USB:
+			if (usb) {
+				dev_err(&tp->intf->dev,
+					"multiple USB firmware encountered");
+				goto fail;
+			}
+
+			usb = (struct fw_type_1 *)block;
+			if (!rtl8152_is_fw_type1_ok(tp, usb)) {
+				dev_err(&tp->intf->dev,
+					"load USB firmware failed\n");
+				goto fail;
+			}
+			break;
+		default:
+			dev_warn(&tp->intf->dev, "Unknown type %u is found\n",
+				 type);
+			break;
+		}
+
+		/* next block */
+		i += ALIGN(__le32_to_cpu(block->length), 8);
+	}
+
+success:
+	return 0;
+fail:
+	return ret;
+}
+
+static void rtl8152_fw_type_1_apply(struct r8152 *tp, struct fw_type_1 *type1)
+{
+	u16 bp_en_addr, bp_index, type, bp_num, fw_ver_reg;
+	u32 length;
+	u8 *data;
+	int i;
+
+	switch (__le32_to_cpu(type1->blk_hdr.type)) {
+	case RTL_FW_PLA:
+		type = MCU_TYPE_PLA;
+		break;
+	case RTL_FW_USB:
+		type = MCU_TYPE_USB;
+		break;
+	default:
+		return;
+	}
+
+	rtl_clear_bp(tp, type);
+
+	/* Enable backup/restore of MACDBG. This is required after clearing PLA
+	 * break points and before applying the PLA firmware.
+	 */
+	if (tp->version == RTL_VER_04 && type == MCU_TYPE_PLA &&
+	    !(ocp_read_word(tp, MCU_TYPE_PLA, PLA_MACDBG_POST) & DEBUG_OE)) {
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MACDBG_PRE, DEBUG_LTSSM);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MACDBG_POST, DEBUG_LTSSM);
+	}
+
+	length = __le32_to_cpu(type1->blk_hdr.length);
+	length -= __le16_to_cpu(type1->fw_offset);
+
+	data = (u8 *)type1;
+	data += __le16_to_cpu(type1->fw_offset);
+
+	generic_ocp_write(tp, __le16_to_cpu(type1->fw_reg), 0xff, length, data,
+			  type);
+
+	ocp_write_word(tp, type, __le16_to_cpu(type1->bp_ba_addr),
+		       __le16_to_cpu(type1->bp_ba_value));
+
+	bp_index = __le16_to_cpu(type1->bp_start);
+	bp_num = __le16_to_cpu(type1->bp_num);
+	for (i = 0; i < bp_num; i++) {
+		ocp_write_word(tp, type, bp_index, __le16_to_cpu(type1->bp[i]));
+		bp_index += 2;
+	}
+
+	bp_en_addr = __le16_to_cpu(type1->bp_en_addr);
+	if (bp_en_addr)
+		ocp_write_word(tp, type, bp_en_addr,
+			       __le16_to_cpu(type1->bp_en_value));
+
+	fw_ver_reg = __le16_to_cpu(type1->fw_ver_reg);
+	if (fw_ver_reg)
+		ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg,
+			       type1->fw_ver_data);
+
+	dev_dbg(&tp->intf->dev, "successfully applied %s\n", type1->info);
+}
+
+static void rtl8152_apply_firmware(struct r8152 *tp)
+{
+	struct rtl_fw *rtl_fw = &tp->rtl_fw;
+	const struct firmware *fw = rtl_fw->fw;
+	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
+	int i;
+
+	if (IS_ERR_OR_NULL(rtl_fw->fw))
+		return;
+
+	if (rtl_fw->pre_fw)
+		rtl_fw->pre_fw(tp);
+
+	for (i = offsetof(struct fw_header, blocks); i < fw->size;) {
+		struct fw_block *block = (struct fw_block *)&fw->data[i];
+
+		switch (__le32_to_cpu(block->type)) {
+		case RTL_FW_END:
+			goto post_fw;
+		case RTL_FW_PLA:
+		case RTL_FW_USB:
+			rtl8152_fw_type_1_apply(tp, (struct fw_type_1 *)block);
+			break;
+		default:
+			break;
+		}
+
+		i += ALIGN(__le32_to_cpu(block->length), 8);
+	}
+
+post_fw:
+	if (rtl_fw->post_fw)
+		rtl_fw->post_fw(tp);
+
+	strscpy(rtl_fw->version, fw_hdr->version, RTL_VER_SIZE);
+	dev_info(&tp->intf->dev, "load %s successfully\n", rtl_fw->version);
+}
+
+static void rtl8152_release_firmware(struct r8152 *tp)
+{
+	struct rtl_fw *rtl_fw = &tp->rtl_fw;
+
+	if (!IS_ERR_OR_NULL(rtl_fw->fw)) {
+		release_firmware(rtl_fw->fw);
+		rtl_fw->fw = NULL;
+	}
+}
+
+static int rtl8152_request_firmware(struct r8152 *tp)
+{
+	struct rtl_fw *rtl_fw = &tp->rtl_fw;
+	long rc;
+
+	if (rtl_fw->fw || !rtl_fw->fw_name) {
+		dev_info(&tp->intf->dev, "skip request firmware\n");
+		rc = 0;
+		goto result;
+	}
+
+	rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, &tp->intf->dev);
+	if (rc < 0)
+		goto result;
+
+	rc = rtl8152_check_firmware(tp, rtl_fw);
+	if (rc < 0)
+		release_firmware(rtl_fw->fw);
+
+result:
+	if (rc) {
+		rtl_fw->fw = ERR_PTR(rc);
+
+		dev_warn(&tp->intf->dev,
+			 "unable to load firmware patch %s (%ld)\n",
+			 rtl_fw->fw_name, rc);
+	}
+
+	return rc;
+}
+
 static void r8152_aldps_en(struct r8152 *tp, bool enable)
 {
 	if (enable) {
@@ -3370,6 +3922,7 @@  static void rtl8152_disable(struct r8152 *tp)
 
 static void r8152b_hw_phy_cfg(struct r8152 *tp)
 {
+	rtl8152_apply_firmware(tp);
 	rtl_eee_enable(tp, tp->eee_en);
 	r8152_aldps_en(tp, true);
 	r8152b_enable_fc(tp);
@@ -3524,6 +4077,126 @@  static int r8153_patch_request(struct r8152 *tp, bool request)
 	}
 }
 
+static int r8153_pre_firmware_1(struct r8152 *tp)
+{
+	int i;
+
+	/* Wait till the WTD timer is ready. It would take at most 104 ms. */
+	for (i = 0; i < 104; i++) {
+		u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_WDT1_CTRL);
+
+		if (!(ocp_data & WTD1_EN))
+			break;
+		usleep_range(1000, 2000);
+	}
+
+	return 0;
+}
+
+static int r8153_post_firmware_1(struct r8152 *tp)
+{
+	/* set USB_BP_4 to support USB_SPEED_SUPER only */
+	if (ocp_read_byte(tp, MCU_TYPE_USB, USB_CSTMR) & FORCE_SUPER)
+		ocp_write_word(tp, MCU_TYPE_USB, USB_BP_4, BP4_SUPER_ONLY);
+
+	/* reset UPHY timer to 36 ms */
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_UPHY_TIMER, 36000 / 16);
+
+	return 0;
+}
+
+static int r8153_pre_firmware_2(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	r8153_pre_firmware_1(tp);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0);
+	ocp_data &= ~FW_FIX_SUSPEND;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0, ocp_data);
+
+	return 0;
+}
+
+static int r8153_post_firmware_2(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	/* enable bp0 if support USB_SPEED_SUPER only */
+	if (ocp_read_byte(tp, MCU_TYPE_USB, USB_CSTMR) & FORCE_SUPER) {
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BP_EN);
+		ocp_data |= BIT(0);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, ocp_data);
+	}
+
+	/* reset UPHY timer to 36 ms */
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_UPHY_TIMER, 36000 / 16);
+
+	/* enable U3P3 check, set the counter to 4 */
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, U3P3_CHECK_EN | 4);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0);
+	ocp_data |= FW_FIX_SUSPEND;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN0, ocp_data);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY);
+	ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND;
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data);
+
+	return 0;
+}
+
+static int r8153_post_firmware_3(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY);
+	ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND;
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
+	ocp_data |= FW_IP_RESET_EN;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
+
+	return 0;
+}
+
+static int r8153b_pre_firmware_1(struct r8152 *tp)
+{
+	/* enable fc timer and set timer to 1 second. */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
+		       CTRL_TIMER_EN | (1000 / 8));
+
+	return 0;
+}
+
+static int r8153b_post_firmware_1(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	/* enable bp0 for RTL8153-BND */
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1);
+	if (ocp_data & BND_MASK) {
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BP_EN);
+		ocp_data |= BIT(0);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, ocp_data);
+	}
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
+	ocp_data |= FLOW_CTRL_PATCH_OPT;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
+	ocp_data |= FC_PATCH_TASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
+	ocp_data |= FW_IP_RESET_EN;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
+
+	return 0;
+}
+
 static void r8153_aldps_en(struct r8152 *tp, bool enable)
 {
 	u16 data;
@@ -3558,6 +4231,8 @@  static void r8153_hw_phy_cfg(struct r8152 *tp)
 	/* disable EEE before updating the PHY parameters */
 	rtl_eee_enable(tp, false);
 
+	rtl8152_apply_firmware(tp);
+
 	if (tp->version == RTL_VER_03) {
 		data = ocp_reg_read(tp, OCP_EEE_CFG);
 		data &= ~CTAP_SHORT_EN;
@@ -3630,6 +4305,8 @@  static void r8153b_hw_phy_cfg(struct r8152 *tp)
 	/* disable EEE before updating the PHY parameters */
 	rtl_eee_enable(tp, false);
 
+	rtl8152_apply_firmware(tp);
+
 	r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
 
 	data = sram_read(tp, SRAM_GREEN_CFG);
@@ -4156,11 +4833,22 @@  static void rtl_hw_phy_work_func_t(struct work_struct *work)
 
 	mutex_lock(&tp->control);
 
+	if (rtl8152_request_firmware(tp) == -ENODEV && tp->rtl_fw.retry) {
+		tp->rtl_fw.retry = false;
+		tp->rtl_fw.fw = NULL;
+
+		/* Delay execution in case request_firmware() is not ready yet.
+		 */
+		queue_delayed_work(system_long_wq, &tp->hw_phy_work, HZ * 10);
+		goto ignore_once;
+	}
+
 	tp->rtl_ops.hw_phy_cfg(tp);
 
 	rtl8152_set_speed(tp, tp->autoneg, tp->speed, tp->duplex,
 			  tp->advertising);
 
+ignore_once:
 	mutex_unlock(&tp->control);
 
 	usb_autopm_put_interface(tp->intf);
@@ -4198,6 +4886,11 @@  static int rtl8152_open(struct net_device *netdev)
 	struct r8152 *tp = netdev_priv(netdev);
 	int res = 0;
 
+	if (work_busy(&tp->hw_phy_work.work) & WORK_BUSY_PENDING) {
+		cancel_delayed_work_sync(&tp->hw_phy_work);
+		rtl_hw_phy_work_func_t(&tp->hw_phy_work.work);
+	}
+
 	res = alloc_all_mem(tp);
 	if (res)
 		goto out;
@@ -4844,6 +5537,9 @@  static void rtl8152_get_drvinfo(struct net_device *netdev,
 	strlcpy(info->driver, MODULENAME, sizeof(info->driver));
 	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
 	usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info));
+	if (!IS_ERR_OR_NULL(tp->rtl_fw.fw))
+		strlcpy(info->fw_version, tp->rtl_fw.version,
+			sizeof(info->fw_version));
 }
 
 static
@@ -5468,6 +6164,47 @@  static int rtl_ops_init(struct r8152 *tp)
 	return ret;
 }
 
+#define FIRMWARE_8153A_2	"rtl_nic/rtl8153a-2.fw"
+#define FIRMWARE_8153A_3	"rtl_nic/rtl8153a-3.fw"
+#define FIRMWARE_8153A_4	"rtl_nic/rtl8153a-4.fw"
+#define FIRMWARE_8153B_2	"rtl_nic/rtl8153b-2.fw"
+
+MODULE_FIRMWARE(FIRMWARE_8153A_2);
+MODULE_FIRMWARE(FIRMWARE_8153A_3);
+MODULE_FIRMWARE(FIRMWARE_8153A_4);
+MODULE_FIRMWARE(FIRMWARE_8153B_2);
+
+static int rtl_fw_init(struct r8152 *tp)
+{
+	struct rtl_fw *rtl_fw = &tp->rtl_fw;
+
+	switch (tp->version) {
+	case RTL_VER_04:
+		rtl_fw->fw_name		= FIRMWARE_8153A_2;
+		rtl_fw->pre_fw		= r8153_pre_firmware_1;
+		rtl_fw->post_fw		= r8153_post_firmware_1;
+		break;
+	case RTL_VER_05:
+		rtl_fw->fw_name		= FIRMWARE_8153A_3;
+		rtl_fw->pre_fw		= r8153_pre_firmware_2;
+		rtl_fw->post_fw		= r8153_post_firmware_2;
+		break;
+	case RTL_VER_06:
+		rtl_fw->fw_name		= FIRMWARE_8153A_4;
+		rtl_fw->post_fw		= r8153_post_firmware_3;
+		break;
+	case RTL_VER_09:
+		rtl_fw->fw_name		= FIRMWARE_8153B_2;
+		rtl_fw->pre_fw		= r8153b_pre_firmware_1;
+		rtl_fw->post_fw		= r8153b_post_firmware_1;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static u8 rtl_get_version(struct usb_interface *intf)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
@@ -5575,6 +6312,8 @@  static int rtl8152_probe(struct usb_interface *intf,
 	if (ret)
 		goto out;
 
+	rtl_fw_init(tp);
+
 	mutex_init(&tp->control);
 	INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t);
 	INIT_DELAYED_WORK(&tp->hw_phy_work, rtl_hw_phy_work_func_t);
@@ -5646,6 +6385,10 @@  static int rtl8152_probe(struct usb_interface *intf,
 	intf->needs_remote_wakeup = 1;
 
 	tp->rtl_ops.init(tp);
+#if IS_BUILTIN(CONFIG_USB_RTL8152)
+	/* Retry in case request_firmware() is not ready yet. */
+	tp->rtl_fw.retry = true;
+#endif
 	queue_delayed_work(system_long_wq, &tp->hw_phy_work, 0);
 	set_ethernet_addr(tp);
 
@@ -5691,6 +6434,7 @@  static void rtl8152_disconnect(struct usb_interface *intf)
 		tasklet_kill(&tp->tx_tl);
 		cancel_delayed_work_sync(&tp->hw_phy_work);
 		tp->rtl_ops.unload(tp);
+		rtl8152_release_firmware(tp);
 		free_netdev(tp->netdev);
 	}
 }