// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) // // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // // Copyright(c) 2018 Intel Corporation. All rights reserved. // // Author: Liam Girdwood // #include #include "ops.h" static bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, u32 mask, u32 value) { struct pci_dev *pci = to_pci_dev(sdev->dev); unsigned int old, new; u32 ret = 0; pci_read_config_dword(pci, offset, &ret); old = ret; dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", old & mask, offset); new = (old & ~mask) | (value & mask); if (old == new) return false; pci_write_config_dword(pci, offset, new); dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, offset); return true; } bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, u32 mask, u32 value) { unsigned long flags; bool change; spin_lock_irqsave(&sdev->hw_lock, flags); change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); spin_unlock_irqrestore(&sdev->hw_lock, flags); return change; } EXPORT_SYMBOL(snd_sof_pci_update_bits); bool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { unsigned int old, new; u32 ret; ret = snd_sof_dsp_read(sdev, bar, offset); old = ret; new = (old & ~mask) | (value & mask); if (old == new) return false; snd_sof_dsp_write(sdev, bar, offset, new); return true; } EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u64 mask, u64 value) { u64 old, new; old = snd_sof_dsp_read64(sdev, bar, offset); new = (old & ~mask) | (value & mask); if (old == new) return false; snd_sof_dsp_write64(sdev, bar, offset, new); return true; } EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); /* This is for registers bits with attribute RWC */ bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { unsigned long flags; bool change; spin_lock_irqsave(&sdev->hw_lock, flags); change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, value); spin_unlock_irqrestore(&sdev->hw_lock, flags); return change; } EXPORT_SYMBOL(snd_sof_dsp_update_bits); bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, u64 mask, u64 value) { unsigned long flags; bool change; spin_lock_irqsave(&sdev->hw_lock, flags); change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, value); spin_unlock_irqrestore(&sdev->hw_lock, flags); return change; } EXPORT_SYMBOL(snd_sof_dsp_update_bits64); static void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { unsigned int old, new; u32 ret; ret = snd_sof_dsp_read(sdev, bar, offset); old = ret; new = (old & ~mask) | (value & mask); snd_sof_dsp_write(sdev, bar, offset, new); } /* This is for registers bits with attribute RWC */ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { unsigned long flags; spin_lock_irqsave(&sdev->hw_lock, flags); snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); spin_unlock_irqrestore(&sdev->hw_lock, flags); } EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) { dev_err(sdev->dev, "error : DSP panic!\n"); /* * check if DSP is not ready and did not set the dsp_oops_offset. * if the dsp_oops_offset is not set, set it from the panic message. * Also add a check to memory window setting with panic message. */ if (!sdev->dsp_oops_offset) sdev->dsp_oops_offset = offset; else dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", sdev->dsp_oops_offset, offset); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_dsp_panic);