Commit 88238d2d authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'r8152-phy-firmware'



Hayes Wang says:

====================
Support loading the firmware of the PHY with the type of RTL_FW_PHY_NC.
====================

Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
parents 39438490 af14288f
Loading
Loading
Loading
Loading
+357 −71
Original line number Diff line number Diff line
@@ -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)
{
	u16 fw_reg, bp_ba_addr, bp_en_addr, bp_start;
	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;
	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;