162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bxt-sst.c - DSP library functions for BXT platform 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-16 Intel Corp 662306a36Sopenharmony_ci * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> 762306a36Sopenharmony_ci * Jeeja KP <jeeja.kp@intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "../common/sst-dsp.h" 1662306a36Sopenharmony_ci#include "../common/sst-dsp-priv.h" 1762306a36Sopenharmony_ci#include "skl.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define BXT_BASEFW_TIMEOUT 3000 2062306a36Sopenharmony_ci#define BXT_ROM_INIT_TIMEOUT 70 2162306a36Sopenharmony_ci#define BXT_IPC_PURGE_FW 0x01004000 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define BXT_ROM_INIT 0x5 2462306a36Sopenharmony_ci#define BXT_ADSP_SRAM0_BASE 0x80000 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Firmware status window */ 2762306a36Sopenharmony_ci#define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE 2862306a36Sopenharmony_ci#define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define BXT_ADSP_SRAM1_BASE 0xA0000 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define BXT_INSTANCE_ID 0 3362306a36Sopenharmony_ci#define BXT_BASE_FW_MODULE_ID 0 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Delay before scheduling D0i3 entry */ 3862306a36Sopenharmony_ci#define BXT_D0I3_DELAY 5000 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic unsigned int bxt_get_errorcode(struct sst_dsp *ctx) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int 4662306a36Sopenharmony_cibxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct snd_dma_buffer dmab; 4962306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 5062306a36Sopenharmony_ci struct firmware stripped_fw; 5162306a36Sopenharmony_ci int ret = 0, i, dma_id, stream_tag; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* library indices start from 1 to N. 0 represents base FW */ 5462306a36Sopenharmony_ci for (i = 1; i < lib_count; i++) { 5562306a36Sopenharmony_ci ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw, 5662306a36Sopenharmony_ci BXT_ADSP_FW_BIN_HDR_OFFSET, i); 5762306a36Sopenharmony_ci if (ret < 0) 5862306a36Sopenharmony_ci goto load_library_failed; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, 6162306a36Sopenharmony_ci stripped_fw.size, &dmab); 6262306a36Sopenharmony_ci if (stream_tag <= 0) { 6362306a36Sopenharmony_ci dev_err(ctx->dev, "Lib prepare DMA err: %x\n", 6462306a36Sopenharmony_ci stream_tag); 6562306a36Sopenharmony_ci ret = stream_tag; 6662306a36Sopenharmony_ci goto load_library_failed; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci dma_id = stream_tag - 1; 7062306a36Sopenharmony_ci memcpy(dmab.area, stripped_fw.data, stripped_fw.size); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ctx->dsp_ops.trigger(ctx->dev, true, stream_tag); 7362306a36Sopenharmony_ci ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i, true); 7462306a36Sopenharmony_ci if (ret < 0) 7562306a36Sopenharmony_ci dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", 7662306a36Sopenharmony_ci linfo[i].name, ret); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ctx->dsp_ops.trigger(ctx->dev, false, stream_tag); 7962306a36Sopenharmony_ci ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciload_library_failed: 8562306a36Sopenharmony_ci skl_release_library(linfo, lib_count); 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * First boot sequence has some extra steps. Core 0 waits for power 9162306a36Sopenharmony_ci * status on core 1, so power up core 1 also momentarily, keep it in 9262306a36Sopenharmony_ci * reset/stall and then turn it off 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic int sst_bxt_prepare_fw(struct sst_dsp *ctx, 9562306a36Sopenharmony_ci const void *fwdata, u32 fwsize) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int stream_tag, ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); 10062306a36Sopenharmony_ci if (stream_tag <= 0) { 10162306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", 10262306a36Sopenharmony_ci stream_tag); 10362306a36Sopenharmony_ci return stream_tag; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ctx->dsp_ops.stream_tag = stream_tag; 10762306a36Sopenharmony_ci memcpy(ctx->dmab.area, fwdata, fwsize); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Step 1: Power up core 0 and core1 */ 11062306a36Sopenharmony_ci ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK | 11162306a36Sopenharmony_ci SKL_DSP_CORE_MASK(1)); 11262306a36Sopenharmony_ci if (ret < 0) { 11362306a36Sopenharmony_ci dev_err(ctx->dev, "dsp core0/1 power up failed\n"); 11462306a36Sopenharmony_ci goto base_fw_load_failed; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Step 2: Purge FW request */ 11862306a36Sopenharmony_ci sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | 11962306a36Sopenharmony_ci (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9))); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Step 3: Unset core0 reset state & unstall/run core0 */ 12262306a36Sopenharmony_ci ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); 12362306a36Sopenharmony_ci if (ret < 0) { 12462306a36Sopenharmony_ci dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret); 12562306a36Sopenharmony_ci ret = -EIO; 12662306a36Sopenharmony_ci goto base_fw_load_failed; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Step 4: Wait for DONE Bit */ 13062306a36Sopenharmony_ci ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_HIPCIE, 13162306a36Sopenharmony_ci SKL_ADSP_REG_HIPCIE_DONE, 13262306a36Sopenharmony_ci SKL_ADSP_REG_HIPCIE_DONE, 13362306a36Sopenharmony_ci BXT_INIT_TIMEOUT, "HIPCIE Done"); 13462306a36Sopenharmony_ci if (ret < 0) { 13562306a36Sopenharmony_ci dev_err(ctx->dev, "Timeout for Purge Request%d\n", ret); 13662306a36Sopenharmony_ci goto base_fw_load_failed; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Step 5: power down core1 */ 14062306a36Sopenharmony_ci ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); 14162306a36Sopenharmony_ci if (ret < 0) { 14262306a36Sopenharmony_ci dev_err(ctx->dev, "dsp core1 power down failed\n"); 14362306a36Sopenharmony_ci goto base_fw_load_failed; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Step 6: Enable Interrupt */ 14762306a36Sopenharmony_ci skl_ipc_int_enable(ctx); 14862306a36Sopenharmony_ci skl_ipc_op_int_enable(ctx); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Step 7: Wait for ROM init */ 15162306a36Sopenharmony_ci ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, 15262306a36Sopenharmony_ci SKL_FW_INIT, BXT_ROM_INIT_TIMEOUT, "ROM Load"); 15362306a36Sopenharmony_ci if (ret < 0) { 15462306a36Sopenharmony_ci dev_err(ctx->dev, "Timeout for ROM init, ret:%d\n", ret); 15562306a36Sopenharmony_ci goto base_fw_load_failed; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cibase_fw_load_failed: 16162306a36Sopenharmony_ci ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); 16262306a36Sopenharmony_ci skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); 16362306a36Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int sst_transfer_fw_host_dma(struct sst_dsp *ctx) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int ret; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); 17262306a36Sopenharmony_ci ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, 17362306a36Sopenharmony_ci BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); 17662306a36Sopenharmony_ci ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int bxt_load_base_firmware(struct sst_dsp *ctx) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct firmware stripped_fw; 18462306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 18562306a36Sopenharmony_ci int ret, i; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (ctx->fw == NULL) { 18862306a36Sopenharmony_ci ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); 18962306a36Sopenharmony_ci if (ret < 0) { 19062306a36Sopenharmony_ci dev_err(ctx->dev, "Request firmware failed %d\n", ret); 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* prase uuids on first boot */ 19662306a36Sopenharmony_ci if (skl->is_first_boot) { 19762306a36Sopenharmony_ci ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci goto sst_load_base_firmware_failed; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci stripped_fw.data = ctx->fw->data; 20362306a36Sopenharmony_ci stripped_fw.size = ctx->fw->size; 20462306a36Sopenharmony_ci skl_dsp_strip_extended_manifest(&stripped_fw); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) { 20862306a36Sopenharmony_ci ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); 20962306a36Sopenharmony_ci if (ret == 0) 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (ret < 0) { 21462306a36Sopenharmony_ci dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", 21562306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), 21662306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); 21962306a36Sopenharmony_ci goto sst_load_base_firmware_failed; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = sst_transfer_fw_host_dma(ctx); 22362306a36Sopenharmony_ci if (ret < 0) { 22462306a36Sopenharmony_ci dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); 22562306a36Sopenharmony_ci dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", 22662306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), 22762306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 23062306a36Sopenharmony_ci } else { 23162306a36Sopenharmony_ci dev_dbg(ctx->dev, "Firmware download successful\n"); 23262306a36Sopenharmony_ci ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, 23362306a36Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 23462306a36Sopenharmony_ci if (ret == 0) { 23562306a36Sopenharmony_ci dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n"); 23662306a36Sopenharmony_ci skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); 23762306a36Sopenharmony_ci ret = -EIO; 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci ret = 0; 24062306a36Sopenharmony_ci skl->fw_loaded = true; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cisst_load_base_firmware_failed: 24762306a36Sopenharmony_ci release_firmware(ctx->fw); 24862306a36Sopenharmony_ci ctx->fw = NULL; 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Decide the D0i3 state that can be targeted based on the usecase 25462306a36Sopenharmony_ci * ref counts and DSP state 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Decision Matrix: (X= dont care; state = target state) 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * DSP state != SKL_DSP_RUNNING ; state = no d0i3 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * DSP state == SKL_DSP_RUNNING , the following matrix applies 26162306a36Sopenharmony_ci * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3 26262306a36Sopenharmony_ci * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3 26362306a36Sopenharmony_ci * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3 26462306a36Sopenharmony_ci * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistatic int bxt_d0i3_target_state(struct sst_dsp *ctx) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 26962306a36Sopenharmony_ci struct skl_d0i3_data *d0i3 = &skl->d0i3; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING) 27262306a36Sopenharmony_ci return SKL_DSP_D0I3_NONE; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (d0i3->non_d0i3) 27562306a36Sopenharmony_ci return SKL_DSP_D0I3_NONE; 27662306a36Sopenharmony_ci else if (d0i3->streaming) 27762306a36Sopenharmony_ci return SKL_DSP_D0I3_STREAMING; 27862306a36Sopenharmony_ci else if (d0i3->non_streaming) 27962306a36Sopenharmony_ci return SKL_DSP_D0I3_NON_STREAMING; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci return SKL_DSP_D0I3_NONE; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void bxt_set_dsp_D0i3(struct work_struct *work) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci struct skl_ipc_d0ix_msg msg; 28862306a36Sopenharmony_ci struct skl_dev *skl = container_of(work, 28962306a36Sopenharmony_ci struct skl_dev, d0i3.work.work); 29062306a36Sopenharmony_ci struct sst_dsp *ctx = skl->dsp; 29162306a36Sopenharmony_ci struct skl_d0i3_data *d0i3 = &skl->d0i3; 29262306a36Sopenharmony_ci int target_state; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci dev_dbg(ctx->dev, "In %s:\n", __func__); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* D0i3 entry allowed only if core 0 alone is running */ 29762306a36Sopenharmony_ci if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) { 29862306a36Sopenharmony_ci dev_warn(ctx->dev, 29962306a36Sopenharmony_ci "D0i3 allowed when only core0 running:Exit\n"); 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci target_state = bxt_d0i3_target_state(ctx); 30462306a36Sopenharmony_ci if (target_state == SKL_DSP_D0I3_NONE) 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci msg.instance_id = 0; 30862306a36Sopenharmony_ci msg.module_id = 0; 30962306a36Sopenharmony_ci msg.wake = 1; 31062306a36Sopenharmony_ci msg.streaming = 0; 31162306a36Sopenharmony_ci if (target_state == SKL_DSP_D0I3_STREAMING) 31262306a36Sopenharmony_ci msg.streaming = 1; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = skl_ipc_set_d0ix(&skl->ipc, &msg); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (ret < 0) { 31762306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n"); 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Set Vendor specific register D0I3C.I3 to enable D0i3*/ 32262306a36Sopenharmony_ci if (skl->update_d0i3c) 32362306a36Sopenharmony_ci skl->update_d0i3c(skl->dev, true); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci d0i3->state = target_state; 32662306a36Sopenharmony_ci skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 33262306a36Sopenharmony_ci struct skl_d0i3_data *d0i3 = &skl->d0i3; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Schedule D0i3 only if the usecase ref counts are appropriate */ 33562306a36Sopenharmony_ci if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) { 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci schedule_delayed_work(&d0i3->work, 34062306a36Sopenharmony_ci msecs_to_jiffies(BXT_D0I3_DELAY)); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int bxt_set_dsp_D0i0(struct sst_dsp *ctx) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci struct skl_ipc_d0ix_msg msg; 35062306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(ctx->dev, "In %s:\n", __func__); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* First Cancel any pending attempt to put DSP to D0i3 */ 35562306a36Sopenharmony_ci cancel_delayed_work_sync(&skl->d0i3.work); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* If DSP is currently in D0i3, bring it to D0i0 */ 35862306a36Sopenharmony_ci if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3) 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci dev_dbg(ctx->dev, "Set DSP to D0i0\n"); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci msg.instance_id = 0; 36462306a36Sopenharmony_ci msg.module_id = 0; 36562306a36Sopenharmony_ci msg.streaming = 0; 36662306a36Sopenharmony_ci msg.wake = 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING) 36962306a36Sopenharmony_ci msg.streaming = 1; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/ 37262306a36Sopenharmony_ci if (skl->update_d0i3c) 37362306a36Sopenharmony_ci skl->update_d0i3c(skl->dev, false); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = skl_ipc_set_d0ix(&skl->ipc, &msg); 37662306a36Sopenharmony_ci if (ret < 0) { 37762306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to set DSP to D0i0\n"); 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; 38262306a36Sopenharmony_ci skl->d0i3.state = SKL_DSP_D0I3_NONE; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 39062306a36Sopenharmony_ci int ret; 39162306a36Sopenharmony_ci struct skl_ipc_dxstate_info dx; 39262306a36Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (skl->fw_loaded == false) { 39562306a36Sopenharmony_ci skl->boot_complete = false; 39662306a36Sopenharmony_ci ret = bxt_load_base_firmware(ctx); 39762306a36Sopenharmony_ci if (ret < 0) { 39862306a36Sopenharmony_ci dev_err(ctx->dev, "reload fw failed: %d\n", ret); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (skl->lib_count > 1) { 40362306a36Sopenharmony_ci ret = bxt_load_library(ctx, skl->lib_info, 40462306a36Sopenharmony_ci skl->lib_count); 40562306a36Sopenharmony_ci if (ret < 0) { 40662306a36Sopenharmony_ci dev_err(ctx->dev, "reload libs failed: %d\n", ret); 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RUNNING; 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* If core 0 is being turned on, turn on core 1 as well */ 41562306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) 41662306a36Sopenharmony_ci ret = skl_dsp_core_power_up(ctx, core_mask | 41762306a36Sopenharmony_ci SKL_DSP_CORE_MASK(1)); 41862306a36Sopenharmony_ci else 41962306a36Sopenharmony_ci ret = skl_dsp_core_power_up(ctx, core_mask); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (ret < 0) 42262306a36Sopenharmony_ci goto err; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * Enable interrupt after SPA is set and before 42862306a36Sopenharmony_ci * DSP is unstalled 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci skl_ipc_int_enable(ctx); 43162306a36Sopenharmony_ci skl_ipc_op_int_enable(ctx); 43262306a36Sopenharmony_ci skl->boot_complete = false; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ret = skl_dsp_start_core(ctx, core_mask); 43662306a36Sopenharmony_ci if (ret < 0) 43762306a36Sopenharmony_ci goto err; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 44062306a36Sopenharmony_ci ret = wait_event_timeout(skl->boot_wait, 44162306a36Sopenharmony_ci skl->boot_complete, 44262306a36Sopenharmony_ci msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* If core 1 was turned on for booting core 0, turn it off */ 44562306a36Sopenharmony_ci skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); 44662306a36Sopenharmony_ci if (ret == 0) { 44762306a36Sopenharmony_ci dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__); 44862306a36Sopenharmony_ci dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", 44962306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), 45062306a36Sopenharmony_ci sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); 45162306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to set core0 to D0 state\n"); 45262306a36Sopenharmony_ci ret = -EIO; 45362306a36Sopenharmony_ci goto err; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Tell FW if additional core in now On */ 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (core_id != SKL_DSP_CORE0_ID) { 46062306a36Sopenharmony_ci dx.core_mask = core_mask; 46162306a36Sopenharmony_ci dx.dx_mask = core_mask; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, 46462306a36Sopenharmony_ci BXT_BASE_FW_MODULE_ID, &dx); 46562306a36Sopenharmony_ci if (ret < 0) { 46662306a36Sopenharmony_ci dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n", 46762306a36Sopenharmony_ci core_id, ret); 46862306a36Sopenharmony_ci goto err; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RUNNING; 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_cierr: 47562306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) 47662306a36Sopenharmony_ci core_mask |= SKL_DSP_CORE_MASK(1); 47762306a36Sopenharmony_ci skl_dsp_disable_core(ctx, core_mask); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int ret; 48562306a36Sopenharmony_ci struct skl_ipc_dxstate_info dx; 48662306a36Sopenharmony_ci struct skl_dev *skl = ctx->thread_context; 48762306a36Sopenharmony_ci unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci dx.core_mask = core_mask; 49062306a36Sopenharmony_ci dx.dx_mask = SKL_IPC_D3_MASK; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n", 49362306a36Sopenharmony_ci dx.core_mask, dx.dx_mask); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, 49662306a36Sopenharmony_ci BXT_BASE_FW_MODULE_ID, &dx); 49762306a36Sopenharmony_ci if (ret < 0) { 49862306a36Sopenharmony_ci dev_err(ctx->dev, 49962306a36Sopenharmony_ci "Failed to set DSP to D3:core id = %d;Continue reset\n", 50062306a36Sopenharmony_ci core_id); 50162306a36Sopenharmony_ci /* 50262306a36Sopenharmony_ci * In case of D3 failure, re-download the firmware, so set 50362306a36Sopenharmony_ci * fw_loaded to false. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci skl->fw_loaded = false; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (core_id == SKL_DSP_CORE0_ID) { 50962306a36Sopenharmony_ci /* disable Interrupt */ 51062306a36Sopenharmony_ci skl_ipc_op_int_disable(ctx); 51162306a36Sopenharmony_ci skl_ipc_int_disable(ctx); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci ret = skl_dsp_disable_core(ctx, core_mask); 51462306a36Sopenharmony_ci if (ret < 0) { 51562306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to disable core %d\n", ret); 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci skl->cores.state[core_id] = SKL_DSP_RESET; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic const struct skl_dsp_fw_ops bxt_fw_ops = { 52362306a36Sopenharmony_ci .set_state_D0 = bxt_set_dsp_D0, 52462306a36Sopenharmony_ci .set_state_D3 = bxt_set_dsp_D3, 52562306a36Sopenharmony_ci .set_state_D0i3 = bxt_schedule_dsp_D0i3, 52662306a36Sopenharmony_ci .set_state_D0i0 = bxt_set_dsp_D0i0, 52762306a36Sopenharmony_ci .load_fw = bxt_load_base_firmware, 52862306a36Sopenharmony_ci .get_fw_errcode = bxt_get_errorcode, 52962306a36Sopenharmony_ci .load_library = bxt_load_library, 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic struct sst_ops skl_ops = { 53362306a36Sopenharmony_ci .irq_handler = skl_dsp_sst_interrupt, 53462306a36Sopenharmony_ci .write = sst_shim32_write, 53562306a36Sopenharmony_ci .read = sst_shim32_read, 53662306a36Sopenharmony_ci .free = skl_dsp_free, 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic struct sst_dsp_device skl_dev = { 54062306a36Sopenharmony_ci .thread = skl_dsp_irq_thread_handler, 54162306a36Sopenharmony_ci .ops = &skl_ops, 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciint bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, 54562306a36Sopenharmony_ci const char *fw_name, struct skl_dsp_loader_ops dsp_ops, 54662306a36Sopenharmony_ci struct skl_dev **dsp) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct skl_dev *skl; 54962306a36Sopenharmony_ci struct sst_dsp *sst; 55062306a36Sopenharmony_ci int ret; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); 55362306a36Sopenharmony_ci if (ret < 0) { 55462306a36Sopenharmony_ci dev_err(dev, "%s: no device\n", __func__); 55562306a36Sopenharmony_ci return ret; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci skl = *dsp; 55962306a36Sopenharmony_ci sst = skl->dsp; 56062306a36Sopenharmony_ci sst->fw_ops = bxt_fw_ops; 56162306a36Sopenharmony_ci sst->addr.lpe = mmio_base; 56262306a36Sopenharmony_ci sst->addr.shim = mmio_base; 56362306a36Sopenharmony_ci sst->addr.sram0_base = BXT_ADSP_SRAM0_BASE; 56462306a36Sopenharmony_ci sst->addr.sram1_base = BXT_ADSP_SRAM1_BASE; 56562306a36Sopenharmony_ci sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ; 56662306a36Sopenharmony_ci sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), 56962306a36Sopenharmony_ci SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci ret = skl_ipc_init(dev, skl); 57262306a36Sopenharmony_ci if (ret) { 57362306a36Sopenharmony_ci skl_dsp_free(sst); 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* set the D0i3 check */ 57862306a36Sopenharmony_ci skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci skl->boot_complete = false; 58162306a36Sopenharmony_ci init_waitqueue_head(&skl->boot_wait); 58262306a36Sopenharmony_ci INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3); 58362306a36Sopenharmony_ci skl->d0i3.state = SKL_DSP_D0I3_NONE; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return skl_dsp_acquire_irq(sst); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bxt_sst_dsp_init); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint bxt_sst_init_fw(struct device *dev, struct skl_dev *skl) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci int ret; 59262306a36Sopenharmony_ci struct sst_dsp *sst = skl->dsp; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci ret = sst->fw_ops.load_fw(sst); 59562306a36Sopenharmony_ci if (ret < 0) { 59662306a36Sopenharmony_ci dev_err(dev, "Load base fw failed: %x\n", ret); 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci skl_dsp_init_core_state(sst); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (skl->lib_count > 1) { 60362306a36Sopenharmony_ci ret = sst->fw_ops.load_library(sst, skl->lib_info, 60462306a36Sopenharmony_ci skl->lib_count); 60562306a36Sopenharmony_ci if (ret < 0) { 60662306a36Sopenharmony_ci dev_err(dev, "Load Library failed : %x\n", ret); 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci skl->is_first_boot = false; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bxt_sst_init_fw); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_civoid bxt_sst_dsp_cleanup(struct device *dev, struct skl_dev *skl) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci skl_release_library(skl->lib_info, skl->lib_count); 62062306a36Sopenharmony_ci if (skl->dsp->fw) 62162306a36Sopenharmony_ci release_firmware(skl->dsp->fw); 62262306a36Sopenharmony_ci skl_freeup_uuid_list(skl); 62362306a36Sopenharmony_ci skl_ipc_free(&skl->ipc); 62462306a36Sopenharmony_ci skl->dsp->ops->free(skl->dsp); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 62962306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Broxton IPC driver"); 630