18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// Copyright(c) 2015-17 Intel Corporation. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * Cadence SoundWire Master module 68c2ecf20Sopenharmony_ci * Used by Master driver 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_registers.h> 188c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/soc.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include "bus.h" 238c2ecf20Sopenharmony_ci#include "cadence_master.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int interrupt_mask; 268c2ecf20Sopenharmony_cimodule_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG 0x0 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24) 328c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16) 338c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_MMASTER BIT(7) 348c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_BUS_REL BIT(6) 358c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_SNIFFER BIT(5) 368c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_SSPMOD BIT(4) 378c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_CMD BIT(3) 388c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_OP GENMASK(2, 0) 398c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_OP_NORMAL 0 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL 0x4 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_RST_DELAY GENMASK(10, 8) 448c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_CMD_RST BIT(7) 458c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_SOFT_RST BIT(6) 468c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_SW_RST BIT(5) 478c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_HW_RST BIT(4) 488c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_CLK_PAUSE BIT(3) 498c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_CLK_STOP_CLR BIT(2) 508c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1) 518c2ecf20Sopenharmony_ci#define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define CDNS_MCP_CMDCTRL 0x8 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define CDNS_MCP_SSPSTAT 0xC 588c2ecf20Sopenharmony_ci#define CDNS_MCP_FRAME_SHAPE 0x10 598c2ecf20Sopenharmony_ci#define CDNS_MCP_FRAME_SHAPE_INIT 0x14 608c2ecf20Sopenharmony_ci#define CDNS_MCP_FRAME_SHAPE_COL_MASK GENMASK(2, 0) 618c2ecf20Sopenharmony_ci#define CDNS_MCP_FRAME_SHAPE_ROW_MASK GENMASK(7, 3) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_UPDATE 0x18 648c2ecf20Sopenharmony_ci#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define CDNS_MCP_PHYCTRL 0x1C 678c2ecf20Sopenharmony_ci#define CDNS_MCP_SSP_CTRL0 0x20 688c2ecf20Sopenharmony_ci#define CDNS_MCP_SSP_CTRL1 0x28 698c2ecf20Sopenharmony_ci#define CDNS_MCP_CLK_CTRL0 0x30 708c2ecf20Sopenharmony_ci#define CDNS_MCP_CLK_CTRL1 0x38 718c2ecf20Sopenharmony_ci#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define CDNS_MCP_STAT 0x40 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define CDNS_MCP_STAT_ACTIVE_BANK BIT(20) 768c2ecf20Sopenharmony_ci#define CDNS_MCP_STAT_CLK_STOP BIT(16) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define CDNS_MCP_INTSTAT 0x44 798c2ecf20Sopenharmony_ci#define CDNS_MCP_INTMASK 0x48 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_IRQ BIT(31) 828c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17) 838c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_WAKEUP BIT(16) 848c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_SLAVE_RSVD BIT(15) 858c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_SLAVE_ALERT BIT(14) 868c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_SLAVE_ATTACH BIT(13) 878c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_SLAVE_NATTACH BIT(12) 888c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_SLAVE_MASK GENMASK(15, 12) 898c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_DPINT BIT(11) 908c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_CTRL_CLASH BIT(10) 918c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_DATA_CLASH BIT(9) 928c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_PARITY BIT(8) 938c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_CMD_ERR BIT(7) 948c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4) 958c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_RX_NE BIT(3) 968c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_RX_WL BIT(2) 978c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_TXE BIT(1) 988c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_TXF BIT(0) 998c2ecf20Sopenharmony_ci#define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define CDNS_MCP_INTSET 0x4C 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_STAT 0x50 1048c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_STAT_MASK GENMASK(1, 0) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT0 0x54 1078c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT1 0x58 1088c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT_NPRESENT BIT(0) 1098c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT_ATTACHED BIT(1) 1108c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT_ALERT BIT(2) 1118c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTSTAT_RESERVED BIT(3) 1128c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_STATUS_BITS GENMASK(3, 0) 1138c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_STATUS_NUM 4 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTMASK0 0x5C 1168c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTMASK1 0x60 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) 1198c2ecf20Sopenharmony_ci#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define CDNS_MCP_PORT_INTSTAT 0x64 1228c2ecf20Sopenharmony_ci#define CDNS_MCP_PDI_STAT 0x6C 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define CDNS_MCP_FIFOLEVEL 0x78 1258c2ecf20Sopenharmony_ci#define CDNS_MCP_FIFOSTAT 0x7C 1268c2ecf20Sopenharmony_ci#define CDNS_MCP_RX_FIFO_AVAIL GENMASK(5, 0) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_BASE 0x80 1298c2ecf20Sopenharmony_ci#define CDNS_MCP_RESP_BASE 0x80 1308c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_LEN 0x20 1318c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_WORD_LEN 0x4 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_SSP_TAG BIT(31) 1348c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_COMMAND GENMASK(30, 28) 1358c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24) 1368c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_REG_ADDR GENMASK(23, 8) 1378c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_READ 2 1408c2ecf20Sopenharmony_ci#define CDNS_MCP_CMD_WRITE 3 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define CDNS_MCP_RESP_RDATA GENMASK(15, 8) 1438c2ecf20Sopenharmony_ci#define CDNS_MCP_RESP_ACK BIT(0) 1448c2ecf20Sopenharmony_ci#define CDNS_MCP_RESP_NACK BIT(1) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define CDNS_DP_SIZE 128 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_CONFIG(n) (0x100 + CDNS_DP_SIZE * (n)) 1498c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_CH_EN(n) (0x104 + CDNS_DP_SIZE * (n)) 1508c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_SAMPLE_CTRL(n) (0x108 + CDNS_DP_SIZE * (n)) 1518c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_OFFSET_CTRL(n) (0x10C + CDNS_DP_SIZE * (n)) 1528c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_HCTRL(n) (0x110 + CDNS_DP_SIZE * (n)) 1538c2ecf20Sopenharmony_ci#define CDNS_DPN_B0_ASYNC_CTRL(n) (0x114 + CDNS_DP_SIZE * (n)) 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_CONFIG(n) (0x118 + CDNS_DP_SIZE * (n)) 1568c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_CH_EN(n) (0x11C + CDNS_DP_SIZE * (n)) 1578c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_SAMPLE_CTRL(n) (0x120 + CDNS_DP_SIZE * (n)) 1588c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_OFFSET_CTRL(n) (0x124 + CDNS_DP_SIZE * (n)) 1598c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_HCTRL(n) (0x128 + CDNS_DP_SIZE * (n)) 1608c2ecf20Sopenharmony_ci#define CDNS_DPN_B1_ASYNC_CTRL(n) (0x12C + CDNS_DP_SIZE * (n)) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define CDNS_DPN_CONFIG_BPM BIT(18) 1638c2ecf20Sopenharmony_ci#define CDNS_DPN_CONFIG_BGC GENMASK(17, 16) 1648c2ecf20Sopenharmony_ci#define CDNS_DPN_CONFIG_WL GENMASK(12, 8) 1658c2ecf20Sopenharmony_ci#define CDNS_DPN_CONFIG_PORT_DAT GENMASK(3, 2) 1668c2ecf20Sopenharmony_ci#define CDNS_DPN_CONFIG_PORT_FLOW GENMASK(1, 0) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define CDNS_DPN_SAMPLE_CTRL_SI GENMASK(15, 0) 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define CDNS_DPN_OFFSET_CTRL_1 GENMASK(7, 0) 1718c2ecf20Sopenharmony_ci#define CDNS_DPN_OFFSET_CTRL_2 GENMASK(15, 8) 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#define CDNS_DPN_HCTRL_HSTOP GENMASK(3, 0) 1748c2ecf20Sopenharmony_ci#define CDNS_DPN_HCTRL_HSTART GENMASK(7, 4) 1758c2ecf20Sopenharmony_ci#define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define CDNS_PORTCTRL 0x130 1788c2ecf20Sopenharmony_ci#define CDNS_PORTCTRL_TEST_FAILED BIT(1) 1798c2ecf20Sopenharmony_ci#define CDNS_PORTCTRL_DIRN BIT(7) 1808c2ecf20Sopenharmony_ci#define CDNS_PORTCTRL_BANK_INVERT BIT(8) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define CDNS_PORT_OFFSET 0x80 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define CDNS_PDI_CONFIG(n) (0x1100 + (n) * 16) 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define CDNS_PDI_CONFIG_SOFT_RESET BIT(24) 1878c2ecf20Sopenharmony_ci#define CDNS_PDI_CONFIG_CHANNEL GENMASK(15, 8) 1888c2ecf20Sopenharmony_ci#define CDNS_PDI_CONFIG_PORT GENMASK(4, 0) 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* Driver defaults */ 1918c2ecf20Sopenharmony_ci#define CDNS_TX_TIMEOUT 2000 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci#define CDNS_SCP_RX_FIFOLEVEL 0x2 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * register accessor helpers 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic inline u32 cdns_readl(struct sdw_cdns *cdns, int offset) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return readl(cdns->registers + offset); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci writel(value, cdns->registers + offset); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic inline void cdns_updatel(struct sdw_cdns *cdns, 2098c2ecf20Sopenharmony_ci int offset, u32 mask, u32 val) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci u32 tmp; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci tmp = cdns_readl(cdns, offset); 2148c2ecf20Sopenharmony_ci tmp = (tmp & ~mask) | val; 2158c2ecf20Sopenharmony_ci cdns_writel(cdns, offset, tmp); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int cdns_set_wait(struct sdw_cdns *cdns, int offset, u32 mask, u32 value) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int timeout = 10; 2218c2ecf20Sopenharmony_ci u32 reg_read; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Wait for bit to be set */ 2248c2ecf20Sopenharmony_ci do { 2258c2ecf20Sopenharmony_ci reg_read = readl(cdns->registers + offset); 2268c2ecf20Sopenharmony_ci if ((reg_read & mask) == value) 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci timeout--; 2308c2ecf20Sopenharmony_ci usleep_range(50, 100); 2318c2ecf20Sopenharmony_ci } while (timeout != 0); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci writel(value, cdns->registers + offset); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Wait for bit to be self cleared */ 2418c2ecf20Sopenharmony_ci return cdns_set_wait(cdns, offset, value, 0); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL 2468c2ecf20Sopenharmony_ci * need to be confirmed with a write to MCP_CONFIG_UPDATE 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_cistatic int cdns_config_update(struct sdw_cdns *cdns) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (sdw_cdns_is_clock_stop(cdns)) { 2538c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Cannot program MCP_CONFIG_UPDATE in ClockStopMode\n"); 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, 2588c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT); 2598c2ecf20Sopenharmony_ci if (ret < 0) 2608c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Config update timedout\n"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * debugfs 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci#define RD_BUF (2 * PAGE_SIZE) 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic ssize_t cdns_sprintf(struct sdw_cdns *cdns, 2738c2ecf20Sopenharmony_ci char *buf, size_t pos, unsigned int reg) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci return scnprintf(buf + pos, RD_BUF - pos, 2768c2ecf20Sopenharmony_ci "%4x\t%8x\n", reg, cdns_readl(cdns, reg)); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int cdns_reg_show(struct seq_file *s, void *data) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = s->private; 2828c2ecf20Sopenharmony_ci char *buf; 2838c2ecf20Sopenharmony_ci ssize_t ret; 2848c2ecf20Sopenharmony_ci int num_ports; 2858c2ecf20Sopenharmony_ci int i, j; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci buf = kzalloc(RD_BUF, GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!buf) 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = scnprintf(buf, RD_BUF, "Register Value\n"); 2928c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n"); 2938c2ecf20Sopenharmony_ci /* 8 MCP registers */ 2948c2ecf20Sopenharmony_ci for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32)) 2958c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, i); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 2988c2ecf20Sopenharmony_ci "\nStatus & Intr Registers\n"); 2998c2ecf20Sopenharmony_ci /* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */ 3008c2ecf20Sopenharmony_ci for (i = CDNS_MCP_STAT; i <= CDNS_MCP_FIFOSTAT; i += sizeof(u32)) 3018c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, i); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3048c2ecf20Sopenharmony_ci "\nSSP & Clk ctrl Registers\n"); 3058c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0); 3068c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1); 3078c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0); 3088c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3118c2ecf20Sopenharmony_ci "\nDPn B0 Registers\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci num_ports = cdns->num_ports; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) { 3168c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3178c2ecf20Sopenharmony_ci "\nDP-%d\n", i); 3188c2ecf20Sopenharmony_ci for (j = CDNS_DPN_B0_CONFIG(i); 3198c2ecf20Sopenharmony_ci j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32)) 3208c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, j); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3248c2ecf20Sopenharmony_ci "\nDPn B1 Registers\n"); 3258c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) { 3268c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3278c2ecf20Sopenharmony_ci "\nDP-%d\n", i); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci for (j = CDNS_DPN_B1_CONFIG(i); 3308c2ecf20Sopenharmony_ci j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32)) 3318c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, j); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3358c2ecf20Sopenharmony_ci "\nDPn Control Registers\n"); 3368c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) 3378c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, 3388c2ecf20Sopenharmony_ci CDNS_PORTCTRL + i * CDNS_PORT_OFFSET); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, RD_BUF - ret, 3418c2ecf20Sopenharmony_ci "\nPDIn Config Registers\n"); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* number of PDI and ports is interchangeable */ 3448c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) 3458c2ecf20Sopenharmony_ci ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i)); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci seq_printf(s, "%s", buf); 3488c2ecf20Sopenharmony_ci kfree(buf); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(cdns_reg); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int cdns_hw_reset(void *data, u64 value) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = data; 3578c2ecf20Sopenharmony_ci int ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (value != 1) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Userspace changed the hardware state behind the kernel's back */ 3638c2ecf20Sopenharmony_ci add_taint(TAINT_USER, LOCKDEP_STILL_OK); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = sdw_cdns_exit_reset(cdns); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int cdns_parity_error_injection(void *data, u64 value) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = data; 3778c2ecf20Sopenharmony_ci struct sdw_bus *bus; 3788c2ecf20Sopenharmony_ci int ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (value != 1) 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci bus = &cdns->bus; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Resume Master device. If this results in a bus reset, the 3878c2ecf20Sopenharmony_ci * Slave devices will re-attach and be re-enumerated. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(bus->dev); 3908c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 3918c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, 3928c2ecf20Sopenharmony_ci "pm_runtime_get_sync failed in %s, ret %d\n", 3938c2ecf20Sopenharmony_ci __func__, ret); 3948c2ecf20Sopenharmony_ci pm_runtime_put_noidle(bus->dev); 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * wait long enough for Slave(s) to be in steady state. This 4008c2ecf20Sopenharmony_ci * does not need to be super precise. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci msleep(200); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Take the bus lock here to make sure that any bus transactions 4068c2ecf20Sopenharmony_ci * will be queued while we inject a parity error on a dummy read 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci mutex_lock(&bus->bus_lock); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* program hardware to inject parity error */ 4118c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CMDCTRL, 4128c2ecf20Sopenharmony_ci CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, 4138c2ecf20Sopenharmony_ci CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* commit changes */ 4168c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, 4178c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT, 4188c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* do a broadcast dummy read to avoid bus clashes */ 4218c2ecf20Sopenharmony_ci ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0); 4228c2ecf20Sopenharmony_ci dev_info(cdns->dev, "parity error injection, read: %d\n", ret); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* program hardware to disable parity error */ 4258c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CMDCTRL, 4268c2ecf20Sopenharmony_ci CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, 4278c2ecf20Sopenharmony_ci 0); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* commit changes */ 4308c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, 4318c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT, 4328c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Continue bus operation with parity error injection disabled */ 4358c2ecf20Sopenharmony_ci mutex_unlock(&bus->bus_lock); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Userspace changed the hardware state behind the kernel's back */ 4388c2ecf20Sopenharmony_ci add_taint(TAINT_USER, LOCKDEP_STILL_OK); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * allow Master device to enter pm_runtime suspend. This may 4428c2ecf20Sopenharmony_ci * also result in Slave devices suspending. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bus->dev); 4458c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bus->dev); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL, 4518c2ecf20Sopenharmony_ci cdns_parity_error_injection, "%llu\n"); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/** 4548c2ecf20Sopenharmony_ci * sdw_cdns_debugfs_init() - Cadence debugfs init 4558c2ecf20Sopenharmony_ci * @cdns: Cadence instance 4568c2ecf20Sopenharmony_ci * @root: debugfs root 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_civoid sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci debugfs_create_file("cdns-hw-reset", 0200, root, cdns, 4638c2ecf20Sopenharmony_ci &cdns_hw_reset_fops); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns, 4668c2ecf20Sopenharmony_ci &cdns_parity_error_fops); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* 4738c2ecf20Sopenharmony_ci * IO Calls 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_cistatic enum sdw_command_response 4768c2ecf20Sopenharmony_cicdns_fill_msg_resp(struct sdw_cdns *cdns, 4778c2ecf20Sopenharmony_ci struct sdw_msg *msg, int count, int offset) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci int nack = 0, no_ack = 0; 4808c2ecf20Sopenharmony_ci int i; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* check message response */ 4838c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 4848c2ecf20Sopenharmony_ci if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { 4858c2ecf20Sopenharmony_ci no_ack = 1; 4868c2ecf20Sopenharmony_ci dev_dbg_ratelimited(cdns->dev, "Msg Ack not received\n"); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { 4898c2ecf20Sopenharmony_ci nack = 1; 4908c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "Msg NACK received\n"); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (nack) { 4958c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); 4968c2ecf20Sopenharmony_ci return SDW_CMD_FAIL; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (no_ack) { 5008c2ecf20Sopenharmony_ci dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); 5018c2ecf20Sopenharmony_ci return SDW_CMD_IGNORED; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (msg->flags == SDW_MSG_FLAG_READ) { 5058c2ecf20Sopenharmony_ci /* fill response */ 5068c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 5078c2ecf20Sopenharmony_ci msg->buf[i + offset] = FIELD_GET(CDNS_MCP_RESP_RDATA, 5088c2ecf20Sopenharmony_ci cdns->response_buf[i]); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return SDW_CMD_OK; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void cdns_read_response(struct sdw_cdns *cdns) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci u32 num_resp, cmd_base; 5178c2ecf20Sopenharmony_ci int i; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* RX_FIFO_AVAIL can be 2 entries more than the FIFO size */ 5208c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(cdns->response_buf) < CDNS_MCP_CMD_LEN + 2); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci num_resp = cdns_readl(cdns, CDNS_MCP_FIFOSTAT); 5238c2ecf20Sopenharmony_ci num_resp &= CDNS_MCP_RX_FIFO_AVAIL; 5248c2ecf20Sopenharmony_ci if (num_resp > ARRAY_SIZE(cdns->response_buf)) { 5258c2ecf20Sopenharmony_ci dev_warn(cdns->dev, "RX AVAIL %d too long\n", num_resp); 5268c2ecf20Sopenharmony_ci num_resp = ARRAY_SIZE(cdns->response_buf); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci cmd_base = CDNS_MCP_CMD_BASE; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci for (i = 0; i < num_resp; i++) { 5328c2ecf20Sopenharmony_ci cdns->response_buf[i] = cdns_readl(cdns, cmd_base); 5338c2ecf20Sopenharmony_ci cmd_base += CDNS_MCP_CMD_WORD_LEN; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic enum sdw_command_response 5388c2ecf20Sopenharmony_ci_cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, 5398c2ecf20Sopenharmony_ci int offset, int count, bool defer) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned long time; 5428c2ecf20Sopenharmony_ci u32 base, i, data; 5438c2ecf20Sopenharmony_ci u16 addr; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Program the watermark level for RX FIFO */ 5468c2ecf20Sopenharmony_ci if (cdns->msg_count != count) { 5478c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, count); 5488c2ecf20Sopenharmony_ci cdns->msg_count = count; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci base = CDNS_MCP_CMD_BASE; 5528c2ecf20Sopenharmony_ci addr = msg->addr; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 5558c2ecf20Sopenharmony_ci data = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num); 5568c2ecf20Sopenharmony_ci data |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, cmd); 5578c2ecf20Sopenharmony_ci data |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, addr); 5588c2ecf20Sopenharmony_ci addr++; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (msg->flags == SDW_MSG_FLAG_WRITE) 5618c2ecf20Sopenharmony_ci data |= msg->buf[i + offset]; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci data |= FIELD_PREP(CDNS_MCP_CMD_SSP_TAG, msg->ssp_sync); 5648c2ecf20Sopenharmony_ci cdns_writel(cdns, base, data); 5658c2ecf20Sopenharmony_ci base += CDNS_MCP_CMD_WORD_LEN; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (defer) 5698c2ecf20Sopenharmony_ci return SDW_CMD_OK; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* wait for timeout or response */ 5728c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&cdns->tx_complete, 5738c2ecf20Sopenharmony_ci msecs_to_jiffies(CDNS_TX_TIMEOUT)); 5748c2ecf20Sopenharmony_ci if (!time) { 5758c2ecf20Sopenharmony_ci dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n", 5768c2ecf20Sopenharmony_ci cmd, msg->dev_num, msg->addr, msg->len); 5778c2ecf20Sopenharmony_ci msg->len = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Drain anything in the RX_FIFO */ 5808c2ecf20Sopenharmony_ci cdns_read_response(cdns); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return SDW_CMD_TIMEOUT; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return cdns_fill_msg_resp(cdns, msg, count, offset); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic enum sdw_command_response 5898c2ecf20Sopenharmony_cicdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci int nack = 0, no_ack = 0; 5928c2ecf20Sopenharmony_ci unsigned long time; 5938c2ecf20Sopenharmony_ci u32 data[2], base; 5948c2ecf20Sopenharmony_ci int i; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Program the watermark level for RX FIFO */ 5978c2ecf20Sopenharmony_ci if (cdns->msg_count != CDNS_SCP_RX_FIFOLEVEL) { 5988c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, CDNS_SCP_RX_FIFOLEVEL); 5998c2ecf20Sopenharmony_ci cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci data[0] = FIELD_PREP(CDNS_MCP_CMD_DEV_ADDR, msg->dev_num); 6038c2ecf20Sopenharmony_ci data[0] |= FIELD_PREP(CDNS_MCP_CMD_COMMAND, 0x3); 6048c2ecf20Sopenharmony_ci data[1] = data[0]; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci data[0] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE1); 6078c2ecf20Sopenharmony_ci data[1] |= FIELD_PREP(CDNS_MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE2); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci data[0] |= msg->addr_page1; 6108c2ecf20Sopenharmony_ci data[1] |= msg->addr_page2; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci base = CDNS_MCP_CMD_BASE; 6138c2ecf20Sopenharmony_ci cdns_writel(cdns, base, data[0]); 6148c2ecf20Sopenharmony_ci base += CDNS_MCP_CMD_WORD_LEN; 6158c2ecf20Sopenharmony_ci cdns_writel(cdns, base, data[1]); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&cdns->tx_complete, 6188c2ecf20Sopenharmony_ci msecs_to_jiffies(CDNS_TX_TIMEOUT)); 6198c2ecf20Sopenharmony_ci if (!time) { 6208c2ecf20Sopenharmony_ci dev_err(cdns->dev, "SCP Msg trf timed out\n"); 6218c2ecf20Sopenharmony_ci msg->len = 0; 6228c2ecf20Sopenharmony_ci return SDW_CMD_TIMEOUT; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* check response the writes */ 6268c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 6278c2ecf20Sopenharmony_ci if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { 6288c2ecf20Sopenharmony_ci no_ack = 1; 6298c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Program SCP Ack not received\n"); 6308c2ecf20Sopenharmony_ci if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { 6318c2ecf20Sopenharmony_ci nack = 1; 6328c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Program SCP NACK received\n"); 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* For NACK, NO ack, don't return err if we are in Broadcast mode */ 6388c2ecf20Sopenharmony_ci if (nack) { 6398c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, 6408c2ecf20Sopenharmony_ci "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); 6418c2ecf20Sopenharmony_ci return SDW_CMD_FAIL; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (no_ack) { 6458c2ecf20Sopenharmony_ci dev_dbg_ratelimited(cdns->dev, 6468c2ecf20Sopenharmony_ci "SCP_addrpage ignored for Slave %d\n", msg->dev_num); 6478c2ecf20Sopenharmony_ci return SDW_CMD_IGNORED; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return SDW_CMD_OK; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci int ret; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (msg->page) { 6588c2ecf20Sopenharmony_ci ret = cdns_program_scp_addr(cdns, msg); 6598c2ecf20Sopenharmony_ci if (ret) { 6608c2ecf20Sopenharmony_ci msg->len = 0; 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci switch (msg->flags) { 6668c2ecf20Sopenharmony_ci case SDW_MSG_FLAG_READ: 6678c2ecf20Sopenharmony_ci *cmd = CDNS_MCP_CMD_READ; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci case SDW_MSG_FLAG_WRITE: 6718c2ecf20Sopenharmony_ci *cmd = CDNS_MCP_CMD_WRITE; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci default: 6758c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Invalid msg cmd: %d\n", msg->flags); 6768c2ecf20Sopenharmony_ci return -EINVAL; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cienum sdw_command_response 6838c2ecf20Sopenharmony_cicdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 6868c2ecf20Sopenharmony_ci int cmd = 0, ret, i; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = cdns_prep_msg(cdns, msg, &cmd); 6898c2ecf20Sopenharmony_ci if (ret) 6908c2ecf20Sopenharmony_ci return SDW_CMD_FAIL_OTHER; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) { 6938c2ecf20Sopenharmony_ci ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, 6948c2ecf20Sopenharmony_ci CDNS_MCP_CMD_LEN, false); 6958c2ecf20Sopenharmony_ci if (ret < 0) 6968c2ecf20Sopenharmony_ci goto exit; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (!(msg->len % CDNS_MCP_CMD_LEN)) 7008c2ecf20Sopenharmony_ci goto exit; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, 7038c2ecf20Sopenharmony_ci msg->len % CDNS_MCP_CMD_LEN, false); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ciexit: 7068c2ecf20Sopenharmony_ci return ret; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cdns_xfer_msg); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cienum sdw_command_response 7118c2ecf20Sopenharmony_cicdns_xfer_msg_defer(struct sdw_bus *bus, 7128c2ecf20Sopenharmony_ci struct sdw_msg *msg, struct sdw_defer *defer) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 7158c2ecf20Sopenharmony_ci int cmd = 0, ret; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* for defer only 1 message is supported */ 7188c2ecf20Sopenharmony_ci if (msg->len > 1) 7198c2ecf20Sopenharmony_ci return -ENOTSUPP; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = cdns_prep_msg(cdns, msg, &cmd); 7228c2ecf20Sopenharmony_ci if (ret) 7238c2ecf20Sopenharmony_ci return SDW_CMD_FAIL_OTHER; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci cdns->defer = defer; 7268c2ecf20Sopenharmony_ci cdns->defer->length = msg->len; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cdns_xfer_msg_defer); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cienum sdw_command_response 7338c2ecf20Sopenharmony_cicdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 7368c2ecf20Sopenharmony_ci struct sdw_msg msg; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Create dummy message with valid device number */ 7398c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 7408c2ecf20Sopenharmony_ci msg.dev_num = dev_num; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return cdns_program_scp_addr(cdns, &msg); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cdns_reset_page_addr); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/* 7478c2ecf20Sopenharmony_ci * IRQ handling 7488c2ecf20Sopenharmony_ci */ 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int cdns_update_slave_status(struct sdw_cdns *cdns, 7518c2ecf20Sopenharmony_ci u32 slave0, u32 slave1) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; 7548c2ecf20Sopenharmony_ci bool is_slave = false; 7558c2ecf20Sopenharmony_ci u64 slave; 7568c2ecf20Sopenharmony_ci u32 mask; 7578c2ecf20Sopenharmony_ci int i, set_status; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* combine the two status */ 7608c2ecf20Sopenharmony_ci slave = ((u64)slave1 << 32) | slave0; 7618c2ecf20Sopenharmony_ci memset(status, 0, sizeof(status)); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci for (i = 0; i <= SDW_MAX_DEVICES; i++) { 7648c2ecf20Sopenharmony_ci mask = (slave >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) & 7658c2ecf20Sopenharmony_ci CDNS_MCP_SLAVE_STATUS_BITS; 7668c2ecf20Sopenharmony_ci if (!mask) 7678c2ecf20Sopenharmony_ci continue; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci is_slave = true; 7708c2ecf20Sopenharmony_ci set_status = 0; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) { 7738c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_RESERVED; 7748c2ecf20Sopenharmony_ci set_status++; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) { 7788c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_ATTACHED; 7798c2ecf20Sopenharmony_ci set_status++; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) { 7838c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_ALERT; 7848c2ecf20Sopenharmony_ci set_status++; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) { 7888c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_UNATTACHED; 7898c2ecf20Sopenharmony_ci set_status++; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* first check if Slave reported multiple status */ 7938c2ecf20Sopenharmony_ci if (set_status > 1) { 7948c2ecf20Sopenharmony_ci u32 val; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci dev_warn_ratelimited(cdns->dev, 7978c2ecf20Sopenharmony_ci "Slave %d reported multiple Status: %d\n", 7988c2ecf20Sopenharmony_ci i, mask); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* check latest status extracted from PING commands */ 8018c2ecf20Sopenharmony_ci val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); 8028c2ecf20Sopenharmony_ci val >>= (i * 2); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci switch (val & 0x3) { 8058c2ecf20Sopenharmony_ci case 0: 8068c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_UNATTACHED; 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci case 1: 8098c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_ATTACHED; 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci case 2: 8128c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_ALERT; 8138c2ecf20Sopenharmony_ci break; 8148c2ecf20Sopenharmony_ci case 3: 8158c2ecf20Sopenharmony_ci default: 8168c2ecf20Sopenharmony_ci status[i] = SDW_SLAVE_RESERVED; 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci dev_warn_ratelimited(cdns->dev, 8218c2ecf20Sopenharmony_ci "Slave %d status updated to %d\n", 8228c2ecf20Sopenharmony_ci i, status[i]); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (is_slave) 8288c2ecf20Sopenharmony_ci return sdw_handle_slave_status(&cdns->bus, status); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci/** 8348c2ecf20Sopenharmony_ci * sdw_cdns_irq() - Cadence interrupt handler 8358c2ecf20Sopenharmony_ci * @irq: irq number 8368c2ecf20Sopenharmony_ci * @dev_id: irq context 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ciirqreturn_t sdw_cdns_irq(int irq, void *dev_id) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = dev_id; 8418c2ecf20Sopenharmony_ci u32 int_status; 8428c2ecf20Sopenharmony_ci int ret = IRQ_HANDLED; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Check if the link is up */ 8458c2ecf20Sopenharmony_ci if (!cdns->link_up) 8468c2ecf20Sopenharmony_ci return IRQ_NONE; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* check for reserved values read as zero */ 8518c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_RESERVED) 8528c2ecf20Sopenharmony_ci return IRQ_NONE; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (!(int_status & CDNS_MCP_INT_IRQ)) 8558c2ecf20Sopenharmony_ci return IRQ_NONE; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_RX_WL) { 8588c2ecf20Sopenharmony_ci cdns_read_response(cdns); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (cdns->defer) { 8618c2ecf20Sopenharmony_ci cdns_fill_msg_resp(cdns, cdns->defer->msg, 8628c2ecf20Sopenharmony_ci cdns->defer->length, 0); 8638c2ecf20Sopenharmony_ci complete(&cdns->defer->complete); 8648c2ecf20Sopenharmony_ci cdns->defer = NULL; 8658c2ecf20Sopenharmony_ci } else { 8668c2ecf20Sopenharmony_ci complete(&cdns->tx_complete); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_PARITY) { 8718c2ecf20Sopenharmony_ci /* Parity error detected by Master */ 8728c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "Parity error\n"); 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_CTRL_CLASH) { 8768c2ecf20Sopenharmony_ci /* Slave is driving bit slot during control word */ 8778c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_DATA_CLASH) { 8818c2ecf20Sopenharmony_ci /* 8828c2ecf20Sopenharmony_ci * Multiple slaves trying to drive bit slot, or issue with 8838c2ecf20Sopenharmony_ci * ownership of data bits or Slave gone bonkers 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL && 8898c2ecf20Sopenharmony_ci int_status & CDNS_MCP_INT_DPINT) { 8908c2ecf20Sopenharmony_ci u32 port_intstat; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* just log which ports report an error */ 8938c2ecf20Sopenharmony_ci port_intstat = cdns_readl(cdns, CDNS_MCP_PORT_INTSTAT); 8948c2ecf20Sopenharmony_ci dev_err_ratelimited(cdns->dev, "DP interrupt: PortIntStat %8x\n", 8958c2ecf20Sopenharmony_ci port_intstat); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* clear status w/ write1 */ 8988c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_PORT_INTSTAT, port_intstat); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (int_status & CDNS_MCP_INT_SLAVE_MASK) { 9028c2ecf20Sopenharmony_ci /* Mask the Slave interrupt and wake thread */ 9038c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_INTMASK, 9048c2ecf20Sopenharmony_ci CDNS_MCP_INT_SLAVE_MASK, 0); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci int_status &= ~CDNS_MCP_INT_SLAVE_MASK; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* 9098c2ecf20Sopenharmony_ci * Deal with possible race condition between interrupt 9108c2ecf20Sopenharmony_ci * handling and disabling interrupts on suspend. 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * If the master is in the process of disabling 9138c2ecf20Sopenharmony_ci * interrupts, don't schedule a workqueue 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci if (cdns->interrupt_enabled) 9168c2ecf20Sopenharmony_ci schedule_work(&cdns->work); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); 9208c2ecf20Sopenharmony_ci return ret; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_irq); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci/** 9258c2ecf20Sopenharmony_ci * To update slave status in a work since we will need to handle 9268c2ecf20Sopenharmony_ci * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave 9278c2ecf20Sopenharmony_ci * process. 9288c2ecf20Sopenharmony_ci * @work: cdns worker thread 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_cistatic void cdns_update_slave_status_work(struct work_struct *work) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = 9338c2ecf20Sopenharmony_ci container_of(work, struct sdw_cdns, work); 9348c2ecf20Sopenharmony_ci u32 slave0, slave1; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); 9398c2ecf20Sopenharmony_ci slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci cdns_update_slave_status(cdns, slave0, slave1); 9428c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0); 9438c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* clear and unmask Slave interrupt now */ 9468c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK); 9478c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_INTMASK, 9488c2ecf20Sopenharmony_ci CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci/* 9538c2ecf20Sopenharmony_ci * init routines 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/** 9578c2ecf20Sopenharmony_ci * sdw_cdns_exit_reset() - Program reset parameters and start bus operations 9588c2ecf20Sopenharmony_ci * @cdns: Cadence instance 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ciint sdw_cdns_exit_reset(struct sdw_cdns *cdns) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci /* program maximum length reset to be safe */ 9638c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 9648c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_RST_DELAY, 9658c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_RST_DELAY); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* use hardware generated reset */ 9688c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 9698c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_HW_RST, 9708c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_HW_RST); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* commit changes */ 9738c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, 9748c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT, 9758c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_UPDATE_BIT); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* don't wait here */ 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_exit_reset); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci/** 9848c2ecf20Sopenharmony_ci * sdw_cdns_enable_slave_interrupt() - Enable SDW slave interrupts 9858c2ecf20Sopenharmony_ci * @cdns: Cadence instance 9868c2ecf20Sopenharmony_ci * @state: boolean for true/false 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_cistatic void cdns_enable_slave_interrupts(struct sdw_cdns *cdns, bool state) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci u32 mask; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci mask = cdns_readl(cdns, CDNS_MCP_INTMASK); 9938c2ecf20Sopenharmony_ci if (state) 9948c2ecf20Sopenharmony_ci mask |= CDNS_MCP_INT_SLAVE_MASK; 9958c2ecf20Sopenharmony_ci else 9968c2ecf20Sopenharmony_ci mask &= ~CDNS_MCP_INT_SLAVE_MASK; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_INTMASK, mask); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/** 10028c2ecf20Sopenharmony_ci * sdw_cdns_enable_interrupt() - Enable SDW interrupts 10038c2ecf20Sopenharmony_ci * @cdns: Cadence instance 10048c2ecf20Sopenharmony_ci * @state: True if we are trying to enable interrupt. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ciint sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci u32 slave_intmask0 = 0; 10098c2ecf20Sopenharmony_ci u32 slave_intmask1 = 0; 10108c2ecf20Sopenharmony_ci u32 mask = 0; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (!state) 10138c2ecf20Sopenharmony_ci goto update_masks; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK; 10168c2ecf20Sopenharmony_ci slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* enable detection of all slave state changes */ 10198c2ecf20Sopenharmony_ci mask = CDNS_MCP_INT_SLAVE_MASK; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* enable detection of bus issues */ 10228c2ecf20Sopenharmony_ci mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | 10238c2ecf20Sopenharmony_ci CDNS_MCP_INT_PARITY; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* port interrupt limited to test modes for now */ 10268c2ecf20Sopenharmony_ci if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL) 10278c2ecf20Sopenharmony_ci mask |= CDNS_MCP_INT_DPINT; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* enable detection of RX fifo level */ 10308c2ecf20Sopenharmony_ci mask |= CDNS_MCP_INT_RX_WL; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * CDNS_MCP_INT_IRQ needs to be set otherwise all previous 10348c2ecf20Sopenharmony_ci * settings are irrelevant 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci mask |= CDNS_MCP_INT_IRQ; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (interrupt_mask) /* parameter override */ 10398c2ecf20Sopenharmony_ci mask = interrupt_mask; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ciupdate_masks: 10428c2ecf20Sopenharmony_ci /* clear slave interrupt status before enabling interrupt */ 10438c2ecf20Sopenharmony_ci if (state) { 10448c2ecf20Sopenharmony_ci u32 slave_state; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); 10478c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state); 10488c2ecf20Sopenharmony_ci slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); 10498c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci cdns->interrupt_enabled = state; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* 10548c2ecf20Sopenharmony_ci * Complete any on-going status updates before updating masks, 10558c2ecf20Sopenharmony_ci * and cancel queued status updates. 10568c2ecf20Sopenharmony_ci * 10578c2ecf20Sopenharmony_ci * There could be a race with a new interrupt thrown before 10588c2ecf20Sopenharmony_ci * the 3 mask updates below are complete, so in the interrupt 10598c2ecf20Sopenharmony_ci * we use the 'interrupt_enabled' status to prevent new work 10608c2ecf20Sopenharmony_ci * from being queued. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci if (!state) 10638c2ecf20Sopenharmony_ci cancel_work_sync(&cdns->work); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); 10668c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); 10678c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_INTMASK, mask); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_enable_interrupt); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic int cdns_allocate_pdi(struct sdw_cdns *cdns, 10748c2ecf20Sopenharmony_ci struct sdw_cdns_pdi **stream, 10758c2ecf20Sopenharmony_ci u32 num, u32 pdi_offset) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct sdw_cdns_pdi *pdi; 10788c2ecf20Sopenharmony_ci int i; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (!num) 10818c2ecf20Sopenharmony_ci return 0; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); 10848c2ecf20Sopenharmony_ci if (!pdi) 10858c2ecf20Sopenharmony_ci return -ENOMEM; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 10888c2ecf20Sopenharmony_ci pdi[i].num = i + pdi_offset; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci *stream = pdi; 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci/** 10968c2ecf20Sopenharmony_ci * sdw_cdns_pdi_init() - PDI initialization routine 10978c2ecf20Sopenharmony_ci * 10988c2ecf20Sopenharmony_ci * @cdns: Cadence instance 10998c2ecf20Sopenharmony_ci * @config: Stream configurations 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_ciint sdw_cdns_pdi_init(struct sdw_cdns *cdns, 11028c2ecf20Sopenharmony_ci struct sdw_cdns_stream_config config) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct sdw_cdns_streams *stream; 11058c2ecf20Sopenharmony_ci int offset; 11068c2ecf20Sopenharmony_ci int ret; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci cdns->pcm.num_bd = config.pcm_bd; 11098c2ecf20Sopenharmony_ci cdns->pcm.num_in = config.pcm_in; 11108c2ecf20Sopenharmony_ci cdns->pcm.num_out = config.pcm_out; 11118c2ecf20Sopenharmony_ci cdns->pdm.num_bd = config.pdm_bd; 11128c2ecf20Sopenharmony_ci cdns->pdm.num_in = config.pdm_in; 11138c2ecf20Sopenharmony_ci cdns->pdm.num_out = config.pdm_out; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* Allocate PDIs for PCMs */ 11168c2ecf20Sopenharmony_ci stream = &cdns->pcm; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* we allocate PDI0 and PDI1 which are used for Bulk */ 11198c2ecf20Sopenharmony_ci offset = 0; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->bd, 11228c2ecf20Sopenharmony_ci stream->num_bd, offset); 11238c2ecf20Sopenharmony_ci if (ret) 11248c2ecf20Sopenharmony_ci return ret; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci offset += stream->num_bd; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->in, 11298c2ecf20Sopenharmony_ci stream->num_in, offset); 11308c2ecf20Sopenharmony_ci if (ret) 11318c2ecf20Sopenharmony_ci return ret; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci offset += stream->num_in; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->out, 11368c2ecf20Sopenharmony_ci stream->num_out, offset); 11378c2ecf20Sopenharmony_ci if (ret) 11388c2ecf20Sopenharmony_ci return ret; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* Update total number of PCM PDIs */ 11418c2ecf20Sopenharmony_ci stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; 11428c2ecf20Sopenharmony_ci cdns->num_ports = stream->num_pdi; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* Allocate PDIs for PDMs */ 11458c2ecf20Sopenharmony_ci stream = &cdns->pdm; 11468c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->bd, 11478c2ecf20Sopenharmony_ci stream->num_bd, offset); 11488c2ecf20Sopenharmony_ci if (ret) 11498c2ecf20Sopenharmony_ci return ret; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci offset += stream->num_bd; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->in, 11548c2ecf20Sopenharmony_ci stream->num_in, offset); 11558c2ecf20Sopenharmony_ci if (ret) 11568c2ecf20Sopenharmony_ci return ret; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci offset += stream->num_in; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci ret = cdns_allocate_pdi(cdns, &stream->out, 11618c2ecf20Sopenharmony_ci stream->num_out, offset); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (ret) 11648c2ecf20Sopenharmony_ci return ret; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Update total number of PDM PDIs */ 11678c2ecf20Sopenharmony_ci stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; 11688c2ecf20Sopenharmony_ci cdns->num_ports += stream->num_pdi; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_pdi_init); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci u32 val; 11778c2ecf20Sopenharmony_ci int c; 11788c2ecf20Sopenharmony_ci int r; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci r = sdw_find_row_index(n_rows); 11818c2ecf20Sopenharmony_ci c = sdw_find_col_index(n_cols); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci val = FIELD_PREP(CDNS_MCP_FRAME_SHAPE_ROW_MASK, r); 11848c2ecf20Sopenharmony_ci val |= FIELD_PREP(CDNS_MCP_FRAME_SHAPE_COL_MASK, c); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return val; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic void cdns_init_clock_ctrl(struct sdw_cdns *cdns) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci struct sdw_bus *bus = &cdns->bus; 11928c2ecf20Sopenharmony_ci struct sdw_master_prop *prop = &bus->prop; 11938c2ecf20Sopenharmony_ci u32 val; 11948c2ecf20Sopenharmony_ci u32 ssp_interval; 11958c2ecf20Sopenharmony_ci int divider; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Set clock divider */ 11988c2ecf20Sopenharmony_ci divider = (prop->mclk_freq / prop->max_clk_freq) - 1; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, 12018c2ecf20Sopenharmony_ci CDNS_MCP_CLK_MCLKD_MASK, divider); 12028c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, 12038c2ecf20Sopenharmony_ci CDNS_MCP_CLK_MCLKD_MASK, divider); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* 12068c2ecf20Sopenharmony_ci * Frame shape changes after initialization have to be done 12078c2ecf20Sopenharmony_ci * with the bank switch mechanism 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci val = cdns_set_initial_frame_shape(prop->default_row, 12108c2ecf20Sopenharmony_ci prop->default_col); 12118c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Set SSP interval to default value */ 12148c2ecf20Sopenharmony_ci ssp_interval = prop->default_frame_rate / SDW_CADENCE_GSYNC_HZ; 12158c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, ssp_interval); 12168c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, ssp_interval); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/** 12208c2ecf20Sopenharmony_ci * sdw_cdns_init() - Cadence initialization 12218c2ecf20Sopenharmony_ci * @cdns: Cadence instance 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_ciint sdw_cdns_init(struct sdw_cdns *cdns) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci u32 val; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci cdns_init_clock_ctrl(cdns); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* reset msg_count to default value of FIFOLEVEL */ 12308c2ecf20Sopenharmony_ci cdns->msg_count = cdns_readl(cdns, CDNS_MCP_FIFOLEVEL); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* flush command FIFOs */ 12338c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, 12348c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CMD_RST); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci /* Set cmd accept mode */ 12378c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 12388c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CMD_ACCEPT); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci /* Configure mcp config */ 12418c2ecf20Sopenharmony_ci val = cdns_readl(cdns, CDNS_MCP_CONFIG); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* enable bus operations with clock and data */ 12448c2ecf20Sopenharmony_ci val &= ~CDNS_MCP_CONFIG_OP; 12458c2ecf20Sopenharmony_ci val |= CDNS_MCP_CONFIG_OP_NORMAL; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* Set cmd mode for Tx and Rx cmds */ 12488c2ecf20Sopenharmony_ci val &= ~CDNS_MCP_CONFIG_CMD; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* Disable sniffer mode */ 12518c2ecf20Sopenharmony_ci val &= ~CDNS_MCP_CONFIG_SNIFFER; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* Disable auto bus release */ 12548c2ecf20Sopenharmony_ci val &= ~CDNS_MCP_CONFIG_BUS_REL; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (cdns->bus.multi_link) 12578c2ecf20Sopenharmony_ci /* Set Multi-master mode to take gsync into account */ 12588c2ecf20Sopenharmony_ci val |= CDNS_MCP_CONFIG_MMASTER; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* leave frame delay to hardware default of 0x1F */ 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* leave command retry to hardware default of 0 */ 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_MCP_CONFIG, val); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* changes will be committed later */ 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_init); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ciint cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct sdw_master_prop *prop = &bus->prop; 12748c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 12758c2ecf20Sopenharmony_ci int mcp_clkctrl_off; 12768c2ecf20Sopenharmony_ci int divider; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (!params->curr_dr_freq) { 12798c2ecf20Sopenharmony_ci dev_err(cdns->dev, "NULL curr_dr_freq\n"); 12808c2ecf20Sopenharmony_ci return -EINVAL; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR / 12848c2ecf20Sopenharmony_ci params->curr_dr_freq; 12858c2ecf20Sopenharmony_ci divider--; /* divider is 1/(N+1) */ 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (params->next_bank) 12888c2ecf20Sopenharmony_ci mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; 12898c2ecf20Sopenharmony_ci else 12908c2ecf20Sopenharmony_ci mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci return 0; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cdns_bus_conf); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic int cdns_port_params(struct sdw_bus *bus, 12998c2ecf20Sopenharmony_ci struct sdw_port_params *p_params, unsigned int bank) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 13028c2ecf20Sopenharmony_ci int dpn_config = 0, dpn_config_off; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (bank) 13058c2ecf20Sopenharmony_ci dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); 13068c2ecf20Sopenharmony_ci else 13078c2ecf20Sopenharmony_ci dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci dpn_config = cdns_readl(cdns, dpn_config_off); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_config, (p_params->bps - 1), CDNS_DPN_CONFIG_WL); 13128c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_config, p_params->flow_mode, CDNS_DPN_CONFIG_PORT_FLOW); 13138c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_config, p_params->data_mode, CDNS_DPN_CONFIG_PORT_DAT); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_config_off, dpn_config); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return 0; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic int cdns_transport_params(struct sdw_bus *bus, 13218c2ecf20Sopenharmony_ci struct sdw_transport_params *t_params, 13228c2ecf20Sopenharmony_ci enum sdw_reg_bank bank) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 13258c2ecf20Sopenharmony_ci int dpn_offsetctrl = 0, dpn_offsetctrl_off; 13268c2ecf20Sopenharmony_ci int dpn_config = 0, dpn_config_off; 13278c2ecf20Sopenharmony_ci int dpn_hctrl = 0, dpn_hctrl_off; 13288c2ecf20Sopenharmony_ci int num = t_params->port_num; 13298c2ecf20Sopenharmony_ci int dpn_samplectrl_off; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* 13328c2ecf20Sopenharmony_ci * Note: Only full data port is supported on the Master side for 13338c2ecf20Sopenharmony_ci * both PCM and PDM ports. 13348c2ecf20Sopenharmony_ci */ 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (bank) { 13378c2ecf20Sopenharmony_ci dpn_config_off = CDNS_DPN_B1_CONFIG(num); 13388c2ecf20Sopenharmony_ci dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); 13398c2ecf20Sopenharmony_ci dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); 13408c2ecf20Sopenharmony_ci dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); 13418c2ecf20Sopenharmony_ci } else { 13428c2ecf20Sopenharmony_ci dpn_config_off = CDNS_DPN_B0_CONFIG(num); 13438c2ecf20Sopenharmony_ci dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); 13448c2ecf20Sopenharmony_ci dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); 13458c2ecf20Sopenharmony_ci dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci dpn_config = cdns_readl(cdns, dpn_config_off); 13498c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_config, t_params->blk_grp_ctrl, CDNS_DPN_CONFIG_BGC); 13508c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_config, t_params->blk_pkg_mode, CDNS_DPN_CONFIG_BPM); 13518c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_config_off, dpn_config); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_offsetctrl, t_params->offset1, CDNS_DPN_OFFSET_CTRL_1); 13548c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_offsetctrl, t_params->offset2, CDNS_DPN_OFFSET_CTRL_2); 13558c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_hctrl, t_params->hstart, CDNS_DPN_HCTRL_HSTART); 13588c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_hctrl, t_params->hstop, CDNS_DPN_HCTRL_HSTOP); 13598c2ecf20Sopenharmony_ci u32p_replace_bits(&dpn_hctrl, t_params->lane_ctrl, CDNS_DPN_HCTRL_LCTRL); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); 13628c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return 0; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic int cdns_port_enable(struct sdw_bus *bus, 13688c2ecf20Sopenharmony_ci struct sdw_enable_ch *enable_ch, unsigned int bank) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = bus_to_cdns(bus); 13718c2ecf20Sopenharmony_ci int dpn_chnen_off, ch_mask; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (bank) 13748c2ecf20Sopenharmony_ci dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num); 13758c2ecf20Sopenharmony_ci else 13768c2ecf20Sopenharmony_ci dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci ch_mask = enable_ch->ch_mask * enable_ch->enable; 13798c2ecf20Sopenharmony_ci cdns_writel(cdns, dpn_chnen_off, ch_mask); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic const struct sdw_master_port_ops cdns_port_ops = { 13858c2ecf20Sopenharmony_ci .dpn_set_port_params = cdns_port_params, 13868c2ecf20Sopenharmony_ci .dpn_set_port_transport_params = cdns_transport_params, 13878c2ecf20Sopenharmony_ci .dpn_port_enable_ch = cdns_port_enable, 13888c2ecf20Sopenharmony_ci}; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci/** 13918c2ecf20Sopenharmony_ci * sdw_cdns_is_clock_stop: Check clock status 13928c2ecf20Sopenharmony_ci * 13938c2ecf20Sopenharmony_ci * @cdns: Cadence instance 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_cibool sdw_cdns_is_clock_stop(struct sdw_cdns *cdns) 13968c2ecf20Sopenharmony_ci{ 13978c2ecf20Sopenharmony_ci return !!(cdns_readl(cdns, CDNS_MCP_STAT) & CDNS_MCP_STAT_CLK_STOP); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_is_clock_stop); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci/** 14028c2ecf20Sopenharmony_ci * sdw_cdns_clock_stop: Cadence clock stop configuration routine 14038c2ecf20Sopenharmony_ci * 14048c2ecf20Sopenharmony_ci * @cdns: Cadence instance 14058c2ecf20Sopenharmony_ci * @block_wake: prevent wakes if required by the platform 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_ciint sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci bool slave_present = false; 14108c2ecf20Sopenharmony_ci struct sdw_slave *slave; 14118c2ecf20Sopenharmony_ci int ret; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* Check suspend status */ 14148c2ecf20Sopenharmony_ci if (sdw_cdns_is_clock_stop(cdns)) { 14158c2ecf20Sopenharmony_ci dev_dbg(cdns->dev, "Clock is already stopped\n"); 14168c2ecf20Sopenharmony_ci return 0; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* 14208c2ecf20Sopenharmony_ci * Before entering clock stop we mask the Slave 14218c2ecf20Sopenharmony_ci * interrupts. This helps avoid having to deal with e.g. a 14228c2ecf20Sopenharmony_ci * Slave becoming UNATTACHED while the clock is being stopped 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_ci cdns_enable_slave_interrupts(cdns, false); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* 14278c2ecf20Sopenharmony_ci * For specific platforms, it is required to be able to put 14288c2ecf20Sopenharmony_ci * master into a state in which it ignores wake-up trials 14298c2ecf20Sopenharmony_ci * in clock stop state 14308c2ecf20Sopenharmony_ci */ 14318c2ecf20Sopenharmony_ci if (block_wake) 14328c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 14338c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_BLOCK_WAKEUP, 14348c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_BLOCK_WAKEUP); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci list_for_each_entry(slave, &cdns->bus.slaves, node) { 14378c2ecf20Sopenharmony_ci if (slave->status == SDW_SLAVE_ATTACHED || 14388c2ecf20Sopenharmony_ci slave->status == SDW_SLAVE_ALERT) { 14398c2ecf20Sopenharmony_ci slave_present = true; 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* 14458c2ecf20Sopenharmony_ci * This CMD_ACCEPT should be used when there are no devices 14468c2ecf20Sopenharmony_ci * attached on the link when entering clock stop mode. If this is 14478c2ecf20Sopenharmony_ci * not set and there is a broadcast write then the command ignored 14488c2ecf20Sopenharmony_ci * will be treated as a failure 14498c2ecf20Sopenharmony_ci */ 14508c2ecf20Sopenharmony_ci if (!slave_present) 14518c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 14528c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CMD_ACCEPT, 14538c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CMD_ACCEPT); 14548c2ecf20Sopenharmony_ci else 14558c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 14568c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CMD_ACCEPT, 0); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* commit changes */ 14598c2ecf20Sopenharmony_ci ret = cdns_config_update(cdns); 14608c2ecf20Sopenharmony_ci if (ret < 0) { 14618c2ecf20Sopenharmony_ci dev_err(cdns->dev, "%s: config_update failed\n", __func__); 14628c2ecf20Sopenharmony_ci return ret; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* Prepare slaves for clock stop */ 14668c2ecf20Sopenharmony_ci if (slave_present) { 14678c2ecf20Sopenharmony_ci ret = sdw_bus_prep_clk_stop(&cdns->bus); 14688c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENODATA) { 14698c2ecf20Sopenharmony_ci dev_err(cdns->dev, "prepare clock stop failed %d\n", ret); 14708c2ecf20Sopenharmony_ci return ret; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* 14758c2ecf20Sopenharmony_ci * Enter clock stop mode and only report errors if there are 14768c2ecf20Sopenharmony_ci * Slave devices present (ALERT or ATTACHED) 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci ret = sdw_bus_clk_stop(&cdns->bus); 14798c2ecf20Sopenharmony_ci if (ret < 0 && slave_present && ret != -ENODATA) { 14808c2ecf20Sopenharmony_ci dev_err(cdns->dev, "bus clock stop failed %d", ret); 14818c2ecf20Sopenharmony_ci return ret; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci ret = cdns_set_wait(cdns, CDNS_MCP_STAT, 14858c2ecf20Sopenharmony_ci CDNS_MCP_STAT_CLK_STOP, 14868c2ecf20Sopenharmony_ci CDNS_MCP_STAT_CLK_STOP); 14878c2ecf20Sopenharmony_ci if (ret < 0) 14888c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Clock stop failed %d\n", ret); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci return ret; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_clock_stop); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci/** 14958c2ecf20Sopenharmony_ci * sdw_cdns_clock_restart: Cadence PM clock restart configuration routine 14968c2ecf20Sopenharmony_ci * 14978c2ecf20Sopenharmony_ci * @cdns: Cadence instance 14988c2ecf20Sopenharmony_ci * @bus_reset: context may be lost while in low power modes and the bus 14998c2ecf20Sopenharmony_ci * may require a Severe Reset and re-enumeration after a wake. 15008c2ecf20Sopenharmony_ci */ 15018c2ecf20Sopenharmony_ciint sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci int ret; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* unmask Slave interrupts that were masked when stopping the clock */ 15068c2ecf20Sopenharmony_ci cdns_enable_slave_interrupts(cdns, true); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, 15098c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_CLK_STOP_CLR); 15108c2ecf20Sopenharmony_ci if (ret < 0) { 15118c2ecf20Sopenharmony_ci dev_err(cdns->dev, "Couldn't exit from clock stop\n"); 15128c2ecf20Sopenharmony_ci return ret; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci ret = cdns_set_wait(cdns, CDNS_MCP_STAT, CDNS_MCP_STAT_CLK_STOP, 0); 15168c2ecf20Sopenharmony_ci if (ret < 0) { 15178c2ecf20Sopenharmony_ci dev_err(cdns->dev, "clock stop exit failed %d\n", ret); 15188c2ecf20Sopenharmony_ci return ret; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, 15228c2ecf20Sopenharmony_ci CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci /* 15258c2ecf20Sopenharmony_ci * clear CMD_ACCEPT so that the command ignored 15268c2ecf20Sopenharmony_ci * will be treated as a failure during a broadcast write 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (!bus_reset) { 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* enable bus operations with clock and data */ 15338c2ecf20Sopenharmony_ci cdns_updatel(cdns, CDNS_MCP_CONFIG, 15348c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_OP, 15358c2ecf20Sopenharmony_ci CDNS_MCP_CONFIG_OP_NORMAL); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci ret = cdns_config_update(cdns); 15388c2ecf20Sopenharmony_ci if (ret < 0) { 15398c2ecf20Sopenharmony_ci dev_err(cdns->dev, "%s: config_update failed\n", __func__); 15408c2ecf20Sopenharmony_ci return ret; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci ret = sdw_bus_exit_clk_stop(&cdns->bus); 15448c2ecf20Sopenharmony_ci if (ret < 0) 15458c2ecf20Sopenharmony_ci dev_err(cdns->dev, "bus failed to exit clock stop %d\n", ret); 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_clock_restart); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci/** 15538c2ecf20Sopenharmony_ci * sdw_cdns_probe() - Cadence probe routine 15548c2ecf20Sopenharmony_ci * @cdns: Cadence instance 15558c2ecf20Sopenharmony_ci */ 15568c2ecf20Sopenharmony_ciint sdw_cdns_probe(struct sdw_cdns *cdns) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci init_completion(&cdns->tx_complete); 15598c2ecf20Sopenharmony_ci cdns->bus.port_ops = &cdns_port_ops; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci INIT_WORK(&cdns->work, cdns_update_slave_status_work); 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_probe); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ciint cdns_set_sdw_stream(struct snd_soc_dai *dai, 15678c2ecf20Sopenharmony_ci void *stream, bool pcm, int direction) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 15708c2ecf20Sopenharmony_ci struct sdw_cdns_dma_data *dma; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (stream) { 15738c2ecf20Sopenharmony_ci /* first paranoia check */ 15748c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) 15758c2ecf20Sopenharmony_ci dma = dai->playback_dma_data; 15768c2ecf20Sopenharmony_ci else 15778c2ecf20Sopenharmony_ci dma = dai->capture_dma_data; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (dma) { 15808c2ecf20Sopenharmony_ci dev_err(dai->dev, 15818c2ecf20Sopenharmony_ci "dma_data already allocated for dai %s\n", 15828c2ecf20Sopenharmony_ci dai->name); 15838c2ecf20Sopenharmony_ci return -EINVAL; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci /* allocate and set dma info */ 15878c2ecf20Sopenharmony_ci dma = kzalloc(sizeof(*dma), GFP_KERNEL); 15888c2ecf20Sopenharmony_ci if (!dma) 15898c2ecf20Sopenharmony_ci return -ENOMEM; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (pcm) 15928c2ecf20Sopenharmony_ci dma->stream_type = SDW_STREAM_PCM; 15938c2ecf20Sopenharmony_ci else 15948c2ecf20Sopenharmony_ci dma->stream_type = SDW_STREAM_PDM; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci dma->bus = &cdns->bus; 15978c2ecf20Sopenharmony_ci dma->link_id = cdns->instance; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci dma->stream = stream; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) 16028c2ecf20Sopenharmony_ci dai->playback_dma_data = dma; 16038c2ecf20Sopenharmony_ci else 16048c2ecf20Sopenharmony_ci dai->capture_dma_data = dma; 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci /* for NULL stream we release allocated dma_data */ 16078c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 16088c2ecf20Sopenharmony_ci kfree(dai->playback_dma_data); 16098c2ecf20Sopenharmony_ci dai->playback_dma_data = NULL; 16108c2ecf20Sopenharmony_ci } else { 16118c2ecf20Sopenharmony_ci kfree(dai->capture_dma_data); 16128c2ecf20Sopenharmony_ci dai->capture_dma_data = NULL; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci return 0; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cdns_set_sdw_stream); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci/** 16208c2ecf20Sopenharmony_ci * cdns_find_pdi() - Find a free PDI 16218c2ecf20Sopenharmony_ci * 16228c2ecf20Sopenharmony_ci * @cdns: Cadence instance 16238c2ecf20Sopenharmony_ci * @offset: Starting offset 16248c2ecf20Sopenharmony_ci * @num: Number of PDIs 16258c2ecf20Sopenharmony_ci * @pdi: PDI instances 16268c2ecf20Sopenharmony_ci * @dai_id: DAI id 16278c2ecf20Sopenharmony_ci * 16288c2ecf20Sopenharmony_ci * Find a PDI for a given PDI array. The PDI num and dai_id are 16298c2ecf20Sopenharmony_ci * expected to match, return NULL otherwise. 16308c2ecf20Sopenharmony_ci */ 16318c2ecf20Sopenharmony_cistatic struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, 16328c2ecf20Sopenharmony_ci unsigned int offset, 16338c2ecf20Sopenharmony_ci unsigned int num, 16348c2ecf20Sopenharmony_ci struct sdw_cdns_pdi *pdi, 16358c2ecf20Sopenharmony_ci int dai_id) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci int i; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci for (i = offset; i < offset + num; i++) 16408c2ecf20Sopenharmony_ci if (pdi[i].num == dai_id) 16418c2ecf20Sopenharmony_ci return &pdi[i]; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci return NULL; 16448c2ecf20Sopenharmony_ci} 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci/** 16478c2ecf20Sopenharmony_ci * sdw_cdns_config_stream: Configure a stream 16488c2ecf20Sopenharmony_ci * 16498c2ecf20Sopenharmony_ci * @cdns: Cadence instance 16508c2ecf20Sopenharmony_ci * @ch: Channel count 16518c2ecf20Sopenharmony_ci * @dir: Data direction 16528c2ecf20Sopenharmony_ci * @pdi: PDI to be used 16538c2ecf20Sopenharmony_ci */ 16548c2ecf20Sopenharmony_civoid sdw_cdns_config_stream(struct sdw_cdns *cdns, 16558c2ecf20Sopenharmony_ci u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci u32 offset, val = 0; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci if (dir == SDW_DATA_DIR_RX) { 16608c2ecf20Sopenharmony_ci val = CDNS_PORTCTRL_DIRN; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL) 16638c2ecf20Sopenharmony_ci val |= CDNS_PORTCTRL_TEST_FAILED; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET; 16668c2ecf20Sopenharmony_ci cdns_updatel(cdns, offset, 16678c2ecf20Sopenharmony_ci CDNS_PORTCTRL_DIRN | CDNS_PORTCTRL_TEST_FAILED, 16688c2ecf20Sopenharmony_ci val); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci val = pdi->num; 16718c2ecf20Sopenharmony_ci val |= CDNS_PDI_CONFIG_SOFT_RESET; 16728c2ecf20Sopenharmony_ci val |= FIELD_PREP(CDNS_PDI_CONFIG_CHANNEL, (1 << ch) - 1); 16738c2ecf20Sopenharmony_ci cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_config_stream); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci/** 16788c2ecf20Sopenharmony_ci * sdw_cdns_alloc_pdi() - Allocate a PDI 16798c2ecf20Sopenharmony_ci * 16808c2ecf20Sopenharmony_ci * @cdns: Cadence instance 16818c2ecf20Sopenharmony_ci * @stream: Stream to be allocated 16828c2ecf20Sopenharmony_ci * @ch: Channel count 16838c2ecf20Sopenharmony_ci * @dir: Data direction 16848c2ecf20Sopenharmony_ci * @dai_id: DAI id 16858c2ecf20Sopenharmony_ci */ 16868c2ecf20Sopenharmony_cistruct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, 16878c2ecf20Sopenharmony_ci struct sdw_cdns_streams *stream, 16888c2ecf20Sopenharmony_ci u32 ch, u32 dir, int dai_id) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci struct sdw_cdns_pdi *pdi = NULL; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci if (dir == SDW_DATA_DIR_RX) 16938c2ecf20Sopenharmony_ci pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, 16948c2ecf20Sopenharmony_ci dai_id); 16958c2ecf20Sopenharmony_ci else 16968c2ecf20Sopenharmony_ci pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, 16978c2ecf20Sopenharmony_ci dai_id); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* check if we found a PDI, else find in bi-directional */ 17008c2ecf20Sopenharmony_ci if (!pdi) 17018c2ecf20Sopenharmony_ci pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, 17028c2ecf20Sopenharmony_ci dai_id); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (pdi) { 17058c2ecf20Sopenharmony_ci pdi->l_ch_num = 0; 17068c2ecf20Sopenharmony_ci pdi->h_ch_num = ch - 1; 17078c2ecf20Sopenharmony_ci pdi->dir = dir; 17088c2ecf20Sopenharmony_ci pdi->ch_count = ch; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return pdi; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sdw_cdns_alloc_pdi); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 17168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cadence Soundwire Library"); 1717