162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
962306a36Sopenharmony_ci//
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include "ops.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic
1562306a36Sopenharmony_cibool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset,
1662306a36Sopenharmony_ci				      u32 mask, u32 value)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(sdev->dev);
1962306a36Sopenharmony_ci	unsigned int old, new;
2062306a36Sopenharmony_ci	u32 ret = 0;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	pci_read_config_dword(pci, offset, &ret);
2362306a36Sopenharmony_ci	old = ret;
2462306a36Sopenharmony_ci	dev_dbg(sdev->dev, "Debug PCIR: %8.8x at  %8.8x\n", old & mask, offset);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (old == new)
2962306a36Sopenharmony_ci		return false;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	pci_write_config_dword(pci, offset, new);
3262306a36Sopenharmony_ci	dev_dbg(sdev->dev, "Debug PCIW: %8.8x at  %8.8x\n", value,
3362306a36Sopenharmony_ci		offset);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return true;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cibool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset,
3962306a36Sopenharmony_ci			     u32 mask, u32 value)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned long flags;
4262306a36Sopenharmony_ci	bool change;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->hw_lock, flags);
4562306a36Sopenharmony_ci	change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value);
4662306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->hw_lock, flags);
4762306a36Sopenharmony_ci	return change;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_pci_update_bits);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cibool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar,
5262306a36Sopenharmony_ci				      u32 offset, u32 mask, u32 value)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	unsigned int old, new;
5562306a36Sopenharmony_ci	u32 ret;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	ret = snd_sof_dsp_read(sdev, bar, offset);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	old = ret;
6062306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (old == new)
6362306a36Sopenharmony_ci		return false;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, bar, offset, new);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return true;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cibool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar,
7262306a36Sopenharmony_ci					u32 offset, u64 mask, u64 value)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	u64 old, new;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	old = snd_sof_dsp_read64(sdev, bar, offset);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (old == new)
8162306a36Sopenharmony_ci		return false;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	snd_sof_dsp_write64(sdev, bar, offset, new);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return true;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* This is for registers bits with attribute RWC */
9062306a36Sopenharmony_cibool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset,
9162306a36Sopenharmony_ci			     u32 mask, u32 value)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	unsigned long flags;
9462306a36Sopenharmony_ci	bool change;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->hw_lock, flags);
9762306a36Sopenharmony_ci	change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask,
9862306a36Sopenharmony_ci						  value);
9962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->hw_lock, flags);
10062306a36Sopenharmony_ci	return change;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_update_bits);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cibool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset,
10562306a36Sopenharmony_ci			       u64 mask, u64 value)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	unsigned long flags;
10862306a36Sopenharmony_ci	bool change;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->hw_lock, flags);
11162306a36Sopenharmony_ci	change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask,
11262306a36Sopenharmony_ci						    value);
11362306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->hw_lock, flags);
11462306a36Sopenharmony_ci	return change;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_update_bits64);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic
11962306a36Sopenharmony_civoid snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar,
12062306a36Sopenharmony_ci					     u32 offset, u32 mask, u32 value)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned int old, new;
12362306a36Sopenharmony_ci	u32 ret;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ret = snd_sof_dsp_read(sdev, bar, offset);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	old = ret;
12862306a36Sopenharmony_ci	new = (old & ~mask) | (value & mask);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, bar, offset, new);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* This is for registers bits with attribute RWC */
13462306a36Sopenharmony_civoid snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar,
13562306a36Sopenharmony_ci				    u32 offset, u32 mask, u32 value)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned long flags;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	spin_lock_irqsave(&sdev->hw_lock, flags);
14062306a36Sopenharmony_ci	snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value);
14162306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdev->hw_lock, flags);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_update_bits_forced);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * snd_sof_dsp_panic - handle a received DSP panic message
14762306a36Sopenharmony_ci * @sdev: Pointer to the device's sdev
14862306a36Sopenharmony_ci * @offset: offset of panic information
14962306a36Sopenharmony_ci * @non_recoverable: the panic is fatal, no recovery will be done by the caller
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_civoid snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * if DSP is not ready and the dsp_oops_offset is not yet set, use the
15562306a36Sopenharmony_ci	 * offset from the panic message.
15662306a36Sopenharmony_ci	 */
15762306a36Sopenharmony_ci	if (!sdev->dsp_oops_offset)
15862306a36Sopenharmony_ci		sdev->dsp_oops_offset = offset;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Print warning if the offset from the panic message differs from
16262306a36Sopenharmony_ci	 * dsp_oops_offset
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	if (sdev->dsp_oops_offset != offset)
16562306a36Sopenharmony_ci		dev_warn(sdev->dev,
16662306a36Sopenharmony_ci			 "%s: dsp_oops_offset %zu differs from panic offset %u\n",
16762306a36Sopenharmony_ci			 __func__, sdev->dsp_oops_offset, offset);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * Set the fw_state to crashed only in case of non recoverable DSP panic
17162306a36Sopenharmony_ci	 * event.
17262306a36Sopenharmony_ci	 * Use different message within the snd_sof_dsp_dbg_dump() depending on
17362306a36Sopenharmony_ci	 * the non_recoverable flag.
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci	sdev->dbg_dump_printed = false;
17662306a36Sopenharmony_ci	if (non_recoverable) {
17762306a36Sopenharmony_ci		snd_sof_dsp_dbg_dump(sdev, "DSP panic!",
17862306a36Sopenharmony_ci				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
17962306a36Sopenharmony_ci		sof_set_fw_state(sdev, SOF_FW_CRASHED);
18062306a36Sopenharmony_ci		sof_fw_trace_fw_crashed(sdev);
18162306a36Sopenharmony_ci	} else {
18262306a36Sopenharmony_ci		snd_sof_dsp_dbg_dump(sdev,
18362306a36Sopenharmony_ci				     "DSP panic (recovery will be attempted)",
18462306a36Sopenharmony_ci				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_panic);
188