18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci// Copyright (c) 2018, Linaro Limited
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/irq.h>
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/init.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
138c2ecf20Sopenharmony_ci#include <linux/slimbus.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci#include <linux/soc/qcom/qmi.h>
198c2ecf20Sopenharmony_ci#include <net/sock.h>
208c2ecf20Sopenharmony_ci#include "slimbus.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* NGD (Non-ported Generic Device) registers */
238c2ecf20Sopenharmony_ci#define	NGD_CFG			0x0
248c2ecf20Sopenharmony_ci#define	NGD_CFG_ENABLE		BIT(0)
258c2ecf20Sopenharmony_ci#define	NGD_CFG_RX_MSGQ_EN	BIT(1)
268c2ecf20Sopenharmony_ci#define	NGD_CFG_TX_MSGQ_EN	BIT(2)
278c2ecf20Sopenharmony_ci#define	NGD_STATUS		0x4
288c2ecf20Sopenharmony_ci#define NGD_LADDR		BIT(1)
298c2ecf20Sopenharmony_ci#define	NGD_RX_MSGQ_CFG		0x8
308c2ecf20Sopenharmony_ci#define	NGD_INT_EN		0x10
318c2ecf20Sopenharmony_ci#define	NGD_INT_RECFG_DONE	BIT(24)
328c2ecf20Sopenharmony_ci#define	NGD_INT_TX_NACKED_2	BIT(25)
338c2ecf20Sopenharmony_ci#define	NGD_INT_MSG_BUF_CONTE	BIT(26)
348c2ecf20Sopenharmony_ci#define	NGD_INT_MSG_TX_INVAL	BIT(27)
358c2ecf20Sopenharmony_ci#define	NGD_INT_IE_VE_CHG	BIT(28)
368c2ecf20Sopenharmony_ci#define	NGD_INT_DEV_ERR		BIT(29)
378c2ecf20Sopenharmony_ci#define	NGD_INT_RX_MSG_RCVD	BIT(30)
388c2ecf20Sopenharmony_ci#define	NGD_INT_TX_MSG_SENT	BIT(31)
398c2ecf20Sopenharmony_ci#define	NGD_INT_STAT		0x14
408c2ecf20Sopenharmony_ci#define	NGD_INT_CLR		0x18
418c2ecf20Sopenharmony_ci#define DEF_NGD_INT_MASK (NGD_INT_TX_NACKED_2 | NGD_INT_MSG_BUF_CONTE | \
428c2ecf20Sopenharmony_ci				NGD_INT_MSG_TX_INVAL | NGD_INT_IE_VE_CHG | \
438c2ecf20Sopenharmony_ci				NGD_INT_DEV_ERR | NGD_INT_TX_MSG_SENT | \
448c2ecf20Sopenharmony_ci				NGD_INT_RX_MSG_RCVD)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Slimbus QMI service */
478c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SVC_ID	0x0301
488c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SVC_V1	1
498c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_INS_ID	0
508c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01	0x0020
518c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01	0x0020
528c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_POWER_REQ_V01		0x0021
538c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_POWER_RESP_V01		0x0021
548c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ	0x0022
558c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP	0x0022
568c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN	14
578c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN	7
588c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN	14
598c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN	7
608c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN	7
618c2ecf20Sopenharmony_ci/* QMI response timeout of 500ms */
628c2ecf20Sopenharmony_ci#define SLIMBUS_QMI_RESP_TOUT	1000
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* User defined commands */
658c2ecf20Sopenharmony_ci#define SLIM_USR_MC_GENERIC_ACK	0x25
668c2ecf20Sopenharmony_ci#define SLIM_USR_MC_MASTER_CAPABILITY	0x0
678c2ecf20Sopenharmony_ci#define SLIM_USR_MC_REPORT_SATELLITE	0x1
688c2ecf20Sopenharmony_ci#define SLIM_USR_MC_ADDR_QUERY		0xD
698c2ecf20Sopenharmony_ci#define SLIM_USR_MC_ADDR_REPLY		0xE
708c2ecf20Sopenharmony_ci#define SLIM_USR_MC_DEFINE_CHAN		0x20
718c2ecf20Sopenharmony_ci#define SLIM_USR_MC_DEF_ACT_CHAN	0x21
728c2ecf20Sopenharmony_ci#define SLIM_USR_MC_CHAN_CTRL		0x23
738c2ecf20Sopenharmony_ci#define SLIM_USR_MC_RECONFIG_NOW	0x24
748c2ecf20Sopenharmony_ci#define SLIM_USR_MC_REQ_BW		0x28
758c2ecf20Sopenharmony_ci#define SLIM_USR_MC_CONNECT_SRC		0x2C
768c2ecf20Sopenharmony_ci#define SLIM_USR_MC_CONNECT_SINK	0x2D
778c2ecf20Sopenharmony_ci#define SLIM_USR_MC_DISCONNECT_PORT	0x2E
788c2ecf20Sopenharmony_ci#define SLIM_USR_MC_REPEAT_CHANGE_VALUE	0x0
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define QCOM_SLIM_NGD_AUTOSUSPEND	MSEC_PER_SEC
818c2ecf20Sopenharmony_ci#define SLIM_RX_MSGQ_TIMEOUT_VAL	0x10000
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define SLIM_LA_MGR	0xFF
848c2ecf20Sopenharmony_ci#define SLIM_ROOT_FREQ	24576000
858c2ecf20Sopenharmony_ci#define LADDR_RETRY	5
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Per spec.max 40 bytes per received message */
888c2ecf20Sopenharmony_ci#define SLIM_MSGQ_BUF_LEN	40
898c2ecf20Sopenharmony_ci#define QCOM_SLIM_NGD_DESC_NUM	32
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
928c2ecf20Sopenharmony_ci		((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define INIT_MX_RETRIES 10
958c2ecf20Sopenharmony_ci#define DEF_RETRY_MS	10
968c2ecf20Sopenharmony_ci#define SAT_MAGIC_LSB	0xD9
978c2ecf20Sopenharmony_ci#define SAT_MAGIC_MSB	0xC5
988c2ecf20Sopenharmony_ci#define SAT_MSG_VER	0x1
998c2ecf20Sopenharmony_ci#define SAT_MSG_PROT	0x1
1008c2ecf20Sopenharmony_ci#define to_ngd(d)	container_of(d, struct qcom_slim_ngd, dev)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct ngd_reg_offset_data {
1038c2ecf20Sopenharmony_ci	u32 offset, size;
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic const struct ngd_reg_offset_data ngd_v1_5_offset_info = {
1078c2ecf20Sopenharmony_ci	.offset = 0x1000,
1088c2ecf20Sopenharmony_ci	.size = 0x1000,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cienum qcom_slim_ngd_state {
1128c2ecf20Sopenharmony_ci	QCOM_SLIM_NGD_CTRL_AWAKE,
1138c2ecf20Sopenharmony_ci	QCOM_SLIM_NGD_CTRL_IDLE,
1148c2ecf20Sopenharmony_ci	QCOM_SLIM_NGD_CTRL_ASLEEP,
1158c2ecf20Sopenharmony_ci	QCOM_SLIM_NGD_CTRL_DOWN,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct qcom_slim_ngd_qmi {
1198c2ecf20Sopenharmony_ci	struct qmi_handle qmi;
1208c2ecf20Sopenharmony_ci	struct sockaddr_qrtr svc_info;
1218c2ecf20Sopenharmony_ci	struct qmi_handle svc_event_hdl;
1228c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
1238c2ecf20Sopenharmony_ci	struct qmi_handle *handle;
1248c2ecf20Sopenharmony_ci	struct completion qmi_comp;
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistruct qcom_slim_ngd_ctrl;
1288c2ecf20Sopenharmony_cistruct qcom_slim_ngd;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistruct qcom_slim_ngd_dma_desc {
1318c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
1328c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl;
1338c2ecf20Sopenharmony_ci	struct completion *comp;
1348c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
1358c2ecf20Sopenharmony_ci	dma_addr_t phys;
1368c2ecf20Sopenharmony_ci	void *base;
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct qcom_slim_ngd {
1408c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1418c2ecf20Sopenharmony_ci	void __iomem *base;
1428c2ecf20Sopenharmony_ci	int id;
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistruct qcom_slim_ngd_ctrl {
1468c2ecf20Sopenharmony_ci	struct slim_framer framer;
1478c2ecf20Sopenharmony_ci	struct slim_controller ctrl;
1488c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_qmi qmi;
1498c2ecf20Sopenharmony_ci	struct qcom_slim_ngd *ngd;
1508c2ecf20Sopenharmony_ci	struct device *dev;
1518c2ecf20Sopenharmony_ci	void __iomem *base;
1528c2ecf20Sopenharmony_ci	struct dma_chan *dma_rx_channel;
1538c2ecf20Sopenharmony_ci	struct dma_chan	*dma_tx_channel;
1548c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc rx_desc[QCOM_SLIM_NGD_DESC_NUM];
1558c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc txdesc[QCOM_SLIM_NGD_DESC_NUM];
1568c2ecf20Sopenharmony_ci	struct completion reconf;
1578c2ecf20Sopenharmony_ci	struct work_struct m_work;
1588c2ecf20Sopenharmony_ci	struct workqueue_struct *mwq;
1598c2ecf20Sopenharmony_ci	spinlock_t tx_buf_lock;
1608c2ecf20Sopenharmony_ci	enum qcom_slim_ngd_state state;
1618c2ecf20Sopenharmony_ci	dma_addr_t rx_phys_base;
1628c2ecf20Sopenharmony_ci	dma_addr_t tx_phys_base;
1638c2ecf20Sopenharmony_ci	void *rx_base;
1648c2ecf20Sopenharmony_ci	void *tx_base;
1658c2ecf20Sopenharmony_ci	int tx_tail;
1668c2ecf20Sopenharmony_ci	int tx_head;
1678c2ecf20Sopenharmony_ci	u32 ver;
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cienum slimbus_mode_enum_type_v01 {
1718c2ecf20Sopenharmony_ci	/* To force a 32 bit signed enum. Do not change or use*/
1728c2ecf20Sopenharmony_ci	SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
1738c2ecf20Sopenharmony_ci	SLIMBUS_MODE_SATELLITE_V01 = 1,
1748c2ecf20Sopenharmony_ci	SLIMBUS_MODE_MASTER_V01 = 2,
1758c2ecf20Sopenharmony_ci	SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cienum slimbus_pm_enum_type_v01 {
1798c2ecf20Sopenharmony_ci	/* To force a 32 bit signed enum. Do not change or use*/
1808c2ecf20Sopenharmony_ci	SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
1818c2ecf20Sopenharmony_ci	SLIMBUS_PM_INACTIVE_V01 = 1,
1828c2ecf20Sopenharmony_ci	SLIMBUS_PM_ACTIVE_V01 = 2,
1838c2ecf20Sopenharmony_ci	SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cienum slimbus_resp_enum_type_v01 {
1878c2ecf20Sopenharmony_ci	SLIMBUS_RESP_ENUM_TYPE_MIN_VAL_V01 = INT_MIN,
1888c2ecf20Sopenharmony_ci	SLIMBUS_RESP_SYNCHRONOUS_V01 = 1,
1898c2ecf20Sopenharmony_ci	SLIMBUS_RESP_ENUM_TYPE_MAX_VAL_V01 = INT_MAX,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistruct slimbus_select_inst_req_msg_v01 {
1938c2ecf20Sopenharmony_ci	uint32_t instance;
1948c2ecf20Sopenharmony_ci	uint8_t mode_valid;
1958c2ecf20Sopenharmony_ci	enum slimbus_mode_enum_type_v01 mode;
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistruct slimbus_select_inst_resp_msg_v01 {
1998c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistruct slimbus_power_req_msg_v01 {
2038c2ecf20Sopenharmony_ci	enum slimbus_pm_enum_type_v01 pm_req;
2048c2ecf20Sopenharmony_ci	uint8_t resp_type_valid;
2058c2ecf20Sopenharmony_ci	enum slimbus_resp_enum_type_v01 resp_type;
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistruct slimbus_power_resp_msg_v01 {
2098c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic struct qmi_elem_info slimbus_select_inst_req_msg_v01_ei[] = {
2138c2ecf20Sopenharmony_ci	{
2148c2ecf20Sopenharmony_ci		.data_type  = QMI_UNSIGNED_4_BYTE,
2158c2ecf20Sopenharmony_ci		.elem_len   = 1,
2168c2ecf20Sopenharmony_ci		.elem_size  = sizeof(uint32_t),
2178c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2188c2ecf20Sopenharmony_ci		.tlv_type   = 0x01,
2198c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_select_inst_req_msg_v01,
2208c2ecf20Sopenharmony_ci				       instance),
2218c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2228c2ecf20Sopenharmony_ci	},
2238c2ecf20Sopenharmony_ci	{
2248c2ecf20Sopenharmony_ci		.data_type  = QMI_OPT_FLAG,
2258c2ecf20Sopenharmony_ci		.elem_len   = 1,
2268c2ecf20Sopenharmony_ci		.elem_size  = sizeof(uint8_t),
2278c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2288c2ecf20Sopenharmony_ci		.tlv_type   = 0x10,
2298c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_select_inst_req_msg_v01,
2308c2ecf20Sopenharmony_ci				       mode_valid),
2318c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2328c2ecf20Sopenharmony_ci	},
2338c2ecf20Sopenharmony_ci	{
2348c2ecf20Sopenharmony_ci		.data_type  = QMI_UNSIGNED_4_BYTE,
2358c2ecf20Sopenharmony_ci		.elem_len   = 1,
2368c2ecf20Sopenharmony_ci		.elem_size  = sizeof(enum slimbus_mode_enum_type_v01),
2378c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2388c2ecf20Sopenharmony_ci		.tlv_type   = 0x10,
2398c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_select_inst_req_msg_v01,
2408c2ecf20Sopenharmony_ci				       mode),
2418c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2428c2ecf20Sopenharmony_ci	},
2438c2ecf20Sopenharmony_ci	{
2448c2ecf20Sopenharmony_ci		.data_type  = QMI_EOTI,
2458c2ecf20Sopenharmony_ci		.elem_len   = 0,
2468c2ecf20Sopenharmony_ci		.elem_size  = 0,
2478c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2488c2ecf20Sopenharmony_ci		.tlv_type   = 0x00,
2498c2ecf20Sopenharmony_ci		.offset     = 0,
2508c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2518c2ecf20Sopenharmony_ci	},
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic struct qmi_elem_info slimbus_select_inst_resp_msg_v01_ei[] = {
2558c2ecf20Sopenharmony_ci	{
2568c2ecf20Sopenharmony_ci		.data_type  = QMI_STRUCT,
2578c2ecf20Sopenharmony_ci		.elem_len   = 1,
2588c2ecf20Sopenharmony_ci		.elem_size  = sizeof(struct qmi_response_type_v01),
2598c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2608c2ecf20Sopenharmony_ci		.tlv_type   = 0x02,
2618c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_select_inst_resp_msg_v01,
2628c2ecf20Sopenharmony_ci				       resp),
2638c2ecf20Sopenharmony_ci		.ei_array   = qmi_response_type_v01_ei,
2648c2ecf20Sopenharmony_ci	},
2658c2ecf20Sopenharmony_ci	{
2668c2ecf20Sopenharmony_ci		.data_type  = QMI_EOTI,
2678c2ecf20Sopenharmony_ci		.elem_len   = 0,
2688c2ecf20Sopenharmony_ci		.elem_size  = 0,
2698c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2708c2ecf20Sopenharmony_ci		.tlv_type   = 0x00,
2718c2ecf20Sopenharmony_ci		.offset     = 0,
2728c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2738c2ecf20Sopenharmony_ci	},
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic struct qmi_elem_info slimbus_power_req_msg_v01_ei[] = {
2778c2ecf20Sopenharmony_ci	{
2788c2ecf20Sopenharmony_ci		.data_type  = QMI_UNSIGNED_4_BYTE,
2798c2ecf20Sopenharmony_ci		.elem_len   = 1,
2808c2ecf20Sopenharmony_ci		.elem_size  = sizeof(enum slimbus_pm_enum_type_v01),
2818c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2828c2ecf20Sopenharmony_ci		.tlv_type   = 0x01,
2838c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_power_req_msg_v01,
2848c2ecf20Sopenharmony_ci				       pm_req),
2858c2ecf20Sopenharmony_ci		.ei_array   = NULL,
2868c2ecf20Sopenharmony_ci	},
2878c2ecf20Sopenharmony_ci	{
2888c2ecf20Sopenharmony_ci		.data_type  = QMI_OPT_FLAG,
2898c2ecf20Sopenharmony_ci		.elem_len   = 1,
2908c2ecf20Sopenharmony_ci		.elem_size  = sizeof(uint8_t),
2918c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
2928c2ecf20Sopenharmony_ci		.tlv_type   = 0x10,
2938c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_power_req_msg_v01,
2948c2ecf20Sopenharmony_ci				       resp_type_valid),
2958c2ecf20Sopenharmony_ci	},
2968c2ecf20Sopenharmony_ci	{
2978c2ecf20Sopenharmony_ci		.data_type  = QMI_SIGNED_4_BYTE_ENUM,
2988c2ecf20Sopenharmony_ci		.elem_len   = 1,
2998c2ecf20Sopenharmony_ci		.elem_size  = sizeof(enum slimbus_resp_enum_type_v01),
3008c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
3018c2ecf20Sopenharmony_ci		.tlv_type   = 0x10,
3028c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_power_req_msg_v01,
3038c2ecf20Sopenharmony_ci				       resp_type),
3048c2ecf20Sopenharmony_ci	},
3058c2ecf20Sopenharmony_ci	{
3068c2ecf20Sopenharmony_ci		.data_type  = QMI_EOTI,
3078c2ecf20Sopenharmony_ci		.elem_len   = 0,
3088c2ecf20Sopenharmony_ci		.elem_size  = 0,
3098c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
3108c2ecf20Sopenharmony_ci		.tlv_type   = 0x00,
3118c2ecf20Sopenharmony_ci		.offset     = 0,
3128c2ecf20Sopenharmony_ci		.ei_array   = NULL,
3138c2ecf20Sopenharmony_ci	},
3148c2ecf20Sopenharmony_ci};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic struct qmi_elem_info slimbus_power_resp_msg_v01_ei[] = {
3178c2ecf20Sopenharmony_ci	{
3188c2ecf20Sopenharmony_ci		.data_type  = QMI_STRUCT,
3198c2ecf20Sopenharmony_ci		.elem_len   = 1,
3208c2ecf20Sopenharmony_ci		.elem_size  = sizeof(struct qmi_response_type_v01),
3218c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
3228c2ecf20Sopenharmony_ci		.tlv_type   = 0x02,
3238c2ecf20Sopenharmony_ci		.offset     = offsetof(struct slimbus_power_resp_msg_v01, resp),
3248c2ecf20Sopenharmony_ci		.ei_array   = qmi_response_type_v01_ei,
3258c2ecf20Sopenharmony_ci	},
3268c2ecf20Sopenharmony_ci	{
3278c2ecf20Sopenharmony_ci		.data_type  = QMI_EOTI,
3288c2ecf20Sopenharmony_ci		.elem_len   = 0,
3298c2ecf20Sopenharmony_ci		.elem_size  = 0,
3308c2ecf20Sopenharmony_ci		.array_type = NO_ARRAY,
3318c2ecf20Sopenharmony_ci		.tlv_type   = 0x00,
3328c2ecf20Sopenharmony_ci		.offset     = 0,
3338c2ecf20Sopenharmony_ci		.ei_array   = NULL,
3348c2ecf20Sopenharmony_ci	},
3358c2ecf20Sopenharmony_ci};
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int qcom_slim_qmi_send_select_inst_req(struct qcom_slim_ngd_ctrl *ctrl,
3388c2ecf20Sopenharmony_ci				struct slimbus_select_inst_req_msg_v01 *req)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct slimbus_select_inst_resp_msg_v01 resp = { { 0, 0 } };
3418c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3428c2ecf20Sopenharmony_ci	int rc;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	rc = qmi_txn_init(ctrl->qmi.handle, &txn,
3458c2ecf20Sopenharmony_ci				slimbus_select_inst_resp_msg_v01_ei, &resp);
3468c2ecf20Sopenharmony_ci	if (rc < 0) {
3478c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI TXN init fail: %d\n", rc);
3488c2ecf20Sopenharmony_ci		return rc;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	rc = qmi_send_request(ctrl->qmi.handle, NULL, &txn,
3528c2ecf20Sopenharmony_ci				SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01,
3538c2ecf20Sopenharmony_ci				SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN,
3548c2ecf20Sopenharmony_ci				slimbus_select_inst_req_msg_v01_ei, req);
3558c2ecf20Sopenharmony_ci	if (rc < 0) {
3568c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI send req fail %d\n", rc);
3578c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
3588c2ecf20Sopenharmony_ci		return rc;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	rc = qmi_txn_wait(&txn, SLIMBUS_QMI_RESP_TOUT);
3628c2ecf20Sopenharmony_ci	if (rc < 0) {
3638c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI TXN wait fail: %d\n", rc);
3648c2ecf20Sopenharmony_ci		return rc;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	/* Check the response */
3678c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
3688c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI request failed 0x%x\n",
3698c2ecf20Sopenharmony_ci			resp.resp.result);
3708c2ecf20Sopenharmony_ci		return -EREMOTEIO;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic void qcom_slim_qmi_power_resp_cb(struct qmi_handle *handle,
3778c2ecf20Sopenharmony_ci					struct sockaddr_qrtr *sq,
3788c2ecf20Sopenharmony_ci					struct qmi_txn *txn, const void *data)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct slimbus_power_resp_msg_v01 *resp;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	resp = (struct slimbus_power_resp_msg_v01 *)data;
3838c2ecf20Sopenharmony_ci	if (resp->resp.result != QMI_RESULT_SUCCESS_V01)
3848c2ecf20Sopenharmony_ci		pr_err("QMI power request failed 0x%x\n",
3858c2ecf20Sopenharmony_ci				resp->resp.result);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	complete(&txn->completion);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int qcom_slim_qmi_send_power_request(struct qcom_slim_ngd_ctrl *ctrl,
3918c2ecf20Sopenharmony_ci					struct slimbus_power_req_msg_v01 *req)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
3948c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3958c2ecf20Sopenharmony_ci	int rc;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	rc = qmi_txn_init(ctrl->qmi.handle, &txn,
3988c2ecf20Sopenharmony_ci				slimbus_power_resp_msg_v01_ei, &resp);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	rc = qmi_send_request(ctrl->qmi.handle, NULL, &txn,
4018c2ecf20Sopenharmony_ci				SLIMBUS_QMI_POWER_REQ_V01,
4028c2ecf20Sopenharmony_ci				SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN,
4038c2ecf20Sopenharmony_ci				slimbus_power_req_msg_v01_ei, req);
4048c2ecf20Sopenharmony_ci	if (rc < 0) {
4058c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI send req fail %d\n", rc);
4068c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
4078c2ecf20Sopenharmony_ci		return rc;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	rc = qmi_txn_wait(&txn, SLIMBUS_QMI_RESP_TOUT);
4118c2ecf20Sopenharmony_ci	if (rc < 0) {
4128c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI TXN wait fail: %d\n", rc);
4138c2ecf20Sopenharmony_ci		return rc;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* Check the response */
4178c2ecf20Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
4188c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI request failed 0x%x\n",
4198c2ecf20Sopenharmony_ci			resp.resp.result);
4208c2ecf20Sopenharmony_ci		return -EREMOTEIO;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic struct qmi_msg_handler qcom_slim_qmi_msg_handlers[] = {
4278c2ecf20Sopenharmony_ci	{
4288c2ecf20Sopenharmony_ci		.type = QMI_RESPONSE,
4298c2ecf20Sopenharmony_ci		.msg_id = SLIMBUS_QMI_POWER_RESP_V01,
4308c2ecf20Sopenharmony_ci		.ei = slimbus_power_resp_msg_v01_ei,
4318c2ecf20Sopenharmony_ci		.decoded_size = sizeof(struct slimbus_power_resp_msg_v01),
4328c2ecf20Sopenharmony_ci		.fn = qcom_slim_qmi_power_resp_cb,
4338c2ecf20Sopenharmony_ci	},
4348c2ecf20Sopenharmony_ci	{}
4358c2ecf20Sopenharmony_ci};
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int qcom_slim_qmi_init(struct qcom_slim_ngd_ctrl *ctrl,
4388c2ecf20Sopenharmony_ci			      bool apps_is_master)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct slimbus_select_inst_req_msg_v01 req;
4418c2ecf20Sopenharmony_ci	struct qmi_handle *handle;
4428c2ecf20Sopenharmony_ci	int rc;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	handle = devm_kzalloc(ctrl->dev, sizeof(*handle), GFP_KERNEL);
4458c2ecf20Sopenharmony_ci	if (!handle)
4468c2ecf20Sopenharmony_ci		return -ENOMEM;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	rc = qmi_handle_init(handle, SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN,
4498c2ecf20Sopenharmony_ci				NULL, qcom_slim_qmi_msg_handlers);
4508c2ecf20Sopenharmony_ci	if (rc < 0) {
4518c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "QMI client init failed: %d\n", rc);
4528c2ecf20Sopenharmony_ci		goto qmi_handle_init_failed;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	rc = kernel_connect(handle->sock,
4568c2ecf20Sopenharmony_ci				(struct sockaddr *)&ctrl->qmi.svc_info,
4578c2ecf20Sopenharmony_ci				sizeof(ctrl->qmi.svc_info), 0);
4588c2ecf20Sopenharmony_ci	if (rc < 0) {
4598c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Remote Service connect failed: %d\n", rc);
4608c2ecf20Sopenharmony_ci		goto qmi_connect_to_service_failed;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Instance is 0 based */
4648c2ecf20Sopenharmony_ci	req.instance = (ctrl->ngd->id >> 1);
4658c2ecf20Sopenharmony_ci	req.mode_valid = 1;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* Mode indicates the role of the ADSP */
4688c2ecf20Sopenharmony_ci	if (apps_is_master)
4698c2ecf20Sopenharmony_ci		req.mode = SLIMBUS_MODE_SATELLITE_V01;
4708c2ecf20Sopenharmony_ci	else
4718c2ecf20Sopenharmony_ci		req.mode = SLIMBUS_MODE_MASTER_V01;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ctrl->qmi.handle = handle;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	rc = qcom_slim_qmi_send_select_inst_req(ctrl, &req);
4768c2ecf20Sopenharmony_ci	if (rc) {
4778c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "failed to select h/w instance\n");
4788c2ecf20Sopenharmony_ci		goto qmi_select_instance_failed;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ciqmi_select_instance_failed:
4848c2ecf20Sopenharmony_ci	ctrl->qmi.handle = NULL;
4858c2ecf20Sopenharmony_ciqmi_connect_to_service_failed:
4868c2ecf20Sopenharmony_ci	qmi_handle_release(handle);
4878c2ecf20Sopenharmony_ciqmi_handle_init_failed:
4888c2ecf20Sopenharmony_ci	devm_kfree(ctrl->dev, handle);
4898c2ecf20Sopenharmony_ci	return rc;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic void qcom_slim_qmi_exit(struct qcom_slim_ngd_ctrl *ctrl)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	if (!ctrl->qmi.handle)
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	qmi_handle_release(ctrl->qmi.handle);
4988c2ecf20Sopenharmony_ci	devm_kfree(ctrl->dev, ctrl->qmi.handle);
4998c2ecf20Sopenharmony_ci	ctrl->qmi.handle = NULL;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int qcom_slim_qmi_power_request(struct qcom_slim_ngd_ctrl *ctrl,
5038c2ecf20Sopenharmony_ci				       bool active)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct slimbus_power_req_msg_v01 req;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (active)
5088c2ecf20Sopenharmony_ci		req.pm_req = SLIMBUS_PM_ACTIVE_V01;
5098c2ecf20Sopenharmony_ci	else
5108c2ecf20Sopenharmony_ci		req.pm_req = SLIMBUS_PM_INACTIVE_V01;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	req.resp_type_valid = 0;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return qcom_slim_qmi_send_power_request(ctrl, &req);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic u32 *qcom_slim_ngd_tx_msg_get(struct qcom_slim_ngd_ctrl *ctrl, int len,
5188c2ecf20Sopenharmony_ci				     struct completion *comp)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc *desc;
5218c2ecf20Sopenharmony_ci	unsigned long flags;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->tx_buf_lock, flags);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if ((ctrl->tx_tail + 1) % QCOM_SLIM_NGD_DESC_NUM == ctrl->tx_head) {
5268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
5278c2ecf20Sopenharmony_ci		return NULL;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	desc  = &ctrl->txdesc[ctrl->tx_tail];
5308c2ecf20Sopenharmony_ci	desc->base = ctrl->tx_base + ctrl->tx_tail * SLIM_MSGQ_BUF_LEN;
5318c2ecf20Sopenharmony_ci	desc->comp = comp;
5328c2ecf20Sopenharmony_ci	ctrl->tx_tail = (ctrl->tx_tail + 1) % QCOM_SLIM_NGD_DESC_NUM;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return desc->base;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_tx_msg_dma_cb(void *args)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc *desc = args;
5428c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = desc->ctrl;
5438c2ecf20Sopenharmony_ci	unsigned long flags;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->tx_buf_lock, flags);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (desc->comp) {
5488c2ecf20Sopenharmony_ci		complete(desc->comp);
5498c2ecf20Sopenharmony_ci		desc->comp = NULL;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	ctrl->tx_head = (ctrl->tx_head + 1) % QCOM_SLIM_NGD_DESC_NUM;
5538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_tx_msg_post(struct qcom_slim_ngd_ctrl *ctrl,
5578c2ecf20Sopenharmony_ci				     void *buf, int len)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc *desc;
5608c2ecf20Sopenharmony_ci	unsigned long flags;
5618c2ecf20Sopenharmony_ci	int index, offset;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->tx_buf_lock, flags);
5648c2ecf20Sopenharmony_ci	offset = buf - ctrl->tx_base;
5658c2ecf20Sopenharmony_ci	index = offset/SLIM_MSGQ_BUF_LEN;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	desc = &ctrl->txdesc[index];
5688c2ecf20Sopenharmony_ci	desc->phys = ctrl->tx_phys_base + offset;
5698c2ecf20Sopenharmony_ci	desc->base = ctrl->tx_base + offset;
5708c2ecf20Sopenharmony_ci	desc->ctrl = ctrl;
5718c2ecf20Sopenharmony_ci	len = (len + 3) & 0xfc;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	desc->desc = dmaengine_prep_slave_single(ctrl->dma_tx_channel,
5748c2ecf20Sopenharmony_ci						desc->phys, len,
5758c2ecf20Sopenharmony_ci						DMA_MEM_TO_DEV,
5768c2ecf20Sopenharmony_ci						DMA_PREP_INTERRUPT);
5778c2ecf20Sopenharmony_ci	if (!desc->desc) {
5788c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "unable to prepare channel\n");
5798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
5808c2ecf20Sopenharmony_ci		return -EINVAL;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	desc->desc->callback = qcom_slim_ngd_tx_msg_dma_cb;
5848c2ecf20Sopenharmony_ci	desc->desc->callback_param = desc;
5858c2ecf20Sopenharmony_ci	desc->desc->cookie = dmaengine_submit(desc->desc);
5868c2ecf20Sopenharmony_ci	dma_async_issue_pending(ctrl->dma_tx_channel);
5878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	return 0;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_rx(struct qcom_slim_ngd_ctrl *ctrl, u8 *buf)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	u8 mc, mt, len;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	mt = SLIM_HEADER_GET_MT(buf[0]);
5978c2ecf20Sopenharmony_ci	len = SLIM_HEADER_GET_RL(buf[0]);
5988c2ecf20Sopenharmony_ci	mc = SLIM_HEADER_GET_MC(buf[1]);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (mc == SLIM_USR_MC_MASTER_CAPABILITY &&
6018c2ecf20Sopenharmony_ci		mt == SLIM_MSG_MT_SRC_REFERRED_USER)
6028c2ecf20Sopenharmony_ci		queue_work(ctrl->mwq, &ctrl->m_work);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
6058c2ecf20Sopenharmony_ci	    mc == SLIM_MSG_MC_REPLY_VALUE || (mc == SLIM_USR_MC_ADDR_REPLY &&
6068c2ecf20Sopenharmony_ci	    mt == SLIM_MSG_MT_SRC_REFERRED_USER) ||
6078c2ecf20Sopenharmony_ci		(mc == SLIM_USR_MC_GENERIC_ACK &&
6088c2ecf20Sopenharmony_ci		 mt == SLIM_MSG_MT_SRC_REFERRED_USER)) {
6098c2ecf20Sopenharmony_ci		slim_msg_response(&ctrl->ctrl, &buf[4], buf[3], len - 4);
6108c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(ctrl->dev);
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_rx_msgq_cb(void *args)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc *desc = args;
6178c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = desc->ctrl;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	qcom_slim_ngd_rx(ctrl, (u8 *)desc->base);
6208c2ecf20Sopenharmony_ci	/* Add descriptor back to the queue */
6218c2ecf20Sopenharmony_ci	desc->desc = dmaengine_prep_slave_single(ctrl->dma_rx_channel,
6228c2ecf20Sopenharmony_ci					desc->phys, SLIM_MSGQ_BUF_LEN,
6238c2ecf20Sopenharmony_ci					DMA_DEV_TO_MEM,
6248c2ecf20Sopenharmony_ci					DMA_PREP_INTERRUPT);
6258c2ecf20Sopenharmony_ci	if (!desc->desc) {
6268c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Unable to prepare rx channel\n");
6278c2ecf20Sopenharmony_ci		return;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	desc->desc->callback = qcom_slim_ngd_rx_msgq_cb;
6318c2ecf20Sopenharmony_ci	desc->desc->callback_param = desc;
6328c2ecf20Sopenharmony_ci	desc->desc->cookie = dmaengine_submit(desc->desc);
6338c2ecf20Sopenharmony_ci	dma_async_issue_pending(ctrl->dma_rx_channel);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_post_rx_msgq(struct qcom_slim_ngd_ctrl *ctrl)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_dma_desc *desc;
6398c2ecf20Sopenharmony_ci	int i;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	for (i = 0; i < QCOM_SLIM_NGD_DESC_NUM; i++) {
6428c2ecf20Sopenharmony_ci		desc = &ctrl->rx_desc[i];
6438c2ecf20Sopenharmony_ci		desc->phys = ctrl->rx_phys_base + i * SLIM_MSGQ_BUF_LEN;
6448c2ecf20Sopenharmony_ci		desc->ctrl = ctrl;
6458c2ecf20Sopenharmony_ci		desc->base = ctrl->rx_base + i * SLIM_MSGQ_BUF_LEN;
6468c2ecf20Sopenharmony_ci		desc->desc = dmaengine_prep_slave_single(ctrl->dma_rx_channel,
6478c2ecf20Sopenharmony_ci						desc->phys, SLIM_MSGQ_BUF_LEN,
6488c2ecf20Sopenharmony_ci						DMA_DEV_TO_MEM,
6498c2ecf20Sopenharmony_ci						DMA_PREP_INTERRUPT);
6508c2ecf20Sopenharmony_ci		if (!desc->desc) {
6518c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "Unable to prepare rx channel\n");
6528c2ecf20Sopenharmony_ci			return -EINVAL;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		desc->desc->callback = qcom_slim_ngd_rx_msgq_cb;
6568c2ecf20Sopenharmony_ci		desc->desc->callback_param = desc;
6578c2ecf20Sopenharmony_ci		desc->desc->cookie = dmaengine_submit(desc->desc);
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci	dma_async_issue_pending(ctrl->dma_rx_channel);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return 0;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_init_rx_msgq(struct qcom_slim_ngd_ctrl *ctrl)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct device *dev = ctrl->dev;
6678c2ecf20Sopenharmony_ci	int ret, size;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	ctrl->dma_rx_channel = dma_request_chan(dev, "rx");
6708c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->dma_rx_channel)) {
6718c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to request RX dma channel");
6728c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->dma_rx_channel);
6738c2ecf20Sopenharmony_ci		ctrl->dma_rx_channel = NULL;
6748c2ecf20Sopenharmony_ci		return ret;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	size = QCOM_SLIM_NGD_DESC_NUM * SLIM_MSGQ_BUF_LEN;
6788c2ecf20Sopenharmony_ci	ctrl->rx_base = dma_alloc_coherent(dev, size, &ctrl->rx_phys_base,
6798c2ecf20Sopenharmony_ci					   GFP_KERNEL);
6808c2ecf20Sopenharmony_ci	if (!ctrl->rx_base) {
6818c2ecf20Sopenharmony_ci		dev_err(dev, "dma_alloc_coherent failed\n");
6828c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6838c2ecf20Sopenharmony_ci		goto rel_rx;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_post_rx_msgq(ctrl);
6878c2ecf20Sopenharmony_ci	if (ret) {
6888c2ecf20Sopenharmony_ci		dev_err(dev, "post_rx_msgq() failed 0x%x\n", ret);
6898c2ecf20Sopenharmony_ci		goto rx_post_err;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cirx_post_err:
6958c2ecf20Sopenharmony_ci	dma_free_coherent(dev, size, ctrl->rx_base, ctrl->rx_phys_base);
6968c2ecf20Sopenharmony_cirel_rx:
6978c2ecf20Sopenharmony_ci	dma_release_channel(ctrl->dma_rx_channel);
6988c2ecf20Sopenharmony_ci	return ret;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_init_tx_msgq(struct qcom_slim_ngd_ctrl *ctrl)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct device *dev = ctrl->dev;
7048c2ecf20Sopenharmony_ci	unsigned long flags;
7058c2ecf20Sopenharmony_ci	int ret = 0;
7068c2ecf20Sopenharmony_ci	int size;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ctrl->dma_tx_channel = dma_request_chan(dev, "tx");
7098c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->dma_tx_channel)) {
7108c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to request TX dma channel");
7118c2ecf20Sopenharmony_ci		ret = PTR_ERR(ctrl->dma_tx_channel);
7128c2ecf20Sopenharmony_ci		ctrl->dma_tx_channel = NULL;
7138c2ecf20Sopenharmony_ci		return ret;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	size = ((QCOM_SLIM_NGD_DESC_NUM + 1) * SLIM_MSGQ_BUF_LEN);
7178c2ecf20Sopenharmony_ci	ctrl->tx_base = dma_alloc_coherent(dev, size, &ctrl->tx_phys_base,
7188c2ecf20Sopenharmony_ci					   GFP_KERNEL);
7198c2ecf20Sopenharmony_ci	if (!ctrl->tx_base) {
7208c2ecf20Sopenharmony_ci		dev_err(dev, "dma_alloc_coherent failed\n");
7218c2ecf20Sopenharmony_ci		ret = -EINVAL;
7228c2ecf20Sopenharmony_ci		goto rel_tx;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ctrl->tx_buf_lock, flags);
7268c2ecf20Sopenharmony_ci	ctrl->tx_tail = 0;
7278c2ecf20Sopenharmony_ci	ctrl->tx_head = 0;
7288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	return 0;
7318c2ecf20Sopenharmony_cirel_tx:
7328c2ecf20Sopenharmony_ci	dma_release_channel(ctrl->dma_tx_channel);
7338c2ecf20Sopenharmony_ci	return ret;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_init_dma(struct qcom_slim_ngd_ctrl *ctrl)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	int ret = 0;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_init_rx_msgq(ctrl);
7418c2ecf20Sopenharmony_ci	if (ret) {
7428c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "rx dma init failed\n");
7438c2ecf20Sopenharmony_ci		return ret;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_init_tx_msgq(ctrl);
7478c2ecf20Sopenharmony_ci	if (ret)
7488c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "tx dma init failed\n");
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return ret;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic irqreturn_t qcom_slim_ngd_interrupt(int irq, void *d)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = d;
7568c2ecf20Sopenharmony_ci	void __iomem *base = ctrl->ngd->base;
7578c2ecf20Sopenharmony_ci	u32 stat = readl(base + NGD_INT_STAT);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if ((stat & NGD_INT_MSG_BUF_CONTE) ||
7608c2ecf20Sopenharmony_ci		(stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
7618c2ecf20Sopenharmony_ci		(stat & NGD_INT_TX_NACKED_2)) {
7628c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Error Interrupt received 0x%x\n", stat);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	writel(stat, base + NGD_INT_CLR);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
7718c2ecf20Sopenharmony_ci				  struct slim_msg_txn *txn)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(sctrl->dev);
7748c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(tx_sent);
7758c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(done);
7768c2ecf20Sopenharmony_ci	int ret, timeout, i;
7778c2ecf20Sopenharmony_ci	u8 wbuf[SLIM_MSGQ_BUF_LEN];
7788c2ecf20Sopenharmony_ci	u8 rbuf[SLIM_MSGQ_BUF_LEN];
7798c2ecf20Sopenharmony_ci	u32 *pbuf;
7808c2ecf20Sopenharmony_ci	u8 *puc;
7818c2ecf20Sopenharmony_ci	u8 la = txn->la;
7828c2ecf20Sopenharmony_ci	bool usr_msg = false;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (txn->mt == SLIM_MSG_MT_CORE &&
7858c2ecf20Sopenharmony_ci		(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
7868c2ecf20Sopenharmony_ci		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
7878c2ecf20Sopenharmony_ci		return 0;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (txn->dt == SLIM_MSG_DEST_ENUMADDR)
7908c2ecf20Sopenharmony_ci		return -EPROTONOSUPPORT;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (txn->msg->num_bytes > SLIM_MSGQ_BUF_LEN ||
7938c2ecf20Sopenharmony_ci			txn->rl > SLIM_MSGQ_BUF_LEN) {
7948c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "msg exceeds HW limit\n");
7958c2ecf20Sopenharmony_ci		return -EINVAL;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	pbuf = qcom_slim_ngd_tx_msg_get(ctrl, txn->rl, &tx_sent);
7998c2ecf20Sopenharmony_ci	if (!pbuf) {
8008c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Message buffer unavailable\n");
8018c2ecf20Sopenharmony_ci		return -ENOMEM;
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (txn->mt == SLIM_MSG_MT_CORE &&
8058c2ecf20Sopenharmony_ci		(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
8068c2ecf20Sopenharmony_ci		txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
8078c2ecf20Sopenharmony_ci		txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
8088c2ecf20Sopenharmony_ci		txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
8098c2ecf20Sopenharmony_ci		switch (txn->mc) {
8108c2ecf20Sopenharmony_ci		case SLIM_MSG_MC_CONNECT_SOURCE:
8118c2ecf20Sopenharmony_ci			txn->mc = SLIM_USR_MC_CONNECT_SRC;
8128c2ecf20Sopenharmony_ci			break;
8138c2ecf20Sopenharmony_ci		case SLIM_MSG_MC_CONNECT_SINK:
8148c2ecf20Sopenharmony_ci			txn->mc = SLIM_USR_MC_CONNECT_SINK;
8158c2ecf20Sopenharmony_ci			break;
8168c2ecf20Sopenharmony_ci		case SLIM_MSG_MC_DISCONNECT_PORT:
8178c2ecf20Sopenharmony_ci			txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
8188c2ecf20Sopenharmony_ci			break;
8198c2ecf20Sopenharmony_ci		default:
8208c2ecf20Sopenharmony_ci			return -EINVAL;
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci		usr_msg = true;
8248c2ecf20Sopenharmony_ci		i = 0;
8258c2ecf20Sopenharmony_ci		wbuf[i++] = txn->la;
8268c2ecf20Sopenharmony_ci		la = SLIM_LA_MGR;
8278c2ecf20Sopenharmony_ci		wbuf[i++] = txn->msg->wbuf[0];
8288c2ecf20Sopenharmony_ci		if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
8298c2ecf20Sopenharmony_ci			wbuf[i++] = txn->msg->wbuf[1];
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		txn->comp = &done;
8328c2ecf20Sopenharmony_ci		ret = slim_alloc_txn_tid(sctrl, txn);
8338c2ecf20Sopenharmony_ci		if (ret) {
8348c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "Unable to allocate TID\n");
8358c2ecf20Sopenharmony_ci			return ret;
8368c2ecf20Sopenharmony_ci		}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		wbuf[i++] = txn->tid;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		txn->msg->num_bytes = i;
8418c2ecf20Sopenharmony_ci		txn->msg->wbuf = wbuf;
8428c2ecf20Sopenharmony_ci		txn->msg->rbuf = rbuf;
8438c2ecf20Sopenharmony_ci		txn->rl = txn->msg->num_bytes + 4;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* HW expects length field to be excluded */
8478c2ecf20Sopenharmony_ci	txn->rl--;
8488c2ecf20Sopenharmony_ci	puc = (u8 *)pbuf;
8498c2ecf20Sopenharmony_ci	*pbuf = 0;
8508c2ecf20Sopenharmony_ci	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) {
8518c2ecf20Sopenharmony_ci		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
8528c2ecf20Sopenharmony_ci				la);
8538c2ecf20Sopenharmony_ci		puc += 3;
8548c2ecf20Sopenharmony_ci	} else {
8558c2ecf20Sopenharmony_ci		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
8568c2ecf20Sopenharmony_ci				la);
8578c2ecf20Sopenharmony_ci		puc += 2;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (slim_tid_txn(txn->mt, txn->mc))
8618c2ecf20Sopenharmony_ci		*(puc++) = txn->tid;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (slim_ec_txn(txn->mt, txn->mc)) {
8648c2ecf20Sopenharmony_ci		*(puc++) = (txn->ec & 0xFF);
8658c2ecf20Sopenharmony_ci		*(puc++) = (txn->ec >> 8) & 0xFF;
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (txn->msg && txn->msg->wbuf)
8698c2ecf20Sopenharmony_ci		memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_tx_msg_post(ctrl, pbuf, txn->rl);
8728c2ecf20Sopenharmony_ci	if (ret)
8738c2ecf20Sopenharmony_ci		return ret;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&tx_sent, HZ);
8768c2ecf20Sopenharmony_ci	if (!timeout) {
8778c2ecf20Sopenharmony_ci		dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
8788c2ecf20Sopenharmony_ci					txn->mt);
8798c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (usr_msg) {
8838c2ecf20Sopenharmony_ci		timeout = wait_for_completion_timeout(&done, HZ);
8848c2ecf20Sopenharmony_ci		if (!timeout) {
8858c2ecf20Sopenharmony_ci			dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x",
8868c2ecf20Sopenharmony_ci				txn->mc, txn->mt);
8878c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
8888c2ecf20Sopenharmony_ci		}
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return 0;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_xfer_msg_sync(struct slim_controller *ctrl,
8958c2ecf20Sopenharmony_ci				       struct slim_msg_txn *txn)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(done);
8988c2ecf20Sopenharmony_ci	int ret, timeout;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	pm_runtime_get_sync(ctrl->dev);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	txn->comp = &done;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_xfer_msg(ctrl, txn);
9058c2ecf20Sopenharmony_ci	if (ret)
9068c2ecf20Sopenharmony_ci		return ret;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&done, HZ);
9098c2ecf20Sopenharmony_ci	if (!timeout) {
9108c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
9118c2ecf20Sopenharmony_ci				txn->mt);
9128c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci	return 0;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_enable_stream(struct slim_stream_runtime *rt)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct slim_device *sdev = rt->dev;
9208c2ecf20Sopenharmony_ci	struct slim_controller *ctrl = sdev->ctrl;
9218c2ecf20Sopenharmony_ci	struct slim_val_inf msg =  {0};
9228c2ecf20Sopenharmony_ci	u8 wbuf[SLIM_MSGQ_BUF_LEN];
9238c2ecf20Sopenharmony_ci	u8 rbuf[SLIM_MSGQ_BUF_LEN];
9248c2ecf20Sopenharmony_ci	struct slim_msg_txn txn = {0,};
9258c2ecf20Sopenharmony_ci	int i, ret;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
9288c2ecf20Sopenharmony_ci	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
9298c2ecf20Sopenharmony_ci	txn.la = SLIM_LA_MGR;
9308c2ecf20Sopenharmony_ci	txn.ec = 0;
9318c2ecf20Sopenharmony_ci	txn.msg = &msg;
9328c2ecf20Sopenharmony_ci	txn.msg->num_bytes = 0;
9338c2ecf20Sopenharmony_ci	txn.msg->wbuf = wbuf;
9348c2ecf20Sopenharmony_ci	txn.msg->rbuf = rbuf;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	for (i = 0; i < rt->num_ports; i++) {
9378c2ecf20Sopenharmony_ci		struct slim_port *port = &rt->ports[i];
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		if (txn.msg->num_bytes == 0) {
9408c2ecf20Sopenharmony_ci			int seg_interval = SLIM_SLOTS_PER_SUPERFRAME/rt->ratem;
9418c2ecf20Sopenharmony_ci			int exp;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci			wbuf[txn.msg->num_bytes++] = sdev->laddr;
9448c2ecf20Sopenharmony_ci			wbuf[txn.msg->num_bytes] = rt->bps >> 2 |
9458c2ecf20Sopenharmony_ci						   (port->ch.aux_fmt << 6);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci			/* Data channel segment interval not multiple of 3 */
9488c2ecf20Sopenharmony_ci			exp = seg_interval % 3;
9498c2ecf20Sopenharmony_ci			if (exp)
9508c2ecf20Sopenharmony_ci				wbuf[txn.msg->num_bytes] |= BIT(5);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci			txn.msg->num_bytes++;
9538c2ecf20Sopenharmony_ci			wbuf[txn.msg->num_bytes++] = exp << 4 | rt->prot;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci			if (rt->prot == SLIM_PROTO_ISO)
9568c2ecf20Sopenharmony_ci				wbuf[txn.msg->num_bytes++] =
9578c2ecf20Sopenharmony_ci						port->ch.prrate |
9588c2ecf20Sopenharmony_ci						SLIM_CHANNEL_CONTENT_FL;
9598c2ecf20Sopenharmony_ci			else
9608c2ecf20Sopenharmony_ci				wbuf[txn.msg->num_bytes++] =  port->ch.prrate;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci			ret = slim_alloc_txn_tid(ctrl, &txn);
9638c2ecf20Sopenharmony_ci			if (ret) {
9648c2ecf20Sopenharmony_ci				dev_err(&sdev->dev, "Fail to allocate TID\n");
9658c2ecf20Sopenharmony_ci				return -ENXIO;
9668c2ecf20Sopenharmony_ci			}
9678c2ecf20Sopenharmony_ci			wbuf[txn.msg->num_bytes++] = txn.tid;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci		wbuf[txn.msg->num_bytes++] = port->ch.id;
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
9738c2ecf20Sopenharmony_ci	txn.rl = txn.msg->num_bytes + 4;
9748c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn);
9758c2ecf20Sopenharmony_ci	if (ret) {
9768c2ecf20Sopenharmony_ci		slim_free_txn_tid(ctrl, &txn);
9778c2ecf20Sopenharmony_ci		dev_err(&sdev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn.mc,
9788c2ecf20Sopenharmony_ci				txn.mt);
9798c2ecf20Sopenharmony_ci		return ret;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	txn.mc = SLIM_USR_MC_RECONFIG_NOW;
9838c2ecf20Sopenharmony_ci	txn.msg->num_bytes = 2;
9848c2ecf20Sopenharmony_ci	wbuf[1] = sdev->laddr;
9858c2ecf20Sopenharmony_ci	txn.rl = txn.msg->num_bytes + 4;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	ret = slim_alloc_txn_tid(ctrl, &txn);
9888c2ecf20Sopenharmony_ci	if (ret) {
9898c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Fail to allocate TID\n");
9908c2ecf20Sopenharmony_ci		return ret;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	wbuf[0] = txn.tid;
9948c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn);
9958c2ecf20Sopenharmony_ci	if (ret) {
9968c2ecf20Sopenharmony_ci		slim_free_txn_tid(ctrl, &txn);
9978c2ecf20Sopenharmony_ci		dev_err(&sdev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn.mc,
9988c2ecf20Sopenharmony_ci				txn.mt);
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return ret;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_get_laddr(struct slim_controller *ctrl,
10058c2ecf20Sopenharmony_ci				   struct slim_eaddr *ea, u8 *laddr)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct slim_val_inf msg =  {0};
10088c2ecf20Sopenharmony_ci	u8 failed_ea[6] = {0, 0, 0, 0, 0, 0};
10098c2ecf20Sopenharmony_ci	struct slim_msg_txn txn;
10108c2ecf20Sopenharmony_ci	u8 wbuf[10] = {0};
10118c2ecf20Sopenharmony_ci	u8 rbuf[10] = {0};
10128c2ecf20Sopenharmony_ci	int ret;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
10158c2ecf20Sopenharmony_ci	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
10168c2ecf20Sopenharmony_ci	txn.la = SLIM_LA_MGR;
10178c2ecf20Sopenharmony_ci	txn.ec = 0;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	txn.mc = SLIM_USR_MC_ADDR_QUERY;
10208c2ecf20Sopenharmony_ci	txn.rl = 11;
10218c2ecf20Sopenharmony_ci	txn.msg = &msg;
10228c2ecf20Sopenharmony_ci	txn.msg->num_bytes = 7;
10238c2ecf20Sopenharmony_ci	txn.msg->wbuf = wbuf;
10248c2ecf20Sopenharmony_ci	txn.msg->rbuf = rbuf;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	ret = slim_alloc_txn_tid(ctrl, &txn);
10278c2ecf20Sopenharmony_ci	if (ret < 0)
10288c2ecf20Sopenharmony_ci		return ret;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	wbuf[0] = (u8)txn.tid;
10318c2ecf20Sopenharmony_ci	memcpy(&wbuf[1], ea, sizeof(*ea));
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn);
10348c2ecf20Sopenharmony_ci	if (ret) {
10358c2ecf20Sopenharmony_ci		slim_free_txn_tid(ctrl, &txn);
10368c2ecf20Sopenharmony_ci		return ret;
10378c2ecf20Sopenharmony_ci	}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	if (!memcmp(rbuf, failed_ea, 6))
10408c2ecf20Sopenharmony_ci		return -ENXIO;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	*laddr = rbuf[6];
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	return ret;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_exit_dma(struct qcom_slim_ngd_ctrl *ctrl)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	if (ctrl->dma_rx_channel) {
10508c2ecf20Sopenharmony_ci		dmaengine_terminate_sync(ctrl->dma_rx_channel);
10518c2ecf20Sopenharmony_ci		dma_release_channel(ctrl->dma_rx_channel);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	if (ctrl->dma_tx_channel) {
10558c2ecf20Sopenharmony_ci		dmaengine_terminate_sync(ctrl->dma_tx_channel);
10568c2ecf20Sopenharmony_ci		dma_release_channel(ctrl->dma_tx_channel);
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	ctrl->dma_tx_channel = ctrl->dma_rx_channel = NULL;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	return 0;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_setup(struct qcom_slim_ngd_ctrl *ctrl)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	u32 cfg = readl_relaxed(ctrl->ngd->base);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN ||
10698c2ecf20Sopenharmony_ci		ctrl->state == QCOM_SLIM_NGD_CTRL_ASLEEP)
10708c2ecf20Sopenharmony_ci		qcom_slim_ngd_init_dma(ctrl);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/* By default enable message queues */
10738c2ecf20Sopenharmony_ci	cfg |= NGD_CFG_RX_MSGQ_EN;
10748c2ecf20Sopenharmony_ci	cfg |= NGD_CFG_TX_MSGQ_EN;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* Enable NGD if it's not already enabled*/
10778c2ecf20Sopenharmony_ci	if (!(cfg & NGD_CFG_ENABLE))
10788c2ecf20Sopenharmony_ci		cfg |= NGD_CFG_ENABLE;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	writel_relaxed(cfg, ctrl->ngd->base);
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_power_up(struct qcom_slim_ngd_ctrl *ctrl)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	enum qcom_slim_ngd_state cur_state = ctrl->state;
10868c2ecf20Sopenharmony_ci	struct qcom_slim_ngd *ngd = ctrl->ngd;
10878c2ecf20Sopenharmony_ci	u32 laddr, rx_msgq;
10888c2ecf20Sopenharmony_ci	int timeout, ret = 0;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) {
10918c2ecf20Sopenharmony_ci		timeout = wait_for_completion_timeout(&ctrl->qmi.qmi_comp, HZ);
10928c2ecf20Sopenharmony_ci		if (!timeout)
10938c2ecf20Sopenharmony_ci			return -EREMOTEIO;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	if (ctrl->state == QCOM_SLIM_NGD_CTRL_ASLEEP ||
10978c2ecf20Sopenharmony_ci		ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) {
10988c2ecf20Sopenharmony_ci		ret = qcom_slim_qmi_power_request(ctrl, true);
10998c2ecf20Sopenharmony_ci		if (ret) {
11008c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "SLIM QMI power request failed:%d\n",
11018c2ecf20Sopenharmony_ci					ret);
11028c2ecf20Sopenharmony_ci			return ret;
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	ctrl->ver = readl_relaxed(ctrl->base);
11078c2ecf20Sopenharmony_ci	/* Version info in 16 MSbits */
11088c2ecf20Sopenharmony_ci	ctrl->ver >>= 16;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	laddr = readl_relaxed(ngd->base + NGD_STATUS);
11118c2ecf20Sopenharmony_ci	if (laddr & NGD_LADDR) {
11128c2ecf20Sopenharmony_ci		/*
11138c2ecf20Sopenharmony_ci		 * external MDM restart case where ADSP itself was active framer
11148c2ecf20Sopenharmony_ci		 * For example, modem restarted when playback was active
11158c2ecf20Sopenharmony_ci		 */
11168c2ecf20Sopenharmony_ci		if (cur_state == QCOM_SLIM_NGD_CTRL_AWAKE) {
11178c2ecf20Sopenharmony_ci			dev_info(ctrl->dev, "Subsys restart: ADSP active framer\n");
11188c2ecf20Sopenharmony_ci			return 0;
11198c2ecf20Sopenharmony_ci		}
11208c2ecf20Sopenharmony_ci		qcom_slim_ngd_setup(ctrl);
11218c2ecf20Sopenharmony_ci		return 0;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	writel_relaxed(DEF_NGD_INT_MASK, ngd->base + NGD_INT_EN);
11258c2ecf20Sopenharmony_ci	rx_msgq = readl_relaxed(ngd->base + NGD_RX_MSGQ_CFG);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	writel_relaxed(rx_msgq|SLIM_RX_MSGQ_TIMEOUT_VAL,
11288c2ecf20Sopenharmony_ci				ngd->base + NGD_RX_MSGQ_CFG);
11298c2ecf20Sopenharmony_ci	qcom_slim_ngd_setup(ctrl);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&ctrl->reconf, HZ);
11328c2ecf20Sopenharmony_ci	if (!timeout) {
11338c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "capability exchange timed-out\n");
11348c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return 0;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_notify_slaves(struct qcom_slim_ngd_ctrl *ctrl)
11418c2ecf20Sopenharmony_ci{
11428c2ecf20Sopenharmony_ci	struct slim_device *sbdev;
11438c2ecf20Sopenharmony_ci	struct device_node *node;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	for_each_child_of_node(ctrl->ngd->pdev->dev.of_node, node) {
11468c2ecf20Sopenharmony_ci		sbdev = of_slim_get_device(&ctrl->ctrl, node);
11478c2ecf20Sopenharmony_ci		if (!sbdev)
11488c2ecf20Sopenharmony_ci			continue;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (slim_get_logical_addr(sbdev))
11518c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "Failed to get logical address\n");
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_master_worker(struct work_struct *work)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl;
11588c2ecf20Sopenharmony_ci	struct slim_msg_txn txn;
11598c2ecf20Sopenharmony_ci	struct slim_val_inf msg = {0};
11608c2ecf20Sopenharmony_ci	int retries = 0;
11618c2ecf20Sopenharmony_ci	u8 wbuf[8];
11628c2ecf20Sopenharmony_ci	int ret = 0;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	ctrl = container_of(work, struct qcom_slim_ngd_ctrl, m_work);
11658c2ecf20Sopenharmony_ci	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
11668c2ecf20Sopenharmony_ci	txn.ec = 0;
11678c2ecf20Sopenharmony_ci	txn.mc = SLIM_USR_MC_REPORT_SATELLITE;
11688c2ecf20Sopenharmony_ci	txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
11698c2ecf20Sopenharmony_ci	txn.la = SLIM_LA_MGR;
11708c2ecf20Sopenharmony_ci	wbuf[0] = SAT_MAGIC_LSB;
11718c2ecf20Sopenharmony_ci	wbuf[1] = SAT_MAGIC_MSB;
11728c2ecf20Sopenharmony_ci	wbuf[2] = SAT_MSG_VER;
11738c2ecf20Sopenharmony_ci	wbuf[3] = SAT_MSG_PROT;
11748c2ecf20Sopenharmony_ci	txn.msg = &msg;
11758c2ecf20Sopenharmony_ci	txn.msg->wbuf = wbuf;
11768c2ecf20Sopenharmony_ci	txn.msg->num_bytes = 4;
11778c2ecf20Sopenharmony_ci	txn.rl = 8;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	dev_info(ctrl->dev, "SLIM SAT: Rcvd master capability\n");
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cicapability_retry:
11828c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_xfer_msg(&ctrl->ctrl, &txn);
11838c2ecf20Sopenharmony_ci	if (!ret) {
11848c2ecf20Sopenharmony_ci		if (ctrl->state >= QCOM_SLIM_NGD_CTRL_ASLEEP)
11858c2ecf20Sopenharmony_ci			complete(&ctrl->reconf);
11868c2ecf20Sopenharmony_ci		else
11878c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "unexpected state:%d\n",
11888c2ecf20Sopenharmony_ci						ctrl->state);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN)
11918c2ecf20Sopenharmony_ci			qcom_slim_ngd_notify_slaves(ctrl);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	} else if (ret == -EIO) {
11948c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "capability message NACKed, retrying\n");
11958c2ecf20Sopenharmony_ci		if (retries < INIT_MX_RETRIES) {
11968c2ecf20Sopenharmony_ci			msleep(DEF_RETRY_MS);
11978c2ecf20Sopenharmony_ci			retries++;
11988c2ecf20Sopenharmony_ci			goto capability_retry;
11998c2ecf20Sopenharmony_ci		}
12008c2ecf20Sopenharmony_ci	} else {
12018c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "SLIM: capability TX failed:%d\n", ret);
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_runtime_resume(struct device *dev)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev);
12088c2ecf20Sopenharmony_ci	int ret = 0;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	if (!ctrl->qmi.handle)
12118c2ecf20Sopenharmony_ci		return 0;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	if (ctrl->state >= QCOM_SLIM_NGD_CTRL_ASLEEP)
12148c2ecf20Sopenharmony_ci		ret = qcom_slim_ngd_power_up(ctrl);
12158c2ecf20Sopenharmony_ci	if (ret) {
12168c2ecf20Sopenharmony_ci		/* Did SSR cause this power up failure */
12178c2ecf20Sopenharmony_ci		if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN)
12188c2ecf20Sopenharmony_ci			ctrl->state = QCOM_SLIM_NGD_CTRL_ASLEEP;
12198c2ecf20Sopenharmony_ci		else
12208c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "HW wakeup attempt during SSR\n");
12218c2ecf20Sopenharmony_ci	} else {
12228c2ecf20Sopenharmony_ci		ctrl->state = QCOM_SLIM_NGD_CTRL_AWAKE;
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	return 0;
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_enable(struct qcom_slim_ngd_ctrl *ctrl, bool enable)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	if (enable) {
12318c2ecf20Sopenharmony_ci		int ret = qcom_slim_qmi_init(ctrl, false);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci		if (ret) {
12348c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "qmi init fail, ret:%d, state:%d\n",
12358c2ecf20Sopenharmony_ci				ret, ctrl->state);
12368c2ecf20Sopenharmony_ci			return ret;
12378c2ecf20Sopenharmony_ci		}
12388c2ecf20Sopenharmony_ci		/* controller state should be in sync with framework state */
12398c2ecf20Sopenharmony_ci		complete(&ctrl->qmi.qmi_comp);
12408c2ecf20Sopenharmony_ci		if (!pm_runtime_enabled(ctrl->dev) ||
12418c2ecf20Sopenharmony_ci				!pm_runtime_suspended(ctrl->dev))
12428c2ecf20Sopenharmony_ci			qcom_slim_ngd_runtime_resume(ctrl->dev);
12438c2ecf20Sopenharmony_ci		else
12448c2ecf20Sopenharmony_ci			pm_runtime_resume(ctrl->dev);
12458c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(ctrl->dev);
12468c2ecf20Sopenharmony_ci		pm_runtime_put(ctrl->dev);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci		ret = slim_register_controller(&ctrl->ctrl);
12498c2ecf20Sopenharmony_ci		if (ret) {
12508c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "error adding slim controller\n");
12518c2ecf20Sopenharmony_ci			return ret;
12528c2ecf20Sopenharmony_ci		}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		dev_info(ctrl->dev, "SLIM controller Registered\n");
12558c2ecf20Sopenharmony_ci	} else {
12568c2ecf20Sopenharmony_ci		qcom_slim_qmi_exit(ctrl);
12578c2ecf20Sopenharmony_ci		slim_unregister_controller(&ctrl->ctrl);
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	return 0;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_qmi_new_server(struct qmi_handle *hdl,
12648c2ecf20Sopenharmony_ci					struct qmi_service *service)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_qmi *qmi =
12678c2ecf20Sopenharmony_ci		container_of(hdl, struct qcom_slim_ngd_qmi, svc_event_hdl);
12688c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl =
12698c2ecf20Sopenharmony_ci		container_of(qmi, struct qcom_slim_ngd_ctrl, qmi);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	qmi->svc_info.sq_family = AF_QIPCRTR;
12728c2ecf20Sopenharmony_ci	qmi->svc_info.sq_node = service->node;
12738c2ecf20Sopenharmony_ci	qmi->svc_info.sq_port = service->port;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	qcom_slim_ngd_enable(ctrl, true);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	return 0;
12788c2ecf20Sopenharmony_ci}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_qmi_del_server(struct qmi_handle *hdl,
12818c2ecf20Sopenharmony_ci					 struct qmi_service *service)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_qmi *qmi =
12848c2ecf20Sopenharmony_ci		container_of(hdl, struct qcom_slim_ngd_qmi, svc_event_hdl);
12858c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl =
12868c2ecf20Sopenharmony_ci		container_of(qmi, struct qcom_slim_ngd_ctrl, qmi);
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	qmi->svc_info.sq_node = 0;
12898c2ecf20Sopenharmony_ci	qmi->svc_info.sq_port = 0;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	qcom_slim_ngd_enable(ctrl, false);
12928c2ecf20Sopenharmony_ci}
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_cistatic struct qmi_ops qcom_slim_ngd_qmi_svc_event_ops = {
12958c2ecf20Sopenharmony_ci	.new_server = qcom_slim_ngd_qmi_new_server,
12968c2ecf20Sopenharmony_ci	.del_server = qcom_slim_ngd_qmi_del_server,
12978c2ecf20Sopenharmony_ci};
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_qmi_svc_event_init(struct qcom_slim_ngd_ctrl *ctrl)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_qmi *qmi = &ctrl->qmi;
13028c2ecf20Sopenharmony_ci	int ret;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	ret = qmi_handle_init(&qmi->svc_event_hdl, 0,
13058c2ecf20Sopenharmony_ci				&qcom_slim_ngd_qmi_svc_event_ops, NULL);
13068c2ecf20Sopenharmony_ci	if (ret < 0) {
13078c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "qmi_handle_init failed: %d\n", ret);
13088c2ecf20Sopenharmony_ci		return ret;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	ret = qmi_add_lookup(&qmi->svc_event_hdl, SLIMBUS_QMI_SVC_ID,
13128c2ecf20Sopenharmony_ci			SLIMBUS_QMI_SVC_V1, SLIMBUS_QMI_INS_ID);
13138c2ecf20Sopenharmony_ci	if (ret < 0) {
13148c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "qmi_add_lookup failed: %d\n", ret);
13158c2ecf20Sopenharmony_ci		qmi_handle_release(&qmi->svc_event_hdl);
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci	return ret;
13188c2ecf20Sopenharmony_ci}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_cistatic void qcom_slim_ngd_qmi_svc_event_deinit(struct qcom_slim_ngd_qmi *qmi)
13218c2ecf20Sopenharmony_ci{
13228c2ecf20Sopenharmony_ci	qmi_handle_release(&qmi->svc_event_hdl);
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_driver;
13268c2ecf20Sopenharmony_ci#define QCOM_SLIM_NGD_DRV_NAME	"qcom,slim-ngd"
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_slim_ngd_dt_match[] = {
13298c2ecf20Sopenharmony_ci	{
13308c2ecf20Sopenharmony_ci		.compatible = "qcom,slim-ngd-v1.5.0",
13318c2ecf20Sopenharmony_ci		.data = &ngd_v1_5_offset_info,
13328c2ecf20Sopenharmony_ci	},{
13338c2ecf20Sopenharmony_ci		.compatible = "qcom,slim-ngd-v2.1.0",
13348c2ecf20Sopenharmony_ci		.data = &ngd_v1_5_offset_info,
13358c2ecf20Sopenharmony_ci	},
13368c2ecf20Sopenharmony_ci	{}
13378c2ecf20Sopenharmony_ci};
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_slim_ngd_dt_match);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_cistatic int of_qcom_slim_ngd_register(struct device *parent,
13428c2ecf20Sopenharmony_ci				     struct qcom_slim_ngd_ctrl *ctrl)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	const struct ngd_reg_offset_data *data;
13458c2ecf20Sopenharmony_ci	struct qcom_slim_ngd *ngd;
13468c2ecf20Sopenharmony_ci	const struct of_device_id *match;
13478c2ecf20Sopenharmony_ci	struct device_node *node;
13488c2ecf20Sopenharmony_ci	u32 id;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	match = of_match_node(qcom_slim_ngd_dt_match, parent->of_node);
13518c2ecf20Sopenharmony_ci	data = match->data;
13528c2ecf20Sopenharmony_ci	for_each_available_child_of_node(parent->of_node, node) {
13538c2ecf20Sopenharmony_ci		if (of_property_read_u32(node, "reg", &id))
13548c2ecf20Sopenharmony_ci			continue;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci		ngd = kzalloc(sizeof(*ngd), GFP_KERNEL);
13578c2ecf20Sopenharmony_ci		if (!ngd) {
13588c2ecf20Sopenharmony_ci			of_node_put(node);
13598c2ecf20Sopenharmony_ci			return -ENOMEM;
13608c2ecf20Sopenharmony_ci		}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		ngd->pdev = platform_device_alloc(QCOM_SLIM_NGD_DRV_NAME, id);
13638c2ecf20Sopenharmony_ci		if (!ngd->pdev) {
13648c2ecf20Sopenharmony_ci			kfree(ngd);
13658c2ecf20Sopenharmony_ci			of_node_put(node);
13668c2ecf20Sopenharmony_ci			return -ENOMEM;
13678c2ecf20Sopenharmony_ci		}
13688c2ecf20Sopenharmony_ci		ngd->id = id;
13698c2ecf20Sopenharmony_ci		ngd->pdev->dev.parent = parent;
13708c2ecf20Sopenharmony_ci		ngd->pdev->driver_override = QCOM_SLIM_NGD_DRV_NAME;
13718c2ecf20Sopenharmony_ci		ngd->pdev->dev.of_node = node;
13728c2ecf20Sopenharmony_ci		ctrl->ngd = ngd;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci		platform_device_add(ngd->pdev);
13758c2ecf20Sopenharmony_ci		ngd->base = ctrl->base + ngd->id * data->offset +
13768c2ecf20Sopenharmony_ci					(ngd->id - 1) * data->size;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci		return 0;
13798c2ecf20Sopenharmony_ci	}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	return -ENODEV;
13828c2ecf20Sopenharmony_ci}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_probe(struct platform_device *pdev)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
13878c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev->parent);
13888c2ecf20Sopenharmony_ci	int ret;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	ctrl->ctrl.dev = dev;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctrl);
13938c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
13948c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, QCOM_SLIM_NGD_AUTOSUSPEND);
13958c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(dev);
13968c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
13978c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(dev);
13988c2ecf20Sopenharmony_ci	ret = qcom_slim_ngd_qmi_svc_event_init(ctrl);
13998c2ecf20Sopenharmony_ci	if (ret) {
14008c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "QMI service registration failed:%d", ret);
14018c2ecf20Sopenharmony_ci		return ret;
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker);
14058c2ecf20Sopenharmony_ci	ctrl->mwq = create_singlethread_workqueue("ngd_master");
14068c2ecf20Sopenharmony_ci	if (!ctrl->mwq) {
14078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to start master worker\n");
14088c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14098c2ecf20Sopenharmony_ci		goto wq_err;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	return 0;
14138c2ecf20Sopenharmony_ciwq_err:
14148c2ecf20Sopenharmony_ci	qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
14158c2ecf20Sopenharmony_ci	if (ctrl->mwq)
14168c2ecf20Sopenharmony_ci		destroy_workqueue(ctrl->mwq);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	return ret;
14198c2ecf20Sopenharmony_ci}
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
14248c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl;
14258c2ecf20Sopenharmony_ci	struct resource *res;
14268c2ecf20Sopenharmony_ci	int ret;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
14298c2ecf20Sopenharmony_ci	if (!ctrl)
14308c2ecf20Sopenharmony_ci		return -ENOMEM;
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, ctrl);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14358c2ecf20Sopenharmony_ci	ctrl->base = devm_ioremap_resource(dev, res);
14368c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->base))
14378c2ecf20Sopenharmony_ci		return PTR_ERR(ctrl->base);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
14408c2ecf20Sopenharmony_ci	if (!res) {
14418c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no slimbus IRQ resource\n");
14428c2ecf20Sopenharmony_ci		return -ENODEV;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, res->start, qcom_slim_ngd_interrupt,
14468c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_HIGH, "slim-ngd", ctrl);
14478c2ecf20Sopenharmony_ci	if (ret) {
14488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "request IRQ failed\n");
14498c2ecf20Sopenharmony_ci		return ret;
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	ctrl->dev = dev;
14538c2ecf20Sopenharmony_ci	ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
14548c2ecf20Sopenharmony_ci	ctrl->framer.superfreq =
14558c2ecf20Sopenharmony_ci		ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	ctrl->ctrl.a_framer = &ctrl->framer;
14588c2ecf20Sopenharmony_ci	ctrl->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
14598c2ecf20Sopenharmony_ci	ctrl->ctrl.get_laddr = qcom_slim_ngd_get_laddr;
14608c2ecf20Sopenharmony_ci	ctrl->ctrl.enable_stream = qcom_slim_ngd_enable_stream;
14618c2ecf20Sopenharmony_ci	ctrl->ctrl.xfer_msg = qcom_slim_ngd_xfer_msg;
14628c2ecf20Sopenharmony_ci	ctrl->ctrl.wakeup = NULL;
14638c2ecf20Sopenharmony_ci	ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	spin_lock_init(&ctrl->tx_buf_lock);
14668c2ecf20Sopenharmony_ci	init_completion(&ctrl->reconf);
14678c2ecf20Sopenharmony_ci	init_completion(&ctrl->qmi.qmi_comp);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	platform_driver_register(&qcom_slim_ngd_driver);
14708c2ecf20Sopenharmony_ci	return of_qcom_slim_ngd_register(dev, ctrl);
14718c2ecf20Sopenharmony_ci}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	platform_driver_unregister(&qcom_slim_ngd_driver);
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	return 0;
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int qcom_slim_ngd_remove(struct platform_device *pdev)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
14858c2ecf20Sopenharmony_ci	qcom_slim_ngd_enable(ctrl, false);
14868c2ecf20Sopenharmony_ci	qcom_slim_ngd_exit_dma(ctrl);
14878c2ecf20Sopenharmony_ci	qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
14888c2ecf20Sopenharmony_ci	if (ctrl->mwq)
14898c2ecf20Sopenharmony_ci		destroy_workqueue(ctrl->mwq);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	kfree(ctrl->ngd);
14928c2ecf20Sopenharmony_ci	ctrl->ngd = NULL;
14938c2ecf20Sopenharmony_ci	return 0;
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_slim_ngd_runtime_idle(struct device *dev)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	if (ctrl->state == QCOM_SLIM_NGD_CTRL_AWAKE)
15018c2ecf20Sopenharmony_ci		ctrl->state = QCOM_SLIM_NGD_CTRL_IDLE;
15028c2ecf20Sopenharmony_ci	pm_request_autosuspend(dev);
15038c2ecf20Sopenharmony_ci	return -EAGAIN;
15048c2ecf20Sopenharmony_ci}
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_cistatic int __maybe_unused qcom_slim_ngd_runtime_suspend(struct device *dev)
15078c2ecf20Sopenharmony_ci{
15088c2ecf20Sopenharmony_ci	struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev);
15098c2ecf20Sopenharmony_ci	int ret = 0;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	qcom_slim_ngd_exit_dma(ctrl);
15128c2ecf20Sopenharmony_ci	if (!ctrl->qmi.handle)
15138c2ecf20Sopenharmony_ci		return 0;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	ret = qcom_slim_qmi_power_request(ctrl, false);
15168c2ecf20Sopenharmony_ci	if (ret && ret != -EBUSY)
15178c2ecf20Sopenharmony_ci		dev_info(ctrl->dev, "slim resource not idle:%d\n", ret);
15188c2ecf20Sopenharmony_ci	if (!ret || ret == -ETIMEDOUT)
15198c2ecf20Sopenharmony_ci		ctrl->state = QCOM_SLIM_NGD_CTRL_ASLEEP;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	return ret;
15228c2ecf20Sopenharmony_ci}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic const struct dev_pm_ops qcom_slim_ngd_dev_pm_ops = {
15258c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
15268c2ecf20Sopenharmony_ci				pm_runtime_force_resume)
15278c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(
15288c2ecf20Sopenharmony_ci		qcom_slim_ngd_runtime_suspend,
15298c2ecf20Sopenharmony_ci		qcom_slim_ngd_runtime_resume,
15308c2ecf20Sopenharmony_ci		qcom_slim_ngd_runtime_idle
15318c2ecf20Sopenharmony_ci	)
15328c2ecf20Sopenharmony_ci};
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_ctrl_driver = {
15358c2ecf20Sopenharmony_ci	.probe = qcom_slim_ngd_ctrl_probe,
15368c2ecf20Sopenharmony_ci	.remove = qcom_slim_ngd_ctrl_remove,
15378c2ecf20Sopenharmony_ci	.driver	= {
15388c2ecf20Sopenharmony_ci		.name = "qcom,slim-ngd-ctrl",
15398c2ecf20Sopenharmony_ci		.of_match_table = qcom_slim_ngd_dt_match,
15408c2ecf20Sopenharmony_ci	},
15418c2ecf20Sopenharmony_ci};
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_driver = {
15448c2ecf20Sopenharmony_ci	.probe = qcom_slim_ngd_probe,
15458c2ecf20Sopenharmony_ci	.remove = qcom_slim_ngd_remove,
15468c2ecf20Sopenharmony_ci	.driver	= {
15478c2ecf20Sopenharmony_ci		.name = QCOM_SLIM_NGD_DRV_NAME,
15488c2ecf20Sopenharmony_ci		.pm = &qcom_slim_ngd_dev_pm_ops,
15498c2ecf20Sopenharmony_ci	},
15508c2ecf20Sopenharmony_ci};
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_cimodule_platform_driver(qcom_slim_ngd_ctrl_driver);
15538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
15548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SLIMBus NGD controller");
1555