Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
#
# MHI bus
#
# Copyright (c) 2021, Linaro Ltd.
#
source "drivers/bus/mhi/host/Kconfig"
source "drivers/bus/mhi/ep/Kconfig"
+5
View File
@@ -0,0 +1,5 @@
# Host MHI stack
obj-$(CONFIG_MHI_BUS) += host/
# Endpoint MHI stack
obj-$(CONFIG_MHI_BUS_EP) += ep/
+326
View File
@@ -0,0 +1,326 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022, Linaro Ltd.
*
*/
#ifndef _MHI_COMMON_H
#define _MHI_COMMON_H
#include <linux/bitfield.h>
#include <linux/mhi.h>
/* MHI registers */
#define MHIREGLEN 0x00
#define MHIVER 0x08
#define MHICFG 0x10
#define CHDBOFF 0x18
#define ERDBOFF 0x20
#define BHIOFF 0x28
#define BHIEOFF 0x2c
#define DEBUGOFF 0x30
#define MHICTRL 0x38
#define MHISTATUS 0x48
#define CCABAP_LOWER 0x58
#define CCABAP_HIGHER 0x5c
#define ECABAP_LOWER 0x60
#define ECABAP_HIGHER 0x64
#define CRCBAP_LOWER 0x68
#define CRCBAP_HIGHER 0x6c
#define CRDB_LOWER 0x70
#define CRDB_HIGHER 0x74
#define MHICTRLBASE_LOWER 0x80
#define MHICTRLBASE_HIGHER 0x84
#define MHICTRLLIMIT_LOWER 0x88
#define MHICTRLLIMIT_HIGHER 0x8c
#define MHIDATABASE_LOWER 0x98
#define MHIDATABASE_HIGHER 0x9c
#define MHIDATALIMIT_LOWER 0xa0
#define MHIDATALIMIT_HIGHER 0xa4
/* MHI BHI registers */
#define BHI_BHIVERSION_MINOR 0x00
#define BHI_BHIVERSION_MAJOR 0x04
#define BHI_IMGADDR_LOW 0x08
#define BHI_IMGADDR_HIGH 0x0c
#define BHI_IMGSIZE 0x10
#define BHI_RSVD1 0x14
#define BHI_IMGTXDB 0x18
#define BHI_RSVD2 0x1c
#define BHI_INTVEC 0x20
#define BHI_RSVD3 0x24
#define BHI_EXECENV 0x28
#define BHI_STATUS 0x2c
#define BHI_ERRCODE 0x30
#define BHI_ERRDBG1 0x34
#define BHI_ERRDBG2 0x38
#define BHI_ERRDBG3 0x3c
#define BHI_SERIALNU 0x40
#define BHI_SBLANTIROLLVER 0x44
#define BHI_NUMSEG 0x48
#define BHI_MSMHWID(n) (0x4c + (0x4 * (n)))
#define BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
#define BHI_RSVD5 0xc4
/* BHI register bits */
#define BHI_TXDB_SEQNUM_BMSK GENMASK(29, 0)
#define BHI_TXDB_SEQNUM_SHFT 0
#define BHI_STATUS_MASK GENMASK(31, 30)
#define BHI_STATUS_ERROR 0x03
#define BHI_STATUS_SUCCESS 0x02
#define BHI_STATUS_RESET 0x00
/* MHI BHIE registers */
#define BHIE_MSMSOCID_OFFS 0x00
#define BHIE_TXVECADDR_LOW_OFFS 0x2c
#define BHIE_TXVECADDR_HIGH_OFFS 0x30
#define BHIE_TXVECSIZE_OFFS 0x34
#define BHIE_TXVECDB_OFFS 0x3c
#define BHIE_TXVECSTATUS_OFFS 0x44
#define BHIE_RXVECADDR_LOW_OFFS 0x60
#define BHIE_RXVECADDR_HIGH_OFFS 0x64
#define BHIE_RXVECSIZE_OFFS 0x68
#define BHIE_RXVECDB_OFFS 0x70
#define BHIE_RXVECSTATUS_OFFS 0x78
/* BHIE register bits */
#define BHIE_TXVECDB_SEQNUM_BMSK GENMASK(29, 0)
#define BHIE_TXVECDB_SEQNUM_SHFT 0
#define BHIE_TXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0)
#define BHIE_TXVECSTATUS_SEQNUM_SHFT 0
#define BHIE_TXVECSTATUS_STATUS_BMSK GENMASK(31, 30)
#define BHIE_TXVECSTATUS_STATUS_SHFT 30
#define BHIE_TXVECSTATUS_STATUS_RESET 0x00
#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL 0x02
#define BHIE_TXVECSTATUS_STATUS_ERROR 0x03
#define BHIE_RXVECDB_SEQNUM_BMSK GENMASK(29, 0)
#define BHIE_RXVECDB_SEQNUM_SHFT 0
#define BHIE_RXVECSTATUS_SEQNUM_BMSK GENMASK(29, 0)
#define BHIE_RXVECSTATUS_SEQNUM_SHFT 0
#define BHIE_RXVECSTATUS_STATUS_BMSK GENMASK(31, 30)
#define BHIE_RXVECSTATUS_STATUS_SHFT 30
#define BHIE_RXVECSTATUS_STATUS_RESET 0x00
#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL 0x02
#define BHIE_RXVECSTATUS_STATUS_ERROR 0x03
/* MHI register bits */
#define MHICFG_NHWER_MASK GENMASK(31, 24)
#define MHICFG_NER_MASK GENMASK(23, 16)
#define MHICFG_NHWCH_MASK GENMASK(15, 8)
#define MHICFG_NCH_MASK GENMASK(7, 0)
#define MHICTRL_MHISTATE_MASK GENMASK(15, 8)
#define MHICTRL_RESET_MASK BIT(1)
#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8)
#define MHISTATUS_SYSERR_MASK BIT(2)
#define MHISTATUS_READY_MASK BIT(0)
/* Command Ring Element macros */
/* No operation command */
#define MHI_TRE_CMD_NOOP_PTR 0
#define MHI_TRE_CMD_NOOP_DWORD0 0
#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), MHI_CMD_NOP))
/* Channel reset command */
#define MHI_TRE_CMD_RESET_PTR 0
#define MHI_TRE_CMD_RESET_DWORD0 0
#define MHI_TRE_CMD_RESET_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
FIELD_PREP(GENMASK(23, 16), \
MHI_CMD_RESET_CHAN))
/* Channel stop command */
#define MHI_TRE_CMD_STOP_PTR 0
#define MHI_TRE_CMD_STOP_DWORD0 0
#define MHI_TRE_CMD_STOP_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
FIELD_PREP(GENMASK(23, 16), \
MHI_CMD_STOP_CHAN))
/* Channel start command */
#define MHI_TRE_CMD_START_PTR 0
#define MHI_TRE_CMD_START_DWORD0 0
#define MHI_TRE_CMD_START_DWORD1(chid) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
FIELD_PREP(GENMASK(23, 16), \
MHI_CMD_START_CHAN))
#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)])
#define MHI_TRE_GET_CMD_CHID(tre) FIELD_GET(GENMASK(31, 24), MHI_TRE_GET_DWORD(tre, 1))
#define MHI_TRE_GET_CMD_TYPE(tre) FIELD_GET(GENMASK(23, 16), MHI_TRE_GET_DWORD(tre, 1))
/* Event descriptor macros */
#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr)
#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), code) | \
FIELD_PREP(GENMASK(15, 0), len))
#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), chid) | \
FIELD_PREP(GENMASK(23, 16), type))
#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr)
#define MHI_TRE_GET_EV_CODE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
#define MHI_TRE_GET_EV_LEN(tre) FIELD_GET(GENMASK(15, 0), (MHI_TRE_GET_DWORD(tre, 0)))
#define MHI_TRE_GET_EV_CHID(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1)))
#define MHI_TRE_GET_EV_TYPE(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 1)))
#define MHI_TRE_GET_EV_STATE(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
#define MHI_TRE_GET_EV_EXECENV(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 0)))
#define MHI_TRE_GET_EV_SEQ(tre) MHI_TRE_GET_DWORD(tre, 0)
#define MHI_TRE_GET_EV_TIME(tre) MHI_TRE_GET_EV_PTR(tre)
#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits(MHI_TRE_GET_EV_PTR(tre))
#define MHI_TRE_GET_EV_VEID(tre) FIELD_GET(GENMASK(23, 16), (MHI_TRE_GET_DWORD(tre, 0)))
#define MHI_TRE_GET_EV_LINKSPEED(tre) FIELD_GET(GENMASK(31, 24), (MHI_TRE_GET_DWORD(tre, 1)))
#define MHI_TRE_GET_EV_LINKWIDTH(tre) FIELD_GET(GENMASK(7, 0), (MHI_TRE_GET_DWORD(tre, 0)))
/* State change event */
#define MHI_SC_EV_PTR 0
#define MHI_SC_EV_DWORD0(state) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), state))
#define MHI_SC_EV_DWORD1(type) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), type))
/* EE event */
#define MHI_EE_EV_PTR 0
#define MHI_EE_EV_DWORD0(ee) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), ee))
#define MHI_EE_EV_DWORD1(type) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), type))
/* Command Completion event */
#define MHI_CC_EV_PTR(ptr) cpu_to_le64(ptr)
#define MHI_CC_EV_DWORD0(code) cpu_to_le32(FIELD_PREP(GENMASK(31, 24), code))
#define MHI_CC_EV_DWORD1(type) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), type))
/* Transfer descriptor macros */
#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr)
#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(FIELD_PREP(GENMASK(15, 0), len))
#define MHI_TRE_TYPE_TRANSFER 2
#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \
MHI_TRE_TYPE_TRANSFER) | \
FIELD_PREP(BIT(10), bei) | \
FIELD_PREP(BIT(9), ieot) | \
FIELD_PREP(BIT(8), ieob) | \
FIELD_PREP(BIT(0), chain))
#define MHI_TRE_DATA_GET_PTR(tre) le64_to_cpu((tre)->ptr)
#define MHI_TRE_DATA_GET_LEN(tre) FIELD_GET(GENMASK(15, 0), MHI_TRE_GET_DWORD(tre, 0))
#define MHI_TRE_DATA_GET_CHAIN(tre) (!!(FIELD_GET(BIT(0), MHI_TRE_GET_DWORD(tre, 1))))
#define MHI_TRE_DATA_GET_IEOB(tre) (!!(FIELD_GET(BIT(8), MHI_TRE_GET_DWORD(tre, 1))))
#define MHI_TRE_DATA_GET_IEOT(tre) (!!(FIELD_GET(BIT(9), MHI_TRE_GET_DWORD(tre, 1))))
#define MHI_TRE_DATA_GET_BEI(tre) (!!(FIELD_GET(BIT(10), MHI_TRE_GET_DWORD(tre, 1))))
/* RSC transfer descriptor macros */
#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(FIELD_PREP(GENMASK(64, 48), len) | ptr)
#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie)
#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(FIELD_PREP(GENMASK(23, 16), \
MHI_PKT_TYPE_COALESCING))
enum mhi_pkt_type {
MHI_PKT_TYPE_INVALID = 0x0,
MHI_PKT_TYPE_NOOP_CMD = 0x1,
MHI_PKT_TYPE_TRANSFER = 0x2,
MHI_PKT_TYPE_COALESCING = 0x8,
MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10,
MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11,
MHI_PKT_TYPE_START_CHAN_CMD = 0x12,
MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20,
MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21,
MHI_PKT_TYPE_TX_EVENT = 0x22,
MHI_PKT_TYPE_RSC_TX_EVENT = 0x28,
MHI_PKT_TYPE_EE_EVENT = 0x40,
MHI_PKT_TYPE_TSYNC_EVENT = 0x48,
MHI_PKT_TYPE_BW_REQ_EVENT = 0x50,
MHI_PKT_TYPE_STALE_EVENT, /* internal event */
};
/* MHI transfer completion events */
enum mhi_ev_ccs {
MHI_EV_CC_INVALID = 0x0,
MHI_EV_CC_SUCCESS = 0x1,
MHI_EV_CC_EOT = 0x2, /* End of transfer event */
MHI_EV_CC_OVERFLOW = 0x3,
MHI_EV_CC_EOB = 0x4, /* End of block event */
MHI_EV_CC_OOB = 0x5, /* Out of block event */
MHI_EV_CC_DB_MODE = 0x6,
MHI_EV_CC_UNDEFINED_ERR = 0x10,
MHI_EV_CC_BAD_TRE = 0x11,
};
/* Channel state */
enum mhi_ch_state {
MHI_CH_STATE_DISABLED,
MHI_CH_STATE_ENABLED,
MHI_CH_STATE_RUNNING,
MHI_CH_STATE_SUSPENDED,
MHI_CH_STATE_STOP,
MHI_CH_STATE_ERROR,
};
enum mhi_cmd_type {
MHI_CMD_NOP = 1,
MHI_CMD_RESET_CHAN = 16,
MHI_CMD_STOP_CHAN = 17,
MHI_CMD_START_CHAN = 18,
};
#define EV_CTX_RESERVED_MASK GENMASK(7, 0)
#define EV_CTX_INTMODC_MASK GENMASK(15, 8)
#define EV_CTX_INTMODT_MASK GENMASK(31, 16)
struct mhi_event_ctxt {
__le32 intmod;
__le32 ertype;
__le32 msivec;
__le64 rbase __packed __aligned(4);
__le64 rlen __packed __aligned(4);
__le64 rp __packed __aligned(4);
__le64 wp __packed __aligned(4);
};
#define CHAN_CTX_CHSTATE_MASK GENMASK(7, 0)
#define CHAN_CTX_BRSTMODE_MASK GENMASK(9, 8)
#define CHAN_CTX_POLLCFG_MASK GENMASK(15, 10)
#define CHAN_CTX_RESERVED_MASK GENMASK(31, 16)
struct mhi_chan_ctxt {
__le32 chcfg;
__le32 chtype;
__le32 erindex;
__le64 rbase __packed __aligned(4);
__le64 rlen __packed __aligned(4);
__le64 rp __packed __aligned(4);
__le64 wp __packed __aligned(4);
};
struct mhi_cmd_ctxt {
__le32 reserved0;
__le32 reserved1;
__le32 reserved2;
__le64 rbase __packed __aligned(4);
__le64 rlen __packed __aligned(4);
__le64 rp __packed __aligned(4);
__le64 wp __packed __aligned(4);
};
struct mhi_ring_element {
__le64 ptr;
__le32 dword[2];
};
#define MHI_STATE_LIST \
mhi_state(RESET, "RESET") \
mhi_state(READY, "READY") \
mhi_state(M0, "M0") \
mhi_state(M1, "M1") \
mhi_state(M2, "M2") \
mhi_state(M3, "M3") \
mhi_state(M3_FAST, "M3_FAST") \
mhi_state(BHI, "BHI") \
mhi_state_end(SYS_ERR, "SYS ERROR")
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) case MHI_STATE_##a: return b;
#define mhi_state_end(a, b) case MHI_STATE_##a: return b;
static inline const char *mhi_state_str(enum mhi_state state)
{
switch (state) {
MHI_STATE_LIST
default:
return "Unknown state";
}
}
#endif /* _MHI_COMMON_H */
+10
View File
@@ -0,0 +1,10 @@
config MHI_BUS_EP
tristate "Modem Host Interface (MHI) bus Endpoint implementation"
help
Bus driver for MHI protocol. Modem Host Interface (MHI) is a
communication protocol used by a host processor to control
and communicate a modem device over a high speed peripheral
bus or shared memory.
MHI_BUS_EP implements the MHI protocol for the endpoint devices,
such as SDX55 modem connected to the host machine over PCIe.
+2
View File
@@ -0,0 +1,2 @@
obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
mhi_ep-y := main.o mmio.o ring.o sm.o
+222
View File
@@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022, Linaro Ltd.
*
*/
#ifndef _MHI_EP_INTERNAL_
#define _MHI_EP_INTERNAL_
#include <linux/bitfield.h>
#include "../common.h"
extern const struct bus_type mhi_ep_bus_type;
#define MHI_REG_OFFSET 0x100
#define BHI_REG_OFFSET 0x200
/* MHI registers */
#define EP_MHIREGLEN (MHI_REG_OFFSET + MHIREGLEN)
#define EP_MHIVER (MHI_REG_OFFSET + MHIVER)
#define EP_MHICFG (MHI_REG_OFFSET + MHICFG)
#define EP_CHDBOFF (MHI_REG_OFFSET + CHDBOFF)
#define EP_ERDBOFF (MHI_REG_OFFSET + ERDBOFF)
#define EP_BHIOFF (MHI_REG_OFFSET + BHIOFF)
#define EP_BHIEOFF (MHI_REG_OFFSET + BHIEOFF)
#define EP_DEBUGOFF (MHI_REG_OFFSET + DEBUGOFF)
#define EP_MHICTRL (MHI_REG_OFFSET + MHICTRL)
#define EP_MHISTATUS (MHI_REG_OFFSET + MHISTATUS)
#define EP_CCABAP_LOWER (MHI_REG_OFFSET + CCABAP_LOWER)
#define EP_CCABAP_HIGHER (MHI_REG_OFFSET + CCABAP_HIGHER)
#define EP_ECABAP_LOWER (MHI_REG_OFFSET + ECABAP_LOWER)
#define EP_ECABAP_HIGHER (MHI_REG_OFFSET + ECABAP_HIGHER)
#define EP_CRCBAP_LOWER (MHI_REG_OFFSET + CRCBAP_LOWER)
#define EP_CRCBAP_HIGHER (MHI_REG_OFFSET + CRCBAP_HIGHER)
#define EP_CRDB_LOWER (MHI_REG_OFFSET + CRDB_LOWER)
#define EP_CRDB_HIGHER (MHI_REG_OFFSET + CRDB_HIGHER)
#define EP_MHICTRLBASE_LOWER (MHI_REG_OFFSET + MHICTRLBASE_LOWER)
#define EP_MHICTRLBASE_HIGHER (MHI_REG_OFFSET + MHICTRLBASE_HIGHER)
#define EP_MHICTRLLIMIT_LOWER (MHI_REG_OFFSET + MHICTRLLIMIT_LOWER)
#define EP_MHICTRLLIMIT_HIGHER (MHI_REG_OFFSET + MHICTRLLIMIT_HIGHER)
#define EP_MHIDATABASE_LOWER (MHI_REG_OFFSET + MHIDATABASE_LOWER)
#define EP_MHIDATABASE_HIGHER (MHI_REG_OFFSET + MHIDATABASE_HIGHER)
#define EP_MHIDATALIMIT_LOWER (MHI_REG_OFFSET + MHIDATALIMIT_LOWER)
#define EP_MHIDATALIMIT_HIGHER (MHI_REG_OFFSET + MHIDATALIMIT_HIGHER)
/* MHI BHI registers */
#define EP_BHI_INTVEC (BHI_REG_OFFSET + BHI_INTVEC)
#define EP_BHI_EXECENV (BHI_REG_OFFSET + BHI_EXECENV)
/* MHI Doorbell registers */
#define CHDB_LOWER_n(n) (0x400 + 0x8 * (n))
#define CHDB_HIGHER_n(n) (0x404 + 0x8 * (n))
#define ERDB_LOWER_n(n) (0x800 + 0x8 * (n))
#define ERDB_HIGHER_n(n) (0x804 + 0x8 * (n))
#define MHI_CTRL_INT_STATUS 0x4
#define MHI_CTRL_INT_STATUS_MSK BIT(0)
#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
#define MHI_CHDB_INT_STATUS_n(n) (0x28 + 0x4 * (n))
#define MHI_ERDB_INT_STATUS_n(n) (0x38 + 0x4 * (n))
#define MHI_CTRL_INT_CLEAR 0x4c
#define MHI_CTRL_INT_MMIO_WR_CLEAR BIT(2)
#define MHI_CTRL_INT_CRDB_CLEAR BIT(1)
#define MHI_CTRL_INT_CRDB_MHICTRL_CLEAR BIT(0)
#define MHI_CHDB_INT_CLEAR_n(n) (0x70 + 0x4 * (n))
#define MHI_CHDB_INT_CLEAR_n_CLEAR_ALL GENMASK(31, 0)
#define MHI_ERDB_INT_CLEAR_n(n) (0x80 + 0x4 * (n))
#define MHI_ERDB_INT_CLEAR_n_CLEAR_ALL GENMASK(31, 0)
/*
* Unlike the usual "masking" convention, writing "1" to a bit in this register
* enables the interrupt and writing "0" will disable it..
*/
#define MHI_CTRL_INT_MASK 0x94
#define MHI_CTRL_INT_MASK_MASK GENMASK(1, 0)
#define MHI_CTRL_MHICTRL_MASK BIT(0)
#define MHI_CTRL_CRDB_MASK BIT(1)
#define MHI_CHDB_INT_MASK_n(n) (0xb8 + 0x4 * (n))
#define MHI_CHDB_INT_MASK_n_EN_ALL GENMASK(31, 0)
#define MHI_ERDB_INT_MASK_n(n) (0xc8 + 0x4 * (n))
#define MHI_ERDB_INT_MASK_n_EN_ALL GENMASK(31, 0)
#define NR_OF_CMD_RINGS 1
#define MHI_MASK_ROWS_CH_DB 4
#define MHI_MASK_ROWS_EV_DB 4
#define MHI_MASK_CH_LEN 32
#define MHI_MASK_EV_LEN 32
/* Generic context */
struct mhi_generic_ctx {
__le32 reserved0;
__le32 reserved1;
__le32 reserved2;
__le64 rbase __packed __aligned(4);
__le64 rlen __packed __aligned(4);
__le64 rp __packed __aligned(4);
__le64 wp __packed __aligned(4);
};
enum mhi_ep_ring_type {
RING_TYPE_CMD,
RING_TYPE_ER,
RING_TYPE_CH,
};
/* Ring element */
union mhi_ep_ring_ctx {
struct mhi_cmd_ctxt cmd;
struct mhi_event_ctxt ev;
struct mhi_chan_ctxt ch;
struct mhi_generic_ctx generic;
};
struct mhi_ep_ring_item {
struct list_head node;
struct mhi_ep_ring *ring;
};
struct mhi_ep_ring {
struct mhi_ep_cntrl *mhi_cntrl;
union mhi_ep_ring_ctx *ring_ctx;
struct mhi_ring_element *ring_cache;
enum mhi_ep_ring_type type;
struct delayed_work intmodt_work;
u64 rbase;
size_t rd_offset;
size_t wr_offset;
size_t ring_size;
u32 db_offset_h;
u32 db_offset_l;
u32 ch_id;
u32 er_index;
u32 irq_vector;
u32 intmodt;
bool started;
bool irq_pending;
};
struct mhi_ep_cmd {
struct mhi_ep_ring ring;
};
struct mhi_ep_event {
struct mhi_ep_ring ring;
};
struct mhi_ep_state_transition {
struct list_head node;
enum mhi_state state;
};
struct mhi_ep_chan {
char *name;
struct mhi_ep_device *mhi_dev;
struct mhi_ep_ring ring;
struct mutex lock;
void (*xfer_cb)(struct mhi_ep_device *mhi_dev, struct mhi_result *result);
enum mhi_ch_state state;
enum dma_data_direction dir;
size_t rd_offset;
u64 tre_loc;
u32 tre_size;
u32 tre_bytes_left;
u32 chan;
bool skip_td;
};
/* MHI Ring related functions */
void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 id);
void mhi_ep_ring_reset(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring);
int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
union mhi_ep_ring_ctx *ctx);
size_t mhi_ep_ring_addr2offset(struct mhi_ep_ring *ring, u64 ptr);
int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *element);
void mhi_ep_ring_inc_index(struct mhi_ep_ring *ring);
int mhi_ep_update_wr_offset(struct mhi_ep_ring *ring);
/* MMIO related functions */
u32 mhi_ep_mmio_read(struct mhi_ep_cntrl *mhi_cntrl, u32 offset);
void mhi_ep_mmio_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 val);
void mhi_ep_mmio_masked_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 mask, u32 val);
u32 mhi_ep_mmio_masked_read(struct mhi_ep_cntrl *dev, u32 offset, u32 mask);
void mhi_ep_mmio_enable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_disable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_enable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_disable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_enable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
void mhi_ep_mmio_disable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
void mhi_ep_mmio_enable_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl);
bool mhi_ep_mmio_read_chdb_status_interrupts(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_mask_interrupts(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_get_chc_base(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_get_erc_base(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_get_crc_base(struct mhi_ep_cntrl *mhi_cntrl);
u64 mhi_ep_mmio_get_db(struct mhi_ep_ring *ring);
void mhi_ep_mmio_set_env(struct mhi_ep_cntrl *mhi_cntrl, u32 value);
void mhi_ep_mmio_clear_reset(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_reset(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_get_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state *state,
bool *mhi_reset);
void mhi_ep_mmio_init(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_mmio_update_ner(struct mhi_ep_cntrl *mhi_cntrl);
/* MHI EP core functions */
int mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state);
int mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env);
bool mhi_ep_check_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state cur_mhi_state,
enum mhi_state mhi_state);
int mhi_ep_set_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state mhi_state);
int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl);
int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl);
int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_handle_syserr(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl);
void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl);
#endif
File diff suppressed because it is too large Load Diff
+273
View File
@@ -0,0 +1,273 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Linaro Ltd.
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/mhi_ep.h>
#include "internal.h"
u32 mhi_ep_mmio_read(struct mhi_ep_cntrl *mhi_cntrl, u32 offset)
{
return readl(mhi_cntrl->mmio + offset);
}
void mhi_ep_mmio_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 val)
{
writel(val, mhi_cntrl->mmio + offset);
}
void mhi_ep_mmio_masked_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 mask, u32 val)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, offset);
regval &= ~mask;
regval |= (val << __ffs(mask)) & mask;
mhi_ep_mmio_write(mhi_cntrl, offset, regval);
}
u32 mhi_ep_mmio_masked_read(struct mhi_ep_cntrl *dev, u32 offset, u32 mask)
{
u32 regval;
regval = mhi_ep_mmio_read(dev, offset);
regval &= mask;
regval >>= __ffs(mask);
return regval;
}
void mhi_ep_mmio_get_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state *state,
bool *mhi_reset)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICTRL);
*state = FIELD_GET(MHICTRL_MHISTATE_MASK, regval);
*mhi_reset = !!FIELD_GET(MHICTRL_RESET_MASK, regval);
}
static void mhi_ep_mmio_set_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id, bool enable)
{
u32 chid_mask, chid_shift, chdb_idx, val;
chid_shift = ch_id % 32;
chid_mask = BIT(chid_shift);
chdb_idx = ch_id / 32;
val = enable ? 1 : 0;
mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CHDB_INT_MASK_n(chdb_idx), chid_mask, val);
/* Update the local copy of the channel mask */
mhi_cntrl->chdb[chdb_idx].mask &= ~chid_mask;
mhi_cntrl->chdb[chdb_idx].mask |= val << chid_shift;
}
void mhi_ep_mmio_enable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
{
mhi_ep_mmio_set_chdb(mhi_cntrl, ch_id, true);
}
void mhi_ep_mmio_disable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
{
mhi_ep_mmio_set_chdb(mhi_cntrl, ch_id, false);
}
static void mhi_ep_mmio_set_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl, bool enable)
{
u32 val, i;
val = enable ? MHI_CHDB_INT_MASK_n_EN_ALL : 0;
for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_MASK_n(i), val);
mhi_cntrl->chdb[i].mask = val;
}
}
void mhi_ep_mmio_enable_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_set_chdb_interrupts(mhi_cntrl, true);
}
static void mhi_ep_mmio_mask_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_set_chdb_interrupts(mhi_cntrl, false);
}
bool mhi_ep_mmio_read_chdb_status_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
bool chdb = false;
u32 i;
for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
mhi_cntrl->chdb[i].status = mhi_ep_mmio_read(mhi_cntrl, MHI_CHDB_INT_STATUS_n(i));
if (mhi_cntrl->chdb[i].status)
chdb = true;
}
/* Return whether a channel doorbell interrupt occurred or not */
return chdb;
}
static void mhi_ep_mmio_set_erdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl, bool enable)
{
u32 val, i;
val = enable ? MHI_ERDB_INT_MASK_n_EN_ALL : 0;
for (i = 0; i < MHI_MASK_ROWS_EV_DB; i++)
mhi_ep_mmio_write(mhi_cntrl, MHI_ERDB_INT_MASK_n(i), val);
}
static void mhi_ep_mmio_mask_erdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_set_erdb_interrupts(mhi_cntrl, false);
}
void mhi_ep_mmio_enable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
MHI_CTRL_MHICTRL_MASK, 1);
}
void mhi_ep_mmio_disable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
MHI_CTRL_MHICTRL_MASK, 0);
}
void mhi_ep_mmio_enable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
MHI_CTRL_CRDB_MASK, 1);
}
void mhi_ep_mmio_disable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
MHI_CTRL_CRDB_MASK, 0);
}
void mhi_ep_mmio_mask_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_disable_ctrl_interrupt(mhi_cntrl);
mhi_ep_mmio_disable_cmdb_interrupt(mhi_cntrl);
mhi_ep_mmio_mask_chdb_interrupts(mhi_cntrl);
mhi_ep_mmio_mask_erdb_interrupts(mhi_cntrl);
}
static void mhi_ep_mmio_clear_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 i;
for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++)
mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_CLEAR_n(i),
MHI_CHDB_INT_CLEAR_n_CLEAR_ALL);
for (i = 0; i < MHI_MASK_ROWS_EV_DB; i++)
mhi_ep_mmio_write(mhi_cntrl, MHI_ERDB_INT_CLEAR_n(i),
MHI_ERDB_INT_CLEAR_n_CLEAR_ALL);
mhi_ep_mmio_write(mhi_cntrl, MHI_CTRL_INT_CLEAR,
MHI_CTRL_INT_MMIO_WR_CLEAR |
MHI_CTRL_INT_CRDB_CLEAR |
MHI_CTRL_INT_CRDB_MHICTRL_CLEAR);
}
void mhi_ep_mmio_get_chc_base(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_CCABAP_HIGHER);
mhi_cntrl->ch_ctx_host_pa = regval;
mhi_cntrl->ch_ctx_host_pa <<= 32;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_CCABAP_LOWER);
mhi_cntrl->ch_ctx_host_pa |= regval;
}
void mhi_ep_mmio_get_erc_base(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_ECABAP_HIGHER);
mhi_cntrl->ev_ctx_host_pa = regval;
mhi_cntrl->ev_ctx_host_pa <<= 32;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_ECABAP_LOWER);
mhi_cntrl->ev_ctx_host_pa |= regval;
}
void mhi_ep_mmio_get_crc_base(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_CRCBAP_HIGHER);
mhi_cntrl->cmd_ctx_host_pa = regval;
mhi_cntrl->cmd_ctx_host_pa <<= 32;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_CRCBAP_LOWER);
mhi_cntrl->cmd_ctx_host_pa |= regval;
}
u64 mhi_ep_mmio_get_db(struct mhi_ep_ring *ring)
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
u64 db_offset;
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, ring->db_offset_h);
db_offset = regval;
db_offset <<= 32;
regval = mhi_ep_mmio_read(mhi_cntrl, ring->db_offset_l);
db_offset |= regval;
return db_offset;
}
void mhi_ep_mmio_set_env(struct mhi_ep_cntrl *mhi_cntrl, u32 value)
{
mhi_ep_mmio_write(mhi_cntrl, EP_BHI_EXECENV, value);
}
void mhi_ep_mmio_clear_reset(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHICTRL, MHICTRL_RESET_MASK, 0);
}
void mhi_ep_mmio_reset(struct mhi_ep_cntrl *mhi_cntrl)
{
mhi_ep_mmio_write(mhi_cntrl, EP_MHICTRL, 0);
mhi_ep_mmio_write(mhi_cntrl, EP_MHISTATUS, 0);
mhi_ep_mmio_clear_interrupts(mhi_cntrl);
}
void mhi_ep_mmio_init(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 regval;
mhi_cntrl->chdb_offset = mhi_ep_mmio_read(mhi_cntrl, EP_CHDBOFF);
mhi_cntrl->erdb_offset = mhi_ep_mmio_read(mhi_cntrl, EP_ERDBOFF);
regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICFG);
mhi_cntrl->event_rings = FIELD_GET(MHICFG_NER_MASK, regval);
mhi_cntrl->hw_event_rings = FIELD_GET(MHICFG_NHWER_MASK, regval);
mhi_ep_mmio_reset(mhi_cntrl);
}
void mhi_ep_mmio_update_ner(struct mhi_ep_cntrl *mhi_cntrl)
{
u32 regval;
regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICFG);
mhi_cntrl->event_rings = FIELD_GET(MHICFG_NER_MASK, regval);
mhi_cntrl->hw_event_rings = FIELD_GET(MHICFG_NHWER_MASK, regval);
}
+234
View File
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Linaro Ltd.
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
*/
#include <linux/mhi_ep.h>
#include "internal.h"
size_t mhi_ep_ring_addr2offset(struct mhi_ep_ring *ring, u64 ptr)
{
return (ptr - ring->rbase) / sizeof(struct mhi_ring_element);
}
static u32 mhi_ep_ring_num_elems(struct mhi_ep_ring *ring)
{
__le64 rlen;
memcpy_fromio(&rlen, (void __iomem *) &ring->ring_ctx->generic.rlen, sizeof(u64));
return le64_to_cpu(rlen) / sizeof(struct mhi_ring_element);
}
void mhi_ep_ring_inc_index(struct mhi_ep_ring *ring)
{
ring->rd_offset = (ring->rd_offset + 1) % ring->ring_size;
}
static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end)
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_ep_buf_info buf_info = {};
size_t start;
int ret;
/* Don't proceed in the case of event ring. This happens during mhi_ep_ring_start(). */
if (ring->type == RING_TYPE_ER)
return 0;
/* No need to cache the ring if write pointer is unmodified */
if (ring->wr_offset == end)
return 0;
start = ring->wr_offset;
if (start < end) {
buf_info.size = (end - start) * sizeof(struct mhi_ring_element);
buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
buf_info.dev_addr = &ring->ring_cache[start];
ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret)
return ret;
} else {
buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element);
buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
buf_info.dev_addr = &ring->ring_cache[start];
ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret)
return ret;
if (end) {
buf_info.host_addr = ring->rbase;
buf_info.dev_addr = &ring->ring_cache[0];
buf_info.size = end * sizeof(struct mhi_ring_element);
ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
if (ret)
return ret;
}
}
dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, buf_info.size);
return 0;
}
static int mhi_ep_cache_ring(struct mhi_ep_ring *ring, u64 wr_ptr)
{
size_t wr_offset;
int ret;
wr_offset = mhi_ep_ring_addr2offset(ring, wr_ptr);
/* Cache the host ring till write offset */
ret = __mhi_ep_cache_ring(ring, wr_offset);
if (ret)
return ret;
ring->wr_offset = wr_offset;
return 0;
}
int mhi_ep_update_wr_offset(struct mhi_ep_ring *ring)
{
u64 wr_ptr;
wr_ptr = mhi_ep_mmio_get_db(ring);
return mhi_ep_cache_ring(ring, wr_ptr);
}
/* TODO: Support for adding multiple ring elements to the ring */
int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
{
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_ep_buf_info buf_info = {};
size_t old_offset = 0;
u32 num_free_elem;
__le64 rp;
int ret;
ret = mhi_ep_update_wr_offset(ring);
if (ret) {
dev_err(dev, "Error updating write pointer\n");
return ret;
}
if (ring->rd_offset < ring->wr_offset)
num_free_elem = (ring->wr_offset - ring->rd_offset) - 1;
else
num_free_elem = ((ring->ring_size - ring->rd_offset) + ring->wr_offset) - 1;
/* Check if there is space in ring for adding at least an element */
if (!num_free_elem) {
dev_err(dev, "No space left in the ring\n");
return -ENOSPC;
}
old_offset = ring->rd_offset;
dev_dbg(dev, "Adding an element to ring at offset (%zu)\n", ring->rd_offset);
buf_info.host_addr = ring->rbase + (old_offset * sizeof(*el));
buf_info.dev_addr = el;
buf_info.size = sizeof(*el);
ret = mhi_cntrl->write_sync(mhi_cntrl, &buf_info);
if (ret)
return ret;
mhi_ep_ring_inc_index(ring);
/* Update rp in ring context */
rp = cpu_to_le64(ring->rd_offset * sizeof(*el) + ring->rbase);
memcpy_toio((void __iomem *) &ring->ring_ctx->generic.rp, &rp, sizeof(u64));
return ret;
}
void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 id)
{
ring->type = type;
if (ring->type == RING_TYPE_CMD) {
ring->db_offset_h = EP_CRDB_HIGHER;
ring->db_offset_l = EP_CRDB_LOWER;
} else if (ring->type == RING_TYPE_CH) {
ring->db_offset_h = CHDB_HIGHER_n(id);
ring->db_offset_l = CHDB_LOWER_n(id);
ring->ch_id = id;
} else {
ring->db_offset_h = ERDB_HIGHER_n(id);
ring->db_offset_l = ERDB_LOWER_n(id);
}
}
static void mhi_ep_raise_irq(struct work_struct *work)
{
struct mhi_ep_ring *ring = container_of(work, struct mhi_ep_ring, intmodt_work.work);
struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
WRITE_ONCE(ring->irq_pending, false);
}
int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
union mhi_ep_ring_ctx *ctx)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
__le64 val;
int ret;
ring->mhi_cntrl = mhi_cntrl;
ring->ring_ctx = ctx;
ring->ring_size = mhi_ep_ring_num_elems(ring);
memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rbase, sizeof(u64));
ring->rbase = le64_to_cpu(val);
if (ring->type == RING_TYPE_CH)
ring->er_index = le32_to_cpu(ring->ring_ctx->ch.erindex);
if (ring->type == RING_TYPE_ER) {
ring->irq_vector = le32_to_cpu(ring->ring_ctx->ev.msivec);
ring->intmodt = FIELD_GET(EV_CTX_INTMODT_MASK,
le32_to_cpu(ring->ring_ctx->ev.intmod));
INIT_DELAYED_WORK(&ring->intmodt_work, mhi_ep_raise_irq);
}
/* During ring init, both rp and wp are equal */
memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rp, sizeof(u64));
ring->rd_offset = mhi_ep_ring_addr2offset(ring, le64_to_cpu(val));
ring->wr_offset = mhi_ep_ring_addr2offset(ring, le64_to_cpu(val));
/* Allocate ring cache memory for holding the copy of host ring */
ring->ring_cache = kzalloc_objs(struct mhi_ring_element,
ring->ring_size);
if (!ring->ring_cache)
return -ENOMEM;
memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.wp, sizeof(u64));
ret = mhi_ep_cache_ring(ring, le64_to_cpu(val));
if (ret) {
dev_err(dev, "Failed to cache ring\n");
kfree(ring->ring_cache);
return ret;
}
ring->started = true;
return 0;
}
void mhi_ep_ring_reset(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring)
{
if (ring->type == RING_TYPE_ER)
cancel_delayed_work_sync(&ring->intmodt_work);
ring->started = false;
kfree(ring->ring_cache);
ring->ring_cache = NULL;
}
+154
View File
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Linaro Ltd.
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
*/
#include <linux/errno.h>
#include <linux/mhi_ep.h>
#include "internal.h"
bool __must_check mhi_ep_check_mhi_state(struct mhi_ep_cntrl *mhi_cntrl,
enum mhi_state cur_mhi_state,
enum mhi_state mhi_state)
{
if (mhi_state == MHI_STATE_SYS_ERR)
return true; /* Allowed in any state */
if (mhi_state == MHI_STATE_READY)
return cur_mhi_state == MHI_STATE_RESET;
if (mhi_state == MHI_STATE_M0)
return cur_mhi_state == MHI_STATE_M3 || cur_mhi_state == MHI_STATE_READY;
if (mhi_state == MHI_STATE_M3)
return cur_mhi_state == MHI_STATE_M0;
return false;
}
int mhi_ep_set_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state mhi_state)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
if (!mhi_ep_check_mhi_state(mhi_cntrl, mhi_cntrl->mhi_state, mhi_state)) {
dev_err(dev, "MHI state change to %s from %s is not allowed!\n",
mhi_state_str(mhi_state),
mhi_state_str(mhi_cntrl->mhi_state));
return -EACCES;
}
/* TODO: Add support for M1 and M2 states */
if (mhi_state == MHI_STATE_M1 || mhi_state == MHI_STATE_M2) {
dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state));
return -EOPNOTSUPP;
}
mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK, mhi_state);
mhi_cntrl->mhi_state = mhi_state;
if (mhi_state == MHI_STATE_READY)
mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK, 1);
if (mhi_state == MHI_STATE_SYS_ERR)
mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_SYSERR_MASK, 1);
return 0;
}
int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_state old_state;
int ret;
/* If MHI is in M3, resume suspended channels */
mutex_lock(&mhi_cntrl->state_lock);
old_state = mhi_cntrl->mhi_state;
if (old_state == MHI_STATE_M3)
mhi_ep_resume_channels(mhi_cntrl);
ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
if (ret) {
mhi_ep_handle_syserr(mhi_cntrl);
goto err_unlock;
}
/* Signal host that the device moved to M0 */
ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0);
if (ret) {
dev_err(dev, "Failed sending M0 state change event\n");
goto err_unlock;
}
if (old_state == MHI_STATE_READY) {
/* Send AMSS EE event to host */
ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS);
if (ret) {
dev_err(dev, "Failed sending AMSS EE event\n");
goto err_unlock;
}
}
err_unlock:
mutex_unlock(&mhi_cntrl->state_lock);
return ret;
}
int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
int ret;
mutex_lock(&mhi_cntrl->state_lock);
ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
if (ret) {
mhi_ep_handle_syserr(mhi_cntrl);
goto err_unlock;
}
mhi_ep_suspend_channels(mhi_cntrl);
/* Signal host that the device moved to M3 */
ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3);
if (ret) {
dev_err(dev, "Failed sending M3 state change event\n");
goto err_unlock;
}
err_unlock:
mutex_unlock(&mhi_cntrl->state_lock);
return ret;
}
int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_state mhi_state;
int ret, is_ready;
mutex_lock(&mhi_cntrl->state_lock);
/* Ensure that the MHISTATUS is set to RESET by host */
mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK);
is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK);
if (mhi_state != MHI_STATE_RESET || is_ready) {
dev_err(dev, "READY state transition failed. MHI host not in RESET state\n");
ret = -EIO;
goto err_unlock;
}
ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
if (ret)
mhi_ep_handle_syserr(mhi_cntrl);
err_unlock:
mutex_unlock(&mhi_cntrl->state_lock);
return ret;
}
+31
View File
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0
#
# MHI bus
#
# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
#
config MHI_BUS
tristate "Modem Host Interface (MHI) bus"
help
Bus driver for MHI protocol. Modem Host Interface (MHI) is a
communication protocol used by the host processors to control
and communicate with modem devices over a high speed peripheral
bus or shared memory.
config MHI_BUS_DEBUG
bool "Debugfs support for the MHI bus"
depends on MHI_BUS && DEBUG_FS
help
Enable debugfs support for use with the MHI transport. Allows
reading and/or modifying some values within the MHI controller
for debug and test purposes.
config MHI_BUS_PCI_GENERIC
tristate "MHI PCI controller driver"
depends on MHI_BUS
depends on PCI
help
This driver provides MHI PCI controller driver for devices such as
Qualcomm SDX55 based PCIe modems.
@@ -0,0 +1,6 @@
obj-$(CONFIG_MHI_BUS) += mhi.o
mhi-y := init.o main.o pm.o boot.o
mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
mhi_pci_generic-y += pci_generic.o
+640
View File
@@ -0,0 +1,640 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include "internal.h"
/* Setup RDDM vector table for RDDM transfer and program RXVEC */
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info)
{
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 sequence_id;
unsigned int i;
int ret;
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(mhi_buf->len);
}
dev_dbg(dev, "BHIe programming for RDDM\n");
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);
ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
BHIE_RXVECDB_SEQNUM_BMSK, sequence_id);
if (ret) {
dev_err(dev, "Failed to write sequence ID for BHIE_RXVECDB\n");
return ret;
}
dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n",
&mhi_buf->dma_addr, mhi_buf->len, sequence_id);
return 0;
}
/* Collect RDDM buffer during kernel panic */
static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
{
int ret;
u32 rx_status;
enum mhi_ee_type ee;
const u32 delayus = 2000;
u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
const u32 rddm_timeout_us = 200000;
int rddm_retry = rddm_timeout_us / delayus;
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
/*
* This should only be executing during a kernel panic, we expect all
* other cores to shutdown while we're collecting RDDM buffer. After
* returning from this function, we expect the device to reset.
*
* Normally, we read/write pm_state only after grabbing the
* pm_lock, since we're in a panic, skipping it. Also there is no
* guarantee that this state change would take effect since
* we're setting it w/o grabbing pm_lock
*/
mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
/* update should take the effect immediately */
smp_wmb();
/*
* Make sure device is not already in RDDM. In case the device asserts
* and a kernel panic follows, device will already be in RDDM.
* Do not trigger SYS ERR again and proceed with waiting for
* image download completion.
*/
ee = mhi_get_exec_env(mhi_cntrl);
if (ee == MHI_EE_MAX)
goto error_exit_rddm;
if (ee != MHI_EE_RDDM) {
dev_dbg(dev, "Trigger device into RDDM mode using SYS ERR\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
dev_dbg(dev, "Waiting for device to enter RDDM\n");
while (rddm_retry--) {
ee = mhi_get_exec_env(mhi_cntrl);
if (ee == MHI_EE_RDDM)
break;
udelay(delayus);
}
if (rddm_retry <= 0) {
/* Hardware reset so force device to enter RDDM */
dev_dbg(dev,
"Did not enter RDDM, do a host req reset\n");
mhi_soc_reset(mhi_cntrl);
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
}
dev_dbg(dev,
"Waiting for RDDM image download via BHIe, current EE:%s\n",
TO_MHI_EXEC_STR(ee));
while (retry--) {
ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status);
if (ret)
return -EIO;
if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL)
return 0;
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status);
dev_err(dev, "RXVEC_STATUS: 0x%x\n", rx_status);
error_exit_rddm:
dev_err(dev, "RDDM transfer failed. Current EE: %s\n",
TO_MHI_EXEC_STR(ee));
return -EIO;
}
/* Download RDDM image from device */
int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
{
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 rx_status;
if (in_panic)
return __mhi_download_rddm_in_panic(mhi_cntrl);
dev_dbg(dev, "Waiting for RDDM image download via BHIe\n");
/* Wait for the image download to complete */
wait_event_timeout(mhi_cntrl->state_event,
mhi_read_reg_field(mhi_cntrl, base,
BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK,
&rx_status) || rx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
}
EXPORT_SYMBOL_GPL(mhi_download_rddm_image);
static void mhi_fw_load_error_dump(struct mhi_controller *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
int ret, i;
u32 val;
struct {
char *name;
u32 offset;
} error_reg[] = {
{ "ERROR_CODE", BHI_ERRCODE },
{ "ERROR_DBG1", BHI_ERRDBG1 },
{ "ERROR_DBG2", BHI_ERRDBG2 },
{ "ERROR_DBG3", BHI_ERRDBG3 },
{ NULL },
};
read_lock_bh(pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
for (i = 0; error_reg[i].name; i++) {
ret = mhi_read_reg(mhi_cntrl, base, error_reg[i].offset, &val);
if (ret)
break;
dev_err(dev, "Reg: %s value: 0x%x\n", error_reg[i].name, val);
}
}
read_unlock_bh(pm_lock);
}
static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
u32 tx_status, sequence_id;
int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
return -EIO;
}
sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK);
dev_dbg(dev, "Starting image download via BHIe. Sequence ID: %u\n",
sequence_id);
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
BHIE_TXVECDB_SEQNUM_BMSK, sequence_id);
read_unlock_bh(pm_lock);
if (ret)
return ret;
/* Wait for the image download to complete */
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base,
BHIE_TXVECSTATUS_OFFS,
BHIE_TXVECSTATUS_STATUS_BMSK,
&tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
tx_status != BHIE_TXVECSTATUS_STATUS_XFER_COMPL)
return -EIO;
return (!ret) ? -ETIMEDOUT : 0;
}
static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
u32 tx_status, session_id;
int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
goto invalid_pm_state;
}
session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK);
dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n",
session_id);
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, mhi_buf->len);
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
read_unlock_bh(pm_lock);
/* Wait for the image download to complete */
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
BHI_STATUS_MASK, &tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
goto invalid_pm_state;
if (tx_status == BHI_STATUS_ERROR) {
dev_err(dev, "Image transfer failed\n");
mhi_fw_load_error_dump(mhi_cntrl);
goto invalid_pm_state;
}
return (!ret) ? -ETIMEDOUT : 0;
invalid_pm_state:
return -EIO;
}
static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
struct mhi_buf *mhi_buf = image_info->mhi_buf;
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr);
kfree(image_info);
}
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
int i;
struct mhi_buf *mhi_buf = image_info->mhi_buf;
for (i = 0; i < image_info->entries; i++, mhi_buf++)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
kfree(image_info);
}
static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
struct image_info *img_info;
struct mhi_buf *mhi_buf;
img_info = kzalloc_flex(*img_info, mhi_buf, 1);
if (!img_info)
return -ENOMEM;
/* Allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
mhi_buf->len = alloc_size;
mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
&mhi_buf->dma_addr, GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
img_info->bhi_vec = NULL;
img_info->entries = 1;
*image_info = img_info;
return 0;
error_alloc_segment:
kfree(img_info);
return -ENOMEM;
}
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
size_t seg_size = mhi_cntrl->seg_len;
int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
int i;
struct image_info *img_info;
struct mhi_buf *mhi_buf;
img_info = kzalloc_flex(*img_info, mhi_buf, segments);
if (!img_info)
return -ENOMEM;
img_info->entries = segments;
/* Allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
for (i = 0; i < segments; i++, mhi_buf++) {
size_t vec_size = seg_size;
/* Vector table is the last entry */
if (i == segments - 1)
vec_size = sizeof(struct bhi_vec_entry) * i;
mhi_buf->len = vec_size;
mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
vec_size, &mhi_buf->dma_addr,
GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
}
img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
*image_info = img_info;
return 0;
error_alloc_segment:
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
kfree(img_info);
return -ENOMEM;
}
static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl,
const u8 *buf, size_t remainder,
struct image_info *img_info)
{
size_t to_cpy;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
while (remainder) {
to_cpy = min(remainder, mhi_buf->len);
memcpy(mhi_buf->buf, buf, to_cpy);
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(to_cpy);
buf += to_cpy;
remainder -= to_cpy;
bhi_vec++;
mhi_buf++;
}
}
static enum mhi_fw_load_type mhi_fw_load_type_get(const struct mhi_controller *mhi_cntrl)
{
if (mhi_cntrl->fbc_download) {
return MHI_FW_LOAD_FBC;
} else {
if (mhi_cntrl->seg_len)
return MHI_FW_LOAD_BHIE;
else
return MHI_FW_LOAD_BHI;
}
}
static int mhi_load_image_bhi(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhi_buffer(mhi_cntrl, &image, size);
if (ret)
return ret;
/* Load the firmware into BHI vec table */
memcpy(image->mhi_buf->buf, fw_data, size);
ret = mhi_fw_load_bhi(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhi_buffer(mhi_cntrl, image);
return ret;
}
static int mhi_load_image_bhie(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhie_table(mhi_cntrl, &image, size);
if (ret)
return ret;
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, size, image);
ret = mhi_fw_load_bhie(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhie_table(mhi_cntrl, image);
return ret;
}
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
const struct firmware *firmware = NULL;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_fw_load_type fw_load_type;
enum mhi_pm_state new_state;
const char *fw_name;
const u8 *fw_data;
size_t size, fw_sz;
int ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
return;
}
/* save hardware info from BHI */
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU,
&mhi_cntrl->serial_number);
if (ret)
dev_err(dev, "Could not capture serial number via BHI\n");
/* wait for ready on pass through or any other execution environment */
if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee))
goto fw_load_ready_state;
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
/* check if the driver has already provided the firmware data */
if (!fw_name && mhi_cntrl->fbc_download &&
mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
if (!mhi_cntrl->sbl_size) {
dev_err(dev, "fw_data provided but no sbl_size\n");
goto error_fw_load;
}
size = mhi_cntrl->sbl_size;
fw_data = mhi_cntrl->fw_data;
fw_sz = mhi_cntrl->fw_sz;
goto skip_req_fw;
}
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
!mhi_cntrl->seg_len))) {
dev_err(dev,
"No firmware image defined or !sbl_size || !seg_len\n");
goto error_fw_load;
}
ret = request_firmware(&firmware, fw_name, dev);
if (ret) {
dev_err(dev, "Error loading firmware: %d\n", ret);
goto error_fw_load;
}
size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
/* SBL size provided is maximum size, not necessarily the image size */
if (size > firmware->size)
size = firmware->size;
fw_data = firmware->data;
fw_sz = firmware->size;
skip_req_fw:
fw_load_type = mhi_fw_load_type_get(mhi_cntrl);
if (fw_load_type == MHI_FW_LOAD_BHIE)
ret = mhi_load_image_bhie(mhi_cntrl, fw_data, size);
else
ret = mhi_load_image_bhi(mhi_cntrl, fw_data, size);
/* Error or in EDL mode, we're done */
if (ret) {
dev_err(dev, "MHI did not load image over BHI%s, ret: %d\n",
fw_load_type == MHI_FW_LOAD_BHIE ? "e" : "",
ret);
release_firmware(firmware);
goto error_fw_load;
}
/* Wait for ready since EDL image was loaded */
if (fw_name && fw_name == mhi_cntrl->edl_image) {
release_firmware(firmware);
goto fw_load_ready_state;
}
write_lock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->dev_state = MHI_STATE_RESET;
write_unlock_irq(&mhi_cntrl->pm_lock);
/*
* If we're doing fbc, populate vector tables while
* device transitioning into MHI READY state
*/
if (fw_load_type == MHI_FW_LOAD_FBC) {
/*
* Some FW combine two separate ELF images (SBL + WLAN FW) in a single
* file. Hence, check for the existence of the second ELF header after
* SBL. If present, load the second image separately.
*/
if (!memcmp(fw_data + mhi_cntrl->sbl_size, ELFMAG, SELFMAG)) {
fw_data += mhi_cntrl->sbl_size;
fw_sz -= mhi_cntrl->sbl_size;
}
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
goto error_fw_load;
}
/* Load the firmware into BHIE vec table */
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
}
release_firmware(firmware);
fw_load_ready_state:
/* Transitioning into MHI RESET->READY state */
ret = mhi_ready_state_transition(mhi_cntrl);
if (ret) {
dev_err(dev, "MHI did not enter READY state\n");
goto error_ready_state;
}
dev_info(dev, "Wait for device to enter SBL or Mission mode\n");
return;
error_ready_state:
if (mhi_cntrl->fbc_image) {
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
mhi_cntrl->fbc_image = NULL;
}
error_fw_load:
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
{
struct image_info *image_info = mhi_cntrl->fbc_image;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_pm_state new_state;
int ret;
if (!image_info)
return -EIO;
ret = mhi_fw_load_bhie(mhi_cntrl,
/* Vector table is the last entry */
&image_info->mhi_buf[image_info->entries - 1]);
if (ret) {
dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
return ret;
}
+414
View File
@@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include <linux/string_choices.h>
#include "internal.h"
static int mhi_debugfs_states_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
/* states */
seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
str_true_false(mhi_cntrl->wake_set));
/* counters */
seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,
mhi_cntrl->M3);
seq_printf(m, " device wake: %u pending packets: %u\n",
atomic_read(&mhi_cntrl->dev_wake),
atomic_read(&mhi_cntrl->pending_pkts));
return 0;
}
static int mhi_debugfs_events_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_event *mhi_event;
struct mhi_event_ctxt *er_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;
mhi_event = mhi_cntrl->mhi_event;
for (i = 0; i < mhi_cntrl->total_ev_rings;
i++, er_ctxt++, mhi_event++) {
struct mhi_ring *ring = &mhi_event->ring;
if (mhi_event->offload_ev) {
seq_printf(m, "Index: %d is an offload event ring\n",
i);
continue;
}
seq_printf(m, "Index: %d intmod count: %lu time: %lu",
i, (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODC_MASK) >>
__ffs(EV_CTX_INTMODC_MASK),
(le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODT_MASK) >>
__ffs(EV_CTX_INTMODT_MASK));
seq_printf(m, " base: 0x%0llx len: 0x%llx", le64_to_cpu(er_ctxt->rbase),
le64_to_cpu(er_ctxt->rlen));
seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp),
le64_to_cpu(er_ctxt->wp));
seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp,
&mhi_event->db_cfg.db_val);
}
return 0;
}
static int mhi_debugfs_channels_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_chan *mhi_chan;
struct mhi_chan_ctxt *chan_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
mhi_chan = mhi_cntrl->mhi_chan;
chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt;
for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) {
struct mhi_ring *ring = &mhi_chan->tre_ring;
if (mhi_chan->offload_ch) {
seq_printf(m, "%s(%u) is an offload channel\n",
mhi_chan->name, mhi_chan->chan);
continue;
}
if (!mhi_chan->mhi_dev)
continue;
seq_printf(m,
"%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx",
mhi_chan->name, mhi_chan->chan, (le32_to_cpu(chan_ctxt->chcfg) &
CHAN_CTX_CHSTATE_MASK) >> __ffs(CHAN_CTX_CHSTATE_MASK),
(le32_to_cpu(chan_ctxt->chcfg) & CHAN_CTX_BRSTMODE_MASK) >>
__ffs(CHAN_CTX_BRSTMODE_MASK), (le32_to_cpu(chan_ctxt->chcfg) &
CHAN_CTX_POLLCFG_MASK) >> __ffs(CHAN_CTX_POLLCFG_MASK));
seq_printf(m, " type: 0x%x event ring: %u", le32_to_cpu(chan_ctxt->chtype),
le32_to_cpu(chan_ctxt->erindex));
seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx",
le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen),
le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp));
seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n",
ring->rp, ring->wp,
&mhi_chan->db_cfg.db_val);
}
return 0;
}
static int mhi_device_info_show(struct device *dev, void *data)
{
struct mhi_device *mhi_dev;
if (dev->bus != &mhi_bus_type)
return 0;
mhi_dev = to_mhi_device(dev);
seq_printf((struct seq_file *)data, "%s: type: %s dev_wake: %u",
mhi_dev->name, mhi_dev->dev_type ? "Controller" : "Transfer",
mhi_dev->dev_wake);
/* for transfer device types only */
if (mhi_dev->dev_type == MHI_DEVICE_XFER)
seq_printf((struct seq_file *)data, " channels: %u(UL)/%u(DL)",
mhi_dev->ul_chan_id, mhi_dev->dl_chan_id);
seq_puts((struct seq_file *)data, "\n");
return 0;
}
static int mhi_debugfs_devices_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
/* Show controller and client(s) info */
mhi_device_info_show(&mhi_cntrl->mhi_dev->dev, m);
device_for_each_child(&mhi_cntrl->mhi_dev->dev, m, mhi_device_info_show);
return 0;
}
static int mhi_debugfs_regdump_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
enum mhi_state state;
enum mhi_ee_type ee;
int i, ret = -EIO;
u32 val;
void __iomem *mhi_base = mhi_cntrl->regs;
void __iomem *bhi_base = mhi_cntrl->bhi;
void __iomem *bhie_base = mhi_cntrl->bhie;
void __iomem *wake_db = mhi_cntrl->wake_db;
struct {
const char *name;
int offset;
void __iomem *base;
} regs[] = {
{ "MHI_REGLEN", MHIREGLEN, mhi_base},
{ "MHI_VER", MHIVER, mhi_base},
{ "MHI_CFG", MHICFG, mhi_base},
{ "MHI_CTRL", MHICTRL, mhi_base},
{ "MHI_STATUS", MHISTATUS, mhi_base},
{ "MHI_WAKE_DB", 0, wake_db},
{ "BHI_EXECENV", BHI_EXECENV, bhi_base},
{ "BHI_STATUS", BHI_STATUS, bhi_base},
{ "BHI_ERRCODE", BHI_ERRCODE, bhi_base},
{ "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base},
{ "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base},
{ "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base},
{ "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base},
{ "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base},
{ "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base},
{ "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base},
{ NULL },
};
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
return ret;
seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee),
mhi_state_str(state));
for (i = 0; regs[i].name; i++) {
if (!regs[i].base)
continue;
ret = mhi_read_reg(mhi_cntrl, regs[i].base, regs[i].offset,
&val);
if (ret)
continue;
seq_printf(m, "%s: 0x%x\n", regs[i].name, val);
}
return 0;
}
static int mhi_debugfs_device_wake_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
seq_printf(m,
"Wake count: %d\n%s\n", mhi_dev->dev_wake,
"Usage: echo get/put > device_wake to vote/unvote for M0");
return 0;
}
static ssize_t mhi_debugfs_device_wake_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
char buf[16];
int ret = -EINVAL;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "get", 3)) {
ret = mhi_device_get_sync(mhi_dev);
} else if (!strncmp(buf, "put", 3)) {
mhi_device_put(mhi_dev);
ret = 0;
}
return ret ? ret : count;
}
static int mhi_debugfs_timeout_ms_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
seq_printf(m, "%u ms\n", mhi_cntrl->timeout_ms);
return 0;
}
static ssize_t mhi_debugfs_timeout_ms_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
u32 timeout_ms;
if (kstrtou32_from_user(ubuf, count, 0, &timeout_ms))
return -EINVAL;
mhi_cntrl->timeout_ms = timeout_ms;
return count;
}
static int mhi_debugfs_states_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_states_show, inode->i_private);
}
static int mhi_debugfs_events_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_events_show, inode->i_private);
}
static int mhi_debugfs_channels_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_channels_show, inode->i_private);
}
static int mhi_debugfs_devices_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_devices_show, inode->i_private);
}
static int mhi_debugfs_regdump_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_regdump_show, inode->i_private);
}
static int mhi_debugfs_device_wake_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_device_wake_show, inode->i_private);
}
static int mhi_debugfs_timeout_ms_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_timeout_ms_show, inode->i_private);
}
static const struct file_operations debugfs_states_fops = {
.open = mhi_debugfs_states_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_events_fops = {
.open = mhi_debugfs_events_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_channels_fops = {
.open = mhi_debugfs_channels_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_devices_fops = {
.open = mhi_debugfs_devices_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_regdump_fops = {
.open = mhi_debugfs_regdump_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_device_wake_fops = {
.open = mhi_debugfs_device_wake_open,
.write = mhi_debugfs_device_wake_write,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_timeout_ms_fops = {
.open = mhi_debugfs_timeout_ms_open,
.write = mhi_debugfs_timeout_ms_write,
.release = single_release,
.read = seq_read,
};
static struct dentry *mhi_debugfs_root;
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
mhi_cntrl->debugfs_dentry =
debugfs_create_dir(dev_name(&mhi_cntrl->mhi_dev->dev),
mhi_debugfs_root);
debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_states_fops);
debugfs_create_file("events", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_events_fops);
debugfs_create_file("channels", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_channels_fops);
debugfs_create_file("devices", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_devices_fops);
debugfs_create_file("regdump", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_regdump_fops);
debugfs_create_file("device_wake", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_device_wake_fops);
debugfs_create_file("timeout_ms", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_timeout_ms_fops);
}
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
debugfs_remove_recursive(mhi_cntrl->debugfs_dentry);
mhi_cntrl->debugfs_dentry = NULL;
}
void mhi_debugfs_init(void)
{
mhi_debugfs_root = debugfs_create_dir(mhi_bus_type.name, NULL);
}
void mhi_debugfs_exit(void)
{
debugfs_remove_recursive(mhi_debugfs_root);
}
File diff suppressed because it is too large Load Diff
+423
View File
@@ -0,0 +1,423 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
*/
#ifndef _MHI_INT_H
#define _MHI_INT_H
#include "../common.h"
extern const struct bus_type mhi_bus_type;
/* Host request register */
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
struct mhi_cmd_ctxt *cmd_ctxt;
dma_addr_t er_ctxt_addr;
dma_addr_t chan_ctxt_addr;
dma_addr_t cmd_ctxt_addr;
};
struct bhi_vec_entry {
__le64 dma_addr;
__le64 size;
};
enum mhi_fw_load_type {
MHI_FW_LOAD_BHI, /* BHI only in PBL */
MHI_FW_LOAD_BHIE, /* BHIe only in PBL */
MHI_FW_LOAD_FBC, /* BHI in PBL followed by BHIe in SBL */
MHI_FW_LOAD_MAX,
};
enum mhi_ch_state_type {
MHI_CH_STATE_TYPE_RESET,
MHI_CH_STATE_TYPE_STOP,
MHI_CH_STATE_TYPE_START,
MHI_CH_STATE_TYPE_MAX,
};
#define MHI_CH_STATE_TYPE_LIST \
ch_state_type(RESET, "RESET") \
ch_state_type(STOP, "STOP") \
ch_state_type_end(START, "START")
extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
"INVALID_STATE" : \
mhi_ch_state_type_str[(state)])
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
mode != MHI_DB_BRST_ENABLE)
#define MHI_EE_LIST \
mhi_ee(PBL, "PRIMARY BOOTLOADER") \
mhi_ee(SBL, "SECONDARY BOOTLOADER") \
mhi_ee(AMSS, "MISSION MODE") \
mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\
mhi_ee(WFW, "WLAN FIRMWARE") \
mhi_ee(PTHRU, "PASS THROUGH") \
mhi_ee(EDL, "EMERGENCY DOWNLOAD") \
mhi_ee(FP, "FLASH PROGRAMMER") \
mhi_ee(DISABLE_TRANSITION, "DISABLE") \
mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED")
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
"INVALID_EE" : mhi_ee_str[ee])
#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
ee == MHI_EE_EDL)
#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS)
#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL)
#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \
ee == MHI_EE_FP)
enum dev_st_transition {
DEV_ST_TRANSITION_PBL,
DEV_ST_TRANSITION_READY,
DEV_ST_TRANSITION_SBL,
DEV_ST_TRANSITION_MISSION_MODE,
DEV_ST_TRANSITION_FP,
DEV_ST_TRANSITION_SYS_ERR,
DEV_ST_TRANSITION_DISABLE,
DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
DEV_ST_TRANSITION_MAX,
};
#define DEV_ST_TRANSITION_LIST \
dev_st_trans(PBL, "PBL") \
dev_st_trans(READY, "READY") \
dev_st_trans(SBL, "SBL") \
dev_st_trans(MISSION_MODE, "MISSION MODE") \
dev_st_trans(FP, "FLASH PROGRAMMER") \
dev_st_trans(SYS_ERR, "SYS ERROR") \
dev_st_trans(DISABLE, "DISABLE") \
dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)")
extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
"INVALID_STATE" : dev_state_tran_str[state])
/* internal power states */
enum mhi_pm_state {
MHI_PM_STATE_DISABLE,
MHI_PM_STATE_POR,
MHI_PM_STATE_M0,
MHI_PM_STATE_M2,
MHI_PM_STATE_M3_ENTER,
MHI_PM_STATE_M3,
MHI_PM_STATE_M3_EXIT,
MHI_PM_STATE_FW_DL_ERR,
MHI_PM_STATE_SYS_ERR_DETECT,
MHI_PM_STATE_SYS_ERR_PROCESS,
MHI_PM_STATE_SYS_ERR_FAIL,
MHI_PM_STATE_SHUTDOWN_PROCESS,
MHI_PM_STATE_LD_ERR_FATAL_DETECT,
MHI_PM_STATE_MAX
};
#define MHI_PM_STATE_LIST \
mhi_pm_state(DISABLE, "DISABLE") \
mhi_pm_state(POR, "POWER ON RESET") \
mhi_pm_state(M0, "M0") \
mhi_pm_state(M2, "M2") \
mhi_pm_state(M3_ENTER, "M?->M3") \
mhi_pm_state(M3, "M3") \
mhi_pm_state(M3_EXIT, "M3->M0") \
mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \
mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \
mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \
mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \
mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \
mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect")
#define MHI_PM_DISABLE BIT(0)
#define MHI_PM_POR BIT(1)
#define MHI_PM_M0 BIT(2)
#define MHI_PM_M2 BIT(3)
#define MHI_PM_M3_ENTER BIT(4)
#define MHI_PM_M3 BIT(5)
#define MHI_PM_M3_EXIT BIT(6)
/* firmware download failure state */
#define MHI_PM_FW_DL_ERR BIT(7)
#define MHI_PM_SYS_ERR_DETECT BIT(8)
#define MHI_PM_SYS_ERR_PROCESS BIT(9)
#define MHI_PM_SYS_ERR_FAIL BIT(10)
#define MHI_PM_SHUTDOWN_PROCESS BIT(11)
/* link not accessible */
#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12)
#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \
MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access)
#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_EXIT))
#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2)
#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state)
#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
MHI_PM_IN_ERROR_STATE(pm_state))
#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
(MHI_PM_M3_ENTER | MHI_PM_M3))
#define MHI_PM_FATAL_ERROR(pm_state) ((pm_state == MHI_PM_FW_DL_ERR) || \
(pm_state >= MHI_PM_SYS_ERR_FAIL))
#define NR_OF_CMD_RINGS 1
#define CMD_EL_PER_RING 128
#define PRIMARY_CMD_RING 0
#define MHI_DEV_WAKE_DB 127
#define MHI_MAX_MTU 0xffff
#define MHI_RANDOM_U32_NONZERO(bmsk) (get_random_u32_inclusive(1, bmsk))
enum mhi_er_type {
MHI_ER_TYPE_INVALID = 0x0,
MHI_ER_TYPE_VALID = 0x1,
};
struct db_cfg {
bool reset_req;
bool db_mode;
u32 pollcfg;
enum mhi_db_brst_mode brstmode;
dma_addr_t db_val;
void (*process_db)(struct mhi_controller *mhi_cntrl,
struct db_cfg *db_cfg, void __iomem *io_addr,
dma_addr_t db_val);
};
struct mhi_pm_transitions {
enum mhi_pm_state from_state;
u32 to_states;
};
struct state_transition {
struct list_head node;
enum dev_st_transition state;
};
struct mhi_ring {
dma_addr_t dma_handle;
dma_addr_t iommu_base;
__le64 *ctxt_wp; /* point to ctxt wp */
void *pre_aligned;
void *base;
void *rp;
void *wp;
size_t el_size;
size_t len;
size_t elements;
size_t alloc_size;
void __iomem *db_addr;
};
struct mhi_cmd {
struct mhi_ring ring;
spinlock_t lock;
};
struct mhi_buf_info {
void *v_addr;
void *bb_addr;
void *wp;
void *cb_buf;
dma_addr_t p_addr;
size_t len;
enum dma_data_direction dir;
bool used; /* Indicates whether the buffer is used or not */
bool pre_mapped; /* Already pre-mapped by client */
};
struct mhi_event {
struct mhi_controller *mhi_cntrl;
struct mhi_chan *mhi_chan; /* dedicated to channel */
u32 er_index;
u32 intmod;
u32 irq;
int chan; /* this event ring is dedicated to a channel (optional) */
u32 priority;
enum mhi_er_data_type data_type;
struct mhi_ring ring;
struct db_cfg db_cfg;
struct tasklet_struct task;
spinlock_t lock;
int (*process_event)(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event,
u32 event_quota);
bool hw_ring;
bool cl_manage;
bool offload_ev; /* managed by a device driver */
};
struct mhi_chan {
const char *name;
/*
* Important: When consuming, increment tre_ring first and when
* releasing, decrement buf_ring first. If tre_ring has space, buf_ring
* is guaranteed to have space so we do not need to check both rings.
*/
struct mhi_ring buf_ring;
struct mhi_ring tre_ring;
u32 chan;
u32 er_index;
u32 intmod;
enum mhi_ch_type type;
enum dma_data_direction dir;
struct db_cfg db_cfg;
enum mhi_ch_ee_mask ee_mask;
enum mhi_ch_state ch_state;
enum mhi_ev_ccs ccs;
struct mhi_device *mhi_dev;
void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result);
struct mutex mutex;
struct completion completion;
rwlock_t lock;
struct list_head node;
bool lpm_notify;
bool configured;
bool offload_ch;
bool wake_capable;
};
/* Default MHI timeout */
#define MHI_TIMEOUT_MS (1000)
/* debugfs related functions */
#ifdef CONFIG_MHI_BUS_DEBUG
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_debugfs_init(void);
void mhi_debugfs_exit(void);
#else
static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_debugfs_init(void)
{
}
static inline void mhi_debugfs_exit(void)
{
}
#endif
struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
int mhi_destroy_device(struct device *dev, void *data);
void mhi_create_devices(struct mhi_controller *mhi_cntrl);
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info, size_t alloc_size);
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info);
/* Power management APIs */
enum mhi_pm_state __must_check mhi_tryset_pm_state(
struct mhi_controller *mhi_cntrl,
enum mhi_pm_state state);
const char *to_mhi_pm_state_str(u32 state);
int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
enum dev_st_transition state);
void mhi_pm_st_worker(struct work_struct *work);
void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl);
int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd);
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl);
static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
{
return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
mhi_cntrl->dev_state <= MHI_STATE_M3_FAST);
}
static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl)
{
pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
/* Register access methods */
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
void __iomem *db_addr, dma_addr_t db_val);
void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
struct db_cfg *db_mode, void __iomem *db_addr,
dma_addr_t db_val);
int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 *out);
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 *out);
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 val, u32 delayus, u32 timeout_ms);
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val);
int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 val);
void mhi_ring_er_db(struct mhi_event *mhi_event);
void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
dma_addr_t db_val);
void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd);
void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
/* Initialization methods */
int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
/* Event processing methods */
void mhi_ctrl_ev_task(unsigned long data);
void mhi_ev_task(unsigned long data);
int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
void mhi_uevent_notify(struct mhi_controller *mhi_cntrl, enum mhi_ee_type ee);
/* ISR handlers */
irqreturn_t mhi_irq_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_handler(int irq_number, void *dev);
int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_buf_info *info, enum mhi_flags flags);
int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
#endif /* _MHI_INT_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+283
View File
@@ -0,0 +1,283 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mhi_host
#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_EVENT_MHI_HOST_H
#include <linux/byteorder/generic.h>
#include <linux/tracepoint.h>
#include <linux/trace_seq.h>
#include "../common.h"
#include "internal.h"
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
MHI_STATE_LIST
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) { MHI_STATE_##a, b },
#define mhi_state_end(a, b) { MHI_STATE_##a, b }
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
MHI_PM_STATE_LIST
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b },
#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b }
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
MHI_EE_LIST
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) { MHI_EE_##a, b },
#define mhi_ee_end(a, b) { MHI_EE_##a, b }
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
MHI_CH_STATE_TYPE_LIST
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b },
#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b }
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
DEV_ST_TRANSITION_LIST
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b },
#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b }
#define TPS(x) tracepoint_string(x)
TRACE_EVENT(mhi_gen_tre,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_ring_element *mhi_tre),
TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(void *, wp)
__field(uint64_t, tre_ptr)
__field(uint32_t, dword0)
__field(uint32_t, dword1)
),
TP_fast_assign(
__assign_str(name);
__entry->ch_num = mhi_chan->chan;
__entry->wp = mhi_tre;
__entry->tre_ptr = le64_to_cpu(mhi_tre->ptr);
__entry->dword0 = le32_to_cpu(mhi_tre->dword[0]);
__entry->dword1 = le32_to_cpu(mhi_tre->dword[1]);
),
TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n",
__get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr,
__entry->dword0, __entry->dword1)
);
TRACE_EVENT(mhi_intvec_states,
TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state),
TP_ARGS(mhi_cntrl, dev_ee, dev_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, local_ee)
__field(int, state)
__field(int, dev_ee)
__field(int, dev_state)
),
TP_fast_assign(
__assign_str(name);
__entry->local_ee = mhi_cntrl->ee;
__entry->state = mhi_cntrl->dev_state;
__entry->dev_ee = dev_ee;
__entry->dev_state = dev_state;
),
TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n",
__get_str(name),
__print_symbolic(__entry->local_ee, MHI_EE_LIST),
__print_symbolic(__entry->state, MHI_STATE_LIST),
__print_symbolic(__entry->dev_ee, MHI_EE_LIST),
__print_symbolic(__entry->dev_state, MHI_STATE_LIST))
);
TRACE_EVENT(mhi_tryset_pm_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state),
TP_ARGS(mhi_cntrl, pm_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, pm_state)
),
TP_fast_assign(
__assign_str(name);
if (pm_state)
pm_state = __fls(pm_state);
__entry->pm_state = pm_state;
),
TP_printk("%s: PM state: %s\n", __get_str(name),
__print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST))
);
DECLARE_EVENT_CLASS(mhi_process_event_ring,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(uint32_t, dword0)
__field(uint32_t, dword1)
__field(int, state)
__field(uint64_t, ptr)
__field(void *, rp)
),
TP_fast_assign(
__assign_str(name);
__entry->rp = rp;
__entry->ptr = le64_to_cpu(rp->ptr);
__entry->dword0 = le32_to_cpu(rp->dword[0]);
__entry->dword1 = le32_to_cpu(rp->dword[1]);
__entry->state = MHI_TRE_GET_EV_STATE(rp);
),
TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n",
__get_str(name), __entry->rp, __entry->ptr, __entry->dword0,
__entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST))
);
DEFINE_EVENT(mhi_process_event_ring, mhi_data_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DECLARE_EVENT_CLASS(mhi_update_channel_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(int, state)
__field(const char *, reason)
),
TP_fast_assign(
__assign_str(name);
__entry->ch_num = mhi_chan->chan;
__entry->state = state;
__entry->reason = reason;
),
TP_printk("%s: chan%d: %s state to: %s\n",
__get_str(name), __entry->ch_num, __entry->reason,
__print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST))
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
TRACE_EVENT(mhi_pm_st_transition,
TP_PROTO(struct mhi_controller *mhi_cntrl, int state),
TP_ARGS(mhi_cntrl, state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, state)
),
TP_fast_assign(
__assign_str(name);
__entry->state = state;
),
TP_printk("%s: Handling state transition: %s\n", __get_str(name),
__print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST))
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>