drivers/bus/soundwire: Add the Soundwire bus driver
This patch adds the Soundwire bus driver which,
* Resets the HDA and DSP.
* Initializes the Soundwire link which involves the link
power-on and soudnwire master configuration.
* Enumerate the codecs, get identification information of
connected codec and reads the codec status.
* Sends Tx message through the Sndw interface and recieves
the corresponding ACK message.
BUG=b:151514169
BRANCH=none
TEST=Build and test boot beep on volteer.
Signed-off-by: V Sowmya <v.sowmya@intel.com>
Change-Id: I0d62333f5f103bb5bc677bb05f6d91583f22f6ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/depthcharge/+/2305490
Reviewed-by: Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Commit-Queue: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/drivers/bus/Kconfig b/src/drivers/bus/Kconfig
index 8d73765..66982a6 100644
--- a/src/drivers/bus/Kconfig
+++ b/src/drivers/bus/Kconfig
@@ -14,3 +14,4 @@
source src/drivers/bus/i2s/Kconfig
source src/drivers/bus/spi/Kconfig
source src/drivers/bus/usb/Kconfig
+source src/drivers/bus/soundwire/Kconfig
diff --git a/src/drivers/bus/Makefile.inc b/src/drivers/bus/Makefile.inc
index 1c79fdb..762506f 100644
--- a/src/drivers/bus/Makefile.inc
+++ b/src/drivers/bus/Makefile.inc
@@ -11,4 +11,4 @@
## GNU General Public License for more details.
##
-subdirs-y += i2c i2s spi usb
+subdirs-y += i2c i2s spi usb soundwire
diff --git a/src/drivers/bus/soundwire/Kconfig b/src/drivers/bus/soundwire/Kconfig
new file mode 100644
index 0000000..51cfcdb
--- /dev/null
+++ b/src/drivers/bus/soundwire/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, Intel Corporation.
+# Copyright 2020 Google LLC.
+
+config DRIVER_BUS_SOUNDWIRE
+ bool "Soundwire driver for Intel platforms"
+ default n
diff --git a/src/drivers/bus/soundwire/Makefile.inc b/src/drivers/bus/soundwire/Makefile.inc
new file mode 100644
index 0000000..7bca037
--- /dev/null
+++ b/src/drivers/bus/soundwire/Makefile.inc
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, Intel Corporation.
+# Copyright 2020 Google LLC.
+
+depthcharge-$(CONFIG_DRIVER_BUS_SOUNDWIRE) += soundwire.c
diff --git a/src/drivers/bus/soundwire/cavs_2_5-sndwregs.h b/src/drivers/bus/soundwire/cavs_2_5-sndwregs.h
new file mode 100644
index 0000000..0f8367a
--- /dev/null
+++ b/src/drivers/bus/soundwire/cavs_2_5-sndwregs.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Intel Corporation.
+ * Copyright 2020 Google LLC.
+ */
+
+
+#ifndef __DRIVERS_BUS_SOUNDWIRE_CAVS_2_5_SNDWREGS_H__
+#define __DRIVERS_BUS_SOUNDWIRE_CAVS_2_5_SNDWREGS_H__
+
+/* PCI device and function number for HDA */
+#define AUDIO_DEV 0x1f
+#define AUDIO_FUNC 3
+
+/* Number of DSP cores */
+#define AUDIO_DSP_CORES 4
+
+/* Soundwire link numbers */
+#define AUDIO_SNDW_LINK0 0
+#define AUDIO_SNDW_LINK1 1
+#define AUDIO_SNDW_LINK2 2
+#define AUDIO_SNDW_LINK3 3
+
+/* HDA memory space registers */
+#define HDA_MEM_GCTL 0x8
+#define HDA_MEM_GCTL_CRST 0x1
+#define HDA_MEM_PPCTL 0x804
+#define HDA_MEM_PPCTL_DSPEN 0x40000000
+
+/* DSP memory space registers */
+#define DSP_MEM_ADSPCS 0x04
+#define DSP_MEM_SNDW_OFFSETS 0x10000
+#define DSP_MEM_ADSPCS_SPA 0x00010000
+#define DSP_MEM_ADSPCS_CPA 0x01000000
+#define DSP_MEM_ADSPIC2 0x10
+#define DSP_MEM_ADSPIC2_SNDW 0x20
+
+/* SoundWire Shim Registers */
+#define DSP_MEM_SNDW 0x2C000
+#define DSP_MEM_SNDW_SNDWLCAP (DSP_MEM_SNDW + 0x00)
+#define DSP_MEM_SNDW_SNDWSC 0x07
+#define DSP_MEM_SNDW_SNDWLCTL (DSP_MEM_SNDW + 0x04)
+#define DSP_MEM_SNDW_SNDWLCTL_SPA 0x1
+#define DSP_MEM_SNDW_SNDWLCTL_CPA 0x100
+#define DSP_MEM_SNDW_SNDWIPPTR (DSP_MEM_SNDW + 0x08)
+#define DSP_MEM_SNDW_SNDWIPPTR_PRT 0xFFFFF
+#define DSP_MEM_SNDW_SNDWxIOCTL(x) (DSP_MEM_SNDW + (0x60 * (x)) + 0x6C)
+#define DSP_MEM_SNDW_SNDWxIOCTL_MIF 0x1
+#define DSP_MEM_SNDW_SNDWxACTMCTL(x) (DSP_MEM_SNDW + (0x60 * (x)) + 0x6E)
+#define DSP_MEM_SNDW_SNDWxACTMCTL_DACTQE 0x1
+
+#define SNDW_POLL_TIME_US 4000
+#define SNDW_WAIT_PERIOD 1
+
+#endif
diff --git a/src/drivers/bus/soundwire/mipi-sndwregs.h b/src/drivers/bus/soundwire/mipi-sndwregs.h
new file mode 100644
index 0000000..f7dfcdd
--- /dev/null
+++ b/src/drivers/bus/soundwire/mipi-sndwregs.h
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Intel Corporation.
+ * Copyright 2020 Google LLC.
+ */
+
+#ifndef __DRIVERS_BUS_SOUNDWIRE_MIPI_SNDWREGS_H__
+#define __DRIVERS_BUS_SOUNDWIRE_MIPI_SNDWREGS_H__
+
+/* MCP_Config - MCP configuration registers. This register contains various control bits for
+ reset, clock, and keeper control. */
+#define SNDW_MEM_CONFIG 0x00
+#define SNDW_MEM_CONFIG_MODE_AHB 0x8
+#define SNDW_MEM_CONFIG_OM_NORMAL 0x7
+#define SNDW_MEM_CTRL 0x04
+#define SNDW_MEM_CTRL_BLOCKWAKEUP 0x1
+#define SNDW_MEM_CTRL_CLOCKSTOPCLEAR 0x4
+
+/* MCP_CmdCtrl - MCP command control register is the ping frame configuration register. */
+#define SNDW_MEM_CMDCTRL 0x08
+
+/* MCP_ConfigUpdate - This register is used to apply new configuration to the Master. */
+#define SNDW_MEM_CONFIGUPDATE 0x18
+#define SNDW_MEM_CONFIGUPDATE_UPDATE_DONE 0x1
+
+/* MCP_ClockCtrl - This register contains fields that define clock source and clock divider */
+#define SNDW_MEM_CLK_CTRL0 0x30
+#define SNDW_MEM_CLK_CTRL1 0x38
+#define SNDW_MEM_CLK_MCLKD_MASK 0xFF
+
+/* MCP_Stat - Master Status contains flags from all sources which can generate interrupts. */
+#define SNDW_MEM_STAT 0x40
+#define SNDW_MEM_STAT_TXE 0x2
+#define SNDW_MEM_STAT_TXE_FIFO_EMPTY 0x2
+#define SNDW_MEM_STAT_RXNE 0x8
+#define SNDW_MEM_STAT_RXNE_FIFO_EMPTY 0x8
+
+/* MCP_IntStat - Write one to specific bits of this register will clear interrupt. */
+#define SNDW_MEM_INTSTAT 0x44
+#define SNDW_MEM_INTMASK 0x48
+#define SNDW_MEM_INTMASK_RXNE 0x8
+#define SNDW_MEM_INTMASK_RXNE_FIFO_EMPTY 0x8
+
+/* MCP_EndpointStat - This register contains endpoint device status */
+#define SNDW_MEM_ENDPOINTSTAT 0x50
+#define SNDW_MEM_ENDPOINTSTAT_STATUS(x) (0x3 << ((x) * 2))
+
+/* MCP_FIFOStat - This register contains levels of the Command FIFO and Response FIFO */
+#define SNDW_MEM_FIFOSTAT 0x7C
+#define SNDW_MEM_FIFOSTAT_AVAIL_MASK 0x3F
+#define SNDW_MEM_FIFOSTAT_AVAIL 0
+#define SNDW_MEM_FIFOSTAT_FREE_MASK 0x3F00
+#define SNDW_MEM_FIFOSTAT_FREE 8
+
+/* MCP_Command - This register is used to access TX_FIFO and RX_FIFO */
+#define SNDW_MEM_COMMAND 0x80
+
+#define SNDW_MAX_ENDPOINT_NUMBER 12
+#define CLK_DIVIDER 0x3
+#define SNDW_DEV_ID_NUM 6
+
+#define SNDW_TXCMD_REGDATA_SHIFT 0
+#define SNDW_TXCMD_REGDATA_MASK 0xFF
+#define SNDW_TXCMD_REGADDR_SHIFT 8
+#define SNDW_TXCMD_REGADDR_MASK 0xFFFF
+#define SNDW_TXCMD_DEVADDR_SHIFT 24
+#define SNDW_TXCMD_DEVADDR_MASK 0xF
+#define SNDW_TXCMD_TYPE_SHIFT 28
+#define SNDW_TXCMD_TYPE_MASK 0x7
+#define SNDW_TXCMD_SSPTAG_SHIFT 31
+#define SNDW_TXCMD_SSPTAG_MASK 0x1
+#define SNDW_RXCMD_ACK_SHIFT 0
+#define SNDW_RXCMD_ACK_MASK 0x1
+#define SNDW_RXCMD_NAK_SHIFT 1
+#define SNDW_RXCMD_NAK_MASK 0x1
+#define SNDW_RXCMD_TYPE_SHIFT 4
+#define SNDW_RXCMD_TYPE_MASK 0x7
+#define SNDW_RXCMD_REGDATA_SHIFT 8
+#define SNDW_RXCMD_REGDATA_MASK 0xFF
+
+#define SNDW_MAKE_CMD_FIELD(type, name, val) \
+ (((val) & SNDW_##type##CMD_##name##_MASK) << SNDW_##type##CMD_##name##_SHIFT)
+#define SNDW_EXTRACT_CMD_FIELD(type, name, val) \
+ (((val) >> SNDW_##type##CMD_##name##_SHIFT) & SNDW_##type##CMD_##name##_MASK)
+
+/* Soundwire endpoint device status */
+enum SNDW_ENDPOINT_STATUS {
+ status_not_present = 0,
+ status_attached_ok = 1,
+ status_alert = 2,
+ status_reserved = 3
+};
+
+/* Soundwire command types */
+enum SNDW_CMD_TYPE {
+ cmd_ping = 0,
+ cmd_reserved = 1,
+ cmd_read = 2,
+ cmd_write = 3,
+ cmd_invalid = 4
+};
+
+/*
+ * Soundwire endpoint device ID
+ * SCP_ENDPOINT_ID_0 - Soundwire version and unique ID.
+ * SCP_ENDPOINT_ID_1 - ManufacturerID [15:8].
+ * SCP_ENDPOINT_ID_2 - ManufacturerID [7:0].
+ * SCP_ENDPOINT_ID_3 - PartID[15:8].
+ * SCP_ENDPOINT_ID_4 - PartID[7:0].
+ * SCP_ENDPOINT_ID_5 - Class.
+ */
+enum SNDW_ENDPOINT_ID {
+ SCP_ENDPOINT_ID_0 = 0x50,
+ SCP_ENDPOINT_ID_1 = 0x51,
+ SCP_ENDPOINT_ID_2 = 0x52,
+ SCP_ENDPOINT_ID_3 = 0x53,
+ SCP_ENDPOINT_ID_4 = 0x54,
+ SCP_ENDPOINT_ID_5 = 0x55,
+};
+
+/*
+ * Host can access the SoundWire devices by sending commands in the Soundwire frames.
+ * sndw_cmd - Tx command format.
+ */
+typedef struct {
+ uint8_t regdata;
+ uint16_t regaddr;
+ uint8_t devaddr;
+ uint8_t cmdtype;
+ uint8_t ssptag;
+} sndw_cmd;
+
+/* Soundwire codec ID */
+typedef union {
+ uint8_t id[SNDW_DEV_ID_NUM];
+ struct {
+ uint8_t version;
+ uint8_t mfgid1;
+ uint8_t mfgid2;
+ uint8_t partid1;
+ uint8_t partid2;
+ uint8_t sndwclass;
+ } codec;
+} sndw_codec_id;
+
+/* Soundwire codec information */
+typedef struct {
+ sndw_codec_id codecid;
+ uint32_t sndwlinkaddr;
+ uint32_t deviceindex;
+} sndw_codec_info;
+
+#endif /* __DRIVERS_BUS_SOUNDWIRE_MIPI_SNDWREGS_H__ */
diff --git a/src/drivers/bus/soundwire/soundwire.c b/src/drivers/bus/soundwire/soundwire.c
new file mode 100644
index 0000000..730429c
--- /dev/null
+++ b/src/drivers/bus/soundwire/soundwire.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Intel Corporation.
+ * Copyright 2020 Google LLC.
+ */
+
+#include <arch/io.h>
+#include <libpayload.h>
+
+#include "base/container_of.h"
+#include "drivers/bus/soundwire/soundwire.h"
+#include "drivers/timer/timer.h"
+
+/* Soundwire message to read the endpoint device ID */
+static const sndw_cmd mipi_sndw_read_endpointid_cmds[] = {
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_0,
+ },
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_1,
+ },
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_2,
+ },
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_3,
+ },
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_4,
+ },
+ {
+ .cmdtype = cmd_read,
+ .regaddr = SCP_ENDPOINT_ID_5,
+ }
+};
+
+/*
+ * poll_status - Function for polling the Status bit.
+ * reg - The register address to read the status.
+ * pollingmask - The bit mapping for polling.
+ * pollingdata - The Data for polling.
+ */
+static int poll_status(uint32_t reg, uint32_t pollingmask, uint32_t pollingdata)
+{
+ struct stopwatch sw;
+ uint32_t data;
+
+ stopwatch_init_usecs_expire(&sw, SNDW_POLL_TIME_US);
+ do {
+ data = readl(reg);
+ if ((data & pollingmask) == pollingdata)
+ return 0;
+ udelay(SNDW_WAIT_PERIOD);
+ } while (!stopwatch_expired(&sw));
+
+ printf("Polling status bit failed.\n");
+ return -1;
+}
+
+/*
+ * printtxcmd - Print message for Sndw codec.
+ * txcmd - Tx Sndw message.
+ */
+#if DEBUG_SNDW
+static void printtxcmd(uint32_t txcmd)
+{
+ printf("Sndw Tx commands\n");
+ printf("Tx:Ssp tag = %x\n",SNDW_EXTRACT_CMD_FIELD(TX, SSPTAG, txcmd));
+ printf("Tx:Command type = %x\n",SNDW_EXTRACT_CMD_FIELD(TX, TYPE, txcmd));
+ printf("Tx:Device address = %x\n",SNDW_EXTRACT_CMD_FIELD(TX, DEVADDR, txcmd));
+ printf("Tx:Register address = %x\n",SNDW_EXTRACT_CMD_FIELD(TX, REGADDR, txcmd));
+ printf("Tx:Register data = %x\n",SNDW_EXTRACT_CMD_FIELD(TX, REGDATA, txcmd));
+}
+#else
+#define printtxcmd(...)
+#endif
+
+/*
+ * printrxcmd - Print response message from Sndw codec.
+ * rxcmd - Rx Sndw message.
+ */
+#if DEBUG_SNDW
+static void printrxcmd(uint32_t rxcmd)
+{
+ printf("Sndw rx commands\n");
+ printf("Rx:Register data = %x\n",SNDW_EXTRACT_CMD_FIELD(RX, REGDATA, rxcmd));
+ printf("Rx:Command type = %x\n",SNDW_EXTRACT_CMD_FIELD(RX, TYPE, rxcmd));
+ printf("Rx:nak = %x\n",SNDW_EXTRACT_CMD_FIELD(RX, NAK, rxcmd));
+ printf("Rx:ack = %x\n",SNDW_EXTRACT_CMD_FIELD(RX, ACK, rxcmd));
+}
+#else
+#define printrxcmd(...)
+#endif
+
+/*
+ * send - Function operating on Sndw Fifo for sending message to codecs.
+ * sndwlinkaddr - Soundwire controller link address.
+ * txcmds - Pointer to send messages.
+ * numofmsgs - Size of messages to send.
+ */
+static void send(uint32_t sndwlinkaddr, sndw_cmd *txcmds, uint32_t devaddr, uint32_t numofmsgs)
+{
+ uint32_t fifofree, txmsg, txindex;
+
+ fifofree = (readl(sndwlinkaddr + SNDW_MEM_FIFOSTAT) & SNDW_MEM_FIFOSTAT_FREE_MASK)
+ >> SNDW_MEM_FIFOSTAT_FREE;
+
+ for (txindex = 0; txindex < fifofree && txindex < numofmsgs; txindex++) {
+ txmsg = SNDW_MAKE_CMD_FIELD(TX, REGDATA, txcmds[txindex].regdata) |
+ SNDW_MAKE_CMD_FIELD(TX, REGADDR, txcmds[txindex].regaddr) |
+ SNDW_MAKE_CMD_FIELD(TX, DEVADDR, devaddr) |
+ SNDW_MAKE_CMD_FIELD(TX, TYPE, txcmds[txindex].cmdtype) |
+ SNDW_MAKE_CMD_FIELD(TX, SSPTAG, txcmds[txindex].ssptag);
+#if DEBUG_SNDW
+ printtxcmd(txmsg);
+#endif
+ writel(txmsg, sndwlinkaddr + SNDW_MEM_COMMAND);
+ }
+}
+
+/*
+ * get_fifo_avail - This function returns number of available responses in the Response FIFO.
+ */
+static unsigned int get_fifo_avail(uint32_t sndwlinkaddr)
+{
+ return (readl(sndwlinkaddr + SNDW_MEM_FIFOSTAT) & SNDW_MEM_FIFOSTAT_AVAIL_MASK)
+ >> SNDW_MEM_FIFOSTAT_AVAIL;
+}
+
+/*
+ * receive - Function operating on Sndw Fifo for receiving messages from codecs.
+ * rxcmds - Pointer to pointer to received messages.
+ * rxsize - Size of received messages.
+ */
+static int receive(uint32_t sndwlinkaddr, uint32_t **rxcmds, uint32_t *rxsize)
+{
+ uint32_t fifoavailable, i, rxmsg, rxindex;
+ rxindex = 0;
+
+ fifoavailable = get_fifo_avail(sndwlinkaddr);
+
+ if (fifoavailable == 0)
+ return -1;
+
+ *rxcmds = xzalloc(sizeof(uint32_t) * fifoavailable);
+
+ for (i = 0; i < fifoavailable; i++) {
+ rxmsg = readl(sndwlinkaddr + SNDW_MEM_COMMAND);
+ mdelay(SNDW_WAIT_PERIOD);
+#if DEBUG_SNDW
+ printrxcmd(rxmsg);
+#endif
+ if (SNDW_EXTRACT_CMD_FIELD(RX, ACK, rxmsg) == 0) {
+ printf(" Failed to recieve the ACK.\n");
+ return -1;
+ }
+ (*rxcmds)[rxindex++] = rxmsg;
+ }
+
+ *rxsize = rxindex;
+
+ /* Clear the RXNE interrupt. */
+ writel(SNDW_MEM_INTMASK_RXNE, sndwlinkaddr + SNDW_MEM_INTMASK);
+
+ return 0;
+}
+
+/*
+ * read_endpointid - Function to get the identification information of connected codec.
+ * sndwlinkaddr - Soundwire controller link address.
+ * codecinfo - Pointer to the codec information.
+ */
+static int read_endpointid(uint32_t sndwlinkaddr, uint32_t deviceindex,
+ sndw_codec_info *codecinfo)
+{
+ uint32_t i, fifostatavail, count, rxsize;
+ uint32_t *rxcmds;
+ struct stopwatch sw;
+
+ count = sizeof(mipi_sndw_read_endpointid_cmds) / sizeof(sndw_cmd);
+
+ send(sndwlinkaddr, (sndw_cmd *)&mipi_sndw_read_endpointid_cmds, deviceindex, count);
+
+ stopwatch_init_usecs_expire(&sw, SNDW_POLL_TIME_US);
+ do {
+ fifostatavail = get_fifo_avail(sndwlinkaddr);
+ if (count == fifostatavail)
+ break;
+ else
+ udelay(SNDW_WAIT_PERIOD);
+ } while (!stopwatch_expired(&sw));
+
+ if (count != fifostatavail) {
+ printf("Response is not available for the txcmds\n");
+ return -1;
+ }
+
+ if (receive(sndwlinkaddr, &rxcmds, &rxsize)) {
+ printf("Failed to recieve the rx messages\n ");
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (SNDW_EXTRACT_CMD_FIELD(RX, ACK, rxcmds[i]) == 1) {
+ codecinfo->codecid.id[i] = SNDW_EXTRACT_CMD_FIELD(RX,
+ REGDATA, rxcmds[i]);
+ }
+ else {
+ printf("Failed to recieve the codec ID.\n");
+ return -1;
+ }
+ }
+
+ free(rxcmds);
+
+ return 0;
+}
+
+/*
+ * getsndwlinkaddress - Function to get the Soundwire link controller address.
+ * bus - Pointer to the Soudnwire structure.
+ */
+static uint32_t getsndwlinkaddress(Soundwire *bus)
+{
+ uint32_t linkaddress;
+ linkaddress = (readl(bus->dspbar + DSP_MEM_SNDW_SNDWIPPTR) & DSP_MEM_SNDW_SNDWIPPTR_PRT)
+ + (bus->sndwlinkindex * DSP_MEM_SNDW_OFFSETS);
+
+ if (linkaddress == 0) {
+ printf("Failed to get the Soundwire link controller address.\n");
+ return -1;
+ }
+
+ return linkaddress;
+}
+
+/*
+ * getnumofsndwlinks - Get the number of supported Soundwire links.
+ * bus - Pointer to the Soudnwire structure.
+ */
+static uint32_t getnumofsndwlinks(Soundwire *bus)
+{
+ /* Get the number of the supported Soundwire links */
+ uint32_t numofsndwlinks = readl(bus->dspbar + DSP_MEM_SNDW_SNDWLCAP)
+ & DSP_MEM_SNDW_SNDWSC;
+ return numofsndwlinks;
+}
+
+/*
+ * sndwcodecstatus - Function to check the endpoint device status.
+ * bus - Pointer to the Soundwire structure.
+ * endpointstatus - Pointer to the endpoint device status.
+ */
+static uint32_t sndwcodecstatus(Soundwire *bus, uint32_t *endpointstatus)
+{
+ uint32_t status;
+ struct stopwatch sw;
+ uint32_t sndwlinkaddress = getsndwlinkaddress(bus);
+
+ stopwatch_init_usecs_expire(&sw, SNDW_POLL_TIME_US);
+ do {
+ status = readl(bus->dspbar + sndwlinkaddress + SNDW_MEM_ENDPOINTSTAT);
+ if (status != 0) {
+ *endpointstatus = status;
+ return 0;
+ } else {
+ udelay(SNDW_WAIT_PERIOD);
+ }
+ } while (!stopwatch_expired(&sw));
+
+ printf("Sndw controller did not detect any codecs.\n");
+ return -1;
+}
+
+/*
+ * enable_sndwcodec - Function for codec enumeration process and read the codec info.
+ * bus - Pointer to the Soundwire structure.
+ * codecinfo - Pointer to the codec information.
+ */
+static int enable_sndwcodec(Soundwire *bus, sndw_codec_info *codecinfo)
+{
+ /* Soundwire codec initialization */
+ uint32_t deviceindex, endpointstatus;
+
+ /* Check the endpoint status */
+ for (deviceindex = 0; deviceindex < SNDW_MAX_ENDPOINT_NUMBER; deviceindex++) {
+ if (sndwcodecstatus(bus, &endpointstatus))
+ return -1;
+
+ if ((endpointstatus & SNDW_MEM_ENDPOINTSTAT_STATUS(deviceindex))
+ == status_attached_ok) {
+ udelay(SNDW_POLL_TIME_US);
+ if (read_endpointid(bus->sndwlinkaddr,deviceindex,codecinfo)) {
+ printf("Couldn't read the endpoint ID\n");
+ return -1;
+ }
+ codecinfo->sndwlinkaddr = bus->sndwlinkaddr;
+ codecinfo->deviceindex = deviceindex;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * sndwmasterinit - Function initializes Sndw master for enumerating connected codecs.
+ * sndwlinkaddr - Soundwire link controller address.
+ */
+static int sndwmasterinit(uint32_t sndwlinkaddr)
+{
+ /* Program MCP_ClockCtrl.Master Clock Divider to configure the Soundwire
+ clock frequency */
+ writel(CLK_DIVIDER, sndwlinkaddr + SNDW_MEM_CLK_CTRL0);
+ writel(CLK_DIVIDER, sndwlinkaddr + SNDW_MEM_CLK_CTRL1);
+
+ /* Program MCP_Config.CMDMode to access the commands/responses via AHB mode */
+ writel((readl(sndwlinkaddr + SNDW_MEM_CONFIG) & ~SNDW_MEM_CONFIG_MODE_AHB),
+ sndwlinkaddr + SNDW_MEM_CONFIG);
+
+ /* Write 0 to MCP_Config.OperationMode to bring the master into normal mode */
+ writel((readl(sndwlinkaddr + SNDW_MEM_CONFIG) & ~SNDW_MEM_CONFIG_OM_NORMAL),
+ sndwlinkaddr + SNDW_MEM_CONFIG);
+
+ /* Write 0 to MCP_ConfigUpdate to update controller settings */
+ writel(SNDW_MEM_CONFIGUPDATE_UPDATE_DONE, sndwlinkaddr + SNDW_MEM_CONFIGUPDATE);
+
+ /* Waiting for FIFO to be ready to send MCP message */
+ if (poll_status(sndwlinkaddr + SNDW_MEM_STAT, SNDW_MEM_STAT_TXE,
+ SNDW_MEM_STAT_TXE_FIFO_EMPTY)) {
+ printf("Soundwire controller not ready to send commands to codecs.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * sndwlink_poweron - Power-on the Soundwire link.
+ * bus - Pointer to the Soudnwire structure.
+ */
+static int sndwlink_poweron(Soundwire *bus)
+{
+ uint32_t value;
+ uint16_t rwval;
+
+ /* Set SPA = 1 to power on the SoundWire link */
+ value = DSP_MEM_SNDW_SNDWLCTL_SPA << bus->sndwlinkindex;
+ writel((readl(bus->dspbar + DSP_MEM_SNDW_SNDWLCTL) | value),
+ bus->dspbar + DSP_MEM_SNDW_SNDWLCTL);
+
+ value = DSP_MEM_SNDW_SNDWLCTL_CPA << bus->sndwlinkindex;
+ /* Wait till current active power bit is set */
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if ((readl(bus->dspbar + DSP_MEM_SNDW_SNDWLCTL) & value) == value)
+ break;
+ mdelay(1);
+ }
+
+ if ((readl(bus->dspbar + DSP_MEM_SNDW_SNDWLCTL) & value) != value) {
+ printf("Failed to poweron the Soundwire link.\n");
+ return -1;
+ }
+
+ /*
+ * Enable Data AC Timing Qualifier in preparation for Master IP being
+ * put into "Normal" Operation
+ */
+ rwval = readw(bus->dspbar + DSP_MEM_SNDW_SNDWxACTMCTL(bus->sndwlinkindex))
+ | DSP_MEM_SNDW_SNDWxACTMCTL_DACTQE;
+ writew(rwval, bus->dspbar + DSP_MEM_SNDW_SNDWxACTMCTL(bus->sndwlinkindex));
+
+ /* Enable the Master IP Flowthrough */
+ rwval = readw(bus->dspbar + DSP_MEM_SNDW_SNDWxIOCTL(bus->sndwlinkindex))
+ | DSP_MEM_SNDW_SNDWxIOCTL_MIF;
+ writew(rwval, bus->dspbar + DSP_MEM_SNDW_SNDWxIOCTL(bus->sndwlinkindex));
+
+ return 0;
+}
+
+/*
+ * sndwlink_init - Initialize the Soundwire link.
+ * bus - Pointer to the Soudnwire structure.
+ */
+static int sndwlink_init(Soundwire *bus)
+{
+ uint32_t sndwlinkaddress;
+
+ if (getnumofsndwlinks(bus) < bus->sndwlinkindex) {
+ printf("Soundwire link %d doesn't exist.\n", bus->sndwlinkindex);
+ return -1;
+ }
+
+ if (sndwlink_poweron(bus)) {
+ printf("Failed to initialize the Sndw Link.\n");
+ return -1;
+ }
+
+ sndwlinkaddress = getsndwlinkaddress(bus);
+ bus->sndwlinkaddr = bus->dspbar + sndwlinkaddress;
+
+ /* Function to check access to Sndw controller */
+ if (readl((bus->sndwlinkaddr) + SNDW_MEM_CONFIG) == 0xffffffff) {
+ printf("Failed to enable access to Sndw controller.\n");
+ return -1;
+ }
+
+ if (sndwmasterinit(bus->sndwlinkaddr)) {
+ printf("Failed to initialize the soundwire controller.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * enable_hda_dsp - Function to reset the HDA and DSP
+ * bus - Pointer to the Soudnwire structure
+ */
+static int enable_hda_dsp(Soundwire *bus)
+{
+ /* Set the CRST bit to get the HDA controller out of reset state */
+ writel(HDA_MEM_GCTL_CRST, bus->hdabar + HDA_MEM_GCTL);
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->hdabar + HDA_MEM_GCTL) == HDA_MEM_GCTL_CRST)
+ break;
+ mdelay(1);
+ }
+
+ if (readl(bus->hdabar + HDA_MEM_GCTL) != HDA_MEM_GCTL_CRST) {
+ printf("HDA controller in reset state.\n");
+ return -1;
+ }
+
+ /* Enable Audio DSP for operation. */
+ writel(HDA_MEM_PPCTL_DSPEN, bus->hdabar + HDA_MEM_PPCTL);
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->hdabar + HDA_MEM_PPCTL) == HDA_MEM_PPCTL_DSPEN)
+ break;
+ mdelay(1);
+ }
+
+ if (readl(bus->hdabar + HDA_MEM_PPCTL) != HDA_MEM_PPCTL_DSPEN) {
+ printf("Failed to enable ADSP for operation \n");
+ return -1;
+ }
+
+ /* Set power active to get DSP subsystem out of reset. */
+ writel((readl(bus->dspbar + DSP_MEM_ADSPCS) | DSP_MEM_ADSPCS_SPA),
+ bus->dspbar + DSP_MEM_ADSPCS);
+ /* Wait till current power active bit is set */
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if ((readl(bus->dspbar + DSP_MEM_ADSPCS) &
+ DSP_MEM_ADSPCS_CPA) == DSP_MEM_ADSPCS_CPA)
+ break;
+ mdelay(1);
+ }
+
+ if ((readl(bus->dspbar + DSP_MEM_ADSPCS) & DSP_MEM_ADSPCS_CPA) != DSP_MEM_ADSPCS_CPA) {
+ printf("Failed to get the ADSP out of reset \n");
+ return -1;
+ }
+
+ /* Enable the Host interrupt generation for SNDW interface */
+ writel((readl(bus->dspbar + DSP_MEM_ADSPIC2) | DSP_MEM_ADSPIC2_SNDW),
+ bus->dspbar + DSP_MEM_ADSPIC2);
+
+ return 0;
+}
+
+/*
+ * sndw_enable - Enable HDA, DSP and Soundwire interfaces.
+ * me - Pointer to the Soundwire ops.
+ * codecinfo - Pointer to the codec information.
+ */
+static int sndw_enable(struct SndwOps *me, sndw_codec_info *codecinfo)
+{
+ Soundwire *sndwbus = container_of(me, Soundwire, ops);
+
+ if (enable_hda_dsp(sndwbus)) {
+ printf("Failed to reset HDA/DSP.\n");
+ return -1;
+ }
+
+ if (sndwlink_init(sndwbus)) {
+ printf("Failed to initialize the Soundwire link.\n");
+ return -1;
+ }
+
+ if (enable_sndwcodec(sndwbus, codecinfo)) {
+ printf("Failed to enable the Soundwire codec.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * sndw_sendwack - Function sends one message through the Sndw interface and waits
+ * for the corresponding ACK message.
+ * sndwlinkaddr - Soundwire Link controller address.
+ * txcmd - Message to send.
+ * sndwindex - Index of the Soudnwire endpoint device.
+ */
+static int sndw_sendwack(uint32_t sndwlinkaddr, sndw_cmd txcmd, uint32_t deviceindex)
+{
+ uint32_t rxsize;
+ uint32_t *rxcmd;
+
+ send(sndwlinkaddr, &txcmd, deviceindex, 1);
+
+ if (poll_status(sndwlinkaddr + SNDW_MEM_STAT, SNDW_MEM_STAT_RXNE,
+ SNDW_MEM_STAT_RXNE_FIFO_EMPTY)) {
+ printf("Sending command failed. No response from codec.\n");
+ return -1;
+ }
+
+ if (receive(sndwlinkaddr, &rxcmd, &rxsize)) {
+ printf("Failed to recieve the rx messages\n ");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * sndw_disable - Disable the soundwire interface by resetting the DSP.
+ * bus - Pointer to the Soundwire structure.
+ */
+static int sndw_disable(SndwOps *me)
+{
+ Soundwire *bus = container_of(me, Soundwire, ops);
+ /* Set SPA=0 to reset ADSP subsystem. */
+ writel((readl(bus->dspbar + DSP_MEM_ADSPCS) & ~DSP_MEM_ADSPCS_SPA), bus->dspbar + DSP_MEM_ADSPCS);
+ /* Wait till current power active bit is set */
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if ((readl(bus->dspbar + DSP_MEM_ADSPCS) &
+ DSP_MEM_ADSPCS_CPA) == 0)
+ break;
+ mdelay(1);
+ }
+ if ((readl(bus->dspbar + DSP_MEM_ADSPCS) & DSP_MEM_ADSPCS_CPA) != 0) {
+ printf("Failed to reset the ADSP.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * new_sndw_structure - Allocate new Soundwire data structures.
+ * Allocate new Soundwire data structures.
+ */
+Soundwire *new_soundwire(int sndwlinkindex)
+{
+ Soundwire *bus = xzalloc(sizeof(*bus));
+ pcidev_t lpe_pcidev = PCI_DEV(0, AUDIO_DEV, AUDIO_FUNC);
+
+ bus->ops.sndw_enable = &sndw_enable;
+ bus->ops.sndw_sendwack = &sndw_sendwack;
+ bus->ops.sndw_disable = &sndw_disable;
+ bus->hdabar = pci_read_config32(lpe_pcidev, REG_BAR0) & (~0xf);
+ bus->dspbar = pci_read_config32(lpe_pcidev, REG_BAR4) & (~0xf);
+ bus->sndwlinkindex = sndwlinkindex;
+
+ return bus;
+}
diff --git a/src/drivers/bus/soundwire/soundwire.h b/src/drivers/bus/soundwire/soundwire.h
new file mode 100644
index 0000000..59b27ae
--- /dev/null
+++ b/src/drivers/bus/soundwire/soundwire.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, Intel Corporation.
+ * Copyright 2020 Google LLC.
+ */
+
+#ifndef __DRIVERS_BUS_SOUNDWIRE_SOUNDWIRE_H__
+#define __DRIVERS_BUS_SOUNDWIRE_SOUNDWIRE_H__
+
+#include "drivers/bus/soundwire/cavs_2_5-sndwregs.h"
+#include "drivers/bus/soundwire/mipi-sndwregs.h"
+
+#define RETRY_COUNT 1000
+#define DEBUG_SNDW 0
+
+/*
+ * Soundwire ops
+ * sndw_enable() - Enables the HDA/DSP/SNDW.
+ * sndw_sendwack() - Send the txcmds and receive the response.
+ * sndw_disable() - Disable the soundwire interface by resetting the DSP.
+ */
+typedef struct SndwOps {
+ int (*sndw_enable)(struct SndwOps *me, sndw_codec_info *codecinfo);
+ int (*sndw_sendwack)(uint32_t sndwlinkaddr, sndw_cmd txcmd, uint32_t deviceindex);
+ int (*sndw_disable)(struct SndwOps *me);
+} SndwOps;
+
+typedef struct {
+ /* Soundwire ops structure.*/
+ SndwOps ops;
+ /* Intel HD Audio Lower Base Address.*/
+ uintptr_t hdabar;
+ /* Audio DSP Lower Base Address.*/
+ uintptr_t dspbar;
+ /* Sndw Link Number */
+ int sndwlinkindex;
+ /* Sndw Link address */
+ uint32_t sndwlinkaddr;
+} Soundwire;
+
+/*
+ * new_soundwire - Allocate new Soundwire data structures.
+ * @Sndwlinkindex: Sndw Link Number
+ */
+Soundwire *new_soundwire(int sndwlinkindex);
+
+#endif