From 5f71c84038d39def573744a145c573758f52a949 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Tue, 1 Oct 2019 01:35:57 -0700 Subject: r8152: Factor out OOB link list waits The same for-loop check for the LINK_LIST_READY bit of an OOB_CTRL register is used in several places. Factor these out into a single function to reduce the lines of code. Change-Id: I20e8f327045a72acc0a83e2d145ae2993ab62915 Signed-off-by: Prashant Malani Reviewed-by: Grant Grundler Acked-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 73 ++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 08726090570e..30a6df73a955 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3377,11 +3377,23 @@ static void r8152b_hw_phy_cfg(struct r8152 *tp) set_bit(PHY_RESET, &tp->flags); } -static void r8152b_exit_oob(struct r8152 *tp) +static void wait_oob_link_list_ready(struct r8152 *tp) { u32 ocp_data; int i; + for (i = 0; i < 1000; i++) { + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + if (ocp_data & LINK_LIST_READY) + break; + usleep_range(1000, 2000); + } +} + +static void r8152b_exit_oob(struct r8152 *tp) +{ + u32 ocp_data; + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); @@ -3399,23 +3411,13 @@ static void r8152b_exit_oob(struct r8152 *tp) ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); rtl8152_nic_reset(tp); @@ -3457,7 +3459,6 @@ static void r8152b_exit_oob(struct r8152 *tp) static void r8152b_enter_oob(struct r8152 *tp) { u32 ocp_data; - int i; ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; @@ -3469,23 +3470,13 @@ static void r8152b_enter_oob(struct r8152 *tp) rtl_disable(tp); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); @@ -3711,7 +3702,6 @@ static void r8153b_hw_phy_cfg(struct r8152 *tp) static void r8153_first_init(struct r8152 *tp) { u32 ocp_data; - int i; r8153_mac_clk_spd(tp, false); rxdy_gated_en(tp, true); @@ -3732,23 +3722,13 @@ static void r8153_first_init(struct r8152 *tp) ocp_data &= ~MCU_BORW_EN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); @@ -3773,7 +3753,6 @@ static void r8153_first_init(struct r8152 *tp) static void r8153_enter_oob(struct r8152 *tp) { u32 ocp_data; - int i; r8153_mac_clk_spd(tp, true); @@ -3784,23 +3763,13 @@ static void r8153_enter_oob(struct r8152 *tp) rtl_disable(tp); rtl_reset_bmu(tp); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ocp_data |= RE_INIT_LL; ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); - for (i = 0; i < 1000; i++) { - ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); - if (ocp_data & LINK_LIST_READY) - break; - usleep_range(1000, 2000); - } + wait_oob_link_list_ready(tp); ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); -- cgit v1.2.3 From c01ebd6c46980654220f6d2b660308a074ee29df Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 30 Sep 2019 12:38:18 -0700 Subject: r8152: Use guard clause and fix comment typos Use a guard clause in tx_bottom() to reduce the indentation of the do-while loop. Also, fix a couple of spelling and grammatical mistakes in the r8152_csum_workaround() function comment. Change-Id: I460befde150ad92248fd85b0f189ec2df2ab8431 Signed-off-by: Prashant Malani Reviewed-by: Grant Grundler Acked-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 30a6df73a955..601259238fd7 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1688,7 +1688,7 @@ static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp) } /* r8152_csum_workaround() - * The hw limites the value the transport offset. When the offset is out of the + * The hw limits the value of the transport offset. When the offset is out of * range, calculate the checksum by sw. */ static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb, @@ -2178,6 +2178,7 @@ static void tx_bottom(struct r8152 *tp) int res; do { + struct net_device *netdev = tp->netdev; struct tx_agg *agg; if (skb_queue_empty(&tp->tx_queue)) @@ -2188,24 +2189,23 @@ static void tx_bottom(struct r8152 *tp) break; res = r8152_tx_agg_fill(tp, agg); - if (res) { - struct net_device *netdev = tp->netdev; + if (!res) + continue; - if (res == -ENODEV) { - rtl_set_unplug(tp); - netif_device_detach(netdev); - } else { - struct net_device_stats *stats = &netdev->stats; - unsigned long flags; + if (res == -ENODEV) { + rtl_set_unplug(tp); + netif_device_detach(netdev); + } else { + struct net_device_stats *stats = &netdev->stats; + unsigned long flags; - netif_warn(tp, tx_err, netdev, - "failed tx_urb %d\n", res); - stats->tx_dropped += agg->skb_num; + netif_warn(tp, tx_err, netdev, + "failed tx_urb %d\n", res); + stats->tx_dropped += agg->skb_num; - spin_lock_irqsave(&tp->tx_lock, flags); - list_add_tail(&agg->list, &tp->tx_free); - spin_unlock_irqrestore(&tp->tx_lock, flags); - } + spin_lock_irqsave(&tp->tx_lock, flags); + list_add_tail(&agg->list, &tp->tx_free); + spin_unlock_irqrestore(&tp->tx_lock, flags); } } while (res == 0); } -- cgit v1.2.3 From 151ea094378d057dfca76c0110da76dd29cf1de3 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Wed, 2 Oct 2019 14:09:33 -0700 Subject: r8152: Add identifier names for function pointers Checkpatch throws warnings for function pointer declarations which lack identifier names. An example of such a warning is: WARNING: function definition argument 'struct r8152 *' should also have an identifier name 739: FILE: drivers/net/usb/r8152.c:739: + void (*init)(struct r8152 *); So, fix those warnings by adding the identifier names. While we are at it, also fix a character limit violation which was causing another checkpatch warning. Change-Id: Idec857ce2dc9592caf3173188be1660052c052ce Signed-off-by: Prashant Malani Reviewed-by: Grant Grundler Acked-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 601259238fd7..8ba478c1ffeb 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -736,16 +736,16 @@ struct r8152 { struct tasklet_struct tx_tl; struct rtl_ops { - void (*init)(struct r8152 *); - int (*enable)(struct r8152 *); - void (*disable)(struct r8152 *); - void (*up)(struct r8152 *); - void (*down)(struct r8152 *); - void (*unload)(struct r8152 *); - int (*eee_get)(struct r8152 *, struct ethtool_eee *); - int (*eee_set)(struct r8152 *, struct ethtool_eee *); - bool (*in_nway)(struct r8152 *); - void (*hw_phy_cfg)(struct r8152 *); + void (*init)(struct r8152 *tp); + int (*enable)(struct r8152 *tp); + void (*disable)(struct r8152 *tp); + void (*up)(struct r8152 *tp); + void (*down)(struct r8152 *tp); + void (*unload)(struct r8152 *tp); + int (*eee_get)(struct r8152 *tp, struct ethtool_eee *eee); + int (*eee_set)(struct r8152 *tp, struct ethtool_eee *eee); + bool (*in_nway)(struct r8152 *tp); + void (*hw_phy_cfg)(struct r8152 *tp); void (*autosuspend_en)(struct r8152 *tp, bool enable); } rtl_ops; @@ -5603,7 +5603,8 @@ static int rtl8152_probe(struct usb_interface *intf, } if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x3011 && udev->serial && - (!strcmp(udev->serial, "000001000000") || !strcmp(udev->serial, "000002000000"))) { + (!strcmp(udev->serial, "000001000000") || + !strcmp(udev->serial, "000002000000"))) { dev_info(&udev->dev, "Dell TB16 Dock, disable RX aggregation"); set_bit(DELL_TB_RX_AGG_BUG, &tp->flags); } -- cgit v1.2.3 From 9370f2d05a2a150b0aa719a3070b26d478180df3 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Wed, 16 Oct 2019 11:02:42 +0800 Subject: r8152: support request_firmware for RTL8153 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 Reviewed-by: Prashant Malani Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 768 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 756 insertions(+), 12 deletions(-) (limited to 'drivers/net/usb/r8152.c') 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 #include #include +#include +#include /* 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: + * + + . + * @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); } } -- cgit v1.2.3 From a66edaafae08c37f9ea31fdfbc64d2a9be8d588f Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Mon, 21 Oct 2019 11:41:10 +0800 Subject: r8152: rename fw_type_1 with fw_mac The struct fw_type_1 is used by MAC only, so rename it to a meaningful one. Besides, adjust two messages. Replace "load xxx fail" with "check xxx fail" Signed-off-by: Hayes Wang Signed-off-by: Jakub Kicinski --- drivers/net/usb/r8152.c | 82 ++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 55d0bcb00aef..55a7674a0c06 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -867,11 +867,11 @@ struct fw_header { } __packed; /** - * struct fw_type_1 - a firmware block used by RTL_FW_PLA and RTL_FW_USB. + * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB. * The layout of the firmware block is: - * + + . + * + + . * @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. + * the data would be the address of struct fw_mac + @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. @@ -888,7 +888,7 @@ struct fw_header { * @info: additional information for debugging, and is followed by the * binary data of firmware. */ -struct fw_type_1 { +struct fw_mac { struct fw_block blk_hdr; __le16 fw_offset; __le16 fw_reg; @@ -3397,14 +3397,14 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type) ocp_write_word(tp, type, PLA_BP_BA, 0); } -static bool rtl8152_is_fw_type1_ok(struct r8152 *tp, struct fw_type_1 *type1) +static bool rtl8152_is_fw_mac_ok(struct r8152 *tp, struct fw_mac *mac) { 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); + type = __le32_to_cpu(mac->blk_hdr.type); if (type == RTL_FW_PLA) { switch (tp->version) { case RTL_VER_01: @@ -3461,46 +3461,46 @@ static bool rtl8152_is_fw_type1_ok(struct r8152 *tp, struct fw_type_1 *type1) goto out; } - length = __le32_to_cpu(type1->blk_hdr.length); - if (length < __le16_to_cpu(type1->fw_offset)) { + length = __le32_to_cpu(mac->blk_hdr.length); + if (length < __le16_to_cpu(mac->fw_offset)) { dev_err(&tp->intf->dev, "invalid fw_offset\n"); goto out; } - length -= __le16_to_cpu(type1->fw_offset); + length -= __le16_to_cpu(mac->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) { + if (__le16_to_cpu(mac->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) { + if (__le16_to_cpu(mac->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) { + if (__le16_to_cpu(mac->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) { + if (__le16_to_cpu(mac->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) { + if (__le16_to_cpu(mac->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]) { + for (i = __le16_to_cpu(mac->bp_num); i < max_bp; i++) { + if (mac->bp[i]) { dev_err(&tp->intf->dev, "unused bp%u is not zero\n", i); goto out; } @@ -3566,7 +3566,7 @@ 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; + struct fw_mac *pla = NULL, *usb = NULL; long ret = -EFAULT; int i; @@ -3601,10 +3601,10 @@ static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw) goto fail; } - pla = (struct fw_type_1 *)block; - if (!rtl8152_is_fw_type1_ok(tp, pla)) { + pla = (struct fw_mac *)block; + if (!rtl8152_is_fw_mac_ok(tp, pla)) { dev_err(&tp->intf->dev, - "load PLA firmware failed\n"); + "check PLA firmware failed\n"); goto fail; } break; @@ -3615,10 +3615,10 @@ static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw) goto fail; } - usb = (struct fw_type_1 *)block; - if (!rtl8152_is_fw_type1_ok(tp, usb)) { + usb = (struct fw_mac *)block; + if (!rtl8152_is_fw_mac_ok(tp, usb)) { dev_err(&tp->intf->dev, - "load USB firmware failed\n"); + "check USB firmware failed\n"); goto fail; } break; @@ -3638,14 +3638,14 @@ fail: return ret; } -static void rtl8152_fw_type_1_apply(struct r8152 *tp, struct fw_type_1 *type1) +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; u32 length; u8 *data; int i; - switch (__le32_to_cpu(type1->blk_hdr.type)) { + switch (__le32_to_cpu(mac->blk_hdr.type)) { case RTL_FW_PLA: type = MCU_TYPE_PLA; break; @@ -3667,36 +3667,36 @@ static void rtl8152_fw_type_1_apply(struct r8152 *tp, struct fw_type_1 *type1) 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); + length = __le32_to_cpu(mac->blk_hdr.length); + length -= __le16_to_cpu(mac->fw_offset); - data = (u8 *)type1; - data += __le16_to_cpu(type1->fw_offset); + data = (u8 *)mac; + data += __le16_to_cpu(mac->fw_offset); - generic_ocp_write(tp, __le16_to_cpu(type1->fw_reg), 0xff, length, data, + generic_ocp_write(tp, __le16_to_cpu(mac->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)); + ocp_write_word(tp, type, __le16_to_cpu(mac->bp_ba_addr), + __le16_to_cpu(mac->bp_ba_value)); - bp_index = __le16_to_cpu(type1->bp_start); - bp_num = __le16_to_cpu(type1->bp_num); + 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(type1->bp[i])); + ocp_write_word(tp, type, bp_index, __le16_to_cpu(mac->bp[i])); bp_index += 2; } - bp_en_addr = __le16_to_cpu(type1->bp_en_addr); + bp_en_addr = __le16_to_cpu(mac->bp_en_addr); if (bp_en_addr) ocp_write_word(tp, type, bp_en_addr, - __le16_to_cpu(type1->bp_en_value)); + __le16_to_cpu(mac->bp_en_value)); - fw_ver_reg = __le16_to_cpu(type1->fw_ver_reg); + fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg); if (fw_ver_reg) ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg, - type1->fw_ver_data); + mac->fw_ver_data); - dev_dbg(&tp->intf->dev, "successfully applied %s\n", type1->info); + dev_dbg(&tp->intf->dev, "successfully applied %s\n", mac->info); } static void rtl8152_apply_firmware(struct r8152 *tp) @@ -3720,7 +3720,7 @@ static void rtl8152_apply_firmware(struct r8152 *tp) goto post_fw; case RTL_FW_PLA: case RTL_FW_USB: - rtl8152_fw_type_1_apply(tp, (struct fw_type_1 *)block); + rtl8152_fw_mac_apply(tp, (struct fw_mac *)block); break; default: break; -- cgit v1.2.3 From 5a16a3d9f9b9714508d6c9ab69897576a3709566 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Mon, 21 Oct 2019 11:41:11 +0800 Subject: r8152: add checking fw_offset field of struct fw_mac Make sure @fw_offset field of struct fw_mac is more than the size of struct fw_mac. Signed-off-by: Hayes Wang Signed-off-by: Jakub Kicinski --- drivers/net/usb/r8152.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 55a7674a0c06..090ddd5fb973 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3399,7 +3399,7 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type) static bool rtl8152_is_fw_mac_ok(struct r8152 *tp, struct fw_mac *mac) { - u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start; + u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start, fw_offset; bool rc = false; u32 length, type; int i, max_bp; @@ -3461,13 +3461,19 @@ static bool rtl8152_is_fw_mac_ok(struct r8152 *tp, struct fw_mac *mac) goto out; } + fw_offset = __le16_to_cpu(mac->fw_offset); + if (fw_offset < sizeof(*mac)) { + dev_err(&tp->intf->dev, "fw_offset too small\n"); + goto out; + } + length = __le32_to_cpu(mac->blk_hdr.length); - if (length < __le16_to_cpu(mac->fw_offset)) { + if (length < fw_offset) { dev_err(&tp->intf->dev, "invalid fw_offset\n"); goto out; } - length -= __le16_to_cpu(mac->fw_offset); + length -= fw_offset; if (length < 4 || (length & 3)) { dev_err(&tp->intf->dev, "invalid block length\n"); goto out; -- cgit v1.2.3 From 470e39194a46c8e447fcf2801c9f3e683af8a475 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Mon, 21 Oct 2019 11:41:12 +0800 Subject: r8152: move r8153_patch_request forward Move r8153_patch_request() forward for later patch. Signed-off-by: Hayes Wang Signed-off-by: Jakub Kicinski --- drivers/net/usb/r8152.c | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 090ddd5fb973..ab57c672c4ca 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3397,6 +3397,33 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type) ocp_write_word(tp, type, PLA_BP_BA, 0); } +static int r8153_patch_request(struct r8152 *tp, bool request) +{ + u16 data; + int i; + + data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD); + if (request) + data |= PATCH_REQUEST; + else + data &= ~PATCH_REQUEST; + ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data); + + for (i = 0; request && i < 5000; i++) { + usleep_range(1000, 2000); + if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY) + break; + } + + if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) { + netif_err(tp, drv, tp->netdev, "patch request fail\n"); + r8153_patch_request(tp, false); + return -ETIME; + } else { + return 0; + } +} + static bool rtl8152_is_fw_mac_ok(struct r8152 *tp, struct fw_mac *mac) { u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start, fw_offset; @@ -4056,33 +4083,6 @@ static void r8152b_enter_oob(struct r8152 *tp) ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } -static int r8153_patch_request(struct r8152 *tp, bool request) -{ - u16 data; - int i; - - data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD); - if (request) - data |= PATCH_REQUEST; - else - data &= ~PATCH_REQUEST; - ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data); - - for (i = 0; request && i < 5000; i++) { - usleep_range(1000, 2000); - if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY) - break; - } - - if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) { - netif_err(tp, drv, tp->netdev, "patch request fail\n"); - r8153_patch_request(tp, false); - return -ETIME; - } else { - return 0; - } -} - static int r8153_pre_firmware_1(struct r8152 *tp) { int i; -- cgit v1.2.3 From af14288f945b4f2967acef8cec168f9c1a56eeb0 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Mon, 21 Oct 2019 11:41:13 +0800 Subject: r8152: support firmware of PHY NC for RTL8153A Support the firmware of PHY NC which is used to fix the issue found for PHY. Currently, only RTL_VER_04, RTL_VER_05, and RTL_VER_06 need it. The order of loading PHY firmware would be RTL_FW_PHY_START RTL_FW_PHY_NC RTL_FW_PHY_STOP The RTL_FW_PHY_START/RTL_FW_PHY_STOP are used to lock/unlock the PHY, and set/clear the patch key from the firmware file. Signed-off-by: Hayes Wang Signed-off-by: Jakub Kicinski --- drivers/net/usb/r8152.c | 284 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 282 insertions(+), 2 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index ab57c672c4ca..d3c30ccc8577 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -187,6 +187,7 @@ #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ #define OCP_PHY_PATCH_STAT 0xb800 #define OCP_PHY_PATCH_CMD 0xb820 +#define OCP_PHY_LOCK 0xb82e #define OCP_ADC_IOFFSET 0xbcfc #define OCP_ADC_CFG 0xbc06 #define OCP_SYSCLK_CFG 0xc416 @@ -197,6 +198,7 @@ #define SRAM_10M_AMP1 0x8080 #define SRAM_10M_AMP2 0x8082 #define SRAM_IMPEDANCE 0x8084 +#define SRAM_PHY_LOCK 0xb82e /* PLA_RCR */ #define RCR_AAP 0x00000001 @@ -577,6 +579,9 @@ enum spd_duplex { /* OCP_PHY_PATCH_CMD */ #define PATCH_REQUEST BIT(4) +/* OCP_PHY_LOCK */ +#define PATCH_LOCK BIT(0) + /* OCP_ADC_CFG */ #define CKADSEL_L 0x0100 #define ADC_EN 0x0080 @@ -601,6 +606,9 @@ enum spd_duplex { /* SRAM_IMPEDANCE */ #define RX_DRIVING_MASK 0x6000 +/* SRAM_PHY_LOCK */ +#define PHY_PATCH_LOCK 0x0001 + /* MAC PASSTHRU */ #define AD_MASK 0xfee0 #define BND_MASK 0x0004 @@ -905,10 +913,65 @@ struct fw_mac { char info[0]; } __packed; +/** + * struct fw_phy_patch_key - a firmware block used by RTL_FW_PHY_START. + * This is used to set patch key when loading the firmware of PHY. + * @key_reg: the register to write the patch key. + * @key_data: patch key. + */ +struct fw_phy_patch_key { + struct fw_block blk_hdr; + __le16 key_reg; + __le16 key_data; + __le32 reserved; +} __packed; + +/** + * struct fw_phy_nc - a firmware block used by RTL_FW_PHY_NC. + * The layout of the firmware block is: + * + + . + * @fw_offset: offset of the firmware binary data. The start address of + * the data would be the address of struct fw_phy_nc + @fw_offset. + * @fw_reg: the register to load the firmware. Depends on chip. + * @ba_reg: the register to write the base address. Depends on chip. + * @ba_data: base address. Depends on chip. + * @patch_en_addr: the register of enabling patch mode. Depends on chip. + * @patch_en_value: patch mode enabled mask. Depends on the firmware. + * @mode_reg: the regitster of switching the mode. + * @mod_pre: the mode needing to be set before loading the firmware. + * @mod_post: the mode to be set when finishing to load 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. + * @info: additional information for debugging, and is followed by the + * binary data of firmware. + */ +struct fw_phy_nc { + struct fw_block blk_hdr; + __le16 fw_offset; + __le16 fw_reg; + __le16 ba_reg; + __le16 ba_data; + __le16 patch_en_addr; + __le16 patch_en_value; + __le16 mode_reg; + __le16 mode_pre; + __le16 mode_post; + __le16 reserved; + __le16 bp_start; + __le16 bp_num; + __le16 bp[4]; + char info[0]; +} __packed; + enum rtl_fw_type { RTL_FW_END = 0, RTL_FW_PLA, RTL_FW_USB, + RTL_FW_PHY_START, + RTL_FW_PHY_STOP, + RTL_FW_PHY_NC, }; enum rtl_version { @@ -3424,6 +3487,114 @@ static int r8153_patch_request(struct r8152 *tp, bool request) } } +static int r8153_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key) +{ + if (r8153_patch_request(tp, true)) { + dev_err(&tp->intf->dev, "patch request fail\n"); + return -ETIME; + } + + sram_write(tp, key_addr, patch_key); + sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK); + + return 0; +} + +static int r8153_post_ram_code(struct r8152 *tp, u16 key_addr) +{ + u16 data; + + sram_write(tp, 0x0000, 0x0000); + + data = ocp_reg_read(tp, OCP_PHY_LOCK); + data &= ~PATCH_LOCK; + ocp_reg_write(tp, OCP_PHY_LOCK, data); + + sram_write(tp, key_addr, 0x0000); + + r8153_patch_request(tp, false); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base); + + return 0; +} + +static bool rtl8152_is_fw_phy_nc_ok(struct r8152 *tp, struct fw_phy_nc *phy) +{ + u32 length; + u16 fw_offset, fw_reg, ba_reg, patch_en_addr, mode_reg, bp_start; + bool rc = false; + + switch (tp->version) { + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + fw_reg = 0xa014; + ba_reg = 0xa012; + patch_en_addr = 0xa01a; + mode_reg = 0xb820; + bp_start = 0xa000; + break; + default: + goto out; + } + + fw_offset = __le16_to_cpu(phy->fw_offset); + if (fw_offset < sizeof(*phy)) { + dev_err(&tp->intf->dev, "fw_offset too small\n"); + goto out; + } + + length = __le32_to_cpu(phy->blk_hdr.length); + if (length < fw_offset) { + dev_err(&tp->intf->dev, "invalid fw_offset\n"); + goto out; + } + + length -= __le16_to_cpu(phy->fw_offset); + if (!length || (length & 1)) { + dev_err(&tp->intf->dev, "invalid block length\n"); + goto out; + } + + if (__le16_to_cpu(phy->fw_reg) != fw_reg) { + dev_err(&tp->intf->dev, "invalid register to load firmware\n"); + goto out; + } + + if (__le16_to_cpu(phy->ba_reg) != ba_reg) { + dev_err(&tp->intf->dev, "invalid base address register\n"); + goto out; + } + + if (__le16_to_cpu(phy->patch_en_addr) != patch_en_addr) { + dev_err(&tp->intf->dev, + "invalid patch mode enabled register\n"); + goto out; + } + + if (__le16_to_cpu(phy->mode_reg) != mode_reg) { + dev_err(&tp->intf->dev, + "invalid register to switch the mode\n"); + goto out; + } + + if (__le16_to_cpu(phy->bp_start) != bp_start) { + dev_err(&tp->intf->dev, + "invalid start register of break point\n"); + goto out; + } + + if (__le16_to_cpu(phy->bp_num) > 4) { + dev_err(&tp->intf->dev, "invalid break point number\n"); + goto out; + } + + rc = true; +out: + return rc; +} + static bool rtl8152_is_fw_mac_ok(struct r8152 *tp, struct fw_mac *mac) { u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start, fw_offset; @@ -3600,6 +3771,9 @@ 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_mac *pla = NULL, *usb = NULL; + struct fw_phy_patch_key *start = NULL; + struct fw_phy_nc *phy_nc = NULL; + struct fw_block *stop = NULL; long ret = -EFAULT; int i; @@ -3626,7 +3800,7 @@ static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw) case RTL_FW_END: if (__le32_to_cpu(block->length) != sizeof(*block)) goto fail; - goto success; + goto fw_end; case RTL_FW_PLA: if (pla) { dev_err(&tp->intf->dev, @@ -3654,6 +3828,57 @@ static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw) "check USB firmware failed\n"); goto fail; } + break; + case RTL_FW_PHY_START: + if (start || phy_nc || stop) { + dev_err(&tp->intf->dev, + "check PHY_START fail\n"); + goto fail; + } + + if (__le32_to_cpu(block->length) != sizeof(*start)) { + dev_err(&tp->intf->dev, + "Invalid length for PHY_START\n"); + goto fail; + } + + start = (struct fw_phy_patch_key *)block; + break; + case RTL_FW_PHY_STOP: + if (stop || !start) { + dev_err(&tp->intf->dev, + "Check PHY_STOP fail\n"); + goto fail; + } + + if (__le32_to_cpu(block->length) != sizeof(*block)) { + dev_err(&tp->intf->dev, + "Invalid length for PHY_STOP\n"); + goto fail; + } + + stop = block; + break; + case RTL_FW_PHY_NC: + if (!start || stop) { + dev_err(&tp->intf->dev, + "check PHY_NC fail\n"); + goto fail; + } + + if (phy_nc) { + dev_err(&tp->intf->dev, + "multiple PHY NC encountered\n"); + goto fail; + } + + phy_nc = (struct fw_phy_nc *)block; + if (!rtl8152_is_fw_phy_nc_ok(tp, phy_nc)) { + dev_err(&tp->intf->dev, + "check PHY NC firmware failed\n"); + goto fail; + } + break; default: dev_warn(&tp->intf->dev, "Unknown type %u is found\n", @@ -3665,12 +3890,52 @@ static long rtl8152_check_firmware(struct r8152 *tp, struct rtl_fw *rtl_fw) i += ALIGN(__le32_to_cpu(block->length), 8); } -success: +fw_end: + if ((phy_nc || start) && !stop) { + dev_err(&tp->intf->dev, "without PHY_STOP\n"); + goto fail; + } + return 0; fail: return ret; } +static void rtl8152_fw_phy_nc_apply(struct r8152 *tp, struct fw_phy_nc *phy) +{ + u16 mode_reg, bp_index; + u32 length, i, num; + __le16 *data; + + mode_reg = __le16_to_cpu(phy->mode_reg); + sram_write(tp, mode_reg, __le16_to_cpu(phy->mode_pre)); + sram_write(tp, __le16_to_cpu(phy->ba_reg), + __le16_to_cpu(phy->ba_data)); + + length = __le32_to_cpu(phy->blk_hdr.length); + length -= __le16_to_cpu(phy->fw_offset); + num = length / 2; + data = (__le16 *)((u8 *)phy + __le16_to_cpu(phy->fw_offset)); + + ocp_reg_write(tp, OCP_SRAM_ADDR, __le16_to_cpu(phy->fw_reg)); + for (i = 0; i < num; i++) + ocp_reg_write(tp, OCP_SRAM_DATA, __le16_to_cpu(data[i])); + + sram_write(tp, __le16_to_cpu(phy->patch_en_addr), + __le16_to_cpu(phy->patch_en_value)); + + bp_index = __le16_to_cpu(phy->bp_start); + num = __le16_to_cpu(phy->bp_num); + for (i = 0; i < num; i++) { + sram_write(tp, bp_index, __le16_to_cpu(phy->bp[i])); + bp_index += 2; + } + + sram_write(tp, mode_reg, __le16_to_cpu(phy->mode_post)); + + dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info); +} + 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; @@ -3737,6 +4002,8 @@ 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; + struct fw_phy_patch_key *key; + u16 key_addr = 0; int i; if (IS_ERR_OR_NULL(rtl_fw->fw)) @@ -3755,6 +4022,19 @@ static void rtl8152_apply_firmware(struct r8152 *tp) case RTL_FW_USB: rtl8152_fw_mac_apply(tp, (struct fw_mac *)block); break; + case RTL_FW_PHY_START: + key = (struct fw_phy_patch_key *)block; + key_addr = __le16_to_cpu(key->key_reg); + r8153_pre_ram_code(tp, key_addr, + __le16_to_cpu(key->key_data)); + break; + case RTL_FW_PHY_STOP: + WARN_ON(!key_addr); + r8153_post_ram_code(tp, key_addr); + break; + case RTL_FW_PHY_NC: + rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block); + break; default: break; } -- cgit v1.2.3 From 8e484ebb1edc8329e1f7c527d556038a395f654e Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Wed, 23 Oct 2019 21:24:44 +0800 Subject: r8152: check the pointer rtl_fw->fw before using it Fix the pointer rtl_fw->fw would be used before checking in rtl8152_apply_firmware() that causes the following kernel oops. 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 : [] lr : [] 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) ... [] (rtl8152_apply_firmware) from [] (r8153_hw_phy_cfg+0x24/0x17c) [] (r8153_hw_phy_cfg) from [] (rtl_hw_phy_work_func_t+0x220/0x3e4) [] (rtl_hw_phy_work_func_t) from [] (process_one_work+0x22c/0x7c8) [] (process_one_work) from [] (worker_thread+0x44/0x520) [] (worker_thread) from [] (kthread+0x130/0x164) [] (kthread) from [] (ret_from_fork+0x14/0x20) Exception stack(0xe75c9fb0 to 0xe75c9ff8) ... Fixes: 9370f2d05a2a ("r8152: support request_firmware for RTL8153") Reported-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/usb/r8152.c') 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); -- cgit v1.2.3 From 9647722befbedcd6735e00655ffec392c05f0c56 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 5 Nov 2019 19:24:52 +0800 Subject: r8152: Add macpassthru support for ThinkPad Thunderbolt 3 Dock Gen 2 ThinkPad Thunderbolt 3 Dock Gen 2 is another docking station that uses RTL8153 based USB ethernet. The device supports macpassthru, but it failed to pass the test of -AD, -BND and -BD. Simply bypass these tests since the device supports this feature just fine. Also the ACPI objects have some differences between Dell's and Lenovo's, so make those ACPI infos no longer hardcoded. BugLink: https://bugs.launchpad.net/bugs/1827961 Signed-off-by: Kai-Heng Feng Acked-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 58 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 19 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index b9f526ed0d30..ac079395c8d4 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -670,6 +670,7 @@ enum rtl8152_flags { SCHEDULE_TASKLET, GREEN_ETHERNET, DELL_TB_RX_AGG_BUG, + LENOVO_MACPASSTHRU, }; /* Define these values to match your device */ @@ -1408,38 +1409,52 @@ static int vendor_mac_passthru_addr_read(struct r8152 *tp, struct sockaddr *sa) int ret = -EINVAL; u32 ocp_data; unsigned char buf[6]; - - /* test for -AD variant of RTL8153 */ - ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); - if ((ocp_data & AD_MASK) == 0x1000) { - /* test for MAC address pass-through bit */ - ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE); - if ((ocp_data & PASS_THRU_MASK) != 1) { - netif_dbg(tp, probe, tp->netdev, - "No efuse for RTL8153-AD MAC pass through\n"); - return -ENODEV; - } + char *mac_obj_name; + acpi_object_type mac_obj_type; + int mac_strlen; + + if (test_bit(LENOVO_MACPASSTHRU, &tp->flags)) { + mac_obj_name = "\\MACA"; + mac_obj_type = ACPI_TYPE_STRING; + mac_strlen = 0x16; } else { - /* test for RTL8153-BND and RTL8153-BD */ - ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1); - if ((ocp_data & BND_MASK) == 0 && (ocp_data & BD_MASK) == 0) { - netif_dbg(tp, probe, tp->netdev, - "Invalid variant for MAC pass through\n"); - return -ENODEV; + /* test for -AD variant of RTL8153 */ + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); + if ((ocp_data & AD_MASK) == 0x1000) { + /* test for MAC address pass-through bit */ + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE); + if ((ocp_data & PASS_THRU_MASK) != 1) { + netif_dbg(tp, probe, tp->netdev, + "No efuse for RTL8153-AD MAC pass through\n"); + return -ENODEV; + } + } else { + /* test for RTL8153-BND and RTL8153-BD */ + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_1); + if ((ocp_data & BND_MASK) == 0 && (ocp_data & BD_MASK) == 0) { + netif_dbg(tp, probe, tp->netdev, + "Invalid variant for MAC pass through\n"); + return -ENODEV; + } } + + mac_obj_name = "\\_SB.AMAC"; + mac_obj_type = ACPI_TYPE_BUFFER; + mac_strlen = 0x17; } /* returns _AUXMAC_#AABBCCDDEEFF# */ - status = acpi_evaluate_object(NULL, "\\_SB.AMAC", NULL, &buffer); + status = acpi_evaluate_object(NULL, mac_obj_name, NULL, &buffer); obj = (union acpi_object *)buffer.pointer; if (!ACPI_SUCCESS(status)) return -ENODEV; - if (obj->type != ACPI_TYPE_BUFFER || obj->string.length != 0x17) { + if (obj->type != mac_obj_type || obj->string.length != mac_strlen) { netif_warn(tp, probe, tp->netdev, "Invalid buffer for pass-thru MAC addr: (%d, %d)\n", obj->type, obj->string.length); goto amacout; } + if (strncmp(obj->string.pointer, "_AUXMAC_#", 9) != 0 || strncmp(obj->string.pointer + 0x15, "#", 1) != 0) { netif_warn(tp, probe, tp->netdev, @@ -6629,6 +6644,10 @@ static int rtl8152_probe(struct usb_interface *intf, netdev->hw_features &= ~NETIF_F_RXCSUM; } + if (le16_to_cpu(udev->descriptor.idVendor) == VENDOR_ID_LENOVO && + le16_to_cpu(udev->descriptor.idProduct) == 0x3082) + set_bit(LENOVO_MACPASSTHRU, &tp->flags); + if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x3011 && udev->serial && (!strcmp(udev->serial, "000001000000") || !strcmp(udev->serial, "000002000000"))) { @@ -6755,6 +6774,7 @@ static const struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)}, {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)}, -- cgit v1.2.3 From 84811412464d66fcf73661a279aa1d7985166495 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Wed, 20 Nov 2019 11:40:21 -0800 Subject: r8152: Re-order napi_disable in rtl8152_close Both rtl_work_func_t() and rtl8152_close() call napi_disable(). Since the two calls aren't protected by a lock, if the close function starts executing before the work function, we can get into a situation where the napi_disable() function is called twice in succession (first by rtl8152_close(), then by set_carrier()). In such a situation, the second call would loop indefinitely, since rtl8152_close() doesn't call napi_enable() to clear the NAPI_STATE_SCHED bit. The rtl8152_close() function in turn issues a cancel_delayed_work_sync(), and so it would wait indefinitely for the rtl_work_func_t() to complete. Since rtl8152_close() is called by a process holding rtnl_lock() which is requested by other processes, this eventually leads to a system deadlock and crash. Re-order the napi_disable() call to occur after the work function disabling and urb cancellation calls are issued. Change-Id: I6ef0b703fc214998a037a68f722f784e1d07815e Reported-by: http://crbug.com/1017928 Signed-off-by: Prashant Malani Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index d4a95b50bda6..4d34c01826f3 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4283,10 +4283,10 @@ static int rtl8152_close(struct net_device *netdev) unregister_pm_notifier(&tp->pm_notifier); #endif tasklet_disable(&tp->tx_tl); - napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); + napi_disable(&tp->napi); netif_stop_queue(netdev); res = usb_autopm_get_interface(tp->intf); -- cgit v1.2.3 From 5b1d9c17a3e0c16e1c9adf9c8a89f2735cb6dff8 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Fri, 22 Nov 2019 16:21:09 +0800 Subject: r8152: avoid to call napi_disable twice Call napi_disable() twice would cause dead lock. There are three situations may result in the issue. 1. rtl8152_pre_reset() and set_carrier() are run at the same time. 2. Call rtl8152_set_tunable() after rtl8152_close(). 3. Call rtl8152_set_ringparam() after rtl8152_close(). For #1, use the same solution as commit 84811412464d ("r8152: Re-order napi_disable in rtl8152_close"). For #2 and #3, add checking the flag of IFF_UP and using napi_disable/napi_enable during mutex. Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/net/usb/r8152.c') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 4d34c01826f3..b2507c59ba8b 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4552,10 +4552,10 @@ static int rtl8152_pre_reset(struct usb_interface *intf) netif_stop_queue(netdev); tasklet_disable(&tp->tx_tl); - napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); + napi_disable(&tp->napi); if (netif_carrier_ok(netdev)) { mutex_lock(&tp->control); tp->rtl_ops.disable(tp); @@ -4673,7 +4673,7 @@ static int rtl8152_system_resume(struct r8152 *tp) netif_device_attach(netdev); - if (netif_running(netdev) && netdev->flags & IFF_UP) { + if (netif_running(netdev) && (netdev->flags & IFF_UP)) { tp->rtl_ops.up(tp); netif_carrier_off(netdev); set_bit(WORK_ENABLE, &tp->flags); @@ -5244,9 +5244,15 @@ static int rtl8152_set_tunable(struct net_device *netdev, } if (tp->rx_copybreak != val) { - napi_disable(&tp->napi); - tp->rx_copybreak = val; - napi_enable(&tp->napi); + if (netdev->flags & IFF_UP) { + mutex_lock(&tp->control); + napi_disable(&tp->napi); + tp->rx_copybreak = val; + napi_enable(&tp->napi); + mutex_unlock(&tp->control); + } else { + tp->rx_copybreak = val; + } } break; default: @@ -5274,9 +5280,15 @@ static int rtl8152_set_ringparam(struct net_device *netdev, return -EINVAL; if (tp->rx_pending != ring->rx_pending) { - napi_disable(&tp->napi); - tp->rx_pending = ring->rx_pending; - napi_enable(&tp->napi); + if (netdev->flags & IFF_UP) { + mutex_lock(&tp->control); + napi_disable(&tp->napi); + tp->rx_pending = ring->rx_pending; + napi_enable(&tp->napi); + mutex_unlock(&tp->control); + } else { + tp->rx_pending = ring->rx_pending; + } } return 0; -- cgit v1.2.3