summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2019-11-04 11:29:12 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2019-11-04 11:29:12 +0000
commit84a4fa3850a51c40a214f58321a2e983ccef38b2 (patch)
tree44c31430ce3e1e04dcd1ff42eeffdc4aec571bd7 /sys/dev
parent77dd3f5c31cd9580d6d4bb755c304c002476b8e3 (diff)
Add support for iwm firmware paging, required for newer 8k device firmware.
Patch by Imre Vadasz Tested for regressions by phessler and Krystian Lewandowski No very obvious mistakes found by kettenis@ ok patrick@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/if_iwm.c290
-rw-r--r--sys/dev/pci/if_iwmreg.h56
-rw-r--r--sys/dev/pci/if_iwmvar.h21
3 files changed, 363 insertions, 4 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 211f1ca47de..529bd80fe0b 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.266 2019/10/28 18:11:10 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.267 2019/11/04 11:29:11 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -457,6 +457,9 @@ int iwm_sf_config(struct iwm_softc *, int);
int iwm_send_bt_init_conf(struct iwm_softc *);
int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *);
void iwm_tt_tx_backoff(struct iwm_softc *, uint32_t);
+void iwm_free_fw_paging(struct iwm_softc *);
+int iwm_save_fw_paging(struct iwm_softc *, const struct iwm_fw_sects *);
+int iwm_send_paging_cmd(struct iwm_softc *, const struct iwm_fw_sects *);
int iwm_init_hw(struct iwm_softc *);
int iwm_init(struct ifnet *);
void iwm_start(struct ifnet *);
@@ -584,6 +587,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
struct iwm_ucode_tlv tlv;
uint32_t tlv_type;
uint8_t *data;
+ uint32_t usniffer_img;
+ uint32_t paging_mem_size;
int err;
size_t len;
@@ -787,6 +792,37 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
goto parse_out;
break;
+ case IWM_UCODE_TLV_PAGING:
+ if (tlv_len != sizeof(uint32_t)) {
+ err = EINVAL;
+ goto parse_out;
+ }
+ paging_mem_size = le32toh(*(const uint32_t *)tlv_data);
+
+ DPRINTF(("%s: Paging: paging enabled (size = %u bytes)\n",
+ DEVNAME(sc), paging_mem_size));
+ if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) {
+ printf("%s: Driver only supports up to %u"
+ " bytes for paging image (%u requested)\n",
+ DEVNAME(sc), IWM_MAX_PAGING_IMAGE_SIZE,
+ paging_mem_size);
+ err = EINVAL;
+ goto out;
+ }
+ if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) {
+ printf("%s: Paging: image isn't multiple of %u\n",
+ DEVNAME(sc), IWM_FW_PAGING_SIZE);
+ err = EINVAL;
+ goto out;
+ }
+
+ fw->fw_sects[IWM_UCODE_TYPE_REGULAR].paging_mem_size =
+ paging_mem_size;
+ usniffer_img = IWM_UCODE_TYPE_REGULAR_USNIFFER;
+ fw->fw_sects[usniffer_img].paging_mem_size =
+ paging_mem_size;
+ break;
+
case IWM_UCODE_TLV_N_SCAN_CHANNELS:
if (tlv_len != sizeof(uint32_t)) {
err = EINVAL;
@@ -3219,6 +3255,7 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc,
enum iwm_ucode_type ucode_type)
{
enum iwm_ucode_type old_type = sc->sc_uc_current;
+ struct iwm_fw_sects *fw = &sc->sc_fw.fw_sects[ucode_type];
int err;
err = iwm_read_firmware(sc, ucode_type);
@@ -3237,7 +3274,33 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc,
return err;
}
- return iwm_post_alive(sc);
+ err = iwm_post_alive(sc);
+ if (err)
+ return err;
+
+ /*
+ * configure and operate fw paging mechanism.
+ * driver configures the paging flow only once, CPU2 paging image
+ * included in the IWM_UCODE_INIT image.
+ */
+ if (fw->paging_mem_size) {
+ err = iwm_save_fw_paging(sc, fw);
+ if (err) {
+ printf("%s: failed to save the FW paging image\n",
+ DEVNAME(sc));
+ return err;
+ }
+
+ err = iwm_send_paging_cmd(sc, fw);
+ if (err) {
+ printf("%s: failed to send the paging cmd\n",
+ DEVNAME(sc));
+ iwm_free_fw_paging(sc);
+ return err;
+ }
+ }
+
+ return 0;
}
int
@@ -6348,6 +6411,227 @@ iwm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff)
iwm_send_cmd(sc, &cmd);
}
+void
+iwm_free_fw_paging(struct iwm_softc *sc)
+{
+ int i;
+
+ if (sc->fw_paging_db[0].fw_paging_block.vaddr == NULL)
+ return;
+
+ for (i = 0; i < IWM_NUM_OF_FW_PAGING_BLOCKS; i++) {
+ iwm_dma_contig_free(&sc->fw_paging_db[i].fw_paging_block);
+ }
+
+ memset(sc->fw_paging_db, 0, sizeof(sc->fw_paging_db));
+}
+
+int
+iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image)
+{
+ int sec_idx, idx;
+ uint32_t offset = 0;
+
+ /*
+ * find where is the paging image start point:
+ * if CPU2 exist and it's in paging format, then the image looks like:
+ * CPU1 sections (2 or more)
+ * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2
+ * CPU2 sections (not paged)
+ * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2
+ * non paged to CPU2 paging sec
+ * CPU2 paging CSS
+ * CPU2 paging image (including instruction and data)
+ */
+ for (sec_idx = 0; sec_idx < IWM_UCODE_SECT_MAX; sec_idx++) {
+ if (image->fw_sect[sec_idx].fws_devoff ==
+ IWM_PAGING_SEPARATOR_SECTION) {
+ sec_idx++;
+ break;
+ }
+ }
+
+ /*
+ * If paging is enabled there should be at least 2 more sections left
+ * (one for CSS and one for Paging data)
+ */
+ if (sec_idx >= nitems(image->fw_sect) - 1) {
+ printf("%s: Paging: Missing CSS and/or paging sections\n",
+ DEVNAME(sc));
+ iwm_free_fw_paging(sc);
+ return EINVAL;
+ }
+
+ /* copy the CSS block to the dram */
+ DPRINTF(("%s: Paging: load paging CSS to FW, sec = %d\n",
+ DEVNAME(sc), sec_idx));
+
+ memcpy(sc->fw_paging_db[0].fw_paging_block.vaddr,
+ image->fw_sect[sec_idx].fws_data,
+ sc->fw_paging_db[0].fw_paging_size);
+
+ DPRINTF(("%s: Paging: copied %d CSS bytes to first block\n",
+ DEVNAME(sc), sc->fw_paging_db[0].fw_paging_size));
+
+ sec_idx++;
+
+ /*
+ * copy the paging blocks to the dram
+ * loop index start from 1 since that CSS block already copied to dram
+ * and CSS index is 0.
+ * loop stop at num_of_paging_blk since that last block is not full.
+ */
+ for (idx = 1; idx < sc->num_of_paging_blk; idx++) {
+ memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr,
+ (const char *)image->fw_sect[sec_idx].fws_data + offset,
+ sc->fw_paging_db[idx].fw_paging_size);
+
+ DPRINTF(("%s: Paging: copied %d paging bytes to block %d\n",
+ DEVNAME(sc), sc->fw_paging_db[idx].fw_paging_size, idx));
+
+ offset += sc->fw_paging_db[idx].fw_paging_size;
+ }
+
+ /* copy the last paging block */
+ if (sc->num_of_pages_in_last_blk > 0) {
+ memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr,
+ (const char *)image->fw_sect[sec_idx].fws_data + offset,
+ IWM_FW_PAGING_SIZE * sc->num_of_pages_in_last_blk);
+
+ DPRINTF(("%s: Paging: copied %d pages in the last block %d\n",
+ DEVNAME(sc), sc->num_of_pages_in_last_blk, idx));
+ }
+
+ return 0;
+}
+
+int
+iwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image)
+{
+ int blk_idx = 0;
+ int error, num_of_pages;
+
+ if (sc->fw_paging_db[0].fw_paging_block.vaddr != NULL) {
+ int i;
+ /* Device got reset, and we setup firmware paging again */
+ bus_dmamap_sync(sc->sc_dmat,
+ sc->fw_paging_db[0].fw_paging_block.map,
+ 0, IWM_FW_PAGING_SIZE,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ for (i = 1; i < sc->num_of_paging_blk + 1; i++) {
+ bus_dmamap_sync(sc->sc_dmat,
+ sc->fw_paging_db[i].fw_paging_block.map,
+ 0, IWM_PAGING_BLOCK_SIZE,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ }
+ return 0;
+ }
+
+ /* ensure IWM_BLOCK_2_EXP_SIZE is power of 2 of IWM_PAGING_BLOCK_SIZE */
+#if (1 << IWM_BLOCK_2_EXP_SIZE) != IWM_PAGING_BLOCK_SIZE
+#error IWM_BLOCK_2_EXP_SIZE must be power of 2 of IWM_PAGING_BLOCK_SIZE
+#endif
+
+ num_of_pages = image->paging_mem_size / IWM_FW_PAGING_SIZE;
+ sc->num_of_paging_blk =
+ ((num_of_pages - 1) / IWM_NUM_OF_PAGE_PER_GROUP) + 1;
+
+ sc->num_of_pages_in_last_blk =
+ num_of_pages -
+ IWM_NUM_OF_PAGE_PER_GROUP * (sc->num_of_paging_blk - 1);
+
+ DPRINTF(("%s: Paging: allocating mem for %d paging blocks, each block"
+ " holds 8 pages, last block holds %d pages\n", DEVNAME(sc),
+ sc->num_of_paging_blk,
+ sc->num_of_pages_in_last_blk));
+
+ /* allocate block of 4Kbytes for paging CSS */
+ error = iwm_dma_contig_alloc(sc->sc_dmat,
+ &sc->fw_paging_db[blk_idx].fw_paging_block, IWM_FW_PAGING_SIZE,
+ 4096);
+ if (error) {
+ /* free all the previous pages since we failed */
+ iwm_free_fw_paging(sc);
+ return ENOMEM;
+ }
+
+ sc->fw_paging_db[blk_idx].fw_paging_size = IWM_FW_PAGING_SIZE;
+
+ DPRINTF(("%s: Paging: allocated 4K(CSS) bytes for firmware paging.\n",
+ DEVNAME(sc)));
+
+ /*
+ * allocate blocks in dram.
+ * since that CSS allocated in fw_paging_db[0] loop start from index 1
+ */
+ for (blk_idx = 1; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) {
+ /* allocate block of IWM_PAGING_BLOCK_SIZE (32K) */
+ /* XXX Use iwm_dma_contig_alloc for allocating */
+ error = iwm_dma_contig_alloc(sc->sc_dmat,
+ &sc->fw_paging_db[blk_idx].fw_paging_block,
+ IWM_PAGING_BLOCK_SIZE, 4096);
+ if (error) {
+ /* free all the previous pages since we failed */
+ iwm_free_fw_paging(sc);
+ return ENOMEM;
+ }
+
+ sc->fw_paging_db[blk_idx].fw_paging_size =
+ IWM_PAGING_BLOCK_SIZE;
+
+ DPRINTF((
+ "%s: Paging: allocated 32K bytes for firmware paging.\n",
+ DEVNAME(sc)));
+ }
+
+ return 0;
+}
+
+int
+iwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_sects *fw)
+{
+ int ret;
+
+ ret = iwm_alloc_fw_paging_mem(sc, fw);
+ if (ret)
+ return ret;
+
+ return iwm_fill_paging_mem(sc, fw);
+}
+
+/* send paging cmd to FW in case CPU2 has paging image */
+int
+iwm_send_paging_cmd(struct iwm_softc *sc, const struct iwm_fw_sects *fw)
+{
+ int blk_idx;
+ uint32_t dev_phy_addr;
+ struct iwm_fw_paging_cmd fw_paging_cmd = {
+ .flags =
+ htole32(IWM_PAGING_CMD_IS_SECURED |
+ IWM_PAGING_CMD_IS_ENABLED |
+ (sc->num_of_pages_in_last_blk <<
+ IWM_PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)),
+ .block_size = htole32(IWM_BLOCK_2_EXP_SIZE),
+ .block_num = htole32(sc->num_of_paging_blk),
+ };
+
+ /* loop for for all paging blocks + CSS block */
+ for (blk_idx = 0; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) {
+ dev_phy_addr = htole32(
+ sc->fw_paging_db[blk_idx].fw_paging_block.paddr >>
+ IWM_PAGE_2_EXP_SIZE);
+ fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr;
+ bus_dmamap_sync(sc->sc_dmat,
+ sc->fw_paging_db[blk_idx].fw_paging_block.map, 0,
+ blk_idx == 0 ? IWM_FW_PAGING_SIZE : IWM_PAGING_BLOCK_SIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+
+ return iwm_send_cmd_pdu(sc, iwm_cmd_id(IWM_FW_PAGING_BLOCK_CMD,
+ IWM_LONG_GROUP, 0),
+ 0, sizeof(fw_paging_cmd), &fw_paging_cmd);
+}
+
int
iwm_init_hw(struct iwm_softc *sc)
{
@@ -7188,6 +7472,8 @@ iwm_notif_intr(struct iwm_softc *sc)
case IWM_REMOVE_STA:
case IWM_TXPATH_FLUSH:
case IWM_LQ_CMD:
+ case IWM_WIDE_ID(IWM_LONG_GROUP,
+ IWM_FW_PAGING_BLOCK_CMD):
case IWM_BT_CONFIG:
case IWM_REPLY_THERMAL_MNG_BACKOFF:
case IWM_NVM_ACCESS_CMD:
diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h
index f1f1f12bf46..e9ecb0c1cb8 100644
--- a/sys/dev/pci/if_iwmreg.h
+++ b/sys/dev/pci/if_iwmreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmreg.h,v 1.37 2019/10/28 18:11:10 stsp Exp $ */
+/* $OpenBSD: if_iwmreg.h,v 1.38 2019/11/04 11:29:11 stsp Exp $ */
/******************************************************************************
*
@@ -1737,6 +1737,9 @@ struct iwm_agn_scd_bc_tbl {
#define IWM_CALIBRATION_COMPLETE_NOTIFICATION 0x67
#define IWM_RADIO_VERSION_NOTIFICATION 0x68
+/* paging block to FW cpu2 */
+#define IWM_FW_PAGING_BLOCK_CMD 0x4f
+
/* Scan offload */
#define IWM_SCAN_OFFLOAD_REQUEST_CMD 0x51
#define IWM_SCAN_OFFLOAD_ABORT_CMD 0x52
@@ -2100,6 +2103,57 @@ struct iwm_nvm_access_cmd {
uint8_t data[];
} __packed; /* IWM_NVM_ACCESS_CMD_API_S_VER_2 */
+/*
+ * Block paging calculations
+ */
+#define IWM_PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */
+#define IWM_FW_PAGING_SIZE (1 << IWM_PAGE_2_EXP_SIZE) /* page size is 4KB */
+#define IWM_PAGE_PER_GROUP_2_EXP_SIZE 3
+/* 8 pages per group */
+#define IWM_NUM_OF_PAGE_PER_GROUP (1 << IWM_PAGE_PER_GROUP_2_EXP_SIZE)
+/* don't change, support only 32KB size */
+#define IWM_PAGING_BLOCK_SIZE (IWM_NUM_OF_PAGE_PER_GROUP * IWM_FW_PAGING_SIZE)
+/* 32K == 2^15 */
+#define IWM_BLOCK_2_EXP_SIZE (IWM_PAGE_2_EXP_SIZE + IWM_PAGE_PER_GROUP_2_EXP_SIZE)
+
+/*
+ * Image paging calculations
+ */
+#define IWM_BLOCK_PER_IMAGE_2_EXP_SIZE 5
+/* 2^5 == 32 blocks per image */
+#define IWM_NUM_OF_BLOCK_PER_IMAGE (1 << IWM_BLOCK_PER_IMAGE_2_EXP_SIZE)
+/* maximum image size 1024KB */
+#define IWM_MAX_PAGING_IMAGE_SIZE (IWM_NUM_OF_BLOCK_PER_IMAGE * IWM_PAGING_BLOCK_SIZE)
+
+/* Virtual address signature */
+#define IWM_PAGING_ADDR_SIG 0xAA000000
+
+#define IWM_PAGING_CMD_IS_SECURED (1 << 9)
+#define IWM_PAGING_CMD_IS_ENABLED (1 << 8)
+#define IWM_PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0
+#define IWM_PAGING_TLV_SECURE_MASK 1
+
+#define IWM_NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */
+
+/*
+ * struct iwm_fw_paging_cmd - paging layout
+ *
+ * (IWM_FW_PAGING_BLOCK_CMD = 0x4f)
+ *
+ * Send to FW the paging layout in the driver.
+ *
+ * @flags: various flags for the command
+ * @block_size: the block size in powers of 2
+ * @block_num: number of blocks specified in the command.
+ * @device_phy_addr: virtual addresses from device side
+*/
+struct iwm_fw_paging_cmd {
+ uint32_t flags;
+ uint32_t block_size;
+ uint32_t block_num;
+ uint32_t device_phy_addr[IWM_NUM_OF_FW_PAGING_BLOCKS];
+} __packed; /* IWM_FW_PAGING_BLOCK_CMD_API_S_VER_1 */
+
/**
* struct iwm_nvm_access_resp_ver2 - response to IWM_NVM_ACCESS_CMD
* @offset: offset in bytes into the section
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index a76797681b1..0d16f393940 100644
--- a/sys/dev/pci/if_iwmvar.h
+++ b/sys/dev/pci/if_iwmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmvar.h,v 1.42 2019/10/28 18:11:10 stsp Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.43 2019/11/04 11:29:11 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -173,6 +173,7 @@ struct iwm_fw_info {
} fw_sect[IWM_UCODE_SECT_MAX];
size_t fw_totlen;
int fw_count;
+ uint32_t paging_mem_size;
} fw_sects[IWM_UCODE_TYPE_MAX];
};
@@ -237,6 +238,16 @@ struct iwm_dma_info {
bus_size_t size;
};
+/**
+ * struct iwm_fw_paging
+ * @fw_paging_block: dma memory info
+ * @fw_paging_size: page size
+ */
+struct iwm_fw_paging {
+ struct iwm_dma_info fw_paging_block;
+ uint32_t fw_paging_size;
+};
+
#define IWM_TX_RING_COUNT 256
#define IWM_TX_RING_LOMARK 192
#define IWM_TX_RING_HIMARK 224
@@ -487,6 +498,14 @@ struct iwm_softc {
int host_interrupt_operation_mode;
int sc_ltr_enabled;
+ /*
+ * Paging parameters - All of the parameters should be set by the
+ * opmode when paging is enabled
+ */
+ struct iwm_fw_paging fw_paging_db[IWM_NUM_OF_FW_PAGING_BLOCKS];
+ uint16_t num_of_paging_blk;
+ uint16_t num_of_pages_in_last_blk;
+
#if NBPFILTER > 0
caddr_t sc_drvbpf;