Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67169247 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "wil6210: support parsing brd file address from fw file"

parents 2082a3b1 8196191f
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -39,7 +40,8 @@ struct bl_dedicated_registers_v1 {
	/* valid only for version 2 and above */
	__le32  bl_assert_code;         /* 0x880A58 BL Assert code */
	__le32  bl_assert_blink;        /* 0x880A5C BL Assert Branch */
	__le32  bl_reserved[22];        /* 0x880A60 - 0x880AB4 */
	__le32  bl_shutdown_handshake;  /* 0x880A60 BL cleaner shutdown */
	__le32  bl_reserved[21];        /* 0x880A64 - 0x880AB4 */
	__le32  bl_magic_number;        /* 0x880AB8 BL Magic number */
} __packed;

@@ -58,4 +60,9 @@ struct bl_dedicated_registers_v0 {
	u8	mac_address[6];			/* 0x880A4c BL mac address */
} __packed;

/* bits for bl_shutdown_handshake */
#define BL_SHUTDOWN_HS_GRTD		BIT(0)
#define BL_SHUTDOWN_HS_RTD		BIT(1)
#define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31)

#endif /* BOOT_LOADER_EXPORT_H_ */
+17 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -58,15 +59,30 @@ struct wil_fw_record_comment { /* type == wil_fw_type_comment */
	u8 data[0]; /* free-form data [data_size], see above */
} __packed;

/* Comment header - common for all comment record types */
struct wil_fw_record_comment_hdr {
	__le32 magic;
};

/* FW capabilities encoded inside a comment record */
#define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba)
struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
	/* identifies capabilities record */
	__le32 magic;
	struct wil_fw_record_comment_hdr hdr;
	/* capabilities (variable size), see enum wmi_fw_capability */
	u8 capabilities[0];
};

/* brd file info encoded inside a comment record */
#define WIL_BRD_FILE_MAGIC (0xabcddcbb)
struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */
	/* identifies brd file record */
	struct wil_fw_record_comment_hdr hdr;
	__le32 version;
	__le32 base_addr;
	__le32 max_size_bytes;
} __packed;

/* perform action
 * data_size = @head.size - offsetof(struct wil_fw_record_action, data)
 */
+158 −9
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -128,14 +129,13 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
}

static int
fw_handle_comment(struct wil6210_priv *wil, const void *data,
fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
		       size_t size)
{
	const struct wil_fw_record_capabilities *rec = data;
	size_t capa_size;

	if (size < sizeof(*rec) ||
	    le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
	if (size < sizeof(*rec)) {
		wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
				data, size, true);
		return 0;
@@ -151,8 +151,56 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data,
	return 0;
}

static int fw_handle_data(struct wil6210_priv *wil, const void *data,
static int
fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
		   size_t size)
{
	const struct wil_fw_record_brd_file *rec = data;

	if (size < sizeof(*rec)) {
		wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
				data, size, true);
		return 0;
	}

	wil->brd_file_addr = le32_to_cpu(rec->base_addr);
	wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes);

	wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n",
		   wil->brd_file_addr, wil->brd_file_max_size);

	return 0;
}

static int
fw_handle_comment(struct wil6210_priv *wil, const void *data,
		  size_t size)
{
	const struct wil_fw_record_comment_hdr *hdr = data;
	u32 magic;
	int rc = 0;

	if (size < sizeof(*hdr))
		return 0;

	magic = le32_to_cpu(hdr->magic);

	switch (magic) {
	case WIL_FW_CAPABILITIES_MAGIC:
		wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n");
		rc = fw_handle_capabilities(wil, data, size);
		break;
	case WIL_BRD_FILE_MAGIC:
		wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
		rc = fw_handle_brd_file(wil, data, size);
		break;
	}

	return rc;
}

static int __fw_handle_data(struct wil6210_priv *wil, const void *data,
			    size_t size, __le32 addr)
{
	const struct wil_fw_record_data *d = data;
	void __iomem *dst;
@@ -163,16 +211,23 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
		return -EINVAL;
	}

	if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
	if (!wil_fw_addr_check(wil, &dst, addr, s, "address"))
		return -EINVAL;
	wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
		   s);
	wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s);
	wil_memcpy_toio_32(dst, d->data, s);
	wmb(); /* finish before processing next record */

	return 0;
}

static int fw_handle_data(struct wil6210_priv *wil, const void *data,
			  size_t size)
{
	const struct wil_fw_record_data *d = data;

	return __fw_handle_data(wil, data, size, d->addr);
}

static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
			  size_t size)
{
@@ -551,6 +606,100 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
	return rc;
}

/**
 * wil_brd_process - process section from BRD file
 *
 * Return error code
 */
static int wil_brd_process(struct wil6210_priv *wil, const void *data,
			   size_t size)
{
	int rc = 0;
	const struct wil_fw_record_head *hdr = data;
	size_t s, hdr_sz;
	u16 type;

	/* Assuming the board file includes only one header record and one data
	 * record. Each record starts with wil_fw_record_head.
	 */
	if (size < sizeof(*hdr))
		return -EINVAL;
	s = sizeof(*hdr) + le32_to_cpu(hdr->size);
	if (s > size)
		return -EINVAL;

	/* Skip the header record and handle the data record */
	hdr = (const void *)hdr + s;
	size -= s;
	if (size < sizeof(*hdr))
		return -EINVAL;
	hdr_sz = le32_to_cpu(hdr->size);

	if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size)
		return -EINVAL;
	if (sizeof(*hdr) + hdr_sz > size)
		return -EINVAL;
	if (hdr_sz % 4) {
		wil_err_fw(wil, "unaligned record size: %zu\n",
			   hdr_sz);
		return -EINVAL;
	}
	type = le16_to_cpu(hdr->type);
	if (type != wil_fw_type_data) {
		wil_err_fw(wil, "invalid record type for board file: %d\n",
			   type);
		return -EINVAL;
	}
	if (hdr_sz < sizeof(struct wil_fw_record_data)) {
		wil_err_fw(wil, "data record too short: %zu\n", hdr_sz);
		return -EINVAL;
	}

	wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n",
		   wil->brd_file_addr);

	rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
			      cpu_to_le32(wil->brd_file_addr));

	return rc;
}

/**
 * wil_request_board - Request board file
 *
 * Request board image from the file
 * board file address and max size are read from FW file
 * during initialization.
 * brd file shall include one header and one data section.
 *
 * Return error code
 */
int wil_request_board(struct wil6210_priv *wil, const char *name)
{
	int rc, dlen;
	const struct firmware *brd;

	rc = request_firmware(&brd, name, wil_to_dev(wil));
	if (rc) {
		wil_err_fw(wil, "Failed to load brd %s\n", name);
		return rc;
	}
	wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size);

	/* Verify the header */
	dlen = wil_fw_verify(wil, brd->data, brd->size);
	if (dlen < 0) {
		rc = dlen;
		goto out;
	}
	/* Process the data record */
	rc = wil_brd_process(wil, brd->data, dlen);

out:
	release_firmware(brd);
	return rc;
}

/**
 * wil_fw_verify_file_exists - checks if firmware file exist
 *
+4 −2
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -395,8 +396,9 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
	wil6210_mask_irq_misc(wil, false);

	if (isr & ISR_MISC_FW_ERROR) {
		u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE);
		u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE);
		u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr);
		u32 ucode_assert_code =
			wil_r(wil, wil->rgf_ucode_assert_code_addr);

		wil_err(wil,
			"Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",
+191 −26
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -642,6 +643,98 @@ void wil_priv_deinit(struct wil6210_priv *wil)
	destroy_workqueue(wil->wmi_wq);
}

static void wil_shutdown_bl(struct wil6210_priv *wil)
{
	u32 val;

	wil_s(wil, RGF_USER_BL +
	      offsetof(struct bl_dedicated_registers_v1,
		       bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD);

	usleep_range(100, 150);

	val = wil_r(wil, RGF_USER_BL +
		    offsetof(struct bl_dedicated_registers_v1,
			     bl_shutdown_handshake));
	if (val & BL_SHUTDOWN_HS_RTD) {
		wil_dbg_misc(wil, "BL is ready for halt\n");
		return;
	}

	wil_err(wil, "BL did not report ready for halt\n");
}

/* this format is used by ARC embedded CPU for instruction memory */
static inline u32 ARC_me_imm32(u32 d)
{
	return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16);
}

/* defines access to interrupt vectors for wil_freeze_bl */
#define ARC_IRQ_VECTOR_OFFSET(N)	((N) * 8)
/* ARC long jump instruction */
#define ARC_JAL_INST			(0x20200f80)

static void wil_freeze_bl(struct wil6210_priv *wil)
{
	u32 jal, upc, saved;
	u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3);

	jal = wil_r(wil, wil->iccm_base + ivt3);
	if (jal != ARC_me_imm32(ARC_JAL_INST)) {
		wil_dbg_misc(wil, "invalid IVT entry found, skipping\n");
		return;
	}

	/* prevent the target from entering deep sleep
	 * and disabling memory access
	 */
	saved = wil_r(wil, RGF_USER_USAGE_8);
	wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP);
	usleep_range(20, 25); /* let the BL process the bit */

	/* redirect to endless loop in the INT_L1 context and let it trap */
	wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3));
	usleep_range(20, 25); /* let the BL get into the trap */

	/* verify the BL is frozen */
	upc = wil_r(wil, RGF_USER_CPU_PC);
	if (upc < ivt3 || (upc > (ivt3 + 8)))
		wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc);

	wil_w(wil, RGF_USER_USAGE_8, saved);
}

static void wil_bl_prepare_halt(struct wil6210_priv *wil)
{
	u32 tmp, ver;

	/* before halting device CPU driver must make sure BL is not accessing
	 * host memory. This is done differently depending on BL version:
	 * 1. For very old BL versions the procedure is skipped
	 * (not supported).
	 * 2. For old BL version we use a special trick to freeze the BL
	 * 3. For new BL versions we shutdown the BL using handshake procedure.
	 */
	tmp = wil_r(wil, RGF_USER_BL +
		    offsetof(struct bl_dedicated_registers_v0,
			     boot_loader_struct_version));
	if (!tmp) {
		wil_dbg_misc(wil, "old BL, skipping halt preperation\n");
		return;
	}

	tmp = wil_r(wil, RGF_USER_BL +
		    offsetof(struct bl_dedicated_registers_v1,
			     bl_shutdown_handshake));
	ver = BL_SHUTDOWN_HS_PROT_VER(tmp);

	if (ver > 0)
		wil_shutdown_bl(wil);
	else
		wil_freeze_bl(wil);
}

static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
	wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
@@ -675,7 +768,7 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode)
	}
}

static int wil_target_reset(struct wil6210_priv *wil)
static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
{
	int delay = 0;
	u32 x, x1 = 0;
@@ -689,9 +782,16 @@ static int wil_target_reset(struct wil6210_priv *wil)

	wil_halt_cpu(wil);

	if (!no_flash) {
		/* clear all boot loader "ready" bits */
		wil_w(wil, RGF_USER_BL +
	      offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
		      offsetof(struct bl_dedicated_registers_v0,
			       boot_loader_ready), 0);
		/* this should be safe to write even with old BLs */
		wil_w(wil, RGF_USER_BL +
		      offsetof(struct bl_dedicated_registers_v1,
			       bl_shutdown_handshake), 0);
	}
	/* Clear Fw Download notification */
	wil_c(wil, RGF_USER_USAGE_6, BIT(0));

@@ -732,13 +832,25 @@ static int wil_target_reset(struct wil6210_priv *wil)
	wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);

	/* wait until device ready. typical time is 20..80 msec */
	if (no_flash)
		do {
			msleep(RST_DELAY);
			x = wil_r(wil, USER_EXT_USER_PMU_3);
			if (delay++ > RST_COUNT) {
				wil_err(wil, "Reset not completed, PMU_3 0x%08x\n",
					x);
				return -ETIME;
			}
		} while ((x & BIT_PMU_DEVICE_RDY) == 0);
	else
		do {
			msleep(RST_DELAY);
			x = wil_r(wil, RGF_USER_BL +
				  offsetof(struct bl_dedicated_registers_v0,
					   boot_loader_ready));
			if (x1 != x) {
			wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
				wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n",
					     x1, x);
				x1 = x;
			}
			if (delay++ > RST_COUNT) {
@@ -754,6 +866,21 @@ static int wil_target_reset(struct wil6210_priv *wil)
	wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
	      BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);

	if (no_flash) {
		/* Reset OTP HW vectors to fit 40MHz */
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME3, 0x1);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME4, 0x20027);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME5, 0x30003);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME6, 0x20002);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME7, 0x60001);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME8, 0x60001);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME9, 0x60001);
		wil_w(wil, RGF_USER_XPM_IFC_RD_TIME10, 0x60001);
		wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57);
	}

	wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
	return 0;
}
@@ -911,6 +1038,27 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err)
	}
}

static int wil_get_otp_info(struct wil6210_priv *wil)
{
	struct net_device *ndev = wil_to_ndev(wil);
	struct wiphy *wiphy = wil_to_wiphy(wil);
	u8 mac[8];

	wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC),
			     sizeof(mac));
	if (!is_valid_ether_addr(mac)) {
		wil_err(wil, "Invalid MAC %pM\n", mac);
		return -EINVAL;
	}

	ether_addr_copy(ndev->perm_addr, mac);
	ether_addr_copy(wiphy->perm_addr, mac);
	if (!is_valid_ether_addr(ndev->dev_addr))
		ether_addr_copy(ndev->dev_addr, mac);

	return 0;
}

static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
	ulong to = msecs_to_jiffies(1000);
@@ -1004,6 +1152,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
{
	int rc;
	unsigned long status_flags = BIT(wil_status_resetting);
	int no_flash;

	wil_dbg_misc(wil, "reset\n");

@@ -1082,20 +1231,28 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
	flush_workqueue(wil->wq_service);
	flush_workqueue(wil->wmi_wq);

	no_flash = test_bit(hw_capa_no_flash, wil->hw_capa);
	if (!no_flash)
		wil_bl_crash_info(wil, false);
	wil_disable_irq(wil);
	rc = wil_target_reset(wil);
	rc = wil_target_reset(wil, no_flash);
	wil6210_clear_irq(wil);
	wil_enable_irq(wil);
	wil_rx_fini(wil);
	if (rc) {
		if (!no_flash)
			wil_bl_crash_info(wil, true);
		goto out;
	}

	if (no_flash) {
		rc = wil_get_otp_info(wil);
	} else {
		rc = wil_get_bl_info(wil);
	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
		if (rc == -EAGAIN && !load_fw)
			/* ignore RF error if not going up */
			rc = 0;
	}
	if (rc)
		goto out;

@@ -1104,13 +1261,21 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
		wil_info(wil, "Use firmware <%s> + board <%s>\n",
			 wil->wil_fw_name, WIL_BOARD_FILE_NAME);

		if (!no_flash)
			wil_bl_prepare_halt(wil);

		wil_halt_cpu(wil);
		memset(wil->fw_version, 0, sizeof(wil->fw_version));
		/* Loading f/w from the file */
		rc = wil_request_firmware(wil, wil->wil_fw_name, true);
		if (rc)
			goto out;
		rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
		if (wil->brd_file_addr)
			rc = wil_request_board(wil, WIL_BOARD_FILE_NAME);
		else
			rc = wil_request_firmware(wil,
						  WIL_BOARD_FILE_NAME,
						  true);
		if (rc)
			goto out;

Loading