|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * skl-sst-dsp.c - SKL SST library generic function | 
|  | * | 
|  | * Copyright (C) 2014-15, Intel Corporation. | 
|  | * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> | 
|  | *	Jeeja KP <jeeja.kp@intel.com> | 
|  | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
|  | */ | 
|  | #include <sound/pcm.h> | 
|  |  | 
|  | #include "../common/sst-dsp.h" | 
|  | #include "../common/sst-ipc.h" | 
|  | #include "../common/sst-dsp-priv.h" | 
|  | #include "skl.h" | 
|  |  | 
|  | /* various timeout values */ | 
|  | #define SKL_DSP_PU_TO		50 | 
|  | #define SKL_DSP_PD_TO		50 | 
|  | #define SKL_DSP_RESET_TO	50 | 
|  |  | 
|  | void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) | 
|  | { | 
|  | mutex_lock(&ctx->mutex); | 
|  | ctx->sst_state = state; | 
|  | mutex_unlock(&ctx->mutex); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialize core power state and usage count. To be called after | 
|  | * successful first boot. Hence core 0 will be running and other cores | 
|  | * will be reset | 
|  | */ | 
|  | void skl_dsp_init_core_state(struct sst_dsp *ctx) | 
|  | { | 
|  | struct skl_dev *skl = ctx->thread_context; | 
|  | int i; | 
|  |  | 
|  | skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; | 
|  | skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1; | 
|  |  | 
|  | for (i = SKL_DSP_CORE0_ID + 1; i < skl->cores.count; i++) { | 
|  | skl->cores.state[i] = SKL_DSP_RESET; | 
|  | skl->cores.usage_count[i] = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Get the mask for all enabled cores */ | 
|  | unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx) | 
|  | { | 
|  | struct skl_dev *skl = ctx->thread_context; | 
|  | unsigned int core_mask, en_cores_mask; | 
|  | u32 val; | 
|  |  | 
|  | core_mask = SKL_DSP_CORES_MASK(skl->cores.count); | 
|  |  | 
|  | val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); | 
|  |  | 
|  | /* Cores having CPA bit set */ | 
|  | en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >> | 
|  | SKL_ADSPCS_CPA_SHIFT; | 
|  |  | 
|  | /* And cores having CRST bit cleared */ | 
|  | en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >> | 
|  | SKL_ADSPCS_CRST_SHIFT; | 
|  |  | 
|  | /* And cores having CSTALL bit cleared */ | 
|  | en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >> | 
|  | SKL_ADSPCS_CSTALL_SHIFT; | 
|  | en_cores_mask &= core_mask; | 
|  |  | 
|  | dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask); | 
|  |  | 
|  | return en_cores_mask; | 
|  | } | 
|  |  | 
|  | static int | 
|  | skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | /* update bits */ | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, | 
|  | SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask), | 
|  | SKL_ADSPCS_CRST_MASK(core_mask)); | 
|  |  | 
|  | /* poll with timeout to check if operation successful */ | 
|  | ret = sst_dsp_register_poll(ctx, | 
|  | SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CRST_MASK(core_mask), | 
|  | SKL_ADSPCS_CRST_MASK(core_mask), | 
|  | SKL_DSP_RESET_TO, | 
|  | "Set reset"); | 
|  | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | 
|  | SKL_ADSPCS_CRST_MASK(core_mask)) != | 
|  | SKL_ADSPCS_CRST_MASK(core_mask)) { | 
|  | dev_err(ctx->dev, "Set reset state failed: core_mask %x\n", | 
|  | core_mask); | 
|  | ret = -EIO; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int skl_dsp_core_unset_reset_state( | 
|  | struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | dev_dbg(ctx->dev, "In %s\n", __func__); | 
|  |  | 
|  | /* update bits */ | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CRST_MASK(core_mask), 0); | 
|  |  | 
|  | /* poll with timeout to check if operation successful */ | 
|  | ret = sst_dsp_register_poll(ctx, | 
|  | SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CRST_MASK(core_mask), | 
|  | 0, | 
|  | SKL_DSP_RESET_TO, | 
|  | "Unset reset"); | 
|  |  | 
|  | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | 
|  | SKL_ADSPCS_CRST_MASK(core_mask)) != 0) { | 
|  | dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n", | 
|  | core_mask); | 
|  | ret = -EIO; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static bool | 
|  | is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int val; | 
|  | bool is_enable; | 
|  |  | 
|  | val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); | 
|  |  | 
|  | is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) && | 
|  | (val & SKL_ADSPCS_SPA_MASK(core_mask)) && | 
|  | !(val & SKL_ADSPCS_CRST_MASK(core_mask)) && | 
|  | !(val & SKL_ADSPCS_CSTALL_MASK(core_mask))); | 
|  |  | 
|  | dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n", | 
|  | is_enable, core_mask); | 
|  |  | 
|  | return is_enable; | 
|  | } | 
|  |  | 
|  | static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | /* stall core */ | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CSTALL_MASK(core_mask), | 
|  | SKL_ADSPCS_CSTALL_MASK(core_mask)); | 
|  |  | 
|  | /* set reset state */ | 
|  | return skl_dsp_core_set_reset_state(ctx, core_mask); | 
|  | } | 
|  |  | 
|  | int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | /* unset reset state */ | 
|  | ret = skl_dsp_core_unset_reset_state(ctx, core_mask); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  |  | 
|  | /* run core */ | 
|  | dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask); | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CSTALL_MASK(core_mask), 0); | 
|  |  | 
|  | if (!is_skl_dsp_core_enable(ctx, core_mask)) { | 
|  | skl_dsp_reset_core(ctx, core_mask); | 
|  | dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", | 
|  | core_mask); | 
|  | ret = -EIO; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | /* update bits */ | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_SPA_MASK(core_mask), | 
|  | SKL_ADSPCS_SPA_MASK(core_mask)); | 
|  |  | 
|  | /* poll with timeout to check if operation successful */ | 
|  | ret = sst_dsp_register_poll(ctx, | 
|  | SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CPA_MASK(core_mask), | 
|  | SKL_ADSPCS_CPA_MASK(core_mask), | 
|  | SKL_DSP_PU_TO, | 
|  | "Power up"); | 
|  |  | 
|  | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | 
|  | SKL_ADSPCS_CPA_MASK(core_mask)) != | 
|  | SKL_ADSPCS_CPA_MASK(core_mask)) { | 
|  | dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n", | 
|  | core_mask); | 
|  | ret = -EIO; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int skl_dsp_core_power_down(struct sst_dsp  *ctx, unsigned int core_mask) | 
|  | { | 
|  | /* update bits */ | 
|  | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_SPA_MASK(core_mask), 0); | 
|  |  | 
|  | /* poll with timeout to check if operation successful */ | 
|  | return sst_dsp_register_poll(ctx, | 
|  | SKL_ADSP_REG_ADSPCS, | 
|  | SKL_ADSPCS_CPA_MASK(core_mask), | 
|  | 0, | 
|  | SKL_DSP_PD_TO, | 
|  | "Power down"); | 
|  | } | 
|  |  | 
|  | int skl_dsp_enable_core(struct sst_dsp  *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | /* power up */ | 
|  | ret = skl_dsp_core_power_up(ctx, core_mask); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n", | 
|  | core_mask); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return skl_dsp_start_core(ctx, core_mask); | 
|  | } | 
|  |  | 
|  | int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = skl_dsp_reset_core(ctx, core_mask); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n", | 
|  | core_mask); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* power down core*/ | 
|  | ret = skl_dsp_core_power_down(ctx, core_mask); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n", | 
|  | core_mask, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (is_skl_dsp_core_enable(ctx, core_mask)) { | 
|  | dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n", | 
|  | core_mask, ret); | 
|  | ret = -EIO; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int skl_dsp_boot(struct sst_dsp *ctx) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) { | 
|  | ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  | } else { | 
|  | ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  | ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) | 
|  | { | 
|  | struct sst_dsp *ctx = dev_id; | 
|  | u32 val; | 
|  | irqreturn_t result = IRQ_NONE; | 
|  |  | 
|  | spin_lock(&ctx->spinlock); | 
|  |  | 
|  | val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); | 
|  | ctx->intr_status = val; | 
|  |  | 
|  | if (val == 0xffffffff) { | 
|  | spin_unlock(&ctx->spinlock); | 
|  | return IRQ_NONE; | 
|  | } | 
|  |  | 
|  | if (val & SKL_ADSPIS_IPC) { | 
|  | skl_ipc_int_disable(ctx); | 
|  | result = IRQ_WAKE_THREAD; | 
|  | } | 
|  |  | 
|  | if (val & SKL_ADSPIS_CL_DMA) { | 
|  | skl_cldma_int_disable(ctx); | 
|  | result = IRQ_WAKE_THREAD; | 
|  | } | 
|  |  | 
|  | spin_unlock(&ctx->spinlock); | 
|  |  | 
|  | return result; | 
|  | } | 
|  | /* | 
|  | * skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context | 
|  | * within the dapm mutex. Hence no separate lock is used. | 
|  | */ | 
|  | int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id) | 
|  | { | 
|  | struct skl_dev *skl = ctx->thread_context; | 
|  | int ret = 0; | 
|  |  | 
|  | if (core_id >= skl->cores.count) { | 
|  | dev_err(ctx->dev, "invalid core id: %d\n", core_id); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | skl->cores.usage_count[core_id]++; | 
|  |  | 
|  | if (skl->cores.state[core_id] == SKL_DSP_RESET) { | 
|  | ret = ctx->fw_ops.set_state_D0(ctx, core_id); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "unable to get core%d\n", core_id); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | out: | 
|  | dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n", | 
|  | core_id, skl->cores.state[core_id], | 
|  | skl->cores.usage_count[core_id]); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(skl_dsp_get_core); | 
|  |  | 
|  | int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id) | 
|  | { | 
|  | struct skl_dev *skl = ctx->thread_context; | 
|  | int ret = 0; | 
|  |  | 
|  | if (core_id >= skl->cores.count) { | 
|  | dev_err(ctx->dev, "invalid core id: %d\n", core_id); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if ((--skl->cores.usage_count[core_id] == 0) && | 
|  | (skl->cores.state[core_id] != SKL_DSP_RESET)) { | 
|  | ret = ctx->fw_ops.set_state_D3(ctx, core_id); | 
|  | if (ret < 0) { | 
|  | dev_err(ctx->dev, "unable to put core %d: %d\n", | 
|  | core_id, ret); | 
|  | skl->cores.usage_count[core_id]++; | 
|  | } | 
|  | } | 
|  |  | 
|  | dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n", | 
|  | core_id, skl->cores.state[core_id], | 
|  | skl->cores.usage_count[core_id]); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(skl_dsp_put_core); | 
|  |  | 
|  | int skl_dsp_wake(struct sst_dsp *ctx) | 
|  | { | 
|  | return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(skl_dsp_wake); | 
|  |  | 
|  | int skl_dsp_sleep(struct sst_dsp *ctx) | 
|  | { | 
|  | return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(skl_dsp_sleep); | 
|  |  | 
|  | struct sst_dsp *skl_dsp_ctx_init(struct device *dev, | 
|  | struct sst_dsp_device *sst_dev, int irq) | 
|  | { | 
|  | int ret; | 
|  | struct sst_dsp *sst; | 
|  |  | 
|  | sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); | 
|  | if (sst == NULL) | 
|  | return NULL; | 
|  |  | 
|  | spin_lock_init(&sst->spinlock); | 
|  | mutex_init(&sst->mutex); | 
|  | sst->dev = dev; | 
|  | sst->sst_dev = sst_dev; | 
|  | sst->irq = irq; | 
|  | sst->ops = sst_dev->ops; | 
|  | sst->thread_context = sst_dev->thread_context; | 
|  |  | 
|  | /* Initialise SST Audio DSP */ | 
|  | if (sst->ops->init) { | 
|  | ret = sst->ops->init(sst); | 
|  | if (ret < 0) | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return sst; | 
|  | } | 
|  |  | 
|  | int skl_dsp_acquire_irq(struct sst_dsp *sst) | 
|  | { | 
|  | struct sst_dsp_device *sst_dev = sst->sst_dev; | 
|  | int ret; | 
|  |  | 
|  | /* Register the ISR */ | 
|  | ret = request_threaded_irq(sst->irq, sst->ops->irq_handler, | 
|  | sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); | 
|  | if (ret) | 
|  | dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n", | 
|  | sst->irq); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void skl_dsp_free(struct sst_dsp *dsp) | 
|  | { | 
|  | skl_ipc_int_disable(dsp); | 
|  |  | 
|  | free_irq(dsp->irq, dsp); | 
|  | skl_ipc_op_int_disable(dsp); | 
|  | skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(skl_dsp_free); | 
|  |  | 
|  | bool is_skl_dsp_running(struct sst_dsp *ctx) | 
|  | { | 
|  | return (ctx->sst_state == SKL_DSP_RUNNING); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(is_skl_dsp_running); |