// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
// Copyright (c) 2018, Linaro Limited
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/slimbus.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/soc/qcom/qmi.h>
#include <net/sock.h>
#include "slimbus.h"
/* NGD (Non-ported Generic Device) registers */
#define NGD_CFG 0x0
#define NGD_CFG_ENABLE BIT(0)
#define NGD_CFG_RX_MSGQ_EN BIT(1)
#define NGD_CFG_TX_MSGQ_EN BIT(2)
#define NGD_STATUS 0x4
#define NGD_LADDR BIT(1)
#define NGD_RX_MSGQ_CFG 0x8
#define NGD_INT_EN 0x10
#define NGD_INT_RECFG_DONE BIT(24)
#define NGD_INT_TX_NACKED_2 BIT(25)
#define NGD_INT_MSG_BUF_CONTE BIT(26)
#define NGD_INT_MSG_TX_INVAL BIT(27)
#define NGD_INT_IE_VE_CHG BIT(28)
#define NGD_INT_DEV_ERR BIT(29)
#define NGD_INT_RX_MSG_RCVD BIT(30)
#define NGD_INT_TX_MSG_SENT BIT(31)
#define NGD_INT_STAT 0x14
#define NGD_INT_CLR 0x18
#define DEF_NGD_INT_MASK (NGD_INT_TX_NACKED_2 | NGD_INT_MSG_BUF_CONTE | \
NGD_INT_MSG_TX_INVAL | NGD_INT_IE_VE_CHG | \
NGD_INT_DEV_ERR | NGD_INT_TX_MSG_SENT | \
NGD_INT_RX_MSG_RCVD)
/* Slimbus QMI service */
#define SLIMBUS_QMI_SVC_ID 0x0301
#define SLIMBUS_QMI_SVC_V1 1
#define SLIMBUS_QMI_INS_ID 0
#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01 0x0020
#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01 0x0020
#define SLIMBUS_QMI_POWER_REQ_V01 0x0021
#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ 0x0022
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP 0x0022
#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN 7
/* QMI response timeout of 500ms */
#define SLIMBUS_QMI_RESP_TOUT 1000
/* User defined commands */
#define SLIM_USR_MC_GENERIC_ACK 0x25
#define SLIM_USR_MC_MASTER_CAPABILITY 0x0
#define SLIM_USR_MC_REPORT_SATELLITE 0x1
#define SLIM_USR_MC_ADDR_QUERY 0xD
#define SLIM_USR_MC_ADDR_REPLY 0xE
#define SLIM_USR_MC_DEFINE_CHAN 0x20
#define SLIM_USR_MC_DEF_ACT_CHAN 0x21
#define SLIM_USR_MC_CHAN_CTRL 0x23
#define SLIM_USR_MC_RECONFIG_NOW 0x24
#define SLIM_USR_MC_REQ_BW 0x28
#define SLIM_USR_MC_CONNECT_SRC 0x2C
#define SLIM_USR_MC_CONNECT_SINK 0x2D
#define SLIM_USR_MC_DISCONNECT_PORT 0x2E
#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0
#define QCOM_SLIM_NGD_AUTOSUSPEND MSEC_PER_SEC
#define SLIM_RX_MSGQ_TIMEOUT_VAL 0x10000
#define SLIM_LA_MGR 0xFF
#define SLIM_ROOT_FREQ 24576000
#define LADDR_RETRY 5
/* Per spec.max 40 bytes per received message */
#define SLIM_MSGQ_BUF_LEN 40
#define QCOM_SLIM_NGD_DESC_NUM 32
#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
#define INIT_MX_RETRIES 10
#define DEF_RETRY_MS 10
#define SAT_MAGIC_LSB 0xD9
#define SAT_MAGIC_MSB 0xC5
#define SAT_MSG_VER 0x1
#define SAT_MSG_PROT 0x1
#define to_ngd(d) container_of(d, struct qcom_slim_ngd, dev)
struct ngd_reg_offset_data {
u32 offset, size;
};
static const struct ngd_reg_offset_data ngd_v1_5_offset_info = {
.offset = 0x1000,
.size = 0x1000,
};
enum qcom_slim_ngd_state {
QCOM_SLIM_NGD_CTRL_AWAKE,
QCOM_SLIM_NGD_CTRL_IDLE,
QCOM_SLIM_NGD_CTRL_ASLEEP,
QCOM_SLIM_NGD_CTRL_DOWN,
};
struct qcom_slim_ngd_qmi {
struct qmi_handle qmi;
struct sockaddr_qrtr svc_info;
struct qmi_handle svc_event_hdl;
struct qmi_response_type_v01 resp;
struct qmi_handle *handle;
struct completion qmi_comp;
};
struct qcom_slim_ngd_ctrl;
struct qcom_slim_ngd;
struct qcom_slim_ngd_dma_desc {
struct dma_async_tx_descriptor *desc;
struct qcom_slim_ngd_ctrl *ctrl;
struct completion *comp;
dma_cookie_t cookie;
dma_addr_t phys;
void *base;
};
struct qcom_slim_ngd {
struct platform_device *pdev;
void __iomem *base;
int id;
};
struct qcom_slim_ngd_ctrl {
struct slim_framer framer;
struct slim_controller ctrl;
struct qcom_slim_ngd_qmi qmi;
struct qcom_slim_ngd *ngd;
struct device *dev;
void __iomem *base;
struct dma_chan *dma_rx_channel;
struct dma_chan *dma_tx_channel;
struct qcom_slim_ngd_dma_desc rx_desc[QCOM_SLIM_NGD_DESC_NUM];
struct qcom_slim_ngd_dma_desc txdesc[QCOM_SLIM_NGD_DESC_NUM];
struct completion reconf;
struct work_struct m_work;
struct workqueue_struct *mwq;
spinlock_t tx_buf_lock;
enum qcom_slim_ngd_state state;
dma_addr_t rx_phys_base;
dma_addr_t tx_phys_base;
void *rx_base;
void *tx_base;
int tx_tail;
int tx_head;
u32 ver;
};
enum slimbus_mode_enum_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
SLIMBUS_MODE_SATELLITE_V01 = 1,
SLIMBUS_MODE_MASTER_V01 = 2,
SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
enum slimbus_pm_enum_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
SLIMBUS_PM_INACTIVE_V01 = 1,
SLIMBUS_PM_ACTIVE_V01 = 2,
SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
enum slimbus_resp_enum_type_v01 {
SLIMBUS_RESP_ENUM_TYPE_MIN_VAL_V01 = INT_MIN,
SLIMBUS_RESP_SYNCHRONOUS_V01 = 1,
SLIMBUS_RESP_ENUM_TYPE_MAX_VAL_V01 = INT_MAX,
};
struct slimbus_select_inst_req_msg_v01 {
uint32_t instance;
uint8_t mode_valid;
enum slimbus_mode_enum_type_v01 mode;
};
struct slimbus_select_inst_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
struct slimbus_power_req_msg_v01 {
enum slimbus_pm_enum_type_v01 pm_req;
uint8_t resp_type_valid;
enum slimbus_resp_enum_type_v01 resp_type;
};
struct slimbus_power_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
static struct qmi_elem_info slimbus_select_inst_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(uint32_t),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct slimbus_select_inst_req_msg_v01,
instance),
.ei_array = NULL,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(uint8_t),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct slimbus_select_inst_req_msg_v01,
mode_valid),
.ei_array = NULL,
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(enum slimbus_mode_enum_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct slimbus_select_inst_req_msg_v01,
mode),
.ei_array = NULL,
},
{
.data_type = QMI_EOTI,
.elem_len = 0,
.elem_size = 0,
.array_type = NO_ARRAY,
.tlv_type = 0x00,
.offset = 0,
.ei_array = NULL,
},
};
static struct qmi_elem_info slimbus_select_inst_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct slimbus_select_inst_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.elem_len = 0,
.elem_size = 0,
.array_type = NO_ARRAY,
.tlv_type = 0x00,
.offset = 0,
.ei_array = NULL,
},
};
static struct qmi_elem_info slimbus_power_req_msg_v01_ei[] = {
{
.data_ty
|