162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/irq.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/dmaengine.h> 1362306a36Sopenharmony_ci#include <linux/slimbus.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/notifier.h> 1862306a36Sopenharmony_ci#include <linux/remoteproc/qcom_rproc.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/soc/qcom/qmi.h> 2262306a36Sopenharmony_ci#include <linux/soc/qcom/pdr.h> 2362306a36Sopenharmony_ci#include <net/sock.h> 2462306a36Sopenharmony_ci#include "slimbus.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* NGD (Non-ported Generic Device) registers */ 2762306a36Sopenharmony_ci#define NGD_CFG 0x0 2862306a36Sopenharmony_ci#define NGD_CFG_ENABLE BIT(0) 2962306a36Sopenharmony_ci#define NGD_CFG_RX_MSGQ_EN BIT(1) 3062306a36Sopenharmony_ci#define NGD_CFG_TX_MSGQ_EN BIT(2) 3162306a36Sopenharmony_ci#define NGD_STATUS 0x4 3262306a36Sopenharmony_ci#define NGD_LADDR BIT(1) 3362306a36Sopenharmony_ci#define NGD_RX_MSGQ_CFG 0x8 3462306a36Sopenharmony_ci#define NGD_INT_EN 0x10 3562306a36Sopenharmony_ci#define NGD_INT_RECFG_DONE BIT(24) 3662306a36Sopenharmony_ci#define NGD_INT_TX_NACKED_2 BIT(25) 3762306a36Sopenharmony_ci#define NGD_INT_MSG_BUF_CONTE BIT(26) 3862306a36Sopenharmony_ci#define NGD_INT_MSG_TX_INVAL BIT(27) 3962306a36Sopenharmony_ci#define NGD_INT_IE_VE_CHG BIT(28) 4062306a36Sopenharmony_ci#define NGD_INT_DEV_ERR BIT(29) 4162306a36Sopenharmony_ci#define NGD_INT_RX_MSG_RCVD BIT(30) 4262306a36Sopenharmony_ci#define NGD_INT_TX_MSG_SENT BIT(31) 4362306a36Sopenharmony_ci#define NGD_INT_STAT 0x14 4462306a36Sopenharmony_ci#define NGD_INT_CLR 0x18 4562306a36Sopenharmony_ci#define DEF_NGD_INT_MASK (NGD_INT_TX_NACKED_2 | NGD_INT_MSG_BUF_CONTE | \ 4662306a36Sopenharmony_ci NGD_INT_MSG_TX_INVAL | NGD_INT_IE_VE_CHG | \ 4762306a36Sopenharmony_ci NGD_INT_DEV_ERR | NGD_INT_TX_MSG_SENT | \ 4862306a36Sopenharmony_ci NGD_INT_RX_MSG_RCVD) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Slimbus QMI service */ 5162306a36Sopenharmony_ci#define SLIMBUS_QMI_SVC_ID 0x0301 5262306a36Sopenharmony_ci#define SLIMBUS_QMI_SVC_V1 1 5362306a36Sopenharmony_ci#define SLIMBUS_QMI_INS_ID 0 5462306a36Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01 0x0020 5562306a36Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01 0x0020 5662306a36Sopenharmony_ci#define SLIMBUS_QMI_POWER_REQ_V01 0x0021 5762306a36Sopenharmony_ci#define SLIMBUS_QMI_POWER_RESP_V01 0x0021 5862306a36Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ 0x0022 5962306a36Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP 0x0022 6062306a36Sopenharmony_ci#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 14 6162306a36Sopenharmony_ci#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7 6262306a36Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14 6362306a36Sopenharmony_ci#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7 6462306a36Sopenharmony_ci#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN 7 6562306a36Sopenharmony_ci/* QMI response timeout of 500ms */ 6662306a36Sopenharmony_ci#define SLIMBUS_QMI_RESP_TOUT 1000 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* User defined commands */ 6962306a36Sopenharmony_ci#define SLIM_USR_MC_GENERIC_ACK 0x25 7062306a36Sopenharmony_ci#define SLIM_USR_MC_MASTER_CAPABILITY 0x0 7162306a36Sopenharmony_ci#define SLIM_USR_MC_REPORT_SATELLITE 0x1 7262306a36Sopenharmony_ci#define SLIM_USR_MC_ADDR_QUERY 0xD 7362306a36Sopenharmony_ci#define SLIM_USR_MC_ADDR_REPLY 0xE 7462306a36Sopenharmony_ci#define SLIM_USR_MC_DEFINE_CHAN 0x20 7562306a36Sopenharmony_ci#define SLIM_USR_MC_DEF_ACT_CHAN 0x21 7662306a36Sopenharmony_ci#define SLIM_USR_MC_CHAN_CTRL 0x23 7762306a36Sopenharmony_ci#define SLIM_USR_MC_RECONFIG_NOW 0x24 7862306a36Sopenharmony_ci#define SLIM_USR_MC_REQ_BW 0x28 7962306a36Sopenharmony_ci#define SLIM_USR_MC_CONNECT_SRC 0x2C 8062306a36Sopenharmony_ci#define SLIM_USR_MC_CONNECT_SINK 0x2D 8162306a36Sopenharmony_ci#define SLIM_USR_MC_DISCONNECT_PORT 0x2E 8262306a36Sopenharmony_ci#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define QCOM_SLIM_NGD_AUTOSUSPEND MSEC_PER_SEC 8562306a36Sopenharmony_ci#define SLIM_RX_MSGQ_TIMEOUT_VAL 0x10000 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define SLIM_LA_MGR 0xFF 8862306a36Sopenharmony_ci#define SLIM_ROOT_FREQ 24576000 8962306a36Sopenharmony_ci#define LADDR_RETRY 5 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Per spec.max 40 bytes per received message */ 9262306a36Sopenharmony_ci#define SLIM_MSGQ_BUF_LEN 40 9362306a36Sopenharmony_ci#define QCOM_SLIM_NGD_DESC_NUM 32 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \ 9662306a36Sopenharmony_ci ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16)) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define INIT_MX_RETRIES 10 9962306a36Sopenharmony_ci#define DEF_RETRY_MS 10 10062306a36Sopenharmony_ci#define SAT_MAGIC_LSB 0xD9 10162306a36Sopenharmony_ci#define SAT_MAGIC_MSB 0xC5 10262306a36Sopenharmony_ci#define SAT_MSG_VER 0x1 10362306a36Sopenharmony_ci#define SAT_MSG_PROT 0x1 10462306a36Sopenharmony_ci#define to_ngd(d) container_of(d, struct qcom_slim_ngd, dev) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct ngd_reg_offset_data { 10762306a36Sopenharmony_ci u32 offset, size; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic const struct ngd_reg_offset_data ngd_v1_5_offset_info = { 11162306a36Sopenharmony_ci .offset = 0x1000, 11262306a36Sopenharmony_ci .size = 0x1000, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cienum qcom_slim_ngd_state { 11662306a36Sopenharmony_ci QCOM_SLIM_NGD_CTRL_AWAKE, 11762306a36Sopenharmony_ci QCOM_SLIM_NGD_CTRL_IDLE, 11862306a36Sopenharmony_ci QCOM_SLIM_NGD_CTRL_ASLEEP, 11962306a36Sopenharmony_ci QCOM_SLIM_NGD_CTRL_DOWN, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct qcom_slim_ngd_qmi { 12362306a36Sopenharmony_ci struct qmi_handle qmi; 12462306a36Sopenharmony_ci struct sockaddr_qrtr svc_info; 12562306a36Sopenharmony_ci struct qmi_handle svc_event_hdl; 12662306a36Sopenharmony_ci struct qmi_response_type_v01 resp; 12762306a36Sopenharmony_ci struct qmi_handle *handle; 12862306a36Sopenharmony_ci struct completion qmi_comp; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct qcom_slim_ngd_ctrl; 13262306a36Sopenharmony_cistruct qcom_slim_ngd; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct qcom_slim_ngd_dma_desc { 13562306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 13662306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl; 13762306a36Sopenharmony_ci struct completion *comp; 13862306a36Sopenharmony_ci dma_cookie_t cookie; 13962306a36Sopenharmony_ci dma_addr_t phys; 14062306a36Sopenharmony_ci void *base; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistruct qcom_slim_ngd { 14462306a36Sopenharmony_ci struct platform_device *pdev; 14562306a36Sopenharmony_ci void __iomem *base; 14662306a36Sopenharmony_ci int id; 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistruct qcom_slim_ngd_ctrl { 15062306a36Sopenharmony_ci struct slim_framer framer; 15162306a36Sopenharmony_ci struct slim_controller ctrl; 15262306a36Sopenharmony_ci struct qcom_slim_ngd_qmi qmi; 15362306a36Sopenharmony_ci struct qcom_slim_ngd *ngd; 15462306a36Sopenharmony_ci struct device *dev; 15562306a36Sopenharmony_ci void __iomem *base; 15662306a36Sopenharmony_ci struct dma_chan *dma_rx_channel; 15762306a36Sopenharmony_ci struct dma_chan *dma_tx_channel; 15862306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc rx_desc[QCOM_SLIM_NGD_DESC_NUM]; 15962306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc txdesc[QCOM_SLIM_NGD_DESC_NUM]; 16062306a36Sopenharmony_ci struct completion reconf; 16162306a36Sopenharmony_ci struct work_struct m_work; 16262306a36Sopenharmony_ci struct work_struct ngd_up_work; 16362306a36Sopenharmony_ci struct workqueue_struct *mwq; 16462306a36Sopenharmony_ci struct completion qmi_up; 16562306a36Sopenharmony_ci spinlock_t tx_buf_lock; 16662306a36Sopenharmony_ci struct mutex tx_lock; 16762306a36Sopenharmony_ci struct mutex ssr_lock; 16862306a36Sopenharmony_ci struct notifier_block nb; 16962306a36Sopenharmony_ci void *notifier; 17062306a36Sopenharmony_ci struct pdr_handle *pdr; 17162306a36Sopenharmony_ci enum qcom_slim_ngd_state state; 17262306a36Sopenharmony_ci dma_addr_t rx_phys_base; 17362306a36Sopenharmony_ci dma_addr_t tx_phys_base; 17462306a36Sopenharmony_ci void *rx_base; 17562306a36Sopenharmony_ci void *tx_base; 17662306a36Sopenharmony_ci int tx_tail; 17762306a36Sopenharmony_ci int tx_head; 17862306a36Sopenharmony_ci u32 ver; 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cienum slimbus_mode_enum_type_v01 { 18262306a36Sopenharmony_ci /* To force a 32 bit signed enum. Do not change or use*/ 18362306a36Sopenharmony_ci SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN, 18462306a36Sopenharmony_ci SLIMBUS_MODE_SATELLITE_V01 = 1, 18562306a36Sopenharmony_ci SLIMBUS_MODE_MASTER_V01 = 2, 18662306a36Sopenharmony_ci SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cienum slimbus_pm_enum_type_v01 { 19062306a36Sopenharmony_ci /* To force a 32 bit signed enum. Do not change or use*/ 19162306a36Sopenharmony_ci SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN, 19262306a36Sopenharmony_ci SLIMBUS_PM_INACTIVE_V01 = 1, 19362306a36Sopenharmony_ci SLIMBUS_PM_ACTIVE_V01 = 2, 19462306a36Sopenharmony_ci SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cienum slimbus_resp_enum_type_v01 { 19862306a36Sopenharmony_ci SLIMBUS_RESP_ENUM_TYPE_MIN_VAL_V01 = INT_MIN, 19962306a36Sopenharmony_ci SLIMBUS_RESP_SYNCHRONOUS_V01 = 1, 20062306a36Sopenharmony_ci SLIMBUS_RESP_ENUM_TYPE_MAX_VAL_V01 = INT_MAX, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistruct slimbus_select_inst_req_msg_v01 { 20462306a36Sopenharmony_ci uint32_t instance; 20562306a36Sopenharmony_ci uint8_t mode_valid; 20662306a36Sopenharmony_ci enum slimbus_mode_enum_type_v01 mode; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistruct slimbus_select_inst_resp_msg_v01 { 21062306a36Sopenharmony_ci struct qmi_response_type_v01 resp; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistruct slimbus_power_req_msg_v01 { 21462306a36Sopenharmony_ci enum slimbus_pm_enum_type_v01 pm_req; 21562306a36Sopenharmony_ci uint8_t resp_type_valid; 21662306a36Sopenharmony_ci enum slimbus_resp_enum_type_v01 resp_type; 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistruct slimbus_power_resp_msg_v01 { 22062306a36Sopenharmony_ci struct qmi_response_type_v01 resp; 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic struct qmi_elem_info slimbus_select_inst_req_msg_v01_ei[] = { 22462306a36Sopenharmony_ci { 22562306a36Sopenharmony_ci .data_type = QMI_UNSIGNED_4_BYTE, 22662306a36Sopenharmony_ci .elem_len = 1, 22762306a36Sopenharmony_ci .elem_size = sizeof(uint32_t), 22862306a36Sopenharmony_ci .array_type = NO_ARRAY, 22962306a36Sopenharmony_ci .tlv_type = 0x01, 23062306a36Sopenharmony_ci .offset = offsetof(struct slimbus_select_inst_req_msg_v01, 23162306a36Sopenharmony_ci instance), 23262306a36Sopenharmony_ci .ei_array = NULL, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci .data_type = QMI_OPT_FLAG, 23662306a36Sopenharmony_ci .elem_len = 1, 23762306a36Sopenharmony_ci .elem_size = sizeof(uint8_t), 23862306a36Sopenharmony_ci .array_type = NO_ARRAY, 23962306a36Sopenharmony_ci .tlv_type = 0x10, 24062306a36Sopenharmony_ci .offset = offsetof(struct slimbus_select_inst_req_msg_v01, 24162306a36Sopenharmony_ci mode_valid), 24262306a36Sopenharmony_ci .ei_array = NULL, 24362306a36Sopenharmony_ci }, 24462306a36Sopenharmony_ci { 24562306a36Sopenharmony_ci .data_type = QMI_UNSIGNED_4_BYTE, 24662306a36Sopenharmony_ci .elem_len = 1, 24762306a36Sopenharmony_ci .elem_size = sizeof(enum slimbus_mode_enum_type_v01), 24862306a36Sopenharmony_ci .array_type = NO_ARRAY, 24962306a36Sopenharmony_ci .tlv_type = 0x10, 25062306a36Sopenharmony_ci .offset = offsetof(struct slimbus_select_inst_req_msg_v01, 25162306a36Sopenharmony_ci mode), 25262306a36Sopenharmony_ci .ei_array = NULL, 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci { 25562306a36Sopenharmony_ci .data_type = QMI_EOTI, 25662306a36Sopenharmony_ci .elem_len = 0, 25762306a36Sopenharmony_ci .elem_size = 0, 25862306a36Sopenharmony_ci .array_type = NO_ARRAY, 25962306a36Sopenharmony_ci .tlv_type = 0x00, 26062306a36Sopenharmony_ci .offset = 0, 26162306a36Sopenharmony_ci .ei_array = NULL, 26262306a36Sopenharmony_ci }, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic struct qmi_elem_info slimbus_select_inst_resp_msg_v01_ei[] = { 26662306a36Sopenharmony_ci { 26762306a36Sopenharmony_ci .data_type = QMI_STRUCT, 26862306a36Sopenharmony_ci .elem_len = 1, 26962306a36Sopenharmony_ci .elem_size = sizeof(struct qmi_response_type_v01), 27062306a36Sopenharmony_ci .array_type = NO_ARRAY, 27162306a36Sopenharmony_ci .tlv_type = 0x02, 27262306a36Sopenharmony_ci .offset = offsetof(struct slimbus_select_inst_resp_msg_v01, 27362306a36Sopenharmony_ci resp), 27462306a36Sopenharmony_ci .ei_array = qmi_response_type_v01_ei, 27562306a36Sopenharmony_ci }, 27662306a36Sopenharmony_ci { 27762306a36Sopenharmony_ci .data_type = QMI_EOTI, 27862306a36Sopenharmony_ci .elem_len = 0, 27962306a36Sopenharmony_ci .elem_size = 0, 28062306a36Sopenharmony_ci .array_type = NO_ARRAY, 28162306a36Sopenharmony_ci .tlv_type = 0x00, 28262306a36Sopenharmony_ci .offset = 0, 28362306a36Sopenharmony_ci .ei_array = NULL, 28462306a36Sopenharmony_ci }, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic struct qmi_elem_info slimbus_power_req_msg_v01_ei[] = { 28862306a36Sopenharmony_ci { 28962306a36Sopenharmony_ci .data_type = QMI_UNSIGNED_4_BYTE, 29062306a36Sopenharmony_ci .elem_len = 1, 29162306a36Sopenharmony_ci .elem_size = sizeof(enum slimbus_pm_enum_type_v01), 29262306a36Sopenharmony_ci .array_type = NO_ARRAY, 29362306a36Sopenharmony_ci .tlv_type = 0x01, 29462306a36Sopenharmony_ci .offset = offsetof(struct slimbus_power_req_msg_v01, 29562306a36Sopenharmony_ci pm_req), 29662306a36Sopenharmony_ci .ei_array = NULL, 29762306a36Sopenharmony_ci }, 29862306a36Sopenharmony_ci { 29962306a36Sopenharmony_ci .data_type = QMI_OPT_FLAG, 30062306a36Sopenharmony_ci .elem_len = 1, 30162306a36Sopenharmony_ci .elem_size = sizeof(uint8_t), 30262306a36Sopenharmony_ci .array_type = NO_ARRAY, 30362306a36Sopenharmony_ci .tlv_type = 0x10, 30462306a36Sopenharmony_ci .offset = offsetof(struct slimbus_power_req_msg_v01, 30562306a36Sopenharmony_ci resp_type_valid), 30662306a36Sopenharmony_ci }, 30762306a36Sopenharmony_ci { 30862306a36Sopenharmony_ci .data_type = QMI_SIGNED_4_BYTE_ENUM, 30962306a36Sopenharmony_ci .elem_len = 1, 31062306a36Sopenharmony_ci .elem_size = sizeof(enum slimbus_resp_enum_type_v01), 31162306a36Sopenharmony_ci .array_type = NO_ARRAY, 31262306a36Sopenharmony_ci .tlv_type = 0x10, 31362306a36Sopenharmony_ci .offset = offsetof(struct slimbus_power_req_msg_v01, 31462306a36Sopenharmony_ci resp_type), 31562306a36Sopenharmony_ci }, 31662306a36Sopenharmony_ci { 31762306a36Sopenharmony_ci .data_type = QMI_EOTI, 31862306a36Sopenharmony_ci .elem_len = 0, 31962306a36Sopenharmony_ci .elem_size = 0, 32062306a36Sopenharmony_ci .array_type = NO_ARRAY, 32162306a36Sopenharmony_ci .tlv_type = 0x00, 32262306a36Sopenharmony_ci .offset = 0, 32362306a36Sopenharmony_ci .ei_array = NULL, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic struct qmi_elem_info slimbus_power_resp_msg_v01_ei[] = { 32862306a36Sopenharmony_ci { 32962306a36Sopenharmony_ci .data_type = QMI_STRUCT, 33062306a36Sopenharmony_ci .elem_len = 1, 33162306a36Sopenharmony_ci .elem_size = sizeof(struct qmi_response_type_v01), 33262306a36Sopenharmony_ci .array_type = NO_ARRAY, 33362306a36Sopenharmony_ci .tlv_type = 0x02, 33462306a36Sopenharmony_ci .offset = offsetof(struct slimbus_power_resp_msg_v01, resp), 33562306a36Sopenharmony_ci .ei_array = qmi_response_type_v01_ei, 33662306a36Sopenharmony_ci }, 33762306a36Sopenharmony_ci { 33862306a36Sopenharmony_ci .data_type = QMI_EOTI, 33962306a36Sopenharmony_ci .elem_len = 0, 34062306a36Sopenharmony_ci .elem_size = 0, 34162306a36Sopenharmony_ci .array_type = NO_ARRAY, 34262306a36Sopenharmony_ci .tlv_type = 0x00, 34362306a36Sopenharmony_ci .offset = 0, 34462306a36Sopenharmony_ci .ei_array = NULL, 34562306a36Sopenharmony_ci }, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int qcom_slim_qmi_send_select_inst_req(struct qcom_slim_ngd_ctrl *ctrl, 34962306a36Sopenharmony_ci struct slimbus_select_inst_req_msg_v01 *req) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct slimbus_select_inst_resp_msg_v01 resp = { { 0, 0 } }; 35262306a36Sopenharmony_ci struct qmi_txn txn; 35362306a36Sopenharmony_ci int rc; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rc = qmi_txn_init(ctrl->qmi.handle, &txn, 35662306a36Sopenharmony_ci slimbus_select_inst_resp_msg_v01_ei, &resp); 35762306a36Sopenharmony_ci if (rc < 0) { 35862306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI TXN init fail: %d\n", rc); 35962306a36Sopenharmony_ci return rc; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci rc = qmi_send_request(ctrl->qmi.handle, NULL, &txn, 36362306a36Sopenharmony_ci SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01, 36462306a36Sopenharmony_ci SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN, 36562306a36Sopenharmony_ci slimbus_select_inst_req_msg_v01_ei, req); 36662306a36Sopenharmony_ci if (rc < 0) { 36762306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI send req fail %d\n", rc); 36862306a36Sopenharmony_ci qmi_txn_cancel(&txn); 36962306a36Sopenharmony_ci return rc; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci rc = qmi_txn_wait(&txn, SLIMBUS_QMI_RESP_TOUT); 37362306a36Sopenharmony_ci if (rc < 0) { 37462306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI TXN wait fail: %d\n", rc); 37562306a36Sopenharmony_ci return rc; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci /* Check the response */ 37862306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 37962306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI request failed 0x%x\n", 38062306a36Sopenharmony_ci resp.resp.result); 38162306a36Sopenharmony_ci return -EREMOTEIO; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void qcom_slim_qmi_power_resp_cb(struct qmi_handle *handle, 38862306a36Sopenharmony_ci struct sockaddr_qrtr *sq, 38962306a36Sopenharmony_ci struct qmi_txn *txn, const void *data) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct slimbus_power_resp_msg_v01 *resp; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci resp = (struct slimbus_power_resp_msg_v01 *)data; 39462306a36Sopenharmony_ci if (resp->resp.result != QMI_RESULT_SUCCESS_V01) 39562306a36Sopenharmony_ci pr_err("QMI power request failed 0x%x\n", 39662306a36Sopenharmony_ci resp->resp.result); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci complete(&txn->completion); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int qcom_slim_qmi_send_power_request(struct qcom_slim_ngd_ctrl *ctrl, 40262306a36Sopenharmony_ci struct slimbus_power_req_msg_v01 *req) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } }; 40562306a36Sopenharmony_ci struct qmi_txn txn; 40662306a36Sopenharmony_ci int rc; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci rc = qmi_txn_init(ctrl->qmi.handle, &txn, 40962306a36Sopenharmony_ci slimbus_power_resp_msg_v01_ei, &resp); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rc = qmi_send_request(ctrl->qmi.handle, NULL, &txn, 41262306a36Sopenharmony_ci SLIMBUS_QMI_POWER_REQ_V01, 41362306a36Sopenharmony_ci SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN, 41462306a36Sopenharmony_ci slimbus_power_req_msg_v01_ei, req); 41562306a36Sopenharmony_ci if (rc < 0) { 41662306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI send req fail %d\n", rc); 41762306a36Sopenharmony_ci qmi_txn_cancel(&txn); 41862306a36Sopenharmony_ci return rc; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci rc = qmi_txn_wait(&txn, SLIMBUS_QMI_RESP_TOUT); 42262306a36Sopenharmony_ci if (rc < 0) { 42362306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI TXN wait fail: %d\n", rc); 42462306a36Sopenharmony_ci return rc; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Check the response */ 42862306a36Sopenharmony_ci if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { 42962306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI request failed 0x%x\n", 43062306a36Sopenharmony_ci resp.resp.result); 43162306a36Sopenharmony_ci return -EREMOTEIO; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const struct qmi_msg_handler qcom_slim_qmi_msg_handlers[] = { 43862306a36Sopenharmony_ci { 43962306a36Sopenharmony_ci .type = QMI_RESPONSE, 44062306a36Sopenharmony_ci .msg_id = SLIMBUS_QMI_POWER_RESP_V01, 44162306a36Sopenharmony_ci .ei = slimbus_power_resp_msg_v01_ei, 44262306a36Sopenharmony_ci .decoded_size = sizeof(struct slimbus_power_resp_msg_v01), 44362306a36Sopenharmony_ci .fn = qcom_slim_qmi_power_resp_cb, 44462306a36Sopenharmony_ci }, 44562306a36Sopenharmony_ci {} 44662306a36Sopenharmony_ci}; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int qcom_slim_qmi_init(struct qcom_slim_ngd_ctrl *ctrl, 44962306a36Sopenharmony_ci bool apps_is_master) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct slimbus_select_inst_req_msg_v01 req; 45262306a36Sopenharmony_ci struct qmi_handle *handle; 45362306a36Sopenharmony_ci int rc; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci handle = devm_kzalloc(ctrl->dev, sizeof(*handle), GFP_KERNEL); 45662306a36Sopenharmony_ci if (!handle) 45762306a36Sopenharmony_ci return -ENOMEM; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci rc = qmi_handle_init(handle, SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN, 46062306a36Sopenharmony_ci NULL, qcom_slim_qmi_msg_handlers); 46162306a36Sopenharmony_ci if (rc < 0) { 46262306a36Sopenharmony_ci dev_err(ctrl->dev, "QMI client init failed: %d\n", rc); 46362306a36Sopenharmony_ci goto qmi_handle_init_failed; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci rc = kernel_connect(handle->sock, 46762306a36Sopenharmony_ci (struct sockaddr *)&ctrl->qmi.svc_info, 46862306a36Sopenharmony_ci sizeof(ctrl->qmi.svc_info), 0); 46962306a36Sopenharmony_ci if (rc < 0) { 47062306a36Sopenharmony_ci dev_err(ctrl->dev, "Remote Service connect failed: %d\n", rc); 47162306a36Sopenharmony_ci goto qmi_connect_to_service_failed; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Instance is 0 based */ 47562306a36Sopenharmony_ci req.instance = (ctrl->ngd->id >> 1); 47662306a36Sopenharmony_ci req.mode_valid = 1; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Mode indicates the role of the ADSP */ 47962306a36Sopenharmony_ci if (apps_is_master) 48062306a36Sopenharmony_ci req.mode = SLIMBUS_MODE_SATELLITE_V01; 48162306a36Sopenharmony_ci else 48262306a36Sopenharmony_ci req.mode = SLIMBUS_MODE_MASTER_V01; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ctrl->qmi.handle = handle; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rc = qcom_slim_qmi_send_select_inst_req(ctrl, &req); 48762306a36Sopenharmony_ci if (rc) { 48862306a36Sopenharmony_ci dev_err(ctrl->dev, "failed to select h/w instance\n"); 48962306a36Sopenharmony_ci goto qmi_select_instance_failed; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciqmi_select_instance_failed: 49562306a36Sopenharmony_ci ctrl->qmi.handle = NULL; 49662306a36Sopenharmony_ciqmi_connect_to_service_failed: 49762306a36Sopenharmony_ci qmi_handle_release(handle); 49862306a36Sopenharmony_ciqmi_handle_init_failed: 49962306a36Sopenharmony_ci devm_kfree(ctrl->dev, handle); 50062306a36Sopenharmony_ci return rc; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void qcom_slim_qmi_exit(struct qcom_slim_ngd_ctrl *ctrl) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci if (!ctrl->qmi.handle) 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci qmi_handle_release(ctrl->qmi.handle); 50962306a36Sopenharmony_ci devm_kfree(ctrl->dev, ctrl->qmi.handle); 51062306a36Sopenharmony_ci ctrl->qmi.handle = NULL; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int qcom_slim_qmi_power_request(struct qcom_slim_ngd_ctrl *ctrl, 51462306a36Sopenharmony_ci bool active) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct slimbus_power_req_msg_v01 req; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (active) 51962306a36Sopenharmony_ci req.pm_req = SLIMBUS_PM_ACTIVE_V01; 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci req.pm_req = SLIMBUS_PM_INACTIVE_V01; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci req.resp_type_valid = 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return qcom_slim_qmi_send_power_request(ctrl, &req); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic u32 *qcom_slim_ngd_tx_msg_get(struct qcom_slim_ngd_ctrl *ctrl, int len, 52962306a36Sopenharmony_ci struct completion *comp) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc *desc; 53262306a36Sopenharmony_ci unsigned long flags; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->tx_buf_lock, flags); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if ((ctrl->tx_tail + 1) % QCOM_SLIM_NGD_DESC_NUM == ctrl->tx_head) { 53762306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 53862306a36Sopenharmony_ci return NULL; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci desc = &ctrl->txdesc[ctrl->tx_tail]; 54162306a36Sopenharmony_ci desc->base = ctrl->tx_base + ctrl->tx_tail * SLIM_MSGQ_BUF_LEN; 54262306a36Sopenharmony_ci desc->comp = comp; 54362306a36Sopenharmony_ci ctrl->tx_tail = (ctrl->tx_tail + 1) % QCOM_SLIM_NGD_DESC_NUM; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return desc->base; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void qcom_slim_ngd_tx_msg_dma_cb(void *args) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc *desc = args; 55362306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = desc->ctrl; 55462306a36Sopenharmony_ci unsigned long flags; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->tx_buf_lock, flags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (desc->comp) { 55962306a36Sopenharmony_ci complete(desc->comp); 56062306a36Sopenharmony_ci desc->comp = NULL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ctrl->tx_head = (ctrl->tx_head + 1) % QCOM_SLIM_NGD_DESC_NUM; 56462306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int qcom_slim_ngd_tx_msg_post(struct qcom_slim_ngd_ctrl *ctrl, 56862306a36Sopenharmony_ci void *buf, int len) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc *desc; 57162306a36Sopenharmony_ci unsigned long flags; 57262306a36Sopenharmony_ci int index, offset; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->tx_buf_lock, flags); 57562306a36Sopenharmony_ci offset = buf - ctrl->tx_base; 57662306a36Sopenharmony_ci index = offset/SLIM_MSGQ_BUF_LEN; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci desc = &ctrl->txdesc[index]; 57962306a36Sopenharmony_ci desc->phys = ctrl->tx_phys_base + offset; 58062306a36Sopenharmony_ci desc->base = ctrl->tx_base + offset; 58162306a36Sopenharmony_ci desc->ctrl = ctrl; 58262306a36Sopenharmony_ci len = (len + 3) & 0xfc; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci desc->desc = dmaengine_prep_slave_single(ctrl->dma_tx_channel, 58562306a36Sopenharmony_ci desc->phys, len, 58662306a36Sopenharmony_ci DMA_MEM_TO_DEV, 58762306a36Sopenharmony_ci DMA_PREP_INTERRUPT); 58862306a36Sopenharmony_ci if (!desc->desc) { 58962306a36Sopenharmony_ci dev_err(ctrl->dev, "unable to prepare channel\n"); 59062306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci desc->desc->callback = qcom_slim_ngd_tx_msg_dma_cb; 59562306a36Sopenharmony_ci desc->desc->callback_param = desc; 59662306a36Sopenharmony_ci desc->desc->cookie = dmaengine_submit(desc->desc); 59762306a36Sopenharmony_ci dma_async_issue_pending(ctrl->dma_tx_channel); 59862306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void qcom_slim_ngd_rx(struct qcom_slim_ngd_ctrl *ctrl, u8 *buf) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci u8 mc, mt, len; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci mt = SLIM_HEADER_GET_MT(buf[0]); 60862306a36Sopenharmony_ci len = SLIM_HEADER_GET_RL(buf[0]); 60962306a36Sopenharmony_ci mc = SLIM_HEADER_GET_MC(buf[1]); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (mc == SLIM_USR_MC_MASTER_CAPABILITY && 61262306a36Sopenharmony_ci mt == SLIM_MSG_MT_SRC_REFERRED_USER) 61362306a36Sopenharmony_ci queue_work(ctrl->mwq, &ctrl->m_work); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (mc == SLIM_MSG_MC_REPLY_INFORMATION || 61662306a36Sopenharmony_ci mc == SLIM_MSG_MC_REPLY_VALUE || (mc == SLIM_USR_MC_ADDR_REPLY && 61762306a36Sopenharmony_ci mt == SLIM_MSG_MT_SRC_REFERRED_USER) || 61862306a36Sopenharmony_ci (mc == SLIM_USR_MC_GENERIC_ACK && 61962306a36Sopenharmony_ci mt == SLIM_MSG_MT_SRC_REFERRED_USER)) { 62062306a36Sopenharmony_ci slim_msg_response(&ctrl->ctrl, &buf[4], buf[3], len - 4); 62162306a36Sopenharmony_ci pm_runtime_mark_last_busy(ctrl->ctrl.dev); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void qcom_slim_ngd_rx_msgq_cb(void *args) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc *desc = args; 62862306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = desc->ctrl; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci qcom_slim_ngd_rx(ctrl, (u8 *)desc->base); 63162306a36Sopenharmony_ci /* Add descriptor back to the queue */ 63262306a36Sopenharmony_ci desc->desc = dmaengine_prep_slave_single(ctrl->dma_rx_channel, 63362306a36Sopenharmony_ci desc->phys, SLIM_MSGQ_BUF_LEN, 63462306a36Sopenharmony_ci DMA_DEV_TO_MEM, 63562306a36Sopenharmony_ci DMA_PREP_INTERRUPT); 63662306a36Sopenharmony_ci if (!desc->desc) { 63762306a36Sopenharmony_ci dev_err(ctrl->dev, "Unable to prepare rx channel\n"); 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci desc->desc->callback = qcom_slim_ngd_rx_msgq_cb; 64262306a36Sopenharmony_ci desc->desc->callback_param = desc; 64362306a36Sopenharmony_ci desc->desc->cookie = dmaengine_submit(desc->desc); 64462306a36Sopenharmony_ci dma_async_issue_pending(ctrl->dma_rx_channel); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int qcom_slim_ngd_post_rx_msgq(struct qcom_slim_ngd_ctrl *ctrl) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct qcom_slim_ngd_dma_desc *desc; 65062306a36Sopenharmony_ci int i; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci for (i = 0; i < QCOM_SLIM_NGD_DESC_NUM; i++) { 65362306a36Sopenharmony_ci desc = &ctrl->rx_desc[i]; 65462306a36Sopenharmony_ci desc->phys = ctrl->rx_phys_base + i * SLIM_MSGQ_BUF_LEN; 65562306a36Sopenharmony_ci desc->ctrl = ctrl; 65662306a36Sopenharmony_ci desc->base = ctrl->rx_base + i * SLIM_MSGQ_BUF_LEN; 65762306a36Sopenharmony_ci desc->desc = dmaengine_prep_slave_single(ctrl->dma_rx_channel, 65862306a36Sopenharmony_ci desc->phys, SLIM_MSGQ_BUF_LEN, 65962306a36Sopenharmony_ci DMA_DEV_TO_MEM, 66062306a36Sopenharmony_ci DMA_PREP_INTERRUPT); 66162306a36Sopenharmony_ci if (!desc->desc) { 66262306a36Sopenharmony_ci dev_err(ctrl->dev, "Unable to prepare rx channel\n"); 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci desc->desc->callback = qcom_slim_ngd_rx_msgq_cb; 66762306a36Sopenharmony_ci desc->desc->callback_param = desc; 66862306a36Sopenharmony_ci desc->desc->cookie = dmaengine_submit(desc->desc); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci dma_async_issue_pending(ctrl->dma_rx_channel); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int qcom_slim_ngd_init_rx_msgq(struct qcom_slim_ngd_ctrl *ctrl) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct device *dev = ctrl->dev; 67862306a36Sopenharmony_ci int ret, size; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci ctrl->dma_rx_channel = dma_request_chan(dev, "rx"); 68162306a36Sopenharmony_ci if (IS_ERR(ctrl->dma_rx_channel)) { 68262306a36Sopenharmony_ci dev_err(dev, "Failed to request RX dma channel"); 68362306a36Sopenharmony_ci ret = PTR_ERR(ctrl->dma_rx_channel); 68462306a36Sopenharmony_ci ctrl->dma_rx_channel = NULL; 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci size = QCOM_SLIM_NGD_DESC_NUM * SLIM_MSGQ_BUF_LEN; 68962306a36Sopenharmony_ci ctrl->rx_base = dma_alloc_coherent(dev, size, &ctrl->rx_phys_base, 69062306a36Sopenharmony_ci GFP_KERNEL); 69162306a36Sopenharmony_ci if (!ctrl->rx_base) { 69262306a36Sopenharmony_ci ret = -ENOMEM; 69362306a36Sopenharmony_ci goto rel_rx; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ret = qcom_slim_ngd_post_rx_msgq(ctrl); 69762306a36Sopenharmony_ci if (ret) { 69862306a36Sopenharmony_ci dev_err(dev, "post_rx_msgq() failed 0x%x\n", ret); 69962306a36Sopenharmony_ci goto rx_post_err; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cirx_post_err: 70562306a36Sopenharmony_ci dma_free_coherent(dev, size, ctrl->rx_base, ctrl->rx_phys_base); 70662306a36Sopenharmony_cirel_rx: 70762306a36Sopenharmony_ci dma_release_channel(ctrl->dma_rx_channel); 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic int qcom_slim_ngd_init_tx_msgq(struct qcom_slim_ngd_ctrl *ctrl) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct device *dev = ctrl->dev; 71462306a36Sopenharmony_ci unsigned long flags; 71562306a36Sopenharmony_ci int ret = 0; 71662306a36Sopenharmony_ci int size; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ctrl->dma_tx_channel = dma_request_chan(dev, "tx"); 71962306a36Sopenharmony_ci if (IS_ERR(ctrl->dma_tx_channel)) { 72062306a36Sopenharmony_ci dev_err(dev, "Failed to request TX dma channel"); 72162306a36Sopenharmony_ci ret = PTR_ERR(ctrl->dma_tx_channel); 72262306a36Sopenharmony_ci ctrl->dma_tx_channel = NULL; 72362306a36Sopenharmony_ci return ret; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci size = ((QCOM_SLIM_NGD_DESC_NUM + 1) * SLIM_MSGQ_BUF_LEN); 72762306a36Sopenharmony_ci ctrl->tx_base = dma_alloc_coherent(dev, size, &ctrl->tx_phys_base, 72862306a36Sopenharmony_ci GFP_KERNEL); 72962306a36Sopenharmony_ci if (!ctrl->tx_base) { 73062306a36Sopenharmony_ci ret = -EINVAL; 73162306a36Sopenharmony_ci goto rel_tx; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->tx_buf_lock, flags); 73562306a36Sopenharmony_ci ctrl->tx_tail = 0; 73662306a36Sopenharmony_ci ctrl->tx_head = 0; 73762306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->tx_buf_lock, flags); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_cirel_tx: 74162306a36Sopenharmony_ci dma_release_channel(ctrl->dma_tx_channel); 74262306a36Sopenharmony_ci return ret; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int qcom_slim_ngd_init_dma(struct qcom_slim_ngd_ctrl *ctrl) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int ret = 0; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = qcom_slim_ngd_init_rx_msgq(ctrl); 75062306a36Sopenharmony_ci if (ret) { 75162306a36Sopenharmony_ci dev_err(ctrl->dev, "rx dma init failed\n"); 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = qcom_slim_ngd_init_tx_msgq(ctrl); 75662306a36Sopenharmony_ci if (ret) 75762306a36Sopenharmony_ci dev_err(ctrl->dev, "tx dma init failed\n"); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic irqreturn_t qcom_slim_ngd_interrupt(int irq, void *d) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = d; 76562306a36Sopenharmony_ci void __iomem *base = ctrl->ngd->base; 76662306a36Sopenharmony_ci u32 stat; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (pm_runtime_suspended(ctrl->ctrl.dev)) { 76962306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "Interrupt received while suspended\n"); 77062306a36Sopenharmony_ci return IRQ_NONE; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci stat = readl(base + NGD_INT_STAT); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if ((stat & NGD_INT_MSG_BUF_CONTE) || 77662306a36Sopenharmony_ci (stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) || 77762306a36Sopenharmony_ci (stat & NGD_INT_TX_NACKED_2)) { 77862306a36Sopenharmony_ci dev_err(ctrl->dev, "Error Interrupt received 0x%x\n", stat); 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci writel(stat, base + NGD_INT_CLR); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return IRQ_HANDLED; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl, 78762306a36Sopenharmony_ci struct slim_msg_txn *txn) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(sctrl->dev); 79062306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(tx_sent); 79162306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 79262306a36Sopenharmony_ci int ret, timeout, i; 79362306a36Sopenharmony_ci u8 wbuf[SLIM_MSGQ_BUF_LEN]; 79462306a36Sopenharmony_ci u8 rbuf[SLIM_MSGQ_BUF_LEN]; 79562306a36Sopenharmony_ci u32 *pbuf; 79662306a36Sopenharmony_ci u8 *puc; 79762306a36Sopenharmony_ci u8 la = txn->la; 79862306a36Sopenharmony_ci bool usr_msg = false; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (txn->mt == SLIM_MSG_MT_CORE && 80162306a36Sopenharmony_ci (txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION && 80262306a36Sopenharmony_ci txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) 80362306a36Sopenharmony_ci return 0; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (txn->dt == SLIM_MSG_DEST_ENUMADDR) 80662306a36Sopenharmony_ci return -EPROTONOSUPPORT; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (txn->msg->num_bytes > SLIM_MSGQ_BUF_LEN || 80962306a36Sopenharmony_ci txn->rl > SLIM_MSGQ_BUF_LEN) { 81062306a36Sopenharmony_ci dev_err(ctrl->dev, "msg exceeds HW limit\n"); 81162306a36Sopenharmony_ci return -EINVAL; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci pbuf = qcom_slim_ngd_tx_msg_get(ctrl, txn->rl, &tx_sent); 81562306a36Sopenharmony_ci if (!pbuf) { 81662306a36Sopenharmony_ci dev_err(ctrl->dev, "Message buffer unavailable\n"); 81762306a36Sopenharmony_ci return -ENOMEM; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (txn->mt == SLIM_MSG_MT_CORE && 82162306a36Sopenharmony_ci (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE || 82262306a36Sopenharmony_ci txn->mc == SLIM_MSG_MC_CONNECT_SINK || 82362306a36Sopenharmony_ci txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) { 82462306a36Sopenharmony_ci txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER; 82562306a36Sopenharmony_ci switch (txn->mc) { 82662306a36Sopenharmony_ci case SLIM_MSG_MC_CONNECT_SOURCE: 82762306a36Sopenharmony_ci txn->mc = SLIM_USR_MC_CONNECT_SRC; 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci case SLIM_MSG_MC_CONNECT_SINK: 83062306a36Sopenharmony_ci txn->mc = SLIM_USR_MC_CONNECT_SINK; 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci case SLIM_MSG_MC_DISCONNECT_PORT: 83362306a36Sopenharmony_ci txn->mc = SLIM_USR_MC_DISCONNECT_PORT; 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci default: 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci usr_msg = true; 84062306a36Sopenharmony_ci i = 0; 84162306a36Sopenharmony_ci wbuf[i++] = txn->la; 84262306a36Sopenharmony_ci la = SLIM_LA_MGR; 84362306a36Sopenharmony_ci wbuf[i++] = txn->msg->wbuf[0]; 84462306a36Sopenharmony_ci if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT) 84562306a36Sopenharmony_ci wbuf[i++] = txn->msg->wbuf[1]; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci txn->comp = &done; 84862306a36Sopenharmony_ci ret = slim_alloc_txn_tid(sctrl, txn); 84962306a36Sopenharmony_ci if (ret) { 85062306a36Sopenharmony_ci dev_err(ctrl->dev, "Unable to allocate TID\n"); 85162306a36Sopenharmony_ci return ret; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci wbuf[i++] = txn->tid; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci txn->msg->num_bytes = i; 85762306a36Sopenharmony_ci txn->msg->wbuf = wbuf; 85862306a36Sopenharmony_ci txn->msg->rbuf = rbuf; 85962306a36Sopenharmony_ci txn->rl = txn->msg->num_bytes + 4; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* HW expects length field to be excluded */ 86362306a36Sopenharmony_ci txn->rl--; 86462306a36Sopenharmony_ci puc = (u8 *)pbuf; 86562306a36Sopenharmony_ci *pbuf = 0; 86662306a36Sopenharmony_ci if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) { 86762306a36Sopenharmony_ci *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0, 86862306a36Sopenharmony_ci la); 86962306a36Sopenharmony_ci puc += 3; 87062306a36Sopenharmony_ci } else { 87162306a36Sopenharmony_ci *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1, 87262306a36Sopenharmony_ci la); 87362306a36Sopenharmony_ci puc += 2; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (slim_tid_txn(txn->mt, txn->mc)) 87762306a36Sopenharmony_ci *(puc++) = txn->tid; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (slim_ec_txn(txn->mt, txn->mc)) { 88062306a36Sopenharmony_ci *(puc++) = (txn->ec & 0xFF); 88162306a36Sopenharmony_ci *(puc++) = (txn->ec >> 8) & 0xFF; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (txn->msg && txn->msg->wbuf) 88562306a36Sopenharmony_ci memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci mutex_lock(&ctrl->tx_lock); 88862306a36Sopenharmony_ci ret = qcom_slim_ngd_tx_msg_post(ctrl, pbuf, txn->rl); 88962306a36Sopenharmony_ci if (ret) { 89062306a36Sopenharmony_ci mutex_unlock(&ctrl->tx_lock); 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&tx_sent, HZ); 89562306a36Sopenharmony_ci if (!timeout) { 89662306a36Sopenharmony_ci dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, 89762306a36Sopenharmony_ci txn->mt); 89862306a36Sopenharmony_ci mutex_unlock(&ctrl->tx_lock); 89962306a36Sopenharmony_ci return -ETIMEDOUT; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (usr_msg) { 90362306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&done, HZ); 90462306a36Sopenharmony_ci if (!timeout) { 90562306a36Sopenharmony_ci dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", 90662306a36Sopenharmony_ci txn->mc, txn->mt); 90762306a36Sopenharmony_ci mutex_unlock(&ctrl->tx_lock); 90862306a36Sopenharmony_ci return -ETIMEDOUT; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci mutex_unlock(&ctrl->tx_lock); 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int qcom_slim_ngd_xfer_msg_sync(struct slim_controller *ctrl, 91762306a36Sopenharmony_ci struct slim_msg_txn *txn) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 92062306a36Sopenharmony_ci int ret, timeout; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ret = pm_runtime_get_sync(ctrl->dev); 92362306a36Sopenharmony_ci if (ret < 0) 92462306a36Sopenharmony_ci goto pm_put; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci txn->comp = &done; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci ret = qcom_slim_ngd_xfer_msg(ctrl, txn); 92962306a36Sopenharmony_ci if (ret) 93062306a36Sopenharmony_ci goto pm_put; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&done, HZ); 93362306a36Sopenharmony_ci if (!timeout) { 93462306a36Sopenharmony_ci dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, 93562306a36Sopenharmony_ci txn->mt); 93662306a36Sopenharmony_ci ret = -ETIMEDOUT; 93762306a36Sopenharmony_ci goto pm_put; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci return 0; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cipm_put: 94262306a36Sopenharmony_ci pm_runtime_put(ctrl->dev); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return ret; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic int qcom_slim_calc_coef(struct slim_stream_runtime *rt, int *exp) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct slim_controller *ctrl = rt->dev->ctrl; 95062306a36Sopenharmony_ci int coef; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (rt->ratem * ctrl->a_framer->superfreq < rt->rate) 95362306a36Sopenharmony_ci rt->ratem++; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci coef = rt->ratem; 95662306a36Sopenharmony_ci *exp = 0; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* 95962306a36Sopenharmony_ci * CRM = Cx(2^E) is the formula we are using. 96062306a36Sopenharmony_ci * Here C is the coffecient and E is the exponent. 96162306a36Sopenharmony_ci * CRM is the Channel Rate Multiplier. 96262306a36Sopenharmony_ci * Coefficeint should be either 1 or 3 and exponenet 96362306a36Sopenharmony_ci * should be an integer between 0 to 9, inclusive. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci while (1) { 96662306a36Sopenharmony_ci while ((coef & 0x1) != 0x1) { 96762306a36Sopenharmony_ci coef >>= 1; 96862306a36Sopenharmony_ci *exp = *exp + 1; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (coef <= 3) 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci coef++; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* 97862306a36Sopenharmony_ci * we rely on the coef value (1 or 3) to set a bit 97962306a36Sopenharmony_ci * in the slimbus message packet. This bit is 98062306a36Sopenharmony_ci * BIT(5) which is the segment rate coefficient. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci if (coef == 1) { 98362306a36Sopenharmony_ci if (*exp > 9) 98462306a36Sopenharmony_ci return -EIO; 98562306a36Sopenharmony_ci coef = 0; 98662306a36Sopenharmony_ci } else { 98762306a36Sopenharmony_ci if (*exp > 8) 98862306a36Sopenharmony_ci return -EIO; 98962306a36Sopenharmony_ci coef = 1; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci return coef; 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic int qcom_slim_ngd_enable_stream(struct slim_stream_runtime *rt) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci struct slim_device *sdev = rt->dev; 99862306a36Sopenharmony_ci struct slim_controller *ctrl = sdev->ctrl; 99962306a36Sopenharmony_ci struct slim_val_inf msg = {0}; 100062306a36Sopenharmony_ci u8 wbuf[SLIM_MSGQ_BUF_LEN]; 100162306a36Sopenharmony_ci u8 rbuf[SLIM_MSGQ_BUF_LEN]; 100262306a36Sopenharmony_ci struct slim_msg_txn txn = {0,}; 100362306a36Sopenharmony_ci int i, ret; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER; 100662306a36Sopenharmony_ci txn.dt = SLIM_MSG_DEST_LOGICALADDR; 100762306a36Sopenharmony_ci txn.la = SLIM_LA_MGR; 100862306a36Sopenharmony_ci txn.ec = 0; 100962306a36Sopenharmony_ci txn.msg = &msg; 101062306a36Sopenharmony_ci txn.msg->num_bytes = 0; 101162306a36Sopenharmony_ci txn.msg->wbuf = wbuf; 101262306a36Sopenharmony_ci txn.msg->rbuf = rbuf; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci for (i = 0; i < rt->num_ports; i++) { 101562306a36Sopenharmony_ci struct slim_port *port = &rt->ports[i]; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (txn.msg->num_bytes == 0) { 101862306a36Sopenharmony_ci int exp = 0, coef = 0; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = sdev->laddr; 102162306a36Sopenharmony_ci wbuf[txn.msg->num_bytes] = rt->bps >> 2 | 102262306a36Sopenharmony_ci (port->ch.aux_fmt << 6); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* calculate coef dynamically */ 102562306a36Sopenharmony_ci coef = qcom_slim_calc_coef(rt, &exp); 102662306a36Sopenharmony_ci if (coef < 0) { 102762306a36Sopenharmony_ci dev_err(&sdev->dev, 102862306a36Sopenharmony_ci "%s: error calculating coef %d\n", __func__, 102962306a36Sopenharmony_ci coef); 103062306a36Sopenharmony_ci return -EIO; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (coef) 103462306a36Sopenharmony_ci wbuf[txn.msg->num_bytes] |= BIT(5); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci txn.msg->num_bytes++; 103762306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = exp << 4 | rt->prot; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (rt->prot == SLIM_PROTO_ISO) 104062306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = 104162306a36Sopenharmony_ci port->ch.prrate | 104262306a36Sopenharmony_ci SLIM_CHANNEL_CONTENT_FL; 104362306a36Sopenharmony_ci else 104462306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = port->ch.prrate; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci ret = slim_alloc_txn_tid(ctrl, &txn); 104762306a36Sopenharmony_ci if (ret) { 104862306a36Sopenharmony_ci dev_err(&sdev->dev, "Fail to allocate TID\n"); 104962306a36Sopenharmony_ci return -ENXIO; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = txn.tid; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci wbuf[txn.msg->num_bytes++] = port->ch.id; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci txn.mc = SLIM_USR_MC_DEF_ACT_CHAN; 105762306a36Sopenharmony_ci txn.rl = txn.msg->num_bytes + 4; 105862306a36Sopenharmony_ci ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn); 105962306a36Sopenharmony_ci if (ret) { 106062306a36Sopenharmony_ci slim_free_txn_tid(ctrl, &txn); 106162306a36Sopenharmony_ci dev_err(&sdev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn.mc, 106262306a36Sopenharmony_ci txn.mt); 106362306a36Sopenharmony_ci return ret; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci txn.mc = SLIM_USR_MC_RECONFIG_NOW; 106762306a36Sopenharmony_ci txn.msg->num_bytes = 2; 106862306a36Sopenharmony_ci wbuf[1] = sdev->laddr; 106962306a36Sopenharmony_ci txn.rl = txn.msg->num_bytes + 4; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ret = slim_alloc_txn_tid(ctrl, &txn); 107262306a36Sopenharmony_ci if (ret) { 107362306a36Sopenharmony_ci dev_err(ctrl->dev, "Fail to allocate TID\n"); 107462306a36Sopenharmony_ci return ret; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci wbuf[0] = txn.tid; 107862306a36Sopenharmony_ci ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn); 107962306a36Sopenharmony_ci if (ret) { 108062306a36Sopenharmony_ci slim_free_txn_tid(ctrl, &txn); 108162306a36Sopenharmony_ci dev_err(&sdev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn.mc, 108262306a36Sopenharmony_ci txn.mt); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return ret; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic int qcom_slim_ngd_get_laddr(struct slim_controller *ctrl, 108962306a36Sopenharmony_ci struct slim_eaddr *ea, u8 *laddr) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct slim_val_inf msg = {0}; 109262306a36Sopenharmony_ci u8 failed_ea[6] = {0, 0, 0, 0, 0, 0}; 109362306a36Sopenharmony_ci struct slim_msg_txn txn; 109462306a36Sopenharmony_ci u8 wbuf[10] = {0}; 109562306a36Sopenharmony_ci u8 rbuf[10] = {0}; 109662306a36Sopenharmony_ci int ret; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER; 109962306a36Sopenharmony_ci txn.dt = SLIM_MSG_DEST_LOGICALADDR; 110062306a36Sopenharmony_ci txn.la = SLIM_LA_MGR; 110162306a36Sopenharmony_ci txn.ec = 0; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci txn.mc = SLIM_USR_MC_ADDR_QUERY; 110462306a36Sopenharmony_ci txn.rl = 11; 110562306a36Sopenharmony_ci txn.msg = &msg; 110662306a36Sopenharmony_ci txn.msg->num_bytes = 7; 110762306a36Sopenharmony_ci txn.msg->wbuf = wbuf; 110862306a36Sopenharmony_ci txn.msg->rbuf = rbuf; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci ret = slim_alloc_txn_tid(ctrl, &txn); 111162306a36Sopenharmony_ci if (ret < 0) 111262306a36Sopenharmony_ci return ret; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci wbuf[0] = (u8)txn.tid; 111562306a36Sopenharmony_ci memcpy(&wbuf[1], ea, sizeof(*ea)); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci ret = qcom_slim_ngd_xfer_msg_sync(ctrl, &txn); 111862306a36Sopenharmony_ci if (ret) { 111962306a36Sopenharmony_ci slim_free_txn_tid(ctrl, &txn); 112062306a36Sopenharmony_ci return ret; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (!memcmp(rbuf, failed_ea, 6)) 112462306a36Sopenharmony_ci return -ENXIO; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci *laddr = rbuf[6]; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci return ret; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic int qcom_slim_ngd_exit_dma(struct qcom_slim_ngd_ctrl *ctrl) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci if (ctrl->dma_rx_channel) { 113462306a36Sopenharmony_ci dmaengine_terminate_sync(ctrl->dma_rx_channel); 113562306a36Sopenharmony_ci dma_release_channel(ctrl->dma_rx_channel); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (ctrl->dma_tx_channel) { 113962306a36Sopenharmony_ci dmaengine_terminate_sync(ctrl->dma_tx_channel); 114062306a36Sopenharmony_ci dma_release_channel(ctrl->dma_tx_channel); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci ctrl->dma_tx_channel = ctrl->dma_rx_channel = NULL; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic void qcom_slim_ngd_setup(struct qcom_slim_ngd_ctrl *ctrl) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci u32 cfg = readl_relaxed(ctrl->ngd->base); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN || 115362306a36Sopenharmony_ci ctrl->state == QCOM_SLIM_NGD_CTRL_ASLEEP) 115462306a36Sopenharmony_ci qcom_slim_ngd_init_dma(ctrl); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* By default enable message queues */ 115762306a36Sopenharmony_ci cfg |= NGD_CFG_RX_MSGQ_EN; 115862306a36Sopenharmony_ci cfg |= NGD_CFG_TX_MSGQ_EN; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci /* Enable NGD if it's not already enabled*/ 116162306a36Sopenharmony_ci if (!(cfg & NGD_CFG_ENABLE)) 116262306a36Sopenharmony_ci cfg |= NGD_CFG_ENABLE; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci writel_relaxed(cfg, ctrl->ngd->base); 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic int qcom_slim_ngd_power_up(struct qcom_slim_ngd_ctrl *ctrl) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci enum qcom_slim_ngd_state cur_state = ctrl->state; 117062306a36Sopenharmony_ci struct qcom_slim_ngd *ngd = ctrl->ngd; 117162306a36Sopenharmony_ci u32 laddr, rx_msgq; 117262306a36Sopenharmony_ci int timeout, ret = 0; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) { 117562306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&ctrl->qmi.qmi_comp, HZ); 117662306a36Sopenharmony_ci if (!timeout) 117762306a36Sopenharmony_ci return -EREMOTEIO; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (ctrl->state == QCOM_SLIM_NGD_CTRL_ASLEEP || 118162306a36Sopenharmony_ci ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) { 118262306a36Sopenharmony_ci ret = qcom_slim_qmi_power_request(ctrl, true); 118362306a36Sopenharmony_ci if (ret) { 118462306a36Sopenharmony_ci dev_err(ctrl->dev, "SLIM QMI power request failed:%d\n", 118562306a36Sopenharmony_ci ret); 118662306a36Sopenharmony_ci return ret; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci ctrl->ver = readl_relaxed(ctrl->base); 119162306a36Sopenharmony_ci /* Version info in 16 MSbits */ 119262306a36Sopenharmony_ci ctrl->ver >>= 16; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci laddr = readl_relaxed(ngd->base + NGD_STATUS); 119562306a36Sopenharmony_ci if (laddr & NGD_LADDR) { 119662306a36Sopenharmony_ci /* 119762306a36Sopenharmony_ci * external MDM restart case where ADSP itself was active framer 119862306a36Sopenharmony_ci * For example, modem restarted when playback was active 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (cur_state == QCOM_SLIM_NGD_CTRL_AWAKE) { 120162306a36Sopenharmony_ci dev_info(ctrl->dev, "Subsys restart: ADSP active framer\n"); 120262306a36Sopenharmony_ci return 0; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci qcom_slim_ngd_setup(ctrl); 120562306a36Sopenharmony_ci return 0; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* 120962306a36Sopenharmony_ci * Reinitialize only when registers are not retained or when enumeration 121062306a36Sopenharmony_ci * is lost for ngd. 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_ci reinit_completion(&ctrl->reconf); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci writel_relaxed(DEF_NGD_INT_MASK, ngd->base + NGD_INT_EN); 121562306a36Sopenharmony_ci rx_msgq = readl_relaxed(ngd->base + NGD_RX_MSGQ_CFG); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci writel_relaxed(rx_msgq|SLIM_RX_MSGQ_TIMEOUT_VAL, 121862306a36Sopenharmony_ci ngd->base + NGD_RX_MSGQ_CFG); 121962306a36Sopenharmony_ci qcom_slim_ngd_setup(ctrl); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&ctrl->reconf, HZ); 122262306a36Sopenharmony_ci if (!timeout) { 122362306a36Sopenharmony_ci dev_err(ctrl->dev, "capability exchange timed-out\n"); 122462306a36Sopenharmony_ci return -ETIMEDOUT; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void qcom_slim_ngd_notify_slaves(struct qcom_slim_ngd_ctrl *ctrl) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct slim_device *sbdev; 123362306a36Sopenharmony_ci struct device_node *node; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci for_each_child_of_node(ctrl->ngd->pdev->dev.of_node, node) { 123662306a36Sopenharmony_ci sbdev = of_slim_get_device(&ctrl->ctrl, node); 123762306a36Sopenharmony_ci if (!sbdev) 123862306a36Sopenharmony_ci continue; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (slim_get_logical_addr(sbdev)) 124162306a36Sopenharmony_ci dev_err(ctrl->dev, "Failed to get logical address\n"); 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic void qcom_slim_ngd_master_worker(struct work_struct *work) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl; 124862306a36Sopenharmony_ci struct slim_msg_txn txn; 124962306a36Sopenharmony_ci struct slim_val_inf msg = {0}; 125062306a36Sopenharmony_ci int retries = 0; 125162306a36Sopenharmony_ci u8 wbuf[8]; 125262306a36Sopenharmony_ci int ret = 0; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci ctrl = container_of(work, struct qcom_slim_ngd_ctrl, m_work); 125562306a36Sopenharmony_ci txn.dt = SLIM_MSG_DEST_LOGICALADDR; 125662306a36Sopenharmony_ci txn.ec = 0; 125762306a36Sopenharmony_ci txn.mc = SLIM_USR_MC_REPORT_SATELLITE; 125862306a36Sopenharmony_ci txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER; 125962306a36Sopenharmony_ci txn.la = SLIM_LA_MGR; 126062306a36Sopenharmony_ci wbuf[0] = SAT_MAGIC_LSB; 126162306a36Sopenharmony_ci wbuf[1] = SAT_MAGIC_MSB; 126262306a36Sopenharmony_ci wbuf[2] = SAT_MSG_VER; 126362306a36Sopenharmony_ci wbuf[3] = SAT_MSG_PROT; 126462306a36Sopenharmony_ci txn.msg = &msg; 126562306a36Sopenharmony_ci txn.msg->wbuf = wbuf; 126662306a36Sopenharmony_ci txn.msg->num_bytes = 4; 126762306a36Sopenharmony_ci txn.rl = 8; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci dev_info(ctrl->dev, "SLIM SAT: Rcvd master capability\n"); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cicapability_retry: 127262306a36Sopenharmony_ci ret = qcom_slim_ngd_xfer_msg(&ctrl->ctrl, &txn); 127362306a36Sopenharmony_ci if (!ret) { 127462306a36Sopenharmony_ci if (ctrl->state >= QCOM_SLIM_NGD_CTRL_ASLEEP) 127562306a36Sopenharmony_ci complete(&ctrl->reconf); 127662306a36Sopenharmony_ci else 127762306a36Sopenharmony_ci dev_err(ctrl->dev, "unexpected state:%d\n", 127862306a36Sopenharmony_ci ctrl->state); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (ctrl->state == QCOM_SLIM_NGD_CTRL_DOWN) 128162306a36Sopenharmony_ci qcom_slim_ngd_notify_slaves(ctrl); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci } else if (ret == -EIO) { 128462306a36Sopenharmony_ci dev_err(ctrl->dev, "capability message NACKed, retrying\n"); 128562306a36Sopenharmony_ci if (retries < INIT_MX_RETRIES) { 128662306a36Sopenharmony_ci msleep(DEF_RETRY_MS); 128762306a36Sopenharmony_ci retries++; 128862306a36Sopenharmony_ci goto capability_retry; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci } else { 129162306a36Sopenharmony_ci dev_err(ctrl->dev, "SLIM: capability TX failed:%d\n", ret); 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int qcom_slim_ngd_update_device_status(struct device *dev, void *null) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci slim_report_absent(to_slim_device(dev)); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int qcom_slim_ngd_runtime_resume(struct device *dev) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev); 130562306a36Sopenharmony_ci int ret = 0; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (!ctrl->qmi.handle) 130862306a36Sopenharmony_ci return 0; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (ctrl->state >= QCOM_SLIM_NGD_CTRL_ASLEEP) 131162306a36Sopenharmony_ci ret = qcom_slim_ngd_power_up(ctrl); 131262306a36Sopenharmony_ci if (ret) { 131362306a36Sopenharmony_ci /* Did SSR cause this power up failure */ 131462306a36Sopenharmony_ci if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) 131562306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_ASLEEP; 131662306a36Sopenharmony_ci else 131762306a36Sopenharmony_ci dev_err(ctrl->dev, "HW wakeup attempt during SSR\n"); 131862306a36Sopenharmony_ci } else { 131962306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_AWAKE; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci return 0; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic int qcom_slim_ngd_enable(struct qcom_slim_ngd_ctrl *ctrl, bool enable) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci if (enable) { 132862306a36Sopenharmony_ci int ret = qcom_slim_qmi_init(ctrl, false); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if (ret) { 133162306a36Sopenharmony_ci dev_err(ctrl->dev, "qmi init fail, ret:%d, state:%d\n", 133262306a36Sopenharmony_ci ret, ctrl->state); 133362306a36Sopenharmony_ci return ret; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci /* controller state should be in sync with framework state */ 133662306a36Sopenharmony_ci complete(&ctrl->qmi.qmi_comp); 133762306a36Sopenharmony_ci if (!pm_runtime_enabled(ctrl->ctrl.dev) || 133862306a36Sopenharmony_ci !pm_runtime_suspended(ctrl->ctrl.dev)) 133962306a36Sopenharmony_ci qcom_slim_ngd_runtime_resume(ctrl->ctrl.dev); 134062306a36Sopenharmony_ci else 134162306a36Sopenharmony_ci pm_runtime_resume(ctrl->ctrl.dev); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci pm_runtime_mark_last_busy(ctrl->ctrl.dev); 134462306a36Sopenharmony_ci pm_runtime_put(ctrl->ctrl.dev); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci ret = slim_register_controller(&ctrl->ctrl); 134762306a36Sopenharmony_ci if (ret) { 134862306a36Sopenharmony_ci dev_err(ctrl->dev, "error adding slim controller\n"); 134962306a36Sopenharmony_ci return ret; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci dev_info(ctrl->dev, "SLIM controller Registered\n"); 135362306a36Sopenharmony_ci } else { 135462306a36Sopenharmony_ci qcom_slim_qmi_exit(ctrl); 135562306a36Sopenharmony_ci slim_unregister_controller(&ctrl->ctrl); 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return 0; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic int qcom_slim_ngd_qmi_new_server(struct qmi_handle *hdl, 136262306a36Sopenharmony_ci struct qmi_service *service) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct qcom_slim_ngd_qmi *qmi = 136562306a36Sopenharmony_ci container_of(hdl, struct qcom_slim_ngd_qmi, svc_event_hdl); 136662306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = 136762306a36Sopenharmony_ci container_of(qmi, struct qcom_slim_ngd_ctrl, qmi); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci qmi->svc_info.sq_family = AF_QIPCRTR; 137062306a36Sopenharmony_ci qmi->svc_info.sq_node = service->node; 137162306a36Sopenharmony_ci qmi->svc_info.sq_port = service->port; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci complete(&ctrl->qmi_up); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci return 0; 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic void qcom_slim_ngd_qmi_del_server(struct qmi_handle *hdl, 137962306a36Sopenharmony_ci struct qmi_service *service) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct qcom_slim_ngd_qmi *qmi = 138262306a36Sopenharmony_ci container_of(hdl, struct qcom_slim_ngd_qmi, svc_event_hdl); 138362306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = 138462306a36Sopenharmony_ci container_of(qmi, struct qcom_slim_ngd_ctrl, qmi); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci reinit_completion(&ctrl->qmi_up); 138762306a36Sopenharmony_ci qmi->svc_info.sq_node = 0; 138862306a36Sopenharmony_ci qmi->svc_info.sq_port = 0; 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_cistatic const struct qmi_ops qcom_slim_ngd_qmi_svc_event_ops = { 139262306a36Sopenharmony_ci .new_server = qcom_slim_ngd_qmi_new_server, 139362306a36Sopenharmony_ci .del_server = qcom_slim_ngd_qmi_del_server, 139462306a36Sopenharmony_ci}; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic int qcom_slim_ngd_qmi_svc_event_init(struct qcom_slim_ngd_ctrl *ctrl) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct qcom_slim_ngd_qmi *qmi = &ctrl->qmi; 139962306a36Sopenharmony_ci int ret; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci ret = qmi_handle_init(&qmi->svc_event_hdl, 0, 140262306a36Sopenharmony_ci &qcom_slim_ngd_qmi_svc_event_ops, NULL); 140362306a36Sopenharmony_ci if (ret < 0) { 140462306a36Sopenharmony_ci dev_err(ctrl->dev, "qmi_handle_init failed: %d\n", ret); 140562306a36Sopenharmony_ci return ret; 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci ret = qmi_add_lookup(&qmi->svc_event_hdl, SLIMBUS_QMI_SVC_ID, 140962306a36Sopenharmony_ci SLIMBUS_QMI_SVC_V1, SLIMBUS_QMI_INS_ID); 141062306a36Sopenharmony_ci if (ret < 0) { 141162306a36Sopenharmony_ci dev_err(ctrl->dev, "qmi_add_lookup failed: %d\n", ret); 141262306a36Sopenharmony_ci qmi_handle_release(&qmi->svc_event_hdl); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci return ret; 141562306a36Sopenharmony_ci} 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_cistatic void qcom_slim_ngd_qmi_svc_event_deinit(struct qcom_slim_ngd_qmi *qmi) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci qmi_handle_release(&qmi->svc_event_hdl); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_driver; 142362306a36Sopenharmony_ci#define QCOM_SLIM_NGD_DRV_NAME "qcom,slim-ngd" 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic const struct of_device_id qcom_slim_ngd_dt_match[] = { 142662306a36Sopenharmony_ci { 142762306a36Sopenharmony_ci .compatible = "qcom,slim-ngd-v1.5.0", 142862306a36Sopenharmony_ci .data = &ngd_v1_5_offset_info, 142962306a36Sopenharmony_ci },{ 143062306a36Sopenharmony_ci .compatible = "qcom,slim-ngd-v2.1.0", 143162306a36Sopenharmony_ci .data = &ngd_v1_5_offset_info, 143262306a36Sopenharmony_ci }, 143362306a36Sopenharmony_ci {} 143462306a36Sopenharmony_ci}; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_slim_ngd_dt_match); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic void qcom_slim_ngd_down(struct qcom_slim_ngd_ctrl *ctrl) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci mutex_lock(&ctrl->ssr_lock); 144162306a36Sopenharmony_ci device_for_each_child(ctrl->ctrl.dev, NULL, 144262306a36Sopenharmony_ci qcom_slim_ngd_update_device_status); 144362306a36Sopenharmony_ci qcom_slim_ngd_enable(ctrl, false); 144462306a36Sopenharmony_ci mutex_unlock(&ctrl->ssr_lock); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic void qcom_slim_ngd_up_worker(struct work_struct *work) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci ctrl = container_of(work, struct qcom_slim_ngd_ctrl, ngd_up_work); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* Make sure qmi service is up before continuing */ 145462306a36Sopenharmony_ci wait_for_completion_interruptible(&ctrl->qmi_up); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci mutex_lock(&ctrl->ssr_lock); 145762306a36Sopenharmony_ci qcom_slim_ngd_enable(ctrl, true); 145862306a36Sopenharmony_ci mutex_unlock(&ctrl->ssr_lock); 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int qcom_slim_ngd_ssr_pdr_notify(struct qcom_slim_ngd_ctrl *ctrl, 146262306a36Sopenharmony_ci unsigned long action) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci switch (action) { 146562306a36Sopenharmony_ci case QCOM_SSR_BEFORE_SHUTDOWN: 146662306a36Sopenharmony_ci case SERVREG_SERVICE_STATE_DOWN: 146762306a36Sopenharmony_ci /* Make sure the last dma xfer is finished */ 146862306a36Sopenharmony_ci mutex_lock(&ctrl->tx_lock); 146962306a36Sopenharmony_ci if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) { 147062306a36Sopenharmony_ci pm_runtime_get_noresume(ctrl->ctrl.dev); 147162306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN; 147262306a36Sopenharmony_ci qcom_slim_ngd_down(ctrl); 147362306a36Sopenharmony_ci qcom_slim_ngd_exit_dma(ctrl); 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci mutex_unlock(&ctrl->tx_lock); 147662306a36Sopenharmony_ci break; 147762306a36Sopenharmony_ci case QCOM_SSR_AFTER_POWERUP: 147862306a36Sopenharmony_ci case SERVREG_SERVICE_STATE_UP: 147962306a36Sopenharmony_ci schedule_work(&ctrl->ngd_up_work); 148062306a36Sopenharmony_ci break; 148162306a36Sopenharmony_ci default: 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci return NOTIFY_OK; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic int qcom_slim_ngd_ssr_notify(struct notifier_block *nb, 148962306a36Sopenharmony_ci unsigned long action, 149062306a36Sopenharmony_ci void *data) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = container_of(nb, 149362306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl, nb); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci return qcom_slim_ngd_ssr_pdr_notify(ctrl, action); 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistatic void slim_pd_status(int state, char *svc_path, void *priv) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = (struct qcom_slim_ngd_ctrl *)priv; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci qcom_slim_ngd_ssr_pdr_notify(ctrl, state); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_cistatic int of_qcom_slim_ngd_register(struct device *parent, 150562306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci const struct ngd_reg_offset_data *data; 150862306a36Sopenharmony_ci struct qcom_slim_ngd *ngd; 150962306a36Sopenharmony_ci const struct of_device_id *match; 151062306a36Sopenharmony_ci struct device_node *node; 151162306a36Sopenharmony_ci u32 id; 151262306a36Sopenharmony_ci int ret; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci match = of_match_node(qcom_slim_ngd_dt_match, parent->of_node); 151562306a36Sopenharmony_ci data = match->data; 151662306a36Sopenharmony_ci for_each_available_child_of_node(parent->of_node, node) { 151762306a36Sopenharmony_ci if (of_property_read_u32(node, "reg", &id)) 151862306a36Sopenharmony_ci continue; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci ngd = kzalloc(sizeof(*ngd), GFP_KERNEL); 152162306a36Sopenharmony_ci if (!ngd) { 152262306a36Sopenharmony_ci of_node_put(node); 152362306a36Sopenharmony_ci return -ENOMEM; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci ngd->pdev = platform_device_alloc(QCOM_SLIM_NGD_DRV_NAME, id); 152762306a36Sopenharmony_ci if (!ngd->pdev) { 152862306a36Sopenharmony_ci kfree(ngd); 152962306a36Sopenharmony_ci of_node_put(node); 153062306a36Sopenharmony_ci return -ENOMEM; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci ngd->id = id; 153362306a36Sopenharmony_ci ngd->pdev->dev.parent = parent; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci ret = driver_set_override(&ngd->pdev->dev, 153662306a36Sopenharmony_ci &ngd->pdev->driver_override, 153762306a36Sopenharmony_ci QCOM_SLIM_NGD_DRV_NAME, 153862306a36Sopenharmony_ci strlen(QCOM_SLIM_NGD_DRV_NAME)); 153962306a36Sopenharmony_ci if (ret) { 154062306a36Sopenharmony_ci platform_device_put(ngd->pdev); 154162306a36Sopenharmony_ci kfree(ngd); 154262306a36Sopenharmony_ci of_node_put(node); 154362306a36Sopenharmony_ci return ret; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci ngd->pdev->dev.of_node = node; 154662306a36Sopenharmony_ci ctrl->ngd = ngd; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci ret = platform_device_add(ngd->pdev); 154962306a36Sopenharmony_ci if (ret) { 155062306a36Sopenharmony_ci platform_device_put(ngd->pdev); 155162306a36Sopenharmony_ci kfree(ngd); 155262306a36Sopenharmony_ci of_node_put(node); 155362306a36Sopenharmony_ci return ret; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci ngd->base = ctrl->base + ngd->id * data->offset + 155662306a36Sopenharmony_ci (ngd->id - 1) * data->size; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci return 0; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci return -ENODEV; 156262306a36Sopenharmony_ci} 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cistatic int qcom_slim_ngd_probe(struct platform_device *pdev) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 156762306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev->parent); 156862306a36Sopenharmony_ci int ret; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci ctrl->ctrl.dev = dev; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci platform_set_drvdata(pdev, ctrl); 157362306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 157462306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, QCOM_SLIM_NGD_AUTOSUSPEND); 157562306a36Sopenharmony_ci pm_runtime_set_suspended(dev); 157662306a36Sopenharmony_ci pm_runtime_enable(dev); 157762306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 157862306a36Sopenharmony_ci ret = qcom_slim_ngd_qmi_svc_event_init(ctrl); 157962306a36Sopenharmony_ci if (ret) { 158062306a36Sopenharmony_ci dev_err(&pdev->dev, "QMI service registration failed:%d", ret); 158162306a36Sopenharmony_ci return ret; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker); 158562306a36Sopenharmony_ci INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker); 158662306a36Sopenharmony_ci ctrl->mwq = create_singlethread_workqueue("ngd_master"); 158762306a36Sopenharmony_ci if (!ctrl->mwq) { 158862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to start master worker\n"); 158962306a36Sopenharmony_ci ret = -ENOMEM; 159062306a36Sopenharmony_ci goto wq_err; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ciwq_err: 159562306a36Sopenharmony_ci qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); 159662306a36Sopenharmony_ci if (ctrl->mwq) 159762306a36Sopenharmony_ci destroy_workqueue(ctrl->mwq); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return ret; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 160562306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl; 160662306a36Sopenharmony_ci int ret; 160762306a36Sopenharmony_ci struct pdr_service *pds; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); 161062306a36Sopenharmony_ci if (!ctrl) 161162306a36Sopenharmony_ci return -ENOMEM; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci dev_set_drvdata(dev, ctrl); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci ctrl->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 161662306a36Sopenharmony_ci if (IS_ERR(ctrl->base)) 161762306a36Sopenharmony_ci return PTR_ERR(ctrl->base); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 162062306a36Sopenharmony_ci if (ret < 0) 162162306a36Sopenharmony_ci return ret; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci ret = devm_request_irq(dev, ret, qcom_slim_ngd_interrupt, 162462306a36Sopenharmony_ci IRQF_TRIGGER_HIGH, "slim-ngd", ctrl); 162562306a36Sopenharmony_ci if (ret) 162662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "request IRQ failed\n"); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify; 162962306a36Sopenharmony_ci ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb); 163062306a36Sopenharmony_ci if (IS_ERR(ctrl->notifier)) 163162306a36Sopenharmony_ci return PTR_ERR(ctrl->notifier); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci ctrl->dev = dev; 163462306a36Sopenharmony_ci ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3; 163562306a36Sopenharmony_ci ctrl->framer.superfreq = 163662306a36Sopenharmony_ci ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci ctrl->ctrl.a_framer = &ctrl->framer; 163962306a36Sopenharmony_ci ctrl->ctrl.clkgear = SLIM_MAX_CLK_GEAR; 164062306a36Sopenharmony_ci ctrl->ctrl.get_laddr = qcom_slim_ngd_get_laddr; 164162306a36Sopenharmony_ci ctrl->ctrl.enable_stream = qcom_slim_ngd_enable_stream; 164262306a36Sopenharmony_ci ctrl->ctrl.xfer_msg = qcom_slim_ngd_xfer_msg; 164362306a36Sopenharmony_ci ctrl->ctrl.wakeup = NULL; 164462306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci mutex_init(&ctrl->tx_lock); 164762306a36Sopenharmony_ci mutex_init(&ctrl->ssr_lock); 164862306a36Sopenharmony_ci spin_lock_init(&ctrl->tx_buf_lock); 164962306a36Sopenharmony_ci init_completion(&ctrl->reconf); 165062306a36Sopenharmony_ci init_completion(&ctrl->qmi.qmi_comp); 165162306a36Sopenharmony_ci init_completion(&ctrl->qmi_up); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl); 165462306a36Sopenharmony_ci if (IS_ERR(ctrl->pdr)) { 165562306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr), 165662306a36Sopenharmony_ci "Failed to init PDR handle\n"); 165762306a36Sopenharmony_ci goto err_pdr_alloc; 165862306a36Sopenharmony_ci } 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd"); 166162306a36Sopenharmony_ci if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { 166262306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(pds), "pdr add lookup failed\n"); 166362306a36Sopenharmony_ci goto err_pdr_lookup; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci platform_driver_register(&qcom_slim_ngd_driver); 166762306a36Sopenharmony_ci return of_qcom_slim_ngd_register(dev, ctrl); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cierr_pdr_alloc: 167062306a36Sopenharmony_ci qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cierr_pdr_lookup: 167362306a36Sopenharmony_ci pdr_handle_release(ctrl->pdr); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci return ret; 167662306a36Sopenharmony_ci} 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_cistatic int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev) 167962306a36Sopenharmony_ci{ 168062306a36Sopenharmony_ci platform_driver_unregister(&qcom_slim_ngd_driver); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci return 0; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cistatic int qcom_slim_ngd_remove(struct platform_device *pdev) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 169062306a36Sopenharmony_ci pdr_handle_release(ctrl->pdr); 169162306a36Sopenharmony_ci qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); 169262306a36Sopenharmony_ci qcom_slim_ngd_enable(ctrl, false); 169362306a36Sopenharmony_ci qcom_slim_ngd_exit_dma(ctrl); 169462306a36Sopenharmony_ci qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); 169562306a36Sopenharmony_ci if (ctrl->mwq) 169662306a36Sopenharmony_ci destroy_workqueue(ctrl->mwq); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci kfree(ctrl->ngd); 169962306a36Sopenharmony_ci ctrl->ngd = NULL; 170062306a36Sopenharmony_ci return 0; 170162306a36Sopenharmony_ci} 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_cistatic int __maybe_unused qcom_slim_ngd_runtime_idle(struct device *dev) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (ctrl->state == QCOM_SLIM_NGD_CTRL_AWAKE) 170862306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_IDLE; 170962306a36Sopenharmony_ci pm_request_autosuspend(dev); 171062306a36Sopenharmony_ci return -EAGAIN; 171162306a36Sopenharmony_ci} 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic int __maybe_unused qcom_slim_ngd_runtime_suspend(struct device *dev) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev); 171662306a36Sopenharmony_ci int ret = 0; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci qcom_slim_ngd_exit_dma(ctrl); 171962306a36Sopenharmony_ci if (!ctrl->qmi.handle) 172062306a36Sopenharmony_ci return 0; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci ret = qcom_slim_qmi_power_request(ctrl, false); 172362306a36Sopenharmony_ci if (ret && ret != -EBUSY) 172462306a36Sopenharmony_ci dev_info(ctrl->dev, "slim resource not idle:%d\n", ret); 172562306a36Sopenharmony_ci if (!ret || ret == -ETIMEDOUT) 172662306a36Sopenharmony_ci ctrl->state = QCOM_SLIM_NGD_CTRL_ASLEEP; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci return ret; 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic const struct dev_pm_ops qcom_slim_ngd_dev_pm_ops = { 173262306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 173362306a36Sopenharmony_ci pm_runtime_force_resume) 173462306a36Sopenharmony_ci SET_RUNTIME_PM_OPS( 173562306a36Sopenharmony_ci qcom_slim_ngd_runtime_suspend, 173662306a36Sopenharmony_ci qcom_slim_ngd_runtime_resume, 173762306a36Sopenharmony_ci qcom_slim_ngd_runtime_idle 173862306a36Sopenharmony_ci ) 173962306a36Sopenharmony_ci}; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_ctrl_driver = { 174262306a36Sopenharmony_ci .probe = qcom_slim_ngd_ctrl_probe, 174362306a36Sopenharmony_ci .remove = qcom_slim_ngd_ctrl_remove, 174462306a36Sopenharmony_ci .driver = { 174562306a36Sopenharmony_ci .name = "qcom,slim-ngd-ctrl", 174662306a36Sopenharmony_ci .of_match_table = qcom_slim_ngd_dt_match, 174762306a36Sopenharmony_ci }, 174862306a36Sopenharmony_ci}; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic struct platform_driver qcom_slim_ngd_driver = { 175162306a36Sopenharmony_ci .probe = qcom_slim_ngd_probe, 175262306a36Sopenharmony_ci .remove = qcom_slim_ngd_remove, 175362306a36Sopenharmony_ci .driver = { 175462306a36Sopenharmony_ci .name = QCOM_SLIM_NGD_DRV_NAME, 175562306a36Sopenharmony_ci .pm = &qcom_slim_ngd_dev_pm_ops, 175662306a36Sopenharmony_ci }, 175762306a36Sopenharmony_ci}; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cimodule_platform_driver(qcom_slim_ngd_ctrl_driver); 176062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 176162306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SLIMBus NGD controller"); 1762