xref: /openbmc/linux/drivers/soundwire/cadence_master.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
12f52a517SVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
22f52a517SVinod Koul // Copyright(c) 2015-17 Intel Corporation.
32f52a517SVinod Koul 
42f52a517SVinod Koul /*
52f52a517SVinod Koul  * Cadence SoundWire Master module
62f52a517SVinod Koul  * Used by Master driver
72f52a517SVinod Koul  */
82f52a517SVinod Koul 
92f52a517SVinod Koul #include <linux/delay.h>
102f52a517SVinod Koul #include <linux/device.h>
11aa85066eSPierre-Louis Bossart #include <linux/debugfs.h>
122f52a517SVinod Koul #include <linux/interrupt.h>
1318de65d9SJan Kotas #include <linux/io.h>
142f52a517SVinod Koul #include <linux/module.h>
152f52a517SVinod Koul #include <linux/mod_devicetable.h>
1632d2a893SPierre-Louis Bossart #include <linux/pm_runtime.h>
172f52a517SVinod Koul #include <linux/soundwire/sdw_registers.h>
182f52a517SVinod Koul #include <linux/soundwire/sdw.h>
195d6b3c8bSVinod Koul #include <sound/pcm_params.h>
205d6b3c8bSVinod Koul #include <sound/soc.h>
214a98a6b2SBard Liao #include <linux/workqueue.h>
222f52a517SVinod Koul #include "bus.h"
232f52a517SVinod Koul #include "cadence_master.h"
242f52a517SVinod Koul 
2504592dceSPierre-Louis Bossart static int interrupt_mask;
2604592dceSPierre-Louis Bossart module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444);
2704592dceSPierre-Louis Bossart MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask");
2804592dceSPierre-Louis Bossart 
292f52a517SVinod Koul #define CDNS_MCP_CONFIG				0x0
302f52a517SVinod Koul #define CDNS_MCP_CONFIG_BUS_REL			BIT(6)
31c5753714SPierre-Louis Bossart 
32c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG			0x0 /* IP offset added at run-time */
33c5753714SPierre-Louis Bossart 
34c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_MCMD_RETRY		GENMASK(27, 24)
35c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_MPREQ_DELAY		GENMASK(20, 16)
36c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_MMASTER		BIT(7)
37c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_SNIFFER		BIT(5)
38c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_CMD			BIT(3)
39c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_OP			GENMASK(2, 0)
40c5753714SPierre-Louis Bossart #define CDNS_IP_MCP_CONFIG_OP_NORMAL		0
412f52a517SVinod Koul 
422f52a517SVinod Koul #define CDNS_MCP_CONTROL			0x4
432f52a517SVinod Koul 
442f52a517SVinod Koul #define CDNS_MCP_CONTROL_CMD_RST		BIT(7)
452f52a517SVinod Koul #define CDNS_MCP_CONTROL_SOFT_RST		BIT(6)
462f52a517SVinod Koul #define CDNS_MCP_CONTROL_HW_RST			BIT(4)
472f52a517SVinod Koul #define CDNS_MCP_CONTROL_CLK_STOP_CLR		BIT(2)
484dc953bcSPierre-Louis Bossart 
494dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL			0x4  /* IP offset added at run-time */
504dc953bcSPierre-Louis Bossart 
514dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL_RST_DELAY		GENMASK(10, 8)
524dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL_SW_RST		BIT(5)
534dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL_CLK_PAUSE		BIT(3)
544dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL_CMD_ACCEPT		BIT(1)
554dc953bcSPierre-Louis Bossart #define CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP	BIT(0)
562f52a517SVinod Koul 
5773a29d3fSPierre-Louis Bossart #define CDNS_IP_MCP_CMDCTRL			0x8 /* IP offset added at run-time */
5832d2a893SPierre-Louis Bossart 
5973a29d3fSPierre-Louis Bossart #define CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR	BIT(2)
6032d2a893SPierre-Louis Bossart 
612f52a517SVinod Koul #define CDNS_MCP_SSPSTAT			0xC
622f52a517SVinod Koul #define CDNS_MCP_FRAME_SHAPE			0x10
632f52a517SVinod Koul #define CDNS_MCP_FRAME_SHAPE_INIT		0x14
6405be59acSPierre-Louis Bossart #define CDNS_MCP_FRAME_SHAPE_COL_MASK		GENMASK(2, 0)
653cf25d63SVinod Koul #define CDNS_MCP_FRAME_SHAPE_ROW_MASK		GENMASK(7, 3)
662f52a517SVinod Koul 
672f52a517SVinod Koul #define CDNS_MCP_CONFIG_UPDATE			0x18
682f52a517SVinod Koul #define CDNS_MCP_CONFIG_UPDATE_BIT		BIT(0)
692f52a517SVinod Koul 
702f52a517SVinod Koul #define CDNS_MCP_PHYCTRL			0x1C
712f52a517SVinod Koul #define CDNS_MCP_SSP_CTRL0			0x20
722f52a517SVinod Koul #define CDNS_MCP_SSP_CTRL1			0x28
732f52a517SVinod Koul #define CDNS_MCP_CLK_CTRL0			0x30
742f52a517SVinod Koul #define CDNS_MCP_CLK_CTRL1			0x38
75a50954e2SRander Wang #define CDNS_MCP_CLK_MCLKD_MASK		GENMASK(7, 0)
762f52a517SVinod Koul 
772f52a517SVinod Koul #define CDNS_MCP_STAT				0x40
782f52a517SVinod Koul 
792f52a517SVinod Koul #define CDNS_MCP_STAT_ACTIVE_BANK		BIT(20)
802f52a517SVinod Koul #define CDNS_MCP_STAT_CLK_STOP			BIT(16)
812f52a517SVinod Koul 
822f52a517SVinod Koul #define CDNS_MCP_INTSTAT			0x44
832f52a517SVinod Koul #define CDNS_MCP_INTMASK			0x48
842f52a517SVinod Koul 
852f52a517SVinod Koul #define CDNS_MCP_INT_IRQ			BIT(31)
86a2cff9eeSPierre-Louis Bossart #define CDNS_MCP_INT_RESERVED1			GENMASK(30, 17)
872f52a517SVinod Koul #define CDNS_MCP_INT_WAKEUP			BIT(16)
882f52a517SVinod Koul #define CDNS_MCP_INT_SLAVE_RSVD			BIT(15)
892f52a517SVinod Koul #define CDNS_MCP_INT_SLAVE_ALERT		BIT(14)
902f52a517SVinod Koul #define CDNS_MCP_INT_SLAVE_ATTACH		BIT(13)
912f52a517SVinod Koul #define CDNS_MCP_INT_SLAVE_NATTACH		BIT(12)
922f52a517SVinod Koul #define CDNS_MCP_INT_SLAVE_MASK			GENMASK(15, 12)
932f52a517SVinod Koul #define CDNS_MCP_INT_DPINT			BIT(11)
942f52a517SVinod Koul #define CDNS_MCP_INT_CTRL_CLASH			BIT(10)
952f52a517SVinod Koul #define CDNS_MCP_INT_DATA_CLASH			BIT(9)
969b5884a0SPierre-Louis Bossart #define CDNS_MCP_INT_PARITY			BIT(8)
972f52a517SVinod Koul #define CDNS_MCP_INT_CMD_ERR			BIT(7)
98a2cff9eeSPierre-Louis Bossart #define CDNS_MCP_INT_RESERVED2			GENMASK(6, 4)
999b5884a0SPierre-Louis Bossart #define CDNS_MCP_INT_RX_NE			BIT(3)
1002f52a517SVinod Koul #define CDNS_MCP_INT_RX_WL			BIT(2)
1012f52a517SVinod Koul #define CDNS_MCP_INT_TXE			BIT(1)
1029b5884a0SPierre-Louis Bossart #define CDNS_MCP_INT_TXF			BIT(0)
103a2cff9eeSPierre-Louis Bossart #define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2)
1042f52a517SVinod Koul 
1052f52a517SVinod Koul #define CDNS_MCP_INTSET				0x4C
1062f52a517SVinod Koul 
107b07dd9b4SPierre-Louis Bossart #define CDNS_MCP_SLAVE_STAT			0x50
108b07dd9b4SPierre-Louis Bossart #define CDNS_MCP_SLAVE_STAT_MASK		GENMASK(1, 0)
1092f52a517SVinod Koul 
1102f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT0			0x54
1112f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT1			0x58
1122f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT_NPRESENT		BIT(0)
1132f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT_ATTACHED		BIT(1)
1142f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT_ALERT		BIT(2)
1152f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTSTAT_RESERVED		BIT(3)
1162f52a517SVinod Koul #define CDNS_MCP_SLAVE_STATUS_BITS		GENMASK(3, 0)
1172f52a517SVinod Koul #define CDNS_MCP_SLAVE_STATUS_NUM		4
1182f52a517SVinod Koul 
1192f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTMASK0			0x5C
1202f52a517SVinod Koul #define CDNS_MCP_SLAVE_INTMASK1			0x60
1212f52a517SVinod Koul 
122664b1658SPierre-Louis Bossart #define CDNS_MCP_SLAVE_INTMASK0_MASK		GENMASK(31, 0)
123664b1658SPierre-Louis Bossart #define CDNS_MCP_SLAVE_INTMASK1_MASK		GENMASK(15, 0)
1242f52a517SVinod Koul 
1252f52a517SVinod Koul #define CDNS_MCP_PORT_INTSTAT			0x64
1262f52a517SVinod Koul #define CDNS_MCP_PDI_STAT			0x6C
1272f52a517SVinod Koul 
1282f52a517SVinod Koul #define CDNS_MCP_FIFOLEVEL			0x78
1292f52a517SVinod Koul #define CDNS_MCP_FIFOSTAT			0x7C
1302f52a517SVinod Koul #define CDNS_MCP_RX_FIFO_AVAIL			GENMASK(5, 0)
1312f52a517SVinod Koul 
13283ae1ccbSPierre-Louis Bossart #define CDNS_IP_MCP_CMD_BASE			0x80 /* IP offset added at run-time */
13383ae1ccbSPierre-Louis Bossart #define CDNS_IP_MCP_RESP_BASE			0x80 /* IP offset added at run-time */
1347cbfee2eSRichard Fitzgerald /* FIFO can hold 8 commands */
1357cbfee2eSRichard Fitzgerald #define CDNS_MCP_CMD_LEN			8
1362f52a517SVinod Koul #define CDNS_MCP_CMD_WORD_LEN			0x4
1372f52a517SVinod Koul 
1382f52a517SVinod Koul #define CDNS_MCP_CMD_SSP_TAG			BIT(31)
1392f52a517SVinod Koul #define CDNS_MCP_CMD_COMMAND			GENMASK(30, 28)
1402f52a517SVinod Koul #define CDNS_MCP_CMD_DEV_ADDR			GENMASK(27, 24)
1413cf25d63SVinod Koul #define CDNS_MCP_CMD_REG_ADDR			GENMASK(23, 8)
1422f52a517SVinod Koul #define CDNS_MCP_CMD_REG_DATA			GENMASK(7, 0)
1432f52a517SVinod Koul 
1442f52a517SVinod Koul #define CDNS_MCP_CMD_READ			2
1452f52a517SVinod Koul #define CDNS_MCP_CMD_WRITE			3
1462f52a517SVinod Koul 
1472f52a517SVinod Koul #define CDNS_MCP_RESP_RDATA			GENMASK(15, 8)
1482f52a517SVinod Koul #define CDNS_MCP_RESP_ACK			BIT(0)
1492f52a517SVinod Koul #define CDNS_MCP_RESP_NACK			BIT(1)
1502f52a517SVinod Koul 
1512f52a517SVinod Koul #define CDNS_DP_SIZE				128
1522f52a517SVinod Koul 
1532f52a517SVinod Koul #define CDNS_DPN_B0_CONFIG(n)			(0x100 + CDNS_DP_SIZE * (n))
1542f52a517SVinod Koul #define CDNS_DPN_B0_CH_EN(n)			(0x104 + CDNS_DP_SIZE * (n))
1552f52a517SVinod Koul #define CDNS_DPN_B0_SAMPLE_CTRL(n)		(0x108 + CDNS_DP_SIZE * (n))
1562f52a517SVinod Koul #define CDNS_DPN_B0_OFFSET_CTRL(n)		(0x10C + CDNS_DP_SIZE * (n))
1572f52a517SVinod Koul #define CDNS_DPN_B0_HCTRL(n)			(0x110 + CDNS_DP_SIZE * (n))
1582f52a517SVinod Koul #define CDNS_DPN_B0_ASYNC_CTRL(n)		(0x114 + CDNS_DP_SIZE * (n))
1592f52a517SVinod Koul 
1602f52a517SVinod Koul #define CDNS_DPN_B1_CONFIG(n)			(0x118 + CDNS_DP_SIZE * (n))
1612f52a517SVinod Koul #define CDNS_DPN_B1_CH_EN(n)			(0x11C + CDNS_DP_SIZE * (n))
1622f52a517SVinod Koul #define CDNS_DPN_B1_SAMPLE_CTRL(n)		(0x120 + CDNS_DP_SIZE * (n))
1632f52a517SVinod Koul #define CDNS_DPN_B1_OFFSET_CTRL(n)		(0x124 + CDNS_DP_SIZE * (n))
1642f52a517SVinod Koul #define CDNS_DPN_B1_HCTRL(n)			(0x128 + CDNS_DP_SIZE * (n))
1652f52a517SVinod Koul #define CDNS_DPN_B1_ASYNC_CTRL(n)		(0x12C + CDNS_DP_SIZE * (n))
1662f52a517SVinod Koul 
1672f52a517SVinod Koul #define CDNS_DPN_CONFIG_BPM			BIT(18)
1682f52a517SVinod Koul #define CDNS_DPN_CONFIG_BGC			GENMASK(17, 16)
1692f52a517SVinod Koul #define CDNS_DPN_CONFIG_WL			GENMASK(12, 8)
1702f52a517SVinod Koul #define CDNS_DPN_CONFIG_PORT_DAT		GENMASK(3, 2)
1712f52a517SVinod Koul #define CDNS_DPN_CONFIG_PORT_FLOW		GENMASK(1, 0)
1722f52a517SVinod Koul 
1732f52a517SVinod Koul #define CDNS_DPN_SAMPLE_CTRL_SI			GENMASK(15, 0)
1742f52a517SVinod Koul 
1752f52a517SVinod Koul #define CDNS_DPN_OFFSET_CTRL_1			GENMASK(7, 0)
1762f52a517SVinod Koul #define CDNS_DPN_OFFSET_CTRL_2			GENMASK(15, 8)
1772f52a517SVinod Koul 
1782f52a517SVinod Koul #define CDNS_DPN_HCTRL_HSTOP			GENMASK(3, 0)
1792f52a517SVinod Koul #define CDNS_DPN_HCTRL_HSTART			GENMASK(7, 4)
1802f52a517SVinod Koul #define CDNS_DPN_HCTRL_LCTRL			GENMASK(10, 8)
1812f52a517SVinod Koul 
1822f52a517SVinod Koul #define CDNS_PORTCTRL				0x130
1839e4e6019SPierre-Louis Bossart #define CDNS_PORTCTRL_TEST_FAILED		BIT(1)
1842f52a517SVinod Koul #define CDNS_PORTCTRL_DIRN			BIT(7)
1852f52a517SVinod Koul #define CDNS_PORTCTRL_BANK_INVERT		BIT(8)
1862f52a517SVinod Koul 
1872f52a517SVinod Koul #define CDNS_PORT_OFFSET			0x80
1882f52a517SVinod Koul 
1892f52a517SVinod Koul #define CDNS_PDI_CONFIG(n)			(0x1100 + (n) * 16)
1902f52a517SVinod Koul 
1912f52a517SVinod Koul #define CDNS_PDI_CONFIG_SOFT_RESET		BIT(24)
1922f52a517SVinod Koul #define CDNS_PDI_CONFIG_CHANNEL			GENMASK(15, 8)
1932f52a517SVinod Koul #define CDNS_PDI_CONFIG_PORT			GENMASK(4, 0)
1942f52a517SVinod Koul 
1952f52a517SVinod Koul /* Driver defaults */
1960cff9911SPierre-Louis Bossart #define CDNS_TX_TIMEOUT				500
1972f52a517SVinod Koul 
1982f52a517SVinod Koul #define CDNS_SCP_RX_FIFOLEVEL			0x2
1992f52a517SVinod Koul 
2002f52a517SVinod Koul /*
2012f52a517SVinod Koul  * register accessor helpers
2022f52a517SVinod Koul  */
cdns_readl(struct sdw_cdns * cdns,int offset)2032f52a517SVinod Koul static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset)
2042f52a517SVinod Koul {
2052f52a517SVinod Koul 	return readl(cdns->registers + offset);
2062f52a517SVinod Koul }
2072f52a517SVinod Koul 
cdns_writel(struct sdw_cdns * cdns,int offset,u32 value)2082f52a517SVinod Koul static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value)
2092f52a517SVinod Koul {
2102f52a517SVinod Koul 	writel(value, cdns->registers + offset);
2112f52a517SVinod Koul }
2122f52a517SVinod Koul 
cdns_ip_readl(struct sdw_cdns * cdns,int offset)2139402e25dSPierre-Louis Bossart static inline u32 cdns_ip_readl(struct sdw_cdns *cdns, int offset)
2149402e25dSPierre-Louis Bossart {
2159402e25dSPierre-Louis Bossart 	return cdns_readl(cdns, cdns->ip_offset + offset);
2169402e25dSPierre-Louis Bossart }
2179402e25dSPierre-Louis Bossart 
cdns_ip_writel(struct sdw_cdns * cdns,int offset,u32 value)2189402e25dSPierre-Louis Bossart static inline void cdns_ip_writel(struct sdw_cdns *cdns, int offset, u32 value)
2199402e25dSPierre-Louis Bossart {
2209402e25dSPierre-Louis Bossart 	return cdns_writel(cdns, cdns->ip_offset + offset, value);
2219402e25dSPierre-Louis Bossart }
2229402e25dSPierre-Louis Bossart 
cdns_updatel(struct sdw_cdns * cdns,int offset,u32 mask,u32 val)2232f52a517SVinod Koul static inline void cdns_updatel(struct sdw_cdns *cdns,
2242f52a517SVinod Koul 				int offset, u32 mask, u32 val)
2252f52a517SVinod Koul {
2262f52a517SVinod Koul 	u32 tmp;
2272f52a517SVinod Koul 
2282f52a517SVinod Koul 	tmp = cdns_readl(cdns, offset);
2292f52a517SVinod Koul 	tmp = (tmp & ~mask) | val;
2302f52a517SVinod Koul 	cdns_writel(cdns, offset, tmp);
2312f52a517SVinod Koul }
2322f52a517SVinod Koul 
cdns_ip_updatel(struct sdw_cdns * cdns,int offset,u32 mask,u32 val)2339402e25dSPierre-Louis Bossart static inline void cdns_ip_updatel(struct sdw_cdns *cdns,
2349402e25dSPierre-Louis Bossart 				   int offset, u32 mask, u32 val)
2359402e25dSPierre-Louis Bossart {
2369402e25dSPierre-Louis Bossart 	cdns_updatel(cdns, cdns->ip_offset + offset, mask, val);
2379402e25dSPierre-Louis Bossart }
2389402e25dSPierre-Louis Bossart 
cdns_set_wait(struct sdw_cdns * cdns,int offset,u32 mask,u32 value)2391032504fSRander Wang static int cdns_set_wait(struct sdw_cdns *cdns, int offset, u32 mask, u32 value)
2401032504fSRander Wang {
2411032504fSRander Wang 	int timeout = 10;
2421032504fSRander Wang 	u32 reg_read;
2431032504fSRander Wang 
2441032504fSRander Wang 	/* Wait for bit to be set */
2451032504fSRander Wang 	do {
2461032504fSRander Wang 		reg_read = readl(cdns->registers + offset);
2471032504fSRander Wang 		if ((reg_read & mask) == value)
2481032504fSRander Wang 			return 0;
2491032504fSRander Wang 
2501032504fSRander Wang 		timeout--;
2511032504fSRander Wang 		usleep_range(50, 100);
2521032504fSRander Wang 	} while (timeout != 0);
2531032504fSRander Wang 
2541032504fSRander Wang 	return -ETIMEDOUT;
2551032504fSRander Wang }
2561032504fSRander Wang 
cdns_clear_bit(struct sdw_cdns * cdns,int offset,u32 value)25712632459SPierre-Louis Bossart static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value)
25812632459SPierre-Louis Bossart {
25912632459SPierre-Louis Bossart 	writel(value, cdns->registers + offset);
26012632459SPierre-Louis Bossart 
26112632459SPierre-Louis Bossart 	/* Wait for bit to be self cleared */
26212632459SPierre-Louis Bossart 	return cdns_set_wait(cdns, offset, value, 0);
26312632459SPierre-Louis Bossart }
26412632459SPierre-Louis Bossart 
2652f52a517SVinod Koul /*
26649ea07d3SPierre-Louis Bossart  * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL
26749ea07d3SPierre-Louis Bossart  * need to be confirmed with a write to MCP_CONFIG_UPDATE
26849ea07d3SPierre-Louis Bossart  */
cdns_config_update(struct sdw_cdns * cdns)269ce1acf01SPierre-Louis Bossart static int cdns_config_update(struct sdw_cdns *cdns)
27049ea07d3SPierre-Louis Bossart {
27149ea07d3SPierre-Louis Bossart 	int ret;
27249ea07d3SPierre-Louis Bossart 
2739bc87cceSPierre-Louis Bossart 	if (sdw_cdns_is_clock_stop(cdns)) {
2749bc87cceSPierre-Louis Bossart 		dev_err(cdns->dev, "Cannot program MCP_CONFIG_UPDATE in ClockStopMode\n");
2759bc87cceSPierre-Louis Bossart 		return -EINVAL;
2769bc87cceSPierre-Louis Bossart 	}
2779bc87cceSPierre-Louis Bossart 
27849ea07d3SPierre-Louis Bossart 	ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
27949ea07d3SPierre-Louis Bossart 			     CDNS_MCP_CONFIG_UPDATE_BIT);
28049ea07d3SPierre-Louis Bossart 	if (ret < 0)
28149ea07d3SPierre-Louis Bossart 		dev_err(cdns->dev, "Config update timedout\n");
28249ea07d3SPierre-Louis Bossart 
28349ea07d3SPierre-Louis Bossart 	return ret;
28449ea07d3SPierre-Louis Bossart }
28549ea07d3SPierre-Louis Bossart 
286ffc363d9SPierre-Louis Bossart /**
287ffc363d9SPierre-Louis Bossart  * sdw_cdns_config_update() - Update configurations
288ffc363d9SPierre-Louis Bossart  * @cdns: Cadence instance
289ffc363d9SPierre-Louis Bossart  */
sdw_cdns_config_update(struct sdw_cdns * cdns)290ffc363d9SPierre-Louis Bossart void sdw_cdns_config_update(struct sdw_cdns *cdns)
291ffc363d9SPierre-Louis Bossart {
292ffc363d9SPierre-Louis Bossart 	/* commit changes */
293ffc363d9SPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT);
294ffc363d9SPierre-Louis Bossart }
295ffc363d9SPierre-Louis Bossart EXPORT_SYMBOL(sdw_cdns_config_update);
296ffc363d9SPierre-Louis Bossart 
297ffc363d9SPierre-Louis Bossart /**
298ffc363d9SPierre-Louis Bossart  * sdw_cdns_config_update_set_wait() - wait until configuration update bit is self-cleared
299ffc363d9SPierre-Louis Bossart  * @cdns: Cadence instance
300ffc363d9SPierre-Louis Bossart  */
sdw_cdns_config_update_set_wait(struct sdw_cdns * cdns)301ffc363d9SPierre-Louis Bossart int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns)
302ffc363d9SPierre-Louis Bossart {
303ffc363d9SPierre-Louis Bossart 	/* the hardware recommendation is to wait at least 300us */
304ffc363d9SPierre-Louis Bossart 	return cdns_set_wait(cdns, CDNS_MCP_CONFIG_UPDATE,
305ffc363d9SPierre-Louis Bossart 			     CDNS_MCP_CONFIG_UPDATE_BIT, 0);
306ffc363d9SPierre-Louis Bossart }
307ffc363d9SPierre-Louis Bossart EXPORT_SYMBOL(sdw_cdns_config_update_set_wait);
308ffc363d9SPierre-Louis Bossart 
30949ea07d3SPierre-Louis Bossart /*
310aa85066eSPierre-Louis Bossart  * debugfs
311aa85066eSPierre-Louis Bossart  */
312aa85066eSPierre-Louis Bossart #ifdef CONFIG_DEBUG_FS
313aa85066eSPierre-Louis Bossart 
314aa85066eSPierre-Louis Bossart #define RD_BUF (2 * PAGE_SIZE)
315aa85066eSPierre-Louis Bossart 
cdns_sprintf(struct sdw_cdns * cdns,char * buf,size_t pos,unsigned int reg)316aa85066eSPierre-Louis Bossart static ssize_t cdns_sprintf(struct sdw_cdns *cdns,
317aa85066eSPierre-Louis Bossart 			    char *buf, size_t pos, unsigned int reg)
318aa85066eSPierre-Louis Bossart {
319aa85066eSPierre-Louis Bossart 	return scnprintf(buf + pos, RD_BUF - pos,
320aa85066eSPierre-Louis Bossart 			 "%4x\t%8x\n", reg, cdns_readl(cdns, reg));
321aa85066eSPierre-Louis Bossart }
322aa85066eSPierre-Louis Bossart 
cdns_reg_show(struct seq_file * s,void * data)323aa85066eSPierre-Louis Bossart static int cdns_reg_show(struct seq_file *s, void *data)
324aa85066eSPierre-Louis Bossart {
325aa85066eSPierre-Louis Bossart 	struct sdw_cdns *cdns = s->private;
326aa85066eSPierre-Louis Bossart 	char *buf;
327aa85066eSPierre-Louis Bossart 	ssize_t ret;
328aa85066eSPierre-Louis Bossart 	int num_ports;
329aa85066eSPierre-Louis Bossart 	int i, j;
330aa85066eSPierre-Louis Bossart 
331aa85066eSPierre-Louis Bossart 	buf = kzalloc(RD_BUF, GFP_KERNEL);
332aa85066eSPierre-Louis Bossart 	if (!buf)
333aa85066eSPierre-Louis Bossart 		return -ENOMEM;
334aa85066eSPierre-Louis Bossart 
335aa85066eSPierre-Louis Bossart 	ret = scnprintf(buf, RD_BUF, "Register  Value\n");
336aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
337aa85066eSPierre-Louis Bossart 	/* 8 MCP registers */
338aa85066eSPierre-Louis Bossart 	for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32))
339aa85066eSPierre-Louis Bossart 		ret += cdns_sprintf(cdns, buf, ret, i);
340aa85066eSPierre-Louis Bossart 
341aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
342aa85066eSPierre-Louis Bossart 			 "\nStatus & Intr Registers\n");
343aa85066eSPierre-Louis Bossart 	/* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */
344aa85066eSPierre-Louis Bossart 	for (i = CDNS_MCP_STAT; i <=  CDNS_MCP_FIFOSTAT; i += sizeof(u32))
345aa85066eSPierre-Louis Bossart 		ret += cdns_sprintf(cdns, buf, ret, i);
346aa85066eSPierre-Louis Bossart 
347aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
348aa85066eSPierre-Louis Bossart 			 "\nSSP & Clk ctrl Registers\n");
349aa85066eSPierre-Louis Bossart 	ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
350aa85066eSPierre-Louis Bossart 	ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
351aa85066eSPierre-Louis Bossart 	ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
352aa85066eSPierre-Louis Bossart 	ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
353aa85066eSPierre-Louis Bossart 
354aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
355aa85066eSPierre-Louis Bossart 			 "\nDPn B0 Registers\n");
356aa85066eSPierre-Louis Bossart 
357807c15bcSPierre-Louis Bossart 	num_ports = cdns->num_ports;
358aa85066eSPierre-Louis Bossart 
359aa85066eSPierre-Louis Bossart 	for (i = 0; i < num_ports; i++) {
360aa85066eSPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret,
361aa85066eSPierre-Louis Bossart 				 "\nDP-%d\n", i);
362aa85066eSPierre-Louis Bossart 		for (j = CDNS_DPN_B0_CONFIG(i);
363aa85066eSPierre-Louis Bossart 		     j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32))
364aa85066eSPierre-Louis Bossart 			ret += cdns_sprintf(cdns, buf, ret, j);
365aa85066eSPierre-Louis Bossart 	}
366aa85066eSPierre-Louis Bossart 
367aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
368aa85066eSPierre-Louis Bossart 			 "\nDPn B1 Registers\n");
369aa85066eSPierre-Louis Bossart 	for (i = 0; i < num_ports; i++) {
370aa85066eSPierre-Louis Bossart 		ret += scnprintf(buf + ret, RD_BUF - ret,
371aa85066eSPierre-Louis Bossart 				 "\nDP-%d\n", i);
372aa85066eSPierre-Louis Bossart 
373aa85066eSPierre-Louis Bossart 		for (j = CDNS_DPN_B1_CONFIG(i);
374aa85066eSPierre-Louis Bossart 		     j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32))
375aa85066eSPierre-Louis Bossart 			ret += cdns_sprintf(cdns, buf, ret, j);
376aa85066eSPierre-Louis Bossart 	}
377aa85066eSPierre-Louis Bossart 
378aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
379aa85066eSPierre-Louis Bossart 			 "\nDPn Control Registers\n");
380aa85066eSPierre-Louis Bossart 	for (i = 0; i < num_ports; i++)
381aa85066eSPierre-Louis Bossart 		ret += cdns_sprintf(cdns, buf, ret,
382aa85066eSPierre-Louis Bossart 				CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
383aa85066eSPierre-Louis Bossart 
384aa85066eSPierre-Louis Bossart 	ret += scnprintf(buf + ret, RD_BUF - ret,
385aa85066eSPierre-Louis Bossart 			 "\nPDIn Config Registers\n");
386aa85066eSPierre-Louis Bossart 
387aa85066eSPierre-Louis Bossart 	/* number of PDI and ports is interchangeable */
388aa85066eSPierre-Louis Bossart 	for (i = 0; i < num_ports; i++)
389aa85066eSPierre-Louis Bossart 		ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i));
390aa85066eSPierre-Louis Bossart 
391aa85066eSPierre-Louis Bossart 	seq_printf(s, "%s", buf);
392aa85066eSPierre-Louis Bossart 	kfree(buf);
393aa85066eSPierre-Louis Bossart 
394aa85066eSPierre-Louis Bossart 	return 0;
395aa85066eSPierre-Louis Bossart }
396aa85066eSPierre-Louis Bossart DEFINE_SHOW_ATTRIBUTE(cdns_reg);
397aa85066eSPierre-Louis Bossart 
cdns_hw_reset(void * data,u64 value)398675d4c9aSPierre-Louis Bossart static int cdns_hw_reset(void *data, u64 value)
399675d4c9aSPierre-Louis Bossart {
400675d4c9aSPierre-Louis Bossart 	struct sdw_cdns *cdns = data;
401675d4c9aSPierre-Louis Bossart 	int ret;
402675d4c9aSPierre-Louis Bossart 
403675d4c9aSPierre-Louis Bossart 	if (value != 1)
404675d4c9aSPierre-Louis Bossart 		return -EINVAL;
405675d4c9aSPierre-Louis Bossart 
406675d4c9aSPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
407675d4c9aSPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
408675d4c9aSPierre-Louis Bossart 
409675d4c9aSPierre-Louis Bossart 	ret = sdw_cdns_exit_reset(cdns);
410675d4c9aSPierre-Louis Bossart 
411675d4c9aSPierre-Louis Bossart 	dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret);
412675d4c9aSPierre-Louis Bossart 
413675d4c9aSPierre-Louis Bossart 	return ret;
414675d4c9aSPierre-Louis Bossart }
415675d4c9aSPierre-Louis Bossart 
416675d4c9aSPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
417675d4c9aSPierre-Louis Bossart 
cdns_parity_error_injection(void * data,u64 value)41832d2a893SPierre-Louis Bossart static int cdns_parity_error_injection(void *data, u64 value)
41932d2a893SPierre-Louis Bossart {
42032d2a893SPierre-Louis Bossart 	struct sdw_cdns *cdns = data;
42132d2a893SPierre-Louis Bossart 	struct sdw_bus *bus;
42232d2a893SPierre-Louis Bossart 	int ret;
42332d2a893SPierre-Louis Bossart 
42432d2a893SPierre-Louis Bossart 	if (value != 1)
42532d2a893SPierre-Louis Bossart 		return -EINVAL;
42632d2a893SPierre-Louis Bossart 
42732d2a893SPierre-Louis Bossart 	bus = &cdns->bus;
42832d2a893SPierre-Louis Bossart 
42932d2a893SPierre-Louis Bossart 	/*
43032d2a893SPierre-Louis Bossart 	 * Resume Master device. If this results in a bus reset, the
43132d2a893SPierre-Louis Bossart 	 * Slave devices will re-attach and be re-enumerated.
43232d2a893SPierre-Louis Bossart 	 */
433915bf27aSPierre-Louis Bossart 	ret = pm_runtime_resume_and_get(bus->dev);
43432d2a893SPierre-Louis Bossart 	if (ret < 0 && ret != -EACCES) {
43532d2a893SPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev,
436915bf27aSPierre-Louis Bossart 				    "pm_runtime_resume_and_get failed in %s, ret %d\n",
43732d2a893SPierre-Louis Bossart 				    __func__, ret);
43832d2a893SPierre-Louis Bossart 		return ret;
43932d2a893SPierre-Louis Bossart 	}
44032d2a893SPierre-Louis Bossart 
44132d2a893SPierre-Louis Bossart 	/*
44232d2a893SPierre-Louis Bossart 	 * wait long enough for Slave(s) to be in steady state. This
44332d2a893SPierre-Louis Bossart 	 * does not need to be super precise.
44432d2a893SPierre-Louis Bossart 	 */
44532d2a893SPierre-Louis Bossart 	msleep(200);
44632d2a893SPierre-Louis Bossart 
44732d2a893SPierre-Louis Bossart 	/*
44832d2a893SPierre-Louis Bossart 	 * Take the bus lock here to make sure that any bus transactions
44932d2a893SPierre-Louis Bossart 	 * will be queued while we inject a parity error on a dummy read
45032d2a893SPierre-Louis Bossart 	 */
45132d2a893SPierre-Louis Bossart 	mutex_lock(&bus->bus_lock);
45232d2a893SPierre-Louis Bossart 
45332d2a893SPierre-Louis Bossart 	/* program hardware to inject parity error */
45473a29d3fSPierre-Louis Bossart 	cdns_ip_updatel(cdns, CDNS_IP_MCP_CMDCTRL,
45573a29d3fSPierre-Louis Bossart 			CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR,
45673a29d3fSPierre-Louis Bossart 			CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR);
45732d2a893SPierre-Louis Bossart 
45832d2a893SPierre-Louis Bossart 	/* commit changes */
45994f89950SPierre-Louis Bossart 	ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT);
46094f89950SPierre-Louis Bossart 	if (ret < 0)
46194f89950SPierre-Louis Bossart 		goto unlock;
46232d2a893SPierre-Louis Bossart 
46332d2a893SPierre-Louis Bossart 	/* do a broadcast dummy read to avoid bus clashes */
46432d2a893SPierre-Louis Bossart 	ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0);
46532d2a893SPierre-Louis Bossart 	dev_info(cdns->dev, "parity error injection, read: %d\n", ret);
46632d2a893SPierre-Louis Bossart 
46732d2a893SPierre-Louis Bossart 	/* program hardware to disable parity error */
46873a29d3fSPierre-Louis Bossart 	cdns_ip_updatel(cdns, CDNS_IP_MCP_CMDCTRL,
46973a29d3fSPierre-Louis Bossart 			CDNS_IP_MCP_CMDCTRL_INSERT_PARITY_ERR,
47032d2a893SPierre-Louis Bossart 			0);
47132d2a893SPierre-Louis Bossart 
47232d2a893SPierre-Louis Bossart 	/* commit changes */
47394f89950SPierre-Louis Bossart 	ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT);
47494f89950SPierre-Louis Bossart 	if (ret < 0)
47594f89950SPierre-Louis Bossart 		goto unlock;
47632d2a893SPierre-Louis Bossart 
47732d2a893SPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
47832d2a893SPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
47932d2a893SPierre-Louis Bossart 
48094f89950SPierre-Louis Bossart unlock:
48194f89950SPierre-Louis Bossart 	/* Continue bus operation with parity error injection disabled */
48294f89950SPierre-Louis Bossart 	mutex_unlock(&bus->bus_lock);
48394f89950SPierre-Louis Bossart 
48432d2a893SPierre-Louis Bossart 	/*
48532d2a893SPierre-Louis Bossart 	 * allow Master device to enter pm_runtime suspend. This may
48632d2a893SPierre-Louis Bossart 	 * also result in Slave devices suspending.
48732d2a893SPierre-Louis Bossart 	 */
48832d2a893SPierre-Louis Bossart 	pm_runtime_mark_last_busy(bus->dev);
48932d2a893SPierre-Louis Bossart 	pm_runtime_put_autosuspend(bus->dev);
49032d2a893SPierre-Louis Bossart 
49132d2a893SPierre-Louis Bossart 	return 0;
49232d2a893SPierre-Louis Bossart }
49332d2a893SPierre-Louis Bossart 
49432d2a893SPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL,
49532d2a893SPierre-Louis Bossart 			 cdns_parity_error_injection, "%llu\n");
49632d2a893SPierre-Louis Bossart 
cdns_set_pdi_loopback_source(void * data,u64 value)4978fba8acdSPierre-Louis Bossart static int cdns_set_pdi_loopback_source(void *data, u64 value)
4988fba8acdSPierre-Louis Bossart {
4998fba8acdSPierre-Louis Bossart 	struct sdw_cdns *cdns = data;
5008fba8acdSPierre-Louis Bossart 	unsigned int pdi_out_num = cdns->pcm.num_bd + cdns->pcm.num_out;
5018fba8acdSPierre-Louis Bossart 
5028fba8acdSPierre-Louis Bossart 	if (value > pdi_out_num)
5038fba8acdSPierre-Louis Bossart 		return -EINVAL;
5048fba8acdSPierre-Louis Bossart 
5058fba8acdSPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
5068fba8acdSPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
5078fba8acdSPierre-Louis Bossart 
5088fba8acdSPierre-Louis Bossart 	cdns->pdi_loopback_source = value;
5098fba8acdSPierre-Louis Bossart 
5108fba8acdSPierre-Louis Bossart 	return 0;
5118fba8acdSPierre-Louis Bossart }
5128fba8acdSPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(cdns_pdi_loopback_source_fops, NULL, cdns_set_pdi_loopback_source, "%llu\n");
5138fba8acdSPierre-Louis Bossart 
cdns_set_pdi_loopback_target(void * data,u64 value)5148fba8acdSPierre-Louis Bossart static int cdns_set_pdi_loopback_target(void *data, u64 value)
5158fba8acdSPierre-Louis Bossart {
5168fba8acdSPierre-Louis Bossart 	struct sdw_cdns *cdns = data;
5178fba8acdSPierre-Louis Bossart 	unsigned int pdi_in_num = cdns->pcm.num_bd + cdns->pcm.num_in;
5188fba8acdSPierre-Louis Bossart 
5198fba8acdSPierre-Louis Bossart 	if (value > pdi_in_num)
5208fba8acdSPierre-Louis Bossart 		return -EINVAL;
5218fba8acdSPierre-Louis Bossart 
5228fba8acdSPierre-Louis Bossart 	/* Userspace changed the hardware state behind the kernel's back */
5238fba8acdSPierre-Louis Bossart 	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
5248fba8acdSPierre-Louis Bossart 
5258fba8acdSPierre-Louis Bossart 	cdns->pdi_loopback_target = value;
5268fba8acdSPierre-Louis Bossart 
5278fba8acdSPierre-Louis Bossart 	return 0;
5288fba8acdSPierre-Louis Bossart }
5298fba8acdSPierre-Louis Bossart DEFINE_DEBUGFS_ATTRIBUTE(cdns_pdi_loopback_target_fops, NULL, cdns_set_pdi_loopback_target, "%llu\n");
5308fba8acdSPierre-Louis Bossart 
531aa85066eSPierre-Louis Bossart /**
532aa85066eSPierre-Louis Bossart  * sdw_cdns_debugfs_init() - Cadence debugfs init
533aa85066eSPierre-Louis Bossart  * @cdns: Cadence instance
534aa85066eSPierre-Louis Bossart  * @root: debugfs root
535aa85066eSPierre-Louis Bossart  */
sdw_cdns_debugfs_init(struct sdw_cdns * cdns,struct dentry * root)536aa85066eSPierre-Louis Bossart void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root)
537aa85066eSPierre-Louis Bossart {
538aa85066eSPierre-Louis Bossart 	debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
539675d4c9aSPierre-Louis Bossart 
540675d4c9aSPierre-Louis Bossart 	debugfs_create_file("cdns-hw-reset", 0200, root, cdns,
541675d4c9aSPierre-Louis Bossart 			    &cdns_hw_reset_fops);
54232d2a893SPierre-Louis Bossart 
54332d2a893SPierre-Louis Bossart 	debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns,
54432d2a893SPierre-Louis Bossart 			    &cdns_parity_error_fops);
5458fba8acdSPierre-Louis Bossart 
5468fba8acdSPierre-Louis Bossart 	cdns->pdi_loopback_source = -1;
5478fba8acdSPierre-Louis Bossart 	cdns->pdi_loopback_target = -1;
5488fba8acdSPierre-Louis Bossart 
5498fba8acdSPierre-Louis Bossart 	debugfs_create_file("cdns-pdi-loopback-source", 0200, root, cdns,
5508fba8acdSPierre-Louis Bossart 			    &cdns_pdi_loopback_source_fops);
5518fba8acdSPierre-Louis Bossart 
5528fba8acdSPierre-Louis Bossart 	debugfs_create_file("cdns-pdi-loopback-target", 0200, root, cdns,
5538fba8acdSPierre-Louis Bossart 			    &cdns_pdi_loopback_target_fops);
5548fba8acdSPierre-Louis Bossart 
555aa85066eSPierre-Louis Bossart }
556aa85066eSPierre-Louis Bossart EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
557aa85066eSPierre-Louis Bossart 
558aa85066eSPierre-Louis Bossart #endif /* CONFIG_DEBUG_FS */
559aa85066eSPierre-Louis Bossart 
560aa85066eSPierre-Louis Bossart /*
561956baa19SSanyog Kale  * IO Calls
562956baa19SSanyog Kale  */
563bbb63817SPierre-Louis Bossart static enum sdw_command_response
cdns_fill_msg_resp(struct sdw_cdns * cdns,struct sdw_msg * msg,int count,int offset)564bbb63817SPierre-Louis Bossart cdns_fill_msg_resp(struct sdw_cdns *cdns,
565956baa19SSanyog Kale 		   struct sdw_msg *msg, int count, int offset)
566956baa19SSanyog Kale {
567956baa19SSanyog Kale 	int nack = 0, no_ack = 0;
568956baa19SSanyog Kale 	int i;
569956baa19SSanyog Kale 
570956baa19SSanyog Kale 	/* check message response */
571956baa19SSanyog Kale 	for (i = 0; i < count; i++) {
572956baa19SSanyog Kale 		if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) {
573956baa19SSanyog Kale 			no_ack = 1;
5749a0c798cSPierre-Louis Bossart 			dev_vdbg(cdns->dev, "Msg Ack not received, cmd %d\n", i);
575db9d9f94SPierre-Louis Bossart 		}
576956baa19SSanyog Kale 		if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) {
577956baa19SSanyog Kale 			nack = 1;
5789a0c798cSPierre-Louis Bossart 			dev_err_ratelimited(cdns->dev, "Msg NACK received, cmd %d\n", i);
579956baa19SSanyog Kale 		}
580956baa19SSanyog Kale 	}
581956baa19SSanyog Kale 
582956baa19SSanyog Kale 	if (nack) {
583eb7df4c8SPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num);
584956baa19SSanyog Kale 		return SDW_CMD_FAIL;
5856f7219feSGuennadi Liakhovetski 	}
5866f7219feSGuennadi Liakhovetski 
5876f7219feSGuennadi Liakhovetski 	if (no_ack) {
588eb7df4c8SPierre-Louis Bossart 		dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num);
589956baa19SSanyog Kale 		return SDW_CMD_IGNORED;
590956baa19SSanyog Kale 	}
591956baa19SSanyog Kale 
592ba05b39dSRichard Fitzgerald 	if (msg->flags == SDW_MSG_FLAG_READ) {
593956baa19SSanyog Kale 		/* fill response */
594956baa19SSanyog Kale 		for (i = 0; i < count; i++)
595ba05b39dSRichard Fitzgerald 			msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA,
596ba05b39dSRichard Fitzgerald 							 cdns->response_buf[i]);
597ba05b39dSRichard Fitzgerald 	}
598956baa19SSanyog Kale 
599956baa19SSanyog Kale 	return SDW_CMD_OK;
600956baa19SSanyog Kale }
601956baa19SSanyog Kale 
cdns_read_response(struct sdw_cdns * cdns)6020603a47bSRichard Fitzgerald static void cdns_read_response(struct sdw_cdns *cdns)
6030603a47bSRichard Fitzgerald {
6040603a47bSRichard Fitzgerald 	u32 num_resp, cmd_base;
6050603a47bSRichard Fitzgerald 	int i;
6060603a47bSRichard Fitzgerald 
6070603a47bSRichard Fitzgerald 	/* RX_FIFO_AVAIL can be 2 entries more than the FIFO size */
6080603a47bSRichard Fitzgerald 	BUILD_BUG_ON(ARRAY_SIZE(cdns->response_buf) < CDNS_MCP_CMD_LEN + 2);
6090603a47bSRichard Fitzgerald 
6100603a47bSRichard Fitzgerald 	num_resp = cdns_readl(cdns, CDNS_MCP_FIFOSTAT);
6110603a47bSRichard Fitzgerald 	num_resp &= CDNS_MCP_RX_FIFO_AVAIL;
6120603a47bSRichard Fitzgerald 	if (num_resp > ARRAY_SIZE(cdns->response_buf)) {
6130603a47bSRichard Fitzgerald 		dev_warn(cdns->dev, "RX AVAIL %d too long\n", num_resp);
6140603a47bSRichard Fitzgerald 		num_resp = ARRAY_SIZE(cdns->response_buf);
6150603a47bSRichard Fitzgerald 	}
6160603a47bSRichard Fitzgerald 
61783ae1ccbSPierre-Louis Bossart 	cmd_base = CDNS_IP_MCP_CMD_BASE;
6180603a47bSRichard Fitzgerald 
6190603a47bSRichard Fitzgerald 	for (i = 0; i < num_resp; i++) {
62083ae1ccbSPierre-Louis Bossart 		cdns->response_buf[i] = cdns_ip_readl(cdns, cmd_base);
6210603a47bSRichard Fitzgerald 		cmd_base += CDNS_MCP_CMD_WORD_LEN;
6220603a47bSRichard Fitzgerald 	}
6230603a47bSRichard Fitzgerald }
6240603a47bSRichard Fitzgerald 
625956baa19SSanyog Kale static enum sdw_command_response
_cdns_xfer_msg(struct sdw_cdns * cdns,struct sdw_msg * msg,int cmd,int offset,int count,bool defer)626956baa19SSanyog Kale _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd,
627956baa19SSanyog Kale 	       int offset, int count, bool defer)
628956baa19SSanyog Kale {
629956baa19SSanyog Kale 	unsigned long time;
630956baa19SSanyog Kale 	u32 base, i, data;
631956baa19SSanyog Kale 	u16 addr;
632956baa19SSanyog Kale 
633956baa19SSanyog Kale 	/* Program the watermark level for RX FIFO */
634956baa19SSanyog Kale 	if (cdns->msg_count != count) {
635956baa19SSanyog Kale 		cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, count);
636956baa19SSanyog Kale 		cdns->msg_count = count;
637956baa19SSanyog Kale 	}
638956baa19SSanyog Kale 
63983ae1ccbSPierre-Louis Bossart 	base = CDNS_IP_MCP_CMD_BASE;
6403ed96fb4SRichard Fitzgerald 	addr = msg->addr + offset;
641956baa19SSanyog Kale 
642956baa19SSanyog Kale 	for (i = 0; i < count; i++) {
6433cf25d63SVinod Koul 		data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
6443cf25d63SVinod Koul 		data |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, cmd);
6453cf25d63SVinod Koul 		data |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, addr);
6463cf25d63SVinod Koul 		addr++;
647956baa19SSanyog Kale 
648956baa19SSanyog Kale 		if (msg->flags == SDW_MSG_FLAG_WRITE)
649956baa19SSanyog Kale 			data |= msg->buf[i + offset];
650956baa19SSanyog Kale 
6513cf25d63SVinod Koul 		data |= FIELD_PREP(CDNS_MCP_CMD_SSP_TAG, msg->ssp_sync);
65283ae1ccbSPierre-Louis Bossart 		cdns_ip_writel(cdns, base, data);
653956baa19SSanyog Kale 		base += CDNS_MCP_CMD_WORD_LEN;
654956baa19SSanyog Kale 	}
655956baa19SSanyog Kale 
656956baa19SSanyog Kale 	if (defer)
657956baa19SSanyog Kale 		return SDW_CMD_OK;
658956baa19SSanyog Kale 
659956baa19SSanyog Kale 	/* wait for timeout or response */
660956baa19SSanyog Kale 	time = wait_for_completion_timeout(&cdns->tx_complete,
661956baa19SSanyog Kale 					   msecs_to_jiffies(CDNS_TX_TIMEOUT));
662956baa19SSanyog Kale 	if (!time) {
66353ee9572SPierre-Louis Bossart 		dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n",
66453ee9572SPierre-Louis Bossart 			cmd, msg->dev_num, msg->addr, msg->len);
665956baa19SSanyog Kale 		msg->len = 0;
6660603a47bSRichard Fitzgerald 
6670603a47bSRichard Fitzgerald 		/* Drain anything in the RX_FIFO */
6680603a47bSRichard Fitzgerald 		cdns_read_response(cdns);
6690603a47bSRichard Fitzgerald 
670956baa19SSanyog Kale 		return SDW_CMD_TIMEOUT;
671956baa19SSanyog Kale 	}
672956baa19SSanyog Kale 
673956baa19SSanyog Kale 	return cdns_fill_msg_resp(cdns, msg, count, offset);
674956baa19SSanyog Kale }
675956baa19SSanyog Kale 
676bbb63817SPierre-Louis Bossart static enum sdw_command_response
cdns_program_scp_addr(struct sdw_cdns * cdns,struct sdw_msg * msg)677bbb63817SPierre-Louis Bossart cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg)
678956baa19SSanyog Kale {
679956baa19SSanyog Kale 	int nack = 0, no_ack = 0;
680956baa19SSanyog Kale 	unsigned long time;
681956baa19SSanyog Kale 	u32 data[2], base;
682956baa19SSanyog Kale 	int i;
683956baa19SSanyog Kale 
684956baa19SSanyog Kale 	/* Program the watermark level for RX FIFO */
685956baa19SSanyog Kale 	if (cdns->msg_count != CDNS_SCP_RX_FIFOLEVEL) {
686956baa19SSanyog Kale 		cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, CDNS_SCP_RX_FIFOLEVEL);
687956baa19SSanyog Kale 		cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL;
688956baa19SSanyog Kale 	}
689956baa19SSanyog Kale 
6903cf25d63SVinod Koul 	data[0] = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num);
6913cf25d63SVinod Koul 	data[0] |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, 0x3);
692956baa19SSanyog Kale 	data[1] = data[0];
693956baa19SSanyog Kale 
6943cf25d63SVinod Koul 	data[0] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE1);
6953cf25d63SVinod Koul 	data[1] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE2);
696956baa19SSanyog Kale 
697956baa19SSanyog Kale 	data[0] |= msg->addr_page1;
698956baa19SSanyog Kale 	data[1] |= msg->addr_page2;
699956baa19SSanyog Kale 
70083ae1ccbSPierre-Louis Bossart 	base = CDNS_IP_MCP_CMD_BASE;
70183ae1ccbSPierre-Louis Bossart 	cdns_ip_writel(cdns, base, data[0]);
702956baa19SSanyog Kale 	base += CDNS_MCP_CMD_WORD_LEN;
70383ae1ccbSPierre-Louis Bossart 	cdns_ip_writel(cdns, base, data[1]);
704956baa19SSanyog Kale 
705956baa19SSanyog Kale 	time = wait_for_completion_timeout(&cdns->tx_complete,
706956baa19SSanyog Kale 					   msecs_to_jiffies(CDNS_TX_TIMEOUT));
707956baa19SSanyog Kale 	if (!time) {
708956baa19SSanyog Kale 		dev_err(cdns->dev, "SCP Msg trf timed out\n");
709956baa19SSanyog Kale 		msg->len = 0;
710956baa19SSanyog Kale 		return SDW_CMD_TIMEOUT;
711956baa19SSanyog Kale 	}
712956baa19SSanyog Kale 
713956baa19SSanyog Kale 	/* check response the writes */
714956baa19SSanyog Kale 	for (i = 0; i < 2; i++) {
715956baa19SSanyog Kale 		if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) {
716956baa19SSanyog Kale 			no_ack = 1;
71717ed5befSPierre-Louis Bossart 			dev_err(cdns->dev, "Program SCP Ack not received\n");
718956baa19SSanyog Kale 			if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) {
719956baa19SSanyog Kale 				nack = 1;
72017ed5befSPierre-Louis Bossart 				dev_err(cdns->dev, "Program SCP NACK received\n");
721956baa19SSanyog Kale 			}
722956baa19SSanyog Kale 		}
723956baa19SSanyog Kale 	}
724956baa19SSanyog Kale 
725956baa19SSanyog Kale 	/* For NACK, NO ack, don't return err if we are in Broadcast mode */
726956baa19SSanyog Kale 	if (nack) {
727eb7df4c8SPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev,
72817ed5befSPierre-Louis Bossart 				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
729956baa19SSanyog Kale 		return SDW_CMD_FAIL;
7306f7219feSGuennadi Liakhovetski 	}
7316f7219feSGuennadi Liakhovetski 
7326f7219feSGuennadi Liakhovetski 	if (no_ack) {
733eb7df4c8SPierre-Louis Bossart 		dev_dbg_ratelimited(cdns->dev,
73417ed5befSPierre-Louis Bossart 				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
735956baa19SSanyog Kale 		return SDW_CMD_IGNORED;
736956baa19SSanyog Kale 	}
737956baa19SSanyog Kale 
738956baa19SSanyog Kale 	return SDW_CMD_OK;
739956baa19SSanyog Kale }
740956baa19SSanyog Kale 
cdns_prep_msg(struct sdw_cdns * cdns,struct sdw_msg * msg,int * cmd)741956baa19SSanyog Kale static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd)
742956baa19SSanyog Kale {
743956baa19SSanyog Kale 	int ret;
744956baa19SSanyog Kale 
745956baa19SSanyog Kale 	if (msg->page) {
746956baa19SSanyog Kale 		ret = cdns_program_scp_addr(cdns, msg);
747956baa19SSanyog Kale 		if (ret) {
748956baa19SSanyog Kale 			msg->len = 0;
749956baa19SSanyog Kale 			return ret;
750956baa19SSanyog Kale 		}
751956baa19SSanyog Kale 	}
752956baa19SSanyog Kale 
753956baa19SSanyog Kale 	switch (msg->flags) {
754956baa19SSanyog Kale 	case SDW_MSG_FLAG_READ:
755956baa19SSanyog Kale 		*cmd = CDNS_MCP_CMD_READ;
756956baa19SSanyog Kale 		break;
757956baa19SSanyog Kale 
758956baa19SSanyog Kale 	case SDW_MSG_FLAG_WRITE:
759956baa19SSanyog Kale 		*cmd = CDNS_MCP_CMD_WRITE;
760956baa19SSanyog Kale 		break;
761956baa19SSanyog Kale 
762956baa19SSanyog Kale 	default:
763956baa19SSanyog Kale 		dev_err(cdns->dev, "Invalid msg cmd: %d\n", msg->flags);
764956baa19SSanyog Kale 		return -EINVAL;
765956baa19SSanyog Kale 	}
766956baa19SSanyog Kale 
767956baa19SSanyog Kale 	return 0;
768956baa19SSanyog Kale }
769956baa19SSanyog Kale 
770c91605f4SShreyas NC enum sdw_command_response
cdns_xfer_msg(struct sdw_bus * bus,struct sdw_msg * msg)771956baa19SSanyog Kale cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
772956baa19SSanyog Kale {
773956baa19SSanyog Kale 	struct sdw_cdns *cdns = bus_to_cdns(bus);
774956baa19SSanyog Kale 	int cmd = 0, ret, i;
775956baa19SSanyog Kale 
776956baa19SSanyog Kale 	ret = cdns_prep_msg(cdns, msg, &cmd);
777956baa19SSanyog Kale 	if (ret)
778956baa19SSanyog Kale 		return SDW_CMD_FAIL_OTHER;
779956baa19SSanyog Kale 
780956baa19SSanyog Kale 	for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) {
781956baa19SSanyog Kale 		ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
782956baa19SSanyog Kale 				     CDNS_MCP_CMD_LEN, false);
7837f6bad4dSRichard Fitzgerald 		if (ret != SDW_CMD_OK)
784bafb1eacSRichard Fitzgerald 			return ret;
785956baa19SSanyog Kale 	}
786956baa19SSanyog Kale 
787956baa19SSanyog Kale 	if (!(msg->len % CDNS_MCP_CMD_LEN))
788bafb1eacSRichard Fitzgerald 		return SDW_CMD_OK;
789956baa19SSanyog Kale 
790bafb1eacSRichard Fitzgerald 	return _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN,
791956baa19SSanyog Kale 			      msg->len % CDNS_MCP_CMD_LEN, false);
792956baa19SSanyog Kale }
793c91605f4SShreyas NC EXPORT_SYMBOL(cdns_xfer_msg);
794956baa19SSanyog Kale 
795c91605f4SShreyas NC enum sdw_command_response
cdns_xfer_msg_defer(struct sdw_bus * bus)79666f95de7SPierre-Louis Bossart cdns_xfer_msg_defer(struct sdw_bus *bus)
797956baa19SSanyog Kale {
798956baa19SSanyog Kale 	struct sdw_cdns *cdns = bus_to_cdns(bus);
79966f95de7SPierre-Louis Bossart 	struct sdw_defer *defer = &bus->defer_msg;
80066f95de7SPierre-Louis Bossart 	struct sdw_msg *msg = defer->msg;
801956baa19SSanyog Kale 	int cmd = 0, ret;
802956baa19SSanyog Kale 
803956baa19SSanyog Kale 	/* for defer only 1 message is supported */
804956baa19SSanyog Kale 	if (msg->len > 1)
805956baa19SSanyog Kale 		return -ENOTSUPP;
806956baa19SSanyog Kale 
807956baa19SSanyog Kale 	ret = cdns_prep_msg(cdns, msg, &cmd);
808956baa19SSanyog Kale 	if (ret)
809956baa19SSanyog Kale 		return SDW_CMD_FAIL_OTHER;
810956baa19SSanyog Kale 
811956baa19SSanyog Kale 	return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true);
812956baa19SSanyog Kale }
813c91605f4SShreyas NC EXPORT_SYMBOL(cdns_xfer_msg_defer);
814956baa19SSanyog Kale 
cdns_read_ping_status(struct sdw_bus * bus)815133547a1SPierre-Louis Bossart u32 cdns_read_ping_status(struct sdw_bus *bus)
816133547a1SPierre-Louis Bossart {
817133547a1SPierre-Louis Bossart 	struct sdw_cdns *cdns = bus_to_cdns(bus);
818133547a1SPierre-Louis Bossart 
819133547a1SPierre-Louis Bossart 	return cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
820133547a1SPierre-Louis Bossart }
821133547a1SPierre-Louis Bossart EXPORT_SYMBOL(cdns_read_ping_status);
822133547a1SPierre-Louis Bossart 
823956baa19SSanyog Kale /*
8242f52a517SVinod Koul  * IRQ handling
8252f52a517SVinod Koul  */
8262f52a517SVinod Koul 
cdns_update_slave_status(struct sdw_cdns * cdns,u64 slave_intstat)8272f52a517SVinod Koul static int cdns_update_slave_status(struct sdw_cdns *cdns,
8286f206833SPierre-Louis Bossart 				    u64 slave_intstat)
8292f52a517SVinod Koul {
8302f52a517SVinod Koul 	enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
8312f52a517SVinod Koul 	bool is_slave = false;
832a78b32d9SPierre-Louis Bossart 	u32 mask;
833fbbc73a2SSimon Trimmer 	u32 val;
8342f52a517SVinod Koul 	int i, set_status;
8352f52a517SVinod Koul 
8362f52a517SVinod Koul 	memset(status, 0, sizeof(status));
8372f52a517SVinod Koul 
8382f52a517SVinod Koul 	for (i = 0; i <= SDW_MAX_DEVICES; i++) {
8396f206833SPierre-Louis Bossart 		mask = (slave_intstat >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) &
8402f52a517SVinod Koul 			CDNS_MCP_SLAVE_STATUS_BITS;
8412f52a517SVinod Koul 
8422f52a517SVinod Koul 		set_status = 0;
8432f52a517SVinod Koul 
844fbbc73a2SSimon Trimmer 		if (mask) {
845fbbc73a2SSimon Trimmer 			is_slave = true;
846fbbc73a2SSimon Trimmer 
8472f52a517SVinod Koul 			if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
8482f52a517SVinod Koul 				status[i] = SDW_SLAVE_RESERVED;
8492f52a517SVinod Koul 				set_status++;
8502f52a517SVinod Koul 			}
8512f52a517SVinod Koul 
8522f52a517SVinod Koul 			if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) {
8532f52a517SVinod Koul 				status[i] = SDW_SLAVE_ATTACHED;
8542f52a517SVinod Koul 				set_status++;
8552f52a517SVinod Koul 			}
8562f52a517SVinod Koul 
8572f52a517SVinod Koul 			if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) {
8582f52a517SVinod Koul 				status[i] = SDW_SLAVE_ALERT;
8592f52a517SVinod Koul 				set_status++;
8602f52a517SVinod Koul 			}
8612f52a517SVinod Koul 
8622f52a517SVinod Koul 			if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) {
8632f52a517SVinod Koul 				status[i] = SDW_SLAVE_UNATTACHED;
8642f52a517SVinod Koul 				set_status++;
8652f52a517SVinod Koul 			}
866fbbc73a2SSimon Trimmer 		}
8672f52a517SVinod Koul 
868fbbc73a2SSimon Trimmer 		/*
869fbbc73a2SSimon Trimmer 		 * check that there was a single reported Slave status and when
870fbbc73a2SSimon Trimmer 		 * there is not use the latest status extracted from PING commands
871fbbc73a2SSimon Trimmer 		 */
872fbbc73a2SSimon Trimmer 		if (set_status != 1) {
8737181b1d4SPierre-Louis Bossart 			val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
8747181b1d4SPierre-Louis Bossart 			val >>= (i * 2);
8757181b1d4SPierre-Louis Bossart 
8767181b1d4SPierre-Louis Bossart 			switch (val & 0x3) {
8777181b1d4SPierre-Louis Bossart 			case 0:
8787181b1d4SPierre-Louis Bossart 				status[i] = SDW_SLAVE_UNATTACHED;
8797181b1d4SPierre-Louis Bossart 				break;
8807181b1d4SPierre-Louis Bossart 			case 1:
8817181b1d4SPierre-Louis Bossart 				status[i] = SDW_SLAVE_ATTACHED;
8827181b1d4SPierre-Louis Bossart 				break;
8837181b1d4SPierre-Louis Bossart 			case 2:
8847181b1d4SPierre-Louis Bossart 				status[i] = SDW_SLAVE_ALERT;
8857181b1d4SPierre-Louis Bossart 				break;
8867181b1d4SPierre-Louis Bossart 			case 3:
8877181b1d4SPierre-Louis Bossart 			default:
8887181b1d4SPierre-Louis Bossart 				status[i] = SDW_SLAVE_RESERVED;
8897181b1d4SPierre-Louis Bossart 				break;
8907181b1d4SPierre-Louis Bossart 			}
8912f52a517SVinod Koul 		}
8922f52a517SVinod Koul 	}
8932f52a517SVinod Koul 
894*740a6059SPierre-Louis Bossart 	if (is_slave) {
895*740a6059SPierre-Louis Bossart 		int ret;
896*740a6059SPierre-Louis Bossart 
897*740a6059SPierre-Louis Bossart 		mutex_lock(&cdns->status_update_lock);
898*740a6059SPierre-Louis Bossart 		ret = sdw_handle_slave_status(&cdns->bus, status);
899*740a6059SPierre-Louis Bossart 		mutex_unlock(&cdns->status_update_lock);
900*740a6059SPierre-Louis Bossart 		return ret;
901*740a6059SPierre-Louis Bossart 	}
9022f52a517SVinod Koul 
9032f52a517SVinod Koul 	return 0;
9042f52a517SVinod Koul }
9052f52a517SVinod Koul 
9062f52a517SVinod Koul /**
9072f52a517SVinod Koul  * sdw_cdns_irq() - Cadence interrupt handler
9082f52a517SVinod Koul  * @irq: irq number
9092f52a517SVinod Koul  * @dev_id: irq context
9102f52a517SVinod Koul  */
sdw_cdns_irq(int irq,void * dev_id)9112f52a517SVinod Koul irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
9122f52a517SVinod Koul {
9132f52a517SVinod Koul 	struct sdw_cdns *cdns = dev_id;
9142f52a517SVinod Koul 	u32 int_status;
9152f52a517SVinod Koul 
9162f52a517SVinod Koul 	/* Check if the link is up */
9172f52a517SVinod Koul 	if (!cdns->link_up)
9182f52a517SVinod Koul 		return IRQ_NONE;
9192f52a517SVinod Koul 
9202f52a517SVinod Koul 	int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT);
9212f52a517SVinod Koul 
922a2cff9eeSPierre-Louis Bossart 	/* check for reserved values read as zero */
923a2cff9eeSPierre-Louis Bossart 	if (int_status & CDNS_MCP_INT_RESERVED)
924a2cff9eeSPierre-Louis Bossart 		return IRQ_NONE;
925a2cff9eeSPierre-Louis Bossart 
9262f52a517SVinod Koul 	if (!(int_status & CDNS_MCP_INT_IRQ))
9272f52a517SVinod Koul 		return IRQ_NONE;
9282f52a517SVinod Koul 
929956baa19SSanyog Kale 	if (int_status & CDNS_MCP_INT_RX_WL) {
930dd0b9619SPierre-Louis Bossart 		struct sdw_bus *bus = &cdns->bus;
931dd0b9619SPierre-Louis Bossart 		struct sdw_defer *defer = &bus->defer_msg;
932dd0b9619SPierre-Louis Bossart 
933956baa19SSanyog Kale 		cdns_read_response(cdns);
934956baa19SSanyog Kale 
935dd0b9619SPierre-Louis Bossart 		if (defer && defer->msg) {
936dd0b9619SPierre-Louis Bossart 			cdns_fill_msg_resp(cdns, defer->msg,
937dd0b9619SPierre-Louis Bossart 					   defer->length, 0);
938dd0b9619SPierre-Louis Bossart 			complete(&defer->complete);
939f6e20967SPierre-Louis Bossart 		} else {
940956baa19SSanyog Kale 			complete(&cdns->tx_complete);
941956baa19SSanyog Kale 		}
942f6e20967SPierre-Louis Bossart 	}
943956baa19SSanyog Kale 
9449b5884a0SPierre-Louis Bossart 	if (int_status & CDNS_MCP_INT_PARITY) {
9459b5884a0SPierre-Louis Bossart 		/* Parity error detected by Master */
9469b5884a0SPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev, "Parity error\n");
9479b5884a0SPierre-Louis Bossart 	}
9489b5884a0SPierre-Louis Bossart 
9492f52a517SVinod Koul 	if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
9502f52a517SVinod Koul 		/* Slave is driving bit slot during control word */
9512f52a517SVinod Koul 		dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
9522f52a517SVinod Koul 	}
9532f52a517SVinod Koul 
9542f52a517SVinod Koul 	if (int_status & CDNS_MCP_INT_DATA_CLASH) {
9552f52a517SVinod Koul 		/*
9562f52a517SVinod Koul 		 * Multiple slaves trying to drive bit slot, or issue with
9572f52a517SVinod Koul 		 * ownership of data bits or Slave gone bonkers
9582f52a517SVinod Koul 		 */
9592f52a517SVinod Koul 		dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
9602f52a517SVinod Koul 	}
9612f52a517SVinod Koul 
9629e4e6019SPierre-Louis Bossart 	if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL &&
9639e4e6019SPierre-Louis Bossart 	    int_status & CDNS_MCP_INT_DPINT) {
9649e4e6019SPierre-Louis Bossart 		u32 port_intstat;
9659e4e6019SPierre-Louis Bossart 
9669e4e6019SPierre-Louis Bossart 		/* just log which ports report an error */
9679e4e6019SPierre-Louis Bossart 		port_intstat = cdns_readl(cdns, CDNS_MCP_PORT_INTSTAT);
9689e4e6019SPierre-Louis Bossart 		dev_err_ratelimited(cdns->dev, "DP interrupt: PortIntStat %8x\n",
9699e4e6019SPierre-Louis Bossart 				    port_intstat);
9709e4e6019SPierre-Louis Bossart 
9719e4e6019SPierre-Louis Bossart 		/* clear status w/ write1 */
9729e4e6019SPierre-Louis Bossart 		cdns_writel(cdns, CDNS_MCP_PORT_INTSTAT, port_intstat);
9739e4e6019SPierre-Louis Bossart 	}
9749e4e6019SPierre-Louis Bossart 
9752f52a517SVinod Koul 	if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
9762f52a517SVinod Koul 		/* Mask the Slave interrupt and wake thread */
9772f52a517SVinod Koul 		cdns_updatel(cdns, CDNS_MCP_INTMASK,
9782f52a517SVinod Koul 			     CDNS_MCP_INT_SLAVE_MASK, 0);
9792f52a517SVinod Koul 
9802f52a517SVinod Koul 		int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
981d2068da5SPierre-Louis Bossart 
982d2068da5SPierre-Louis Bossart 		/*
983d2068da5SPierre-Louis Bossart 		 * Deal with possible race condition between interrupt
984d2068da5SPierre-Louis Bossart 		 * handling and disabling interrupts on suspend.
985d2068da5SPierre-Louis Bossart 		 *
986d2068da5SPierre-Louis Bossart 		 * If the master is in the process of disabling
987d2068da5SPierre-Louis Bossart 		 * interrupts, don't schedule a workqueue
988d2068da5SPierre-Louis Bossart 		 */
989d2068da5SPierre-Louis Bossart 		if (cdns->interrupt_enabled)
9904a98a6b2SBard Liao 			schedule_work(&cdns->work);
9912f52a517SVinod Koul 	}
9922f52a517SVinod Koul 
9932f52a517SVinod Koul 	cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
99400d3c2b3SPeter Ujfalusi 	return IRQ_HANDLED;
9952f52a517SVinod Koul }
9962f52a517SVinod Koul EXPORT_SYMBOL(sdw_cdns_irq);
9972f52a517SVinod Koul 
cdns_check_attached_status_dwork(struct work_struct * work)998*740a6059SPierre-Louis Bossart static void cdns_check_attached_status_dwork(struct work_struct *work)
999*740a6059SPierre-Louis Bossart {
1000*740a6059SPierre-Louis Bossart 	struct sdw_cdns *cdns =
1001*740a6059SPierre-Louis Bossart 		container_of(work, struct sdw_cdns, attach_dwork.work);
1002*740a6059SPierre-Louis Bossart 	enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
1003*740a6059SPierre-Louis Bossart 	u32 val;
1004*740a6059SPierre-Louis Bossart 	int ret;
1005*740a6059SPierre-Louis Bossart 	int i;
1006*740a6059SPierre-Louis Bossart 
1007*740a6059SPierre-Louis Bossart 	val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
1008*740a6059SPierre-Louis Bossart 
1009*740a6059SPierre-Louis Bossart 	for (i = 0; i <= SDW_MAX_DEVICES; i++) {
1010*740a6059SPierre-Louis Bossart 		status[i] = val & 0x3;
1011*740a6059SPierre-Louis Bossart 		if (status[i])
1012*740a6059SPierre-Louis Bossart 			dev_dbg(cdns->dev, "Peripheral %d status: %d\n", i, status[i]);
1013*740a6059SPierre-Louis Bossart 		val >>= 2;
1014*740a6059SPierre-Louis Bossart 	}
1015*740a6059SPierre-Louis Bossart 
1016*740a6059SPierre-Louis Bossart 	mutex_lock(&cdns->status_update_lock);
1017*740a6059SPierre-Louis Bossart 	ret = sdw_handle_slave_status(&cdns->bus, status);
1018*740a6059SPierre-Louis Bossart 	mutex_unlock(&cdns->status_update_lock);
1019*740a6059SPierre-Louis Bossart 	if (ret < 0)
1020*740a6059SPierre-Louis Bossart 		dev_err(cdns->dev, "%s: sdw_handle_slave_status failed: %d\n", __func__, ret);
1021*740a6059SPierre-Louis Bossart }
1022*740a6059SPierre-Louis Bossart 
10232f52a517SVinod Koul /**
1024b76f3fbaSPierre-Louis Bossart  * cdns_update_slave_status_work - update slave status in a work since we will need to handle
10254a98a6b2SBard Liao  * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave
10264a98a6b2SBard Liao  * process.
10274a98a6b2SBard Liao  * @work: cdns worker thread
10282f52a517SVinod Koul  */
cdns_update_slave_status_work(struct work_struct * work)10294a98a6b2SBard Liao static void cdns_update_slave_status_work(struct work_struct *work)
10302f52a517SVinod Koul {
10314a98a6b2SBard Liao 	struct sdw_cdns *cdns =
10324a98a6b2SBard Liao 		container_of(work, struct sdw_cdns, work);
10332f52a517SVinod Koul 	u32 slave0, slave1;
10346f206833SPierre-Louis Bossart 	u64 slave_intstat;
10353db50a99SPierre-Louis Bossart 	u32 device0_status;
10363db50a99SPierre-Louis Bossart 	int retry_count = 0;
10372f52a517SVinod Koul 
10380c5e99c4SRichard Fitzgerald 	/*
10390c5e99c4SRichard Fitzgerald 	 * Clear main interrupt first so we don't lose any assertions
10400c5e99c4SRichard Fitzgerald 	 * that happen during this function.
10410c5e99c4SRichard Fitzgerald 	 */
10420c5e99c4SRichard Fitzgerald 	cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK);
10430c5e99c4SRichard Fitzgerald 
10442f52a517SVinod Koul 	slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
10452f52a517SVinod Koul 	slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
10462f52a517SVinod Koul 
10470c5e99c4SRichard Fitzgerald 	/*
10480c5e99c4SRichard Fitzgerald 	 * Clear the bits before handling so we don't lose any
10490c5e99c4SRichard Fitzgerald 	 * bits that re-assert.
10500c5e99c4SRichard Fitzgerald 	 */
10510c5e99c4SRichard Fitzgerald 	cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0);
10520c5e99c4SRichard Fitzgerald 	cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1);
10530c5e99c4SRichard Fitzgerald 
10546f206833SPierre-Louis Bossart 	/* combine the two status */
10556f206833SPierre-Louis Bossart 	slave_intstat = ((u64)slave1 << 32) | slave0;
10566f206833SPierre-Louis Bossart 
10576f206833SPierre-Louis Bossart 	dev_dbg_ratelimited(cdns->dev, "Slave status change: 0x%llx\n", slave_intstat);
10586f206833SPierre-Louis Bossart 
10593db50a99SPierre-Louis Bossart update_status:
10606f206833SPierre-Louis Bossart 	cdns_update_slave_status(cdns, slave_intstat);
10612f52a517SVinod Koul 
10623db50a99SPierre-Louis Bossart 	/*
10633db50a99SPierre-Louis Bossart 	 * When there is more than one peripheral per link, it's
10643db50a99SPierre-Louis Bossart 	 * possible that a deviceB becomes attached after we deal with
10653db50a99SPierre-Louis Bossart 	 * the attachment of deviceA. Since the hardware does a
10663db50a99SPierre-Louis Bossart 	 * logical AND, the attachment of the second device does not
10673db50a99SPierre-Louis Bossart 	 * change the status seen by the driver.
10683db50a99SPierre-Louis Bossart 	 *
10693db50a99SPierre-Louis Bossart 	 * In that case, clearing the registers above would result in
10703db50a99SPierre-Louis Bossart 	 * the deviceB never being detected - until a change of status
10713db50a99SPierre-Louis Bossart 	 * is observed on the bus.
10723db50a99SPierre-Louis Bossart 	 *
10733db50a99SPierre-Louis Bossart 	 * To avoid this race condition, re-check if any device0 needs
10743db50a99SPierre-Louis Bossart 	 * attention with PING commands. There is no need to check for
10753db50a99SPierre-Louis Bossart 	 * ALERTS since they are not allowed until a non-zero
10763db50a99SPierre-Louis Bossart 	 * device_number is assigned.
10770c5e99c4SRichard Fitzgerald 	 *
10780c5e99c4SRichard Fitzgerald 	 * Do not clear the INTSTAT0/1. While looping to enumerate devices on
10790c5e99c4SRichard Fitzgerald 	 * #0 there could be status changes on other devices - these must
10800c5e99c4SRichard Fitzgerald 	 * be kept in the INTSTAT so they can be handled when all #0 devices
10810c5e99c4SRichard Fitzgerald 	 * have been handled.
10823db50a99SPierre-Louis Bossart 	 */
10833db50a99SPierre-Louis Bossart 
10843db50a99SPierre-Louis Bossart 	device0_status = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
10853db50a99SPierre-Louis Bossart 	device0_status &= 3;
10863db50a99SPierre-Louis Bossart 
10873db50a99SPierre-Louis Bossart 	if (device0_status == SDW_SLAVE_ATTACHED) {
10883db50a99SPierre-Louis Bossart 		if (retry_count++ < SDW_MAX_DEVICES) {
10893db50a99SPierre-Louis Bossart 			dev_dbg_ratelimited(cdns->dev,
10903db50a99SPierre-Louis Bossart 					    "Device0 detected after clearing status, iteration %d\n",
10913db50a99SPierre-Louis Bossart 					    retry_count);
10923db50a99SPierre-Louis Bossart 			slave_intstat = CDNS_MCP_SLAVE_INTSTAT_ATTACHED;
10933db50a99SPierre-Louis Bossart 			goto update_status;
10943db50a99SPierre-Louis Bossart 		} else {
10953db50a99SPierre-Louis Bossart 			dev_err_ratelimited(cdns->dev,
10963db50a99SPierre-Louis Bossart 					    "Device0 detected after %d iterations\n",
10973db50a99SPierre-Louis Bossart 					    retry_count);
10983db50a99SPierre-Louis Bossart 		}
10993db50a99SPierre-Louis Bossart 	}
11003db50a99SPierre-Louis Bossart 
11010c5e99c4SRichard Fitzgerald 	/* unmask Slave interrupt now */
11022f52a517SVinod Koul 	cdns_updatel(cdns, CDNS_MCP_INTMASK,
11032f52a517SVinod Koul 		     CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
11042f52a517SVinod Koul 
11052f52a517SVinod Koul }
11062f52a517SVinod Koul 
1107ff560946SPierre-Louis Bossart /* paranoia check to make sure self-cleared bits are indeed cleared */
sdw_cdns_check_self_clearing_bits(struct sdw_cdns * cdns,const char * string,bool initial_delay,int reset_iterations)1108ff560946SPierre-Louis Bossart void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
1109ff560946SPierre-Louis Bossart 				       bool initial_delay, int reset_iterations)
1110ff560946SPierre-Louis Bossart {
11114dc953bcSPierre-Louis Bossart 	u32 ip_mcp_control;
1112ff560946SPierre-Louis Bossart 	u32 mcp_control;
1113ff560946SPierre-Louis Bossart 	u32 mcp_config_update;
1114ff560946SPierre-Louis Bossart 	int i;
1115ff560946SPierre-Louis Bossart 
1116ff560946SPierre-Louis Bossart 	if (initial_delay)
1117ff560946SPierre-Louis Bossart 		usleep_range(1000, 1500);
1118ff560946SPierre-Louis Bossart 
11194dc953bcSPierre-Louis Bossart 	ip_mcp_control = cdns_ip_readl(cdns, CDNS_IP_MCP_CONTROL);
11204dc953bcSPierre-Louis Bossart 
11214dc953bcSPierre-Louis Bossart 	/* the following bits should be cleared immediately */
11224dc953bcSPierre-Louis Bossart 	if (ip_mcp_control & CDNS_IP_MCP_CONTROL_SW_RST)
11234dc953bcSPierre-Louis Bossart 		dev_err(cdns->dev, "%s failed: IP_MCP_CONTROL_SW_RST is not cleared\n", string);
11244dc953bcSPierre-Louis Bossart 
1125ff560946SPierre-Louis Bossart 	mcp_control = cdns_readl(cdns, CDNS_MCP_CONTROL);
1126ff560946SPierre-Louis Bossart 
1127ff560946SPierre-Louis Bossart 	/* the following bits should be cleared immediately */
1128ff560946SPierre-Louis Bossart 	if (mcp_control & CDNS_MCP_CONTROL_CMD_RST)
1129ff560946SPierre-Louis Bossart 		dev_err(cdns->dev, "%s failed: MCP_CONTROL_CMD_RST is not cleared\n", string);
1130ff560946SPierre-Louis Bossart 	if (mcp_control & CDNS_MCP_CONTROL_SOFT_RST)
1131ff560946SPierre-Louis Bossart 		dev_err(cdns->dev, "%s failed: MCP_CONTROL_SOFT_RST is not cleared\n", string);
1132ff560946SPierre-Louis Bossart 	if (mcp_control & CDNS_MCP_CONTROL_CLK_STOP_CLR)
1133ff560946SPierre-Louis Bossart 		dev_err(cdns->dev, "%s failed: MCP_CONTROL_CLK_STOP_CLR is not cleared\n", string);
11344dc953bcSPierre-Louis Bossart 
1135ff560946SPierre-Louis Bossart 	mcp_config_update = cdns_readl(cdns, CDNS_MCP_CONFIG_UPDATE);
1136ff560946SPierre-Louis Bossart 	if (mcp_config_update & CDNS_MCP_CONFIG_UPDATE_BIT)
1137ff560946SPierre-Louis Bossart 		dev_err(cdns->dev, "%s failed: MCP_CONFIG_UPDATE_BIT is not cleared\n", string);
1138ff560946SPierre-Louis Bossart 
1139ff560946SPierre-Louis Bossart 	i = 0;
1140ff560946SPierre-Louis Bossart 	while (mcp_control & CDNS_MCP_CONTROL_HW_RST) {
1141ff560946SPierre-Louis Bossart 		if (i == reset_iterations) {
1142ff560946SPierre-Louis Bossart 			dev_err(cdns->dev, "%s failed: MCP_CONTROL_HW_RST is not cleared\n", string);
1143ff560946SPierre-Louis Bossart 			break;
1144ff560946SPierre-Louis Bossart 		}
1145ff560946SPierre-Louis Bossart 
1146ff560946SPierre-Louis Bossart 		dev_dbg(cdns->dev, "%s: MCP_CONTROL_HW_RST is not cleared at iteration %d\n", string, i);
1147ff560946SPierre-Louis Bossart 		i++;
1148ff560946SPierre-Louis Bossart 
1149ff560946SPierre-Louis Bossart 		usleep_range(1000, 1500);
1150ff560946SPierre-Louis Bossart 		mcp_control = cdns_readl(cdns, CDNS_MCP_CONTROL);
1151ff560946SPierre-Louis Bossart 	}
1152ff560946SPierre-Louis Bossart 
1153ff560946SPierre-Louis Bossart }
1154ff560946SPierre-Louis Bossart EXPORT_SYMBOL(sdw_cdns_check_self_clearing_bits);
1155ff560946SPierre-Louis Bossart 
11562f52a517SVinod Koul /*
11572f52a517SVinod Koul  * init routines
11582f52a517SVinod Koul  */
115949ea07d3SPierre-Louis Bossart 
116049ea07d3SPierre-Louis Bossart /**
116149ea07d3SPierre-Louis Bossart  * sdw_cdns_exit_reset() - Program reset parameters and start bus operations
116249ea07d3SPierre-Louis Bossart  * @cdns: Cadence instance
116349ea07d3SPierre-Louis Bossart  */
sdw_cdns_exit_reset(struct sdw_cdns * cdns)116449ea07d3SPierre-Louis Bossart int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
116549ea07d3SPierre-Louis Bossart {
11662564a2d4SPierre-Louis Bossart 	/* keep reset delay unchanged to 4096 cycles */
116749ea07d3SPierre-Louis Bossart 
116849ea07d3SPierre-Louis Bossart 	/* use hardware generated reset */
116949ea07d3SPierre-Louis Bossart 	cdns_updatel(cdns, CDNS_MCP_CONTROL,
117049ea07d3SPierre-Louis Bossart 		     CDNS_MCP_CONTROL_HW_RST,
117149ea07d3SPierre-Louis Bossart 		     CDNS_MCP_CONTROL_HW_RST);
117249ea07d3SPierre-Louis Bossart 
117349ea07d3SPierre-Louis Bossart 	/* commit changes */
1174ffc363d9SPierre-Louis Bossart 	return cdns_config_update(cdns);
117549ea07d3SPierre-Louis Bossart }
117649ea07d3SPierre-Louis Bossart EXPORT_SYMBOL(sdw_cdns_exit_reset);
117749ea07d3SPierre-Louis Bossart 
117849ea07d3SPierre-Louis Bossart /**
1179b76f3fbaSPierre-Louis Bossart  * cdns_enable_slave_interrupts() - Enable SDW slave interrupts
1180af4cc917SPierre-Louis Bossart  * @cdns: Cadence instance
1181af4cc917SPierre-Louis Bossart  * @state: boolean for true/false
1182af4cc917SPierre-Louis Bossart  */
cdns_enable_slave_interrupts(struct sdw_cdns * cdns,bool state)1183af4cc917SPierre-Louis Bossart static void cdns_enable_slave_interrupts(struct sdw_cdns *cdns, bool state)
1184af4cc917SPierre-Louis Bossart {
1185af4cc917SPierre-Louis Bossart 	u32 mask;
1186af4cc917SPierre-Louis Bossart 
1187af4cc917SPierre-Louis Bossart 	mask = cdns_readl(cdns, CDNS_MCP_INTMASK);
1188af4cc917SPierre-Louis Bossart 	if (state)
1189af4cc917SPierre-Louis Bossart 		mask |= CDNS_MCP_INT_SLAVE_MASK;
1190af4cc917SPierre-Louis Bossart 	else
1191af4cc917SPierre-Louis Bossart 		mask &= ~CDNS_MCP_INT_SLAVE_MASK;
1192af4cc917SPierre-Louis Bossart 
1193af4cc917SPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
1194af4cc917SPierre-Louis Bossart }
1195af4cc917SPierre-Louis Bossart 
1196af4cc917SPierre-Louis Bossart /**
1197ae478d6eSRander Wang  * sdw_cdns_enable_interrupt() - Enable SDW interrupts
119849ea07d3SPierre-Louis Bossart  * @cdns: Cadence instance
1199550f9052SPierre-Louis Bossart  * @state: True if we are trying to enable interrupt.
120049ea07d3SPierre-Louis Bossart  */
sdw_cdns_enable_interrupt(struct sdw_cdns * cdns,bool state)12019e3d47fbSPierre-Louis Bossart int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
1202956baa19SSanyog Kale {
12039e3d47fbSPierre-Louis Bossart 	u32 slave_intmask0 = 0;
12049e3d47fbSPierre-Louis Bossart 	u32 slave_intmask1 = 0;
12059e3d47fbSPierre-Louis Bossart 	u32 mask = 0;
1206956baa19SSanyog Kale 
12079e3d47fbSPierre-Louis Bossart 	if (!state)
12089e3d47fbSPierre-Louis Bossart 		goto update_masks;
12099e3d47fbSPierre-Louis Bossart 
12109e3d47fbSPierre-Louis Bossart 	slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK;
12119e3d47fbSPierre-Louis Bossart 	slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK;
1212956baa19SSanyog Kale 
12139b5884a0SPierre-Louis Bossart 	/* enable detection of all slave state changes */
12149b5884a0SPierre-Louis Bossart 	mask = CDNS_MCP_INT_SLAVE_MASK;
12159b5884a0SPierre-Louis Bossart 
12169b5884a0SPierre-Louis Bossart 	/* enable detection of bus issues */
12179b5884a0SPierre-Louis Bossart 	mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
12189b5884a0SPierre-Louis Bossart 		CDNS_MCP_INT_PARITY;
12199b5884a0SPierre-Louis Bossart 
12209e4e6019SPierre-Louis Bossart 	/* port interrupt limited to test modes for now */
12219e4e6019SPierre-Louis Bossart 	if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL)
12229e4e6019SPierre-Louis Bossart 		mask |= CDNS_MCP_INT_DPINT;
12239b5884a0SPierre-Louis Bossart 
12249b5884a0SPierre-Louis Bossart 	/* enable detection of RX fifo level */
12259b5884a0SPierre-Louis Bossart 	mask |= CDNS_MCP_INT_RX_WL;
12269b5884a0SPierre-Louis Bossart 
12279b5884a0SPierre-Louis Bossart 	/*
12289b5884a0SPierre-Louis Bossart 	 * CDNS_MCP_INT_IRQ needs to be set otherwise all previous
12299b5884a0SPierre-Louis Bossart 	 * settings are irrelevant
12309b5884a0SPierre-Louis Bossart 	 */
12319b5884a0SPierre-Louis Bossart 	mask |= CDNS_MCP_INT_IRQ;
1232956baa19SSanyog Kale 
123304592dceSPierre-Louis Bossart 	if (interrupt_mask) /* parameter override */
123404592dceSPierre-Louis Bossart 		mask = interrupt_mask;
1235956baa19SSanyog Kale 
12369e3d47fbSPierre-Louis Bossart update_masks:
12375ebb0945SRander Wang 	/* clear slave interrupt status before enabling interrupt */
12385ebb0945SRander Wang 	if (state) {
12395ebb0945SRander Wang 		u32 slave_state;
12405ebb0945SRander Wang 
12415ebb0945SRander Wang 		slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0);
12425ebb0945SRander Wang 		cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state);
12435ebb0945SRander Wang 		slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
12445ebb0945SRander Wang 		cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state);
12455ebb0945SRander Wang 	}
1246d2068da5SPierre-Louis Bossart 	cdns->interrupt_enabled = state;
1247d2068da5SPierre-Louis Bossart 
1248d2068da5SPierre-Louis Bossart 	/*
1249d2068da5SPierre-Louis Bossart 	 * Complete any on-going status updates before updating masks,
1250d2068da5SPierre-Louis Bossart 	 * and cancel queued status updates.
1251d2068da5SPierre-Louis Bossart 	 *
1252d2068da5SPierre-Louis Bossart 	 * There could be a race with a new interrupt thrown before
1253d2068da5SPierre-Louis Bossart 	 * the 3 mask updates below are complete, so in the interrupt
1254d2068da5SPierre-Louis Bossart 	 * we use the 'interrupt_enabled' status to prevent new work
1255d2068da5SPierre-Louis Bossart 	 * from being queued.
1256d2068da5SPierre-Louis Bossart 	 */
1257d2068da5SPierre-Louis Bossart 	if (!state)
1258d2068da5SPierre-Louis Bossart 		cancel_work_sync(&cdns->work);
12595ebb0945SRander Wang 
12609e3d47fbSPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
12619e3d47fbSPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
1262956baa19SSanyog Kale 	cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
1263956baa19SSanyog Kale 
1264ae478d6eSRander Wang 	return 0;
1265956baa19SSanyog Kale }
1266956baa19SSanyog Kale EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
12672f52a517SVinod Koul 
cdns_allocate_pdi(struct sdw_cdns * cdns,struct sdw_cdns_pdi ** stream,u32 num,u32 pdi_offset)126807abeff1SVinod Koul static int cdns_allocate_pdi(struct sdw_cdns *cdns,
126907abeff1SVinod Koul 			     struct sdw_cdns_pdi **stream,
127007abeff1SVinod Koul 			     u32 num, u32 pdi_offset)
127107abeff1SVinod Koul {
127207abeff1SVinod Koul 	struct sdw_cdns_pdi *pdi;
127307abeff1SVinod Koul 	int i;
127407abeff1SVinod Koul 
127507abeff1SVinod Koul 	if (!num)
127607abeff1SVinod Koul 		return 0;
127707abeff1SVinod Koul 
127807abeff1SVinod Koul 	pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL);
127907abeff1SVinod Koul 	if (!pdi)
128007abeff1SVinod Koul 		return -ENOMEM;
128107abeff1SVinod Koul 
128207abeff1SVinod Koul 	for (i = 0; i < num; i++) {
128307abeff1SVinod Koul 		pdi[i].num = i + pdi_offset;
128407abeff1SVinod Koul 	}
128507abeff1SVinod Koul 
128607abeff1SVinod Koul 	*stream = pdi;
128707abeff1SVinod Koul 	return 0;
128807abeff1SVinod Koul }
128907abeff1SVinod Koul 
129007abeff1SVinod Koul /**
129107abeff1SVinod Koul  * sdw_cdns_pdi_init() - PDI initialization routine
129207abeff1SVinod Koul  *
129307abeff1SVinod Koul  * @cdns: Cadence instance
129407abeff1SVinod Koul  * @config: Stream configurations
129507abeff1SVinod Koul  */
sdw_cdns_pdi_init(struct sdw_cdns * cdns,struct sdw_cdns_stream_config config)129607abeff1SVinod Koul int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
129707abeff1SVinod Koul 		      struct sdw_cdns_stream_config config)
129807abeff1SVinod Koul {
129907abeff1SVinod Koul 	struct sdw_cdns_streams *stream;
130057a34790SPierre-Louis Bossart 	int offset;
130157a34790SPierre-Louis Bossart 	int ret;
130207abeff1SVinod Koul 
130307abeff1SVinod Koul 	cdns->pcm.num_bd = config.pcm_bd;
130407abeff1SVinod Koul 	cdns->pcm.num_in = config.pcm_in;
130507abeff1SVinod Koul 	cdns->pcm.num_out = config.pcm_out;
130607abeff1SVinod Koul 
130707abeff1SVinod Koul 	/* Allocate PDIs for PCMs */
130807abeff1SVinod Koul 	stream = &cdns->pcm;
130907abeff1SVinod Koul 
1310807c15bcSPierre-Louis Bossart 	/* we allocate PDI0 and PDI1 which are used for Bulk */
1311807c15bcSPierre-Louis Bossart 	offset = 0;
131207abeff1SVinod Koul 
131307abeff1SVinod Koul 	ret = cdns_allocate_pdi(cdns, &stream->bd,
131407abeff1SVinod Koul 				stream->num_bd, offset);
131507abeff1SVinod Koul 	if (ret)
131607abeff1SVinod Koul 		return ret;
131707abeff1SVinod Koul 
131807abeff1SVinod Koul 	offset += stream->num_bd;
131907abeff1SVinod Koul 
132007abeff1SVinod Koul 	ret = cdns_allocate_pdi(cdns, &stream->in,
132107abeff1SVinod Koul 				stream->num_in, offset);
132207abeff1SVinod Koul 	if (ret)
132307abeff1SVinod Koul 		return ret;
132407abeff1SVinod Koul 
132507abeff1SVinod Koul 	offset += stream->num_in;
132607abeff1SVinod Koul 
132707abeff1SVinod Koul 	ret = cdns_allocate_pdi(cdns, &stream->out,
132807abeff1SVinod Koul 				stream->num_out, offset);
132907abeff1SVinod Koul 	if (ret)
133007abeff1SVinod Koul 		return ret;
133107abeff1SVinod Koul 
133207abeff1SVinod Koul 	/* Update total number of PCM PDIs */
133307abeff1SVinod Koul 	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
133407abeff1SVinod Koul 	cdns->num_ports = stream->num_pdi;
133507abeff1SVinod Koul 
133607abeff1SVinod Koul 	return 0;
133707abeff1SVinod Koul }
133807abeff1SVinod Koul EXPORT_SYMBOL(sdw_cdns_pdi_init);
133907abeff1SVinod Koul 
cdns_set_initial_frame_shape(int n_rows,int n_cols)134005be59acSPierre-Louis Bossart static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols)
134105be59acSPierre-Louis Bossart {
134205be59acSPierre-Louis Bossart 	u32 val;
134305be59acSPierre-Louis Bossart 	int c;
134405be59acSPierre-Louis Bossart 	int r;
134505be59acSPierre-Louis Bossart 
134605be59acSPierre-Louis Bossart 	r = sdw_find_row_index(n_rows);
13473cf25d63SVinod Koul 	c = sdw_find_col_index(n_cols);
134805be59acSPierre-Louis Bossart 
13493cf25d63SVinod Koul 	val = FIELD_PREP(CDNS_MCP_FRAME_SHAPE_ROW_MASK, r);
13503cf25d63SVinod Koul 	val |= FIELD_PREP(CDNS_MCP_FRAME_SHAPE_COL_MASK, c);
135105be59acSPierre-Louis Bossart 
135205be59acSPierre-Louis Bossart 	return val;
135305be59acSPierre-Louis Bossart }
135405be59acSPierre-Louis Bossart 
cdns_init_clock_ctrl(struct sdw_cdns * cdns)13550cdcdedcSPierre-Louis Bossart static void cdns_init_clock_ctrl(struct sdw_cdns *cdns)
13562f52a517SVinod Koul {
13573859872fSPierre-Louis Bossart 	struct sdw_bus *bus = &cdns->bus;
13583859872fSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
13592f52a517SVinod Koul 	u32 val;
13601dd6a17fSPierre-Louis Bossart 	u32 ssp_interval;
13613859872fSPierre-Louis Bossart 	int divider;
13622f52a517SVinod Koul 
13632f52a517SVinod Koul 	/* Set clock divider */
13643859872fSPierre-Louis Bossart 	divider	= (prop->mclk_freq / prop->max_clk_freq) - 1;
13652f52a517SVinod Koul 
1366a50954e2SRander Wang 	cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
1367a50954e2SRander Wang 		     CDNS_MCP_CLK_MCLKD_MASK, divider);
1368a50954e2SRander Wang 	cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
1369a50954e2SRander Wang 		     CDNS_MCP_CLK_MCLKD_MASK, divider);
13702f52a517SVinod Koul 
137105be59acSPierre-Louis Bossart 	/*
137205be59acSPierre-Louis Bossart 	 * Frame shape changes after initialization have to be done
137305be59acSPierre-Louis Bossart 	 * with the bank switch mechanism
137405be59acSPierre-Louis Bossart 	 */
137505be59acSPierre-Louis Bossart 	val = cdns_set_initial_frame_shape(prop->default_row,
137605be59acSPierre-Louis Bossart 					   prop->default_col);
137705be59acSPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
13782f52a517SVinod Koul 
13792f52a517SVinod Koul 	/* Set SSP interval to default value */
13801dd6a17fSPierre-Louis Bossart 	ssp_interval = prop->default_frame_rate / SDW_CADENCE_GSYNC_HZ;
13811dd6a17fSPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, ssp_interval);
13821dd6a17fSPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval);
13830cdcdedcSPierre-Louis Bossart }
13840cdcdedcSPierre-Louis Bossart 
13850cdcdedcSPierre-Louis Bossart /**
13860cdcdedcSPierre-Louis Bossart  * sdw_cdns_init() - Cadence initialization
13870cdcdedcSPierre-Louis Bossart  * @cdns: Cadence instance
13880cdcdedcSPierre-Louis Bossart  */
sdw_cdns_init(struct sdw_cdns * cdns)13890cdcdedcSPierre-Louis Bossart int sdw_cdns_init(struct sdw_cdns *cdns)
13900cdcdedcSPierre-Louis Bossart {
13910cdcdedcSPierre-Louis Bossart 	u32 val;
13920cdcdedcSPierre-Louis Bossart 
13930cdcdedcSPierre-Louis Bossart 	cdns_init_clock_ctrl(cdns);
13942f52a517SVinod Koul 
1395ff560946SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__, false, 0);
1396ff560946SPierre-Louis Bossart 
13970d667d01SRander Wang 	/* reset msg_count to default value of FIFOLEVEL */
13980d667d01SRander Wang 	cdns->msg_count = cdns_readl(cdns, CDNS_MCP_FIFOLEVEL);
13990d667d01SRander Wang 
140049ea07d3SPierre-Louis Bossart 	/* flush command FIFOs */
140149ea07d3SPierre-Louis Bossart 	cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST,
140249ea07d3SPierre-Louis Bossart 		     CDNS_MCP_CONTROL_CMD_RST);
140349ea07d3SPierre-Louis Bossart 
14042f52a517SVinod Koul 	/* Set cmd accept mode */
14054dc953bcSPierre-Louis Bossart 	cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT,
14064dc953bcSPierre-Louis Bossart 			CDNS_IP_MCP_CONTROL_CMD_ACCEPT);
14072f52a517SVinod Koul 
14082f52a517SVinod Koul 	/* Configure mcp config */
14092f52a517SVinod Koul 	val = cdns_readl(cdns, CDNS_MCP_CONFIG);
14102f52a517SVinod Koul 
1411b62e76cfSPierre-Louis Bossart 	/* Disable auto bus release */
1412b62e76cfSPierre-Louis Bossart 	val &= ~CDNS_MCP_CONFIG_BUS_REL;
1413b62e76cfSPierre-Louis Bossart 
1414c5753714SPierre-Louis Bossart 	cdns_writel(cdns, CDNS_MCP_CONFIG, val);
1415c5753714SPierre-Louis Bossart 
1416c5753714SPierre-Louis Bossart 	/* Configure IP mcp config */
1417c5753714SPierre-Louis Bossart 	val = cdns_ip_readl(cdns, CDNS_IP_MCP_CONFIG);
1418c5753714SPierre-Louis Bossart 
1419c5753714SPierre-Louis Bossart 	/* enable bus operations with clock and data */
1420c5753714SPierre-Louis Bossart 	val &= ~CDNS_IP_MCP_CONFIG_OP;
1421c5753714SPierre-Louis Bossart 	val |= CDNS_IP_MCP_CONFIG_OP_NORMAL;
1422c5753714SPierre-Louis Bossart 
1423c5753714SPierre-Louis Bossart 	/* Set cmd mode for Tx and Rx cmds */
1424c5753714SPierre-Louis Bossart 	val &= ~CDNS_IP_MCP_CONFIG_CMD;
1425c5753714SPierre-Louis Bossart 
1426c5753714SPierre-Louis Bossart 	/* Disable sniffer mode */
1427c5753714SPierre-Louis Bossart 	val &= ~CDNS_IP_MCP_CONFIG_SNIFFER;
1428c5753714SPierre-Louis Bossart 
14292c800e3bSPierre-Louis Bossart 	if (cdns->bus.multi_link)
14302c800e3bSPierre-Louis Bossart 		/* Set Multi-master mode to take gsync into account */
1431c5753714SPierre-Louis Bossart 		val |= CDNS_IP_MCP_CONFIG_MMASTER;
1432b62e76cfSPierre-Louis Bossart 
143391080111SPierre-Louis Bossart 	/* leave frame delay to hardware default of 0x1F */
1434b62e76cfSPierre-Louis Bossart 
1435ad473db4SPierre-Louis Bossart 	/* leave command retry to hardware default of 0 */
14362f52a517SVinod Koul 
1437c5753714SPierre-Louis Bossart 	cdns_ip_writel(cdns, CDNS_IP_MCP_CONFIG, val);
14382f52a517SVinod Koul 
1439b17350e4SPierre-Louis Bossart 	/* changes will be committed later */
1440b17350e4SPierre-Louis Bossart 	return 0;
14412f52a517SVinod Koul }
14422f52a517SVinod Koul EXPORT_SYMBOL(sdw_cdns_init);
14432f52a517SVinod Koul 
cdns_bus_conf(struct sdw_bus * bus,struct sdw_bus_params * params)144407abeff1SVinod Koul int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
144507abeff1SVinod Koul {
14463859872fSPierre-Louis Bossart 	struct sdw_master_prop *prop = &bus->prop;
144707abeff1SVinod Koul 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1448a50954e2SRander Wang 	int mcp_clkctrl_off;
144907abeff1SVinod Koul 	int divider;
145007abeff1SVinod Koul 
145107abeff1SVinod Koul 	if (!params->curr_dr_freq) {
145217ed5befSPierre-Louis Bossart 		dev_err(cdns->dev, "NULL curr_dr_freq\n");
145307abeff1SVinod Koul 		return -EINVAL;
145407abeff1SVinod Koul 	}
145507abeff1SVinod Koul 
14563859872fSPierre-Louis Bossart 	divider	= prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
14573859872fSPierre-Louis Bossart 		params->curr_dr_freq;
14583859872fSPierre-Louis Bossart 	divider--; /* divider is 1/(N+1) */
145907abeff1SVinod Koul 
146007abeff1SVinod Koul 	if (params->next_bank)
146107abeff1SVinod Koul 		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
146207abeff1SVinod Koul 	else
146307abeff1SVinod Koul 		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
146407abeff1SVinod Koul 
1465a50954e2SRander Wang 	cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
146607abeff1SVinod Koul 
146707abeff1SVinod Koul 	return 0;
146807abeff1SVinod Koul }
146907abeff1SVinod Koul EXPORT_SYMBOL(cdns_bus_conf);
147007abeff1SVinod Koul 
cdns_port_params(struct sdw_bus * bus,struct sdw_port_params * p_params,unsigned int bank)147107abeff1SVinod Koul static int cdns_port_params(struct sdw_bus *bus,
147207abeff1SVinod Koul 			    struct sdw_port_params *p_params, unsigned int bank)
147307abeff1SVinod Koul {
147407abeff1SVinod Koul 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1475dd81e7c3SPierre-Louis Bossart 	int dpn_config_off_source;
1476dd81e7c3SPierre-Louis Bossart 	int dpn_config_off_target;
1477dd81e7c3SPierre-Louis Bossart 	int target_num = p_params->num;
1478dd81e7c3SPierre-Louis Bossart 	int source_num = p_params->num;
1479dd81e7c3SPierre-Louis Bossart 	bool override = false;
1480dd81e7c3SPierre-Louis Bossart 	int dpn_config;
148107abeff1SVinod Koul 
1482dd81e7c3SPierre-Louis Bossart 	if (target_num == cdns->pdi_loopback_target &&
1483dd81e7c3SPierre-Louis Bossart 	    cdns->pdi_loopback_source != -1) {
1484dd81e7c3SPierre-Louis Bossart 		source_num = cdns->pdi_loopback_source;
1485dd81e7c3SPierre-Louis Bossart 		override = true;
1486dd81e7c3SPierre-Louis Bossart 	}
148707abeff1SVinod Koul 
1488dd81e7c3SPierre-Louis Bossart 	if (bank) {
1489dd81e7c3SPierre-Louis Bossart 		dpn_config_off_source = CDNS_DPN_B1_CONFIG(source_num);
1490dd81e7c3SPierre-Louis Bossart 		dpn_config_off_target = CDNS_DPN_B1_CONFIG(target_num);
1491dd81e7c3SPierre-Louis Bossart 	} else {
1492dd81e7c3SPierre-Louis Bossart 		dpn_config_off_source = CDNS_DPN_B0_CONFIG(source_num);
1493dd81e7c3SPierre-Louis Bossart 		dpn_config_off_target = CDNS_DPN_B0_CONFIG(target_num);
1494dd81e7c3SPierre-Louis Bossart 	}
149507abeff1SVinod Koul 
1496dd81e7c3SPierre-Louis Bossart 	dpn_config = cdns_readl(cdns, dpn_config_off_source);
1497dd81e7c3SPierre-Louis Bossart 
1498dd81e7c3SPierre-Louis Bossart 	/* use port params if there is no loopback, otherwise use source as is */
1499dd81e7c3SPierre-Louis Bossart 	if (!override) {
1500dd81e7c3SPierre-Louis Bossart 		u32p_replace_bits(&dpn_config, p_params->bps - 1, CDNS_DPN_CONFIG_WL);
1501714db045SVinod Koul 		u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW);
1502714db045SVinod Koul 		u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT);
1503dd81e7c3SPierre-Louis Bossart 	}
150407abeff1SVinod Koul 
1505dd81e7c3SPierre-Louis Bossart 	cdns_writel(cdns, dpn_config_off_target, dpn_config);
150607abeff1SVinod Koul 
150707abeff1SVinod Koul 	return 0;
150807abeff1SVinod Koul }
150907abeff1SVinod Koul 
cdns_transport_params(struct sdw_bus * bus,struct sdw_transport_params * t_params,enum sdw_reg_bank bank)151007abeff1SVinod Koul static int cdns_transport_params(struct sdw_bus *bus,
151107abeff1SVinod Koul 				 struct sdw_transport_params *t_params,
151207abeff1SVinod Koul 				 enum sdw_reg_bank bank)
151307abeff1SVinod Koul {
151407abeff1SVinod Koul 	struct sdw_cdns *cdns = bus_to_cdns(bus);
1515dd81e7c3SPierre-Louis Bossart 	int dpn_config;
1516dd81e7c3SPierre-Louis Bossart 	int dpn_config_off_source;
1517dd81e7c3SPierre-Louis Bossart 	int dpn_config_off_target;
1518dd81e7c3SPierre-Louis Bossart 	int dpn_hctrl;
1519dd81e7c3SPierre-Louis Bossart 	int dpn_hctrl_off_source;
1520dd81e7c3SPierre-Louis Bossart 	int dpn_hctrl_off_target;
1521dd81e7c3SPierre-Louis Bossart 	int dpn_offsetctrl;
1522dd81e7c3SPierre-Louis Bossart 	int dpn_offsetctrl_off_source;
1523dd81e7c3SPierre-Louis Bossart 	int dpn_offsetctrl_off_target;
1524dd81e7c3SPierre-Louis Bossart 	int dpn_samplectrl;
1525dd81e7c3SPierre-Louis Bossart 	int dpn_samplectrl_off_source;
1526dd81e7c3SPierre-Louis Bossart 	int dpn_samplectrl_off_target;
1527dd81e7c3SPierre-Louis Bossart 	int source_num = t_params->port_num;
1528dd81e7c3SPierre-Louis Bossart 	int target_num = t_params->port_num;
1529dd81e7c3SPierre-Louis Bossart 	bool override = false;
1530dd81e7c3SPierre-Louis Bossart 
1531dd81e7c3SPierre-Louis Bossart 	if (target_num == cdns->pdi_loopback_target &&
1532dd81e7c3SPierre-Louis Bossart 	    cdns->pdi_loopback_source != -1) {
1533dd81e7c3SPierre-Louis Bossart 		source_num = cdns->pdi_loopback_source;
1534dd81e7c3SPierre-Louis Bossart 		override = true;
1535dd81e7c3SPierre-Louis Bossart 	}
153607abeff1SVinod Koul 
153707abeff1SVinod Koul 	/*
153807abeff1SVinod Koul 	 * Note: Only full data port is supported on the Master side for
153907abeff1SVinod Koul 	 * both PCM and PDM ports.
154007abeff1SVinod Koul 	 */
154107abeff1SVinod Koul 
154207abeff1SVinod Koul 	if (bank) {
1543dd81e7c3SPierre-Louis Bossart 		dpn_config_off_source = CDNS_DPN_B1_CONFIG(source_num);
1544dd81e7c3SPierre-Louis Bossart 		dpn_hctrl_off_source = CDNS_DPN_B1_HCTRL(source_num);
1545dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl_off_source = CDNS_DPN_B1_OFFSET_CTRL(source_num);
1546dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl_off_source = CDNS_DPN_B1_SAMPLE_CTRL(source_num);
1547dd81e7c3SPierre-Louis Bossart 
1548dd81e7c3SPierre-Louis Bossart 		dpn_config_off_target = CDNS_DPN_B1_CONFIG(target_num);
1549dd81e7c3SPierre-Louis Bossart 		dpn_hctrl_off_target = CDNS_DPN_B1_HCTRL(target_num);
1550dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl_off_target = CDNS_DPN_B1_OFFSET_CTRL(target_num);
1551dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl_off_target = CDNS_DPN_B1_SAMPLE_CTRL(target_num);
1552dd81e7c3SPierre-Louis Bossart 
155307abeff1SVinod Koul 	} else {
1554dd81e7c3SPierre-Louis Bossart 		dpn_config_off_source = CDNS_DPN_B0_CONFIG(source_num);
1555dd81e7c3SPierre-Louis Bossart 		dpn_hctrl_off_source = CDNS_DPN_B0_HCTRL(source_num);
1556dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl_off_source = CDNS_DPN_B0_OFFSET_CTRL(source_num);
1557dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl_off_source = CDNS_DPN_B0_SAMPLE_CTRL(source_num);
1558dd81e7c3SPierre-Louis Bossart 
1559dd81e7c3SPierre-Louis Bossart 		dpn_config_off_target = CDNS_DPN_B0_CONFIG(target_num);
1560dd81e7c3SPierre-Louis Bossart 		dpn_hctrl_off_target = CDNS_DPN_B0_HCTRL(target_num);
1561dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl_off_target = CDNS_DPN_B0_OFFSET_CTRL(target_num);
1562dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl_off_target = CDNS_DPN_B0_SAMPLE_CTRL(target_num);
156307abeff1SVinod Koul 	}
156407abeff1SVinod Koul 
1565dd81e7c3SPierre-Louis Bossart 	dpn_config = cdns_readl(cdns, dpn_config_off_source);
1566dd81e7c3SPierre-Louis Bossart 	if (!override) {
1567714db045SVinod Koul 		u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC);
1568714db045SVinod Koul 		u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM);
1569dd81e7c3SPierre-Louis Bossart 	}
1570dd81e7c3SPierre-Louis Bossart 	cdns_writel(cdns, dpn_config_off_target, dpn_config);
157107abeff1SVinod Koul 
1572dd81e7c3SPierre-Louis Bossart 	if (!override) {
1573dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl = 0;
1574714db045SVinod Koul 		u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1);
1575714db045SVinod Koul 		u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2);
1576dd81e7c3SPierre-Louis Bossart 	} else {
1577dd81e7c3SPierre-Louis Bossart 		dpn_offsetctrl = cdns_readl(cdns, dpn_offsetctrl_off_source);
1578dd81e7c3SPierre-Louis Bossart 	}
1579dd81e7c3SPierre-Louis Bossart 	cdns_writel(cdns, dpn_offsetctrl_off_target,  dpn_offsetctrl);
158007abeff1SVinod Koul 
1581dd81e7c3SPierre-Louis Bossart 	if (!override) {
1582dd81e7c3SPierre-Louis Bossart 		dpn_hctrl = 0;
1583714db045SVinod Koul 		u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART);
1584714db045SVinod Koul 		u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP);
1585714db045SVinod Koul 		u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL);
1586dd81e7c3SPierre-Louis Bossart 	} else {
1587dd81e7c3SPierre-Louis Bossart 		dpn_hctrl = cdns_readl(cdns, dpn_hctrl_off_source);
1588dd81e7c3SPierre-Louis Bossart 	}
1589dd81e7c3SPierre-Louis Bossart 	cdns_writel(cdns, dpn_hctrl_off_target, dpn_hctrl);
159007abeff1SVinod Koul 
1591dd81e7c3SPierre-Louis Bossart 	if (!override)
1592dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl = t_params->sample_interval - 1;
1593dd81e7c3SPierre-Louis Bossart 	else
1594dd81e7c3SPierre-Louis Bossart 		dpn_samplectrl = cdns_readl(cdns, dpn_samplectrl_off_source);
1595dd81e7c3SPierre-Louis Bossart 	cdns_writel(cdns, dpn_samplectrl_off_target, dpn_samplectrl);
159607abeff1SVinod Koul 
159707abeff1SVinod Koul 	return 0;
159807abeff1SVinod Koul }
159907abeff1SVinod Koul 
cdns_port_enable(struct sdw_bus * bus,struct sdw_enable_ch * enable_ch,unsigned int bank)160007abeff1SVinod Koul static int cdns_port_enable(struct sdw_bus *bus,
160107abeff1SVinod Koul 			    struct sdw_enable_ch *enable_ch, unsigned int bank)
160207abeff1SVinod Koul {
160307abeff1SVinod Koul 	struct sdw_cdns *cdns = bus_to_cdns(bus);
160407abeff1SVinod Koul 	int dpn_chnen_off, ch_mask;
160507abeff1SVinod Koul 
160607abeff1SVinod Koul 	if (bank)
160707abeff1SVinod Koul 		dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num);
160807abeff1SVinod Koul 	else
160907abeff1SVinod Koul 		dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num);
161007abeff1SVinod Koul 
161107abeff1SVinod Koul 	ch_mask = enable_ch->ch_mask * enable_ch->enable;
161207abeff1SVinod Koul 	cdns_writel(cdns, dpn_chnen_off, ch_mask);
161307abeff1SVinod Koul 
161407abeff1SVinod Koul 	return 0;
161507abeff1SVinod Koul }
161607abeff1SVinod Koul 
161707abeff1SVinod Koul static const struct sdw_master_port_ops cdns_port_ops = {
161807abeff1SVinod Koul 	.dpn_set_port_params = cdns_port_params,
161907abeff1SVinod Koul 	.dpn_set_port_transport_params = cdns_transport_params,
162007abeff1SVinod Koul 	.dpn_port_enable_ch = cdns_port_enable,
162107abeff1SVinod Koul };
162207abeff1SVinod Koul 
1623956baa19SSanyog Kale /**
16245a885c52SRander Wang  * sdw_cdns_is_clock_stop: Check clock status
16255a885c52SRander Wang  *
16265a885c52SRander Wang  * @cdns: Cadence instance
16275a885c52SRander Wang  */
sdw_cdns_is_clock_stop(struct sdw_cdns * cdns)16285a885c52SRander Wang bool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns)
16295a885c52SRander Wang {
16305a885c52SRander Wang 	return !!(cdns_readl(cdns, CDNS_MCP_STAT) & CDNS_MCP_STAT_CLK_STOP);
16315a885c52SRander Wang }
16325a885c52SRander Wang EXPORT_SYMBOL(sdw_cdns_is_clock_stop);
16335a885c52SRander Wang 
16345a885c52SRander Wang /**
16351032504fSRander Wang  * sdw_cdns_clock_stop: Cadence clock stop configuration routine
16361032504fSRander Wang  *
16371032504fSRander Wang  * @cdns: Cadence instance
16381032504fSRander Wang  * @block_wake: prevent wakes if required by the platform
16391032504fSRander Wang  */
sdw_cdns_clock_stop(struct sdw_cdns * cdns,bool block_wake)16401032504fSRander Wang int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake)
16411032504fSRander Wang {
16421032504fSRander Wang 	bool slave_present = false;
16431032504fSRander Wang 	struct sdw_slave *slave;
16441032504fSRander Wang 	int ret;
16451032504fSRander Wang 
1646ff560946SPierre-Louis Bossart 	sdw_cdns_check_self_clearing_bits(cdns, __func__, false, 0);
1647ff560946SPierre-Louis Bossart 
16481032504fSRander Wang 	/* Check suspend status */
16491032504fSRander Wang 	if (sdw_cdns_is_clock_stop(cdns)) {
16501032504fSRander Wang 		dev_dbg(cdns->dev, "Clock is already stopped\n");
16511032504fSRander Wang 		return 0;
16521032504fSRander Wang 	}
16531032504fSRander Wang 
16541032504fSRander Wang 	/*
1655af4cc917SPierre-Louis Bossart 	 * Before entering clock stop we mask the Slave
1656af4cc917SPierre-Louis Bossart 	 * interrupts. This helps avoid having to deal with e.g. a
1657af4cc917SPierre-Louis Bossart 	 * Slave becoming UNATTACHED while the clock is being stopped
1658af4cc917SPierre-Louis Bossart 	 */
1659af4cc917SPierre-Louis Bossart 	cdns_enable_slave_interrupts(cdns, false);
1660af4cc917SPierre-Louis Bossart 
1661af4cc917SPierre-Louis Bossart 	/*
16621032504fSRander Wang 	 * For specific platforms, it is required to be able to put
16631032504fSRander Wang 	 * master into a state in which it ignores wake-up trials
16641032504fSRander Wang 	 * in clock stop state
16651032504fSRander Wang 	 */
16661032504fSRander Wang 	if (block_wake)
16674dc953bcSPierre-Louis Bossart 		cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL,
16684dc953bcSPierre-Louis Bossart 				CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP,
16694dc953bcSPierre-Louis Bossart 				CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP);
16701032504fSRander Wang 
16711032504fSRander Wang 	list_for_each_entry(slave, &cdns->bus.slaves, node) {
16721032504fSRander Wang 		if (slave->status == SDW_SLAVE_ATTACHED ||
16731032504fSRander Wang 		    slave->status == SDW_SLAVE_ALERT) {
16741032504fSRander Wang 			slave_present = true;
16751032504fSRander Wang 			break;
16761032504fSRander Wang 		}
16771032504fSRander Wang 	}
16781032504fSRander Wang 
16791032504fSRander Wang 	/* commit changes */
16801032504fSRander Wang 	ret = cdns_config_update(cdns);
16811032504fSRander Wang 	if (ret < 0) {
16821032504fSRander Wang 		dev_err(cdns->dev, "%s: config_update failed\n", __func__);
16831032504fSRander Wang 		return ret;
16841032504fSRander Wang 	}
16851032504fSRander Wang 
16861032504fSRander Wang 	/* Prepare slaves for clock stop */
168758ef9356SPierre-Louis Bossart 	if (slave_present) {
16881032504fSRander Wang 		ret = sdw_bus_prep_clk_stop(&cdns->bus);
168958ef9356SPierre-Louis Bossart 		if (ret < 0 && ret != -ENODATA) {
169058ef9356SPierre-Louis Bossart 			dev_err(cdns->dev, "prepare clock stop failed %d\n", ret);
16911032504fSRander Wang 			return ret;
16921032504fSRander Wang 		}
169358ef9356SPierre-Louis Bossart 	}
16941032504fSRander Wang 
16951032504fSRander Wang 	/*
16961032504fSRander Wang 	 * Enter clock stop mode and only report errors if there are
16971032504fSRander Wang 	 * Slave devices present (ALERT or ATTACHED)
16981032504fSRander Wang 	 */
16991032504fSRander Wang 	ret = sdw_bus_clk_stop(&cdns->bus);
17001032504fSRander Wang 	if (ret < 0 && slave_present && ret != -ENODATA) {
17017dbdcd61SPierre-Louis Bossart 		dev_err(cdns->dev, "bus clock stop failed %d\n", ret);
17021032504fSRander Wang 		return ret;
17031032504fSRander Wang 	}
17041032504fSRander Wang 
17051032504fSRander Wang 	ret = cdns_set_wait(cdns, CDNS_MCP_STAT,
17061032504fSRander Wang 			    CDNS_MCP_STAT_CLK_STOP,
17071032504fSRander Wang 			    CDNS_MCP_STAT_CLK_STOP);
17081032504fSRander Wang 	if (ret < 0)
17091032504fSRander Wang 		dev_err(cdns->dev, "Clock stop failed %d\n", ret);
17101032504fSRander Wang 
17111032504fSRander Wang 	return ret;
17121032504fSRander Wang }
17131032504fSRander Wang EXPORT_SYMBOL(sdw_cdns_clock_stop);
17141032504fSRander Wang 
17151032504fSRander Wang /**
17161032504fSRander Wang  * sdw_cdns_clock_restart: Cadence PM clock restart configuration routine
17171032504fSRander Wang  *
17181032504fSRander Wang  * @cdns: Cadence instance
17191032504fSRander Wang  * @bus_reset: context may be lost while in low power modes and the bus
17201032504fSRander Wang  * may require a Severe Reset and re-enumeration after a wake.
17211032504fSRander Wang  */
sdw_cdns_clock_restart(struct sdw_cdns * cdns,bool bus_reset)17221032504fSRander Wang int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset)
17231032504fSRander Wang {
17241032504fSRander Wang 	int ret;
17251032504fSRander Wang 
1726af4cc917SPierre-Louis Bossart 	/* unmask Slave interrupts that were masked when stopping the clock */
1727af4cc917SPierre-Louis Bossart 	cdns_enable_slave_interrupts(cdns, true);
1728af4cc917SPierre-Louis Bossart 
17291032504fSRander Wang 	ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
17301032504fSRander Wang 			     CDNS_MCP_CONTROL_CLK_STOP_CLR);
17311032504fSRander Wang 	if (ret < 0) {
17321032504fSRander Wang 		dev_err(cdns->dev, "Couldn't exit from clock stop\n");
17331032504fSRander Wang 		return ret;
17341032504fSRander Wang 	}
17351032504fSRander Wang 
17361032504fSRander Wang 	ret = cdns_set_wait(cdns, CDNS_MCP_STAT, CDNS_MCP_STAT_CLK_STOP, 0);
17371032504fSRander Wang 	if (ret < 0) {
17381032504fSRander Wang 		dev_err(cdns->dev, "clock stop exit failed %d\n", ret);
17391032504fSRander Wang 		return ret;
17401032504fSRander Wang 	}
17411032504fSRander Wang 
17424dc953bcSPierre-Louis Bossart 	cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL,
17434dc953bcSPierre-Louis Bossart 			CDNS_IP_MCP_CONTROL_BLOCK_WAKEUP, 0);
17441032504fSRander Wang 
17454dc953bcSPierre-Louis Bossart 	cdns_ip_updatel(cdns, CDNS_IP_MCP_CONTROL, CDNS_IP_MCP_CONTROL_CMD_ACCEPT,
17464dc953bcSPierre-Louis Bossart 			CDNS_IP_MCP_CONTROL_CMD_ACCEPT);
17471032504fSRander Wang 
17481032504fSRander Wang 	if (!bus_reset) {
17491032504fSRander Wang 
17501032504fSRander Wang 		/* enable bus operations with clock and data */
1751c5753714SPierre-Louis Bossart 		cdns_ip_updatel(cdns, CDNS_IP_MCP_CONFIG,
1752c5753714SPierre-Louis Bossart 				CDNS_IP_MCP_CONFIG_OP,
1753c5753714SPierre-Louis Bossart 				CDNS_IP_MCP_CONFIG_OP_NORMAL);
17541032504fSRander Wang 
17551032504fSRander Wang 		ret = cdns_config_update(cdns);
17561032504fSRander Wang 		if (ret < 0) {
17571032504fSRander Wang 			dev_err(cdns->dev, "%s: config_update failed\n", __func__);
17581032504fSRander Wang 			return ret;
17591032504fSRander Wang 		}
17601032504fSRander Wang 
17611032504fSRander Wang 		ret = sdw_bus_exit_clk_stop(&cdns->bus);
17621032504fSRander Wang 		if (ret < 0)
17631032504fSRander Wang 			dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret);
17641032504fSRander Wang 	}
17651032504fSRander Wang 
17661032504fSRander Wang 	return ret;
17671032504fSRander Wang }
17681032504fSRander Wang EXPORT_SYMBOL(sdw_cdns_clock_restart);
17691032504fSRander Wang 
17701032504fSRander Wang /**
1771956baa19SSanyog Kale  * sdw_cdns_probe() - Cadence probe routine
1772956baa19SSanyog Kale  * @cdns: Cadence instance
1773956baa19SSanyog Kale  */
sdw_cdns_probe(struct sdw_cdns * cdns)1774956baa19SSanyog Kale int sdw_cdns_probe(struct sdw_cdns *cdns)
1775956baa19SSanyog Kale {
1776956baa19SSanyog Kale 	init_completion(&cdns->tx_complete);
177707abeff1SVinod Koul 	cdns->bus.port_ops = &cdns_port_ops;
1778956baa19SSanyog Kale 
1779*740a6059SPierre-Louis Bossart 	mutex_init(&cdns->status_update_lock);
1780*740a6059SPierre-Louis Bossart 
17814a98a6b2SBard Liao 	INIT_WORK(&cdns->work, cdns_update_slave_status_work);
1782*740a6059SPierre-Louis Bossart 	INIT_DELAYED_WORK(&cdns->attach_dwork, cdns_check_attached_status_dwork);
1783*740a6059SPierre-Louis Bossart 
1784956baa19SSanyog Kale 	return 0;
1785956baa19SSanyog Kale }
1786956baa19SSanyog Kale EXPORT_SYMBOL(sdw_cdns_probe);
1787956baa19SSanyog Kale 
cdns_set_sdw_stream(struct snd_soc_dai * dai,void * stream,int direction)17885d6b3c8bSVinod Koul int cdns_set_sdw_stream(struct snd_soc_dai *dai,
178963a6aa96SPierre-Louis Bossart 			void *stream, int direction)
17905d6b3c8bSVinod Koul {
17915d6b3c8bSVinod Koul 	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
1792e0767e39SPierre-Louis Bossart 	struct sdw_cdns_dai_runtime *dai_runtime;
17935d6b3c8bSVinod Koul 
17947dddead7SPierre-Louis Bossart 	dai_runtime = cdns->dai_runtime_array[dai->id];
17957dddead7SPierre-Louis Bossart 
1796b5e9e687SPierre-Louis Bossart 	if (stream) {
1797b5e9e687SPierre-Louis Bossart 		/* first paranoia check */
1798e0767e39SPierre-Louis Bossart 		if (dai_runtime) {
1799b5e9e687SPierre-Louis Bossart 			dev_err(dai->dev,
1800e0767e39SPierre-Louis Bossart 				"dai_runtime already allocated for dai %s\n",
1801b5e9e687SPierre-Louis Bossart 				dai->name);
1802b5e9e687SPierre-Louis Bossart 			return -EINVAL;
1803b5e9e687SPierre-Louis Bossart 		}
1804b5e9e687SPierre-Louis Bossart 
1805e0767e39SPierre-Louis Bossart 		/* allocate and set dai_runtime info */
1806e0767e39SPierre-Louis Bossart 		dai_runtime = kzalloc(sizeof(*dai_runtime), GFP_KERNEL);
1807e0767e39SPierre-Louis Bossart 		if (!dai_runtime)
18085d6b3c8bSVinod Koul 			return -ENOMEM;
18095d6b3c8bSVinod Koul 
1810e0767e39SPierre-Louis Bossart 		dai_runtime->stream_type = SDW_STREAM_PCM;
18115d6b3c8bSVinod Koul 
1812e0767e39SPierre-Louis Bossart 		dai_runtime->bus = &cdns->bus;
1813e0767e39SPierre-Louis Bossart 		dai_runtime->link_id = cdns->instance;
18145d6b3c8bSVinod Koul 
1815e0767e39SPierre-Louis Bossart 		dai_runtime->stream = stream;
18167dddead7SPierre-Louis Bossart 		dai_runtime->direction = direction;
18175d6b3c8bSVinod Koul 
18187dddead7SPierre-Louis Bossart 		cdns->dai_runtime_array[dai->id] = dai_runtime;
1819b5e9e687SPierre-Louis Bossart 	} else {
18207dddead7SPierre-Louis Bossart 		/* second paranoia check */
18217dddead7SPierre-Louis Bossart 		if (!dai_runtime) {
18227dddead7SPierre-Louis Bossart 			dev_err(dai->dev,
18237dddead7SPierre-Louis Bossart 				"dai_runtime not allocated for dai %s\n",
18247dddead7SPierre-Louis Bossart 				dai->name);
18257dddead7SPierre-Louis Bossart 			return -EINVAL;
1826b5e9e687SPierre-Louis Bossart 		}
18277dddead7SPierre-Louis Bossart 
18287dddead7SPierre-Louis Bossart 		/* for NULL stream we release allocated dai_runtime */
18297dddead7SPierre-Louis Bossart 		kfree(dai_runtime);
18307dddead7SPierre-Louis Bossart 		cdns->dai_runtime_array[dai->id] = NULL;
1831b5e9e687SPierre-Louis Bossart 	}
18325d6b3c8bSVinod Koul 	return 0;
18335d6b3c8bSVinod Koul }
18345d6b3c8bSVinod Koul EXPORT_SYMBOL(cdns_set_sdw_stream);
18355d6b3c8bSVinod Koul 
18365d6b3c8bSVinod Koul /**
18375d6b3c8bSVinod Koul  * cdns_find_pdi() - Find a free PDI
18385d6b3c8bSVinod Koul  *
18395d6b3c8bSVinod Koul  * @cdns: Cadence instance
184039737a31SPierre-Louis Bossart  * @offset: Starting offset
18415d6b3c8bSVinod Koul  * @num: Number of PDIs
18425d6b3c8bSVinod Koul  * @pdi: PDI instances
184339737a31SPierre-Louis Bossart  * @dai_id: DAI id
18445d6b3c8bSVinod Koul  *
18451b53385eSBard Liao  * Find a PDI for a given PDI array. The PDI num and dai_id are
18461b53385eSBard Liao  * expected to match, return NULL otherwise.
18475d6b3c8bSVinod Koul  */
cdns_find_pdi(struct sdw_cdns * cdns,unsigned int offset,unsigned int num,struct sdw_cdns_pdi * pdi,int dai_id)18485d6b3c8bSVinod Koul static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns,
1849807c15bcSPierre-Louis Bossart 					  unsigned int offset,
1850bbb63817SPierre-Louis Bossart 					  unsigned int num,
18511b53385eSBard Liao 					  struct sdw_cdns_pdi *pdi,
18521b53385eSBard Liao 					  int dai_id)
18535d6b3c8bSVinod Koul {
18545d6b3c8bSVinod Koul 	int i;
18555d6b3c8bSVinod Koul 
18561b53385eSBard Liao 	for (i = offset; i < offset + num; i++)
18571b53385eSBard Liao 		if (pdi[i].num == dai_id)
18585d6b3c8bSVinod Koul 			return &pdi[i];
18595d6b3c8bSVinod Koul 
18605d6b3c8bSVinod Koul 	return NULL;
18615d6b3c8bSVinod Koul }
18625d6b3c8bSVinod Koul 
18635d6b3c8bSVinod Koul /**
18645d6b3c8bSVinod Koul  * sdw_cdns_config_stream: Configure a stream
18655d6b3c8bSVinod Koul  *
18665d6b3c8bSVinod Koul  * @cdns: Cadence instance
18675d6b3c8bSVinod Koul  * @ch: Channel count
18685d6b3c8bSVinod Koul  * @dir: Data direction
18695d6b3c8bSVinod Koul  * @pdi: PDI to be used
18705d6b3c8bSVinod Koul  */
sdw_cdns_config_stream(struct sdw_cdns * cdns,u32 ch,u32 dir,struct sdw_cdns_pdi * pdi)18715d6b3c8bSVinod Koul void sdw_cdns_config_stream(struct sdw_cdns *cdns,
18725d6b3c8bSVinod Koul 			    u32 ch, u32 dir, struct sdw_cdns_pdi *pdi)
18735d6b3c8bSVinod Koul {
18745d6b3c8bSVinod Koul 	u32 offset, val = 0;
18755d6b3c8bSVinod Koul 
18769e4e6019SPierre-Louis Bossart 	if (dir == SDW_DATA_DIR_RX) {
18775d6b3c8bSVinod Koul 		val = CDNS_PORTCTRL_DIRN;
18785d6b3c8bSVinod Koul 
18799e4e6019SPierre-Louis Bossart 		if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL)
18809e4e6019SPierre-Louis Bossart 			val |= CDNS_PORTCTRL_TEST_FAILED;
18819e4e6019SPierre-Louis Bossart 	}
188257a34790SPierre-Louis Bossart 	offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET;
18839e4e6019SPierre-Louis Bossart 	cdns_updatel(cdns, offset,
18849e4e6019SPierre-Louis Bossart 		     CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED,
18859e4e6019SPierre-Louis Bossart 		     val);
18865d6b3c8bSVinod Koul 
188757a34790SPierre-Louis Bossart 	val = pdi->num;
1888b468a785Sranderwang 	val |= CDNS_PDI_CONFIG_SOFT_RESET;
18893cf25d63SVinod Koul 	val |= FIELD_PREP(CDNS_PDI_CONFIG_CHANNEL, (1 << ch) - 1);
18905d6b3c8bSVinod Koul 	cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val);
18915d6b3c8bSVinod Koul }
18925d6b3c8bSVinod Koul EXPORT_SYMBOL(sdw_cdns_config_stream);
18935d6b3c8bSVinod Koul 
18945d6b3c8bSVinod Koul /**
189557a34790SPierre-Louis Bossart  * sdw_cdns_alloc_pdi() - Allocate a PDI
18965d6b3c8bSVinod Koul  *
18975d6b3c8bSVinod Koul  * @cdns: Cadence instance
18985d6b3c8bSVinod Koul  * @stream: Stream to be allocated
18995d6b3c8bSVinod Koul  * @ch: Channel count
19005d6b3c8bSVinod Koul  * @dir: Data direction
190139737a31SPierre-Louis Bossart  * @dai_id: DAI id
19025d6b3c8bSVinod Koul  */
sdw_cdns_alloc_pdi(struct sdw_cdns * cdns,struct sdw_cdns_streams * stream,u32 ch,u32 dir,int dai_id)190357a34790SPierre-Louis Bossart struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
19045d6b3c8bSVinod Koul 					struct sdw_cdns_streams *stream,
19051b53385eSBard Liao 					u32 ch, u32 dir, int dai_id)
19065d6b3c8bSVinod Koul {
19075d6b3c8bSVinod Koul 	struct sdw_cdns_pdi *pdi = NULL;
19085d6b3c8bSVinod Koul 
19095d6b3c8bSVinod Koul 	if (dir == SDW_DATA_DIR_RX)
19101b53385eSBard Liao 		pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in,
19111b53385eSBard Liao 				    dai_id);
19125d6b3c8bSVinod Koul 	else
19131b53385eSBard Liao 		pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out,
19141b53385eSBard Liao 				    dai_id);
19155d6b3c8bSVinod Koul 
19165d6b3c8bSVinod Koul 	/* check if we found a PDI, else find in bi-directional */
19175d6b3c8bSVinod Koul 	if (!pdi)
19187eeef1e9SPierre-Louis Bossart 		pdi = cdns_find_pdi(cdns, 0, stream->num_bd, stream->bd,
19191b53385eSBard Liao 				    dai_id);
19205d6b3c8bSVinod Koul 
192157a34790SPierre-Louis Bossart 	if (pdi) {
19225d6b3c8bSVinod Koul 		pdi->l_ch_num = 0;
19235d6b3c8bSVinod Koul 		pdi->h_ch_num = ch - 1;
19245d6b3c8bSVinod Koul 		pdi->dir = dir;
19255d6b3c8bSVinod Koul 		pdi->ch_count = ch;
19265d6b3c8bSVinod Koul 	}
192757a34790SPierre-Louis Bossart 
192857a34790SPierre-Louis Bossart 	return pdi;
192957a34790SPierre-Louis Bossart }
193057a34790SPierre-Louis Bossart EXPORT_SYMBOL(sdw_cdns_alloc_pdi);
19315d6b3c8bSVinod Koul 
19322f52a517SVinod Koul MODULE_LICENSE("Dual BSD/GPL");
19332f52a517SVinod Koul MODULE_DESCRIPTION("Cadence Soundwire Library");
1934