1314209Sadrian/*- 2314209Sadrian * Based on BSD-licensed source modules in the Linux iwlwifi driver, 3314209Sadrian * which were used as the reference documentation for this implementation. 4314209Sadrian * 5314209Sadrian * Driver version we are currently based off of is 6314209Sadrian * Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75) 7314209Sadrian * 8314209Sadrian *********************************************************************** 9314209Sadrian * 10314209Sadrian * This file is provided under a dual BSD/GPLv2 license. When using or 11314209Sadrian * redistributing this file, you may do so under either license. 12314209Sadrian * 13314209Sadrian * GPL LICENSE SUMMARY 14314209Sadrian * 15314209Sadrian * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 16314209Sadrian * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 17314209Sadrian * Copyright(c) 2016 Intel Deutschland GmbH 18314209Sadrian * 19314209Sadrian * This program is free software; you can redistribute it and/or modify 20314209Sadrian * it under the terms of version 2 of the GNU General Public License as 21314209Sadrian * published by the Free Software Foundation. 22314209Sadrian * 23314209Sadrian * This program is distributed in the hope that it will be useful, but 24314209Sadrian * WITHOUT ANY WARRANTY; without even the implied warranty of 25314209Sadrian * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26314209Sadrian * General Public License for more details. 27314209Sadrian * 28314209Sadrian * You should have received a copy of the GNU General Public License 29314209Sadrian * along with this program; if not, write to the Free Software 30314209Sadrian * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 31314209Sadrian * USA 32314209Sadrian * 33314209Sadrian * The full GNU General Public License is included in this distribution 34314209Sadrian * in the file called COPYING. 35314209Sadrian * 36314209Sadrian * Contact Information: 37314209Sadrian * Intel Linux Wireless <linuxwifi@intel.com> 38314209Sadrian * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 39314209Sadrian * 40314209Sadrian * BSD LICENSE 41314209Sadrian * 42314209Sadrian * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 43314209Sadrian * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 44314209Sadrian * All rights reserved. 45314209Sadrian * 46314209Sadrian * Redistribution and use in source and binary forms, with or without 47314209Sadrian * modification, are permitted provided that the following conditions 48314209Sadrian * are met: 49314209Sadrian * 50314209Sadrian * * Redistributions of source code must retain the above copyright 51314209Sadrian * notice, this list of conditions and the following disclaimer. 52314209Sadrian * * Redistributions in binary form must reproduce the above copyright 53314209Sadrian * notice, this list of conditions and the following disclaimer in 54314209Sadrian * the documentation and/or other materials provided with the 55314209Sadrian * distribution. 56314209Sadrian * * Neither the name Intel Corporation nor the names of its 57314209Sadrian * contributors may be used to endorse or promote products derived 58314209Sadrian * from this software without specific prior written permission. 59314209Sadrian * 60314209Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 61314209Sadrian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 62314209Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 63314209Sadrian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 64314209Sadrian * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 65314209Sadrian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 66314209Sadrian * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 67314209Sadrian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 68314209Sadrian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 69314209Sadrian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 70314209Sadrian * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 71314209Sadrian */ 72314209Sadrian 73314209Sadrian#include <sys/cdefs.h> 74314209Sadrian__FBSDID("$FreeBSD: stable/11/sys/dev/iwm/if_iwm_fw.c 330455 2018-03-05 08:05:30Z eadler $"); 75314209Sadrian 76314209Sadrian#include "opt_wlan.h" 77330455Seadler#include "opt_iwm.h" 78314209Sadrian 79314209Sadrian#include <sys/param.h> 80314209Sadrian#include <sys/bus.h> 81314209Sadrian#include <sys/conf.h> 82314209Sadrian#include <sys/endian.h> 83314209Sadrian#include <sys/firmware.h> 84314209Sadrian#include <sys/kernel.h> 85314209Sadrian#include <sys/malloc.h> 86314209Sadrian#include <sys/mbuf.h> 87314209Sadrian#include <sys/mutex.h> 88314209Sadrian#include <sys/module.h> 89314209Sadrian#include <sys/proc.h> 90314209Sadrian#include <sys/rman.h> 91314209Sadrian#include <sys/socket.h> 92314209Sadrian#include <sys/sockio.h> 93314209Sadrian#include <sys/sysctl.h> 94314209Sadrian#include <sys/linker.h> 95314209Sadrian 96314209Sadrian#include <machine/bus.h> 97314209Sadrian#include <machine/endian.h> 98314209Sadrian#include <machine/resource.h> 99314209Sadrian 100314209Sadrian#include <dev/pci/pcivar.h> 101314209Sadrian#include <dev/pci/pcireg.h> 102314209Sadrian 103314209Sadrian#include <net/bpf.h> 104314209Sadrian 105314209Sadrian#include <net/if.h> 106314209Sadrian#include <net/if_var.h> 107314209Sadrian#include <net/if_arp.h> 108314209Sadrian#include <net/if_dl.h> 109314209Sadrian#include <net/if_media.h> 110314209Sadrian#include <net/if_types.h> 111314209Sadrian 112314209Sadrian#include <netinet/in.h> 113314209Sadrian#include <netinet/in_systm.h> 114314209Sadrian#include <netinet/if_ether.h> 115314209Sadrian#include <netinet/ip.h> 116314209Sadrian 117314209Sadrian#include <net80211/ieee80211_var.h> 118314209Sadrian#include <net80211/ieee80211_regdomain.h> 119314209Sadrian#include <net80211/ieee80211_ratectl.h> 120314209Sadrian#include <net80211/ieee80211_radiotap.h> 121314209Sadrian 122314209Sadrian#include <dev/iwm/if_iwmreg.h> 123314209Sadrian#include <dev/iwm/if_iwmvar.h> 124314209Sadrian#include <dev/iwm/if_iwm_debug.h> 125314209Sadrian#include <dev/iwm/if_iwm_util.h> 126314209Sadrian#include <dev/iwm/if_iwm_fw.h> 127314209Sadrian 128314209Sadrianvoid 129314209Sadrianiwm_free_fw_paging(struct iwm_softc *sc) 130314209Sadrian{ 131314209Sadrian int i; 132314209Sadrian 133314209Sadrian if (sc->fw_paging_db[0].fw_paging_block.vaddr == NULL) 134314209Sadrian return; 135314209Sadrian 136314209Sadrian for (i = 0; i < IWM_NUM_OF_FW_PAGING_BLOCKS; i++) { 137314209Sadrian iwm_dma_contig_free(&sc->fw_paging_db[i].fw_paging_block); 138314209Sadrian } 139314209Sadrian 140314209Sadrian memset(sc->fw_paging_db, 0, sizeof(sc->fw_paging_db)); 141314209Sadrian} 142314209Sadrian 143314209Sadrianstatic int 144314209Sadrianiwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) 145314209Sadrian{ 146314209Sadrian int sec_idx, idx; 147314209Sadrian uint32_t offset = 0; 148314209Sadrian 149314209Sadrian /* 150314209Sadrian * find where is the paging image start point: 151314209Sadrian * if CPU2 exist and it's in paging format, then the image looks like: 152314209Sadrian * CPU1 sections (2 or more) 153314209Sadrian * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 154314209Sadrian * CPU2 sections (not paged) 155314209Sadrian * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 156314209Sadrian * non paged to CPU2 paging sec 157314209Sadrian * CPU2 paging CSS 158314209Sadrian * CPU2 paging image (including instruction and data) 159314209Sadrian */ 160314209Sadrian for (sec_idx = 0; sec_idx < IWM_UCODE_SECTION_MAX; sec_idx++) { 161314209Sadrian if (image->fw_sect[sec_idx].offset == IWM_PAGING_SEPARATOR_SECTION) { 162314209Sadrian sec_idx++; 163314209Sadrian break; 164314209Sadrian } 165314209Sadrian } 166314209Sadrian 167314209Sadrian /* 168314209Sadrian * If paging is enabled there should be at least 2 more sections left 169314209Sadrian * (one for CSS and one for Paging data) 170314209Sadrian */ 171314209Sadrian if (sec_idx >= nitems(image->fw_sect) - 1) { 172314209Sadrian device_printf(sc->sc_dev, 173314209Sadrian "Paging: Missing CSS and/or paging sections\n"); 174314209Sadrian iwm_free_fw_paging(sc); 175314209Sadrian return EINVAL; 176314209Sadrian } 177314209Sadrian 178314209Sadrian /* copy the CSS block to the dram */ 179314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 180314209Sadrian "Paging: load paging CSS to FW, sec = %d\n", 181314209Sadrian sec_idx); 182314209Sadrian 183314209Sadrian memcpy(sc->fw_paging_db[0].fw_paging_block.vaddr, 184314209Sadrian image->fw_sect[sec_idx].data, 185314209Sadrian sc->fw_paging_db[0].fw_paging_size); 186314209Sadrian 187314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 188314209Sadrian "Paging: copied %d CSS bytes to first block\n", 189314209Sadrian sc->fw_paging_db[0].fw_paging_size); 190314209Sadrian 191314209Sadrian sec_idx++; 192314209Sadrian 193314209Sadrian /* 194314209Sadrian * copy the paging blocks to the dram 195314209Sadrian * loop index start from 1 since that CSS block already copied to dram 196314209Sadrian * and CSS index is 0. 197314209Sadrian * loop stop at num_of_paging_blk since that last block is not full. 198314209Sadrian */ 199314209Sadrian for (idx = 1; idx < sc->num_of_paging_blk; idx++) { 200314209Sadrian memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr, 201314209Sadrian (const char *)image->fw_sect[sec_idx].data + offset, 202314209Sadrian sc->fw_paging_db[idx].fw_paging_size); 203314209Sadrian 204314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 205314209Sadrian "Paging: copied %d paging bytes to block %d\n", 206314209Sadrian sc->fw_paging_db[idx].fw_paging_size, 207314209Sadrian idx); 208314209Sadrian 209314209Sadrian offset += sc->fw_paging_db[idx].fw_paging_size; 210314209Sadrian } 211314209Sadrian 212314209Sadrian /* copy the last paging block */ 213314209Sadrian if (sc->num_of_pages_in_last_blk > 0) { 214314209Sadrian memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr, 215314209Sadrian (const char *)image->fw_sect[sec_idx].data + offset, 216314209Sadrian IWM_FW_PAGING_SIZE * sc->num_of_pages_in_last_blk); 217314209Sadrian 218314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 219314209Sadrian "Paging: copied %d pages in the last block %d\n", 220314209Sadrian sc->num_of_pages_in_last_blk, idx); 221314209Sadrian } 222314209Sadrian 223314209Sadrian return 0; 224314209Sadrian} 225314209Sadrian 226314209Sadrianstatic int 227314209Sadrianiwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) 228314209Sadrian{ 229314209Sadrian int blk_idx = 0; 230314209Sadrian int error, num_of_pages; 231314209Sadrian 232314209Sadrian if (sc->fw_paging_db[0].fw_paging_block.vaddr != NULL) { 233314209Sadrian int i; 234314209Sadrian /* Device got reset, and we setup firmware paging again */ 235314209Sadrian for (i = 0; i < sc->num_of_paging_blk + 1; i++) { 236314209Sadrian bus_dmamap_sync(sc->sc_dmat, 237314209Sadrian sc->fw_paging_db[i].fw_paging_block.map, 238314209Sadrian BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 239314209Sadrian } 240314209Sadrian return 0; 241314209Sadrian } 242314209Sadrian 243314209Sadrian /* ensure IWM_BLOCK_2_EXP_SIZE is power of 2 of IWM_PAGING_BLOCK_SIZE */ 244314209Sadrian _Static_assert((1 << IWM_BLOCK_2_EXP_SIZE) == IWM_PAGING_BLOCK_SIZE, 245314209Sadrian "IWM_BLOCK_2_EXP_SIZE must be power of 2 of IWM_PAGING_BLOCK_SIZE"); 246314209Sadrian 247314209Sadrian num_of_pages = image->paging_mem_size / IWM_FW_PAGING_SIZE; 248314209Sadrian sc->num_of_paging_blk = ((num_of_pages - 1) / 249314209Sadrian IWM_NUM_OF_PAGE_PER_GROUP) + 1; 250314209Sadrian 251314209Sadrian sc->num_of_pages_in_last_blk = 252314209Sadrian num_of_pages - 253314209Sadrian IWM_NUM_OF_PAGE_PER_GROUP * (sc->num_of_paging_blk - 1); 254314209Sadrian 255314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 256314209Sadrian "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", 257314209Sadrian sc->num_of_paging_blk, 258314209Sadrian sc->num_of_pages_in_last_blk); 259314209Sadrian 260314209Sadrian /* allocate block of 4Kbytes for paging CSS */ 261314209Sadrian error = iwm_dma_contig_alloc(sc->sc_dmat, 262314209Sadrian &sc->fw_paging_db[blk_idx].fw_paging_block, IWM_FW_PAGING_SIZE, 263314209Sadrian 4096); 264314209Sadrian if (error) { 265314209Sadrian /* free all the previous pages since we failed */ 266314209Sadrian iwm_free_fw_paging(sc); 267314209Sadrian return ENOMEM; 268314209Sadrian } 269314209Sadrian 270314209Sadrian sc->fw_paging_db[blk_idx].fw_paging_size = IWM_FW_PAGING_SIZE; 271314209Sadrian 272314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 273314209Sadrian "Paging: allocated 4K(CSS) bytes for firmware paging.\n"); 274314209Sadrian 275314209Sadrian /* 276314209Sadrian * allocate blocks in dram. 277314209Sadrian * since that CSS allocated in fw_paging_db[0] loop start from index 1 278314209Sadrian */ 279314209Sadrian for (blk_idx = 1; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) { 280314209Sadrian /* allocate block of IWM_PAGING_BLOCK_SIZE (32K) */ 281314209Sadrian /* XXX Use iwm_dma_contig_alloc for allocating */ 282314209Sadrian error = iwm_dma_contig_alloc(sc->sc_dmat, 283314209Sadrian &sc->fw_paging_db[blk_idx].fw_paging_block, 284314209Sadrian IWM_PAGING_BLOCK_SIZE, 4096); 285314209Sadrian if (error) { 286314209Sadrian /* free all the previous pages since we failed */ 287314209Sadrian iwm_free_fw_paging(sc); 288314209Sadrian return ENOMEM; 289314209Sadrian } 290314209Sadrian 291314209Sadrian sc->fw_paging_db[blk_idx].fw_paging_size = IWM_PAGING_BLOCK_SIZE; 292314209Sadrian 293314209Sadrian IWM_DPRINTF(sc, IWM_DEBUG_FW, 294314209Sadrian "Paging: allocated 32K bytes for firmware paging.\n"); 295314209Sadrian } 296314209Sadrian 297314209Sadrian return 0; 298314209Sadrian} 299314209Sadrian 300314209Sadrianint 301314209Sadrianiwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_sects *fw) 302314209Sadrian{ 303314209Sadrian int ret; 304314209Sadrian 305314209Sadrian ret = iwm_alloc_fw_paging_mem(sc, fw); 306314209Sadrian if (ret) 307314209Sadrian return ret; 308314209Sadrian 309314209Sadrian return iwm_fill_paging_mem(sc, fw); 310314209Sadrian} 311314209Sadrian 312314209Sadrian/* send paging cmd to FW in case CPU2 has paging image */ 313314209Sadrianint 314314209Sadrianiwm_send_paging_cmd(struct iwm_softc *sc, const struct iwm_fw_sects *fw) 315314209Sadrian{ 316314209Sadrian int blk_idx; 317314209Sadrian uint32_t dev_phy_addr; 318314209Sadrian struct iwm_fw_paging_cmd fw_paging_cmd = { 319314209Sadrian .flags = 320314209Sadrian htole32(IWM_PAGING_CMD_IS_SECURED | 321314209Sadrian IWM_PAGING_CMD_IS_ENABLED | 322314209Sadrian (sc->num_of_pages_in_last_blk << 323314209Sadrian IWM_PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), 324314209Sadrian .block_size = htole32(IWM_BLOCK_2_EXP_SIZE), 325314209Sadrian .block_num = htole32(sc->num_of_paging_blk), 326314209Sadrian }; 327314209Sadrian 328314209Sadrian /* loop for for all paging blocks + CSS block */ 329314209Sadrian for (blk_idx = 0; blk_idx < sc->num_of_paging_blk + 1; blk_idx++) { 330314209Sadrian dev_phy_addr = htole32( 331314209Sadrian sc->fw_paging_db[blk_idx].fw_paging_block.paddr >> 332314209Sadrian IWM_PAGE_2_EXP_SIZE); 333314209Sadrian fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr; 334314209Sadrian bus_dmamap_sync(sc->sc_dmat, 335314209Sadrian sc->fw_paging_db[blk_idx].fw_paging_block.map, 336314209Sadrian BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 337314209Sadrian } 338314209Sadrian 339314209Sadrian return iwm_mvm_send_cmd_pdu(sc, iwm_cmd_id(IWM_FW_PAGING_BLOCK_CMD, 340314209Sadrian IWM_ALWAYS_LONG_GROUP, 0), 341314209Sadrian 0, sizeof(fw_paging_cmd), &fw_paging_cmd); 342314209Sadrian} 343