162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of wl1251 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "reg.h" 1162306a36Sopenharmony_ci#include "boot.h" 1262306a36Sopenharmony_ci#include "io.h" 1362306a36Sopenharmony_ci#include "spi.h" 1462306a36Sopenharmony_ci#include "event.h" 1562306a36Sopenharmony_ci#include "acx.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_civoid wl1251_boot_target_enable_interrupts(struct wl1251 *wl) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); 2062306a36Sopenharmony_ci wl1251_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint wl1251_boot_soft_reset(struct wl1251 *wl) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long timeout; 2662306a36Sopenharmony_ci u32 boot_data; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* perform soft reset */ 2962306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* SOFT_RESET is self clearing */ 3262306a36Sopenharmony_ci timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); 3362306a36Sopenharmony_ci while (1) { 3462306a36Sopenharmony_ci boot_data = wl1251_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); 3562306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); 3662306a36Sopenharmony_ci if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 4062306a36Sopenharmony_ci /* 1.2 check pWhalBus->uSelfClearTime if the 4162306a36Sopenharmony_ci * timeout was reached */ 4262306a36Sopenharmony_ci wl1251_error("soft reset timeout"); 4362306a36Sopenharmony_ci return -1; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci udelay(SOFT_RESET_STALL_TIME); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* disable Rx/Tx */ 5062306a36Sopenharmony_ci wl1251_reg_write32(wl, ENABLE, 0x0); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* disable auto calibration on start*/ 5362306a36Sopenharmony_ci wl1251_reg_write32(wl, SPARE_A2, 0xffff); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint wl1251_boot_init_seq(struct wl1251 *wl) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * col #1: INTEGER_DIVIDER 6462306a36Sopenharmony_ci * col #2: FRACTIONAL_DIVIDER 6562306a36Sopenharmony_ci * col #3: ATTN_BB 6662306a36Sopenharmony_ci * col #4: ALPHA_BB 6762306a36Sopenharmony_ci * col #5: STOP_TIME_BB 6862306a36Sopenharmony_ci * col #6: BB_PLL_LOOP_FILTER 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = { 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci { 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/ 7362306a36Sopenharmony_ci { 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/ 7462306a36Sopenharmony_ci { 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/ 7562306a36Sopenharmony_ci { 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/ 7662306a36Sopenharmony_ci { 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */ 7762306a36Sopenharmony_ci }; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* read NVS params */ 8062306a36Sopenharmony_ci scr_pad6 = wl1251_reg_read32(wl, SCR_PAD6); 8162306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* read ELP_CMD */ 8462306a36Sopenharmony_ci elp_cmd = wl1251_reg_read32(wl, ELP_CMD); 8562306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */ 8862306a36Sopenharmony_ci ref_freq = scr_pad6 & 0x000000FF; 8962306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci wl1251_reg_write32(wl, PLL_CAL_TIME, 0x9); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME) 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci wl1251_reg_write32(wl, CLK_BUF_TIME, 0x6); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * set the clock detect feature to work in the restart wu procedure 10062306a36Sopenharmony_ci * (ELP_CFG_MODE[14]) and Select the clock source type 10162306a36Sopenharmony_ci * (ELP_CFG_MODE[13:12]) 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000; 10462306a36Sopenharmony_ci wl1251_reg_write32(wl, ELP_CFG_MODE, tmp); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */ 10762306a36Sopenharmony_ci elp_cmd |= 0x00000040; 10862306a36Sopenharmony_ci wl1251_reg_write32(wl, ELP_CMD, elp_cmd); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* PG 1.2: Set the BB PLL stable time to be 1000usec 11162306a36Sopenharmony_ci * (PLL_STABLE_TIME) */ 11262306a36Sopenharmony_ci wl1251_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* PG 1.2: read clock request time */ 11562306a36Sopenharmony_ci init_data = wl1251_reg_read32(wl, CLK_REQ_TIME); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * PG 1.2: set the clock request time to be ref_clk_settling_time - 11962306a36Sopenharmony_ci * 1ms = 4ms 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (init_data > 0x21) 12262306a36Sopenharmony_ci tmp = init_data - 0x21; 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci tmp = 0; 12562306a36Sopenharmony_ci wl1251_reg_write32(wl, CLK_REQ_TIME, tmp); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* set BB PLL configurations in RF AFE */ 12862306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x003058cc, 0x4B5); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* set RF_AFE_REG_5 */ 13162306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x003058d4, 0x50); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* set RF_AFE_CTRL_REG_2 */ 13462306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305948, 0x11c001); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * change RF PLL and BB PLL divider for VCO clock and adjust VCO 13862306a36Sopenharmony_ci * bais current(RF_AFE_REG_13) 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x003058f4, 0x1e); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* set BB PLL configurations */ 14362306a36Sopenharmony_ci tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000; 14462306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305840, tmp); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* set fractional divider according to Appendix C-BB PLL 14762306a36Sopenharmony_ci * Calculations 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER]; 15062306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305844, tmp); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* set the initial data for the sigma delta */ 15362306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305848, 0x3039); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * set the accumulator attenuation value, calibration loop1 15762306a36Sopenharmony_ci * (alpha), calibration loop2 (beta), calibration loop3 (gamma) and 15862306a36Sopenharmony_ci * the VCO gain 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) | 16162306a36Sopenharmony_ci (LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1; 16262306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305854, tmp); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * set the calibration stop time after holdoff time expires and set 16662306a36Sopenharmony_ci * settling time HOLD_OFF_TIME_BB 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000; 16962306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x00305858, tmp); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL 17362306a36Sopenharmony_ci * constant leakage current to linearize PFD to 0uA - 17462306a36Sopenharmony_ci * BB_ILOOPF[7:3] 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030; 17762306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x003058f8, tmp); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * set regulator output voltage for n divider to 18162306a36Sopenharmony_ci * 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2], 18262306a36Sopenharmony_ci * set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB 18362306a36Sopenharmony_ci * PLL auto-call to normal mode- BB_CALGAIN_3DB[8] 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci wl1251_reg_write32(wl, 0x003058f0, 0x29); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* enable restart wakeup sequence (ELP_CMD[0]) */ 18862306a36Sopenharmony_ci wl1251_reg_write32(wl, ELP_CMD, elp_cmd | 0x1); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* restart sequence completed */ 19162306a36Sopenharmony_ci udelay(2000); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void wl1251_boot_set_ecpu_ctrl(struct wl1251 *wl, u32 flag) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 cpu_ctrl; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* 10.5.0 run the firmware (I) */ 20162306a36Sopenharmony_ci cpu_ctrl = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 10.5.1 run the firmware (II) */ 20462306a36Sopenharmony_ci cpu_ctrl &= ~flag; 20562306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint wl1251_boot_run_firmware(struct wl1251 *wl) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int loop, ret; 21162306a36Sopenharmony_ci u32 chip_id, acx_intr; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci wl1251_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci chip_id = wl1251_reg_read32(wl, CHIP_ID_B); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (chip_id != wl->chip_id) { 22062306a36Sopenharmony_ci wl1251_error("chip id doesn't match after firmware boot"); 22162306a36Sopenharmony_ci return -EIO; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* wait for init to complete */ 22562306a36Sopenharmony_ci loop = 0; 22662306a36Sopenharmony_ci while (loop++ < INIT_LOOP) { 22762306a36Sopenharmony_ci udelay(INIT_LOOP_DELAY); 22862306a36Sopenharmony_ci acx_intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (acx_intr == 0xffffffff) { 23162306a36Sopenharmony_ci wl1251_error("error reading hardware complete " 23262306a36Sopenharmony_ci "init indication"); 23362306a36Sopenharmony_ci return -EIO; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci /* check that ACX_INTR_INIT_COMPLETE is enabled */ 23662306a36Sopenharmony_ci else if (acx_intr & WL1251_ACX_INTR_INIT_COMPLETE) { 23762306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, 23862306a36Sopenharmony_ci WL1251_ACX_INTR_INIT_COMPLETE); 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (loop > INIT_LOOP) { 24462306a36Sopenharmony_ci wl1251_error("timeout waiting for the hardware to " 24562306a36Sopenharmony_ci "complete initialization"); 24662306a36Sopenharmony_ci return -EIO; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* get hardware config command mail box */ 25062306a36Sopenharmony_ci wl->cmd_box_addr = wl1251_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* get hardware config event mail box */ 25362306a36Sopenharmony_ci wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* set the working partition to its "running" mode offset */ 25662306a36Sopenharmony_ci wl1251_set_partition(wl, WL1251_PART_WORK_MEM_START, 25762306a36Sopenharmony_ci WL1251_PART_WORK_MEM_SIZE, 25862306a36Sopenharmony_ci WL1251_PART_WORK_REG_START, 25962306a36Sopenharmony_ci WL1251_PART_WORK_REG_SIZE); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", 26262306a36Sopenharmony_ci wl->cmd_box_addr, wl->event_box_addr); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci wl1251_acx_fw_version(wl, wl->fw_ver, sizeof(wl->fw_ver)); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * in case of full asynchronous mode the firmware event must be 26862306a36Sopenharmony_ci * ready to receive event from the command mailbox 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* enable gpio interrupts */ 27262306a36Sopenharmony_ci wl1251_enable_interrupts(wl); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Enable target's interrupts */ 27562306a36Sopenharmony_ci wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | 27662306a36Sopenharmony_ci WL1251_ACX_INTR_RX1_DATA | 27762306a36Sopenharmony_ci WL1251_ACX_INTR_TX_RESULT | 27862306a36Sopenharmony_ci WL1251_ACX_INTR_EVENT_A | 27962306a36Sopenharmony_ci WL1251_ACX_INTR_EVENT_B | 28062306a36Sopenharmony_ci WL1251_ACX_INTR_INIT_COMPLETE; 28162306a36Sopenharmony_ci wl1251_boot_target_enable_interrupts(wl); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID | 28462306a36Sopenharmony_ci SYNCHRONIZATION_TIMEOUT_EVENT_ID | 28562306a36Sopenharmony_ci ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | 28662306a36Sopenharmony_ci ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | 28762306a36Sopenharmony_ci REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | 28862306a36Sopenharmony_ci BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID | 28962306a36Sopenharmony_ci PS_REPORT_EVENT_ID; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = wl1251_event_unmask(wl); 29262306a36Sopenharmony_ci if (ret < 0) { 29362306a36Sopenharmony_ci wl1251_error("EVENT mask setting failed"); 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci wl1251_event_mbox_config(wl); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* firmware startup completed */ 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int wl1251_boot_upload_firmware(struct wl1251 *wl) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int addr, chunk_num, partition_limit; 30662306a36Sopenharmony_ci size_t fw_data_len, len; 30762306a36Sopenharmony_ci u8 *p, *buf; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* whal_FwCtrl_LoadFwImageSm() */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", 31262306a36Sopenharmony_ci wl1251_reg_read32(wl, CHIP_ID_B)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 10.0 check firmware length and set partition */ 31562306a36Sopenharmony_ci fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | 31662306a36Sopenharmony_ci (wl->fw[6] << 8) | (wl->fw[7]); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, 31962306a36Sopenharmony_ci CHUNK_SIZE); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if ((fw_data_len % 4) != 0) { 32262306a36Sopenharmony_ci wl1251_error("firmware length not multiple of four"); 32362306a36Sopenharmony_ci return -EIO; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); 32762306a36Sopenharmony_ci if (!buf) { 32862306a36Sopenharmony_ci wl1251_error("allocation for firmware upload chunk failed"); 32962306a36Sopenharmony_ci return -ENOMEM; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, 33362306a36Sopenharmony_ci WL1251_PART_DOWN_MEM_SIZE, 33462306a36Sopenharmony_ci WL1251_PART_DOWN_REG_START, 33562306a36Sopenharmony_ci WL1251_PART_DOWN_REG_SIZE); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 10.1 set partition limit and chunk num */ 33862306a36Sopenharmony_ci chunk_num = 0; 33962306a36Sopenharmony_ci partition_limit = WL1251_PART_DOWN_MEM_SIZE; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci while (chunk_num < fw_data_len / CHUNK_SIZE) { 34262306a36Sopenharmony_ci /* 10.2 update partition, if needed */ 34362306a36Sopenharmony_ci addr = WL1251_PART_DOWN_MEM_START + 34462306a36Sopenharmony_ci (chunk_num + 2) * CHUNK_SIZE; 34562306a36Sopenharmony_ci if (addr > partition_limit) { 34662306a36Sopenharmony_ci addr = WL1251_PART_DOWN_MEM_START + 34762306a36Sopenharmony_ci chunk_num * CHUNK_SIZE; 34862306a36Sopenharmony_ci partition_limit = chunk_num * CHUNK_SIZE + 34962306a36Sopenharmony_ci WL1251_PART_DOWN_MEM_SIZE; 35062306a36Sopenharmony_ci wl1251_set_partition(wl, 35162306a36Sopenharmony_ci addr, 35262306a36Sopenharmony_ci WL1251_PART_DOWN_MEM_SIZE, 35362306a36Sopenharmony_ci WL1251_PART_DOWN_REG_START, 35462306a36Sopenharmony_ci WL1251_PART_DOWN_REG_SIZE); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 10.3 upload the chunk */ 35862306a36Sopenharmony_ci addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; 35962306a36Sopenharmony_ci p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; 36062306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", 36162306a36Sopenharmony_ci p, addr); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* need to copy the chunk for dma */ 36462306a36Sopenharmony_ci len = CHUNK_SIZE; 36562306a36Sopenharmony_ci memcpy(buf, p, len); 36662306a36Sopenharmony_ci wl1251_mem_write(wl, addr, buf, len); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci chunk_num++; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* 10.4 upload the last chunk */ 37262306a36Sopenharmony_ci addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; 37362306a36Sopenharmony_ci p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* need to copy the chunk for dma */ 37662306a36Sopenharmony_ci len = fw_data_len % CHUNK_SIZE; 37762306a36Sopenharmony_ci memcpy(buf, p, len); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", 38062306a36Sopenharmony_ci len, p, addr); 38162306a36Sopenharmony_ci wl1251_mem_write(wl, addr, buf, len); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci kfree(buf); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int wl1251_boot_upload_nvs(struct wl1251 *wl) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci size_t nvs_len, nvs_bytes_written, burst_len; 39162306a36Sopenharmony_ci int nvs_start, i; 39262306a36Sopenharmony_ci u32 dest_addr, val; 39362306a36Sopenharmony_ci u8 *nvs_ptr, *nvs; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci nvs = wl->nvs; 39662306a36Sopenharmony_ci if (nvs == NULL) 39762306a36Sopenharmony_ci return -ENODEV; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci nvs_ptr = nvs; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci nvs_len = wl->nvs_len; 40262306a36Sopenharmony_ci nvs_start = wl->fw_len; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Layout before the actual NVS tables: 40662306a36Sopenharmony_ci * 1 byte : burst length. 40762306a36Sopenharmony_ci * 2 bytes: destination address. 40862306a36Sopenharmony_ci * n bytes: data to burst copy. 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * This is ended by a 0 length, then the NVS tables. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci while (nvs_ptr[0]) { 41462306a36Sopenharmony_ci burst_len = nvs_ptr[0]; 41562306a36Sopenharmony_ci dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* We move our pointer to the data */ 41862306a36Sopenharmony_ci nvs_ptr += 3; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci for (i = 0; i < burst_len; i++) { 42162306a36Sopenharmony_ci val = (nvs_ptr[0] | (nvs_ptr[1] << 8) 42262306a36Sopenharmony_ci | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, 42562306a36Sopenharmony_ci "nvs burst write 0x%x: 0x%x", 42662306a36Sopenharmony_ci dest_addr, val); 42762306a36Sopenharmony_ci wl1251_mem_write32(wl, dest_addr, val); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci nvs_ptr += 4; 43062306a36Sopenharmony_ci dest_addr += 4; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * We've reached the first zero length, the first NVS table 43662306a36Sopenharmony_ci * is 7 bytes further. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci nvs_ptr += 7; 43962306a36Sopenharmony_ci nvs_len -= nvs_ptr - nvs; 44062306a36Sopenharmony_ci nvs_len = ALIGN(nvs_len, 4); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Now we must set the partition correctly */ 44362306a36Sopenharmony_ci wl1251_set_partition(wl, nvs_start, 44462306a36Sopenharmony_ci WL1251_PART_DOWN_MEM_SIZE, 44562306a36Sopenharmony_ci WL1251_PART_DOWN_REG_START, 44662306a36Sopenharmony_ci WL1251_PART_DOWN_REG_SIZE); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* And finally we upload the NVS tables */ 44962306a36Sopenharmony_ci nvs_bytes_written = 0; 45062306a36Sopenharmony_ci while (nvs_bytes_written < nvs_len) { 45162306a36Sopenharmony_ci val = (nvs_ptr[0] | (nvs_ptr[1] << 8) 45262306a36Sopenharmony_ci | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, 45562306a36Sopenharmony_ci "nvs write table 0x%x: 0x%x", 45662306a36Sopenharmony_ci nvs_start, val); 45762306a36Sopenharmony_ci wl1251_mem_write32(wl, nvs_start, val); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci nvs_ptr += 4; 46062306a36Sopenharmony_ci nvs_bytes_written += 4; 46162306a36Sopenharmony_ci nvs_start += 4; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ciint wl1251_boot(struct wl1251 *wl) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int ret = 0, minor_minor_e2_ver; 47062306a36Sopenharmony_ci u32 tmp, boot_data; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* halt embedded ARM CPU while loading firmware */ 47362306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, ECPU_CONTROL_HALT); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = wl1251_boot_soft_reset(wl); 47662306a36Sopenharmony_ci if (ret < 0) 47762306a36Sopenharmony_ci goto out; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* 2. start processing NVS file */ 48062306a36Sopenharmony_ci if (wl->use_eeprom) { 48162306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR); 48262306a36Sopenharmony_ci /* Wait for EEPROM NVS burst read to complete */ 48362306a36Sopenharmony_ci msleep(40); 48462306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM); 48562306a36Sopenharmony_ci } else { 48662306a36Sopenharmony_ci ret = wl1251_boot_upload_nvs(wl); 48762306a36Sopenharmony_ci if (ret < 0) 48862306a36Sopenharmony_ci goto out; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* write firmware's last address (ie. it's length) to 49162306a36Sopenharmony_ci * ACX_EEPROMLESS_IND_REG */ 49262306a36Sopenharmony_ci wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 6. read the EEPROM parameters */ 49662306a36Sopenharmony_ci tmp = wl1251_reg_read32(wl, SCR_PAD2); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 7. read bootdata */ 49962306a36Sopenharmony_ci wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; 50062306a36Sopenharmony_ci wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; 50162306a36Sopenharmony_ci tmp = wl1251_reg_read32(wl, SCR_PAD3); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* 8. check bootdata and call restart sequence */ 50462306a36Sopenharmony_ci wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; 50562306a36Sopenharmony_ci minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " 50862306a36Sopenharmony_ci "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", 50962306a36Sopenharmony_ci wl->boot_attr.radio_type, wl->boot_attr.major, 51062306a36Sopenharmony_ci wl->boot_attr.minor, minor_minor_e2_ver); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = wl1251_boot_init_seq(wl); 51362306a36Sopenharmony_ci if (ret < 0) 51462306a36Sopenharmony_ci goto out; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* 9. NVS processing done */ 51762306a36Sopenharmony_ci boot_data = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 10. check that ECPU_CONTROL_HALT bits are set in 52262306a36Sopenharmony_ci * pWhalBus->uBootData and start uploading firmware 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci if ((boot_data & ECPU_CONTROL_HALT) == 0) { 52562306a36Sopenharmony_ci wl1251_error("boot failed, ECPU_CONTROL_HALT not set"); 52662306a36Sopenharmony_ci ret = -EIO; 52762306a36Sopenharmony_ci goto out; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = wl1251_boot_upload_firmware(wl); 53162306a36Sopenharmony_ci if (ret < 0) 53262306a36Sopenharmony_ci goto out; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 10.5 start firmware */ 53562306a36Sopenharmony_ci ret = wl1251_boot_run_firmware(wl); 53662306a36Sopenharmony_ci if (ret < 0) 53762306a36Sopenharmony_ci goto out; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciout: 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci} 542