forked from luck/tmp_suning_uos_patched
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 <hayeswang@realtek.com> Reviewed-by: Prashant Malani <pmalani@chromium.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d9f45ab9e6
commit
9370f2d05a
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user