12879516fSCezary Rojewski // SPDX-License-Identifier: GPL-2.0-only 22879516fSCezary Rojewski // 32879516fSCezary Rojewski // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 42879516fSCezary Rojewski // 52879516fSCezary Rojewski // Authors: Cezary Rojewski <cezary.rojewski@intel.com> 62879516fSCezary Rojewski // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 72879516fSCezary Rojewski // 82879516fSCezary Rojewski 92879516fSCezary Rojewski #include <linux/slab.h> 102879516fSCezary Rojewski #include <sound/hdaudio_ext.h> 112879516fSCezary Rojewski #include "avs.h" 122879516fSCezary Rojewski #include "messages.h" 132879516fSCezary Rojewski #include "registers.h" 142879516fSCezary Rojewski 152879516fSCezary Rojewski #define AVS_IPC_TIMEOUT_MS 300 16*335c4cbdSCezary Rojewski #define AVS_D0IX_DELAY_MS 300 17*335c4cbdSCezary Rojewski 18*335c4cbdSCezary Rojewski static int 19*335c4cbdSCezary Rojewski avs_dsp_set_d0ix(struct avs_dev *adev, bool enable) 20*335c4cbdSCezary Rojewski { 21*335c4cbdSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 22*335c4cbdSCezary Rojewski int ret; 23*335c4cbdSCezary Rojewski 24*335c4cbdSCezary Rojewski /* Is transition required? */ 25*335c4cbdSCezary Rojewski if (ipc->in_d0ix == enable) 26*335c4cbdSCezary Rojewski return 0; 27*335c4cbdSCezary Rojewski 28*335c4cbdSCezary Rojewski ret = avs_dsp_op(adev, set_d0ix, enable); 29*335c4cbdSCezary Rojewski if (ret) { 30*335c4cbdSCezary Rojewski /* Prevent further d0ix attempts on conscious IPC failure. */ 31*335c4cbdSCezary Rojewski if (ret == -AVS_EIPC) 32*335c4cbdSCezary Rojewski atomic_inc(&ipc->d0ix_disable_depth); 33*335c4cbdSCezary Rojewski 34*335c4cbdSCezary Rojewski ipc->in_d0ix = false; 35*335c4cbdSCezary Rojewski return ret; 36*335c4cbdSCezary Rojewski } 37*335c4cbdSCezary Rojewski 38*335c4cbdSCezary Rojewski ipc->in_d0ix = enable; 39*335c4cbdSCezary Rojewski return 0; 40*335c4cbdSCezary Rojewski } 41*335c4cbdSCezary Rojewski 42*335c4cbdSCezary Rojewski static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx) 43*335c4cbdSCezary Rojewski { 44*335c4cbdSCezary Rojewski if (atomic_read(&adev->ipc->d0ix_disable_depth)) 45*335c4cbdSCezary Rojewski return; 46*335c4cbdSCezary Rojewski 47*335c4cbdSCezary Rojewski mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work, 48*335c4cbdSCezary Rojewski msecs_to_jiffies(AVS_D0IX_DELAY_MS)); 49*335c4cbdSCezary Rojewski } 50*335c4cbdSCezary Rojewski 51*335c4cbdSCezary Rojewski static void avs_dsp_d0ix_work(struct work_struct *work) 52*335c4cbdSCezary Rojewski { 53*335c4cbdSCezary Rojewski struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work); 54*335c4cbdSCezary Rojewski 55*335c4cbdSCezary Rojewski avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true); 56*335c4cbdSCezary Rojewski } 57*335c4cbdSCezary Rojewski 58*335c4cbdSCezary Rojewski static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx) 59*335c4cbdSCezary Rojewski { 60*335c4cbdSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 61*335c4cbdSCezary Rojewski 62*335c4cbdSCezary Rojewski if (!atomic_read(&ipc->d0ix_disable_depth)) { 63*335c4cbdSCezary Rojewski cancel_delayed_work_sync(&ipc->d0ix_work); 64*335c4cbdSCezary Rojewski return avs_dsp_set_d0ix(adev, false); 65*335c4cbdSCezary Rojewski } 66*335c4cbdSCezary Rojewski 67*335c4cbdSCezary Rojewski return 0; 68*335c4cbdSCezary Rojewski } 69*335c4cbdSCezary Rojewski 70*335c4cbdSCezary Rojewski int avs_dsp_disable_d0ix(struct avs_dev *adev) 71*335c4cbdSCezary Rojewski { 72*335c4cbdSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 73*335c4cbdSCezary Rojewski 74*335c4cbdSCezary Rojewski /* Prevent PG only on the first disable. */ 75*335c4cbdSCezary Rojewski if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) { 76*335c4cbdSCezary Rojewski cancel_delayed_work_sync(&ipc->d0ix_work); 77*335c4cbdSCezary Rojewski return avs_dsp_set_d0ix(adev, false); 78*335c4cbdSCezary Rojewski } 79*335c4cbdSCezary Rojewski 80*335c4cbdSCezary Rojewski return 0; 81*335c4cbdSCezary Rojewski } 82*335c4cbdSCezary Rojewski 83*335c4cbdSCezary Rojewski int avs_dsp_enable_d0ix(struct avs_dev *adev) 84*335c4cbdSCezary Rojewski { 85*335c4cbdSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 86*335c4cbdSCezary Rojewski 87*335c4cbdSCezary Rojewski if (atomic_dec_and_test(&ipc->d0ix_disable_depth)) 88*335c4cbdSCezary Rojewski queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work, 89*335c4cbdSCezary Rojewski msecs_to_jiffies(AVS_D0IX_DELAY_MS)); 90*335c4cbdSCezary Rojewski return 0; 91*335c4cbdSCezary Rojewski } 922879516fSCezary Rojewski 932f1f570cSCezary Rojewski static void avs_dsp_recovery(struct avs_dev *adev) 942f1f570cSCezary Rojewski { 952f1f570cSCezary Rojewski struct avs_soc_component *acomp; 962f1f570cSCezary Rojewski unsigned int core_mask; 972f1f570cSCezary Rojewski int ret; 982f1f570cSCezary Rojewski 992f1f570cSCezary Rojewski mutex_lock(&adev->comp_list_mutex); 1002f1f570cSCezary Rojewski /* disconnect all running streams */ 1012f1f570cSCezary Rojewski list_for_each_entry(acomp, &adev->comp_list, node) { 1022f1f570cSCezary Rojewski struct snd_soc_pcm_runtime *rtd; 1032f1f570cSCezary Rojewski struct snd_soc_card *card; 1042f1f570cSCezary Rojewski 1052f1f570cSCezary Rojewski card = acomp->base.card; 1062f1f570cSCezary Rojewski if (!card) 1072f1f570cSCezary Rojewski continue; 1082f1f570cSCezary Rojewski 1092f1f570cSCezary Rojewski for_each_card_rtds(card, rtd) { 1102f1f570cSCezary Rojewski struct snd_pcm *pcm; 1112f1f570cSCezary Rojewski int dir; 1122f1f570cSCezary Rojewski 1132f1f570cSCezary Rojewski pcm = rtd->pcm; 1142f1f570cSCezary Rojewski if (!pcm || rtd->dai_link->no_pcm) 1152f1f570cSCezary Rojewski continue; 1162f1f570cSCezary Rojewski 1172f1f570cSCezary Rojewski for_each_pcm_streams(dir) { 1182f1f570cSCezary Rojewski struct snd_pcm_substream *substream; 1192f1f570cSCezary Rojewski 1202f1f570cSCezary Rojewski substream = pcm->streams[dir].substream; 1212f1f570cSCezary Rojewski if (!substream || !substream->runtime) 1222f1f570cSCezary Rojewski continue; 1232f1f570cSCezary Rojewski 1242f1f570cSCezary Rojewski snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); 1252f1f570cSCezary Rojewski } 1262f1f570cSCezary Rojewski } 1272f1f570cSCezary Rojewski } 1282f1f570cSCezary Rojewski mutex_unlock(&adev->comp_list_mutex); 1292f1f570cSCezary Rojewski 1302f1f570cSCezary Rojewski /* forcibly shutdown all cores */ 1312f1f570cSCezary Rojewski core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); 1322f1f570cSCezary Rojewski avs_dsp_core_disable(adev, core_mask); 1332f1f570cSCezary Rojewski 1342f1f570cSCezary Rojewski /* attempt dsp reboot */ 1352f1f570cSCezary Rojewski ret = avs_dsp_boot_firmware(adev, true); 1362f1f570cSCezary Rojewski if (ret < 0) 1372f1f570cSCezary Rojewski dev_err(adev->dev, "dsp reboot failed: %d\n", ret); 1382f1f570cSCezary Rojewski 1392f1f570cSCezary Rojewski pm_runtime_mark_last_busy(adev->dev); 1402f1f570cSCezary Rojewski pm_runtime_enable(adev->dev); 1412f1f570cSCezary Rojewski pm_request_autosuspend(adev->dev); 1422f1f570cSCezary Rojewski 1432f1f570cSCezary Rojewski atomic_set(&adev->ipc->recovering, 0); 1442f1f570cSCezary Rojewski } 1452f1f570cSCezary Rojewski 1462f1f570cSCezary Rojewski static void avs_dsp_recovery_work(struct work_struct *work) 1472f1f570cSCezary Rojewski { 1482f1f570cSCezary Rojewski struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work); 1492f1f570cSCezary Rojewski 1502f1f570cSCezary Rojewski avs_dsp_recovery(to_avs_dev(ipc->dev)); 1512f1f570cSCezary Rojewski } 1522f1f570cSCezary Rojewski 1532f1f570cSCezary Rojewski static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg) 1542f1f570cSCezary Rojewski { 1552f1f570cSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 1562f1f570cSCezary Rojewski 1572f1f570cSCezary Rojewski /* Account for the double-exception case. */ 1582f1f570cSCezary Rojewski ipc->ready = false; 1592f1f570cSCezary Rojewski 1602f1f570cSCezary Rojewski if (!atomic_add_unless(&ipc->recovering, 1, 1)) { 1612f1f570cSCezary Rojewski dev_err(adev->dev, "dsp recovery is already in progress\n"); 1622f1f570cSCezary Rojewski return; 1632f1f570cSCezary Rojewski } 1642f1f570cSCezary Rojewski 1652f1f570cSCezary Rojewski dev_crit(adev->dev, "communication severed, rebooting dsp..\n"); 1662f1f570cSCezary Rojewski 167*335c4cbdSCezary Rojewski cancel_delayed_work_sync(&ipc->d0ix_work); 168*335c4cbdSCezary Rojewski ipc->in_d0ix = false; 1692f1f570cSCezary Rojewski /* Re-enabled on recovery completion. */ 1702f1f570cSCezary Rojewski pm_runtime_disable(adev->dev); 1712f1f570cSCezary Rojewski 1722f1f570cSCezary Rojewski /* Process received notification. */ 1732f1f570cSCezary Rojewski avs_dsp_op(adev, coredump, msg); 1742f1f570cSCezary Rojewski 1752f1f570cSCezary Rojewski schedule_work(&ipc->recovery_work); 1762f1f570cSCezary Rojewski } 1772f1f570cSCezary Rojewski 1782879516fSCezary Rojewski static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) 1792879516fSCezary Rojewski { 1802879516fSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 1812879516fSCezary Rojewski union avs_reply_msg msg = AVS_MSG(header); 1822879516fSCezary Rojewski 1832879516fSCezary Rojewski ipc->rx.header = header; 1842879516fSCezary Rojewski /* Abort copying payload if request processing was unsuccessful. */ 185f14a1c5aSCezary Rojewski if (!msg.status) { 186f14a1c5aSCezary Rojewski /* update size in case of LARGE_CONFIG_GET */ 187f14a1c5aSCezary Rojewski if (msg.msg_target == AVS_MOD_MSG && 188f14a1c5aSCezary Rojewski msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET) 189f14a1c5aSCezary Rojewski ipc->rx.size = msg.ext.large_config.data_off_size; 190f14a1c5aSCezary Rojewski 1912879516fSCezary Rojewski memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size); 1922879516fSCezary Rojewski } 193f14a1c5aSCezary Rojewski } 1942879516fSCezary Rojewski 1952879516fSCezary Rojewski static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) 1962879516fSCezary Rojewski { 1972879516fSCezary Rojewski struct avs_notify_mod_data mod_data; 1982879516fSCezary Rojewski union avs_notify_msg msg = AVS_MSG(header); 1992879516fSCezary Rojewski size_t data_size = 0; 2002879516fSCezary Rojewski void *data = NULL; 2012879516fSCezary Rojewski 2022879516fSCezary Rojewski /* Ignore spurious notifications until handshake is established. */ 2032879516fSCezary Rojewski if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) { 2042879516fSCezary Rojewski dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n", msg.primary); 2052879516fSCezary Rojewski return; 2062879516fSCezary Rojewski } 2072879516fSCezary Rojewski 2082879516fSCezary Rojewski /* Calculate notification payload size. */ 2092879516fSCezary Rojewski switch (msg.notify_msg_type) { 2102879516fSCezary Rojewski case AVS_NOTIFY_FW_READY: 2112879516fSCezary Rojewski break; 2122879516fSCezary Rojewski 2132879516fSCezary Rojewski case AVS_NOTIFY_PHRASE_DETECTED: 2142879516fSCezary Rojewski data_size = sizeof(struct avs_notify_voice_data); 2152879516fSCezary Rojewski break; 2162879516fSCezary Rojewski 2172879516fSCezary Rojewski case AVS_NOTIFY_RESOURCE_EVENT: 2182879516fSCezary Rojewski data_size = sizeof(struct avs_notify_res_data); 2192879516fSCezary Rojewski break; 2202879516fSCezary Rojewski 2214b86115cSCezary Rojewski case AVS_NOTIFY_LOG_BUFFER_STATUS: 2222f1f570cSCezary Rojewski case AVS_NOTIFY_EXCEPTION_CAUGHT: 2232f1f570cSCezary Rojewski break; 2242f1f570cSCezary Rojewski 2252879516fSCezary Rojewski case AVS_NOTIFY_MODULE_EVENT: 2262879516fSCezary Rojewski /* To know the total payload size, header needs to be read first. */ 2272879516fSCezary Rojewski memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data)); 2282879516fSCezary Rojewski data_size = sizeof(mod_data) + mod_data.data_size; 2292879516fSCezary Rojewski break; 2302879516fSCezary Rojewski 2312879516fSCezary Rojewski default: 2322879516fSCezary Rojewski dev_info(adev->dev, "unknown notification: 0x%08x\n", msg.primary); 2332879516fSCezary Rojewski break; 2342879516fSCezary Rojewski } 2352879516fSCezary Rojewski 2362879516fSCezary Rojewski if (data_size) { 2372879516fSCezary Rojewski data = kmalloc(data_size, GFP_KERNEL); 2382879516fSCezary Rojewski if (!data) 2392879516fSCezary Rojewski return; 2402879516fSCezary Rojewski 2412879516fSCezary Rojewski memcpy_fromio(data, avs_uplink_addr(adev), data_size); 2422879516fSCezary Rojewski } 2432879516fSCezary Rojewski 2442879516fSCezary Rojewski /* Perform notification-specific operations. */ 2452879516fSCezary Rojewski switch (msg.notify_msg_type) { 2462879516fSCezary Rojewski case AVS_NOTIFY_FW_READY: 2472879516fSCezary Rojewski dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary); 2482879516fSCezary Rojewski adev->ipc->ready = true; 2492879516fSCezary Rojewski complete(&adev->fw_ready); 2502879516fSCezary Rojewski break; 2512879516fSCezary Rojewski 2524b86115cSCezary Rojewski case AVS_NOTIFY_LOG_BUFFER_STATUS: 2534b86115cSCezary Rojewski avs_dsp_op(adev, log_buffer_status, &msg); 2544b86115cSCezary Rojewski break; 2554b86115cSCezary Rojewski 2562f1f570cSCezary Rojewski case AVS_NOTIFY_EXCEPTION_CAUGHT: 2572f1f570cSCezary Rojewski avs_dsp_exception_caught(adev, &msg); 2582f1f570cSCezary Rojewski break; 2592f1f570cSCezary Rojewski 2602879516fSCezary Rojewski default: 2612879516fSCezary Rojewski break; 2622879516fSCezary Rojewski } 2632879516fSCezary Rojewski 2642879516fSCezary Rojewski kfree(data); 2652879516fSCezary Rojewski } 2662879516fSCezary Rojewski 2672879516fSCezary Rojewski void avs_dsp_process_response(struct avs_dev *adev, u64 header) 2682879516fSCezary Rojewski { 2692879516fSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 2702879516fSCezary Rojewski 2712879516fSCezary Rojewski /* 2722879516fSCezary Rojewski * Response may either be solicited - a reply for a request that has 2732879516fSCezary Rojewski * been sent beforehand - or unsolicited (notification). 2742879516fSCezary Rojewski */ 2752879516fSCezary Rojewski if (avs_msg_is_reply(header)) { 2762879516fSCezary Rojewski /* Response processing is invoked from IRQ thread. */ 2772879516fSCezary Rojewski spin_lock_irq(&ipc->rx_lock); 2782879516fSCezary Rojewski avs_dsp_receive_rx(adev, header); 2792879516fSCezary Rojewski ipc->rx_completed = true; 2802879516fSCezary Rojewski spin_unlock_irq(&ipc->rx_lock); 2812879516fSCezary Rojewski } else { 2822879516fSCezary Rojewski avs_dsp_process_notification(adev, header); 2832879516fSCezary Rojewski } 2842879516fSCezary Rojewski 2852879516fSCezary Rojewski complete(&ipc->busy_completion); 2862879516fSCezary Rojewski } 2872879516fSCezary Rojewski 2882879516fSCezary Rojewski irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) 2892879516fSCezary Rojewski { 2902879516fSCezary Rojewski struct avs_dev *adev = dev_id; 2912879516fSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 2922879516fSCezary Rojewski u32 adspis, hipc_rsp, hipc_ack; 2932879516fSCezary Rojewski irqreturn_t ret = IRQ_NONE; 2942879516fSCezary Rojewski 2952879516fSCezary Rojewski adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); 2962879516fSCezary Rojewski if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC)) 2972879516fSCezary Rojewski return ret; 2982879516fSCezary Rojewski 2992879516fSCezary Rojewski hipc_ack = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCIE); 3002879516fSCezary Rojewski hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); 3012879516fSCezary Rojewski 3022879516fSCezary Rojewski /* DSP acked host's request */ 3032879516fSCezary Rojewski if (hipc_ack & SKL_ADSP_HIPCIE_DONE) { 3042879516fSCezary Rojewski /* 3052879516fSCezary Rojewski * As an extra precaution, mask done interrupt. Code executed 3062879516fSCezary Rojewski * due to complete() found below does not assume any masking. 3072879516fSCezary Rojewski */ 3082879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, 3092879516fSCezary Rojewski AVS_ADSP_HIPCCTL_DONE, 0); 3102879516fSCezary Rojewski 3112879516fSCezary Rojewski complete(&ipc->done_completion); 3122879516fSCezary Rojewski 3132879516fSCezary Rojewski /* tell DSP it has our attention */ 3142879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE, 3152879516fSCezary Rojewski SKL_ADSP_HIPCIE_DONE, 3162879516fSCezary Rojewski SKL_ADSP_HIPCIE_DONE); 3172879516fSCezary Rojewski /* unmask done interrupt */ 3182879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, 3192879516fSCezary Rojewski AVS_ADSP_HIPCCTL_DONE, 3202879516fSCezary Rojewski AVS_ADSP_HIPCCTL_DONE); 3212879516fSCezary Rojewski ret = IRQ_HANDLED; 3222879516fSCezary Rojewski } 3232879516fSCezary Rojewski 3242879516fSCezary Rojewski /* DSP sent new response to process */ 3252879516fSCezary Rojewski if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) { 3262879516fSCezary Rojewski /* mask busy interrupt */ 3272879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, 3282879516fSCezary Rojewski AVS_ADSP_HIPCCTL_BUSY, 0); 3292879516fSCezary Rojewski 3302879516fSCezary Rojewski ret = IRQ_WAKE_THREAD; 3312879516fSCezary Rojewski } 3322879516fSCezary Rojewski 3332879516fSCezary Rojewski return ret; 3342879516fSCezary Rojewski } 3352879516fSCezary Rojewski 3362879516fSCezary Rojewski irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) 3372879516fSCezary Rojewski { 3382879516fSCezary Rojewski struct avs_dev *adev = dev_id; 3392879516fSCezary Rojewski union avs_reply_msg msg; 3402879516fSCezary Rojewski u32 hipct, hipcte; 3412879516fSCezary Rojewski 3422879516fSCezary Rojewski hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); 3432879516fSCezary Rojewski hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); 3442879516fSCezary Rojewski 3452879516fSCezary Rojewski /* ensure DSP sent new response to process */ 3462879516fSCezary Rojewski if (!(hipct & SKL_ADSP_HIPCT_BUSY)) 3472879516fSCezary Rojewski return IRQ_NONE; 3482879516fSCezary Rojewski 3492879516fSCezary Rojewski msg.primary = hipct; 3502879516fSCezary Rojewski msg.ext.val = hipcte; 3512879516fSCezary Rojewski avs_dsp_process_response(adev, msg.val); 3522879516fSCezary Rojewski 3532879516fSCezary Rojewski /* tell DSP we accepted its message */ 3542879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, 3552879516fSCezary Rojewski SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); 3562879516fSCezary Rojewski /* unmask busy interrupt */ 3572879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, 3582879516fSCezary Rojewski AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); 3592879516fSCezary Rojewski 3602879516fSCezary Rojewski return IRQ_HANDLED; 3612879516fSCezary Rojewski } 3622879516fSCezary Rojewski 3632879516fSCezary Rojewski static bool avs_ipc_is_busy(struct avs_ipc *ipc) 3642879516fSCezary Rojewski { 3652879516fSCezary Rojewski struct avs_dev *adev = to_avs_dev(ipc->dev); 3662879516fSCezary Rojewski u32 hipc_rsp; 3672879516fSCezary Rojewski 3682879516fSCezary Rojewski hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); 3692879516fSCezary Rojewski return hipc_rsp & SKL_ADSP_HIPCT_BUSY; 3702879516fSCezary Rojewski } 3712879516fSCezary Rojewski 3722879516fSCezary Rojewski static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout) 3732879516fSCezary Rojewski { 3742879516fSCezary Rojewski u32 repeats_left = 128; /* to avoid infinite looping */ 3752879516fSCezary Rojewski int ret; 3762879516fSCezary Rojewski 3772879516fSCezary Rojewski again: 3782879516fSCezary Rojewski ret = wait_for_completion_timeout(&ipc->busy_completion, msecs_to_jiffies(timeout)); 3792879516fSCezary Rojewski 3802879516fSCezary Rojewski /* DSP could be unresponsive at this point. */ 3812879516fSCezary Rojewski if (!ipc->ready) 3822879516fSCezary Rojewski return -EPERM; 3832879516fSCezary Rojewski 3842879516fSCezary Rojewski if (!ret) { 3852879516fSCezary Rojewski if (!avs_ipc_is_busy(ipc)) 3862879516fSCezary Rojewski return -ETIMEDOUT; 3872879516fSCezary Rojewski /* 3882879516fSCezary Rojewski * Firmware did its job, either notification or reply 3892879516fSCezary Rojewski * has been received - now wait until it's processed. 3902879516fSCezary Rojewski */ 3912879516fSCezary Rojewski wait_for_completion_killable(&ipc->busy_completion); 3922879516fSCezary Rojewski } 3932879516fSCezary Rojewski 3942879516fSCezary Rojewski /* Ongoing notification's bottom-half may cause early wakeup */ 3952879516fSCezary Rojewski spin_lock(&ipc->rx_lock); 3962879516fSCezary Rojewski if (!ipc->rx_completed) { 3972879516fSCezary Rojewski if (repeats_left) { 3982879516fSCezary Rojewski /* Reply delayed due to notification. */ 3992879516fSCezary Rojewski repeats_left--; 4002879516fSCezary Rojewski reinit_completion(&ipc->busy_completion); 4012879516fSCezary Rojewski spin_unlock(&ipc->rx_lock); 4022879516fSCezary Rojewski goto again; 4032879516fSCezary Rojewski } 4042879516fSCezary Rojewski 4052879516fSCezary Rojewski spin_unlock(&ipc->rx_lock); 4062879516fSCezary Rojewski return -ETIMEDOUT; 4072879516fSCezary Rojewski } 4082879516fSCezary Rojewski 4092879516fSCezary Rojewski spin_unlock(&ipc->rx_lock); 4102879516fSCezary Rojewski return 0; 4112879516fSCezary Rojewski } 4122879516fSCezary Rojewski 4132879516fSCezary Rojewski static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) 4142879516fSCezary Rojewski { 4152879516fSCezary Rojewski lockdep_assert_held(&ipc->rx_lock); 4162879516fSCezary Rojewski 4172879516fSCezary Rojewski ipc->rx.header = 0; 4182879516fSCezary Rojewski ipc->rx.size = reply ? reply->size : 0; 4192879516fSCezary Rojewski ipc->rx_completed = false; 4202879516fSCezary Rojewski 4212879516fSCezary Rojewski reinit_completion(&ipc->done_completion); 4222879516fSCezary Rojewski reinit_completion(&ipc->busy_completion); 4232879516fSCezary Rojewski } 4242879516fSCezary Rojewski 4252879516fSCezary Rojewski static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx) 4262879516fSCezary Rojewski { 4272879516fSCezary Rojewski tx->header |= SKL_ADSP_HIPCI_BUSY; 4282879516fSCezary Rojewski 4292879516fSCezary Rojewski if (tx->size) 4302879516fSCezary Rojewski memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); 4312879516fSCezary Rojewski snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32); 4322879516fSCezary Rojewski snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX); 4332879516fSCezary Rojewski } 4342879516fSCezary Rojewski 4352879516fSCezary Rojewski static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, 4362879516fSCezary Rojewski struct avs_ipc_msg *reply, int timeout) 4372879516fSCezary Rojewski { 4382879516fSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 4392879516fSCezary Rojewski int ret; 4402879516fSCezary Rojewski 4412879516fSCezary Rojewski if (!ipc->ready) 4422879516fSCezary Rojewski return -EPERM; 4432879516fSCezary Rojewski 4442879516fSCezary Rojewski mutex_lock(&ipc->msg_mutex); 4452879516fSCezary Rojewski 4462879516fSCezary Rojewski spin_lock(&ipc->rx_lock); 4472879516fSCezary Rojewski avs_ipc_msg_init(ipc, reply); 4482879516fSCezary Rojewski avs_dsp_send_tx(adev, request); 4492879516fSCezary Rojewski spin_unlock(&ipc->rx_lock); 4502879516fSCezary Rojewski 4512879516fSCezary Rojewski ret = avs_ipc_wait_busy_completion(ipc, timeout); 4522879516fSCezary Rojewski if (ret) { 4532879516fSCezary Rojewski if (ret == -ETIMEDOUT) { 4542f1f570cSCezary Rojewski union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT); 4552879516fSCezary Rojewski 4562f1f570cSCezary Rojewski /* Same treatment as on exception, just stack_dump=0. */ 4572f1f570cSCezary Rojewski avs_dsp_exception_caught(adev, &msg); 4582879516fSCezary Rojewski } 4592879516fSCezary Rojewski goto exit; 4602879516fSCezary Rojewski } 4612879516fSCezary Rojewski 4622879516fSCezary Rojewski ret = ipc->rx.rsp.status; 4632879516fSCezary Rojewski if (reply) { 4642879516fSCezary Rojewski reply->header = ipc->rx.header; 4652879516fSCezary Rojewski if (reply->data && ipc->rx.size) 4662879516fSCezary Rojewski memcpy(reply->data, ipc->rx.data, reply->size); 4672879516fSCezary Rojewski } 4682879516fSCezary Rojewski 4692879516fSCezary Rojewski exit: 4702879516fSCezary Rojewski mutex_unlock(&ipc->msg_mutex); 4712879516fSCezary Rojewski return ret; 4722879516fSCezary Rojewski } 4732879516fSCezary Rojewski 474*335c4cbdSCezary Rojewski static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request, 475*335c4cbdSCezary Rojewski struct avs_ipc_msg *reply, int timeout, bool wake_d0i0, 476*335c4cbdSCezary Rojewski bool schedule_d0ix) 477*335c4cbdSCezary Rojewski { 478*335c4cbdSCezary Rojewski int ret; 479*335c4cbdSCezary Rojewski 480*335c4cbdSCezary Rojewski if (wake_d0i0) { 481*335c4cbdSCezary Rojewski ret = avs_dsp_wake_d0i0(adev, request); 482*335c4cbdSCezary Rojewski if (ret) 483*335c4cbdSCezary Rojewski return ret; 484*335c4cbdSCezary Rojewski } 485*335c4cbdSCezary Rojewski 486*335c4cbdSCezary Rojewski ret = avs_dsp_do_send_msg(adev, request, reply, timeout); 487*335c4cbdSCezary Rojewski if (ret) 488*335c4cbdSCezary Rojewski return ret; 489*335c4cbdSCezary Rojewski 490*335c4cbdSCezary Rojewski if (schedule_d0ix) 491*335c4cbdSCezary Rojewski avs_dsp_schedule_d0ix(adev, request); 492*335c4cbdSCezary Rojewski 493*335c4cbdSCezary Rojewski return 0; 494*335c4cbdSCezary Rojewski } 495*335c4cbdSCezary Rojewski 4962879516fSCezary Rojewski int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, 4972879516fSCezary Rojewski struct avs_ipc_msg *reply, int timeout) 4982879516fSCezary Rojewski { 499*335c4cbdSCezary Rojewski bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true); 500*335c4cbdSCezary Rojewski bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false); 501*335c4cbdSCezary Rojewski 502*335c4cbdSCezary Rojewski return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix); 5032879516fSCezary Rojewski } 5042879516fSCezary Rojewski 5052879516fSCezary Rojewski int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, 5062879516fSCezary Rojewski struct avs_ipc_msg *reply) 5072879516fSCezary Rojewski { 5082879516fSCezary Rojewski return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms); 5092879516fSCezary Rojewski } 5102879516fSCezary Rojewski 511*335c4cbdSCezary Rojewski int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, 512*335c4cbdSCezary Rojewski struct avs_ipc_msg *reply, int timeout, bool wake_d0i0) 513*335c4cbdSCezary Rojewski { 514*335c4cbdSCezary Rojewski return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false); 515*335c4cbdSCezary Rojewski } 516*335c4cbdSCezary Rojewski 517*335c4cbdSCezary Rojewski int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request, 518*335c4cbdSCezary Rojewski struct avs_ipc_msg *reply, bool wake_d0i0) 519*335c4cbdSCezary Rojewski { 520*335c4cbdSCezary Rojewski return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms, 521*335c4cbdSCezary Rojewski wake_d0i0); 522*335c4cbdSCezary Rojewski } 523*335c4cbdSCezary Rojewski 5242879516fSCezary Rojewski static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout) 5252879516fSCezary Rojewski { 5262879516fSCezary Rojewski struct avs_ipc *ipc = adev->ipc; 5272879516fSCezary Rojewski int ret; 5282879516fSCezary Rojewski 5292879516fSCezary Rojewski mutex_lock(&ipc->msg_mutex); 5302879516fSCezary Rojewski 5312879516fSCezary Rojewski spin_lock(&ipc->rx_lock); 5322879516fSCezary Rojewski avs_ipc_msg_init(ipc, NULL); 5332879516fSCezary Rojewski avs_dsp_send_tx(adev, request); 5342879516fSCezary Rojewski spin_unlock(&ipc->rx_lock); 5352879516fSCezary Rojewski 5362879516fSCezary Rojewski /* ROM messages must be sent before main core is unstalled */ 5372879516fSCezary Rojewski ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false); 5382879516fSCezary Rojewski if (!ret) { 5392879516fSCezary Rojewski ret = wait_for_completion_timeout(&ipc->done_completion, msecs_to_jiffies(timeout)); 5402879516fSCezary Rojewski ret = ret ? 0 : -ETIMEDOUT; 5412879516fSCezary Rojewski } 5422879516fSCezary Rojewski 5432879516fSCezary Rojewski mutex_unlock(&ipc->msg_mutex); 5442879516fSCezary Rojewski 5452879516fSCezary Rojewski return ret; 5462879516fSCezary Rojewski } 5472879516fSCezary Rojewski 5482879516fSCezary Rojewski int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout) 5492879516fSCezary Rojewski { 5502879516fSCezary Rojewski return avs_dsp_do_send_rom_msg(adev, request, timeout); 5512879516fSCezary Rojewski } 5522879516fSCezary Rojewski 5532879516fSCezary Rojewski int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request) 5542879516fSCezary Rojewski { 5552879516fSCezary Rojewski return avs_dsp_send_rom_msg_timeout(adev, request, adev->ipc->default_timeout_ms); 5562879516fSCezary Rojewski } 5572879516fSCezary Rojewski 5582879516fSCezary Rojewski void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) 5592879516fSCezary Rojewski { 5602879516fSCezary Rojewski u32 value, mask; 5612879516fSCezary Rojewski 5622879516fSCezary Rojewski /* 5632879516fSCezary Rojewski * No particular bit setting order. All of these are required 5642879516fSCezary Rojewski * to have a functional SW <-> FW communication. 5652879516fSCezary Rojewski */ 5662879516fSCezary Rojewski value = enable ? AVS_ADSP_ADSPIC_IPC : 0; 5672879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_IPC, value); 5682879516fSCezary Rojewski 5692879516fSCezary Rojewski mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY; 5702879516fSCezary Rojewski value = enable ? mask : 0; 5712879516fSCezary Rojewski snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value); 5722879516fSCezary Rojewski } 5732879516fSCezary Rojewski 5742879516fSCezary Rojewski int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) 5752879516fSCezary Rojewski { 5762879516fSCezary Rojewski ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); 5772879516fSCezary Rojewski if (!ipc->rx.data) 5782879516fSCezary Rojewski return -ENOMEM; 5792879516fSCezary Rojewski 5802879516fSCezary Rojewski ipc->dev = dev; 5812879516fSCezary Rojewski ipc->ready = false; 5822879516fSCezary Rojewski ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS; 5832f1f570cSCezary Rojewski INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work); 584*335c4cbdSCezary Rojewski INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work); 5852879516fSCezary Rojewski init_completion(&ipc->done_completion); 5862879516fSCezary Rojewski init_completion(&ipc->busy_completion); 5872879516fSCezary Rojewski spin_lock_init(&ipc->rx_lock); 5882879516fSCezary Rojewski mutex_init(&ipc->msg_mutex); 5892879516fSCezary Rojewski 5902879516fSCezary Rojewski return 0; 5912879516fSCezary Rojewski } 5922879516fSCezary Rojewski 5932879516fSCezary Rojewski void avs_ipc_block(struct avs_ipc *ipc) 5942879516fSCezary Rojewski { 5952879516fSCezary Rojewski ipc->ready = false; 5962f1f570cSCezary Rojewski cancel_work_sync(&ipc->recovery_work); 597*335c4cbdSCezary Rojewski cancel_delayed_work_sync(&ipc->d0ix_work); 598*335c4cbdSCezary Rojewski ipc->in_d0ix = false; 5992879516fSCezary Rojewski } 600