1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2018 Intel Corporation 7 // 8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 // 10 11 #include <linux/pci.h> 12 #include "ops.h" 13 14 static 15 bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, 16 u32 mask, u32 value) 17 { 18 struct pci_dev *pci = to_pci_dev(sdev->dev); 19 unsigned int old, new; 20 u32 ret = 0; 21 22 pci_read_config_dword(pci, offset, &ret); 23 old = ret; 24 dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", old & mask, offset); 25 26 new = (old & ~mask) | (value & mask); 27 28 if (old == new) 29 return false; 30 31 pci_write_config_dword(pci, offset, new); 32 dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, 33 offset); 34 35 return true; 36 } 37 38 bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, 39 u32 mask, u32 value) 40 { 41 guard(spinlock_irqsave)(&sdev->hw_lock); 42 return snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); 43 } 44 EXPORT_SYMBOL(snd_sof_pci_update_bits); 45 46 bool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, 47 u32 offset, u32 mask, u32 value) 48 { 49 unsigned int old, new; 50 u32 ret; 51 52 ret = snd_sof_dsp_read(sdev, bar, offset); 53 54 old = ret; 55 new = (old & ~mask) | (value & mask); 56 57 if (old == new) 58 return false; 59 60 snd_sof_dsp_write(sdev, bar, offset, new); 61 62 return true; 63 } 64 EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); 65 66 bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, 67 u32 offset, u64 mask, u64 value) 68 { 69 u64 old, new; 70 71 old = snd_sof_dsp_read64(sdev, bar, offset); 72 73 new = (old & ~mask) | (value & mask); 74 75 if (old == new) 76 return false; 77 78 snd_sof_dsp_write64(sdev, bar, offset, new); 79 80 return true; 81 } 82 EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); 83 84 /* This is for registers bits with attribute RWC */ 85 bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, 86 u32 mask, u32 value) 87 { 88 guard(spinlock_irqsave)(&sdev->hw_lock); 89 return snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, value); 90 } 91 EXPORT_SYMBOL(snd_sof_dsp_update_bits); 92 93 bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, 94 u64 mask, u64 value) 95 { 96 guard(spinlock_irqsave)(&sdev->hw_lock); 97 return snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, value); 98 } 99 EXPORT_SYMBOL(snd_sof_dsp_update_bits64); 100 101 static 102 void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, 103 u32 offset, u32 mask, u32 value) 104 { 105 unsigned int old, new; 106 u32 ret; 107 108 ret = snd_sof_dsp_read(sdev, bar, offset); 109 110 old = ret; 111 new = (old & ~mask) | (value & mask); 112 113 snd_sof_dsp_write(sdev, bar, offset, new); 114 } 115 116 /* This is for registers bits with attribute RWC */ 117 void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, 118 u32 offset, u32 mask, u32 value) 119 { 120 guard(spinlock_irqsave)(&sdev->hw_lock); 121 snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); 122 } 123 EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); 124 125 /** 126 * snd_sof_dsp_panic - handle a received DSP panic message 127 * @sdev: Pointer to the device's sdev 128 * @offset: offset of panic information 129 * @non_recoverable: the panic is fatal, no recovery will be done by the caller 130 */ 131 void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable) 132 { 133 /* 134 * if DSP is not ready and the dsp_oops_offset is not yet set, use the 135 * offset from the panic message. 136 */ 137 if (!sdev->dsp_oops_offset) 138 sdev->dsp_oops_offset = offset; 139 140 /* 141 * Print warning if the offset from the panic message differs from 142 * dsp_oops_offset 143 */ 144 if (sdev->dsp_oops_offset != offset) 145 dev_warn(sdev->dev, 146 "%s: dsp_oops_offset %zu differs from panic offset %u\n", 147 __func__, sdev->dsp_oops_offset, offset); 148 149 /* 150 * Set the fw_state to crashed only in case of non recoverable DSP panic 151 * event. 152 * Use different message within the snd_sof_dsp_dbg_dump() depending on 153 * the non_recoverable flag. 154 */ 155 sdev->dbg_dump_printed = false; 156 if (non_recoverable) { 157 snd_sof_dsp_dbg_dump(sdev, "DSP panic!", 158 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 159 sof_set_fw_state(sdev, SOF_FW_CRASHED); 160 sof_fw_trace_fw_crashed(sdev); 161 } else { 162 snd_sof_dsp_dbg_dump(sdev, 163 "DSP panic (recovery will be attempted)", 164 SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 165 } 166 } 167 EXPORT_SYMBOL(snd_sof_dsp_panic); 168