2022-02-14 10:40:47

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 00/25] Add initial support for MHI endpoint stack

Hello,

This series adds initial support for the Qualcomm specific Modem Host Interface
(MHI) bus in endpoint devices like SDX55 modems. The MHI bus in endpoint devices
communicates with the MHI bus in host machines like x86 over any physical bus
like PCIe. The MHI host support is already in mainline [1] and been used by PCIe
based modems and WLAN devices running vendor code (downstream).

Overview
========

This series aims at adding the MHI support in the endpoint devices with the goal
of getting data connectivity using the mainline kernel running on the modems.
Modems here refer to the combination of an APPS processor (Cortex A grade) and
a baseband processor (DSP). The MHI bus is located in the APPS processor and it
transfers data packets from the baseband processor to the host machine.

The MHI Endpoint (MHI EP) stack proposed here is inspired by the downstream
code written by Qualcomm. But the complete stack is mostly re-written to adapt
to the "bus" framework and made it modular so that it can work with the upstream
subsystems like "PCI Endpoint". The code structure of the MHI endpoint stack
follows the MHI host stack to maintain uniformity.

With this initial MHI EP stack (along with few other drivers), we can establish
the network interface between host and endpoint over the MHI software channels
(IP_SW0) and can do things like IP forwarding, SSH, etc...

Stack Organization
==================

The MHI EP stack has the concept of controller and device drivers as like the
MHI host stack. The MHI EP controller driver can be a PCI Endpoint Function
driver and the MHI device driver can be a MHI EP Networking driver or QRTR
driver. The MHI EP controller driver is tied to the PCI Endpoint subsystem and
handles all bus related activities like mapping the host memory, raising IRQ,
passing link specific events etc... The MHI EP networking driver is tied to the
Networking stack and handles all networking related activities like
sending/receiving the SKBs from netdev, statistics collection etc...

This series only contains the MHI EP code, whereas the PCIe EPF driver and MHI
EP Networking drivers are not yet submitted and can be found here [2]. Though
the MHI EP stack doesn't have the build time dependency, it cannot function
without them.

Test setup
==========

This series has been tested on Telit FN980 TLB board powered by Qualcomm SDX55
(a.k.a X55 modem) and Qualcomm SM8450 based dev board.

For testing the stability and performance, networking tools such as iperf, ssh
and ping are used.

Limitations
===========

We are not _yet_ there to get the data packets from the modem as that involves
the Qualcomm IP Accelerator (IPA) integration with MHI endpoint stack. But we
are planning to add support for it in the coming days.

References
==========

MHI bus: https://www.kernel.org/doc/html/latest/mhi/mhi.html
Linaro connect presentation around this topic: https://connect.linaro.org/resources/lvc21f/lvc21f-222/

Thanks,
Mani

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/bus/mhi
[2] https://git.linaro.org/landing-teams/working/qualcomm/kernel.git/log/?h=tracking-qcomlt-sdx55-drivers

Changes in v3:

* Splitted the patch 20/23 into two.
* Fixed the error handling in patch 21/23.
* Removed spurious change in patch 01/23.
* Added check for xfer callbacks in client driver probe.

Changes in v2:

v2 mostly addresses the issues seen while testing the stack on SM8450 that is a
SMP platform and also incorporates the review comments from Alex.

Major changes are:

* Added a cleanup patch for getting rid of SHIFT macros and used the bitfield
operations.
* Added the endianess patches that were submitted to MHI list and used the
endianess conversion in EP patches also.
* Added support for multiple event rings.
* Fixed the MSI generation based on the event ring index.
* Fixed the doorbell list handling by making use of list splice and not locking
the entire list manipulation.
* Added new APIs for wrapping the reading and writing to host memory (Dmitry).
* Optimized the read_channel and queue_skb function logics.
* Added Hemant's R-o-b tag.

Manivannan Sadhasivam (23):
bus: mhi: Move host MHI code to "host" directory
bus: mhi: Move common MHI definitions out of host directory
bus: mhi: Make mhi_state_str[] array static inline and move to
common.h
bus: mhi: Cleanup the register definitions used in headers
bus: mhi: Get rid of SHIFT macros and use bitfield operations
bus: mhi: ep: Add support for registering MHI endpoint controllers
bus: mhi: ep: Add support for registering MHI endpoint client drivers
bus: mhi: ep: Add support for creating and destroying MHI EP devices
bus: mhi: ep: Add support for managing MMIO registers
bus: mhi: ep: Add support for ring management
bus: mhi: ep: Add support for sending events to the host
bus: mhi: ep: Add support for managing MHI state machine
bus: mhi: ep: Add support for processing MHI endpoint interrupts
bus: mhi: ep: Add support for powering up the MHI endpoint stack
bus: mhi: ep: Add support for powering down the MHI endpoint stack
bus: mhi: ep: Add support for handling MHI_RESET
bus: mhi: ep: Add support for handling SYS_ERR condition
bus: mhi: ep: Add support for processing command ring
bus: mhi: ep: Add support for reading from the host
bus: mhi: ep: Add support for processing transfer ring
bus: mhi: ep: Add support for queueing SKBs to the host
bus: mhi: ep: Add support for suspending and resuming channels
bus: mhi: ep: Add uevent support for module autoloading

Paul Davey (2):
bus: mhi: Fix pm_state conversion to string
bus: mhi: Fix MHI DMA structure endianness

drivers/bus/Makefile | 2 +-
drivers/bus/mhi/Kconfig | 28 +-
drivers/bus/mhi/Makefile | 9 +-
drivers/bus/mhi/common.h | 319 ++++
drivers/bus/mhi/ep/Kconfig | 10 +
drivers/bus/mhi/ep/Makefile | 2 +
drivers/bus/mhi/ep/internal.h | 254 ++++
drivers/bus/mhi/ep/main.c | 1601 +++++++++++++++++++++
drivers/bus/mhi/ep/mmio.c | 274 ++++
drivers/bus/mhi/ep/ring.c | 267 ++++
drivers/bus/mhi/ep/sm.c | 174 +++
drivers/bus/mhi/host/Kconfig | 31 +
drivers/bus/mhi/{core => host}/Makefile | 4 +-
drivers/bus/mhi/{core => host}/boot.c | 17 +-
drivers/bus/mhi/{core => host}/debugfs.c | 40 +-
drivers/bus/mhi/{core => host}/init.c | 123 +-
drivers/bus/mhi/{core => host}/internal.h | 427 +-----
drivers/bus/mhi/{core => host}/main.c | 46 +-
drivers/bus/mhi/{ => host}/pci_generic.c | 0
drivers/bus/mhi/{core => host}/pm.c | 36 +-
include/linux/mhi_ep.h | 293 ++++
include/linux/mod_devicetable.h | 2 +
scripts/mod/file2alias.c | 10 +
23 files changed, 3442 insertions(+), 527 deletions(-)
create mode 100644 drivers/bus/mhi/common.h
create mode 100644 drivers/bus/mhi/ep/Kconfig
create mode 100644 drivers/bus/mhi/ep/Makefile
create mode 100644 drivers/bus/mhi/ep/internal.h
create mode 100644 drivers/bus/mhi/ep/main.c
create mode 100644 drivers/bus/mhi/ep/mmio.c
create mode 100644 drivers/bus/mhi/ep/ring.c
create mode 100644 drivers/bus/mhi/ep/sm.c
create mode 100644 drivers/bus/mhi/host/Kconfig
rename drivers/bus/mhi/{core => host}/Makefile (54%)
rename drivers/bus/mhi/{core => host}/boot.c (96%)
rename drivers/bus/mhi/{core => host}/debugfs.c (90%)
rename drivers/bus/mhi/{core => host}/init.c (93%)
rename drivers/bus/mhi/{core => host}/internal.h (50%)
rename drivers/bus/mhi/{core => host}/main.c (98%)
rename drivers/bus/mhi/{ => host}/pci_generic.c (100%)
rename drivers/bus/mhi/{core => host}/pm.c (97%)
create mode 100644 include/linux/mhi_ep.h

--
2.25.1


2022-02-14 11:15:28

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 06/25] bus: mhi: Cleanup the register definitions used in headers

Cleanup includes:

1. Moving the MHI register definitions to common.h header with REG_ prefix
and using them in the host/internal.h file as an alias. This makes it
possible to reuse the register definitions in EP stack that differs by
a fixed offset.
2. Using the GENMASK macro for masks
3. Removing brackets for single values
4. Using lowercase for hex values
5. Using two digits for hex values where applicable

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/common.h | 243 ++++++++++++++++++++++++-----
drivers/bus/mhi/host/internal.h | 265 +++++++++-----------------------
2 files changed, 278 insertions(+), 230 deletions(-)

diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
index 288e47168649..f226f06d4ff9 100644
--- a/drivers/bus/mhi/common.h
+++ b/drivers/bus/mhi/common.h
@@ -9,62 +9,223 @@

#include <linux/mhi.h>

+/* MHI registers */
+#define REG_MHIREGLEN 0x00
+#define REG_MHIVER 0x08
+#define REG_MHICFG 0x10
+#define REG_CHDBOFF 0x18
+#define REG_ERDBOFF 0x20
+#define REG_BHIOFF 0x28
+#define REG_BHIEOFF 0x2c
+#define REG_DEBUGOFF 0x30
+#define REG_MHICTRL 0x38
+#define REG_MHISTATUS 0x48
+#define REG_CCABAP_LOWER 0x58
+#define REG_CCABAP_HIGHER 0x5c
+#define REG_ECABAP_LOWER 0x60
+#define REG_ECABAP_HIGHER 0x64
+#define REG_CRCBAP_LOWER 0x68
+#define REG_CRCBAP_HIGHER 0x6c
+#define REG_CRDB_LOWER 0x70
+#define REG_CRDB_HIGHER 0x74
+#define REG_MHICTRLBASE_LOWER 0x80
+#define REG_MHICTRLBASE_HIGHER 0x84
+#define REG_MHICTRLLIMIT_LOWER 0x88
+#define REG_MHICTRLLIMIT_HIGHER 0x8c
+#define REG_MHIDATABASE_LOWER 0x98
+#define REG_MHIDATABASE_HIGHER 0x9c
+#define REG_MHIDATALIMIT_LOWER 0xa0
+#define REG_MHIDATALIMIT_HIGHER 0xa4
+
+/* MHI BHI registers */
+#define REG_BHI_BHIVERSION_MINOR 0x00
+#define REG_BHI_BHIVERSION_MAJOR 0x04
+#define REG_BHI_IMGADDR_LOW 0x08
+#define REG_BHI_IMGADDR_HIGH 0x0c
+#define REG_BHI_IMGSIZE 0x10
+#define REG_BHI_RSVD1 0x14
+#define REG_BHI_IMGTXDB 0x18
+#define REG_BHI_RSVD2 0x1c
+#define REG_BHI_INTVEC 0x20
+#define REG_BHI_RSVD3 0x24
+#define REG_BHI_EXECENV 0x28
+#define REG_BHI_STATUS 0x2c
+#define REG_BHI_ERRCODE 0x30
+#define REG_BHI_ERRDBG1 0x34
+#define REG_BHI_ERRDBG2 0x38
+#define REG_BHI_ERRDBG3 0x3c
+#define REG_BHI_SERIALNU 0x40
+#define REG_BHI_SBLANTIROLLVER 0x44
+#define REG_BHI_NUMSEG 0x48
+#define REG_BHI_MSMHWID(n) (0x4c + (0x4 * (n)))
+#define REG_BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
+#define REG_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_SHIFT 30
+#define BHI_STATUS_ERROR 0x03
+#define BHI_STATUS_SUCCESS 0x02
+#define BHI_STATUS_RESET 0x00
+
+/* MHI BHIE registers */
+#define REG_BHIE_MSMSOCID_OFFS 0x00
+#define REG_BHIE_TXVECADDR_LOW_OFFS 0x2c
+#define REG_BHIE_TXVECADDR_HIGH_OFFS 0x30
+#define REG_BHIE_TXVECSIZE_OFFS 0x34
+#define REG_BHIE_TXVECDB_OFFS 0x3c
+#define REG_BHIE_TXVECSTATUS_OFFS 0x44
+#define REG_BHIE_RXVECADDR_LOW_OFFS 0x60
+#define REG_BHIE_RXVECADDR_HIGH_OFFS 0x64
+#define REG_BHIE_RXVECSIZE_OFFS 0x68
+#define REG_BHIE_RXVECDB_OFFS 0x70
+#define REG_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 MHIREGLEN_MHIREGLEN_MASK GENMASK(31, 0)
+#define MHIREGLEN_MHIREGLEN_SHIFT 0
+#define MHIVER_MHIVER_MASK GENMASK(31, 0)
+#define MHIVER_MHIVER_SHIFT 0
+#define MHICFG_NHWER_MASK GENMASK(31, 24)
+#define MHICFG_NHWER_SHIFT 24
+#define MHICFG_NER_MASK GENMASK(23, 16)
+#define MHICFG_NER_SHIFT 16
+#define MHICFG_NHWCH_MASK GENMASK(15, 8)
+#define MHICFG_NHWCH_SHIFT 8
+#define MHICFG_NCH_MASK GENMASK(7, 0)
+#define MHICFG_NCH_SHIFT 0
+#define CHDBOFF_CHDBOFF_MASK GENMASK(31, 0)
+#define CHDBOFF_CHDBOFF_SHIFT 0
+#define ERDBOFF_ERDBOFF_MASK GENMASK(31, 0)
+#define ERDBOFF_ERDBOFF_SHIFT 0
+#define BHIOFF_BHIOFF_MASK GENMASK(31, 0)
+#define BHIOFF_BHIOFF_SHIFT 0
+#define BHIEOFF_BHIEOFF_MASK GENMASK(31, 0)
+#define BHIEOFF_BHIEOFF_SHIFT 0
+#define DEBUGOFF_DEBUGOFF_MASK GENMASK(31, 0)
+#define DEBUGOFF_DEBUGOFF_SHIFT 0
+#define MHICTRL_MHISTATE_MASK GENMASK(15, 8)
+#define MHICTRL_MHISTATE_SHIFT 8
+#define MHICTRL_RESET_MASK BIT(1)
+#define MHICTRL_RESET_SHIFT 1
+#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8)
+#define MHISTATUS_MHISTATE_SHIFT 8
+#define MHISTATUS_SYSERR_MASK BIT(2)
+#define MHISTATUS_SYSERR_SHIFT 2
+#define MHISTATUS_READY_MASK BIT(0)
+#define MHISTATUS_READY_SHIFT 0
+#define CCABAP_LOWER_CCABAP_LOWER_MASK GENMASK(31, 0)
+#define CCABAP_LOWER_CCABAP_LOWER_SHIFT 0
+#define CCABAP_HIGHER_CCABAP_HIGHER_MASK GENMASK(31, 0)
+#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT 0
+#define ECABAP_LOWER_ECABAP_LOWER_MASK GENMASK(31, 0)
+#define ECABAP_LOWER_ECABAP_LOWER_SHIFT 0
+#define ECABAP_HIGHER_ECABAP_HIGHER_MASK GENMASK(31, 0)
+#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT 0
+#define CRCBAP_LOWER_CRCBAP_LOWER_MASK GENMASK(31, 0)
+#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT 0
+#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK GENMASK(31, 0)
+#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT 0
+#define CRDB_LOWER_CRDB_LOWER_MASK GENMASK(31, 0)
+#define CRDB_LOWER_CRDB_LOWER_SHIFT 0
+#define CRDB_HIGHER_CRDB_HIGHER_MASK GENMASK(31, 0)
+#define CRDB_HIGHER_CRDB_HIGHER_SHIFT 0
+#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK GENMASK(31, 0)
+#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT 0
+#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK GENMASK(31, 0)
+#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT 0
+#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK GENMASK(31, 0)
+#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT 0
+#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK GENMASK(31, 0)
+#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT 0
+#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK GENMASK(31, 0)
+#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT 0
+#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK GENMASK(31, 0)
+#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT 0
+#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK GENMASK(31, 0)
+#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT 0
+#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK GENMASK(31, 0)
+#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT 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(MHI_CMD_NOP << 16))
+#define MHI_TRE_CMD_NOOP_PTR 0
+#define MHI_TRE_CMD_NOOP_DWORD0 0
+#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(MHI_CMD_NOP << 16)

/* 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((chid << 24) | \
- (MHI_CMD_RESET_CHAN << 16)))
+#define MHI_TRE_CMD_RESET_PTR 0
+#define MHI_TRE_CMD_RESET_DWORD0 0
+#define MHI_TRE_CMD_RESET_DWORD1(chid) (cpu_to_le32((chid << 24) | \
+ (MHI_CMD_RESET_CHAN << 16)))

/* 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((chid << 24) | \
- (MHI_CMD_STOP_CHAN << 16)))
+#define MHI_TRE_CMD_STOP_PTR 0
+#define MHI_TRE_CMD_STOP_DWORD0 0
+#define MHI_TRE_CMD_STOP_DWORD1(chid) (cpu_to_le32((chid << 24) | \
+ (MHI_CMD_STOP_CHAN << 16)))

/* 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((chid << 24) | \
- (MHI_CMD_START_CHAN << 16)))
+#define MHI_TRE_CMD_START_PTR 0
+#define MHI_TRE_CMD_START_DWORD0 0
+#define MHI_TRE_CMD_START_DWORD1(chid) (cpu_to_le32((chid << 24) | \
+ (MHI_CMD_START_CHAN << 16)))

-#define MHI_TRE_GET_DWORD(tre, word) (le32_to_cpu((tre)->dword[(word)]))
-#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
-#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
+#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)])
+#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
+#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)

/* Event descriptor macros */
-#define MHI_TRE_EV_PTR(ptr) (cpu_to_le64(ptr))
-#define MHI_TRE_EV_DWORD0(code, len) (cpu_to_le32((code << 24) | len))
-#define MHI_TRE_EV_DWORD1(chid, type) (cpu_to_le32((chid << 24) | (type << 16)))
-#define MHI_TRE_GET_EV_PTR(tre) (le64_to_cpu((tre)->ptr))
-#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
-#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
-#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
-#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
-#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
-#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)
+/* Transfer completion event */
+#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr)
+#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32((code << 24) | len)
+#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32((chid << 24) | (type << 16))
+#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr)
+#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
+#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
+#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
+#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
+#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
+#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
+#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
+#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
+#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)

/* Transfer descriptor macros */
-#define MHI_TRE_DATA_PTR(ptr) (cpu_to_le64(ptr))
-#define MHI_TRE_DATA_DWORD0(len) (cpu_to_le32(len & MHI_MAX_MTU))
-#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
- | (ieot << 9) | (ieob << 8) | chain))
+#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr)
+#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(len & MHI_MAX_MTU)
+#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
+ | (ieot << 9) | (ieob << 8) | chain))

/* RSC transfer descriptor macros */
-#define MHI_RSCTRE_DATA_PTR(ptr, len) (cpu_to_le64(((u64)len << 48) | ptr))
-#define MHI_RSCTRE_DATA_DWORD0(cookie) (cpu_to_le32(cookie))
-#define MHI_RSCTRE_DATA_DWORD1 (cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16))
+#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(((u64)len << 48) | ptr)
+#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie)
+#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16)

enum mhi_pkt_type {
MHI_PKT_TYPE_INVALID = 0x0,
diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
index 622de6ba1a0b..762055a6ec9f 100644
--- a/drivers/bus/mhi/host/internal.h
+++ b/drivers/bus/mhi/host/internal.h
@@ -11,197 +11,84 @@

extern struct bus_type mhi_bus_type;

-#define MHIREGLEN (0x0)
-#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF)
-#define MHIREGLEN_MHIREGLEN_SHIFT (0)
-
-#define MHIVER (0x8)
-#define MHIVER_MHIVER_MASK (0xFFFFFFFF)
-#define MHIVER_MHIVER_SHIFT (0)
-
-#define MHICFG (0x10)
-#define MHICFG_NHWER_MASK (0xFF000000)
-#define MHICFG_NHWER_SHIFT (24)
-#define MHICFG_NER_MASK (0xFF0000)
-#define MHICFG_NER_SHIFT (16)
-#define MHICFG_NHWCH_MASK (0xFF00)
-#define MHICFG_NHWCH_SHIFT (8)
-#define MHICFG_NCH_MASK (0xFF)
-#define MHICFG_NCH_SHIFT (0)
-
-#define CHDBOFF (0x18)
-#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF)
-#define CHDBOFF_CHDBOFF_SHIFT (0)
-
-#define ERDBOFF (0x20)
-#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF)
-#define ERDBOFF_ERDBOFF_SHIFT (0)
-
-#define BHIOFF (0x28)
-#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF)
-#define BHIOFF_BHIOFF_SHIFT (0)
-
-#define BHIEOFF (0x2C)
-#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF)
-#define BHIEOFF_BHIEOFF_SHIFT (0)
-
-#define DEBUGOFF (0x30)
-#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF)
-#define DEBUGOFF_DEBUGOFF_SHIFT (0)
-
-#define MHICTRL (0x38)
-#define MHICTRL_MHISTATE_MASK (0x0000FF00)
-#define MHICTRL_MHISTATE_SHIFT (8)
-#define MHICTRL_RESET_MASK (0x2)
-#define MHICTRL_RESET_SHIFT (1)
-
-#define MHISTATUS (0x48)
-#define MHISTATUS_MHISTATE_MASK (0x0000FF00)
-#define MHISTATUS_MHISTATE_SHIFT (8)
-#define MHISTATUS_SYSERR_MASK (0x4)
-#define MHISTATUS_SYSERR_SHIFT (2)
-#define MHISTATUS_READY_MASK (0x1)
-#define MHISTATUS_READY_SHIFT (0)
-
-#define CCABAP_LOWER (0x58)
-#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF)
-#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0)
-
-#define CCABAP_HIGHER (0x5C)
-#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF)
-#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0)
-
-#define ECABAP_LOWER (0x60)
-#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF)
-#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0)
-
-#define ECABAP_HIGHER (0x64)
-#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF)
-#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0)
-
-#define CRCBAP_LOWER (0x68)
-#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF)
-#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0)
-
-#define CRCBAP_HIGHER (0x6C)
-#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF)
-#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0)
-
-#define CRDB_LOWER (0x70)
-#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF)
-#define CRDB_LOWER_CRDB_LOWER_SHIFT (0)
-
-#define CRDB_HIGHER (0x74)
-#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF)
-#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0)
-
-#define MHICTRLBASE_LOWER (0x80)
-#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF)
-#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0)
-
-#define MHICTRLBASE_HIGHER (0x84)
-#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF)
-#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0)
-
-#define MHICTRLLIMIT_LOWER (0x88)
-#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF)
-#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0)
-
-#define MHICTRLLIMIT_HIGHER (0x8C)
-#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF)
-#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0)
-
-#define MHIDATABASE_LOWER (0x98)
-#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF)
-#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0)
-
-#define MHIDATABASE_HIGHER (0x9C)
-#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF)
-#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0)
-
-#define MHIDATALIMIT_LOWER (0xA0)
-#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF)
-#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0)
-
-#define MHIDATALIMIT_HIGHER (0xA4)
-#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF)
-#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0)
+/* MHI registers */
+#define MHIREGLEN REG_MHIREGLEN
+#define MHIVER REG_MHIVER
+#define MHICFG REG_MHICFG
+#define CHDBOFF REG_CHDBOFF
+#define ERDBOFF REG_ERDBOFF
+#define BHIOFF REG_BHIOFF
+#define BHIEOFF REG_BHIEOFF
+#define DEBUGOFF REG_DEBUGOFF
+#define MHICTRL REG_MHICTRL
+#define MHISTATUS REG_MHISTATUS
+#define CCABAP_LOWER REG_CCABAP_LOWER
+#define CCABAP_HIGHER REG_CCABAP_HIGHER
+#define ECABAP_LOWER REG_ECABAP_LOWER
+#define ECABAP_HIGHER REG_ECABAP_HIGHER
+#define CRCBAP_LOWER REG_CRCBAP_LOWER
+#define CRCBAP_HIGHER REG_CRCBAP_HIGHER
+#define CRDB_LOWER REG_CRDB_LOWER
+#define CRDB_HIGHER REG_CRDB_HIGHER
+#define MHICTRLBASE_LOWER REG_MHICTRLBASE_LOWER
+#define MHICTRLBASE_HIGHER REG_MHICTRLBASE_HIGHER
+#define MHICTRLLIMIT_LOWER REG_MHICTRLLIMIT_LOWER
+#define MHICTRLLIMIT_HIGHER REG_MHICTRLLIMIT_HIGHER
+#define MHIDATABASE_LOWER REG_MHIDATABASE_LOWER
+#define MHIDATABASE_HIGHER REG_MHIDATABASE_HIGHER
+#define MHIDATALIMIT_LOWER REG_MHIDATALIMIT_LOWER
+#define MHIDATALIMIT_HIGHER REG_MHIDATALIMIT_HIGHER

/* Host request register */
-#define MHI_SOC_RESET_REQ_OFFSET (0xB0)
-#define MHI_SOC_RESET_REQ BIT(0)
-
-/* MHI BHI offfsets */
-#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_TXDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHI_TXDB_SEQNUM_SHFT (0)
-#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)
-#define BHI_STATUS_MASK (0xC0000000)
-#define BHI_STATUS_SHIFT (30)
-#define BHI_STATUS_ERROR (3)
-#define BHI_STATUS_SUCCESS (2)
-#define BHI_STATUS_RESET (0)
-
-/* MHI BHIE offsets */
-#define BHIE_MSMSOCID_OFFS (0x0000)
-#define BHIE_TXVECADDR_LOW_OFFS (0x002C)
-#define BHIE_TXVECADDR_HIGH_OFFS (0x0030)
-#define BHIE_TXVECSIZE_OFFS (0x0034)
-#define BHIE_TXVECDB_OFFS (0x003C)
-#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_TXVECDB_SEQNUM_SHFT (0)
-#define BHIE_TXVECSTATUS_OFFS (0x0044)
-#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
-#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
-#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_RXVECADDR_LOW_OFFS (0x0060)
-#define BHIE_RXVECADDR_HIGH_OFFS (0x0064)
-#define BHIE_RXVECSIZE_OFFS (0x0068)
-#define BHIE_RXVECDB_OFFS (0x0070)
-#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_RXVECDB_SEQNUM_SHFT (0)
-#define BHIE_RXVECSTATUS_OFFS (0x0078)
-#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
-#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
-#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
-#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)
-
-#define SOC_HW_VERSION_OFFS (0x224)
-#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000)
-#define SOC_HW_VERSION_FAM_NUM_SHFT (28)
-#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000)
-#define SOC_HW_VERSION_DEV_NUM_SHFT (16)
-#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00)
-#define SOC_HW_VERSION_MAJOR_VER_SHFT (8)
-#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF)
-#define SOC_HW_VERSION_MINOR_VER_SHFT (0)
+#define MHI_SOC_RESET_REQ_OFFSET 0xb0
+#define MHI_SOC_RESET_REQ BIT(0)
+
+/* MHI BHI registers */
+#define BHI_BHIVERSION_MINOR REG_BHI_BHIVERSION_MINOR
+#define BHI_BHIVERSION_MAJOR REG_BHI_BHIVERSION_MAJOR
+#define BHI_IMGADDR_LOW REG_BHI_IMGADDR_LOW
+#define BHI_IMGADDR_HIGH REG_BHI_IMGADDR_HIGH
+#define BHI_IMGSIZE REG_BHI_IMGSIZE
+#define BHI_RSVD1 REG_BHI_RSVD1
+#define BHI_IMGTXDB REG_BHI_IMGTXDB
+#define BHI_RSVD2 REG_BHI_RSVD2
+#define BHI_INTVEC REG_BHI_INTVEC
+#define BHI_RSVD3 REG_BHI_RSVD3
+#define BHI_EXECENV REG_BHI_EXECENV
+#define BHI_STATUS REG_BHI_STATUS
+#define BHI_ERRCODE REG_BHI_ERRCODE
+#define BHI_ERRDBG1 REG_BHI_ERRDBG1
+#define BHI_ERRDBG2 REG_BHI_ERRDBG2
+#define BHI_ERRDBG3 REG_BHI_ERRDBG3
+#define BHI_SERIALNU REG_BHI_SERIALNU
+#define BHI_SBLANTIROLLVER REG_BHI_SBLANTIROLLVER
+#define BHI_NUMSEG REG_BHI_NUMSEG
+#define BHI_MSMHWID(n) REG_BHI_MSMHWID(n)
+#define BHI_OEMPKHASH(n) REG_BHI_OEMPKHASH(n)
+#define BHI_RSVD5 REG_BHI_RSVD5
+
+/* MHI BHIE registers */
+#define BHIE_MSMSOCID_OFFS REG_BHIE_MSMSOCID_OFFS
+#define BHIE_TXVECADDR_LOW_OFFS REG_BHIE_TXVECADDR_LOW_OFFS
+#define BHIE_TXVECADDR_HIGH_OFFS REG_BHIE_TXVECADDR_HIGH_OFFS
+#define BHIE_TXVECSIZE_OFFS REG_BHIE_TXVECSIZE_OFFS
+#define BHIE_TXVECDB_OFFS REG_BHIE_TXVECDB_OFFS
+#define BHIE_TXVECSTATUS_OFFS REG_BHIE_TXVECSTATUS_OFFS
+#define BHIE_RXVECADDR_LOW_OFFS REG_BHIE_RXVECADDR_LOW_OFFS
+#define BHIE_RXVECADDR_HIGH_OFFS REG_BHIE_RXVECADDR_HIGH_OFFS
+#define BHIE_RXVECSIZE_OFFS REG_BHIE_RXVECSIZE_OFFS
+#define BHIE_RXVECDB_OFFS REG_BHIE_RXVECDB_OFFS
+#define BHIE_RXVECSTATUS_OFFS REG_BHIE_RXVECSTATUS_OFFS
+
+#define SOC_HW_VERSION_OFFS 0x224
+#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
+#define SOC_HW_VERSION_FAM_NUM_SHFT 28
+#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
+#define SOC_HW_VERSION_DEV_NUM_SHFT 16
+#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
+#define SOC_HW_VERSION_MAJOR_VER_SHFT 8
+#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
+#define SOC_HW_VERSION_MINOR_VER_SHFT 0

struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
--
2.25.1

2022-02-14 13:58:27

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 21/25] bus: mhi: ep: Add support for reading from the host

Data transfer between host and the ep device happens over the transfer
ring associated with each bi-directional channel pair. Host defines the
transfer ring by allocating memory for it. The read and write pointer
addresses of the transfer ring are stored in the channel context.

Once host places the elements in the transfer ring, it increments the
write pointer and rings the channel doorbell. Device will receive the
doorbell interrupt and will process the transfer ring elements.

This commit adds support for reading the transfer ring elements from
the transfer ring till write pointer, incrementing the read pointer and
finally sending the completion event to the host through corresponding
event ring.

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/ep/main.c | 103 ++++++++++++++++++++++++++++++++++++++
include/linux/mhi_ep.h | 9 ++++
2 files changed, 112 insertions(+)

diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 4c2ee517832c..b937c6cda9ba 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -336,6 +336,109 @@ int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ep_ring_element
return ret;
}

+bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir)
+{
+ struct mhi_ep_chan *mhi_chan = (dir == DMA_FROM_DEVICE) ? mhi_dev->dl_chan :
+ mhi_dev->ul_chan;
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
+ struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
+
+ return !!(ring->rd_offset == ring->wr_offset);
+}
+EXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty);
+
+static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
+ struct mhi_ep_ring *ring,
+ struct mhi_result *result,
+ u32 len)
+{
+ struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
+ size_t bytes_to_read, read_offset, write_offset;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ struct mhi_ep_ring_element *el;
+ bool td_done = false;
+ void *write_to_loc;
+ u64 read_from_loc;
+ u32 buf_remaining;
+ int ret;
+
+ buf_remaining = len;
+
+ do {
+ /* Don't process the transfer ring if the channel is not in RUNNING state */
+ if (mhi_chan->state != MHI_CH_STATE_RUNNING)
+ return -ENODEV;
+
+ el = &ring->ring_cache[ring->rd_offset];
+
+ /* Check if there is data pending to be read from previous read operation */
+ if (mhi_chan->tre_bytes_left) {
+ dev_dbg(dev, "TRE bytes remaining: %d\n", mhi_chan->tre_bytes_left);
+ bytes_to_read = min(buf_remaining, mhi_chan->tre_bytes_left);
+ } else {
+ mhi_chan->tre_loc = MHI_EP_TRE_GET_PTR(el);
+ mhi_chan->tre_size = MHI_EP_TRE_GET_LEN(el);
+ mhi_chan->tre_bytes_left = mhi_chan->tre_size;
+
+ bytes_to_read = min(buf_remaining, mhi_chan->tre_size);
+ }
+
+ read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
+ write_offset = len - buf_remaining;
+ read_from_loc = mhi_chan->tre_loc + read_offset;
+ write_to_loc = result->buf_addr + write_offset;
+
+ dev_dbg(dev, "Reading %zd bytes from channel (%d)\n", bytes_to_read, ring->ch_id);
+ ret = mhi_cntrl->read_from_host(mhi_cntrl, read_from_loc, write_to_loc,
+ bytes_to_read);
+ if (ret < 0)
+ return ret;
+
+ buf_remaining -= bytes_to_read;
+ mhi_chan->tre_bytes_left -= bytes_to_read;
+
+ /*
+ * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been
+ * read completely:
+ *
+ * 1. Send completion event to the host based on the flags set in TRE.
+ * 2. Increment the local read offset of the transfer ring.
+ */
+ if (!mhi_chan->tre_bytes_left) {
+ /*
+ * The host will split the data packet into multiple TREs if it can't fit
+ * the packet in a single TRE. In that case, CHAIN flag will be set by the
+ * host for all TREs except the last one.
+ */
+ if (MHI_EP_TRE_GET_CHAIN(el)) {
+ /*
+ * IEOB (Interrupt on End of Block) flag will be set by the host if
+ * it expects the completion event for all TREs of a TD.
+ */
+ if (MHI_EP_TRE_GET_IEOB(el))
+ mhi_ep_send_completion_event(mhi_cntrl,
+ ring, MHI_EP_TRE_GET_LEN(el), MHI_EV_CC_EOB);
+ } else {
+ /*
+ * IEOT (Interrupt on End of Transfer) flag will be set by the host
+ * for the last TRE of the TD and expects the completion event for
+ * the same.
+ */
+ if (MHI_EP_TRE_GET_IEOT(el))
+ mhi_ep_send_completion_event(mhi_cntrl,
+ ring, MHI_EP_TRE_GET_LEN(el), MHI_EV_CC_EOT);
+ td_done = true;
+ }
+
+ mhi_ep_ring_inc_index(ring);
+ }
+
+ result->bytes_xferd += bytes_to_read;
+ } while (buf_remaining && !td_done);
+
+ return 0;
+}
+
static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index 276d29fef465..aaf4b6942037 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -268,4 +268,13 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl);
*/
void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl);

+/**
+ * mhi_ep_queue_is_empty - Determine whether the transfer queue is empty
+ * @mhi_dev: Device associated with the channels
+ * @dir: DMA direction for the channel
+ *
+ * Return: true if the queue is empty, false otherwise.
+ */
+bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir);
+
#endif
--
2.25.1

2022-02-14 19:57:05

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 09/25] bus: mhi: ep: Add support for registering MHI endpoint client drivers

This commit adds support for registering MHI endpoint client drivers
with the MHI endpoint stack. MHI endpoint client drivers binds to one
or more MHI endpoint devices inorder to send and receive the upper-layer
protocol packets like IP packets, modem control messages, and diagnostics
messages over MHI bus.

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/ep/main.c | 86 +++++++++++++++++++++++++++++++++++++++
include/linux/mhi_ep.h | 53 ++++++++++++++++++++++++
2 files changed, 139 insertions(+)

diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index b006011d025d..f66404181972 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -196,9 +196,89 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
}
EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);

+static int mhi_ep_driver_probe(struct device *dev)
+{
+ struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+ struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
+ struct mhi_ep_chan *ul_chan = mhi_dev->ul_chan;
+ struct mhi_ep_chan *dl_chan = mhi_dev->dl_chan;
+
+ /* Client drivers should have callbacks for both channels */
+ if (!mhi_drv->ul_xfer_cb || !mhi_drv->dl_xfer_cb)
+ return -EINVAL;
+
+ ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
+ dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
+
+ return mhi_drv->probe(mhi_dev, mhi_dev->id);
+}
+
+static int mhi_ep_driver_remove(struct device *dev)
+{
+ struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+ struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
+ struct mhi_result result = {};
+ struct mhi_ep_chan *mhi_chan;
+ int dir;
+
+ /* Skip if it is a controller device */
+ if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+ return 0;
+
+ /* Disconnect the channels associated with the driver */
+ for (dir = 0; dir < 2; dir++) {
+ mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
+
+ if (!mhi_chan)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ /* Send channel disconnect status to the client driver */
+ if (mhi_chan->xfer_cb) {
+ result.transaction_status = -ENOTCONN;
+ result.bytes_xferd = 0;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+ }
+
+ /* Set channel state to DISABLED */
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ mhi_chan->xfer_cb = NULL;
+ mutex_unlock(&mhi_chan->lock);
+ }
+
+ /* Remove the client driver now */
+ mhi_drv->remove(mhi_dev);
+
+ return 0;
+}
+
+int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner)
+{
+ struct device_driver *driver = &mhi_drv->driver;
+
+ if (!mhi_drv->probe || !mhi_drv->remove)
+ return -EINVAL;
+
+ driver->bus = &mhi_ep_bus_type;
+ driver->owner = owner;
+ driver->probe = mhi_ep_driver_probe;
+ driver->remove = mhi_ep_driver_remove;
+
+ return driver_register(driver);
+}
+EXPORT_SYMBOL_GPL(__mhi_ep_driver_register);
+
+void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
+{
+ driver_unregister(&mhi_drv->driver);
+}
+EXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
+
static int mhi_ep_match(struct device *dev, struct device_driver *drv)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+ struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
+ const struct mhi_device_id *id;

/*
* If the device is a controller type then there is no client driver
@@ -207,6 +287,12 @@ static int mhi_ep_match(struct device *dev, struct device_driver *drv)
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
return 0;

+ for (id = mhi_drv->id_table; id->chan[0]; id++)
+ if (!strcmp(mhi_dev->name, id->chan)) {
+ mhi_dev->id = id;
+ return 1;
+ }
+
return 0;
};

diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index 20238e9df1b3..da865f9d3646 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -122,7 +122,60 @@ struct mhi_ep_device {
enum mhi_device_type dev_type;
};

+/**
+ * struct mhi_ep_driver - Structure representing a MHI Endpoint client driver
+ * @id_table: Pointer to MHI Endpoint device ID table
+ * @driver: Device driver model driver
+ * @probe: CB function for client driver probe function
+ * @remove: CB function for client driver remove function
+ * @ul_xfer_cb: CB function for UL data transfer
+ * @dl_xfer_cb: CB function for DL data transfer
+ */
+struct mhi_ep_driver {
+ const struct mhi_device_id *id_table;
+ struct device_driver driver;
+ int (*probe)(struct mhi_ep_device *mhi_ep,
+ const struct mhi_device_id *id);
+ void (*remove)(struct mhi_ep_device *mhi_ep);
+ void (*ul_xfer_cb)(struct mhi_ep_device *mhi_dev,
+ struct mhi_result *result);
+ void (*dl_xfer_cb)(struct mhi_ep_device *mhi_dev,
+ struct mhi_result *result);
+};
+
#define to_mhi_ep_device(dev) container_of(dev, struct mhi_ep_device, dev)
+#define to_mhi_ep_driver(drv) container_of(drv, struct mhi_ep_driver, driver)
+
+/*
+ * module_mhi_ep_driver() - Helper macro for drivers that don't do
+ * anything special other than using default mhi_ep_driver_register() and
+ * mhi_ep_driver_unregister(). This eliminates a lot of boilerplate.
+ * Each module may only use this macro once.
+ */
+#define module_mhi_ep_driver(mhi_drv) \
+ module_driver(mhi_drv, mhi_ep_driver_register, \
+ mhi_ep_driver_unregister)
+
+/*
+ * Macro to avoid include chaining to get THIS_MODULE
+ */
+#define mhi_ep_driver_register(mhi_drv) \
+ __mhi_ep_driver_register(mhi_drv, THIS_MODULE)
+
+/**
+ * __mhi_ep_driver_register - Register a driver with MHI Endpoint bus
+ * @mhi_drv: Driver to be associated with the device
+ * @owner: The module owner
+ *
+ * Return: 0 if driver registrations succeeds, a negative error code otherwise.
+ */
+int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner);
+
+/**
+ * mhi_ep_driver_unregister - Unregister a driver from MHI Endpoint bus
+ * @mhi_drv: Driver associated with the device
+ */
+void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv);

/**
* mhi_ep_register_controller - Register MHI Endpoint controller
--
2.25.1

2022-02-14 20:05:45

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 24/25] bus: mhi: ep: Add support for suspending and resuming channels

Add support for suspending and resuming the channels in MHI endpoint stack.
The channels will be moved to the suspended state during M3 state
transition and will be resumed during M0 transition.

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/ep/internal.h | 2 ++
drivers/bus/mhi/ep/main.c | 58 +++++++++++++++++++++++++++++++++++
drivers/bus/mhi/ep/sm.c | 4 +++
3 files changed, 64 insertions(+)

diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
index 8654af7caf40..e23d2fd04282 100644
--- a/drivers/bus/mhi/ep/internal.h
+++ b/drivers/bus/mhi/ep/internal.h
@@ -242,6 +242,8 @@ 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);

/* MHI EP memory management functions */
int mhi_ep_alloc_map(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, size_t size,
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index e4186b012257..315409705b91 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1106,6 +1106,64 @@ void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
}
EXPORT_SYMBOL_GPL(mhi_ep_power_down);

+void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct mhi_ep_chan *mhi_chan;
+ u32 tmp;
+ int i;
+
+ for (i = 0; i < mhi_cntrl->max_chan; i++) {
+ mhi_chan = &mhi_cntrl->mhi_chan[i];
+
+ if (!mhi_chan->mhi_dev)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ /* Skip if the channel is not currently running */
+ tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
+ if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_RUNNING) {
+ mutex_unlock(&mhi_chan->lock);
+ continue;
+ }
+
+ dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
+ /* Set channel state to SUSPENDED */
+ tmp &= ~CHAN_CTX_CHSTATE_MASK;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);
+ mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
+ mutex_unlock(&mhi_chan->lock);
+ }
+}
+
+void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct mhi_ep_chan *mhi_chan;
+ u32 tmp;
+ int i;
+
+ for (i = 0; i < mhi_cntrl->max_chan; i++) {
+ mhi_chan = &mhi_cntrl->mhi_chan[i];
+
+ if (!mhi_chan->mhi_dev)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ /* Skip if the channel is not currently suspended */
+ tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
+ if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_SUSPENDED) {
+ mutex_unlock(&mhi_chan->lock);
+ continue;
+ }
+
+ dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
+ /* Set channel state to RUNNING */
+ tmp &= ~CHAN_CTX_CHSTATE_MASK;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
+ mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
+ mutex_unlock(&mhi_chan->lock);
+ }
+}
+
static void mhi_ep_release_device(struct device *dev)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c
index 9a75ecfe1adf..e24ba2d85e13 100644
--- a/drivers/bus/mhi/ep/sm.c
+++ b/drivers/bus/mhi/ep/sm.c
@@ -88,8 +88,11 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
enum mhi_state old_state;
int ret;

+ /* If MHI is in M3, resume suspended channels */
spin_lock_bh(&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) {
@@ -135,6 +138,7 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
}

spin_unlock_bh(&mhi_cntrl->state_lock);
+ 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);
--
2.25.1

2022-02-14 20:33:24

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 14/25] bus: mhi: ep: Add support for managing MHI state machine

Add support for managing the MHI state machine by controlling the state
transitions. Only the following MHI state transitions are supported:

1. Ready state
2. M0 state
3. M3 state
4. SYS_ERR state

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/ep/Makefile | 2 +-
drivers/bus/mhi/ep/internal.h | 11 +++
drivers/bus/mhi/ep/main.c | 51 ++++++++++-
drivers/bus/mhi/ep/sm.c | 168 ++++++++++++++++++++++++++++++++++
include/linux/mhi_ep.h | 6 ++
5 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 drivers/bus/mhi/ep/sm.c

diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
index 7ba0e04801eb..aad85f180b70 100644
--- a/drivers/bus/mhi/ep/Makefile
+++ b/drivers/bus/mhi/ep/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
-mhi_ep-y := main.o mmio.o ring.o
+mhi_ep-y := main.o mmio.o ring.o sm.o
diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
index fd63f79c6aec..e4e8f06c2898 100644
--- a/drivers/bus/mhi/ep/internal.h
+++ b/drivers/bus/mhi/ep/internal.h
@@ -173,6 +173,11 @@ 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;
@@ -230,5 +235,11 @@ 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_ep_execenv 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);

#endif
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 61f066c6286b..ccb3c2795041 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -185,6 +185,43 @@ static void mhi_ep_ring_worker(struct work_struct *work)
}
}

+static void mhi_ep_state_worker(struct work_struct *work)
+{
+ struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ struct mhi_ep_state_transition *itr, *tmp;
+ unsigned long flags;
+ LIST_HEAD(head);
+ int ret;
+
+ spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
+ list_splice_tail_init(&mhi_cntrl->st_transition_list, &head);
+ spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
+
+ list_for_each_entry_safe(itr, tmp, &head, node) {
+ list_del(&itr->node);
+ dev_dbg(dev, "Handling MHI state transition to %s\n",
+ mhi_state_str(itr->state));
+
+ switch (itr->state) {
+ case MHI_STATE_M0:
+ ret = mhi_ep_set_m0_state(mhi_cntrl);
+ if (ret)
+ dev_err(dev, "Failed to transition to M0 state\n");
+ break;
+ case MHI_STATE_M3:
+ ret = mhi_ep_set_m3_state(mhi_cntrl);
+ if (ret)
+ dev_err(dev, "Failed to transition to M3 state\n");
+ break;
+ default:
+ dev_err(dev, "Invalid MHI state transition: %d\n", itr->state);
+ break;
+ }
+ kfree(itr);
+ }
+}
+
static void mhi_ep_release_device(struct device *dev)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
@@ -386,6 +423,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
}

INIT_WORK(&mhi_cntrl->ring_work, mhi_ep_ring_worker);
+ INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);

mhi_cntrl->ring_wq = alloc_workqueue("mhi_ep_ring_wq", 0, 0);
if (!mhi_cntrl->ring_wq) {
@@ -393,8 +431,16 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
goto err_free_cmd;
}

+ mhi_cntrl->state_wq = alloc_workqueue("mhi_ep_state_wq", 0, 0);
+ if (!mhi_cntrl->state_wq) {
+ ret = -ENOMEM;
+ goto err_destroy_ring_wq;
+ }
+
INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
+ INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
spin_lock_init(&mhi_cntrl->list_lock);
+ spin_lock_init(&mhi_cntrl->state_lock);
mutex_init(&mhi_cntrl->event_lock);

/* Set MHI version and AMSS EE before enumeration */
@@ -405,7 +451,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
if (mhi_cntrl->index < 0) {
ret = mhi_cntrl->index;
- goto err_destroy_ring_wq;
+ goto err_destroy_state_wq;
}

/* Allocate the controller device */
@@ -433,6 +479,8 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
put_device(&mhi_dev->dev);
err_ida_free:
ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
+err_destroy_state_wq:
+ destroy_workqueue(mhi_cntrl->state_wq);
err_destroy_ring_wq:
destroy_workqueue(mhi_cntrl->ring_wq);
err_free_cmd:
@@ -448,6 +496,7 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
{
struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;

+ destroy_workqueue(mhi_cntrl->state_wq);
destroy_workqueue(mhi_cntrl->ring_wq);

kfree(mhi_cntrl->mhi_cmd);
diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c
new file mode 100644
index 000000000000..68e7f99b9137
--- /dev/null
+++ b/drivers/bus/mhi/ep/sm.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <[email protected]>
+ */
+
+#include <linux/delay.h>
+#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)
+{
+ bool valid = false;
+
+ switch (mhi_state) {
+ case MHI_STATE_READY:
+ valid = (cur_mhi_state == MHI_STATE_RESET);
+ break;
+ case MHI_STATE_M0:
+ valid = (cur_mhi_state == MHI_STATE_READY ||
+ cur_mhi_state == MHI_STATE_M3);
+ break;
+ case MHI_STATE_M3:
+ valid = (cur_mhi_state == MHI_STATE_M0);
+ break;
+ case MHI_STATE_SYS_ERR:
+ /* Transition to SYS_ERR state is allowed all the time */
+ valid = true;
+ break;
+ default:
+ break;
+ }
+
+ return valid;
+}
+
+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;
+ }
+
+ switch (mhi_state) {
+ case MHI_STATE_READY:
+ mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
+ MHISTATUS_READY_MASK, 1);
+
+ mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
+ MHISTATUS_MHISTATE_MASK, mhi_state);
+ break;
+ case MHI_STATE_SYS_ERR:
+ mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
+ MHISTATUS_SYSERR_MASK, 1);
+
+ mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
+ MHISTATUS_MHISTATE_MASK, mhi_state);
+ break;
+ case MHI_STATE_M1:
+ case MHI_STATE_M2:
+ dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state));
+ return -EOPNOTSUPP;
+ case MHI_STATE_M0:
+ case MHI_STATE_M3:
+ mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
+ MHISTATUS_MHISTATE_MASK, mhi_state);
+ break;
+ default:
+ dev_err(dev, "Invalid MHI state (%d)\n", mhi_state);
+ return -EINVAL;
+ }
+
+ mhi_cntrl->mhi_state = mhi_state;
+
+ 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;
+
+ spin_lock_bh(&mhi_cntrl->state_lock);
+ old_state = mhi_cntrl->mhi_state;
+
+ ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
+ if (ret) {
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+ return ret;
+ }
+
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+ /* 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");
+ return ret;
+ }
+
+ if (old_state == MHI_STATE_READY) {
+ /* Allow the host to process state change event */
+ mdelay(1);
+
+ /* Send AMSS EE event to host */
+ ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EP_AMSS_EE);
+ if (ret) {
+ dev_err(dev, "Failed sending AMSS EE event\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ int ret;
+
+ spin_lock_bh(&mhi_cntrl->state_lock);
+ ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
+ if (ret) {
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+ return ret;
+ }
+
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+
+ /* 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");
+ return ret;
+ }
+
+ return 0;
+}
+
+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;
+
+ spin_lock_bh(&mhi_cntrl->state_lock);
+ /* Ensure that the MHISTATUS is set to RESET by host */
+ mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, MHISTATUS, MHISTATUS_MHISTATE_MASK);
+ is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, 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");
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+ return -EFAULT;
+ }
+
+ ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
+ spin_unlock_bh(&mhi_cntrl->state_lock);
+
+ return ret;
+}
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index 062133a68118..72ce30cbe87e 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -65,11 +65,14 @@ struct mhi_ep_db_info {
* @ch_ctx_host_pa: Physical address of host channel context data structure
* @ev_ctx_host_pa: Physical address of host event context data structure
* @cmd_ctx_host_pa: Physical address of host command context data structure
+ * @state_wq: Dedicated workqueue for handling MHI state transitions
* @ring_wq: Dedicated workqueue for processing MHI rings
+ * @state_work: State transition worker
* @ring_work: Ring worker
* @ch_db_list: List of queued channel doorbells
* @st_transition_list: List of state transitions
* @list_lock: Lock for protecting state transition and channel doorbell lists
+ * @state_lock: Lock for protecting state transitions
* @event_lock: Lock for protecting event rings
* @chdb: Array of channel doorbell interrupt info
* @raise_irq: CB function for raising IRQ to the host
@@ -105,12 +108,15 @@ struct mhi_ep_cntrl {
u64 ev_ctx_host_pa;
u64 cmd_ctx_host_pa;

+ struct workqueue_struct *state_wq;
struct workqueue_struct *ring_wq;
+ struct work_struct state_work;
struct work_struct ring_work;

struct list_head ch_db_list;
struct list_head st_transition_list;
spinlock_t list_lock;
+ spinlock_t state_lock;
struct mutex event_lock;
struct mhi_ep_db_info chdb[4];

--
2.25.1

2022-02-14 21:08:56

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

This commit adds support for registering MHI endpoint controller drivers
with the MHI endpoint stack. MHI endpoint controller drivers manages
the interaction with the host machines such as x86. They are also the
MHI endpoint bus master in charge of managing the physical link between the
host and endpoint device.

The endpoint controller driver encloses all information about the
underlying physical bus like PCIe. The registration process involves
parsing the channel configuration and allocating an MHI EP device.

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/Kconfig | 1 +
drivers/bus/mhi/Makefile | 3 +
drivers/bus/mhi/ep/Kconfig | 10 ++
drivers/bus/mhi/ep/Makefile | 2 +
drivers/bus/mhi/ep/internal.h | 160 +++++++++++++++++++++++
drivers/bus/mhi/ep/main.c | 234 ++++++++++++++++++++++++++++++++++
include/linux/mhi_ep.h | 143 +++++++++++++++++++++
7 files changed, 553 insertions(+)
create mode 100644 drivers/bus/mhi/ep/Kconfig
create mode 100644 drivers/bus/mhi/ep/Makefile
create mode 100644 drivers/bus/mhi/ep/internal.h
create mode 100644 drivers/bus/mhi/ep/main.c
create mode 100644 include/linux/mhi_ep.h

diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig
index 4748df7f9cd5..b39a11e6c624 100644
--- a/drivers/bus/mhi/Kconfig
+++ b/drivers/bus/mhi/Kconfig
@@ -6,3 +6,4 @@
#

source "drivers/bus/mhi/host/Kconfig"
+source "drivers/bus/mhi/ep/Kconfig"
diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
index 5f5708a249f5..46981331b38f 100644
--- a/drivers/bus/mhi/Makefile
+++ b/drivers/bus/mhi/Makefile
@@ -1,2 +1,5 @@
# Host MHI stack
obj-y += host/
+
+# Endpoint MHI stack
+obj-y += ep/
diff --git a/drivers/bus/mhi/ep/Kconfig b/drivers/bus/mhi/ep/Kconfig
new file mode 100644
index 000000000000..229c71397b30
--- /dev/null
+++ b/drivers/bus/mhi/ep/Kconfig
@@ -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 the host processors to control
+ and communicate with modem devices over a high speed peripheral
+ bus or shared memory.
+
+ MHI_BUS_EP implements the MHI protocol for the endpoint devices
+ like SDX55 modem connected to the host machine over PCIe.
diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
new file mode 100644
index 000000000000..64e29252b608
--- /dev/null
+++ b/drivers/bus/mhi/ep/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
+mhi_ep-y := main.o
diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
new file mode 100644
index 000000000000..e313a2546664
--- /dev/null
+++ b/drivers/bus/mhi/ep/internal.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, Linaro Ltd.
+ *
+ */
+
+#ifndef _MHI_EP_INTERNAL_
+#define _MHI_EP_INTERNAL_
+
+#include <linux/bitfield.h>
+
+#include "../common.h"
+
+extern struct bus_type mhi_ep_bus_type;
+
+#define MHI_REG_OFFSET 0x100
+#define BHI_REG_OFFSET 0x200
+
+/* MHI registers */
+#define MHIREGLEN (MHI_REG_OFFSET + REG_MHIREGLEN)
+#define MHIVER (MHI_REG_OFFSET + REG_MHIVER)
+#define MHICFG (MHI_REG_OFFSET + REG_MHICFG)
+#define CHDBOFF (MHI_REG_OFFSET + REG_CHDBOFF)
+#define ERDBOFF (MHI_REG_OFFSET + REG_ERDBOFF)
+#define BHIOFF (MHI_REG_OFFSET + REG_BHIOFF)
+#define BHIEOFF (MHI_REG_OFFSET + REG_BHIEOFF)
+#define DEBUGOFF (MHI_REG_OFFSET + REG_DEBUGOFF)
+#define MHICTRL (MHI_REG_OFFSET + REG_MHICTRL)
+#define MHISTATUS (MHI_REG_OFFSET + REG_MHISTATUS)
+#define CCABAP_LOWER (MHI_REG_OFFSET + REG_CCABAP_LOWER)
+#define CCABAP_HIGHER (MHI_REG_OFFSET + REG_CCABAP_HIGHER)
+#define ECABAP_LOWER (MHI_REG_OFFSET + REG_ECABAP_LOWER)
+#define ECABAP_HIGHER (MHI_REG_OFFSET + REG_ECABAP_HIGHER)
+#define CRCBAP_LOWER (MHI_REG_OFFSET + REG_CRCBAP_LOWER)
+#define CRCBAP_HIGHER (MHI_REG_OFFSET + REG_CRCBAP_HIGHER)
+#define CRDB_LOWER (MHI_REG_OFFSET + REG_CRDB_LOWER)
+#define CRDB_HIGHER (MHI_REG_OFFSET + REG_CRDB_HIGHER)
+#define MHICTRLBASE_LOWER (MHI_REG_OFFSET + REG_MHICTRLBASE_LOWER)
+#define MHICTRLBASE_HIGHER (MHI_REG_OFFSET + REG_MHICTRLBASE_HIGHER)
+#define MHICTRLLIMIT_LOWER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_LOWER)
+#define MHICTRLLIMIT_HIGHER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_HIGHER)
+#define MHIDATABASE_LOWER (MHI_REG_OFFSET + REG_MHIDATABASE_LOWER)
+#define MHIDATABASE_HIGHER (MHI_REG_OFFSET + REG_MHIDATABASE_HIGHER)
+#define MHIDATALIMIT_LOWER (MHI_REG_OFFSET + REG_MHIDATALIMIT_LOWER)
+#define MHIDATALIMIT_HIGHER (MHI_REG_OFFSET + REG_MHIDATALIMIT_HIGHER)
+
+/* MHI BHI registers */
+#define BHI_IMGTXDB (BHI_REG_OFFSET + REG_BHI_IMGTXDB)
+#define BHI_EXECENV (BHI_REG_OFFSET + REG_BHI_EXECENV)
+#define BHI_INTVEC (BHI_REG_OFFSET + REG_BHI_INTVEC)
+
+/* 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_A7 0x4
+#define MHI_CTRL_INT_STATUS_A7_MSK BIT(0)
+#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
+#define MHI_CHDB_INT_STATUS_A7_n(n) (0x28 + 0x4 * (n))
+#define MHI_ERDB_INT_STATUS_A7_n(n) (0x38 + 0x4 * (n))
+
+#define MHI_CTRL_INT_CLEAR_A7 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_A7_n(n) (0x70 + 0x4 * (n))
+#define MHI_CHDB_INT_CLEAR_A7_n_CLEAR_ALL GENMASK(31, 0)
+#define MHI_ERDB_INT_CLEAR_A7_n(n) (0x80 + 0x4 * (n))
+#define MHI_ERDB_INT_CLEAR_A7_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_A7 0x94
+#define MHI_CTRL_INT_MASK_A7_MASK GENMASK(1, 0)
+#define MHI_CTRL_MHICTRL_MASK BIT(0)
+#define MHI_CTRL_CRDB_MASK BIT(1)
+
+#define MHI_CHDB_INT_MASK_A7_n(n) (0xb8 + 0x4 * (n))
+#define MHI_CHDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
+#define MHI_ERDB_INT_MASK_A7_n(n) (0xc8 + 0x4 * (n))
+#define MHI_ERDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
+
+#define NR_OF_CMD_RINGS 1
+#define MHI_MASK_ROWS_CH_EV_DB 4
+#define MHI_MASK_CH_EV_LEN 32
+
+/* Generic context */
+struct mhi_generic_ctx {
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+
+ __u64 rbase __packed __aligned(4);
+ __u64 rlen __packed __aligned(4);
+ __u64 rp __packed __aligned(4);
+ __u64 wp __packed __aligned(4);
+};
+
+enum mhi_ep_ring_type {
+ RING_TYPE_CMD = 0,
+ RING_TYPE_ER,
+ RING_TYPE_CH,
+};
+
+struct mhi_ep_ring_element {
+ u64 ptr;
+ u32 dword[2];
+};
+
+/* 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 {
+ struct mhi_ep_cntrl *mhi_cntrl;
+ int (*ring_cb)(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el);
+ union mhi_ep_ring_ctx *ring_ctx;
+ struct mhi_ep_ring_element *ring_cache;
+ enum mhi_ep_ring_type type;
+ size_t rd_offset;
+ size_t wr_offset;
+ size_t ring_size;
+ u32 db_offset_h;
+ u32 db_offset_l;
+ u32 ch_id;
+};
+
+struct mhi_ep_cmd {
+ struct mhi_ep_ring ring;
+};
+
+struct mhi_ep_event {
+ struct mhi_ep_ring ring;
+};
+
+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;
+ u64 tre_loc;
+ u32 tre_size;
+ u32 tre_bytes_left;
+ u32 chan;
+ bool skip_td;
+};
+
+#endif
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
new file mode 100644
index 000000000000..b006011d025d
--- /dev/null
+++ b/drivers/bus/mhi/ep/main.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MHI Bus Endpoint stack
+ *
+ * Copyright (C) 2021 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/dma-direction.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mhi_ep.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include "internal.h"
+
+static DEFINE_IDA(mhi_ep_cntrl_ida);
+
+static void mhi_ep_release_device(struct device *dev)
+{
+ struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+
+ if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+ mhi_dev->mhi_cntrl->mhi_dev = NULL;
+
+ /*
+ * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
+ * devices for the channels will only get created during start
+ * channel if the mhi_dev associated with it is NULL.
+ */
+ if (mhi_dev->ul_chan)
+ mhi_dev->ul_chan->mhi_dev = NULL;
+
+ if (mhi_dev->dl_chan)
+ mhi_dev->dl_chan->mhi_dev = NULL;
+
+ kfree(mhi_dev);
+}
+
+static struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl,
+ enum mhi_device_type dev_type)
+{
+ struct mhi_ep_device *mhi_dev;
+ struct device *dev;
+
+ mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
+ if (!mhi_dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev = &mhi_dev->dev;
+ device_initialize(dev);
+ dev->bus = &mhi_ep_bus_type;
+ dev->release = mhi_ep_release_device;
+
+ if (dev_type == MHI_DEVICE_CONTROLLER)
+ /* for MHI controller device, parent is the bus device (e.g. PCI EPF) */
+ dev->parent = mhi_cntrl->cntrl_dev;
+ else
+ /* for MHI client devices, parent is the MHI controller device */
+ dev->parent = &mhi_cntrl->mhi_dev->dev;
+
+ mhi_dev->mhi_cntrl = mhi_cntrl;
+ mhi_dev->dev_type = dev_type;
+
+ return mhi_dev;
+}
+
+static int parse_ch_cfg(struct mhi_ep_cntrl *mhi_cntrl,
+ const struct mhi_ep_cntrl_config *config)
+{
+ const struct mhi_ep_channel_config *ch_cfg;
+ struct device *dev = mhi_cntrl->cntrl_dev;
+ u32 chan, i;
+ int ret = -EINVAL;
+
+ mhi_cntrl->max_chan = config->max_channels;
+
+ /*
+ * Allocate max_channels supported by the MHI endpoint and populate
+ * only the defined channels
+ */
+ mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
+ GFP_KERNEL);
+ if (!mhi_cntrl->mhi_chan)
+ return -ENOMEM;
+
+ for (i = 0; i < config->num_channels; i++) {
+ struct mhi_ep_chan *mhi_chan;
+
+ ch_cfg = &config->ch_cfg[i];
+
+ chan = ch_cfg->num;
+ if (chan >= mhi_cntrl->max_chan) {
+ dev_err(dev, "Channel %d not available\n", chan);
+ goto error_chan_cfg;
+ }
+
+ /* Bi-directional and direction less channels are not supported */
+ if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
+ dev_err(dev, "Invalid channel configuration\n");
+ goto error_chan_cfg;
+ }
+
+ mhi_chan = &mhi_cntrl->mhi_chan[chan];
+ mhi_chan->name = ch_cfg->name;
+ mhi_chan->chan = chan;
+ mhi_chan->dir = ch_cfg->dir;
+ mutex_init(&mhi_chan->lock);
+ }
+
+ return 0;
+
+error_chan_cfg:
+ kfree(mhi_cntrl->mhi_chan);
+
+ return ret;
+}
+
+/*
+ * Allocate channel and command rings here. Event rings will be allocated
+ * in mhi_ep_power_up() as the config comes from the host.
+ */
+int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
+ const struct mhi_ep_cntrl_config *config)
+{
+ struct mhi_ep_device *mhi_dev;
+ int ret;
+
+ if (!mhi_cntrl || !mhi_cntrl->cntrl_dev)
+ return -EINVAL;
+
+ ret = parse_ch_cfg(mhi_cntrl, config);
+ if (ret)
+ return ret;
+
+ mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
+ if (!mhi_cntrl->mhi_cmd) {
+ ret = -ENOMEM;
+ goto err_free_ch;
+ }
+
+ /* Set controller index */
+ mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
+ if (mhi_cntrl->index < 0) {
+ ret = mhi_cntrl->index;
+ goto err_free_cmd;
+ }
+
+ /* Allocate the controller device */
+ mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_CONTROLLER);
+ if (IS_ERR(mhi_dev)) {
+ dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n");
+ ret = PTR_ERR(mhi_dev);
+ goto err_ida_free;
+ }
+
+ dev_set_name(&mhi_dev->dev, "mhi_ep%d", mhi_cntrl->index);
+ mhi_dev->name = dev_name(&mhi_dev->dev);
+
+ ret = device_add(&mhi_dev->dev);
+ if (ret)
+ goto err_put_dev;
+
+ mhi_cntrl->mhi_dev = mhi_dev;
+
+ dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n");
+
+ return 0;
+
+err_put_dev:
+ put_device(&mhi_dev->dev);
+err_ida_free:
+ ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
+err_free_cmd:
+ kfree(mhi_cntrl->mhi_cmd);
+err_free_ch:
+ kfree(mhi_cntrl->mhi_chan);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mhi_ep_register_controller);
+
+void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
+
+ kfree(mhi_cntrl->mhi_cmd);
+ kfree(mhi_cntrl->mhi_chan);
+
+ device_del(&mhi_dev->dev);
+ put_device(&mhi_dev->dev);
+
+ ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
+}
+EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
+
+static int mhi_ep_match(struct device *dev, struct device_driver *drv)
+{
+ struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
+
+ /*
+ * If the device is a controller type then there is no client driver
+ * associated with it
+ */
+ if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
+ return 0;
+
+ return 0;
+};
+
+struct bus_type mhi_ep_bus_type = {
+ .name = "mhi_ep",
+ .dev_name = "mhi_ep",
+ .match = mhi_ep_match,
+};
+
+static int __init mhi_ep_init(void)
+{
+ return bus_register(&mhi_ep_bus_type);
+}
+
+static void __exit mhi_ep_exit(void)
+{
+ bus_unregister(&mhi_ep_bus_type);
+}
+
+postcore_initcall(mhi_ep_init);
+module_exit(mhi_ep_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MHI Bus Endpoint stack");
+MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
new file mode 100644
index 000000000000..20238e9df1b3
--- /dev/null
+++ b/include/linux/mhi_ep.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021, Linaro Ltd.
+ *
+ */
+#ifndef _MHI_EP_H_
+#define _MHI_EP_H_
+
+#include <linux/dma-direction.h>
+#include <linux/mhi.h>
+
+#define MHI_EP_DEFAULT_MTU 0x8000
+
+/**
+ * struct mhi_ep_channel_config - Channel configuration structure for controller
+ * @name: The name of this channel
+ * @num: The number assigned to this channel
+ * @num_elements: The number of elements that can be queued to this channel
+ * @dir: Direction that data may flow on this channel
+ */
+struct mhi_ep_channel_config {
+ char *name;
+ u32 num;
+ u32 num_elements;
+ enum dma_data_direction dir;
+};
+
+/**
+ * struct mhi_ep_cntrl_config - MHI Endpoint controller configuration
+ * @max_channels: Maximum number of channels supported
+ * @num_channels: Number of channels defined in @ch_cfg
+ * @ch_cfg: Array of defined channels
+ * @mhi_version: MHI spec version supported by the controller
+ */
+struct mhi_ep_cntrl_config {
+ u32 max_channels;
+ u32 num_channels;
+ const struct mhi_ep_channel_config *ch_cfg;
+ u32 mhi_version;
+};
+
+/**
+ * struct mhi_ep_db_info - MHI Endpoint doorbell info
+ * @mask: Mask of the doorbell interrupt
+ * @status: Status of the doorbell interrupt
+ */
+struct mhi_ep_db_info {
+ u32 mask;
+ u32 status;
+};
+
+/**
+ * struct mhi_ep_cntrl - MHI Endpoint controller structure
+ * @cntrl_dev: Pointer to the struct device of physical bus acting as the MHI
+ * Endpoint controller
+ * @mhi_dev: MHI Endpoint device instance for the controller
+ * @mmio: MMIO region containing the MHI registers
+ * @mhi_chan: Points to the channel configuration table
+ * @mhi_event: Points to the event ring configurations table
+ * @mhi_cmd: Points to the command ring configurations table
+ * @sm: MHI Endpoint state machine
+ * @raise_irq: CB function for raising IRQ to the host
+ * @alloc_addr: CB function for allocating memory in endpoint for storing host context
+ * @map_addr: CB function for mapping host context to endpoint
+ * @free_addr: CB function to free the allocated memory in endpoint for storing host context
+ * @unmap_addr: CB function to unmap the host context in endpoint
+ * @read_from_host: CB function for reading from host memory from endpoint
+ * @write_to_host: CB function for writing to host memory from endpoint
+ * @mhi_state: MHI Endpoint state
+ * @max_chan: Maximum channels supported by the endpoint controller
+ * @mru: MRU (Maximum Receive Unit) value of the endpoint controller
+ * @index: MHI Endpoint controller index
+ */
+struct mhi_ep_cntrl {
+ struct device *cntrl_dev;
+ struct mhi_ep_device *mhi_dev;
+ void __iomem *mmio;
+
+ struct mhi_ep_chan *mhi_chan;
+ struct mhi_ep_event *mhi_event;
+ struct mhi_ep_cmd *mhi_cmd;
+ struct mhi_ep_sm *sm;
+
+ void (*raise_irq)(struct mhi_ep_cntrl *mhi_cntrl, u32 vector);
+ void __iomem *(*alloc_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t *phys_addr,
+ size_t size);
+ int (*map_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr, u64 pci_addr,
+ size_t size);
+ void (*free_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr,
+ void __iomem *virt_addr, size_t size);
+ void (*unmap_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr);
+ int (*read_from_host)(struct mhi_ep_cntrl *mhi_cntrl, u64 from, void __iomem *to,
+ size_t size);
+ int (*write_to_host)(struct mhi_ep_cntrl *mhi_cntrl, void __iomem *from, u64 to,
+ size_t size);
+
+ enum mhi_state mhi_state;
+
+ u32 max_chan;
+ u32 mru;
+ int index;
+};
+
+/**
+ * struct mhi_ep_device - Structure representing an MHI Endpoint device that binds
+ * to channels or is associated with controllers
+ * @dev: Driver model device node for the MHI Endpoint device
+ * @mhi_cntrl: Controller the device belongs to
+ * @id: Pointer to MHI Endpoint device ID struct
+ * @name: Name of the associated MHI Endpoint device
+ * @ul_chan: UL channel for the device
+ * @dl_chan: DL channel for the device
+ * @dev_type: MHI device type
+ */
+struct mhi_ep_device {
+ struct device dev;
+ struct mhi_ep_cntrl *mhi_cntrl;
+ const struct mhi_device_id *id;
+ const char *name;
+ struct mhi_ep_chan *ul_chan;
+ struct mhi_ep_chan *dl_chan;
+ enum mhi_device_type dev_type;
+};
+
+#define to_mhi_ep_device(dev) container_of(dev, struct mhi_ep_device, dev)
+
+/**
+ * mhi_ep_register_controller - Register MHI Endpoint controller
+ * @mhi_cntrl: MHI Endpoint controller to register
+ * @config: Configuration to use for the controller
+ *
+ * Return: 0 if controller registrations succeeds, a negative error code otherwise.
+ */
+int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
+ const struct mhi_ep_cntrl_config *config);
+
+/**
+ * mhi_ep_unregister_controller - Unregister MHI Endpoint controller
+ * @mhi_cntrl: MHI Endpoint controller to unregister
+ */
+void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl);
+
+#endif
--
2.25.1

2022-02-15 00:50:32

by Hemant Kumar

[permalink] [raw]
Subject: Re: [PATCH v3 06/25] bus: mhi: Cleanup the register definitions used in headers



On 2/12/2022 10:20 AM, Manivannan Sadhasivam wrote:
> Cleanup includes:
>
> 1. Moving the MHI register definitions to common.h header with REG_ prefix
> and using them in the host/internal.h file as an alias. This makes it
> possible to reuse the register definitions in EP stack that differs by
> a fixed offset.
> 2. Using the GENMASK macro for masks
> 3. Removing brackets for single values
> 4. Using lowercase for hex values
> 5. Using two digits for hex values where applicable
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---

Reviewed-by: Hemant Kumar <[email protected]>

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum, a Linux Foundation Collaborative Project

2022-02-15 02:03:22

by Hemant Kumar

[permalink] [raw]
Subject: Re: [PATCH v3 09/25] bus: mhi: ep: Add support for registering MHI endpoint client drivers



On 2/12/2022 10:21 AM, Manivannan Sadhasivam wrote:
> This commit adds support for registering MHI endpoint client drivers
> with the MHI endpoint stack. MHI endpoint client drivers binds to one
> or more MHI endpoint devices inorder to send and receive the upper-layer
> protocol packets like IP packets, modem control messages, and diagnostics
> messages over MHI bus.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
Reviewed-by: Hemant Kumar <[email protected]>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum, a Linux Foundation Collaborative Project

2022-02-15 05:25:42

by Hemant Kumar

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

Hi Mani,

On 2/12/2022 10:21 AM, Manivannan Sadhasivam wrote:
> This commit adds support for registering MHI endpoint controller drivers
> with the MHI endpoint stack. MHI endpoint controller drivers manages
> the interaction with the host machines such as x86. They are also the
> MHI endpoint bus master in charge of managing the physical link between the
> host and endpoint device.
>
> The endpoint controller driver encloses all information about the
> underlying physical bus like PCIe. The registration process involves
> parsing the channel configuration and allocating an MHI EP device.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> drivers/bus/mhi/Kconfig | 1 +
> drivers/bus/mhi/Makefile | 3 +
> drivers/bus/mhi/ep/Kconfig | 10 ++
> drivers/bus/mhi/ep/Makefile | 2 +
> drivers/bus/mhi/ep/internal.h | 160 +++++++++++++++++++++++
> drivers/bus/mhi/ep/main.c | 234 ++++++++++++++++++++++++++++++++++
> include/linux/mhi_ep.h | 143 +++++++++++++++++++++
> 7 files changed, 553 insertions(+)
> create mode 100644 drivers/bus/mhi/ep/Kconfig
> create mode 100644 drivers/bus/mhi/ep/Makefile
> create mode 100644 drivers/bus/mhi/ep/internal.h
> create mode 100644 drivers/bus/mhi/ep/main.c
> create mode 100644 include/linux/mhi_ep.h
>
> diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig
> index 4748df7f9cd5..b39a11e6c624 100644
> --- a/drivers/bus/mhi/Kconfig
> +++ b/drivers/bus/mhi/Kconfig
> @@ -6,3 +6,4 @@
> #
>
> source "drivers/bus/mhi/host/Kconfig"
> +source "drivers/bus/mhi/ep/Kconfig"
> diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
> index 5f5708a249f5..46981331b38f 100644
> --- a/drivers/bus/mhi/Makefile
> +++ b/drivers/bus/mhi/Makefile
> @@ -1,2 +1,5 @@
> # Host MHI stack
> obj-y += host/
> +
> +# Endpoint MHI stack
> +obj-y += ep/
> diff --git a/drivers/bus/mhi/ep/Kconfig b/drivers/bus/mhi/ep/Kconfig
> new file mode 100644
> index 000000000000..229c71397b30
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/Kconfig
> @@ -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 the host processors to control
> + and communicate with modem devices over a high speed peripheral
> + bus or shared memory.
> +
> + MHI_BUS_EP implements the MHI protocol for the endpoint devices
> + like SDX55 modem connected to the host machine over PCIe.
> diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
> new file mode 100644
> index 000000000000..64e29252b608
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
> +mhi_ep-y := main.o
> diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
> new file mode 100644
> index 000000000000..e313a2546664
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/internal.h
> @@ -0,0 +1,160 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021, Linaro Ltd.
> + *
> + */
> +
> +#ifndef _MHI_EP_INTERNAL_
> +#define _MHI_EP_INTERNAL_
> +
> +#include <linux/bitfield.h>
> +
> +#include "../common.h"
> +
> +extern struct bus_type mhi_ep_bus_type;
> +
> +#define MHI_REG_OFFSET 0x100
> +#define BHI_REG_OFFSET 0x200
> +
> +/* MHI registers */
> +#define MHIREGLEN (MHI_REG_OFFSET + REG_MHIREGLEN)
> +#define MHIVER (MHI_REG_OFFSET + REG_MHIVER)
> +#define MHICFG (MHI_REG_OFFSET + REG_MHICFG)
> +#define CHDBOFF (MHI_REG_OFFSET + REG_CHDBOFF)
> +#define ERDBOFF (MHI_REG_OFFSET + REG_ERDBOFF)
> +#define BHIOFF (MHI_REG_OFFSET + REG_BHIOFF)
> +#define BHIEOFF (MHI_REG_OFFSET + REG_BHIEOFF)
> +#define DEBUGOFF (MHI_REG_OFFSET + REG_DEBUGOFF)
> +#define MHICTRL (MHI_REG_OFFSET + REG_MHICTRL)
> +#define MHISTATUS (MHI_REG_OFFSET + REG_MHISTATUS)
> +#define CCABAP_LOWER (MHI_REG_OFFSET + REG_CCABAP_LOWER)
> +#define CCABAP_HIGHER (MHI_REG_OFFSET + REG_CCABAP_HIGHER)
> +#define ECABAP_LOWER (MHI_REG_OFFSET + REG_ECABAP_LOWER)
> +#define ECABAP_HIGHER (MHI_REG_OFFSET + REG_ECABAP_HIGHER)
> +#define CRCBAP_LOWER (MHI_REG_OFFSET + REG_CRCBAP_LOWER)
> +#define CRCBAP_HIGHER (MHI_REG_OFFSET + REG_CRCBAP_HIGHER)
> +#define CRDB_LOWER (MHI_REG_OFFSET + REG_CRDB_LOWER)
> +#define CRDB_HIGHER (MHI_REG_OFFSET + REG_CRDB_HIGHER)
> +#define MHICTRLBASE_LOWER (MHI_REG_OFFSET + REG_MHICTRLBASE_LOWER)
> +#define MHICTRLBASE_HIGHER (MHI_REG_OFFSET + REG_MHICTRLBASE_HIGHER)
> +#define MHICTRLLIMIT_LOWER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_LOWER)
> +#define MHICTRLLIMIT_HIGHER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_HIGHER)
> +#define MHIDATABASE_LOWER (MHI_REG_OFFSET + REG_MHIDATABASE_LOWER)
> +#define MHIDATABASE_HIGHER (MHI_REG_OFFSET + REG_MHIDATABASE_HIGHER)
> +#define MHIDATALIMIT_LOWER (MHI_REG_OFFSET + REG_MHIDATALIMIT_LOWER)
> +#define MHIDATALIMIT_HIGHER (MHI_REG_OFFSET + REG_MHIDATALIMIT_HIGHER)
> +
> +/* MHI BHI registers */
> +#define BHI_IMGTXDB (BHI_REG_OFFSET + REG_BHI_IMGTXDB)
> +#define BHI_EXECENV (BHI_REG_OFFSET + REG_BHI_EXECENV)
> +#define BHI_INTVEC (BHI_REG_OFFSET + REG_BHI_INTVEC)
> +
> +/* 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_A7 0x4
can we get rid of all instances of "_A7" as this corresponds to
Cortex-A7, in future this can change? At MHI core layer, we can avoid
this naming convetion, even though register names are inculding them now
and may change to something different later. This MHI EP driver would
still be used for those new cortex vers.
> +#define MHI_CTRL_INT_STATUS_A7_MSK BIT(0)
> +#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
> +#define MHI_CHDB_INT_STATUS_A7_n(n) (0x28 + 0x4 * (n))
> +#define MHI_ERDB_INT_STATUS_A7_n(n) (0x38 + 0x4 * (n))
> +
[..]

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum, a Linux Foundation Collaborative Project

2022-02-15 22:41:11

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 00/25] Add initial support for MHI endpoint stack

On 2/12/22 12:20 PM, Manivannan Sadhasivam wrote:
> Hello,
>
> This series adds initial support for the Qualcomm specific Modem Host Interface
> (MHI) bus in endpoint devices like SDX55 modems. The MHI bus in endpoint devices
> communicates with the MHI bus in host machines like x86 over any physical bus
> like PCIe. The MHI host support is already in mainline [1] and been used by PCIe
> based modems and WLAN devices running vendor code (downstream).

Maybe "running (downstream) vendor code".



I have a few general comments, which I'll mention here.

- This description goes out of its way to say MHI *could* be used over
almost any transport, and PCIe just happens to be one of them. The
reality is, you are only supporting it over PCIe, and as far as I
know you have no plans to go beyond that. Even if you did, I think
it should be clearer that you are doing MHI support over PCIe, even
though other options are possible (and could be incorporated in the
future).
- The first two patches are bug fixes; I think you should send those
out right away, without waiting for the entire series to get accepted.
- But ideally, can we get a "Tested-by" tag on these first?
- The next several, maybe up to patch 7, are also sort of preparatory
for the "real" code you're adding. Maybe those could be sent out
early/separately too, knowing that the end goal is to get the MHI
endpoint support accepted.
- Given the endianness issues (which I pointed out last time, but
which seem to be addressed), are you able to test this code using
a host that has different endianness than the modem CPU (SDX55)?
- Paul Davey seems to have the ability to test this.
- I have a few very minor suggestions in the wording below.
- You really need a picture to make it easier to see at a glance what
the hardware model is. Here's one I did at one point, but it also
includes the IPA in it (which is the FUUUUTURE!!!). The SDX55 AP
controls the PCIe endpoint.

.................... ..................................
: "Intel host" : : "SDX55" :
: : : ------------ :
: : : | SDX55 AP | :
: : : ------------ :
: : | | : | :
: -------- (root complex) |P| (endpoint) ------- --------- :
: | Host |----------------|C|------------| IPA |-------| Modem | :
: -------- : |I| : ------- --------- :
:..................: |e| :................................:
| |

Something this picture does not show is that the transfer,
command and event rings (and buffers) reside in host memory,
while information *about* those rings (size, location, and
current read/write pointers) reside in PCIe memory.

> Overview
> ========
>
> This series aims at adding the MHI support in the endpoint devices with the goal

This series adds the MHI support...

> of getting data connectivity using the mainline kernel running on the modems.
> Modems here refer to the combination of an APPS processor (Cortex A grade) and
> a baseband processor (DSP). The MHI bus is located in the APPS processor and it
> transfers data packets from the baseband processor to the host machine.
>
> The MHI Endpoint (MHI EP) stack proposed here is inspired by the downstream
> code written by Qualcomm. But the complete stack is mostly re-written to adapt
> to the "bus" framework and made it modular so that it can work with the upstream

...framework to make it modular, so that...

> subsystems like "PCI Endpoint". The code structure of the MHI endpoint stack
> follows the MHI host stack to maintain uniformity.
>
> With this initial MHI EP stack (along with few other drivers), we can establish
> the network interface between host and endpoint over the MHI software channels
> (IP_SW0) and can do things like IP forwarding, SSH, etc...
>
> Stack Organization
> ==================
>
> The MHI EP stack has the concept of controller and device drivers as like the
> MHI host stack. The MHI EP controller driver can be a PCI Endpoint Function
> driver and the MHI device driver can be a MHI EP Networking driver or QRTR
> driver. The MHI EP controller driver is tied to the PCI Endpoint subsystem and
> handles all bus related activities like mapping the host memory, raising IRQ,
> passing link specific events etc... The MHI EP networking driver is tied to the
> Networking stack and handles all networking related activities like
> sending/receiving the SKBs from netdev, statistics collection etc...
>
> This series only contains the MHI EP code, whereas the PCIe EPF driver and MHI
> EP Networking drivers are not yet submitted and can be found here [2]. Though
> the MHI EP stack doesn't have the build time dependency, it cannot function
> without them.
>
> Test setup
> ==========
>
> This series has been tested on Telit FN980 TLB board powered by Qualcomm SDX55
> (a.k.a X55 modem) and Qualcomm SM8450 based dev board.
>
> For testing the stability and performance, networking tools such as iperf, ssh
> and ping are used.
>
> Limitations
> ===========
>
> We are not _yet_ there to get the data packets from the modem as that involves
> the Qualcomm IP Accelerator (IPA) integration with MHI endpoint stack. But we
> are planning to add support for it in the coming days.

s/days/months/

And now I'm going to send this, along with my comments on the first
half of the patches. I'll keep going on the rest after that.

-Alex

>
> References
> ==========
>
> MHI bus: https://www.kernel.org/doc/html/latest/mhi/mhi.html
> Linaro connect presentation around this topic: https://connect.linaro.org/resources/lvc21f/lvc21f-222/
>
> Thanks,
> Mani
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/bus/mhi
> [2] https://git.linaro.org/landing-teams/working/qualcomm/kernel.git/log/?h=tracking-qcomlt-sdx55-drivers
>
> Changes in v3:
>
> * Splitted the patch 20/23 into two.
> * Fixed the error handling in patch 21/23.
> * Removed spurious change in patch 01/23.
> * Added check for xfer callbacks in client driver probe.
>
> Changes in v2:
>
> v2 mostly addresses the issues seen while testing the stack on SM8450 that is a
> SMP platform and also incorporates the review comments from Alex.
>
> Major changes are:
>
> * Added a cleanup patch for getting rid of SHIFT macros and used the bitfield
> operations.
> * Added the endianess patches that were submitted to MHI list and used the
> endianess conversion in EP patches also.
> * Added support for multiple event rings.
> * Fixed the MSI generation based on the event ring index.
> * Fixed the doorbell list handling by making use of list splice and not locking
> the entire list manipulation.
> * Added new APIs for wrapping the reading and writing to host memory (Dmitry).
> * Optimized the read_channel and queue_skb function logics.
> * Added Hemant's R-o-b tag.
>
> Manivannan Sadhasivam (23):
> bus: mhi: Move host MHI code to "host" directory
> bus: mhi: Move common MHI definitions out of host directory
> bus: mhi: Make mhi_state_str[] array static inline and move to
> common.h
> bus: mhi: Cleanup the register definitions used in headers
> bus: mhi: Get rid of SHIFT macros and use bitfield operations
> bus: mhi: ep: Add support for registering MHI endpoint controllers
> bus: mhi: ep: Add support for registering MHI endpoint client drivers
> bus: mhi: ep: Add support for creating and destroying MHI EP devices
> bus: mhi: ep: Add support for managing MMIO registers
> bus: mhi: ep: Add support for ring management
> bus: mhi: ep: Add support for sending events to the host
> bus: mhi: ep: Add support for managing MHI state machine
> bus: mhi: ep: Add support for processing MHI endpoint interrupts
> bus: mhi: ep: Add support for powering up the MHI endpoint stack
> bus: mhi: ep: Add support for powering down the MHI endpoint stack
> bus: mhi: ep: Add support for handling MHI_RESET
> bus: mhi: ep: Add support for handling SYS_ERR condition
> bus: mhi: ep: Add support for processing command ring
> bus: mhi: ep: Add support for reading from the host
> bus: mhi: ep: Add support for processing transfer ring
> bus: mhi: ep: Add support for queueing SKBs to the host
> bus: mhi: ep: Add support for suspending and resuming channels
> bus: mhi: ep: Add uevent support for module autoloading
>
> Paul Davey (2):
> bus: mhi: Fix pm_state conversion to string
> bus: mhi: Fix MHI DMA structure endianness
>
> drivers/bus/Makefile | 2 +-
> drivers/bus/mhi/Kconfig | 28 +-
> drivers/bus/mhi/Makefile | 9 +-
> drivers/bus/mhi/common.h | 319 ++++
> drivers/bus/mhi/ep/Kconfig | 10 +
> drivers/bus/mhi/ep/Makefile | 2 +
> drivers/bus/mhi/ep/internal.h | 254 ++++
> drivers/bus/mhi/ep/main.c | 1601 +++++++++++++++++++++
> drivers/bus/mhi/ep/mmio.c | 274 ++++
> drivers/bus/mhi/ep/ring.c | 267 ++++
> drivers/bus/mhi/ep/sm.c | 174 +++
> drivers/bus/mhi/host/Kconfig | 31 +
> drivers/bus/mhi/{core => host}/Makefile | 4 +-
> drivers/bus/mhi/{core => host}/boot.c | 17 +-
> drivers/bus/mhi/{core => host}/debugfs.c | 40 +-
> drivers/bus/mhi/{core => host}/init.c | 123 +-
> drivers/bus/mhi/{core => host}/internal.h | 427 +-----
> drivers/bus/mhi/{core => host}/main.c | 46 +-
> drivers/bus/mhi/{ => host}/pci_generic.c | 0
> drivers/bus/mhi/{core => host}/pm.c | 36 +-
> include/linux/mhi_ep.h | 293 ++++
> include/linux/mod_devicetable.h | 2 +
> scripts/mod/file2alias.c | 10 +
> 23 files changed, 3442 insertions(+), 527 deletions(-)
> create mode 100644 drivers/bus/mhi/common.h
> create mode 100644 drivers/bus/mhi/ep/Kconfig
> create mode 100644 drivers/bus/mhi/ep/Makefile
> create mode 100644 drivers/bus/mhi/ep/internal.h
> create mode 100644 drivers/bus/mhi/ep/main.c
> create mode 100644 drivers/bus/mhi/ep/mmio.c
> create mode 100644 drivers/bus/mhi/ep/ring.c
> create mode 100644 drivers/bus/mhi/ep/sm.c
> create mode 100644 drivers/bus/mhi/host/Kconfig
> rename drivers/bus/mhi/{core => host}/Makefile (54%)
> rename drivers/bus/mhi/{core => host}/boot.c (96%)
> rename drivers/bus/mhi/{core => host}/debugfs.c (90%)
> rename drivers/bus/mhi/{core => host}/init.c (93%)
> rename drivers/bus/mhi/{core => host}/internal.h (50%)
> rename drivers/bus/mhi/{core => host}/main.c (98%)
> rename drivers/bus/mhi/{ => host}/pci_generic.c (100%)
> rename drivers/bus/mhi/{core => host}/pm.c (97%)
> create mode 100644 include/linux/mhi_ep.h
>

2022-02-15 23:31:44

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> This commit adds support for registering MHI endpoint controller drivers
> with the MHI endpoint stack. MHI endpoint controller drivers manages

s/manages/manage/

> the interaction with the host machines such as x86. They are also the

(such as x86)

> MHI endpoint bus master in charge of managing the physical link between the
> host and endpoint device.
>
> The endpoint controller driver encloses all information about the
> underlying physical bus like PCIe. The registration process involves

s/like PCIe/(i.e., PCIe)/

> parsing the channel configuration and allocating an MHI EP device.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>

OK!!! On to the MHI endpoint code!

Quite a few comments below, but nothing very major.

-Alex

> ---
> drivers/bus/mhi/Kconfig | 1 +
> drivers/bus/mhi/Makefile | 3 +
> drivers/bus/mhi/ep/Kconfig | 10 ++
> drivers/bus/mhi/ep/Makefile | 2 +
> drivers/bus/mhi/ep/internal.h | 160 +++++++++++++++++++++++
> drivers/bus/mhi/ep/main.c | 234 ++++++++++++++++++++++++++++++++++
> include/linux/mhi_ep.h | 143 +++++++++++++++++++++
> 7 files changed, 553 insertions(+)
> create mode 100644 drivers/bus/mhi/ep/Kconfig
> create mode 100644 drivers/bus/mhi/ep/Makefile
> create mode 100644 drivers/bus/mhi/ep/internal.h
> create mode 100644 drivers/bus/mhi/ep/main.c
> create mode 100644 include/linux/mhi_ep.h
>
> diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig
> index 4748df7f9cd5..b39a11e6c624 100644
> --- a/drivers/bus/mhi/Kconfig
> +++ b/drivers/bus/mhi/Kconfig
> @@ -6,3 +6,4 @@
> #
>
> source "drivers/bus/mhi/host/Kconfig"
> +source "drivers/bus/mhi/ep/Kconfig"
> diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
> index 5f5708a249f5..46981331b38f 100644
> --- a/drivers/bus/mhi/Makefile
> +++ b/drivers/bus/mhi/Makefile
> @@ -1,2 +1,5 @@
> # Host MHI stack
> obj-y += host/
> +
> +# Endpoint MHI stack
> +obj-y += ep/
> diff --git a/drivers/bus/mhi/ep/Kconfig b/drivers/bus/mhi/ep/Kconfig
> new file mode 100644
> index 000000000000..229c71397b30
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/Kconfig
> @@ -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 the host processors to control

s/the host processors/a host processor/

> + and communicate with modem devices over a high speed peripheral

s/modem devices/a modem device/

> + bus or shared memory.
> +
> + MHI_BUS_EP implements the MHI protocol for the endpoint devices
> + like SDX55 modem connected to the host machine over PCIe.

s/devices like/devices, such as/

> diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
> new file mode 100644
> index 000000000000..64e29252b608
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
> +mhi_ep-y := main.o
> diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
> new file mode 100644
> index 000000000000..e313a2546664
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/internal.h
> @@ -0,0 +1,160 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021, Linaro Ltd.

Update your copyright statement (here and everywhere before you
send your next version).

> + *
> + */
> +
> +#ifndef _MHI_EP_INTERNAL_
> +#define _MHI_EP_INTERNAL_
> +
> +#include <linux/bitfield.h>
> +
> +#include "../common.h"
> +
> +extern struct bus_type mhi_ep_bus_type;
> +
> +#define MHI_REG_OFFSET 0x100
> +#define BHI_REG_OFFSET 0x200

Rather than defining the REG_OFFSET values here and adding
them to every definition below, why not have the base
address used (e.g., in mhi_write_reg_field()) be adjusted
by the constant amount?

I'm just looking at mhi_init_mmio() (in the existing code)
as an example, but for example, the base address used
comes from mhi_cntrl->regs. Can you instead just define
a pointer somewhere that is the base of the MHI register
range, which is already offset by the appropriate amount?

> +
> +/* MHI registers */
> +#define MHIREGLEN (MHI_REG_OFFSET + REG_MHIREGLEN)
> +#define MHIVER (MHI_REG_OFFSET + REG_MHIVER)
> +#define MHICFG (MHI_REG_OFFSET + REG_MHICFG)
> +#define CHDBOFF (MHI_REG_OFFSET + REG_CHDBOFF)
> +#define ERDBOFF (MHI_REG_OFFSET + REG_ERDBOFF)
> +#define BHIOFF (MHI_REG_OFFSET + REG_BHIOFF)
> +#define BHIEOFF (MHI_REG_OFFSET + REG_BHIEOFF)
> +#define DEBUGOFF (MHI_REG_OFFSET + REG_DEBUGOFF)
> +#define MHICTRL (MHI_REG_OFFSET + REG_MHICTRL)
> +#define MHISTATUS (MHI_REG_OFFSET + REG_MHISTATUS)
> +#define CCABAP_LOWER (MHI_REG_OFFSET + REG_CCABAP_LOWER)
> +#define CCABAP_HIGHER (MHI_REG_OFFSET + REG_CCABAP_HIGHER)
> +#define ECABAP_LOWER (MHI_REG_OFFSET + REG_ECABAP_LOWER)
> +#define ECABAP_HIGHER (MHI_REG_OFFSET + REG_ECABAP_HIGHER)
> +#define CRCBAP_LOWER (MHI_REG_OFFSET + REG_CRCBAP_LOWER)
> +#define CRCBAP_HIGHER (MHI_REG_OFFSET + REG_CRCBAP_HIGHER)
> +#define CRDB_LOWER (MHI_REG_OFFSET + REG_CRDB_LOWER)
> +#define CRDB_HIGHER (MHI_REG_OFFSET + REG_CRDB_HIGHER)
> +#define MHICTRLBASE_LOWER (MHI_REG_OFFSET + REG_MHICTRLBASE_LOWER)
> +#define MHICTRLBASE_HIGHER (MHI_REG_OFFSET + REG_MHICTRLBASE_HIGHER)
> +#define MHICTRLLIMIT_LOWER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_LOWER)
> +#define MHICTRLLIMIT_HIGHER (MHI_REG_OFFSET + REG_MHICTRLLIMIT_HIGHER)
> +#define MHIDATABASE_LOWER (MHI_REG_OFFSET + REG_MHIDATABASE_LOWER)
> +#define MHIDATABASE_HIGHER (MHI_REG_OFFSET + REG_MHIDATABASE_HIGHER)
> +#define MHIDATALIMIT_LOWER (MHI_REG_OFFSET + REG_MHIDATALIMIT_LOWER)
> +#define MHIDATALIMIT_HIGHER (MHI_REG_OFFSET + REG_MHIDATALIMIT_HIGHER)
> +
> +/* MHI BHI registers */
> +#define BHI_IMGTXDB (BHI_REG_OFFSET + REG_BHI_IMGTXDB)
> +#define BHI_EXECENV (BHI_REG_OFFSET + REG_BHI_EXECENV)
> +#define BHI_INTVEC (BHI_REG_OFFSET + REG_BHI_INTVEC)
> +
> +/* 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_A7 0x4
> +#define MHI_CTRL_INT_STATUS_A7_MSK BIT(0)
> +#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
> +#define MHI_CHDB_INT_STATUS_A7_n(n) (0x28 + 0x4 * (n))
> +#define MHI_ERDB_INT_STATUS_A7_n(n) (0x38 + 0x4 * (n))
> +
> +#define MHI_CTRL_INT_CLEAR_A7 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_A7_n(n) (0x70 + 0x4 * (n))
> +#define MHI_CHDB_INT_CLEAR_A7_n_CLEAR_ALL GENMASK(31, 0)
> +#define MHI_ERDB_INT_CLEAR_A7_n(n) (0x80 + 0x4 * (n))
> +#define MHI_ERDB_INT_CLEAR_A7_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_A7 0x94
> +#define MHI_CTRL_INT_MASK_A7_MASK GENMASK(1, 0)
> +#define MHI_CTRL_MHICTRL_MASK BIT(0)
> +#define MHI_CTRL_CRDB_MASK BIT(1)
> +
> +#define MHI_CHDB_INT_MASK_A7_n(n) (0xb8 + 0x4 * (n))
> +#define MHI_CHDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
> +#define MHI_ERDB_INT_MASK_A7_n(n) (0xc8 + 0x4 * (n))
> +#define MHI_ERDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
> +
> +#define NR_OF_CMD_RINGS 1
> +#define MHI_MASK_ROWS_CH_EV_DB 4
> +#define MHI_MASK_CH_EV_LEN 32
> +
> +/* Generic context */
> +struct mhi_generic_ctx {
> + __u32 reserved0;
> + __u32 reserved1;
> + __u32 reserved2;
> +
> + __u64 rbase __packed __aligned(4);
> + __u64 rlen __packed __aligned(4);
> + __u64 rp __packed __aligned(4);
> + __u64 wp __packed __aligned(4);
> +};

I'm pretty sure this constitutes an external interface, so
every field should have its endianness annotated.

Mentioned elsewhere, I think you can define the structure
with those attributes rather than the multiple fields.

> +
> +enum mhi_ep_ring_type {
> + RING_TYPE_CMD = 0,
> + RING_TYPE_ER,
> + RING_TYPE_CH,
> +};
> +
> +struct mhi_ep_ring_element {
> + u64 ptr;
> + u32 dword[2];
> +};

Are these host resident rings? Even if not, this is an external
interface, so this should be defined with explicit endianness.
The cpu_to_le64() call will be a no-op so there is no cost
to correcting this.

> +
> +/* 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 {
> + struct mhi_ep_cntrl *mhi_cntrl;
> + int (*ring_cb)(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el);
> + union mhi_ep_ring_ctx *ring_ctx;
> + struct mhi_ep_ring_element *ring_cache;
> + enum mhi_ep_ring_type type;
> + size_t rd_offset;
> + size_t wr_offset;
> + size_t ring_size;
> + u32 db_offset_h;
> + u32 db_offset_l;
> + u32 ch_id;
> +};

Not sure about the db_offset fields, etc. here, but it's possible
they need endianness annotations. I'm going to stop making this
comment; please make sure anything that's exposed to the host
specifies that it's little endian. (The host and endpoint should
have a common definition of these shared structures anyway; maybe
I'm misreading this or assuming something incorrectly.)

> +
> +struct mhi_ep_cmd {
> + struct mhi_ep_ring ring;
> +};
> +
> +struct mhi_ep_event {
> + struct mhi_ep_ring ring;
> +};
> +
> +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;
> + u64 tre_loc;
> + u32 tre_size;
> + u32 tre_bytes_left;
> + u32 chan;
> + bool skip_td;
> +};
> +
> +#endif
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> new file mode 100644
> index 000000000000..b006011d025d
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -0,0 +1,234 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MHI Bus Endpoint stack
> + *
> + * Copyright (C) 2021 Linaro Ltd.
> + * Author: Manivannan Sadhasivam <[email protected]>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/dma-direction.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mhi_ep.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include "internal.h"
> +
> +static DEFINE_IDA(mhi_ep_cntrl_ida);
> +
> +static void mhi_ep_release_device(struct device *dev)
> +{
> + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> +
> + if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> + mhi_dev->mhi_cntrl->mhi_dev = NULL;
> +
> + /*
> + * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
> + * devices for the channels will only get created during start
> + * channel if the mhi_dev associated with it is NULL.
> + */

Can you mention where in the code the above occurs? Just for
reference. Like, "will only get created in mhi_ep_create_device()
if the..." or whatever.

> + if (mhi_dev->ul_chan)
> + mhi_dev->ul_chan->mhi_dev = NULL;
> +
> + if (mhi_dev->dl_chan)
> + mhi_dev->dl_chan->mhi_dev = NULL;
> +
> + kfree(mhi_dev);
> +}
> +
> +static struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl,
> + enum mhi_device_type dev_type)
> +{
> + struct mhi_ep_device *mhi_dev;
> + struct device *dev;
> +
> + mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
> + if (!mhi_dev)
> + return ERR_PTR(-ENOMEM);
> +
> + dev = &mhi_dev->dev;
> + device_initialize(dev);
> + dev->bus = &mhi_ep_bus_type;
> + dev->release = mhi_ep_release_device;
> +

Maybe mention that the controller device is always allocated
first.

> + if (dev_type == MHI_DEVICE_CONTROLLER)
> + /* for MHI controller device, parent is the bus device (e.g. PCI EPF) */
> + dev->parent = mhi_cntrl->cntrl_dev;
> + else
> + /* for MHI client devices, parent is the MHI controller device */
> + dev->parent = &mhi_cntrl->mhi_dev->dev;
> +
> + mhi_dev->mhi_cntrl = mhi_cntrl;
> + mhi_dev->dev_type = dev_type;
> +
> + return mhi_dev;
> +}
> +

I think the name of the next function could be better. Yes, it
parses the channel configuration, but what it *really* does is
alloocate and initialize the channel array. So maybe something
more like mhi_chan_init()?

> +static int parse_ch_cfg(struct mhi_ep_cntrl *mhi_cntrl,
> + const struct mhi_ep_cntrl_config *config)
> +{
> + const struct mhi_ep_channel_config *ch_cfg;
> + struct device *dev = mhi_cntrl->cntrl_dev;
> + u32 chan, i;
> + int ret = -EINVAL;
> +
> + mhi_cntrl->max_chan = config->max_channels;
> +
> + /*
> + * Allocate max_channels supported by the MHI endpoint and populate
> + * only the defined channels
> + */
> + mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
> + GFP_KERNEL);
> + if (!mhi_cntrl->mhi_chan)
> + return -ENOMEM;
> +
> + for (i = 0; i < config->num_channels; i++) {
> + struct mhi_ep_chan *mhi_chan;

This entire block could be encapsulated in mhi_channel_add()
or something,

> + ch_cfg = &config->ch_cfg[i];

Move the above assignment down a few lines, to just before
where it's used.

> +
> + chan = ch_cfg->num;
> + if (chan >= mhi_cntrl->max_chan) {
> + dev_err(dev, "Channel %d not available\n", chan);

Maybe report the maximum channel so it's obvious why it's
not available.

> + goto error_chan_cfg;
> + }
> +
> + /* Bi-directional and direction less channels are not supported */
> + if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
> + dev_err(dev, "Invalid channel configuration\n");

Maybe be more specific in your message about what's wrong here.

> + goto error_chan_cfg;
> + }
> +
> + mhi_chan = &mhi_cntrl->mhi_chan[chan];
> + mhi_chan->name = ch_cfg->name;
> + mhi_chan->chan = chan;
> + mhi_chan->dir = ch_cfg->dir;
> + mutex_init(&mhi_chan->lock);
> + }
> +
> + return 0;
> +
> +error_chan_cfg:
> + kfree(mhi_cntrl->mhi_chan);

I'm not sure what the caller does, but maybe null this
after it's freed, or don't assign mhi_cntrll->mhi_chan
until the initialization is successful.


> + return ret;
> +}
> +
> +/*
> + * Allocate channel and command rings here. Event rings will be allocated
> + * in mhi_ep_power_up() as the config comes from the host.
> + */
> +int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> + const struct mhi_ep_cntrl_config *config)
> +{
> + struct mhi_ep_device *mhi_dev;
> + int ret;
> +
> + if (!mhi_cntrl || !mhi_cntrl->cntrl_dev)
> + return -EINVAL;
> +
> + ret = parse_ch_cfg(mhi_cntrl, config);
> + if (ret)
> + return ret;
> +
> + mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);

I said before I thought it was silly to even define NR_OF_CMD_RINGS.
Does the MHI specification actually allow more than one command
ring for a given MHI controller? Ever?

> + if (!mhi_cntrl->mhi_cmd) {
> + ret = -ENOMEM;
> + goto err_free_ch;
> + }
> +
> + /* Set controller index */
> + mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
> + if (mhi_cntrl->index < 0) {
> + ret = mhi_cntrl->index;
> + goto err_free_cmd;
> + }
> +
> + /* Allocate the controller device */
> + mhi_dev = mhi_ep_alloc_device(mhi_cntrl, MHI_DEVICE_CONTROLLER);
> + if (IS_ERR(mhi_dev)) {
> + dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n");
> + ret = PTR_ERR(mhi_dev);
> + goto err_ida_free;
> + }
> +
> + dev_set_name(&mhi_dev->dev, "mhi_ep%d", mhi_cntrl->index);
> + mhi_dev->name = dev_name(&mhi_dev->dev);
> +
> + ret = device_add(&mhi_dev->dev);
> + if (ret)
> + goto err_put_dev;
> +

Should the mhi_dev pointer be set before device_add() gets called?

> + mhi_cntrl->mhi_dev = mhi_dev;
> +
> + dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n");
> +
> + return 0;
> +
> +err_put_dev:
> + put_device(&mhi_dev->dev);
> +err_ida_free:
> + ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
> +err_free_cmd:
> + kfree(mhi_cntrl->mhi_cmd);
> +err_free_ch:
> + kfree(mhi_cntrl->mhi_chan);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(mhi_ep_register_controller);
> +
> +void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
> +{
> + struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
> +
> + kfree(mhi_cntrl->mhi_cmd);
> + kfree(mhi_cntrl->mhi_chan);
> +
> + device_del(&mhi_dev->dev);
> + put_device(&mhi_dev->dev);
> +
> + ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
> +}
> +EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
> +
> +static int mhi_ep_match(struct device *dev, struct device_driver *drv)
> +{
> + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> +
> + /*
> + * If the device is a controller type then there is no client driver
> + * associated with it
> + */
> + if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> + return 0;
> +
> + return 0;
> +};
> +
> +struct bus_type mhi_ep_bus_type = {
> + .name = "mhi_ep",
> + .dev_name = "mhi_ep",
> + .match = mhi_ep_match,
> +};
> +
> +static int __init mhi_ep_init(void)
> +{
> + return bus_register(&mhi_ep_bus_type);
> +}
> +
> +static void __exit mhi_ep_exit(void)
> +{
> + bus_unregister(&mhi_ep_bus_type);
> +}
> +
> +postcore_initcall(mhi_ep_init);
> +module_exit(mhi_ep_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MHI Bus Endpoint stack");
> +MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> new file mode 100644
> index 000000000000..20238e9df1b3
> --- /dev/null
> +++ b/include/linux/mhi_ep.h
> @@ -0,0 +1,143 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021, Linaro Ltd.
> + *
> + */
> +#ifndef _MHI_EP_H_
> +#define _MHI_EP_H_
> +
> +#include <linux/dma-direction.h>
> +#include <linux/mhi.h>
> +
> +#define MHI_EP_DEFAULT_MTU 0x8000
> +
> +/**
> + * struct mhi_ep_channel_config - Channel configuration structure for controller
> + * @name: The name of this channel
> + * @num: The number assigned to this channel
> + * @num_elements: The number of elements that can be queued to this channel
> + * @dir: Direction that data may flow on this channel
> + */
> +struct mhi_ep_channel_config {
> + char *name;
> + u32 num;
> + u32 num_elements;
> + enum dma_data_direction dir;
> +};
> +
> +/**
> + * struct mhi_ep_cntrl_config - MHI Endpoint controller configuration
> + * @max_channels: Maximum number of channels supported
> + * @num_channels: Number of channels defined in @ch_cfg
> + * @ch_cfg: Array of defined channels
> + * @mhi_version: MHI spec version supported by the controller
> + */
> +struct mhi_ep_cntrl_config {
> + u32 max_channels;
> + u32 num_channels;
> + const struct mhi_ep_channel_config *ch_cfg;
> + u32 mhi_version;

Put mhi_version first?

> +};
> +
> +/**
> + * struct mhi_ep_db_info - MHI Endpoint doorbell info
> + * @mask: Mask of the doorbell interrupt
> + * @status: Status of the doorbell interrupt
> + */
> +struct mhi_ep_db_info {
> + u32 mask;
> + u32 status;
> +};
> +
> +/**
> + * struct mhi_ep_cntrl - MHI Endpoint controller structure
> + * @cntrl_dev: Pointer to the struct device of physical bus acting as the MHI
> + * Endpoint controller
> + * @mhi_dev: MHI Endpoint device instance for the controller
> + * @mmio: MMIO region containing the MHI registers
> + * @mhi_chan: Points to the channel configuration table
> + * @mhi_event: Points to the event ring configurations table
> + * @mhi_cmd: Points to the command ring configurations table
> + * @sm: MHI Endpoint state machine
> + * @raise_irq: CB function for raising IRQ to the host
> + * @alloc_addr: CB function for allocating memory in endpoint for storing host context
> + * @map_addr: CB function for mapping host context to endpoint
> + * @free_addr: CB function to free the allocated memory in endpoint for storing host context
> + * @unmap_addr: CB function to unmap the host context in endpoint
> + * @read_from_host: CB function for reading from host memory from endpoint
> + * @write_to_host: CB function for writing to host memory from endpoint
> + * @mhi_state: MHI Endpoint state
> + * @max_chan: Maximum channels supported by the endpoint controller
> + * @mru: MRU (Maximum Receive Unit) value of the endpoint controller
> + * @index: MHI Endpoint controller index
> + */
> +struct mhi_ep_cntrl {
> + struct device *cntrl_dev;
> + struct mhi_ep_device *mhi_dev;
> + void __iomem *mmio;
> +
> + struct mhi_ep_chan *mhi_chan;
> + struct mhi_ep_event *mhi_event;
> + struct mhi_ep_cmd *mhi_cmd;
> + struct mhi_ep_sm *sm;
> +
> + void (*raise_irq)(struct mhi_ep_cntrl *mhi_cntrl, u32 vector);
> + void __iomem *(*alloc_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t *phys_addr,
> + size_t size);
> + int (*map_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr, u64 pci_addr,
> + size_t size);
> + void (*free_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr,
> + void __iomem *virt_addr, size_t size);
> + void (*unmap_addr)(struct mhi_ep_cntrl *mhi_cntrl, phys_addr_t phys_addr);
> + int (*read_from_host)(struct mhi_ep_cntrl *mhi_cntrl, u64 from, void __iomem *to,
> + size_t size);
> + int (*write_to_host)(struct mhi_ep_cntrl *mhi_cntrl, void __iomem *from, u64 to,
> + size_t size);
> +
> + enum mhi_state mhi_state;
> +
> + u32 max_chan;
> + u32 mru;
> + int index;

Will index ever be negative?

> +};
> +
> +/**
> + * struct mhi_ep_device - Structure representing an MHI Endpoint device that binds
> + * to channels or is associated with controllers
> + * @dev: Driver model device node for the MHI Endpoint device
> + * @mhi_cntrl: Controller the device belongs to
> + * @id: Pointer to MHI Endpoint device ID struct
> + * @name: Name of the associated MHI Endpoint device
> + * @ul_chan: UL channel for the device
> + * @dl_chan: DL channel for the device
> + * @dev_type: MHI device type
> + */
> +struct mhi_ep_device {
> + struct device dev;
> + struct mhi_ep_cntrl *mhi_cntrl;
> + const struct mhi_device_id *id;
> + const char *name;
> + struct mhi_ep_chan *ul_chan;
> + struct mhi_ep_chan *dl_chan;
> + enum mhi_device_type dev_type;

There are two device types, controller and transfer. Unless
there is ever going to be anything more than that, I think
the distinction is better represented as a Boolean, such as:

bool controller;

> +};
> +
> +#define to_mhi_ep_device(dev) container_of(dev, struct mhi_ep_device, dev)
> +
> +/**
> + * mhi_ep_register_controller - Register MHI Endpoint controller
> + * @mhi_cntrl: MHI Endpoint controller to register
> + * @config: Configuration to use for the controller
> + *
> + * Return: 0 if controller registrations succeeds, a negative error code otherwise.
> + */
> +int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> + const struct mhi_ep_cntrl_config *config);
> +
> +/**
> + * mhi_ep_unregister_controller - Unregister MHI Endpoint controller
> + * @mhi_cntrl: MHI Endpoint controller to unregister
> + */
> +void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl);
> +
> +#endif

2022-02-16 06:01:05

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 09/25] bus: mhi: ep: Add support for registering MHI endpoint client drivers

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> This commit adds support for registering MHI endpoint client drivers
> with the MHI endpoint stack. MHI endpoint client drivers binds to one

s/binds/bind/

> or more MHI endpoint devices inorder to send and receive the upper-layer
> protocol packets like IP packets, modem control messages, and diagnostics
> messages over MHI bus.

I have a few more comments here but generally this looks good.

-Alex

> Signed-off-by: Manivannan Sadhasivam <[email protected]>
> ---
> drivers/bus/mhi/ep/main.c | 86 +++++++++++++++++++++++++++++++++++++++
> include/linux/mhi_ep.h | 53 ++++++++++++++++++++++++
> 2 files changed, 139 insertions(+)
>
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index b006011d025d..f66404181972 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -196,9 +196,89 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
> }
> EXPORT_SYMBOL_GPL(mhi_ep_unregister_controller);
>
> +static int mhi_ep_driver_probe(struct device *dev)
> +{
> + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> + struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
> + struct mhi_ep_chan *ul_chan = mhi_dev->ul_chan;
> + struct mhi_ep_chan *dl_chan = mhi_dev->dl_chan;
> +
> + /* Client drivers should have callbacks for both channels */
> + if (!mhi_drv->ul_xfer_cb || !mhi_drv->dl_xfer_cb)
> + return -EINVAL;
> +
> + ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
> + dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
> +
> + return mhi_drv->probe(mhi_dev, mhi_dev->id);
> +}
> +
> +static int mhi_ep_driver_remove(struct device *dev)
> +{
> + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> + struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
> + struct mhi_result result = {};
> + struct mhi_ep_chan *mhi_chan;
> + int dir;
> +
> + /* Skip if it is a controller device */
> + if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> + return 0;
> +

It would be my preference to encapsulate the body of the
following loop into a called function, then call that once
for the UL channel and once for the DL channel.

> + /* Disconnect the channels associated with the driver */
> + for (dir = 0; dir < 2; dir++) {
> + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
> +
> + if (!mhi_chan)
> + continue;
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Send channel disconnect status to the client driver */
> + if (mhi_chan->xfer_cb) {
> + result.transaction_status = -ENOTCONN;
> + result.bytes_xferd = 0;
> + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);

It appears the result is ignored here. If so, can we
define the xfer_cb() function so that a NULL pointer may
be supplied by the caller in cases like this?

> + }
> +
> + /* Set channel state to DISABLED */

That comment is a little tautological. Just omit it.

> + mhi_chan->state = MHI_CH_STATE_DISABLED;
> + mhi_chan->xfer_cb = NULL;
> + mutex_unlock(&mhi_chan->lock);
> + }
> +
> + /* Remove the client driver now */
> + mhi_drv->remove(mhi_dev);
> +
> + return 0;
> +}
> +
> +int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner)
> +{
> + struct device_driver *driver = &mhi_drv->driver;
> +
> + if (!mhi_drv->probe || !mhi_drv->remove)
> + return -EINVAL;
> +
> + driver->bus = &mhi_ep_bus_type;
> + driver->owner = owner;
> + driver->probe = mhi_ep_driver_probe;
> + driver->remove = mhi_ep_driver_remove;
> +
> + return driver_register(driver);
> +}
> +EXPORT_SYMBOL_GPL(__mhi_ep_driver_register);
> +
> +void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv)
> +{
> + driver_unregister(&mhi_drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(mhi_ep_driver_unregister);
> +
> static int mhi_ep_match(struct device *dev, struct device_driver *drv)
> {
> struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> + struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(drv);
> + const struct mhi_device_id *id;
>
> /*
> * If the device is a controller type then there is no client driver
> @@ -207,6 +287,12 @@ static int mhi_ep_match(struct device *dev, struct device_driver *drv)
> if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> return 0;
>
> + for (id = mhi_drv->id_table; id->chan[0]; id++)
> + if (!strcmp(mhi_dev->name, id->chan)) {
> + mhi_dev->id = id;
> + return 1;
> + }
> +
> return 0;
> };
>
> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> index 20238e9df1b3..da865f9d3646 100644
> --- a/include/linux/mhi_ep.h
> +++ b/include/linux/mhi_ep.h
> @@ -122,7 +122,60 @@ struct mhi_ep_device {
> enum mhi_device_type dev_type;
> };
>
> +/**
> + * struct mhi_ep_driver - Structure representing a MHI Endpoint client driver
> + * @id_table: Pointer to MHI Endpoint device ID table
> + * @driver: Device driver model driver
> + * @probe: CB function for client driver probe function
> + * @remove: CB function for client driver remove function
> + * @ul_xfer_cb: CB function for UL data transfer
> + * @dl_xfer_cb: CB function for DL data transfer
> + */
> +struct mhi_ep_driver {
> + const struct mhi_device_id *id_table;
> + struct device_driver driver;
> + int (*probe)(struct mhi_ep_device *mhi_ep,
> + const struct mhi_device_id *id);
> + void (*remove)(struct mhi_ep_device *mhi_ep);

I get confused by the "ul" versus "dl" naming scheme here.
Is "ul" from the perspective of the host, meaning upload
is from the host toward the WWAN network (and therefore
toward the SDX AP), and download is from the WWAN toward
the host? Somewhere this should be stated clearly in
comments; maybe I just missed it.

> + void (*ul_xfer_cb)(struct mhi_ep_device *mhi_dev,
> + struct mhi_result *result);
> + void (*dl_xfer_cb)(struct mhi_ep_device *mhi_dev,
> + struct mhi_result *result);
> +};
> +
> #define to_mhi_ep_device(dev) container_of(dev, struct mhi_ep_device, dev)
> +#define to_mhi_ep_driver(drv) container_of(drv, struct mhi_ep_driver, driver)
> +
> +/*
> + * module_mhi_ep_driver() - Helper macro for drivers that don't do
> + * anything special other than using default mhi_ep_driver_register() and
> + * mhi_ep_driver_unregister(). This eliminates a lot of boilerplate.
> + * Each module may only use this macro once.
> + */
> +#define module_mhi_ep_driver(mhi_drv) \
> + module_driver(mhi_drv, mhi_ep_driver_register, \
> + mhi_ep_driver_unregister)
> +
> +/*
> + * Macro to avoid include chaining to get THIS_MODULE
> + */
> +#define mhi_ep_driver_register(mhi_drv) \
> + __mhi_ep_driver_register(mhi_drv, THIS_MODULE)
> +
> +/**
> + * __mhi_ep_driver_register - Register a driver with MHI Endpoint bus
> + * @mhi_drv: Driver to be associated with the device
> + * @owner: The module owner
> + *
> + * Return: 0 if driver registrations succeeds, a negative error code otherwise.
> + */
> +int __mhi_ep_driver_register(struct mhi_ep_driver *mhi_drv, struct module *owner);
> +
> +/**
> + * mhi_ep_driver_unregister - Unregister a driver from MHI Endpoint bus
> + * @mhi_drv: Driver associated with the device
> + */
> +void mhi_ep_driver_unregister(struct mhi_ep_driver *mhi_drv);
>
> /**
> * mhi_ep_register_controller - Register MHI Endpoint controller

2022-02-16 06:19:20

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 24/25] bus: mhi: ep: Add support for suspending and resuming channels

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> Add support for suspending and resuming the channels in MHI endpoint stack.
> The channels will be moved to the suspended state during M3 state
> transition and will be resumed during M0 transition.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>

Looks good.

Reviewed-by: Alex Elder <[email protected]>

> ---
> drivers/bus/mhi/ep/internal.h | 2 ++
> drivers/bus/mhi/ep/main.c | 58 +++++++++++++++++++++++++++++++++++
> drivers/bus/mhi/ep/sm.c | 4 +++
> 3 files changed, 64 insertions(+)
>
> diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
> index 8654af7caf40..e23d2fd04282 100644
> --- a/drivers/bus/mhi/ep/internal.h
> +++ b/drivers/bus/mhi/ep/internal.h
> @@ -242,6 +242,8 @@ 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);
>
> /* MHI EP memory management functions */
> int mhi_ep_alloc_map(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, size_t size,
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index e4186b012257..315409705b91 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -1106,6 +1106,64 @@ void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl)
> }
> EXPORT_SYMBOL_GPL(mhi_ep_power_down);
>
> +void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl)
> +{
> + struct mhi_ep_chan *mhi_chan;
> + u32 tmp;
> + int i;
> +
> + for (i = 0; i < mhi_cntrl->max_chan; i++) {
> + mhi_chan = &mhi_cntrl->mhi_chan[i];
> +
> + if (!mhi_chan->mhi_dev)
> + continue;
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Skip if the channel is not currently running */
> + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
> + if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_RUNNING) {
> + mutex_unlock(&mhi_chan->lock);
> + continue;
> + }
> +
> + dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n");
> + /* Set channel state to SUSPENDED */
> + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED);

Somebody really needs to write a FIELD_UPDATE() macro to
do this read/modify/write pattern.

> + mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
> + mutex_unlock(&mhi_chan->lock);
> + }
> +}
> +
> +void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl)
> +{
> + struct mhi_ep_chan *mhi_chan;
> + u32 tmp;
> + int i;
> +
> + for (i = 0; i < mhi_cntrl->max_chan; i++) {
> + mhi_chan = &mhi_cntrl->mhi_chan[i];
> +
> + if (!mhi_chan->mhi_dev)
> + continue;
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Skip if the channel is not currently suspended */
> + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[i].chcfg);
> + if (FIELD_GET(CHAN_CTX_CHSTATE_MASK, tmp) != MHI_CH_STATE_SUSPENDED) {
> + mutex_unlock(&mhi_chan->lock);
> + continue;
> + }
> +
> + dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n");
> + /* Set channel state to RUNNING */
> + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
> + mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp);
> + mutex_unlock(&mhi_chan->lock);
> + }
> +}
> +
> static void mhi_ep_release_device(struct device *dev)
> {
> struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c
> index 9a75ecfe1adf..e24ba2d85e13 100644
> --- a/drivers/bus/mhi/ep/sm.c
> +++ b/drivers/bus/mhi/ep/sm.c
> @@ -88,8 +88,11 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
> enum mhi_state old_state;
> int ret;
>
> + /* If MHI is in M3, resume suspended channels */
> spin_lock_bh(&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) {
> @@ -135,6 +138,7 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
> }
>
> spin_unlock_bh(&mhi_cntrl->state_lock);
> + 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);

2022-02-16 07:18:44

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 21/25] bus: mhi: ep: Add support for reading from the host

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> Data transfer between host and the ep device happens over the transfer
> ring associated with each bi-directional channel pair. Host defines the
> transfer ring by allocating memory for it. The read and write pointer
> addresses of the transfer ring are stored in the channel context.
>
> Once host places the elements in the transfer ring, it increments the
> write pointer and rings the channel doorbell. Device will receive the
> doorbell interrupt and will process the transfer ring elements.
>
> This commit adds support for reading the transfer ring elements from
> the transfer ring till write pointer, incrementing the read pointer and
> finally sending the completion event to the host through corresponding
> event ring.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>

Indentation nits mentioned.

Reviewed-by: Alex Elder <[email protected]>

> ---
> drivers/bus/mhi/ep/main.c | 103 ++++++++++++++++++++++++++++++++++++++
> include/linux/mhi_ep.h | 9 ++++
> 2 files changed, 112 insertions(+)
>
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index 4c2ee517832c..b937c6cda9ba 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -336,6 +336,109 @@ int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ep_ring_element
> return ret;
> }
>
> +bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir)
> +{
> + struct mhi_ep_chan *mhi_chan = (dir == DMA_FROM_DEVICE) ? mhi_dev->dl_chan :
> + mhi_dev->ul_chan;
> + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
> + struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring;
> +
> + return !!(ring->rd_offset == ring->wr_offset);
> +}
> +EXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty);
> +
> +static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
> + struct mhi_ep_ring *ring,
> + struct mhi_result *result,
> + u32 len)
> +{
> + struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
> + size_t bytes_to_read, read_offset, write_offset;
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + struct mhi_ep_ring_element *el;
> + bool td_done = false;
> + void *write_to_loc;
> + u64 read_from_loc;
> + u32 buf_remaining;
> + int ret;
> +
> + buf_remaining = len;
> +
> + do {
> + /* Don't process the transfer ring if the channel is not in RUNNING state */
> + if (mhi_chan->state != MHI_CH_STATE_RUNNING)
> + return -ENODEV;
> +
> + el = &ring->ring_cache[ring->rd_offset];
> +
> + /* Check if there is data pending to be read from previous read operation */
> + if (mhi_chan->tre_bytes_left) {
> + dev_dbg(dev, "TRE bytes remaining: %d\n", mhi_chan->tre_bytes_left);
> + bytes_to_read = min(buf_remaining, mhi_chan->tre_bytes_left);
> + } else {
> + mhi_chan->tre_loc = MHI_EP_TRE_GET_PTR(el);
> + mhi_chan->tre_size = MHI_EP_TRE_GET_LEN(el);
> + mhi_chan->tre_bytes_left = mhi_chan->tre_size;
> +
> + bytes_to_read = min(buf_remaining, mhi_chan->tre_size);
> + }
> +
> + read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
> + write_offset = len - buf_remaining;
> + read_from_loc = mhi_chan->tre_loc + read_offset;
> + write_to_loc = result->buf_addr + write_offset;
> +
> + dev_dbg(dev, "Reading %zd bytes from channel (%d)\n", bytes_to_read, ring->ch_id);
> + ret = mhi_cntrl->read_from_host(mhi_cntrl, read_from_loc, write_to_loc,
> + bytes_to_read);
> + if (ret < 0)
> + return ret;
> +
> + buf_remaining -= bytes_to_read;
> + mhi_chan->tre_bytes_left -= bytes_to_read;
> +
> + /*
> + * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been
> + * read completely:
> + *
> + * 1. Send completion event to the host based on the flags set in TRE.
> + * 2. Increment the local read offset of the transfer ring.

Your comments in this section explain some things that
I did not completely understand for a *very* long time.
The same flags are used in IPA, but are not as well
documented as they are for MHI.

> + */
> + if (!mhi_chan->tre_bytes_left) {
> + /*
> + * The host will split the data packet into multiple TREs if it can't fit
> + * the packet in a single TRE. In that case, CHAIN flag will be set by the
> + * host for all TREs except the last one.
> + */
> + if (MHI_EP_TRE_GET_CHAIN(el)) {
> + /*
> + * IEOB (Interrupt on End of Block) flag will be set by the host if
> + * it expects the completion event for all TREs of a TD.
> + */
> + if (MHI_EP_TRE_GET_IEOB(el))
> + mhi_ep_send_completion_event(mhi_cntrl,
> + ring, MHI_EP_TRE_GET_LEN(el), MHI_EV_CC_EOB);

Check your indentation above.

> + } else {
> + /*
> + * IEOT (Interrupt on End of Transfer) flag will be set by the host
> + * for the last TRE of the TD and expects the completion event for
> + * the same.
> + */
> + if (MHI_EP_TRE_GET_IEOT(el))
> + mhi_ep_send_completion_event(mhi_cntrl,
> + ring, MHI_EP_TRE_GET_LEN(el), MHI_EV_CC_EOT);

Indentation here too.

> + td_done = true;
> + }
> +
> + mhi_ep_ring_inc_index(ring);
> + }
> +
> + result->bytes_xferd += bytes_to_read;
> + } while (buf_remaining && !td_done);
> +
> + return 0;
> +}
> +
> static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
> {
> struct device *dev = &mhi_cntrl->mhi_dev->dev;
> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> index 276d29fef465..aaf4b6942037 100644
> --- a/include/linux/mhi_ep.h
> +++ b/include/linux/mhi_ep.h
> @@ -268,4 +268,13 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl);
> */
> void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl);
>
> +/**
> + * mhi_ep_queue_is_empty - Determine whether the transfer queue is empty
> + * @mhi_dev: Device associated with the channels
> + * @dir: DMA direction for the channel
> + *
> + * Return: true if the queue is empty, false otherwise.
> + */
> +bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_direction dir);
> +
> #endif

2022-02-16 07:34:33

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 14/25] bus: mhi: ep: Add support for managing MHI state machine

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> Add support for managing the MHI state machine by controlling the state
> transitions. Only the following MHI state transitions are supported:
>
> 1. Ready state
> 2. M0 state
> 3. M3 state
> 4. SYS_ERR state
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>

Minor suggestions here. -Alex

> ---
> drivers/bus/mhi/ep/Makefile | 2 +-
> drivers/bus/mhi/ep/internal.h | 11 +++
> drivers/bus/mhi/ep/main.c | 51 ++++++++++-
> drivers/bus/mhi/ep/sm.c | 168 ++++++++++++++++++++++++++++++++++
> include/linux/mhi_ep.h | 6 ++
> 5 files changed, 236 insertions(+), 2 deletions(-)
> create mode 100644 drivers/bus/mhi/ep/sm.c
>
> diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
> index 7ba0e04801eb..aad85f180b70 100644
> --- a/drivers/bus/mhi/ep/Makefile
> +++ b/drivers/bus/mhi/ep/Makefile
> @@ -1,2 +1,2 @@
> obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
> -mhi_ep-y := main.o mmio.o ring.o
> +mhi_ep-y := main.o mmio.o ring.o sm.o
> diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
> index fd63f79c6aec..e4e8f06c2898 100644
> --- a/drivers/bus/mhi/ep/internal.h
> +++ b/drivers/bus/mhi/ep/internal.h
> @@ -173,6 +173,11 @@ 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;
> @@ -230,5 +235,11 @@ 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_ep_execenv 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);
>
> #endif
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index 61f066c6286b..ccb3c2795041 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -185,6 +185,43 @@ static void mhi_ep_ring_worker(struct work_struct *work)
> }
> }
>
> +static void mhi_ep_state_worker(struct work_struct *work)
> +{
> + struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + struct mhi_ep_state_transition *itr, *tmp;
> + unsigned long flags;
> + LIST_HEAD(head);
> + int ret;
> +
> + spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
> + list_splice_tail_init(&mhi_cntrl->st_transition_list, &head);
> + spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
> +
> + list_for_each_entry_safe(itr, tmp, &head, node) {
> + list_del(&itr->node);
> + dev_dbg(dev, "Handling MHI state transition to %s\n",
> + mhi_state_str(itr->state));
> +
> + switch (itr->state) {
> + case MHI_STATE_M0:
> + ret = mhi_ep_set_m0_state(mhi_cntrl);
> + if (ret)
> + dev_err(dev, "Failed to transition to M0 state\n");
> + break;
> + case MHI_STATE_M3:
> + ret = mhi_ep_set_m3_state(mhi_cntrl);
> + if (ret)
> + dev_err(dev, "Failed to transition to M3 state\n");
> + break;
> + default:
> + dev_err(dev, "Invalid MHI state transition: %d\n", itr->state);
> + break;
> + }
> + kfree(itr);
> + }
> +}
> +
> static void mhi_ep_release_device(struct device *dev)
> {
> struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> @@ -386,6 +423,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> }
>
> INIT_WORK(&mhi_cntrl->ring_work, mhi_ep_ring_worker);
> + INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
>
> mhi_cntrl->ring_wq = alloc_workqueue("mhi_ep_ring_wq", 0, 0);
> if (!mhi_cntrl->ring_wq) {
> @@ -393,8 +431,16 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> goto err_free_cmd;
> }
>
> + mhi_cntrl->state_wq = alloc_workqueue("mhi_ep_state_wq", 0, 0);

Maybe it's not a big deal, but do we really need several separate
work queues? Would one suffice? Could a system workqueue be used
in some cases (such as state changes)?

> + if (!mhi_cntrl->state_wq) {
> + ret = -ENOMEM;
> + goto err_destroy_ring_wq;
> + }
> +
> INIT_LIST_HEAD(&mhi_cntrl->ch_db_list);
> + INIT_LIST_HEAD(&mhi_cntrl->st_transition_list);
> spin_lock_init(&mhi_cntrl->list_lock);
> + spin_lock_init(&mhi_cntrl->state_lock);
> mutex_init(&mhi_cntrl->event_lock);
>
> /* Set MHI version and AMSS EE before enumeration */
> @@ -405,7 +451,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
> if (mhi_cntrl->index < 0) {
> ret = mhi_cntrl->index;
> - goto err_destroy_ring_wq;
> + goto err_destroy_state_wq;
> }
>
> /* Allocate the controller device */
> @@ -433,6 +479,8 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> put_device(&mhi_dev->dev);
> err_ida_free:
> ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
> +err_destroy_state_wq:
> + destroy_workqueue(mhi_cntrl->state_wq);
> err_destroy_ring_wq:
> destroy_workqueue(mhi_cntrl->ring_wq);
> err_free_cmd:
> @@ -448,6 +496,7 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
> {
> struct mhi_ep_device *mhi_dev = mhi_cntrl->mhi_dev;
>
> + destroy_workqueue(mhi_cntrl->state_wq);
> destroy_workqueue(mhi_cntrl->ring_wq);
>
> kfree(mhi_cntrl->mhi_cmd);
> diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c
> new file mode 100644
> index 000000000000..68e7f99b9137
> --- /dev/null
> +++ b/drivers/bus/mhi/ep/sm.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 Linaro Ltd.
> + * Author: Manivannan Sadhasivam <[email protected]>
> + */
> +
> +#include <linux/delay.h>
> +#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)
> +{
> + bool valid = false;
> +
> + switch (mhi_state) {
> + case MHI_STATE_READY:
> + valid = (cur_mhi_state == MHI_STATE_RESET);

Just do:
return cur_mhi_state == MHI_STATE_RESET;

And similar for all. No parentheses needed.

It *might* be easier to understand if you test based
on the current state:

if (mhi_state == MHI_STATE_SYS_ERR)
return true; /* Allowed in any state */

if (mhi_state == MHI_STATE_RESET)
return mhi_state == MHI_STATE_READY;

if (mhi_state == MHI_STATE_READY)
return mhi_state == MHI_STATE_M0;

if (mhi_state == MHI_STATE_M0)
return mhi_state == MHI_STATE_M3;

if (mhi_state == MHI_STATE_M3)
return mhi_state == MHI_STATE_M0;

return false;
}

> + break;
> + case MHI_STATE_M0:
> + valid = (cur_mhi_state == MHI_STATE_READY ||
> + cur_mhi_state == MHI_STATE_M3);
> + break;
> + case MHI_STATE_M3:
> + valid = (cur_mhi_state == MHI_STATE_M0);
> + break;
> + case MHI_STATE_SYS_ERR:
> + /* Transition to SYS_ERR state is allowed all the time */
> + valid = true;
> + break;
> + default:
> + break;
> + }
> +
> + return valid;
> +}
> +
> +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;
> + }
> +

In all (valid) cases, you set the state. Maybe do that in common
outside of the switch statement.

> + switch (mhi_state) {
> + case MHI_STATE_READY:
> + mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
> + MHISTATUS_READY_MASK, 1);
> +
> + mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
> + MHISTATUS_MHISTATE_MASK, mhi_state);

Maybe set the state before the READY bit?

> + break;
> + case MHI_STATE_SYS_ERR:
> + mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
> + MHISTATUS_SYSERR_MASK, 1);
> +
> + mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
> + MHISTATUS_MHISTATE_MASK, mhi_state);

Here too, maybe set the state before the SYSERR bit.

> + break;
> + case MHI_STATE_M1:
> + case MHI_STATE_M2:
> + dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state));
> + return -EOPNOTSUPP;
> + case MHI_STATE_M0:
> + case MHI_STATE_M3:
> + mhi_ep_mmio_masked_write(mhi_cntrl, MHISTATUS,
> + MHISTATUS_MHISTATE_MASK, mhi_state);
> + break;
> + default:

I think you can tell by inspection that the new state passed will
always be valid.

> + dev_err(dev, "Invalid MHI state (%d)\n", mhi_state);
> + return -EINVAL;
> + }
> +
> + mhi_cntrl->mhi_state = mhi_state;
> +
> + return 0;
> +}
> +

/* M0 state is entered only from READY or M3 state */

> +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;
> +
> + spin_lock_bh(&mhi_cntrl->state_lock);
> + old_state = mhi_cntrl->mhi_state;
> +
> + ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
> + if (ret) {
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> + return ret;
> + }

Rearrange this:

ret = mhi_ep_set_mhi_state();

spin_unlock_bh();

if (ret)
return ret;

There are other instances below where I suggest the same change.

> +
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> + /* 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");
> + return ret;
> + }
> +
> + if (old_state == MHI_STATE_READY) {
> + /* Allow the host to process state change event */
> + mdelay(1);

Why is 1 millisecond the correct delay? Why not microseconds,
or seconds?

> +
> + /* Send AMSS EE event to host */
> + ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EP_AMSS_EE);
> + if (ret) {
> + dev_err(dev, "Failed sending AMSS EE event\n");
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +

/* M3 state is entered only from M0 state */

> +int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
> +{
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + int ret;
> +
> + spin_lock_bh(&mhi_cntrl->state_lock);
> + ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
> + if (ret) {
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> + return ret;
> + }
> +
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> +
> + /* 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");
> + return ret;
> + }
> +
> + return 0;
> +}
> +

/* READY state is entered only from RESET state */

> +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;
> +
> + spin_lock_bh(&mhi_cntrl->state_lock);
> + /* Ensure that the MHISTATUS is set to RESET by host */
> + mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, MHISTATUS, MHISTATUS_MHISTATE_MASK);
> + is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, 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");
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> + return -EFAULT;

EFAULT means that there was a problem copying memory. This is
not the right error code. I'm not sure what's right, but you
could use EIO or soemthing.

> + }
> +
> + ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
> + spin_unlock_bh(&mhi_cntrl->state_lock);
> +
> + return ret;
> +}
> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> index 062133a68118..72ce30cbe87e 100644
> --- a/include/linux/mhi_ep.h
> +++ b/include/linux/mhi_ep.h
> @@ -65,11 +65,14 @@ struct mhi_ep_db_info {
> * @ch_ctx_host_pa: Physical address of host channel context data structure
> * @ev_ctx_host_pa: Physical address of host event context data structure
> * @cmd_ctx_host_pa: Physical address of host command context data structure
> + * @state_wq: Dedicated workqueue for handling MHI state transitions
> * @ring_wq: Dedicated workqueue for processing MHI rings
> + * @state_work: State transition worker
> * @ring_work: Ring worker
> * @ch_db_list: List of queued channel doorbells
> * @st_transition_list: List of state transitions
> * @list_lock: Lock for protecting state transition and channel doorbell lists
> + * @state_lock: Lock for protecting state transitions
> * @event_lock: Lock for protecting event rings
> * @chdb: Array of channel doorbell interrupt info
> * @raise_irq: CB function for raising IRQ to the host
> @@ -105,12 +108,15 @@ struct mhi_ep_cntrl {
> u64 ev_ctx_host_pa;
> u64 cmd_ctx_host_pa;
>
> + struct workqueue_struct *state_wq;
> struct workqueue_struct *ring_wq;
> + struct work_struct state_work;
> struct work_struct ring_work;
>
> struct list_head ch_db_list;
> struct list_head st_transition_list;
> spinlock_t list_lock;
> + spinlock_t state_lock;
> struct mutex event_lock;
> struct mhi_ep_db_info chdb[4];
>

2022-02-16 18:25:53

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

On Mon, Feb 14, 2022 at 05:04:23PM -0800, Hemant Kumar wrote:
> Hi Mani,
>
> On 2/12/2022 10:21 AM, Manivannan Sadhasivam wrote:
> > This commit adds support for registering MHI endpoint controller drivers
> > with the MHI endpoint stack. MHI endpoint controller drivers manages
> > the interaction with the host machines such as x86. They are also the
> > MHI endpoint bus master in charge of managing the physical link between the
> > host and endpoint device.
> >
> > The endpoint controller driver encloses all information about the
> > underlying physical bus like PCIe. The registration process involves
> > parsing the channel configuration and allocating an MHI EP device.
> >
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
> > ---
> > drivers/bus/mhi/Kconfig | 1 +
> > drivers/bus/mhi/Makefile | 3 +
> > drivers/bus/mhi/ep/Kconfig | 10 ++
> > drivers/bus/mhi/ep/Makefile | 2 +
> > drivers/bus/mhi/ep/internal.h | 160 +++++++++++++++++++++++
> > drivers/bus/mhi/ep/main.c | 234 ++++++++++++++++++++++++++++++++++
> > include/linux/mhi_ep.h | 143 +++++++++++++++++++++
> > 7 files changed, 553 insertions(+)
> > create mode 100644 drivers/bus/mhi/ep/Kconfig
> > create mode 100644 drivers/bus/mhi/ep/Makefile
> > create mode 100644 drivers/bus/mhi/ep/internal.h
> > create mode 100644 drivers/bus/mhi/ep/main.c
> > create mode 100644 include/linux/mhi_ep.h
> >

[...]

> > +#define MHI_CTRL_INT_STATUS_A7 0x4
> can we get rid of all instances of "_A7" as this corresponds to Cortex-A7,
> in future this can change? At MHI core layer, we can avoid this naming
> convetion, even though register names are inculding them now and may change
> to something different later. This MHI EP driver would still be used for
> those new cortex vers.

Since these registers are not documented by the spec, I just went with
the register definition available for SDX55. But you are right, and Alex
too, that it may change in future.

I'll remove the A7 suffix.

Thanks,
Mani

> > +#define MHI_CTRL_INT_STATUS_A7_MSK BIT(0)
> > +#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
> > +#define MHI_CHDB_INT_STATUS_A7_n(n) (0x28 + 0x4 * (n))
> > +#define MHI_ERDB_INT_STATUS_A7_n(n) (0x38 + 0x4 * (n))
> > +
> [..]
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a
> Linux Foundation Collaborative Project

2022-02-16 19:41:42

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 06/25] bus: mhi: Cleanup the register definitions used in headers

On Tue, Feb 15, 2022 at 02:02:28PM -0600, Alex Elder wrote:
> On 2/12/22 12:20 PM, Manivannan Sadhasivam wrote:
> > Cleanup includes:
> >
> > 1. Moving the MHI register definitions to common.h header with REG_ prefix
> > and using them in the host/internal.h file as an alias. This makes it
> > possible to reuse the register definitions in EP stack that differs by
> > a fixed offset.
>
> I like that you're doing this. But I don't see the point of this
> kind of definition, made in "drivers/bus/mhi/host/internal.h
> ":
>
> #define MHIREGLEN REG_MHIREGLEN
>
> Just use REG_MHIREGLEN in the host code too. (Or use MHIREGLEN in
> both places, whichever you prefer.)
>

My intention is to use the original MHI register definitions in both
host and endpoint. So REG_ prefix acts like an overlay here. Earlier I
was defining the MHI registers in both host and endpoint separately.

But I came up with this after your v2 review.

>
> > 2. Using the GENMASK macro for masks
>
> Great!
>
> > 3. Removing brackets for single values
>
> They're normally called "parentheses." Brackets more typically []
> (and {} is "braces", though that's not always the case).
>

Ah, sorry for that. I used to call them "round brackets", so that came in ;)

> > 4. Using lowercase for hex values
>
> I think I saw a few upper case hex values in another patch.
> Not a big deal, just FYI.
>

Will change them.

> > 5. Using two digits for hex values where applicable
>
> I think I suggested most of these things, so of course
> they look awesome to me.
>
> You could use bitfield accessor macros in a few more places.
> For example, this:
>
> #define MHI_TRE_CMD_RESET_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> (MHI_CMD_RESET_CHAN << 16)))
>
> Could use something more like this:
>
> #define MHI_CMD_CHANNEL_MASK GENMASK(31, 24)
> #define MHI_CMD_COMMAND_MASK GENMASK(23, 16)
>
> #define MHI_TRE_CMD_RESET_DWORD1(chid) \
> (le32_encode_bits(chid, MHI_CMD_CHANNEL_MASK) | \
> le32_encode_bits(MHI_CMD_RESET_CHAN, MHI_CMD_CMD_MASK))
>

This adds more code and also makes the reading a bit complicated. I'd
prefer to stick with open coding for these.

Thanks,
Mani

> (But of course I already said I preferred CPU byte order on
> these values...)
>
> I would like to see you get rid of one-to-one definitions
> I mentioned at the top. I haven't done an exhaustive check
> of all the symbols, but this looks good generally, so:
>
> Reviewed-by: Alex Elder <[email protected]>
>
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
> > ---
> > drivers/bus/mhi/common.h | 243 ++++++++++++++++++++++++-----
> > drivers/bus/mhi/host/internal.h | 265 +++++++++-----------------------
> > 2 files changed, 278 insertions(+), 230 deletions(-)
> >
> > diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
> > index 288e47168649..f226f06d4ff9 100644
> > --- a/drivers/bus/mhi/common.h
> > +++ b/drivers/bus/mhi/common.h
> > @@ -9,62 +9,223 @@
> > #include <linux/mhi.h>
> > +/* MHI registers */
> > +#define REG_MHIREGLEN 0x00
> > +#define REG_MHIVER 0x08
> > +#define REG_MHICFG 0x10
> > +#define REG_CHDBOFF 0x18
> > +#define REG_ERDBOFF 0x20
> > +#define REG_BHIOFF 0x28
> > +#define REG_BHIEOFF 0x2c
> > +#define REG_DEBUGOFF 0x30
> > +#define REG_MHICTRL 0x38
> > +#define REG_MHISTATUS 0x48
> > +#define REG_CCABAP_LOWER 0x58
> > +#define REG_CCABAP_HIGHER 0x5c
> > +#define REG_ECABAP_LOWER 0x60
> > +#define REG_ECABAP_HIGHER 0x64
> > +#define REG_CRCBAP_LOWER 0x68
> > +#define REG_CRCBAP_HIGHER 0x6c
> > +#define REG_CRDB_LOWER 0x70
> > +#define REG_CRDB_HIGHER 0x74
> > +#define REG_MHICTRLBASE_LOWER 0x80
> > +#define REG_MHICTRLBASE_HIGHER 0x84
> > +#define REG_MHICTRLLIMIT_LOWER 0x88
> > +#define REG_MHICTRLLIMIT_HIGHER 0x8c
> > +#define REG_MHIDATABASE_LOWER 0x98
> > +#define REG_MHIDATABASE_HIGHER 0x9c
> > +#define REG_MHIDATALIMIT_LOWER 0xa0
> > +#define REG_MHIDATALIMIT_HIGHER 0xa4
> > +
> > +/* MHI BHI registers */
> > +#define REG_BHI_BHIVERSION_MINOR 0x00
> > +#define REG_BHI_BHIVERSION_MAJOR 0x04
> > +#define REG_BHI_IMGADDR_LOW 0x08
> > +#define REG_BHI_IMGADDR_HIGH 0x0c
> > +#define REG_BHI_IMGSIZE 0x10
> > +#define REG_BHI_RSVD1 0x14
> > +#define REG_BHI_IMGTXDB 0x18
> > +#define REG_BHI_RSVD2 0x1c
> > +#define REG_BHI_INTVEC 0x20
> > +#define REG_BHI_RSVD3 0x24
> > +#define REG_BHI_EXECENV 0x28
> > +#define REG_BHI_STATUS 0x2c
> > +#define REG_BHI_ERRCODE 0x30
> > +#define REG_BHI_ERRDBG1 0x34
> > +#define REG_BHI_ERRDBG2 0x38
> > +#define REG_BHI_ERRDBG3 0x3c
> > +#define REG_BHI_SERIALNU 0x40
> > +#define REG_BHI_SBLANTIROLLVER 0x44
> > +#define REG_BHI_NUMSEG 0x48
> > +#define REG_BHI_MSMHWID(n) (0x4c + (0x4 * (n)))
> > +#define REG_BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
> > +#define REG_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_SHIFT 30
> > +#define BHI_STATUS_ERROR 0x03
> > +#define BHI_STATUS_SUCCESS 0x02
> > +#define BHI_STATUS_RESET 0x00
> > +
> > +/* MHI BHIE registers */
> > +#define REG_BHIE_MSMSOCID_OFFS 0x00
> > +#define REG_BHIE_TXVECADDR_LOW_OFFS 0x2c
> > +#define REG_BHIE_TXVECADDR_HIGH_OFFS 0x30
> > +#define REG_BHIE_TXVECSIZE_OFFS 0x34
> > +#define REG_BHIE_TXVECDB_OFFS 0x3c
> > +#define REG_BHIE_TXVECSTATUS_OFFS 0x44
> > +#define REG_BHIE_RXVECADDR_LOW_OFFS 0x60
> > +#define REG_BHIE_RXVECADDR_HIGH_OFFS 0x64
> > +#define REG_BHIE_RXVECSIZE_OFFS 0x68
> > +#define REG_BHIE_RXVECDB_OFFS 0x70
> > +#define REG_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 MHIREGLEN_MHIREGLEN_MASK GENMASK(31, 0)
> > +#define MHIREGLEN_MHIREGLEN_SHIFT 0
> > +#define MHIVER_MHIVER_MASK GENMASK(31, 0)
> > +#define MHIVER_MHIVER_SHIFT 0
> > +#define MHICFG_NHWER_MASK GENMASK(31, 24)
> > +#define MHICFG_NHWER_SHIFT 24
> > +#define MHICFG_NER_MASK GENMASK(23, 16)
> > +#define MHICFG_NER_SHIFT 16
> > +#define MHICFG_NHWCH_MASK GENMASK(15, 8)
> > +#define MHICFG_NHWCH_SHIFT 8
> > +#define MHICFG_NCH_MASK GENMASK(7, 0)
> > +#define MHICFG_NCH_SHIFT 0
> > +#define CHDBOFF_CHDBOFF_MASK GENMASK(31, 0)
> > +#define CHDBOFF_CHDBOFF_SHIFT 0
> > +#define ERDBOFF_ERDBOFF_MASK GENMASK(31, 0)
> > +#define ERDBOFF_ERDBOFF_SHIFT 0
> > +#define BHIOFF_BHIOFF_MASK GENMASK(31, 0)
> > +#define BHIOFF_BHIOFF_SHIFT 0
> > +#define BHIEOFF_BHIEOFF_MASK GENMASK(31, 0)
> > +#define BHIEOFF_BHIEOFF_SHIFT 0
> > +#define DEBUGOFF_DEBUGOFF_MASK GENMASK(31, 0)
> > +#define DEBUGOFF_DEBUGOFF_SHIFT 0
> > +#define MHICTRL_MHISTATE_MASK GENMASK(15, 8)
> > +#define MHICTRL_MHISTATE_SHIFT 8
> > +#define MHICTRL_RESET_MASK BIT(1)
> > +#define MHICTRL_RESET_SHIFT 1
> > +#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8)
> > +#define MHISTATUS_MHISTATE_SHIFT 8
> > +#define MHISTATUS_SYSERR_MASK BIT(2)
> > +#define MHISTATUS_SYSERR_SHIFT 2
> > +#define MHISTATUS_READY_MASK BIT(0)
> > +#define MHISTATUS_READY_SHIFT 0
> > +#define CCABAP_LOWER_CCABAP_LOWER_MASK GENMASK(31, 0)
> > +#define CCABAP_LOWER_CCABAP_LOWER_SHIFT 0
> > +#define CCABAP_HIGHER_CCABAP_HIGHER_MASK GENMASK(31, 0)
> > +#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT 0
> > +#define ECABAP_LOWER_ECABAP_LOWER_MASK GENMASK(31, 0)
> > +#define ECABAP_LOWER_ECABAP_LOWER_SHIFT 0
> > +#define ECABAP_HIGHER_ECABAP_HIGHER_MASK GENMASK(31, 0)
> > +#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT 0
> > +#define CRCBAP_LOWER_CRCBAP_LOWER_MASK GENMASK(31, 0)
> > +#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT 0
> > +#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK GENMASK(31, 0)
> > +#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT 0
> > +#define CRDB_LOWER_CRDB_LOWER_MASK GENMASK(31, 0)
> > +#define CRDB_LOWER_CRDB_LOWER_SHIFT 0
> > +#define CRDB_HIGHER_CRDB_HIGHER_MASK GENMASK(31, 0)
> > +#define CRDB_HIGHER_CRDB_HIGHER_SHIFT 0
> > +#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK GENMASK(31, 0)
> > +#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT 0
> > +#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK GENMASK(31, 0)
> > +#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT 0
> > +#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK GENMASK(31, 0)
> > +#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT 0
> > +#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK GENMASK(31, 0)
> > +#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT 0
> > +#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK GENMASK(31, 0)
> > +#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT 0
> > +#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK GENMASK(31, 0)
> > +#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT 0
> > +#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK GENMASK(31, 0)
> > +#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT 0
> > +#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK GENMASK(31, 0)
> > +#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT 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(MHI_CMD_NOP << 16))
> > +#define MHI_TRE_CMD_NOOP_PTR 0
> > +#define MHI_TRE_CMD_NOOP_DWORD0 0
> > +#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(MHI_CMD_NOP << 16)
> > /* 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((chid << 24) | \
> > - (MHI_CMD_RESET_CHAN << 16)))
> > +#define MHI_TRE_CMD_RESET_PTR 0
> > +#define MHI_TRE_CMD_RESET_DWORD0 0
> > +#define MHI_TRE_CMD_RESET_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > + (MHI_CMD_RESET_CHAN << 16)))
> > /* 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((chid << 24) | \
> > - (MHI_CMD_STOP_CHAN << 16)))
> > +#define MHI_TRE_CMD_STOP_PTR 0
> > +#define MHI_TRE_CMD_STOP_DWORD0 0
> > +#define MHI_TRE_CMD_STOP_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > + (MHI_CMD_STOP_CHAN << 16)))
> > /* 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((chid << 24) | \
> > - (MHI_CMD_START_CHAN << 16)))
> > +#define MHI_TRE_CMD_START_PTR 0
> > +#define MHI_TRE_CMD_START_DWORD0 0
> > +#define MHI_TRE_CMD_START_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > + (MHI_CMD_START_CHAN << 16)))
> > -#define MHI_TRE_GET_DWORD(tre, word) (le32_to_cpu((tre)->dword[(word)]))
> > -#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > -#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > +#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)])
> > +#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > +#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > /* Event descriptor macros */
> > -#define MHI_TRE_EV_PTR(ptr) (cpu_to_le64(ptr))
> > -#define MHI_TRE_EV_DWORD0(code, len) (cpu_to_le32((code << 24) | len))
> > -#define MHI_TRE_EV_DWORD1(chid, type) (cpu_to_le32((chid << 24) | (type << 16)))
> > -#define MHI_TRE_GET_EV_PTR(tre) (le64_to_cpu((tre)->ptr))
> > -#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > -#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
> > -#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > -#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > -#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > -#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > -#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
> > -#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > -#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)
> > +/* Transfer completion event */
> > +#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr)
> > +#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32((code << 24) | len)
> > +#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32((chid << 24) | (type << 16))
> > +#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr)
> > +#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > +#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
> > +#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > +#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > +#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > +#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > +#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
> > +#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > +#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)
> > /* Transfer descriptor macros */
> > -#define MHI_TRE_DATA_PTR(ptr) (cpu_to_le64(ptr))
> > -#define MHI_TRE_DATA_DWORD0(len) (cpu_to_le32(len & MHI_MAX_MTU))
> > -#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
> > - | (ieot << 9) | (ieob << 8) | chain))
> > +#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr)
> > +#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(len & MHI_MAX_MTU)
> > +#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
> > + | (ieot << 9) | (ieob << 8) | chain))
> > /* RSC transfer descriptor macros */
> > -#define MHI_RSCTRE_DATA_PTR(ptr, len) (cpu_to_le64(((u64)len << 48) | ptr))
> > -#define MHI_RSCTRE_DATA_DWORD0(cookie) (cpu_to_le32(cookie))
> > -#define MHI_RSCTRE_DATA_DWORD1 (cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16))
> > +#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(((u64)len << 48) | ptr)
> > +#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie)
> > +#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16)
> > enum mhi_pkt_type {
> > MHI_PKT_TYPE_INVALID = 0x0,
> > diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
> > index 622de6ba1a0b..762055a6ec9f 100644
> > --- a/drivers/bus/mhi/host/internal.h
> > +++ b/drivers/bus/mhi/host/internal.h
> > @@ -11,197 +11,84 @@
> > extern struct bus_type mhi_bus_type;
> > -#define MHIREGLEN (0x0)
> > -#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF)
> > -#define MHIREGLEN_MHIREGLEN_SHIFT (0)
> > -
> > -#define MHIVER (0x8)
> > -#define MHIVER_MHIVER_MASK (0xFFFFFFFF)
> > -#define MHIVER_MHIVER_SHIFT (0)
> > -
> > -#define MHICFG (0x10)
> > -#define MHICFG_NHWER_MASK (0xFF000000)
> > -#define MHICFG_NHWER_SHIFT (24)
> > -#define MHICFG_NER_MASK (0xFF0000)
> > -#define MHICFG_NER_SHIFT (16)
> > -#define MHICFG_NHWCH_MASK (0xFF00)
> > -#define MHICFG_NHWCH_SHIFT (8)
> > -#define MHICFG_NCH_MASK (0xFF)
> > -#define MHICFG_NCH_SHIFT (0)
> > -
> > -#define CHDBOFF (0x18)
> > -#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF)
> > -#define CHDBOFF_CHDBOFF_SHIFT (0)
> > -
> > -#define ERDBOFF (0x20)
> > -#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF)
> > -#define ERDBOFF_ERDBOFF_SHIFT (0)
> > -
> > -#define BHIOFF (0x28)
> > -#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF)
> > -#define BHIOFF_BHIOFF_SHIFT (0)
> > -
> > -#define BHIEOFF (0x2C)
> > -#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF)
> > -#define BHIEOFF_BHIEOFF_SHIFT (0)
> > -
> > -#define DEBUGOFF (0x30)
> > -#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF)
> > -#define DEBUGOFF_DEBUGOFF_SHIFT (0)
> > -
> > -#define MHICTRL (0x38)
> > -#define MHICTRL_MHISTATE_MASK (0x0000FF00)
> > -#define MHICTRL_MHISTATE_SHIFT (8)
> > -#define MHICTRL_RESET_MASK (0x2)
> > -#define MHICTRL_RESET_SHIFT (1)
> > -
> > -#define MHISTATUS (0x48)
> > -#define MHISTATUS_MHISTATE_MASK (0x0000FF00)
> > -#define MHISTATUS_MHISTATE_SHIFT (8)
> > -#define MHISTATUS_SYSERR_MASK (0x4)
> > -#define MHISTATUS_SYSERR_SHIFT (2)
> > -#define MHISTATUS_READY_MASK (0x1)
> > -#define MHISTATUS_READY_SHIFT (0)
> > -
> > -#define CCABAP_LOWER (0x58)
> > -#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF)
> > -#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0)
> > -
> > -#define CCABAP_HIGHER (0x5C)
> > -#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF)
> > -#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0)
> > -
> > -#define ECABAP_LOWER (0x60)
> > -#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF)
> > -#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0)
> > -
> > -#define ECABAP_HIGHER (0x64)
> > -#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF)
> > -#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0)
> > -
> > -#define CRCBAP_LOWER (0x68)
> > -#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF)
> > -#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0)
> > -
> > -#define CRCBAP_HIGHER (0x6C)
> > -#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF)
> > -#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0)
> > -
> > -#define CRDB_LOWER (0x70)
> > -#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF)
> > -#define CRDB_LOWER_CRDB_LOWER_SHIFT (0)
> > -
> > -#define CRDB_HIGHER (0x74)
> > -#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF)
> > -#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0)
> > -
> > -#define MHICTRLBASE_LOWER (0x80)
> > -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF)
> > -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0)
> > -
> > -#define MHICTRLBASE_HIGHER (0x84)
> > -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF)
> > -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0)
> > -
> > -#define MHICTRLLIMIT_LOWER (0x88)
> > -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF)
> > -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0)
> > -
> > -#define MHICTRLLIMIT_HIGHER (0x8C)
> > -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF)
> > -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0)
> > -
> > -#define MHIDATABASE_LOWER (0x98)
> > -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF)
> > -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0)
> > -
> > -#define MHIDATABASE_HIGHER (0x9C)
> > -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF)
> > -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0)
> > -
> > -#define MHIDATALIMIT_LOWER (0xA0)
> > -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF)
> > -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0)
> > -
> > -#define MHIDATALIMIT_HIGHER (0xA4)
> > -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF)
> > -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0)
> > +/* MHI registers */
> > +#define MHIREGLEN REG_MHIREGLEN
> > +#define MHIVER REG_MHIVER
> > +#define MHICFG REG_MHICFG
> > +#define CHDBOFF REG_CHDBOFF
> > +#define ERDBOFF REG_ERDBOFF
> > +#define BHIOFF REG_BHIOFF
> > +#define BHIEOFF REG_BHIEOFF
> > +#define DEBUGOFF REG_DEBUGOFF
> > +#define MHICTRL REG_MHICTRL
> > +#define MHISTATUS REG_MHISTATUS
> > +#define CCABAP_LOWER REG_CCABAP_LOWER
> > +#define CCABAP_HIGHER REG_CCABAP_HIGHER
> > +#define ECABAP_LOWER REG_ECABAP_LOWER
> > +#define ECABAP_HIGHER REG_ECABAP_HIGHER
> > +#define CRCBAP_LOWER REG_CRCBAP_LOWER
> > +#define CRCBAP_HIGHER REG_CRCBAP_HIGHER
> > +#define CRDB_LOWER REG_CRDB_LOWER
> > +#define CRDB_HIGHER REG_CRDB_HIGHER
> > +#define MHICTRLBASE_LOWER REG_MHICTRLBASE_LOWER
> > +#define MHICTRLBASE_HIGHER REG_MHICTRLBASE_HIGHER
> > +#define MHICTRLLIMIT_LOWER REG_MHICTRLLIMIT_LOWER
> > +#define MHICTRLLIMIT_HIGHER REG_MHICTRLLIMIT_HIGHER
> > +#define MHIDATABASE_LOWER REG_MHIDATABASE_LOWER
> > +#define MHIDATABASE_HIGHER REG_MHIDATABASE_HIGHER
> > +#define MHIDATALIMIT_LOWER REG_MHIDATALIMIT_LOWER
> > +#define MHIDATALIMIT_HIGHER REG_MHIDATALIMIT_HIGHER
> > /* Host request register */
> > -#define MHI_SOC_RESET_REQ_OFFSET (0xB0)
> > -#define MHI_SOC_RESET_REQ BIT(0)
> > -
> > -/* MHI BHI offfsets */
> > -#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_TXDB_SEQNUM_BMSK (0x3FFFFFFF)
> > -#define BHI_TXDB_SEQNUM_SHFT (0)
> > -#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)
> > -#define BHI_STATUS_MASK (0xC0000000)
> > -#define BHI_STATUS_SHIFT (30)
> > -#define BHI_STATUS_ERROR (3)
> > -#define BHI_STATUS_SUCCESS (2)
> > -#define BHI_STATUS_RESET (0)
> > -
> > -/* MHI BHIE offsets */
> > -#define BHIE_MSMSOCID_OFFS (0x0000)
> > -#define BHIE_TXVECADDR_LOW_OFFS (0x002C)
> > -#define BHIE_TXVECADDR_HIGH_OFFS (0x0030)
> > -#define BHIE_TXVECSIZE_OFFS (0x0034)
> > -#define BHIE_TXVECDB_OFFS (0x003C)
> > -#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
> > -#define BHIE_TXVECDB_SEQNUM_SHFT (0)
> > -#define BHIE_TXVECSTATUS_OFFS (0x0044)
> > -#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
> > -#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
> > -#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
> > -#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_RXVECADDR_LOW_OFFS (0x0060)
> > -#define BHIE_RXVECADDR_HIGH_OFFS (0x0064)
> > -#define BHIE_RXVECSIZE_OFFS (0x0068)
> > -#define BHIE_RXVECDB_OFFS (0x0070)
> > -#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
> > -#define BHIE_RXVECDB_SEQNUM_SHFT (0)
> > -#define BHIE_RXVECSTATUS_OFFS (0x0078)
> > -#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
> > -#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
> > -#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
> > -#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)
> > -
> > -#define SOC_HW_VERSION_OFFS (0x224)
> > -#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000)
> > -#define SOC_HW_VERSION_FAM_NUM_SHFT (28)
> > -#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000)
> > -#define SOC_HW_VERSION_DEV_NUM_SHFT (16)
> > -#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00)
> > -#define SOC_HW_VERSION_MAJOR_VER_SHFT (8)
> > -#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF)
> > -#define SOC_HW_VERSION_MINOR_VER_SHFT (0)
> > +#define MHI_SOC_RESET_REQ_OFFSET 0xb0
> > +#define MHI_SOC_RESET_REQ BIT(0)
> > +
> > +/* MHI BHI registers */
> > +#define BHI_BHIVERSION_MINOR REG_BHI_BHIVERSION_MINOR
> > +#define BHI_BHIVERSION_MAJOR REG_BHI_BHIVERSION_MAJOR
> > +#define BHI_IMGADDR_LOW REG_BHI_IMGADDR_LOW
> > +#define BHI_IMGADDR_HIGH REG_BHI_IMGADDR_HIGH
> > +#define BHI_IMGSIZE REG_BHI_IMGSIZE
> > +#define BHI_RSVD1 REG_BHI_RSVD1
> > +#define BHI_IMGTXDB REG_BHI_IMGTXDB
> > +#define BHI_RSVD2 REG_BHI_RSVD2
> > +#define BHI_INTVEC REG_BHI_INTVEC
> > +#define BHI_RSVD3 REG_BHI_RSVD3
> > +#define BHI_EXECENV REG_BHI_EXECENV
> > +#define BHI_STATUS REG_BHI_STATUS
> > +#define BHI_ERRCODE REG_BHI_ERRCODE
> > +#define BHI_ERRDBG1 REG_BHI_ERRDBG1
> > +#define BHI_ERRDBG2 REG_BHI_ERRDBG2
> > +#define BHI_ERRDBG3 REG_BHI_ERRDBG3
> > +#define BHI_SERIALNU REG_BHI_SERIALNU
> > +#define BHI_SBLANTIROLLVER REG_BHI_SBLANTIROLLVER
> > +#define BHI_NUMSEG REG_BHI_NUMSEG
> > +#define BHI_MSMHWID(n) REG_BHI_MSMHWID(n)
> > +#define BHI_OEMPKHASH(n) REG_BHI_OEMPKHASH(n)
> > +#define BHI_RSVD5 REG_BHI_RSVD5
> > +
> > +/* MHI BHIE registers */
> > +#define BHIE_MSMSOCID_OFFS REG_BHIE_MSMSOCID_OFFS
> > +#define BHIE_TXVECADDR_LOW_OFFS REG_BHIE_TXVECADDR_LOW_OFFS
> > +#define BHIE_TXVECADDR_HIGH_OFFS REG_BHIE_TXVECADDR_HIGH_OFFS
> > +#define BHIE_TXVECSIZE_OFFS REG_BHIE_TXVECSIZE_OFFS
> > +#define BHIE_TXVECDB_OFFS REG_BHIE_TXVECDB_OFFS
> > +#define BHIE_TXVECSTATUS_OFFS REG_BHIE_TXVECSTATUS_OFFS
> > +#define BHIE_RXVECADDR_LOW_OFFS REG_BHIE_RXVECADDR_LOW_OFFS
> > +#define BHIE_RXVECADDR_HIGH_OFFS REG_BHIE_RXVECADDR_HIGH_OFFS
> > +#define BHIE_RXVECSIZE_OFFS REG_BHIE_RXVECSIZE_OFFS
> > +#define BHIE_RXVECDB_OFFS REG_BHIE_RXVECDB_OFFS
> > +#define BHIE_RXVECSTATUS_OFFS REG_BHIE_RXVECSTATUS_OFFS
> > +
> > +#define SOC_HW_VERSION_OFFS 0x224
> > +#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
> > +#define SOC_HW_VERSION_FAM_NUM_SHFT 28
> > +#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
> > +#define SOC_HW_VERSION_DEV_NUM_SHFT 16
> > +#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
> > +#define SOC_HW_VERSION_MAJOR_VER_SHFT 8
> > +#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
> > +#define SOC_HW_VERSION_MINOR_VER_SHFT 0
> > struct mhi_ctxt {
> > struct mhi_event_ctxt *er_ctxt;
>

2022-02-17 10:52:38

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 09/25] bus: mhi: ep: Add support for registering MHI endpoint client drivers

On Tue, Feb 15, 2022 at 02:02:50PM -0600, Alex Elder wrote:

[...]

> > +static int mhi_ep_driver_remove(struct device *dev)
> > +{
> > + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> > + struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
> > + struct mhi_result result = {};
> > + struct mhi_ep_chan *mhi_chan;
> > + int dir;
> > +
> > + /* Skip if it is a controller device */
> > + if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> > + return 0;
> > +
>
> It would be my preference to encapsulate the body of the
> following loop into a called function, then call that once
> for the UL channel and once for the DL channel.
>

This follows the host stack, so I'd like to keep it the same.

> > + /* Disconnect the channels associated with the driver */
> > + for (dir = 0; dir < 2; dir++) {
> > + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
> > +
> > + if (!mhi_chan)
> > + continue;
> > +
> > + mutex_lock(&mhi_chan->lock);
> > + /* Send channel disconnect status to the client driver */
> > + if (mhi_chan->xfer_cb) {
> > + result.transaction_status = -ENOTCONN;
> > + result.bytes_xferd = 0;
> > + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
>
> It appears the result is ignored here. If so, can we
> define the xfer_cb() function so that a NULL pointer may
> be supplied by the caller in cases like this?
>

result is not ignored, only the bytes_xfered. "transaction_status" will
be used by the client drivers for error handling.

> > + }
> > +
> > + /* Set channel state to DISABLED */
>
> That comment is a little tautological. Just omit it.
>
> > + mhi_chan->state = MHI_CH_STATE_DISABLED;
> > + mhi_chan->xfer_cb = NULL;
> > + mutex_unlock(&mhi_chan->lock);
> > + }
> > +
> > + /* Remove the client driver now */
> > + mhi_drv->remove(mhi_dev);
> > +
> > + return 0;
> > +}

[...]

> > +struct mhi_ep_driver {
> > + const struct mhi_device_id *id_table;
> > + struct device_driver driver;
> > + int (*probe)(struct mhi_ep_device *mhi_ep,
> > + const struct mhi_device_id *id);
> > + void (*remove)(struct mhi_ep_device *mhi_ep);
>
> I get confused by the "ul" versus "dl" naming scheme here.
> Is "ul" from the perspective of the host, meaning upload
> is from the host toward the WWAN network (and therefore
> toward the SDX AP), and download is from the WWAN toward
> the host? Somewhere this should be stated clearly in
> comments; maybe I just missed it.
>

Yes UL and DL are as per host context. I didn't state this explicitly
since this is the MHI host stack behaviour but I'll add a comment for
clarity

Thanks,
Mani

2022-02-17 11:41:23

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 06/25] bus: mhi: Cleanup the register definitions used in headers

On Wed, Feb 16, 2022 at 10:51:45PM +0530, Manivannan Sadhasivam wrote:
> On Tue, Feb 15, 2022 at 02:02:28PM -0600, Alex Elder wrote:
> > On 2/12/22 12:20 PM, Manivannan Sadhasivam wrote:
> > > Cleanup includes:
> > >
> > > 1. Moving the MHI register definitions to common.h header with REG_ prefix
> > > and using them in the host/internal.h file as an alias. This makes it
> > > possible to reuse the register definitions in EP stack that differs by
> > > a fixed offset.
> >
> > I like that you're doing this. But I don't see the point of this
> > kind of definition, made in "drivers/bus/mhi/host/internal.h
> > ":
> >
> > #define MHIREGLEN REG_MHIREGLEN
> >
> > Just use REG_MHIREGLEN in the host code too. (Or use MHIREGLEN in
> > both places, whichever you prefer.)
> >
>
> My intention is to use the original MHI register definitions in both
> host and endpoint. So REG_ prefix acts like an overlay here. Earlier I
> was defining the MHI registers in both host and endpoint separately.
>
> But I came up with this after your v2 review.
>

Hmm, I got your suggestion now. Just looked at patch 08/25. Please
ignore my above comment.

Thanks,
Mani

> >
> > > 2. Using the GENMASK macro for masks
> >
> > Great!
> >
> > > 3. Removing brackets for single values
> >
> > They're normally called "parentheses." Brackets more typically []
> > (and {} is "braces", though that's not always the case).
> >
>
> Ah, sorry for that. I used to call them "round brackets", so that came in ;)
>
> > > 4. Using lowercase for hex values
> >
> > I think I saw a few upper case hex values in another patch.
> > Not a big deal, just FYI.
> >
>
> Will change them.
>
> > > 5. Using two digits for hex values where applicable
> >
> > I think I suggested most of these things, so of course
> > they look awesome to me.
> >
> > You could use bitfield accessor macros in a few more places.
> > For example, this:
> >
> > #define MHI_TRE_CMD_RESET_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > (MHI_CMD_RESET_CHAN << 16)))
> >
> > Could use something more like this:
> >
> > #define MHI_CMD_CHANNEL_MASK GENMASK(31, 24)
> > #define MHI_CMD_COMMAND_MASK GENMASK(23, 16)
> >
> > #define MHI_TRE_CMD_RESET_DWORD1(chid) \
> > (le32_encode_bits(chid, MHI_CMD_CHANNEL_MASK) | \
> > le32_encode_bits(MHI_CMD_RESET_CHAN, MHI_CMD_CMD_MASK))
> >
>
> This adds more code and also makes the reading a bit complicated. I'd
> prefer to stick with open coding for these.
>
> Thanks,
> Mani
>
> > (But of course I already said I preferred CPU byte order on
> > these values...)
> >
> > I would like to see you get rid of one-to-one definitions
> > I mentioned at the top. I haven't done an exhaustive check
> > of all the symbols, but this looks good generally, so:
> >
> > Reviewed-by: Alex Elder <[email protected]>
> >
> > > Signed-off-by: Manivannan Sadhasivam <[email protected]>
> > > ---
> > > drivers/bus/mhi/common.h | 243 ++++++++++++++++++++++++-----
> > > drivers/bus/mhi/host/internal.h | 265 +++++++++-----------------------
> > > 2 files changed, 278 insertions(+), 230 deletions(-)
> > >
> > > diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h
> > > index 288e47168649..f226f06d4ff9 100644
> > > --- a/drivers/bus/mhi/common.h
> > > +++ b/drivers/bus/mhi/common.h
> > > @@ -9,62 +9,223 @@
> > > #include <linux/mhi.h>
> > > +/* MHI registers */
> > > +#define REG_MHIREGLEN 0x00
> > > +#define REG_MHIVER 0x08
> > > +#define REG_MHICFG 0x10
> > > +#define REG_CHDBOFF 0x18
> > > +#define REG_ERDBOFF 0x20
> > > +#define REG_BHIOFF 0x28
> > > +#define REG_BHIEOFF 0x2c
> > > +#define REG_DEBUGOFF 0x30
> > > +#define REG_MHICTRL 0x38
> > > +#define REG_MHISTATUS 0x48
> > > +#define REG_CCABAP_LOWER 0x58
> > > +#define REG_CCABAP_HIGHER 0x5c
> > > +#define REG_ECABAP_LOWER 0x60
> > > +#define REG_ECABAP_HIGHER 0x64
> > > +#define REG_CRCBAP_LOWER 0x68
> > > +#define REG_CRCBAP_HIGHER 0x6c
> > > +#define REG_CRDB_LOWER 0x70
> > > +#define REG_CRDB_HIGHER 0x74
> > > +#define REG_MHICTRLBASE_LOWER 0x80
> > > +#define REG_MHICTRLBASE_HIGHER 0x84
> > > +#define REG_MHICTRLLIMIT_LOWER 0x88
> > > +#define REG_MHICTRLLIMIT_HIGHER 0x8c
> > > +#define REG_MHIDATABASE_LOWER 0x98
> > > +#define REG_MHIDATABASE_HIGHER 0x9c
> > > +#define REG_MHIDATALIMIT_LOWER 0xa0
> > > +#define REG_MHIDATALIMIT_HIGHER 0xa4
> > > +
> > > +/* MHI BHI registers */
> > > +#define REG_BHI_BHIVERSION_MINOR 0x00
> > > +#define REG_BHI_BHIVERSION_MAJOR 0x04
> > > +#define REG_BHI_IMGADDR_LOW 0x08
> > > +#define REG_BHI_IMGADDR_HIGH 0x0c
> > > +#define REG_BHI_IMGSIZE 0x10
> > > +#define REG_BHI_RSVD1 0x14
> > > +#define REG_BHI_IMGTXDB 0x18
> > > +#define REG_BHI_RSVD2 0x1c
> > > +#define REG_BHI_INTVEC 0x20
> > > +#define REG_BHI_RSVD3 0x24
> > > +#define REG_BHI_EXECENV 0x28
> > > +#define REG_BHI_STATUS 0x2c
> > > +#define REG_BHI_ERRCODE 0x30
> > > +#define REG_BHI_ERRDBG1 0x34
> > > +#define REG_BHI_ERRDBG2 0x38
> > > +#define REG_BHI_ERRDBG3 0x3c
> > > +#define REG_BHI_SERIALNU 0x40
> > > +#define REG_BHI_SBLANTIROLLVER 0x44
> > > +#define REG_BHI_NUMSEG 0x48
> > > +#define REG_BHI_MSMHWID(n) (0x4c + (0x4 * (n)))
> > > +#define REG_BHI_OEMPKHASH(n) (0x64 + (0x4 * (n)))
> > > +#define REG_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_SHIFT 30
> > > +#define BHI_STATUS_ERROR 0x03
> > > +#define BHI_STATUS_SUCCESS 0x02
> > > +#define BHI_STATUS_RESET 0x00
> > > +
> > > +/* MHI BHIE registers */
> > > +#define REG_BHIE_MSMSOCID_OFFS 0x00
> > > +#define REG_BHIE_TXVECADDR_LOW_OFFS 0x2c
> > > +#define REG_BHIE_TXVECADDR_HIGH_OFFS 0x30
> > > +#define REG_BHIE_TXVECSIZE_OFFS 0x34
> > > +#define REG_BHIE_TXVECDB_OFFS 0x3c
> > > +#define REG_BHIE_TXVECSTATUS_OFFS 0x44
> > > +#define REG_BHIE_RXVECADDR_LOW_OFFS 0x60
> > > +#define REG_BHIE_RXVECADDR_HIGH_OFFS 0x64
> > > +#define REG_BHIE_RXVECSIZE_OFFS 0x68
> > > +#define REG_BHIE_RXVECDB_OFFS 0x70
> > > +#define REG_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 MHIREGLEN_MHIREGLEN_MASK GENMASK(31, 0)
> > > +#define MHIREGLEN_MHIREGLEN_SHIFT 0
> > > +#define MHIVER_MHIVER_MASK GENMASK(31, 0)
> > > +#define MHIVER_MHIVER_SHIFT 0
> > > +#define MHICFG_NHWER_MASK GENMASK(31, 24)
> > > +#define MHICFG_NHWER_SHIFT 24
> > > +#define MHICFG_NER_MASK GENMASK(23, 16)
> > > +#define MHICFG_NER_SHIFT 16
> > > +#define MHICFG_NHWCH_MASK GENMASK(15, 8)
> > > +#define MHICFG_NHWCH_SHIFT 8
> > > +#define MHICFG_NCH_MASK GENMASK(7, 0)
> > > +#define MHICFG_NCH_SHIFT 0
> > > +#define CHDBOFF_CHDBOFF_MASK GENMASK(31, 0)
> > > +#define CHDBOFF_CHDBOFF_SHIFT 0
> > > +#define ERDBOFF_ERDBOFF_MASK GENMASK(31, 0)
> > > +#define ERDBOFF_ERDBOFF_SHIFT 0
> > > +#define BHIOFF_BHIOFF_MASK GENMASK(31, 0)
> > > +#define BHIOFF_BHIOFF_SHIFT 0
> > > +#define BHIEOFF_BHIEOFF_MASK GENMASK(31, 0)
> > > +#define BHIEOFF_BHIEOFF_SHIFT 0
> > > +#define DEBUGOFF_DEBUGOFF_MASK GENMASK(31, 0)
> > > +#define DEBUGOFF_DEBUGOFF_SHIFT 0
> > > +#define MHICTRL_MHISTATE_MASK GENMASK(15, 8)
> > > +#define MHICTRL_MHISTATE_SHIFT 8
> > > +#define MHICTRL_RESET_MASK BIT(1)
> > > +#define MHICTRL_RESET_SHIFT 1
> > > +#define MHISTATUS_MHISTATE_MASK GENMASK(15, 8)
> > > +#define MHISTATUS_MHISTATE_SHIFT 8
> > > +#define MHISTATUS_SYSERR_MASK BIT(2)
> > > +#define MHISTATUS_SYSERR_SHIFT 2
> > > +#define MHISTATUS_READY_MASK BIT(0)
> > > +#define MHISTATUS_READY_SHIFT 0
> > > +#define CCABAP_LOWER_CCABAP_LOWER_MASK GENMASK(31, 0)
> > > +#define CCABAP_LOWER_CCABAP_LOWER_SHIFT 0
> > > +#define CCABAP_HIGHER_CCABAP_HIGHER_MASK GENMASK(31, 0)
> > > +#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT 0
> > > +#define ECABAP_LOWER_ECABAP_LOWER_MASK GENMASK(31, 0)
> > > +#define ECABAP_LOWER_ECABAP_LOWER_SHIFT 0
> > > +#define ECABAP_HIGHER_ECABAP_HIGHER_MASK GENMASK(31, 0)
> > > +#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT 0
> > > +#define CRCBAP_LOWER_CRCBAP_LOWER_MASK GENMASK(31, 0)
> > > +#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT 0
> > > +#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK GENMASK(31, 0)
> > > +#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT 0
> > > +#define CRDB_LOWER_CRDB_LOWER_MASK GENMASK(31, 0)
> > > +#define CRDB_LOWER_CRDB_LOWER_SHIFT 0
> > > +#define CRDB_HIGHER_CRDB_HIGHER_MASK GENMASK(31, 0)
> > > +#define CRDB_HIGHER_CRDB_HIGHER_SHIFT 0
> > > +#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK GENMASK(31, 0)
> > > +#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT 0
> > > +#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK GENMASK(31, 0)
> > > +#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT 0
> > > +#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK GENMASK(31, 0)
> > > +#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT 0
> > > +#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK GENMASK(31, 0)
> > > +#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT 0
> > > +#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK GENMASK(31, 0)
> > > +#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT 0
> > > +#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK GENMASK(31, 0)
> > > +#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT 0
> > > +#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK GENMASK(31, 0)
> > > +#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT 0
> > > +#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK GENMASK(31, 0)
> > > +#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT 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(MHI_CMD_NOP << 16))
> > > +#define MHI_TRE_CMD_NOOP_PTR 0
> > > +#define MHI_TRE_CMD_NOOP_DWORD0 0
> > > +#define MHI_TRE_CMD_NOOP_DWORD1 cpu_to_le32(MHI_CMD_NOP << 16)
> > > /* 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((chid << 24) | \
> > > - (MHI_CMD_RESET_CHAN << 16)))
> > > +#define MHI_TRE_CMD_RESET_PTR 0
> > > +#define MHI_TRE_CMD_RESET_DWORD0 0
> > > +#define MHI_TRE_CMD_RESET_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > > + (MHI_CMD_RESET_CHAN << 16)))
> > > /* 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((chid << 24) | \
> > > - (MHI_CMD_STOP_CHAN << 16)))
> > > +#define MHI_TRE_CMD_STOP_PTR 0
> > > +#define MHI_TRE_CMD_STOP_DWORD0 0
> > > +#define MHI_TRE_CMD_STOP_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > > + (MHI_CMD_STOP_CHAN << 16)))
> > > /* 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((chid << 24) | \
> > > - (MHI_CMD_START_CHAN << 16)))
> > > +#define MHI_TRE_CMD_START_PTR 0
> > > +#define MHI_TRE_CMD_START_DWORD0 0
> > > +#define MHI_TRE_CMD_START_DWORD1(chid) (cpu_to_le32((chid << 24) | \
> > > + (MHI_CMD_START_CHAN << 16)))
> > > -#define MHI_TRE_GET_DWORD(tre, word) (le32_to_cpu((tre)->dword[(word)]))
> > > -#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > -#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > > +#define MHI_TRE_GET_DWORD(tre, word) le32_to_cpu((tre)->dword[(word)])
> > > +#define MHI_TRE_GET_CMD_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > +#define MHI_TRE_GET_CMD_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > > /* Event descriptor macros */
> > > -#define MHI_TRE_EV_PTR(ptr) (cpu_to_le64(ptr))
> > > -#define MHI_TRE_EV_DWORD0(code, len) (cpu_to_le32((code << 24) | len))
> > > -#define MHI_TRE_EV_DWORD1(chid, type) (cpu_to_le32((chid << 24) | (type << 16)))
> > > -#define MHI_TRE_GET_EV_PTR(tre) (le64_to_cpu((tre)->ptr))
> > > -#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > -#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
> > > -#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > -#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > > -#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > -#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > -#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
> > > -#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > -#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)
> > > +/* Transfer completion event */
> > > +#define MHI_TRE_EV_PTR(ptr) cpu_to_le64(ptr)
> > > +#define MHI_TRE_EV_DWORD0(code, len) cpu_to_le32((code << 24) | len)
> > > +#define MHI_TRE_EV_DWORD1(chid, type) cpu_to_le32((chid << 24) | (type << 16))
> > > +#define MHI_TRE_GET_EV_PTR(tre) le64_to_cpu((tre)->ptr)
> > > +#define MHI_TRE_GET_EV_CODE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > +#define MHI_TRE_GET_EV_LEN(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFFFF)
> > > +#define MHI_TRE_GET_EV_CHID(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > +#define MHI_TRE_GET_EV_TYPE(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 16) & 0xFF)
> > > +#define MHI_TRE_GET_EV_STATE(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > +#define MHI_TRE_GET_EV_EXECENV(tre) ((MHI_TRE_GET_DWORD(tre, 0) >> 24) & 0xFF)
> > > +#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) ((MHI_TRE_GET_DWORD(tre, 0) >> 16) & 0xFF)
> > > +#define MHI_TRE_GET_EV_LINKSPEED(tre) ((MHI_TRE_GET_DWORD(tre, 1) >> 24) & 0xFF)
> > > +#define MHI_TRE_GET_EV_LINKWIDTH(tre) (MHI_TRE_GET_DWORD(tre, 0) & 0xFF)
> > > /* Transfer descriptor macros */
> > > -#define MHI_TRE_DATA_PTR(ptr) (cpu_to_le64(ptr))
> > > -#define MHI_TRE_DATA_DWORD0(len) (cpu_to_le32(len & MHI_MAX_MTU))
> > > -#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
> > > - | (ieot << 9) | (ieob << 8) | chain))
> > > +#define MHI_TRE_DATA_PTR(ptr) cpu_to_le64(ptr)
> > > +#define MHI_TRE_DATA_DWORD0(len) cpu_to_le32(len & MHI_MAX_MTU)
> > > +#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) (cpu_to_le32((2 << 16) | (bei << 10) \
> > > + | (ieot << 9) | (ieob << 8) | chain))
> > > /* RSC transfer descriptor macros */
> > > -#define MHI_RSCTRE_DATA_PTR(ptr, len) (cpu_to_le64(((u64)len << 48) | ptr))
> > > -#define MHI_RSCTRE_DATA_DWORD0(cookie) (cpu_to_le32(cookie))
> > > -#define MHI_RSCTRE_DATA_DWORD1 (cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16))
> > > +#define MHI_RSCTRE_DATA_PTR(ptr, len) cpu_to_le64(((u64)len << 48) | ptr)
> > > +#define MHI_RSCTRE_DATA_DWORD0(cookie) cpu_to_le32(cookie)
> > > +#define MHI_RSCTRE_DATA_DWORD1 cpu_to_le32(MHI_PKT_TYPE_COALESCING << 16)
> > > enum mhi_pkt_type {
> > > MHI_PKT_TYPE_INVALID = 0x0,
> > > diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h
> > > index 622de6ba1a0b..762055a6ec9f 100644
> > > --- a/drivers/bus/mhi/host/internal.h
> > > +++ b/drivers/bus/mhi/host/internal.h
> > > @@ -11,197 +11,84 @@
> > > extern struct bus_type mhi_bus_type;
> > > -#define MHIREGLEN (0x0)
> > > -#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF)
> > > -#define MHIREGLEN_MHIREGLEN_SHIFT (0)
> > > -
> > > -#define MHIVER (0x8)
> > > -#define MHIVER_MHIVER_MASK (0xFFFFFFFF)
> > > -#define MHIVER_MHIVER_SHIFT (0)
> > > -
> > > -#define MHICFG (0x10)
> > > -#define MHICFG_NHWER_MASK (0xFF000000)
> > > -#define MHICFG_NHWER_SHIFT (24)
> > > -#define MHICFG_NER_MASK (0xFF0000)
> > > -#define MHICFG_NER_SHIFT (16)
> > > -#define MHICFG_NHWCH_MASK (0xFF00)
> > > -#define MHICFG_NHWCH_SHIFT (8)
> > > -#define MHICFG_NCH_MASK (0xFF)
> > > -#define MHICFG_NCH_SHIFT (0)
> > > -
> > > -#define CHDBOFF (0x18)
> > > -#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF)
> > > -#define CHDBOFF_CHDBOFF_SHIFT (0)
> > > -
> > > -#define ERDBOFF (0x20)
> > > -#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF)
> > > -#define ERDBOFF_ERDBOFF_SHIFT (0)
> > > -
> > > -#define BHIOFF (0x28)
> > > -#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF)
> > > -#define BHIOFF_BHIOFF_SHIFT (0)
> > > -
> > > -#define BHIEOFF (0x2C)
> > > -#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF)
> > > -#define BHIEOFF_BHIEOFF_SHIFT (0)
> > > -
> > > -#define DEBUGOFF (0x30)
> > > -#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF)
> > > -#define DEBUGOFF_DEBUGOFF_SHIFT (0)
> > > -
> > > -#define MHICTRL (0x38)
> > > -#define MHICTRL_MHISTATE_MASK (0x0000FF00)
> > > -#define MHICTRL_MHISTATE_SHIFT (8)
> > > -#define MHICTRL_RESET_MASK (0x2)
> > > -#define MHICTRL_RESET_SHIFT (1)
> > > -
> > > -#define MHISTATUS (0x48)
> > > -#define MHISTATUS_MHISTATE_MASK (0x0000FF00)
> > > -#define MHISTATUS_MHISTATE_SHIFT (8)
> > > -#define MHISTATUS_SYSERR_MASK (0x4)
> > > -#define MHISTATUS_SYSERR_SHIFT (2)
> > > -#define MHISTATUS_READY_MASK (0x1)
> > > -#define MHISTATUS_READY_SHIFT (0)
> > > -
> > > -#define CCABAP_LOWER (0x58)
> > > -#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF)
> > > -#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0)
> > > -
> > > -#define CCABAP_HIGHER (0x5C)
> > > -#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF)
> > > -#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0)
> > > -
> > > -#define ECABAP_LOWER (0x60)
> > > -#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF)
> > > -#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0)
> > > -
> > > -#define ECABAP_HIGHER (0x64)
> > > -#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF)
> > > -#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0)
> > > -
> > > -#define CRCBAP_LOWER (0x68)
> > > -#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF)
> > > -#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0)
> > > -
> > > -#define CRCBAP_HIGHER (0x6C)
> > > -#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF)
> > > -#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0)
> > > -
> > > -#define CRDB_LOWER (0x70)
> > > -#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF)
> > > -#define CRDB_LOWER_CRDB_LOWER_SHIFT (0)
> > > -
> > > -#define CRDB_HIGHER (0x74)
> > > -#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF)
> > > -#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0)
> > > -
> > > -#define MHICTRLBASE_LOWER (0x80)
> > > -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF)
> > > -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0)
> > > -
> > > -#define MHICTRLBASE_HIGHER (0x84)
> > > -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF)
> > > -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0)
> > > -
> > > -#define MHICTRLLIMIT_LOWER (0x88)
> > > -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF)
> > > -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0)
> > > -
> > > -#define MHICTRLLIMIT_HIGHER (0x8C)
> > > -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF)
> > > -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0)
> > > -
> > > -#define MHIDATABASE_LOWER (0x98)
> > > -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF)
> > > -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0)
> > > -
> > > -#define MHIDATABASE_HIGHER (0x9C)
> > > -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF)
> > > -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0)
> > > -
> > > -#define MHIDATALIMIT_LOWER (0xA0)
> > > -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF)
> > > -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0)
> > > -
> > > -#define MHIDATALIMIT_HIGHER (0xA4)
> > > -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF)
> > > -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0)
> > > +/* MHI registers */
> > > +#define MHIREGLEN REG_MHIREGLEN
> > > +#define MHIVER REG_MHIVER
> > > +#define MHICFG REG_MHICFG
> > > +#define CHDBOFF REG_CHDBOFF
> > > +#define ERDBOFF REG_ERDBOFF
> > > +#define BHIOFF REG_BHIOFF
> > > +#define BHIEOFF REG_BHIEOFF
> > > +#define DEBUGOFF REG_DEBUGOFF
> > > +#define MHICTRL REG_MHICTRL
> > > +#define MHISTATUS REG_MHISTATUS
> > > +#define CCABAP_LOWER REG_CCABAP_LOWER
> > > +#define CCABAP_HIGHER REG_CCABAP_HIGHER
> > > +#define ECABAP_LOWER REG_ECABAP_LOWER
> > > +#define ECABAP_HIGHER REG_ECABAP_HIGHER
> > > +#define CRCBAP_LOWER REG_CRCBAP_LOWER
> > > +#define CRCBAP_HIGHER REG_CRCBAP_HIGHER
> > > +#define CRDB_LOWER REG_CRDB_LOWER
> > > +#define CRDB_HIGHER REG_CRDB_HIGHER
> > > +#define MHICTRLBASE_LOWER REG_MHICTRLBASE_LOWER
> > > +#define MHICTRLBASE_HIGHER REG_MHICTRLBASE_HIGHER
> > > +#define MHICTRLLIMIT_LOWER REG_MHICTRLLIMIT_LOWER
> > > +#define MHICTRLLIMIT_HIGHER REG_MHICTRLLIMIT_HIGHER
> > > +#define MHIDATABASE_LOWER REG_MHIDATABASE_LOWER
> > > +#define MHIDATABASE_HIGHER REG_MHIDATABASE_HIGHER
> > > +#define MHIDATALIMIT_LOWER REG_MHIDATALIMIT_LOWER
> > > +#define MHIDATALIMIT_HIGHER REG_MHIDATALIMIT_HIGHER
> > > /* Host request register */
> > > -#define MHI_SOC_RESET_REQ_OFFSET (0xB0)
> > > -#define MHI_SOC_RESET_REQ BIT(0)
> > > -
> > > -/* MHI BHI offfsets */
> > > -#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_TXDB_SEQNUM_BMSK (0x3FFFFFFF)
> > > -#define BHI_TXDB_SEQNUM_SHFT (0)
> > > -#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)
> > > -#define BHI_STATUS_MASK (0xC0000000)
> > > -#define BHI_STATUS_SHIFT (30)
> > > -#define BHI_STATUS_ERROR (3)
> > > -#define BHI_STATUS_SUCCESS (2)
> > > -#define BHI_STATUS_RESET (0)
> > > -
> > > -/* MHI BHIE offsets */
> > > -#define BHIE_MSMSOCID_OFFS (0x0000)
> > > -#define BHIE_TXVECADDR_LOW_OFFS (0x002C)
> > > -#define BHIE_TXVECADDR_HIGH_OFFS (0x0030)
> > > -#define BHIE_TXVECSIZE_OFFS (0x0034)
> > > -#define BHIE_TXVECDB_OFFS (0x003C)
> > > -#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
> > > -#define BHIE_TXVECDB_SEQNUM_SHFT (0)
> > > -#define BHIE_TXVECSTATUS_OFFS (0x0044)
> > > -#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
> > > -#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
> > > -#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
> > > -#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_RXVECADDR_LOW_OFFS (0x0060)
> > > -#define BHIE_RXVECADDR_HIGH_OFFS (0x0064)
> > > -#define BHIE_RXVECSIZE_OFFS (0x0068)
> > > -#define BHIE_RXVECDB_OFFS (0x0070)
> > > -#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
> > > -#define BHIE_RXVECDB_SEQNUM_SHFT (0)
> > > -#define BHIE_RXVECSTATUS_OFFS (0x0078)
> > > -#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
> > > -#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
> > > -#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
> > > -#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)
> > > -
> > > -#define SOC_HW_VERSION_OFFS (0x224)
> > > -#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000)
> > > -#define SOC_HW_VERSION_FAM_NUM_SHFT (28)
> > > -#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000)
> > > -#define SOC_HW_VERSION_DEV_NUM_SHFT (16)
> > > -#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00)
> > > -#define SOC_HW_VERSION_MAJOR_VER_SHFT (8)
> > > -#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF)
> > > -#define SOC_HW_VERSION_MINOR_VER_SHFT (0)
> > > +#define MHI_SOC_RESET_REQ_OFFSET 0xb0
> > > +#define MHI_SOC_RESET_REQ BIT(0)
> > > +
> > > +/* MHI BHI registers */
> > > +#define BHI_BHIVERSION_MINOR REG_BHI_BHIVERSION_MINOR
> > > +#define BHI_BHIVERSION_MAJOR REG_BHI_BHIVERSION_MAJOR
> > > +#define BHI_IMGADDR_LOW REG_BHI_IMGADDR_LOW
> > > +#define BHI_IMGADDR_HIGH REG_BHI_IMGADDR_HIGH
> > > +#define BHI_IMGSIZE REG_BHI_IMGSIZE
> > > +#define BHI_RSVD1 REG_BHI_RSVD1
> > > +#define BHI_IMGTXDB REG_BHI_IMGTXDB
> > > +#define BHI_RSVD2 REG_BHI_RSVD2
> > > +#define BHI_INTVEC REG_BHI_INTVEC
> > > +#define BHI_RSVD3 REG_BHI_RSVD3
> > > +#define BHI_EXECENV REG_BHI_EXECENV
> > > +#define BHI_STATUS REG_BHI_STATUS
> > > +#define BHI_ERRCODE REG_BHI_ERRCODE
> > > +#define BHI_ERRDBG1 REG_BHI_ERRDBG1
> > > +#define BHI_ERRDBG2 REG_BHI_ERRDBG2
> > > +#define BHI_ERRDBG3 REG_BHI_ERRDBG3
> > > +#define BHI_SERIALNU REG_BHI_SERIALNU
> > > +#define BHI_SBLANTIROLLVER REG_BHI_SBLANTIROLLVER
> > > +#define BHI_NUMSEG REG_BHI_NUMSEG
> > > +#define BHI_MSMHWID(n) REG_BHI_MSMHWID(n)
> > > +#define BHI_OEMPKHASH(n) REG_BHI_OEMPKHASH(n)
> > > +#define BHI_RSVD5 REG_BHI_RSVD5
> > > +
> > > +/* MHI BHIE registers */
> > > +#define BHIE_MSMSOCID_OFFS REG_BHIE_MSMSOCID_OFFS
> > > +#define BHIE_TXVECADDR_LOW_OFFS REG_BHIE_TXVECADDR_LOW_OFFS
> > > +#define BHIE_TXVECADDR_HIGH_OFFS REG_BHIE_TXVECADDR_HIGH_OFFS
> > > +#define BHIE_TXVECSIZE_OFFS REG_BHIE_TXVECSIZE_OFFS
> > > +#define BHIE_TXVECDB_OFFS REG_BHIE_TXVECDB_OFFS
> > > +#define BHIE_TXVECSTATUS_OFFS REG_BHIE_TXVECSTATUS_OFFS
> > > +#define BHIE_RXVECADDR_LOW_OFFS REG_BHIE_RXVECADDR_LOW_OFFS
> > > +#define BHIE_RXVECADDR_HIGH_OFFS REG_BHIE_RXVECADDR_HIGH_OFFS
> > > +#define BHIE_RXVECSIZE_OFFS REG_BHIE_RXVECSIZE_OFFS
> > > +#define BHIE_RXVECDB_OFFS REG_BHIE_RXVECDB_OFFS
> > > +#define BHIE_RXVECSTATUS_OFFS REG_BHIE_RXVECSTATUS_OFFS
> > > +
> > > +#define SOC_HW_VERSION_OFFS 0x224
> > > +#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
> > > +#define SOC_HW_VERSION_FAM_NUM_SHFT 28
> > > +#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
> > > +#define SOC_HW_VERSION_DEV_NUM_SHFT 16
> > > +#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
> > > +#define SOC_HW_VERSION_MAJOR_VER_SHFT 8
> > > +#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
> > > +#define SOC_HW_VERSION_MINOR_VER_SHFT 0
> > > struct mhi_ctxt {
> > > struct mhi_event_ctxt *er_ctxt;
> >
>

2022-02-17 11:48:16

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

On Tue, Feb 15, 2022 at 02:02:41PM -0600, Alex Elder wrote:

[...]

> > +#define MHI_REG_OFFSET 0x100
> > +#define BHI_REG_OFFSET 0x200
>
> Rather than defining the REG_OFFSET values here and adding
> them to every definition below, why not have the base
> address used (e.g., in mhi_write_reg_field()) be adjusted
> by the constant amount?
>
> I'm just looking at mhi_init_mmio() (in the existing code)
> as an example, but for example, the base address used
> comes from mhi_cntrl->regs. Can you instead just define
> a pointer somewhere that is the base of the MHI register
> range, which is already offset by the appropriate amount?
>

I've defined two set of APIs for MHI and BHI read/write. They will add the
respective offsets.

> > +

[...]

> > +/* Generic context */
> > +struct mhi_generic_ctx {
> > + __u32 reserved0;
> > + __u32 reserved1;
> > + __u32 reserved2;
> > +
> > + __u64 rbase __packed __aligned(4);
> > + __u64 rlen __packed __aligned(4);
> > + __u64 rp __packed __aligned(4);
> > + __u64 wp __packed __aligned(4);
> > +};
>
> I'm pretty sure this constitutes an external interface, so
> every field should have its endianness annotated.
>
> Mentioned elsewhere, I think you can define the structure
> with those attributes rather than the multiple fields.
>

As I said before, this was suggested by Arnd during MHI host review. He
suggested adding the alignment and packed to only members that require
them.

But I think I should change it now...

> > +
> > +enum mhi_ep_ring_type {
> > + RING_TYPE_CMD = 0,
> > + RING_TYPE_ER,
> > + RING_TYPE_CH,
> > +};
> > +
> > +struct mhi_ep_ring_element {
> > + u64 ptr;
> > + u32 dword[2];
> > +};
>
> Are these host resident rings? Even if not, this is an external
> interface, so this should be defined with explicit endianness.
> The cpu_to_le64() call will be a no-op so there is no cost
> to correcting this.
>

Ah, this should be reusing the "struct mhi_tre" defined in host. Will do.

> > +
> > +/* 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 {
> > + struct mhi_ep_cntrl *mhi_cntrl;
> > + int (*ring_cb)(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el);
> > + union mhi_ep_ring_ctx *ring_ctx;
> > + struct mhi_ep_ring_element *ring_cache;
> > + enum mhi_ep_ring_type type;
> > + size_t rd_offset;
> > + size_t wr_offset;
> > + size_t ring_size;
> > + u32 db_offset_h;
> > + u32 db_offset_l;
> > + u32 ch_id;
> > +};
>
> Not sure about the db_offset fields, etc. here, but it's possible
> they need endianness annotations. I'm going to stop making this
> comment; please make sure anything that's exposed to the host
> specifies that it's little endian. (The host and endpoint should
> have a common definition of these shared structures anyway; maybe
> I'm misreading this or assuming something incorrectly.)
>

db_offset_* just holds the register offsets so they don't require
endianness annotation. All MMIO read/write are using readl/writel APIs
and they handle the endianness conversion implicitly.

Rest of the host memory accesses are annotated properly.

> > +

[...]

> > + /*
> > + * Allocate max_channels supported by the MHI endpoint and populate
> > + * only the defined channels
> > + */
> > + mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
> > + GFP_KERNEL);
> > + if (!mhi_cntrl->mhi_chan)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < config->num_channels; i++) {
> > + struct mhi_ep_chan *mhi_chan;
>
> This entire block could be encapsulated in mhi_channel_add()
> or something,

Wrapping up in a function is useful if the same code is used in
different places. But I don't think it adds any value here.

>
> > + ch_cfg = &config->ch_cfg[i];
>
> Move the above assignment down a few lines, to just before
> where it's used.
>

No, ch_cfg is used just below this.

> > +
> > + chan = ch_cfg->num;
> > + if (chan >= mhi_cntrl->max_chan) {
> > + dev_err(dev, "Channel %d not available\n", chan);
>
> Maybe report the maximum channel so it's obvious why it's
> not available.
>
> > + goto error_chan_cfg;
> > + }
> > +
> > + /* Bi-directional and direction less channels are not supported */
> > + if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
> > + dev_err(dev, "Invalid channel configuration\n");
>
> Maybe be more specific in your message about what's wrong here.
>
> > + goto error_chan_cfg;
> > + }
> > +
> > + mhi_chan = &mhi_cntrl->mhi_chan[chan];
> > + mhi_chan->name = ch_cfg->name;
> > + mhi_chan->chan = chan;
> > + mhi_chan->dir = ch_cfg->dir;
> > + mutex_init(&mhi_chan->lock);
> > + }
> > +
> > + return 0;
> > +
> > +error_chan_cfg:
> > + kfree(mhi_cntrl->mhi_chan);
>
> I'm not sure what the caller does, but maybe null this
> after it's freed, or don't assign mhi_cntrll->mhi_chan
> until the initialization is successful.
>

This is not required here as there will be no access to the pointer
after failing.

>
> > + return ret;
> > +}
> > +
> > +/*
> > + * Allocate channel and command rings here. Event rings will be allocated
> > + * in mhi_ep_power_up() as the config comes from the host.
> > + */
> > +int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> > + const struct mhi_ep_cntrl_config *config)
> > +{
> > + struct mhi_ep_device *mhi_dev;
> > + int ret;
> > +
> > + if (!mhi_cntrl || !mhi_cntrl->cntrl_dev)
> > + return -EINVAL;
> > +
> > + ret = parse_ch_cfg(mhi_cntrl, config);
> > + if (ret)
> > + return ret;
> > +
> > + mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
>
> I said before I thought it was silly to even define NR_OF_CMD_RINGS.
> Does the MHI specification actually allow more than one command
> ring for a given MHI controller? Ever?
>

MHI spec doesn't limit the number of command rings. Eventhough I don't
envision adding more command rings in the future, I'm going to keep this
macro for now as the MHI host does the same.

[...]

> > diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> > new file mode 100644
> > index 000000000000..20238e9df1b3
> > --- /dev/null
> > +++ b/include/linux/mhi_ep.h

[...]

> > +struct mhi_ep_device {
> > + struct device dev;
> > + struct mhi_ep_cntrl *mhi_cntrl;
> > + const struct mhi_device_id *id;
> > + const char *name;
> > + struct mhi_ep_chan *ul_chan;
> > + struct mhi_ep_chan *dl_chan;
> > + enum mhi_device_type dev_type;
>
> There are two device types, controller and transfer. Unless
> there is ever going to be anything more than that, I think
> the distinction is better represented as a Boolean, such as:
>
> bool controller;

Again, this is how it is done in MHI host also. Since I'm going to
maintain both stacks, it makes it easier for me if similarities are
maintained. But I'll keep this suggestion and the one above for later.

Thanks,
Mani

2022-02-17 19:48:15

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

On 2/17/22 3:53 AM, Manivannan Sadhasivam wrote:
> On Tue, Feb 15, 2022 at 02:02:41PM -0600, Alex Elder wrote:
>
> [...]
>
>>> +#define MHI_REG_OFFSET 0x100
>>> +#define BHI_REG_OFFSET 0x200

. . .

> [...]
>
>>> +/* Generic context */
>>> +struct mhi_generic_ctx {
>>> + __u32 reserved0;
>>> + __u32 reserved1;
>>> + __u32 reserved2;
>>> +
>>> + __u64 rbase __packed __aligned(4);
>>> + __u64 rlen __packed __aligned(4);
>>> + __u64 rp __packed __aligned(4);
>>> + __u64 wp __packed __aligned(4);
>>> +};
>>
>> I'm pretty sure this constitutes an external interface, so
>> every field should have its endianness annotated.
>>
>> Mentioned elsewhere, I think you can define the structure
>> with those attributes rather than the multiple fields.
>>
>
> As I said before, this was suggested by Arnd during MHI host review. He
> suggested adding the alignment and packed to only members that require
> them.
>
> But I think I should change it now...

Despite suggesting this more than once, I'm not 100% sure it's
even a correct suggestion. I trust Arnd's judgement, and I
can see the value of being explicit about *which* fields have
the alignment requirement. So I'll leave it up to you to
decide... If you make my suggested change, be sure to test
it. But I'm fine if you leave these as-is.

>>> +enum mhi_ep_ring_type {
>>> + RING_TYPE_CMD = 0,
>>> + RING_TYPE_ER,
>>> + RING_TYPE_CH,
>>> +};
>>> +
>>> +struct mhi_ep_ring_element {
>>> + u64 ptr;
>>> + u32 dword[2];
>>> +};
>>
>> Are these host resident rings? Even if not, this is an external
>> interface, so this should be defined with explicit endianness.
>> The cpu_to_le64() call will be a no-op so there is no cost
>> to correcting this.
>>
>
> Ah, this should be reusing the "struct mhi_tre" defined in host. Will do.
>
>>> +
>>> +/* 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 {
>>> + struct mhi_ep_cntrl *mhi_cntrl;
>>> + int (*ring_cb)(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el);
>>> + union mhi_ep_ring_ctx *ring_ctx;
>>> + struct mhi_ep_ring_element *ring_cache;
>>> + enum mhi_ep_ring_type type;
>>> + size_t rd_offset;
>>> + size_t wr_offset;
>>> + size_t ring_size;
>>> + u32 db_offset_h;
>>> + u32 db_offset_l;
>>> + u32 ch_id;
>>> +};
>>
>> Not sure about the db_offset fields, etc. here, but it's possible
>> they need endianness annotations. I'm going to stop making this
>> comment; please make sure anything that's exposed to the host
>> specifies that it's little endian. (The host and endpoint should
>> have a common definition of these shared structures anyway; maybe
>> I'm misreading this or assuming something incorrectly.)
>>
>
> db_offset_* just holds the register offsets so they don't require
> endianness annotation. All MMIO read/write are using readl/writel APIs
> and they handle the endianness conversion implicitly.
>
> Rest of the host memory accesses are annotated properly.

OK, good.

>
>>> +
>
> [...]
>
>>> + /*
>>> + * Allocate max_channels supported by the MHI endpoint and populate
>>> + * only the defined channels
>>> + */
>>> + mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
>>> + GFP_KERNEL);
>>> + if (!mhi_cntrl->mhi_chan)
>>> + return -ENOMEM;
>>> +
>>> + for (i = 0; i < config->num_channels; i++) {
>>> + struct mhi_ep_chan *mhi_chan;
>>
>> This entire block could be encapsulated in mhi_channel_add()
>> or something,
>
> Wrapping up in a function is useful if the same code is used in
> different places. But I don't think it adds any value here.
>
>>
>>> + ch_cfg = &config->ch_cfg[i];
>>
>> Move the above assignment down a few lines, to just before
>> where it's used.
>>
>
> No, ch_cfg is used just below this.

Yes you're right, I missed that.

>>> +
>>> + chan = ch_cfg->num;
>>> + if (chan >= mhi_cntrl->max_chan) {
>>> + dev_err(dev, "Channel %d not available\n", chan);
>>
>> Maybe report the maximum channel so it's obvious why it's
>> not available.
>>
>>> + goto error_chan_cfg;
>>> + }
>>> +
>>> + /* Bi-directional and direction less channels are not supported */
>>> + if (ch_cfg->dir == DMA_BIDIRECTIONAL || ch_cfg->dir == DMA_NONE) {
>>> + dev_err(dev, "Invalid channel configuration\n");
>>
>> Maybe be more specific in your message about what's wrong here.
>>
>>> + goto error_chan_cfg;
>>> + }
>>> +
>>> + mhi_chan = &mhi_cntrl->mhi_chan[chan];
>>> + mhi_chan->name = ch_cfg->name;
>>> + mhi_chan->chan = chan;
>>> + mhi_chan->dir = ch_cfg->dir;
>>> + mutex_init(&mhi_chan->lock);
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +error_chan_cfg:
>>> + kfree(mhi_cntrl->mhi_chan);
>>
>> I'm not sure what the caller does, but maybe null this
>> after it's freed, or don't assign mhi_cntrll->mhi_chan
>> until the initialization is successful.
>>
>
> This is not required here as there will be no access to the pointer
> after failing.

OK.

>>> + return ret;
>>> +}
>>> +
>>> +/*
>>> + * Allocate channel and command rings here. Event rings will be allocated
>>> + * in mhi_ep_power_up() as the config comes from the host.
>>> + */
>>> +int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
>>> + const struct mhi_ep_cntrl_config *config)
>>> +{
>>> + struct mhi_ep_device *mhi_dev;
>>> + int ret;
>>> +
>>> + if (!mhi_cntrl || !mhi_cntrl->cntrl_dev)
>>> + return -EINVAL;
>>> +
>>> + ret = parse_ch_cfg(mhi_cntrl, config);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
>>
>> I said before I thought it was silly to even define NR_OF_CMD_RINGS.
>> Does the MHI specification actually allow more than one command
>> ring for a given MHI controller? Ever?
>>
>
> MHI spec doesn't limit the number of command rings. Eventhough I don't
> envision adding more command rings in the future, I'm going to keep this
> macro for now as the MHI host does the same.

OK.

> [...]
>
>>> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
>>> new file mode 100644
>>> index 000000000000..20238e9df1b3
>>> --- /dev/null
>>> +++ b/include/linux/mhi_ep.h
>
> [...]
>
>>> +struct mhi_ep_device {
>>> + struct device dev;
>>> + struct mhi_ep_cntrl *mhi_cntrl;
>>> + const struct mhi_device_id *id;
>>> + const char *name;
>>> + struct mhi_ep_chan *ul_chan;
>>> + struct mhi_ep_chan *dl_chan;
>>> + enum mhi_device_type dev_type;
>>
>> There are two device types, controller and transfer. Unless
>> there is ever going to be anything more than that, I think
>> the distinction is better represented as a Boolean, such as:
>>
>> bool controller;
>
> Again, this is how it is done in MHI host also. Since I'm going to
> maintain both stacks, it makes it easier for me if similarities are
> maintained. But I'll keep this suggestion and the one above for later.

Sounds good. Thanks.

-Alex

> Thanks,
> Mani

2022-02-17 23:49:16

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 09/25] bus: mhi: ep: Add support for registering MHI endpoint client drivers

On 2/17/22 4:20 AM, Manivannan Sadhasivam wrote:
> On Tue, Feb 15, 2022 at 02:02:50PM -0600, Alex Elder wrote:
>
> [...]
>
>>> +static int mhi_ep_driver_remove(struct device *dev)
>>> +{
>>> + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
>>> + struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
>>> + struct mhi_result result = {};
>>> + struct mhi_ep_chan *mhi_chan;
>>> + int dir;
>>> +
>>> + /* Skip if it is a controller device */
>>> + if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
>>> + return 0;
>>> +
>>
>> It would be my preference to encapsulate the body of the
>> following loop into a called function, then call that once
>> for the UL channel and once for the DL channel.
>>
>
> This follows the host stack, so I'd like to keep it the same.

I think you should change both, but I'll leave that up to you.

>>> + /* Disconnect the channels associated with the driver */
>>> + for (dir = 0; dir < 2; dir++) {
>>> + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
>>> +
>>> + if (!mhi_chan)
>>> + continue;
>>> +
>>> + mutex_lock(&mhi_chan->lock);
>>> + /* Send channel disconnect status to the client driver */
>>> + if (mhi_chan->xfer_cb) {
>>> + result.transaction_status = -ENOTCONN;
>>> + result.bytes_xferd = 0;
>>> + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
>>
>> It appears the result is ignored here. If so, can we
>> define the xfer_cb() function so that a NULL pointer may
>> be supplied by the caller in cases like this?
>>
>
> result is not ignored, only the bytes_xfered. "transaction_status" will
> be used by the client drivers for error handling.

Sorry, I was looking at the code *after* the call, and was
ignoring that it was information being passed in... My
mistake.

>>> + }
>>> +
>>> + /* Set channel state to DISABLED */
>>
>> That comment is a little tautological. Just omit it.
>>
>>> + mhi_chan->state = MHI_CH_STATE_DISABLED;
>>> + mhi_chan->xfer_cb = NULL;
>>> + mutex_unlock(&mhi_chan->lock);
>>> + }
>>> +
>>> + /* Remove the client driver now */
>>> + mhi_drv->remove(mhi_dev);
>>> +
>>> + return 0;
>>> +}
>
> [...]
>
>>> +struct mhi_ep_driver {
>>> + const struct mhi_device_id *id_table;
>>> + struct device_driver driver;
>>> + int (*probe)(struct mhi_ep_device *mhi_ep,
>>> + const struct mhi_device_id *id);
>>> + void (*remove)(struct mhi_ep_device *mhi_ep);
>>
>> I get confused by the "ul" versus "dl" naming scheme here.
>> Is "ul" from the perspective of the host, meaning upload
>> is from the host toward the WWAN network (and therefore
>> toward the SDX AP), and download is from the WWAN toward
>> the host? Somewhere this should be stated clearly in
>> comments; maybe I just missed it.
>>
>
> Yes UL and DL are as per host context. I didn't state this explicitly
> since this is the MHI host stack behaviour but I'll add a comment for
> clarity

Sounds good, thanks.

-Alex

>
> Thanks,
> Mani

2022-02-22 07:03:30

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 14/25] bus: mhi: ep: Add support for managing MHI state machine

On Tue, Feb 15, 2022 at 04:39:24PM -0600, Alex Elder wrote:
> On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> > Add support for managing the MHI state machine by controlling the state
> > transitions. Only the following MHI state transitions are supported:
> >
> > 1. Ready state
> > 2. M0 state
> > 3. M3 state
> > 4. SYS_ERR state
> >
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
>
> Minor suggestions here. -Alex
>
> > ---
> > drivers/bus/mhi/ep/Makefile | 2 +-
> > drivers/bus/mhi/ep/internal.h | 11 +++
> > drivers/bus/mhi/ep/main.c | 51 ++++++++++-
> > drivers/bus/mhi/ep/sm.c | 168 ++++++++++++++++++++++++++++++++++
> > include/linux/mhi_ep.h | 6 ++
> > 5 files changed, 236 insertions(+), 2 deletions(-)
> > create mode 100644 drivers/bus/mhi/ep/sm.c
> >
> > diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile
> > index 7ba0e04801eb..aad85f180b70 100644
> > --- a/drivers/bus/mhi/ep/Makefile
> > +++ b/drivers/bus/mhi/ep/Makefile
> > @@ -1,2 +1,2 @@
> > obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o
> > -mhi_ep-y := main.o mmio.o ring.o
> > +mhi_ep-y := main.o mmio.o ring.o sm.o
> > diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h
> > index fd63f79c6aec..e4e8f06c2898 100644
> > --- a/drivers/bus/mhi/ep/internal.h
> > +++ b/drivers/bus/mhi/ep/internal.h
> > @@ -173,6 +173,11 @@ 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;
> > @@ -230,5 +235,11 @@ 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_ep_execenv 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);
> > #endif
> > diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> > index 61f066c6286b..ccb3c2795041 100644
> > --- a/drivers/bus/mhi/ep/main.c
> > +++ b/drivers/bus/mhi/ep/main.c
> > @@ -185,6 +185,43 @@ static void mhi_ep_ring_worker(struct work_struct *work)
> > }
> > }
> > +static void mhi_ep_state_worker(struct work_struct *work)
> > +{
> > + struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, state_work);
> > + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> > + struct mhi_ep_state_transition *itr, *tmp;
> > + unsigned long flags;
> > + LIST_HEAD(head);
> > + int ret;
> > +
> > + spin_lock_irqsave(&mhi_cntrl->list_lock, flags);
> > + list_splice_tail_init(&mhi_cntrl->st_transition_list, &head);
> > + spin_unlock_irqrestore(&mhi_cntrl->list_lock, flags);
> > +
> > + list_for_each_entry_safe(itr, tmp, &head, node) {
> > + list_del(&itr->node);
> > + dev_dbg(dev, "Handling MHI state transition to %s\n",
> > + mhi_state_str(itr->state));
> > +
> > + switch (itr->state) {
> > + case MHI_STATE_M0:
> > + ret = mhi_ep_set_m0_state(mhi_cntrl);
> > + if (ret)
> > + dev_err(dev, "Failed to transition to M0 state\n");
> > + break;
> > + case MHI_STATE_M3:
> > + ret = mhi_ep_set_m3_state(mhi_cntrl);
> > + if (ret)
> > + dev_err(dev, "Failed to transition to M3 state\n");
> > + break;
> > + default:
> > + dev_err(dev, "Invalid MHI state transition: %d\n", itr->state);
> > + break;
> > + }
> > + kfree(itr);
> > + }
> > +}
> > +
> > static void mhi_ep_release_device(struct device *dev)
> > {
> > struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> > @@ -386,6 +423,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> > }
> > INIT_WORK(&mhi_cntrl->ring_work, mhi_ep_ring_worker);
> > + INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker);
> > mhi_cntrl->ring_wq = alloc_workqueue("mhi_ep_ring_wq", 0, 0);
> > if (!mhi_cntrl->ring_wq) {
> > @@ -393,8 +431,16 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> > goto err_free_cmd;
> > }
> > + mhi_cntrl->state_wq = alloc_workqueue("mhi_ep_state_wq", 0, 0);
>
> Maybe it's not a big deal, but do we really need several separate
> work queues? Would one suffice? Could a system workqueue be used
> in some cases (such as state changes)?

Good question. The reason to have 2 separate workqueue was to avoid running the
two work items parallely during bringup. But the code has changed a lot afterwards,
so this could go into the other workqueue.

Thanks,
Mani

2022-03-04 21:59:13

by Jeffrey Hugo

[permalink] [raw]
Subject: Re: [PATCH v3 08/25] bus: mhi: ep: Add support for registering MHI endpoint controllers

On 2/17/2022 2:53 AM, Manivannan Sadhasivam wrote:
> On Tue, Feb 15, 2022 at 02:02:41PM -0600, Alex Elder wrote:
>
> [...]
>
>>> +#define MHI_REG_OFFSET 0x100
>>> +#define BHI_REG_OFFSET 0x200
>>
>> Rather than defining the REG_OFFSET values here and adding
>> them to every definition below, why not have the base
>> address used (e.g., in mhi_write_reg_field()) be adjusted
>> by the constant amount?
>>
>> I'm just looking at mhi_init_mmio() (in the existing code)
>> as an example, but for example, the base address used
>> comes from mhi_cntrl->regs. Can you instead just define
>> a pointer somewhere that is the base of the MHI register
>> range, which is already offset by the appropriate amount?
>>
>
> I've defined two set of APIs for MHI and BHI read/write. They will add the
> respective offsets.
>

While you are making changes, maybe don't have a set BHI_REG_OFFSET?
Sure, I think it is always 0x200, but that is a convention and nothing
I've seen in the spec mandates it. You can derive it from the bhi
offset register.

This way, if it ever moves in some future chip, this code should just work.