[PATCH net] net: wwan: iosm: Fix 7360 WWAN card control channel mapping

From: Shane Parslow
Date: Mon Sep 26 2022 - 00:07:32 EST


This patch fixes the control channel mapping for the 7360, which was
previously the same as the 7560.

As shown by the reverse engineering efforts of James Wah [1], the layout
of channels on the 7360 is actually somewhat different from that of the
7560.

A new ipc_chnl_cfg is added specifically for the 7360. The new config
updates channel 7 to be an AT port and removes the mbim interface, as
it does not exist on the 7360. The config is otherwise left the same as
the 7560. ipc_chnl_cfg_get is updated to switch between the two configs.
In ipc_imem, a special case for the mbim port is removed as it no longer
exists in the 7360 ipc_chnl_cfg.

As a result of this, the second userspace AT port now functions whereas
previously it was routed to the trace channel. Modem crashes ("confused
phase", "msg timeout", "PORT open refused") resulting from garbage being
sent to the modem are also fixed.

[1] https://github.com/xmm7360/reversing

Signed-off-by: Shane Parslow <shaneparslow808@xxxxxxxxx>
---
drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c | 58 ++++++++++++++++++++---
drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h | 7 ++-
drivers/net/wwan/iosm/iosm_ipc_devlink.c | 3 +-
drivers/net/wwan/iosm/iosm_ipc_imem.c | 12 ++---
drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 3 +-
drivers/net/wwan/iosm/iosm_ipc_trace.c | 3 +-
6 files changed, 68 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c
index 128c999e08bb..011bae887f8b 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.c
@@ -5,6 +5,7 @@

#include <linux/wwan.h>

+#include "iosm_ipc_imem.h"
#include "iosm_ipc_chnl_cfg.h"

/* Max. sizes of a downlink buffers */
@@ -28,10 +29,10 @@
/* MUX acc backoff 1ms */
#define IRQ_ACC_BACKOFF_MUX 1000

-/* Modem channel configuration table
+/* 7560 modem channel configuration table
* Always reserve element zero for flash channel.
*/
-static struct ipc_chnl_cfg modem_cfg[] = {
+static struct ipc_chnl_cfg modem_cfg_7560[IPC_MEM_MAX_CHANNELS] = {
/* IP Mux */
{ IPC_MEM_IP_CHL_ID_0, IPC_MEM_PIPE_0, IPC_MEM_PIPE_1,
IPC_MEM_MAX_TDS_MUX_LITE_UL, IPC_MEM_MAX_TDS_MUX_LITE_DL,
@@ -66,11 +67,56 @@ static struct ipc_chnl_cfg modem_cfg[] = {
IPC_MEM_MAX_DL_FLASH_BUF_SIZE, WWAN_PORT_UNKNOWN },
};

-int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg, int index)
+/* Channel layout for the 7360
+ * Copied from the 7560, and modified based on the reverse
+ * engineering efforts of James Wah
+ */
+static struct ipc_chnl_cfg modem_cfg_7360[IPC_MEM_MAX_CHANNELS] = {
+ /* IP Mux */
+ { IPC_MEM_IP_CHL_ID_0, IPC_MEM_PIPE_0, IPC_MEM_PIPE_1,
+ IPC_MEM_MAX_TDS_MUX_LITE_UL, IPC_MEM_MAX_TDS_MUX_LITE_DL,
+ IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE, WWAN_PORT_UNKNOWN },
+ /* RPC - 0 */
+ { IPC_MEM_CTRL_CHL_ID_1, IPC_MEM_PIPE_2, IPC_MEM_PIPE_3,
+ IPC_MEM_MAX_TDS_RPC, IPC_MEM_MAX_TDS_RPC,
+ IPC_MEM_MAX_DL_RPC_BUF_SIZE, WWAN_PORT_UNKNOWN },
+ /* Trace? */
+ { IPC_MEM_CTRL_CHL_ID_2, IPC_MEM_PIPE_4, IPC_MEM_PIPE_5,
+ IPC_MEM_TDS_TRC, IPC_MEM_TDS_TRC, IPC_MEM_MAX_DL_TRC_BUF_SIZE,
+ WWAN_PORT_UNKNOWN },
+ /* Trace? */
+ { IPC_MEM_CTRL_CHL_ID_3, IPC_MEM_PIPE_6, IPC_MEM_PIPE_7,
+ IPC_MEM_TDS_TRC, IPC_MEM_TDS_TRC, IPC_MEM_MAX_DL_TRC_BUF_SIZE,
+ WWAN_PORT_UNKNOWN },
+ /* IAT0 */
+ { IPC_MEM_CTRL_CHL_ID_4, IPC_MEM_PIPE_8, IPC_MEM_PIPE_9,
+ IPC_MEM_MAX_TDS_AT, IPC_MEM_MAX_TDS_AT, IPC_MEM_MAX_DL_AT_BUF_SIZE,
+ WWAN_PORT_AT },
+ /* Unknown */
+ { IPC_MEM_CTRL_CHL_ID_5, IPC_MEM_PIPE_10, IPC_MEM_PIPE_11,
+ IPC_MEM_MAX_TDS_LOOPBACK, IPC_MEM_MAX_TDS_LOOPBACK,
+ IPC_MEM_MAX_DL_LOOPBACK_SIZE, WWAN_PORT_UNKNOWN },
+ /* Unknown */
+ { IPC_MEM_CTRL_CHL_ID_6, IPC_MEM_PIPE_12, IPC_MEM_PIPE_13,
+ IPC_MEM_MAX_TDS_MBIM, IPC_MEM_MAX_TDS_MBIM,
+ IPC_MEM_MAX_DL_MBIM_BUF_SIZE, WWAN_PORT_UNKNOWN },
+ /* IAT1 */
+ { IPC_MEM_CTRL_CHL_ID_7, IPC_MEM_PIPE_14, IPC_MEM_PIPE_15,
+ IPC_MEM_MAX_TDS_AT, IPC_MEM_MAX_TDS_AT,
+ IPC_MEM_MAX_DL_AT_BUF_SIZE, WWAN_PORT_AT },
+};
+
+int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg,
+ struct pci_dev *dev, int index)
{
- if (index >= ARRAY_SIZE(modem_cfg)) {
- pr_err("index: %d and array size %zu", index,
- ARRAY_SIZE(modem_cfg));
+ struct ipc_chnl_cfg *modem_cfg = modem_cfg_7560;
+
+ if (dev->device == INTEL_CP_DEVICE_7360_ID)
+ modem_cfg = modem_cfg_7360;
+
+ if (index >= IPC_MEM_MAX_CHANNELS) {
+ pr_err("index: %d and array size %d", index,
+ IPC_MEM_MAX_CHANNELS);
return -ECHRNG;
}

diff --git a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h
index e77084e76718..4703c2baf3e1 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h
+++ b/drivers/net/wwan/iosm/iosm_ipc_chnl_cfg.h
@@ -6,6 +6,8 @@
#ifndef IOSM_IPC_CHNL_CFG_H
#define IOSM_IPC_CHNL_CFG_H

+#include <linux/pci.h>
+
#include "iosm_ipc_mux.h"

/* Number of TDs on the trace channel */
@@ -51,10 +53,11 @@ struct ipc_chnl_cfg {
/**
* ipc_chnl_cfg_get - Get pipe configuration.
* @chnl_cfg: Array of ipc_chnl_cfg struct
+ * @dev: PCI device struct of the modem
* @index: Channel index (upto MAX_CHANNELS)
*
* Return: 0 on success and failure value on error
*/
-int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg, int index);
-
+int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg,
+ struct pci_dev *dev, int index);
#endif
diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.c b/drivers/net/wwan/iosm/iosm_ipc_devlink.c
index 17da85a8f337..e5dcff9845cc 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_devlink.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.c
@@ -272,7 +272,8 @@ struct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem)
goto region_create_fail;
}

- if (ipc_chnl_cfg_get(&chnl_cfg_flash, IPC_MEM_CTRL_CHL_ID_7) < 0)
+ if (ipc_chnl_cfg_get(&chnl_cfg_flash, ipc_imem->pcie->pci,
+ IPC_MEM_CTRL_CHL_ID_7) < 0)
goto chnl_get_fail;

ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL,
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c
index 1e6a47976642..98055532e065 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c
@@ -310,7 +310,9 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem,
ipc_pcie_addr_unmap(ipc_imem->pcie, IPC_CB(skb)->len,
IPC_CB(skb)->mapping,
IPC_CB(skb)->direction);
- if (port_id == IPC_MEM_CTRL_CHL_ID_7)
+ /* 7360: Channel 7 is an AT port, do not fwd to devlink */
+ if (port_id == IPC_MEM_CTRL_CHL_ID_7 &&
+ ipc_imem->pcie->pci->device != INTEL_CP_DEVICE_7360_ID)
ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink,
skb);
else if (ipc_is_trace_channel(ipc_imem, port_id))
@@ -585,13 +587,9 @@ static void ipc_imem_run_state_worker(struct work_struct *instance)
ipc_imem->mux->wwan = ipc_imem->wwan;

while (ctrl_chl_idx < IPC_MEM_MAX_CHANNELS) {
- if (!ipc_chnl_cfg_get(&chnl_cfg_port, ctrl_chl_idx)) {
+ if (!ipc_chnl_cfg_get(&chnl_cfg_port, ipc_imem->pcie->pci,
+ ctrl_chl_idx)) {
ipc_imem->ipc_port[ctrl_chl_idx] = NULL;
- if (ipc_imem->pcie->pci->device == INTEL_CP_DEVICE_7360_ID &&
- chnl_cfg_port.wwan_port_type == WWAN_PORT_MBIM) {
- ctrl_chl_idx++;
- continue;
- }
if (chnl_cfg_port.wwan_port_type != WWAN_PORT_UNKNOWN) {
ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL,
chnl_cfg_port,
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
index 57304a5adf68..6fd7d4a3c9f7 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
@@ -90,7 +90,8 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
return;
}

- ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->nr_of_channels);
+ ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->pcie->pci,
+ ipc_imem->nr_of_channels);
ipc_imem_channel_init(ipc_imem, IPC_CTYPE_WWAN, chnl_cfg,
IRQ_MOD_OFF);

diff --git a/drivers/net/wwan/iosm/iosm_ipc_trace.c b/drivers/net/wwan/iosm/iosm_ipc_trace.c
index eeecfa3d10c5..6012f9099d6a 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_trace.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_trace.c
@@ -137,7 +137,8 @@ struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
struct ipc_chnl_cfg chnl_cfg = { 0 };
struct iosm_trace *ipc_trace;

- ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
+ ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->pcie->pci,
+ IPC_MEM_CTRL_CHL_ID_3);
ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
IRQ_MOD_OFF);

--
2.37.3