diff options
Diffstat (limited to 'drivers/net/usb/r8152.c')
-rw-r--r-- | drivers/net/usb/r8152.c | 428 |
1 files changed, 357 insertions, 71 deletions
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 55d0bcb00aef..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 @@ -867,11 +875,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: - * <struct fw_type_1> + <info> + <firmware data>. + * <struct fw_mac> + <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. + * 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 +896,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; @@ -905,10 +913,65 @@ struct fw_type_1 { 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: + * <struct fw_phy_nc> + <info> + <firmware data>. + * @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 { @@ -3397,14 +3460,149 @@ 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 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_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; + u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start, fw_offset; 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 +3659,52 @@ 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)) { + 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 < fw_offset) { dev_err(&tp->intf->dev, "invalid fw_offset\n"); goto out; } - length -= __le16_to_cpu(type1->fw_offset); + length -= 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 +3770,10 @@ 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; + struct fw_phy_patch_key *start = NULL; + struct fw_phy_nc *phy_nc = NULL; + struct fw_block *stop = NULL; long ret = -EFAULT; int i; @@ -3593,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, @@ -3601,10 +3808,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,12 +3822,63 @@ 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, + "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, - "load USB firmware failed\n"); + "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", @@ -3632,20 +3890,60 @@ 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_type_1_apply(struct r8152 *tp, struct fw_type_1 *type1) +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; 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 +3965,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) @@ -3704,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)) @@ -3720,7 +4020,20 @@ 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; + 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; @@ -4050,33 +4363,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; |