// SPDX-License-Identifier: (GPL-2.0-only 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 <liam.r.girdwood@linux.intel.com>
//
/*
* Hardware interface for audio DSP on Baytrail, Braswell and Cherrytrail.
*/
#include <linux/module.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
#include "../ops.h"
#include "shim.h"
#include "../sof-audio.h"
#include "../../intel/common/soc-intel-quirks.h"
/* DSP memories */
#define IRAM_OFFSET 0x0C0000
#define IRAM_SIZE (80 * 1024)
#define DRAM_OFFSET 0x100000
#define DRAM_SIZE (160 * 1024)
#define SHIM_OFFSET 0x140000
#define SHIM_SIZE_BYT 0x100
#define SHIM_SIZE_CHT 0x118
#define MBOX_OFFSET 0x144000
#define MBOX_SIZE 0x1000
#define EXCEPT_OFFSET 0x800
#define EXCEPT_MAX_HDR_SIZE 0x400
/* DSP peripherals */
#define DMAC0_OFFSET 0x098000
#define DMAC1_OFFSET 0x09c000
#define DMAC2_OFFSET 0x094000
#define DMAC_SIZE 0x420
#define SSP0_OFFSET 0x0a0000
#define SSP1_OFFSET 0x0a1000
#define SSP2_OFFSET 0x0a2000
#define SSP3_OFFSET 0x0a4000
#define SSP4_OFFSET 0x0a5000
#define SSP5_OFFSET 0x0a6000
#define SSP_SIZE 0x100
#define BYT_STACK_DUMP_SIZE 32
#define BYT_PCI_BAR_SIZE 0x200000
#define BYT_PANIC_OFFSET(x) (((x) & GENMASK_ULL(47, 32)) >> 32)
/*
* Debug
*/
#define MBOX_DUMP_SIZE 0x30
/* BARs */
#define BYT_DSP_BAR 0
#define BYT_PCI_BAR 1
#define BYT_IMR_BAR 2
static const struct snd_sof_debugfs_map byt_debugfs[] = {
{"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
{"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
{"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
{"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
{"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
{"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
{"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
{"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
SOF_DEBUGFS_ACCESS_ALWAYS},
};
static void byt_host_done(struct snd_sof_dev *sdev);
static void byt_dsp_done(struct snd_sof_dev *sdev);
static void byt_get_reply(struct snd_sof_dev *sdev);
/*
* Debug
*/
static void byt_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_dsp_oops_xtensa *xoops,
struct sof_ipc_panic_info *panic_info,
u32 *stack, size_t stack_words)
{
u32 offset = sdev->dsp_oops_offset;
/* first read regsisters */
sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
/* note: variable AR register array is not read */
/* then get panic info */
if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
xoops->arch_hdr.totalsize);
return;
}
offset += xoops->arch_hdr.totalsize;
sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
/* then get the stack */
offset += sizeof(*panic_info);
sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
}
static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
{
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[BYT_STACK_DUMP_SIZE];
u64 status, panic, imrd, imrx;
/* now try generic SOF status messages */
status = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
panic = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
byt_get_registers(sdev, &xoops, &panic_info, stack,
BYT_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
BYT_STACK_DUMP_SIZE);
/* provide some context for firmware debug */
imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX);
imrd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRD);
dev_err(sdev->dev,
"error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
(panic & SHIM_IPCX_BUSY) ? "yes" : "no",
(panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
dev_err(sdev->dev,
"error: mask host: pending %s complete %s raw 0x%llx\n",
(imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
(imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
dev_err(sdev->dev,
"error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
(status & SHIM_IPCD_BUSY) ? "yes" : "no",
(status & SHIM_IPCD_DONE) ? "yes" : "no", status);
dev_err(sdev->dev,
"error: mask DSP: pending %s complete %s raw 0x%llx\n",
(imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
(imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
}