18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on: 98c2ecf20Sopenharmony_ci * ST-Ericsson UMAC CW1200 driver which is 108c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 118c2ecf20Sopenharmony_ci * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/firmware.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "cw1200.h" 198c2ecf20Sopenharmony_ci#include "fwio.h" 208c2ecf20Sopenharmony_ci#include "hwio.h" 218c2ecf20Sopenharmony_ci#include "hwbus.h" 228c2ecf20Sopenharmony_ci#include "bh.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int hw_type = -1; 278c2ecf20Sopenharmony_ci u32 silicon_type = (config_reg_val >> 24) & 0x7; 288c2ecf20Sopenharmony_ci u32 silicon_vers = (config_reg_val >> 31) & 0x1; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci switch (silicon_type) { 318c2ecf20Sopenharmony_ci case 0x00: 328c2ecf20Sopenharmony_ci *major_revision = 1; 338c2ecf20Sopenharmony_ci hw_type = HIF_9000_SILICON_VERSATILE; 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case 0x01: 368c2ecf20Sopenharmony_ci case 0x02: /* CW1x00 */ 378c2ecf20Sopenharmony_ci case 0x04: /* CW1x60 */ 388c2ecf20Sopenharmony_ci *major_revision = silicon_type; 398c2ecf20Sopenharmony_ci if (silicon_vers) 408c2ecf20Sopenharmony_ci hw_type = HIF_8601_VERSATILE; 418c2ecf20Sopenharmony_ci else 428c2ecf20Sopenharmony_ci hw_type = HIF_8601_SILICON; 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci default: 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return hw_type; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int cw1200_load_firmware_cw1200(struct cw1200_common *priv) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int ret, block, num_blocks; 548c2ecf20Sopenharmony_ci unsigned i; 558c2ecf20Sopenharmony_ci u32 val32; 568c2ecf20Sopenharmony_ci u32 put = 0, get = 0; 578c2ecf20Sopenharmony_ci u8 *buf = NULL; 588c2ecf20Sopenharmony_ci const char *fw_path; 598c2ecf20Sopenharmony_ci const struct firmware *firmware = NULL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Macroses are local. */ 628c2ecf20Sopenharmony_ci#define APB_WRITE(reg, val) \ 638c2ecf20Sopenharmony_ci do { \ 648c2ecf20Sopenharmony_ci ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ 658c2ecf20Sopenharmony_ci if (ret < 0) \ 668c2ecf20Sopenharmony_ci goto exit; \ 678c2ecf20Sopenharmony_ci } while (0) 688c2ecf20Sopenharmony_ci#define APB_WRITE2(reg, val) \ 698c2ecf20Sopenharmony_ci do { \ 708c2ecf20Sopenharmony_ci ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ 718c2ecf20Sopenharmony_ci if (ret < 0) \ 728c2ecf20Sopenharmony_ci goto free_buffer; \ 738c2ecf20Sopenharmony_ci } while (0) 748c2ecf20Sopenharmony_ci#define APB_READ(reg, val) \ 758c2ecf20Sopenharmony_ci do { \ 768c2ecf20Sopenharmony_ci ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ 778c2ecf20Sopenharmony_ci if (ret < 0) \ 788c2ecf20Sopenharmony_ci goto free_buffer; \ 798c2ecf20Sopenharmony_ci } while (0) 808c2ecf20Sopenharmony_ci#define REG_WRITE(reg, val) \ 818c2ecf20Sopenharmony_ci do { \ 828c2ecf20Sopenharmony_ci ret = cw1200_reg_write_32(priv, (reg), (val)); \ 838c2ecf20Sopenharmony_ci if (ret < 0) \ 848c2ecf20Sopenharmony_ci goto exit; \ 858c2ecf20Sopenharmony_ci } while (0) 868c2ecf20Sopenharmony_ci#define REG_READ(reg, val) \ 878c2ecf20Sopenharmony_ci do { \ 888c2ecf20Sopenharmony_ci ret = cw1200_reg_read_32(priv, (reg), &(val)); \ 898c2ecf20Sopenharmony_ci if (ret < 0) \ 908c2ecf20Sopenharmony_ci goto exit; \ 918c2ecf20Sopenharmony_ci } while (0) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci switch (priv->hw_revision) { 948c2ecf20Sopenharmony_ci case CW1200_HW_REV_CUT10: 958c2ecf20Sopenharmony_ci fw_path = FIRMWARE_CUT10; 968c2ecf20Sopenharmony_ci if (!priv->sdd_path) 978c2ecf20Sopenharmony_ci priv->sdd_path = SDD_FILE_10; 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci case CW1200_HW_REV_CUT11: 1008c2ecf20Sopenharmony_ci fw_path = FIRMWARE_CUT11; 1018c2ecf20Sopenharmony_ci if (!priv->sdd_path) 1028c2ecf20Sopenharmony_ci priv->sdd_path = SDD_FILE_11; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case CW1200_HW_REV_CUT20: 1058c2ecf20Sopenharmony_ci fw_path = FIRMWARE_CUT20; 1068c2ecf20Sopenharmony_ci if (!priv->sdd_path) 1078c2ecf20Sopenharmony_ci priv->sdd_path = SDD_FILE_20; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci case CW1200_HW_REV_CUT22: 1108c2ecf20Sopenharmony_ci fw_path = FIRMWARE_CUT22; 1118c2ecf20Sopenharmony_ci if (!priv->sdd_path) 1128c2ecf20Sopenharmony_ci priv->sdd_path = SDD_FILE_22; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case CW1X60_HW_REV: 1158c2ecf20Sopenharmony_ci fw_path = FIRMWARE_CW1X60; 1168c2ecf20Sopenharmony_ci if (!priv->sdd_path) 1178c2ecf20Sopenharmony_ci priv->sdd_path = SDD_FILE_CW1X60; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci pr_err("Invalid silicon revision %d.\n", priv->hw_revision); 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Initialize common registers */ 1258c2ecf20Sopenharmony_ci APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); 1268c2ecf20Sopenharmony_ci APB_WRITE(DOWNLOAD_PUT_REG, 0); 1278c2ecf20Sopenharmony_ci APB_WRITE(DOWNLOAD_GET_REG, 0); 1288c2ecf20Sopenharmony_ci APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); 1298c2ecf20Sopenharmony_ci APB_WRITE(DOWNLOAD_FLAGS_REG, 0); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Write the NOP Instruction */ 1328c2ecf20Sopenharmony_ci REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); 1338c2ecf20Sopenharmony_ci REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Release CPU from RESET */ 1368c2ecf20Sopenharmony_ci REG_READ(ST90TDS_CONFIG_REG_ID, val32); 1378c2ecf20Sopenharmony_ci val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; 1388c2ecf20Sopenharmony_ci REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Enable Clock */ 1418c2ecf20Sopenharmony_ci val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; 1428c2ecf20Sopenharmony_ci REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Load a firmware file */ 1458c2ecf20Sopenharmony_ci ret = request_firmware(&firmware, fw_path, priv->pdev); 1468c2ecf20Sopenharmony_ci if (ret) { 1478c2ecf20Sopenharmony_ci pr_err("Can't load firmware file %s.\n", fw_path); 1488c2ecf20Sopenharmony_ci goto exit; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); 1528c2ecf20Sopenharmony_ci if (!buf) { 1538c2ecf20Sopenharmony_ci pr_err("Can't allocate firmware load buffer.\n"); 1548c2ecf20Sopenharmony_ci ret = -ENOMEM; 1558c2ecf20Sopenharmony_ci goto firmware_release; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Check if the bootloader is ready */ 1598c2ecf20Sopenharmony_ci for (i = 0; i < 100; i += 1 + i / 2) { 1608c2ecf20Sopenharmony_ci APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); 1618c2ecf20Sopenharmony_ci if (val32 == DOWNLOAD_I_AM_HERE) 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci mdelay(i); 1648c2ecf20Sopenharmony_ci } /* End of for loop */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (val32 != DOWNLOAD_I_AM_HERE) { 1678c2ecf20Sopenharmony_ci pr_err("Bootloader is not ready.\n"); 1688c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1698c2ecf20Sopenharmony_ci goto free_buffer; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Calculcate number of download blocks */ 1738c2ecf20Sopenharmony_ci num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Updating the length in Download Ctrl Area */ 1768c2ecf20Sopenharmony_ci val32 = firmware->size; /* Explicit cast from size_t to u32 */ 1778c2ecf20Sopenharmony_ci APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Firmware downloading loop */ 1808c2ecf20Sopenharmony_ci for (block = 0; block < num_blocks; block++) { 1818c2ecf20Sopenharmony_ci size_t tx_size; 1828c2ecf20Sopenharmony_ci size_t block_size; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* check the download status */ 1858c2ecf20Sopenharmony_ci APB_READ(DOWNLOAD_STATUS_REG, val32); 1868c2ecf20Sopenharmony_ci if (val32 != DOWNLOAD_PENDING) { 1878c2ecf20Sopenharmony_ci pr_err("Bootloader reported error %d.\n", val32); 1888c2ecf20Sopenharmony_ci ret = -EIO; 1898c2ecf20Sopenharmony_ci goto free_buffer; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* loop until put - get <= 24K */ 1938c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 1948c2ecf20Sopenharmony_ci APB_READ(DOWNLOAD_GET_REG, get); 1958c2ecf20Sopenharmony_ci if ((put - get) <= 1968c2ecf20Sopenharmony_ci (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci mdelay(i); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { 2028c2ecf20Sopenharmony_ci pr_err("Timeout waiting for FIFO.\n"); 2038c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2048c2ecf20Sopenharmony_ci goto free_buffer; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* calculate the block size */ 2088c2ecf20Sopenharmony_ci tx_size = block_size = min_t(size_t, firmware->size - put, 2098c2ecf20Sopenharmony_ci DOWNLOAD_BLOCK_SIZE); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci memcpy(buf, &firmware->data[put], block_size); 2128c2ecf20Sopenharmony_ci if (block_size < DOWNLOAD_BLOCK_SIZE) { 2138c2ecf20Sopenharmony_ci memset(&buf[block_size], 0, 2148c2ecf20Sopenharmony_ci DOWNLOAD_BLOCK_SIZE - block_size); 2158c2ecf20Sopenharmony_ci tx_size = DOWNLOAD_BLOCK_SIZE; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* send the block to sram */ 2198c2ecf20Sopenharmony_ci ret = cw1200_apb_write(priv, 2208c2ecf20Sopenharmony_ci CW1200_APB(DOWNLOAD_FIFO_OFFSET + 2218c2ecf20Sopenharmony_ci (put & (DOWNLOAD_FIFO_SIZE - 1))), 2228c2ecf20Sopenharmony_ci buf, tx_size); 2238c2ecf20Sopenharmony_ci if (ret < 0) { 2248c2ecf20Sopenharmony_ci pr_err("Can't write firmware block @ %d!\n", 2258c2ecf20Sopenharmony_ci put & (DOWNLOAD_FIFO_SIZE - 1)); 2268c2ecf20Sopenharmony_ci goto free_buffer; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* update the put register */ 2308c2ecf20Sopenharmony_ci put += block_size; 2318c2ecf20Sopenharmony_ci APB_WRITE2(DOWNLOAD_PUT_REG, put); 2328c2ecf20Sopenharmony_ci } /* End of firmware download loop */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Wait for the download completion */ 2358c2ecf20Sopenharmony_ci for (i = 0; i < 300; i += 1 + i / 2) { 2368c2ecf20Sopenharmony_ci APB_READ(DOWNLOAD_STATUS_REG, val32); 2378c2ecf20Sopenharmony_ci if (val32 != DOWNLOAD_PENDING) 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci mdelay(i); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci if (val32 != DOWNLOAD_SUCCESS) { 2428c2ecf20Sopenharmony_ci pr_err("Wait for download completion failed: 0x%.8X\n", val32); 2438c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2448c2ecf20Sopenharmony_ci goto free_buffer; 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci pr_info("Firmware download completed.\n"); 2478c2ecf20Sopenharmony_ci ret = 0; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cifree_buffer: 2518c2ecf20Sopenharmony_ci kfree(buf); 2528c2ecf20Sopenharmony_cifirmware_release: 2538c2ecf20Sopenharmony_ci release_firmware(firmware); 2548c2ecf20Sopenharmony_ciexit: 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#undef APB_WRITE 2588c2ecf20Sopenharmony_ci#undef APB_WRITE2 2598c2ecf20Sopenharmony_ci#undef APB_READ 2608c2ecf20Sopenharmony_ci#undef REG_WRITE 2618c2ecf20Sopenharmony_ci#undef REG_READ 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int config_reg_read(struct cw1200_common *priv, u32 *val) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci switch (priv->hw_type) { 2688c2ecf20Sopenharmony_ci case HIF_9000_SILICON_VERSATILE: { 2698c2ecf20Sopenharmony_ci u16 val16; 2708c2ecf20Sopenharmony_ci int ret = cw1200_reg_read_16(priv, 2718c2ecf20Sopenharmony_ci ST90TDS_CONFIG_REG_ID, 2728c2ecf20Sopenharmony_ci &val16); 2738c2ecf20Sopenharmony_ci if (ret < 0) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci *val = val16; 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci case HIF_8601_VERSATILE: 2798c2ecf20Sopenharmony_ci case HIF_8601_SILICON: 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int config_reg_write(struct cw1200_common *priv, u32 val) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci switch (priv->hw_type) { 2908c2ecf20Sopenharmony_ci case HIF_9000_SILICON_VERSATILE: 2918c2ecf20Sopenharmony_ci return cw1200_reg_write_16(priv, 2928c2ecf20Sopenharmony_ci ST90TDS_CONFIG_REG_ID, 2938c2ecf20Sopenharmony_ci (u16)val); 2948c2ecf20Sopenharmony_ci case HIF_8601_VERSATILE: 2958c2ecf20Sopenharmony_ci case HIF_8601_SILICON: 2968c2ecf20Sopenharmony_ci default: 2978c2ecf20Sopenharmony_ci return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciint cw1200_load_firmware(struct cw1200_common *priv) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci u32 val32; 3078c2ecf20Sopenharmony_ci u16 val16; 3088c2ecf20Sopenharmony_ci int major_revision = -1; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Read CONFIG Register */ 3118c2ecf20Sopenharmony_ci ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); 3128c2ecf20Sopenharmony_ci if (ret < 0) { 3138c2ecf20Sopenharmony_ci pr_err("Can't read config register.\n"); 3148c2ecf20Sopenharmony_ci goto out; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (val32 == 0 || val32 == 0xffffffff) { 3188c2ecf20Sopenharmony_ci pr_err("Bad config register value (0x%08x)\n", val32); 3198c2ecf20Sopenharmony_ci ret = -EIO; 3208c2ecf20Sopenharmony_ci goto out; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = cw1200_get_hw_type(val32, &major_revision); 3248c2ecf20Sopenharmony_ci if (ret < 0) { 3258c2ecf20Sopenharmony_ci pr_err("Can't deduce hardware type.\n"); 3268c2ecf20Sopenharmony_ci goto out; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci priv->hw_type = ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Set DPLL Reg value, and read back to confirm writes work */ 3318c2ecf20Sopenharmony_ci ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, 3328c2ecf20Sopenharmony_ci cw1200_dpll_from_clk(priv->hw_refclk)); 3338c2ecf20Sopenharmony_ci if (ret < 0) { 3348c2ecf20Sopenharmony_ci pr_err("Can't write DPLL register.\n"); 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci msleep(20); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = cw1200_reg_read_32(priv, 3418c2ecf20Sopenharmony_ci ST90TDS_TSET_GEN_R_W_REG_ID, &val32); 3428c2ecf20Sopenharmony_ci if (ret < 0) { 3438c2ecf20Sopenharmony_ci pr_err("Can't read DPLL register.\n"); 3448c2ecf20Sopenharmony_ci goto out; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { 3488c2ecf20Sopenharmony_ci pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", 3498c2ecf20Sopenharmony_ci cw1200_dpll_from_clk(priv->hw_refclk), val32); 3508c2ecf20Sopenharmony_ci ret = -EIO; 3518c2ecf20Sopenharmony_ci goto out; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Set wakeup bit in device */ 3558c2ecf20Sopenharmony_ci ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); 3568c2ecf20Sopenharmony_ci if (ret < 0) { 3578c2ecf20Sopenharmony_ci pr_err("set_wakeup: can't read control register.\n"); 3588c2ecf20Sopenharmony_ci goto out; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 3628c2ecf20Sopenharmony_ci val16 | ST90TDS_CONT_WUP_BIT); 3638c2ecf20Sopenharmony_ci if (ret < 0) { 3648c2ecf20Sopenharmony_ci pr_err("set_wakeup: can't write control register.\n"); 3658c2ecf20Sopenharmony_ci goto out; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Wait for wakeup */ 3698c2ecf20Sopenharmony_ci for (i = 0; i < 300; i += (1 + i / 2)) { 3708c2ecf20Sopenharmony_ci ret = cw1200_reg_read_16(priv, 3718c2ecf20Sopenharmony_ci ST90TDS_CONTROL_REG_ID, &val16); 3728c2ecf20Sopenharmony_ci if (ret < 0) { 3738c2ecf20Sopenharmony_ci pr_err("wait_for_wakeup: can't read control register.\n"); 3748c2ecf20Sopenharmony_ci goto out; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (val16 & ST90TDS_CONT_RDY_BIT) 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci msleep(i); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { 3848c2ecf20Sopenharmony_ci pr_err("wait_for_wakeup: device is not responding.\n"); 3858c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3868c2ecf20Sopenharmony_ci goto out; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci switch (major_revision) { 3908c2ecf20Sopenharmony_ci case 1: 3918c2ecf20Sopenharmony_ci /* CW1200 Hardware detection logic : Check for CUT1.1 */ 3928c2ecf20Sopenharmony_ci ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); 3938c2ecf20Sopenharmony_ci if (ret) { 3948c2ecf20Sopenharmony_ci pr_err("HW detection: can't read CUT ID.\n"); 3958c2ecf20Sopenharmony_ci goto out; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci switch (val32) { 3998c2ecf20Sopenharmony_ci case CW1200_CUT_11_ID_STR: 4008c2ecf20Sopenharmony_ci pr_info("CW1x00 Cut 1.1 silicon detected.\n"); 4018c2ecf20Sopenharmony_ci priv->hw_revision = CW1200_HW_REV_CUT11; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci default: 4048c2ecf20Sopenharmony_ci pr_info("CW1x00 Cut 1.0 silicon detected.\n"); 4058c2ecf20Sopenharmony_ci priv->hw_revision = CW1200_HW_REV_CUT10; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* According to ST-E, CUT<2.0 has busted BA TID0-3. 4108c2ecf20Sopenharmony_ci Just disable it entirely... 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci priv->ba_rx_tid_mask = 0; 4138c2ecf20Sopenharmony_ci priv->ba_tx_tid_mask = 0; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case 2: { 4168c2ecf20Sopenharmony_ci u32 ar1, ar2, ar3; 4178c2ecf20Sopenharmony_ci ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); 4188c2ecf20Sopenharmony_ci if (ret) { 4198c2ecf20Sopenharmony_ci pr_err("(1) HW detection: can't read CUT ID\n"); 4208c2ecf20Sopenharmony_ci goto out; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); 4238c2ecf20Sopenharmony_ci if (ret) { 4248c2ecf20Sopenharmony_ci pr_err("(2) HW detection: can't read CUT ID.\n"); 4258c2ecf20Sopenharmony_ci goto out; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); 4298c2ecf20Sopenharmony_ci if (ret) { 4308c2ecf20Sopenharmony_ci pr_err("(3) HW detection: can't read CUT ID.\n"); 4318c2ecf20Sopenharmony_ci goto out; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (ar1 == CW1200_CUT_22_ID_STR1 && 4358c2ecf20Sopenharmony_ci ar2 == CW1200_CUT_22_ID_STR2 && 4368c2ecf20Sopenharmony_ci ar3 == CW1200_CUT_22_ID_STR3) { 4378c2ecf20Sopenharmony_ci pr_info("CW1x00 Cut 2.2 silicon detected.\n"); 4388c2ecf20Sopenharmony_ci priv->hw_revision = CW1200_HW_REV_CUT22; 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci pr_info("CW1x00 Cut 2.0 silicon detected.\n"); 4418c2ecf20Sopenharmony_ci priv->hw_revision = CW1200_HW_REV_CUT20; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci case 4: 4468c2ecf20Sopenharmony_ci pr_info("CW1x60 silicon detected.\n"); 4478c2ecf20Sopenharmony_ci priv->hw_revision = CW1X60_HW_REV; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci default: 4508c2ecf20Sopenharmony_ci pr_err("Unsupported silicon major revision %d.\n", 4518c2ecf20Sopenharmony_ci major_revision); 4528c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 4538c2ecf20Sopenharmony_ci goto out; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Checking for access mode */ 4578c2ecf20Sopenharmony_ci ret = config_reg_read(priv, &val32); 4588c2ecf20Sopenharmony_ci if (ret < 0) { 4598c2ecf20Sopenharmony_ci pr_err("Can't read config register.\n"); 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { 4648c2ecf20Sopenharmony_ci pr_err("Device is already in QUEUE mode!\n"); 4658c2ecf20Sopenharmony_ci ret = -EINVAL; 4668c2ecf20Sopenharmony_ci goto out; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci switch (priv->hw_type) { 4708c2ecf20Sopenharmony_ci case HIF_8601_SILICON: 4718c2ecf20Sopenharmony_ci if (priv->hw_revision == CW1X60_HW_REV) { 4728c2ecf20Sopenharmony_ci pr_err("Can't handle CW1160/1260 firmware load yet.\n"); 4738c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 4748c2ecf20Sopenharmony_ci goto out; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci ret = cw1200_load_firmware_cw1200(priv); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci pr_err("Can't perform firmware load for hw type %d.\n", 4808c2ecf20Sopenharmony_ci priv->hw_type); 4818c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 4828c2ecf20Sopenharmony_ci goto out; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci if (ret < 0) { 4858c2ecf20Sopenharmony_ci pr_err("Firmware load error.\n"); 4868c2ecf20Sopenharmony_ci goto out; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Enable interrupt signalling */ 4908c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 4918c2ecf20Sopenharmony_ci ret = __cw1200_irq_enable(priv, 1); 4928c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 4938c2ecf20Sopenharmony_ci if (ret < 0) 4948c2ecf20Sopenharmony_ci goto unsubscribe; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Configure device for MESSSAGE MODE */ 4978c2ecf20Sopenharmony_ci ret = config_reg_read(priv, &val32); 4988c2ecf20Sopenharmony_ci if (ret < 0) { 4998c2ecf20Sopenharmony_ci pr_err("Can't read config register.\n"); 5008c2ecf20Sopenharmony_ci goto unsubscribe; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); 5038c2ecf20Sopenharmony_ci if (ret < 0) { 5048c2ecf20Sopenharmony_ci pr_err("Can't write config register.\n"); 5058c2ecf20Sopenharmony_ci goto unsubscribe; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Unless we read the CONFIG Register we are 5098c2ecf20Sopenharmony_ci * not able to get an interrupt 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci mdelay(10); 5128c2ecf20Sopenharmony_ci config_reg_read(priv, &val32); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciout: 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciunsubscribe: 5188c2ecf20Sopenharmony_ci /* Disable interrupt signalling */ 5198c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 5208c2ecf20Sopenharmony_ci ret = __cw1200_irq_enable(priv, 0); 5218c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci} 524