Commit 13bb8429 authored by M Chetan Kumar's avatar M Chetan Kumar Committed by David S. Miller

net: wwan: iosm: firmware flashing and coredump collection

This patch brings-in support for M.2 7560 Device firmware flashing &
coredump collection using devlink.
- Driver Registers with Devlink framework.
- Register devlink params callback for configuring device params
  required in flashing or coredump flow.
- Implements devlink ops flash_update callback that programs modem
  firmware.
- Creates region & snapshot required for device coredump log collection.

On early detection of device in boot rom stage. Driver registers with
Devlink framework and establish transport channel for PSI (Primary Signed
Image) injection. Once PSI is injected to device, the device execution
stage details are read to determine whether device is in flash or
exception mode. The collected information is reported to devlink user
space application & based on this informationi, application proceeds with
either modem firmware flashing or coredump collection.
Signed-off-by: default avatarM Chetan Kumar <m.chetan.kumar@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 08c53aee
...@@ -71,6 +71,7 @@ config RPMSG_WWAN_CTRL ...@@ -71,6 +71,7 @@ config RPMSG_WWAN_CTRL
config IOSM config IOSM
tristate "IOSM Driver for Intel M.2 WWAN Device" tristate "IOSM Driver for Intel M.2 WWAN Device"
depends on INTEL_IOMMU depends on INTEL_IOMMU
select NET_DEVLINK
help help
This driver enables Intel M.2 WWAN Device communication. This driver enables Intel M.2 WWAN Device communication.
......
...@@ -18,6 +18,9 @@ iosm-y = \ ...@@ -18,6 +18,9 @@ iosm-y = \
iosm_ipc_protocol.o \ iosm_ipc_protocol.o \
iosm_ipc_protocol_ops.o \ iosm_ipc_protocol_ops.o \
iosm_ipc_mux.o \ iosm_ipc_mux.o \
iosm_ipc_mux_codec.o iosm_ipc_mux_codec.o \
iosm_ipc_devlink.o \
iosm_ipc_flash.o \
iosm_ipc_coredump.o
obj-$(CONFIG_IOSM) := iosm.o obj-$(CONFIG_IOSM) := iosm.o
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include "iosm_ipc_chnl_cfg.h" #include "iosm_ipc_chnl_cfg.h"
/* Max. sizes of a downlink buffers */ /* Max. sizes of a downlink buffers */
#define IPC_MEM_MAX_DL_FLASH_BUF_SIZE (16 * 1024) #define IPC_MEM_MAX_DL_FLASH_BUF_SIZE (64 * 1024)
#define IPC_MEM_MAX_DL_LOOPBACK_SIZE (1 * 1024 * 1024) #define IPC_MEM_MAX_DL_LOOPBACK_SIZE (1 * 1024 * 1024)
#define IPC_MEM_MAX_DL_AT_BUF_SIZE 2048 #define IPC_MEM_MAX_DL_AT_BUF_SIZE 2048
#define IPC_MEM_MAX_DL_RPC_BUF_SIZE (32 * 1024) #define IPC_MEM_MAX_DL_RPC_BUF_SIZE (32 * 1024)
...@@ -60,6 +60,10 @@ static struct ipc_chnl_cfg modem_cfg[] = { ...@@ -60,6 +60,10 @@ static struct ipc_chnl_cfg modem_cfg[] = {
{ IPC_MEM_CTRL_CHL_ID_6, IPC_MEM_PIPE_12, IPC_MEM_PIPE_13, { 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_TDS_MBIM, IPC_MEM_MAX_TDS_MBIM,
IPC_MEM_MAX_DL_MBIM_BUF_SIZE, WWAN_PORT_MBIM }, IPC_MEM_MAX_DL_MBIM_BUF_SIZE, WWAN_PORT_MBIM },
/* Flash Channel/Coredump Channel */
{ IPC_MEM_CTRL_CHL_ID_7, IPC_MEM_PIPE_0, IPC_MEM_PIPE_1,
IPC_MEM_MAX_TDS_FLASH_UL, IPC_MEM_MAX_TDS_FLASH_DL,
IPC_MEM_MAX_DL_FLASH_BUF_SIZE, WWAN_PORT_UNKNOWN },
}; };
int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg, int index) int ipc_chnl_cfg_get(struct ipc_chnl_cfg *chnl_cfg, int index)
......
...@@ -23,6 +23,7 @@ enum ipc_channel_id { ...@@ -23,6 +23,7 @@ enum ipc_channel_id {
IPC_MEM_CTRL_CHL_ID_4, IPC_MEM_CTRL_CHL_ID_4,
IPC_MEM_CTRL_CHL_ID_5, IPC_MEM_CTRL_CHL_ID_5,
IPC_MEM_CTRL_CHL_ID_6, IPC_MEM_CTRL_CHL_ID_6,
IPC_MEM_CTRL_CHL_ID_7,
}; };
/** /**
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2021 Intel Corporation.
*/
#include "iosm_ipc_coredump.h"
/* Collect coredump data from modem */
int ipc_coredump_collect(struct iosm_devlink *devlink, u8 **data, int entry,
u32 region_size)
{
int ret, bytes_to_read, bytes_read = 0, i = 0;
s32 remaining;
u8 *data_ptr;
data_ptr = vmalloc(region_size);
if (!data_ptr)
return -ENOMEM;
remaining = devlink->cd_file_info[entry].actual_size;
ret = ipc_devlink_send_cmd(devlink, rpsi_cmd_coredump_get, entry);
if (ret) {
dev_err(devlink->dev, "Send coredump_get cmd failed");
goto get_cd_fail;
}
while (remaining > 0) {
bytes_to_read = min(remaining, MAX_DATA_SIZE);
bytes_read = 0;
ret = ipc_imem_sys_devlink_read(devlink, data_ptr + i,
bytes_to_read, &bytes_read);
if (ret) {
dev_err(devlink->dev, "CD data read failed");
goto get_cd_fail;
}
remaining -= bytes_read;
i += bytes_read;
}
*data = data_ptr;
return ret;
get_cd_fail:
vfree(data_ptr);
return ret;
}
/* Get coredump list to be collected from modem */
int ipc_coredump_get_list(struct iosm_devlink *devlink, u16 cmd)
{
u32 byte_read, num_entries, file_size;
struct iosm_cd_table *cd_table;
u8 size[MAX_SIZE_LEN], i;
char *filename;
int ret = 0;
cd_table = kzalloc(MAX_CD_LIST_SIZE, GFP_KERNEL);
if (!cd_table) {
ret = -ENOMEM;
goto cd_init_fail;
}
ret = ipc_devlink_send_cmd(devlink, cmd, MAX_CD_LIST_SIZE);
if (ret) {
dev_err(devlink->dev, "rpsi_cmd_coredump_start failed");
goto cd_init_fail;
}
ret = ipc_imem_sys_devlink_read(devlink, (u8 *)cd_table,
MAX_CD_LIST_SIZE, &byte_read);
if (ret) {
dev_err(devlink->dev, "Coredump data is invalid");
goto cd_init_fail;
}
if (byte_read != MAX_CD_LIST_SIZE)
goto cd_init_fail;
if (cmd == rpsi_cmd_coredump_start) {
num_entries = le32_to_cpu(cd_table->list.num_entries);
if (num_entries == 0 || num_entries > IOSM_NOF_CD_REGION) {
ret = -EINVAL;
goto cd_init_fail;
}
for (i = 0; i < num_entries; i++) {
file_size = le32_to_cpu(cd_table->list.entry[i].size);
filename = cd_table->list.entry[i].filename;
if (file_size > devlink->cd_file_info[i].default_size) {
ret = -EINVAL;
goto cd_init_fail;
}
devlink->cd_file_info[i].actual_size = file_size;
dev_dbg(devlink->dev, "file: %s actual size %d",
filename, file_size);
devlink_flash_update_status_notify(devlink->devlink_ctx,
filename,
"FILENAME", 0, 0);
snprintf(size, sizeof(size), "%d", file_size);
devlink_flash_update_status_notify(devlink->devlink_ctx,
size, "FILE SIZE",
0, 0);
}
}
cd_init_fail:
kfree(cd_table);
return ret;
}
/* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2020-2021 Intel Corporation.
*/
#ifndef _IOSM_IPC_COREDUMP_H_
#define _IOSM_IPC_COREDUMP_H_
#include "iosm_ipc_devlink.h"
/* Max number of bytes to receive for Coredump list structure */
#define MAX_CD_LIST_SIZE 0x1000
/* Max buffer allocated to receive coredump data */
#define MAX_DATA_SIZE 0x00010000
/* Max number of file entries */
#define MAX_NOF_ENTRY 256
/* Max length */
#define MAX_SIZE_LEN 32
/**
* struct iosm_cd_list_entry - Structure to hold coredump file info.
* @size: Number of bytes for the entry
* @filename: Coredump filename to be generated on host
*/
struct iosm_cd_list_entry {
__le32 size;
char filename[IOSM_MAX_FILENAME_LEN];
} __packed;
/**
* struct iosm_cd_list - Structure to hold list of coredump files
* to be collected.
* @num_entries: Number of entries to be received
* @entry: Contains File info
*/
struct iosm_cd_list {
__le32 num_entries;
struct iosm_cd_list_entry entry[MAX_NOF_ENTRY];
} __packed;
/**
* struct iosm_cd_table - Common Coredump table
* @version: Version of coredump structure
* @list: Coredump list structure
*/
struct iosm_cd_table {
__le32 version;
struct iosm_cd_list list;
} __packed;
/**
* ipc_coredump_collect - To collect coredump
* @devlink: Pointer to devlink instance.
* @data: Pointer to snapshot
* @entry: ID of requested snapshot
* @region_size: Region size
*
* Returns: 0 on success, error on failure
*/
int ipc_coredump_collect(struct iosm_devlink *devlink, u8 **data, int entry,
u32 region_size);
/**
* ipc_coredump_get_list - Get coredump list
* @devlink: Pointer to devlink instance.
* @cmd: RPSI command to be sent
*
* Returns: 0 on success, error on failure
*/
int ipc_coredump_get_list(struct iosm_devlink *devlink, u16 cmd);
#endif /* _IOSM_IPC_COREDUMP_H_ */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2020-2021 Intel Corporation.
*/
#ifndef _IOSM_IPC_DEVLINK_H_
#define _IOSM_IPC_DEVLINK_H_
#include <net/devlink.h>
#include "iosm_ipc_imem.h"
#include "iosm_ipc_imem_ops.h"
#include "iosm_ipc_pcie.h"
/* MAX file name length */
#define IOSM_MAX_FILENAME_LEN 32
/* EBL response size */
#define IOSM_EBL_RSP_SIZE 76
/* MAX number of regions supported */
#define IOSM_NOF_CD_REGION 6
/* MAX number of SNAPSHOTS supported */
#define MAX_SNAPSHOTS 1
/* Default Coredump file size */
#define REPORT_JSON_SIZE 0x800
#define COREDUMP_FCD_SIZE 0x10E00000
#define CDD_LOG_SIZE 0x30000
#define EEPROM_BIN_SIZE 0x10000
#define BOOTCORE_TRC_BIN_SIZE 0x8000
#define BOOTCORE_PREV_TRC_BIN_SIZE 0x20000
/**
* enum iosm_devlink_param_id - Enum type to different devlink params
* @IOSM_DEVLINK_PARAM_ID_BASE: Devlink param base ID
* @IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH: Set if full erase required
* @IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION: Set if fls file to be
* flashed is Loadmap/region file
* @IOSM_DEVLINK_PARAM_ID_ADDRESS: Address of the region to be
* flashed
* @IOSM_DEVLINK_PARAM_ID_REGION_COUNT: Max region count
*/
enum iosm_devlink_param_id {
IOSM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH,
IOSM_DEVLINK_PARAM_ID_DOWNLOAD_REGION,
IOSM_DEVLINK_PARAM_ID_ADDRESS,
IOSM_DEVLINK_PARAM_ID_REGION_COUNT,
};
/**
* enum iosm_rpsi_cmd_code - Enum type for RPSI command list
* @rpsi_cmd_code_ebl: Command to load ebl
* @rpsi_cmd_coredump_start: Command to get list of files and
* file size info from PSI
* @rpsi_cmd_coredump_get: Command to get the coredump data
* @rpsi_cmd_coredump_end: Command to stop receiving the coredump
*/
enum iosm_rpsi_cmd_code {
rpsi_cmd_code_ebl = 0x02,
rpsi_cmd_coredump_start = 0x10,
rpsi_cmd_coredump_get = 0x11,
rpsi_cmd_coredump_end = 0x12,
};
/**
* enum iosm_flash_comp_type - Enum for different flash component types
* @FLASH_COMP_TYPE_PSI: PSI flash comp type
* @FLASH_COMP_TYPE_EBL: EBL flash comp type
* @FLASH_COMP_TYPE_FLS: FLS flash comp type
* @FLASH_COMP_TYPE_INVAL: Invalid flash comp type
*/
enum iosm_flash_comp_type {
FLASH_COMP_TYPE_PSI,
FLASH_COMP_TYPE_EBL,
FLASH_COMP_TYPE_FLS,
FLASH_COMP_TYPE_INVAL,
};
/**
* struct iosm_devlink_sio - SIO instance
* @rx_list: Downlink skbuf list received from CP
* @read_sem: Needed for the blocking read or downlink transfer
* @channel_id: Reserved channel id for flashing/CD collection to RAM
* @channel: Channel instance for flashing and coredump
* @devlink_read_pend: Check if read is pending
*/
struct iosm_devlink_sio {
struct sk_buff_head rx_list;
struct completion read_sem;
int channel_id;
struct ipc_mem_channel *channel;
u32 devlink_read_pend;
};
/**
* struct iosm_flash_params - List of flash params required for flashing
* @address: Address of the region file to be flashed
* @region_count: Maximum no of regions for each fls file
* @download_region: To be set if region is being flashed
* @erase_full_flash: To set the flashing mode
* erase_full_flash = 1; full erase
* erase_full_flash = 0; no erase
* @erase_full_flash_done: Flag to check if it is a full erase
*/
struct iosm_flash_params {
u32 address;
u8 region_count;
u8 download_region;
u8 erase_full_flash;
u8 erase_full_flash_done;
};
/**
* struct iosm_ebl_ctx_data - EBL ctx data used during flashing
* @ebl_sw_info_version: SWID version info obtained from EBL
* @m_ebl_resp: Buffer used to read and write the ebl data
*/
struct iosm_ebl_ctx_data {
u8 ebl_sw_info_version;
u8 m_ebl_resp[IOSM_EBL_RSP_SIZE];
};
/**
* struct iosm_coredump_file_info - Coredump file info
* @filename: Name of coredump file
* @default_size: Default size of coredump file
* @actual_size: Actual size of coredump file
* @entry: Index of the coredump file
*/
struct iosm_coredump_file_info {
char filename[IOSM_MAX_FILENAME_LEN];
u32 default_size;
u32 actual_size;
u32 entry;
};
/**
* struct iosm_devlink - IOSM Devlink structure
* @devlink_sio: SIO instance for read/write functionality
* @pcie: Pointer to PCIe component
* @dev: Pointer to device struct
* @devlink_ctx: Pointer to devlink context
* @param: Params required for flashing
* @ebl_ctx: Data to be read and written to Modem
* @cd_file_info: coredump file info
* @iosm_devlink_mdm_coredump: region ops for coredump collection
* @cd_regions: coredump regions
*/
struct iosm_devlink {
struct iosm_devlink_sio devlink_sio;
struct iosm_pcie *pcie;
struct device *dev;
struct devlink *devlink_ctx;
struct iosm_flash_params param;
struct iosm_ebl_ctx_data ebl_ctx;
struct iosm_coredump_file_info *cd_file_info;
struct devlink_region_ops iosm_devlink_mdm_coredump[IOSM_NOF_CD_REGION];
struct devlink_region *cd_regions[IOSM_NOF_CD_REGION];
};
/**
* union iosm_rpsi_param_u - RPSI cmd param for CRC calculation
* @word: Words member used in CRC calculation
* @dword: Actual data
*/
union iosm_rpsi_param_u {
__le16 word[2];
__le32 dword;
};
/**
* struct iosm_rpsi_cmd - Structure for RPSI Command
* @param: Used to calculate CRC
* @cmd: Stores the RPSI command
* @crc: Stores the CRC value
*/
struct iosm_rpsi_cmd {
union iosm_rpsi_param_u param;
__le16 cmd;
__le16 crc;
};
/**
* ipc_devlink_init - To initialize the devlink to IOSM driver
* @ipc_imem: Pointer to struct iosm_imem
*
* Returns: Pointer to iosm_devlink on success and NULL on failure
*/
struct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem);
/**
* ipc_devlink_deinit - To unintialize the devlink from IOSM driver.
* @ipc_devlink: Devlink instance
*/
void ipc_devlink_deinit(struct iosm_devlink *ipc_devlink);
/**
* ipc_devlink_send_cmd - Send command to Modem
* @ipc_devlink: Pointer to struct iosm_devlink
* @cmd: Command to be sent to modem
* @entry: Command entry number
*
* Returns: 0 on success and failure value on error
*/
int ipc_devlink_send_cmd(struct iosm_devlink *ipc_devlink, u16 cmd, u32 entry);
#endif /* _IOSM_IPC_DEVLINK_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2020-2021 Intel Corporation.
*/
#ifndef _IOSM_IPC_FLASH_H
#define _IOSM_IPC_FLASH_H
/* Buffer size used to read the fls image */
#define IOSM_FLS_BUF_SIZE 0x00100000
/* Full erase start address */
#define IOSM_ERASE_START_ADDR 0x00000000
/* Erase length for NAND flash */
#define IOSM_ERASE_LEN 0xFFFFFFFF
/* EBL response Header size */
#define IOSM_EBL_HEAD_SIZE 8
/* EBL payload size */
#define IOSM_EBL_W_PAYL_SIZE 2048
/* Total EBL pack size */
#define IOSM_EBL_W_PACK_SIZE (IOSM_EBL_HEAD_SIZE + IOSM_EBL_W_PAYL_SIZE)
/* EBL payload size */
#define IOSM_EBL_DW_PAYL_SIZE 16384
/* Total EBL pack size */
#define IOSM_EBL_DW_PACK_SIZE (IOSM_EBL_HEAD_SIZE + IOSM_EBL_DW_PAYL_SIZE)
/* EBL name size */
#define IOSM_EBL_NAME 32
/* Maximum supported error types */
#define IOSM_MAX_ERRORS 8
/* Read size for RPSI/EBL response */
#define IOSM_READ_SIZE 2
/* Link establishment response ack size */
#define IOSM_LER_ACK_SIZE 2
/* PSI ACK len */
#define IOSM_PSI_ACK 8
/* SWID capability for packed swid type */
#define IOSM_EXT_CAP_SWID_OOS_PACK 0x02
/* EBL error response buffer */
#define IOSM_EBL_RSP_BUFF 0x0041
/* SWID string length */
#define IOSM_SWID_STR 64
/* Load EBL command size */
#define IOSM_RPSI_LOAD_SIZE 0
/* EBL payload checksum */
#define IOSM_EBL_CKSM 0x0000FFFF
/* SWID msg len and argument */
#define IOSM_MSG_LEN_ARG 0
/* Data to be sent to modem */
#define IOSM_MDM_SEND_DATA 0x0000
/* Data received from modem as part of erase check */
#define IOSM_MDM_ERASE_RSP 0x0001
/* Bit shift to calculate Checksum */
#define IOSM_EBL_PAYL_SHIFT 16
/* Flag To be set */
#define IOSM_SET_FLAG 1
/* Set flash erase check timeout to 100 msec */
#define IOSM_FLASH_ERASE_CHECK_TIMEOUT 100
/* Set flash erase check interval to 20 msec */
#define IOSM_FLASH_ERASE_CHECK_INTERVAL 20
/* Link establishment response ack size */
#define IOSM_LER_RSP_SIZE 60
/**
* enum iosm_flash_package_type - Enum for the flashing operations
* @FLASH_SET_PROT_CONF: Write EBL capabilities
* @FLASH_SEC_START: Start writing the secpack
* @FLASH_SEC_END: Validate secpack end
* @FLASH_SET_ADDRESS: Set the address for flashing
* @FLASH_ERASE_START: Start erase before flashing
* @FLASH_ERASE_CHECK: Validate the erase functionality
* @FLASH_OOS_CONTROL: Retrieve data based on oos actions
* @FLASH_OOS_DATA_READ: Read data from EBL
* @FLASH_WRITE_IMAGE_RAW: Write the raw image to flash
*/
enum iosm_flash_package_type {
FLASH_SET_PROT_CONF = 0x0086,
FLASH_SEC_START = 0x0204,
FLASH_SEC_END,
FLASH_SET_ADDRESS = 0x0802,
FLASH_ERASE_START = 0x0805,
FLASH_ERASE_CHECK,
FLASH_OOS_CONTROL = 0x080C,
FLASH_OOS_DATA_READ = 0x080E,
FLASH_WRITE_IMAGE_RAW,
};
/**
* enum iosm_out_of_session_action - Actions possible over the
* OutOfSession command interface
* @FLASH_OOSC_ACTION_READ: Read data according to its type
* @FLASH_OOSC_ACTION_ERASE: Erase data according to its type
*/
enum iosm_out_of_session_action {
FLASH_OOSC_ACTION_READ = 2,
FLASH_OOSC_ACTION_ERASE = 3,
};
/**
* enum iosm_out_of_session_type - Data types that can be handled over the
* Out Of Session command Interface
* @FLASH_OOSC_TYPE_ALL_FLASH: The whole flash area
* @FLASH_OOSC_TYPE_SWID_TABLE: Read the swid table from the target
*/
enum iosm_out_of_session_type {
FLASH_OOSC_TYPE_ALL_FLASH = 8,
FLASH_OOSC_TYPE_SWID_TABLE = 16,
};
/**
* enum iosm_ebl_caps - EBL capability settings
* @IOSM_CAP_NOT_ENHANCED: If capability not supported
* @IOSM_CAP_USE_EXT_CAP: To be set if extended capability is set
* @IOSM_EXT_CAP_ERASE_ALL: Set Erase all capability
* @IOSM_EXT_CAP_COMMIT_ALL: Set the commit all capability
*/
enum iosm_ebl_caps {
IOSM_CAP_NOT_ENHANCED = 0x00,
IOSM_CAP_USE_EXT_CAP = 0x01,
IOSM_EXT_CAP_ERASE_ALL = 0x08,
IOSM_EXT_CAP_COMMIT_ALL = 0x20,
};
/**
* enum iosm_ebl_rsp - EBL response field
* @EBL_CAPS_FLAG: EBL capability flag
* @EBL_SKIP_ERASE: EBL skip erase flag
* @EBL_SKIP_CRC: EBL skip wr_pack crc
* @EBL_EXT_CAPS_HANDLED: EBL extended capability handled flag
* @EBL_OOS_CONFIG: EBL oos configuration
* @EBL_RSP_SW_INFO_VER: EBL SW info version
*/
enum iosm_ebl_rsp {
EBL_CAPS_FLAG = 50,
EBL_SKIP_ERASE = 54,
EBL_SKIP_CRC = 55,
EBL_EXT_CAPS_HANDLED = 57,
EBL_OOS_CONFIG = 64,
EBL_RSP_SW_INFO_VER = 70,
};
/**
* enum iosm_mdm_send_recv_data - Data to send to modem
* @IOSM_MDM_SEND_2: Send 2 bytes of payload
* @IOSM_MDM_SEND_4: Send 4 bytes of payload
* @IOSM_MDM_SEND_8: Send 8 bytes of payload
* @IOSM_MDM_SEND_16: Send 16 bytes of payload
*/
enum iosm_mdm_send_recv_data {
IOSM_MDM_SEND_2 = 2,
IOSM_MDM_SEND_4 = 4,
IOSM_MDM_SEND_8 = 8,
IOSM_MDM_SEND_16 = 16,
};
/**
* struct iosm_ebl_one_error - Structure containing error details
* @error_class: Error type- standard, security and text error
* @error_code: Specific error from error type
*/
struct iosm_ebl_one_error {
u16 error_class;
u16 error_code;
};
/**
* struct iosm_ebl_error- Structure with max error type supported
* @error: Array of one_error structure with max errors
*/
struct iosm_ebl_error {
struct iosm_ebl_one_error error[IOSM_MAX_ERRORS];
};
/**
* struct iosm_swid_table - SWID table data for modem
* @number_of_data_sets: Number of swid types
* @sw_id_type: SWID type - SWID
* @sw_id_val: SWID value
* @rf_engine_id_type: RF engine ID type - RF_ENGINE_ID
* @rf_engine_id_val: RF engine ID value
*/
struct iosm_swid_table {
u32 number_of_data_sets;
char sw_id_type[IOSM_EBL_NAME];
u32 sw_id_val;
char rf_engine_id_type[IOSM_EBL_NAME];
u32 rf_engine_id_val;
};
/**
* struct iosm_flash_msg_control - Data sent to modem
* @action: Action to be performed
* @type: Type of action
* @length: Length of the action
* @arguments: Argument value sent to modem
*/
struct iosm_flash_msg_control {
__le32 action;
__le32 type;
__le32 length;
__le32 arguments;
};
/**
* struct iosm_flash_data - Header Data to be sent to modem
* @checksum: Checksum value calculated for the payload data
* @pack_id: Flash Action type
* @msg_length: Payload length
*/
struct iosm_flash_data {
__le16 checksum;
__le16 pack_id;
__le32 msg_length;
};
/**
* ipc_flash_boot_psi - Inject PSI image
* @ipc_devlink: Pointer to devlink structure
* @fw: FW image
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_boot_psi(struct iosm_devlink *ipc_devlink,
const struct firmware *fw);
/**
* ipc_flash_boot_ebl - Inject EBL image
* @ipc_devlink: Pointer to devlink structure
* @fw: FW image
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_boot_ebl(struct iosm_devlink *ipc_devlink,
const struct firmware *fw);
/**
* ipc_flash_boot_set_capabilities - Set modem bool capabilities in flash
* @ipc_devlink: Pointer to devlink structure
* @mdm_rsp: Pointer to modem response buffer
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_boot_set_capabilities(struct iosm_devlink *ipc_devlink,
u8 *mdm_rsp);
/**
* ipc_flash_link_establish - Flash link establishment
* @ipc_imem: Pointer to struct iosm_imem
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_link_establish(struct iosm_imem *ipc_imem);
/**
* ipc_flash_read_swid - Get swid during flash phase
* @ipc_devlink: Pointer to devlink structure
* @mdm_rsp: Pointer to modem response buffer
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_read_swid(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp);
/**
* ipc_flash_send_fls - Inject Modem subsystem fls file to device
* @ipc_devlink: Pointer to devlink structure
* @fw: FW image
* @mdm_rsp: Pointer to modem response buffer
*
* Returns: 0 on success and failure value on error
*/
int ipc_flash_send_fls(struct iosm_devlink *ipc_devlink,
const struct firmware *fw, u8 *mdm_rsp);
#endif
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include "iosm_ipc_chnl_cfg.h" #include "iosm_ipc_chnl_cfg.h"
#include "iosm_ipc_devlink.h"
#include "iosm_ipc_flash.h"
#include "iosm_ipc_imem.h" #include "iosm_ipc_imem.h"
#include "iosm_ipc_port.h" #include "iosm_ipc_port.h"
...@@ -263,9 +265,12 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem, ...@@ -263,9 +265,12 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem,
switch (pipe->channel->ctype) { switch (pipe->channel->ctype) {
case IPC_CTYPE_CTRL: case IPC_CTYPE_CTRL:
port_id = pipe->channel->channel_id; port_id = pipe->channel->channel_id;
if (port_id == IPC_MEM_CTRL_CHL_ID_7)
/* Pass the packet to the wwan layer. */ ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink,
wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port, skb); skb);
else
wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port,
skb);
break; break;
case IPC_CTYPE_WWAN: case IPC_CTYPE_WWAN:
...@@ -399,19 +404,8 @@ static void ipc_imem_rom_irq_exec(struct iosm_imem *ipc_imem) ...@@ -399,19 +404,8 @@ static void ipc_imem_rom_irq_exec(struct iosm_imem *ipc_imem)
{ {
struct ipc_mem_channel *channel; struct ipc_mem_channel *channel;
if (ipc_imem->flash_channel_id < 0) { channel = ipc_imem->ipc_devlink->devlink_sio.channel;
ipc_imem->rom_exit_code = IMEM_ROM_EXIT_FAIL;
dev_err(ipc_imem->dev, "Missing flash app:%d",
ipc_imem->flash_channel_id);
return;
}
ipc_imem->rom_exit_code = ipc_mmio_get_rom_exit_code(ipc_imem->mmio); ipc_imem->rom_exit_code = ipc_mmio_get_rom_exit_code(ipc_imem->mmio);
/* Wake up the flash app to continue or to terminate depending
* on the CP ROM exit code.
*/
channel = &ipc_imem->channels[ipc_imem->flash_channel_id];
complete(&channel->ul_sem); complete(&channel->ul_sem);
} }
...@@ -572,7 +566,7 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) ...@@ -572,7 +566,7 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq)
enum ipc_phase old_phase, phase; enum ipc_phase old_phase, phase;
bool retry_allocation = false; bool retry_allocation = false;
bool ul_pending = false; bool ul_pending = false;
int ch_id, i; int i;
if (irq != IMEM_IRQ_DONT_CARE) if (irq != IMEM_IRQ_DONT_CARE)
ipc_imem->ev_irq_pending[irq] = false; ipc_imem->ev_irq_pending[irq] = false;
...@@ -696,11 +690,8 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) ...@@ -696,11 +690,8 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq)
if ((phase == IPC_P_PSI || phase == IPC_P_EBL) && if ((phase == IPC_P_PSI || phase == IPC_P_EBL) &&
ipc_imem->ipc_requested_state == IPC_MEM_DEVICE_IPC_RUNNING && ipc_imem->ipc_requested_state == IPC_MEM_DEVICE_IPC_RUNNING &&
ipc_mmio_get_ipc_state(ipc_imem->mmio) == ipc_mmio_get_ipc_state(ipc_imem->mmio) ==
IPC_MEM_DEVICE_IPC_RUNNING && IPC_MEM_DEVICE_IPC_RUNNING) {
ipc_imem->flash_channel_id >= 0) { complete(&ipc_imem->ipc_devlink->devlink_sio.channel->ul_sem);
/* Wake up the flash app to open the pipes. */
ch_id = ipc_imem->flash_channel_id;
complete(&ipc_imem->channels[ch_id].ul_sem);
} }
/* Reset the expected CP state. */ /* Reset the expected CP state. */
...@@ -1176,6 +1167,9 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem) ...@@ -1176,6 +1167,9 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem)
ipc_port_deinit(ipc_imem->ipc_port); ipc_port_deinit(ipc_imem->ipc_port);
} }
if (ipc_imem->ipc_devlink)
ipc_devlink_deinit(ipc_imem->ipc_devlink);
ipc_imem_device_ipc_uninit(ipc_imem); ipc_imem_device_ipc_uninit(ipc_imem);
ipc_imem_channel_reset(ipc_imem); ipc_imem_channel_reset(ipc_imem);
...@@ -1258,6 +1252,7 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, ...@@ -1258,6 +1252,7 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
void __iomem *mmio, struct device *dev) void __iomem *mmio, struct device *dev)
{ {
struct iosm_imem *ipc_imem = kzalloc(sizeof(*pcie->imem), GFP_KERNEL); struct iosm_imem *ipc_imem = kzalloc(sizeof(*pcie->imem), GFP_KERNEL);
enum ipc_mem_exec_stage stage;
if (!ipc_imem) if (!ipc_imem)
return NULL; return NULL;
...@@ -1272,9 +1267,6 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, ...@@ -1272,9 +1267,6 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
ipc_imem->cp_version = 0; ipc_imem->cp_version = 0;
ipc_imem->device_sleep = IPC_HOST_SLEEP_ENTER_SLEEP; ipc_imem->device_sleep = IPC_HOST_SLEEP_ENTER_SLEEP;
/* Reset the flash channel id. */
ipc_imem->flash_channel_id = -1;
/* Reset the max number of configured channels */ /* Reset the max number of configured channels */
ipc_imem->nr_of_channels = 0; ipc_imem->nr_of_channels = 0;
...@@ -1328,8 +1320,21 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, ...@@ -1328,8 +1320,21 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
goto imem_config_fail; goto imem_config_fail;
} }
return ipc_imem; stage = ipc_mmio_get_exec_stage(ipc_imem->mmio);
if (stage == IPC_MEM_EXEC_STAGE_BOOT) {
/* Alloc and Register devlink */
ipc_imem->ipc_devlink = ipc_devlink_init(ipc_imem);
if (!ipc_imem->ipc_devlink) {
dev_err(ipc_imem->dev, "Devlink register failed");
goto imem_config_fail;
}
if (ipc_flash_link_establish(ipc_imem))
goto devlink_channel_fail;
}
return ipc_imem;
devlink_channel_fail:
ipc_devlink_deinit(ipc_imem->ipc_devlink);
imem_config_fail: imem_config_fail:
hrtimer_cancel(&ipc_imem->td_alloc_timer); hrtimer_cancel(&ipc_imem->td_alloc_timer);
hrtimer_cancel(&ipc_imem->fast_update_timer); hrtimer_cancel(&ipc_imem->fast_update_timer);
...@@ -1361,3 +1366,51 @@ void ipc_imem_td_update_timer_suspend(struct iosm_imem *ipc_imem, bool suspend) ...@@ -1361,3 +1366,51 @@ void ipc_imem_td_update_timer_suspend(struct iosm_imem *ipc_imem, bool suspend)
{ {
ipc_imem->td_update_timer_suspended = suspend; ipc_imem->td_update_timer_suspended = suspend;
} }
/* Verify the CP execution state, copy the chip info,
* change the execution phase to ROM
*/
static int ipc_imem_devlink_trigger_chip_info_cb(struct iosm_imem *ipc_imem,
int arg, void *msg,
size_t msgsize)
{
enum ipc_mem_exec_stage stage;
struct sk_buff *skb;
int rc = -EINVAL;
size_t size;
/* Test the CP execution state. */
stage = ipc_mmio_get_exec_stage(ipc_imem->mmio);
if (stage != IPC_MEM_EXEC_STAGE_BOOT) {
dev_err(ipc_imem->dev,
"Execution_stage: expected BOOT, received = %X", stage);
goto trigger_chip_info_fail;
}
/* Allocate a new sk buf for the chip info. */
size = ipc_imem->mmio->chip_info_size;
if (size > IOSM_CHIP_INFO_SIZE_MAX)
goto trigger_chip_info_fail;
skb = ipc_pcie_alloc_local_skb(ipc_imem->pcie, GFP_ATOMIC, size);
if (!skb) {
dev_err(ipc_imem->dev, "exhausted skbuf kernel DL memory");
rc = -ENOMEM;
goto trigger_chip_info_fail;
}
/* Copy the chip info characters into the ipc_skb. */
ipc_mmio_copy_chip_info(ipc_imem->mmio, skb_put(skb, size), size);
/* First change to the ROM boot phase. */
dev_dbg(ipc_imem->dev, "execution_stage[%X] eq. BOOT", stage);
ipc_imem->phase = ipc_imem_phase_update(ipc_imem);
ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink, skb);
rc = 0;
trigger_chip_info_fail:
return rc;
}
int ipc_imem_devlink_trigger_chip_info(struct iosm_imem *ipc_imem)
{
return ipc_task_queue_send_task(ipc_imem,
ipc_imem_devlink_trigger_chip_info_cb,
0, NULL, 0, true);
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#define IOSM_IPC_IMEM_H #define IOSM_IPC_IMEM_H
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <stdbool.h>
#include "iosm_ipc_mmio.h" #include "iosm_ipc_mmio.h"
#include "iosm_ipc_pcie.h" #include "iosm_ipc_pcie.h"
...@@ -69,7 +70,7 @@ struct ipc_chnl_cfg; ...@@ -69,7 +70,7 @@ struct ipc_chnl_cfg;
#define IMEM_IRQ_DONT_CARE (-1) #define IMEM_IRQ_DONT_CARE (-1)
#define IPC_MEM_MAX_CHANNELS 7 #define IPC_MEM_MAX_CHANNELS 8
#define IPC_MEM_MUX_IP_SESSION_ENTRIES 8 #define IPC_MEM_MUX_IP_SESSION_ENTRIES 8
...@@ -98,6 +99,7 @@ struct ipc_chnl_cfg; ...@@ -98,6 +99,7 @@ struct ipc_chnl_cfg;
#define IPC_MEM_DL_ETH_OFFSET 16 #define IPC_MEM_DL_ETH_OFFSET 16
#define IPC_CB(skb) ((struct ipc_skb_cb *)((skb)->cb)) #define IPC_CB(skb) ((struct ipc_skb_cb *)((skb)->cb))
#define IOSM_CHIP_INFO_SIZE_MAX 100
#define FULLY_FUNCTIONAL 0 #define FULLY_FUNCTIONAL 0
...@@ -304,9 +306,9 @@ enum ipc_phase { ...@@ -304,9 +306,9 @@ enum ipc_phase {
* @ipc_port: IPC PORT data structure pointer * @ipc_port: IPC PORT data structure pointer
* @pcie: IPC PCIe * @pcie: IPC PCIe
* @dev: Pointer to device structure * @dev: Pointer to device structure
* @flash_channel_id: Reserved channel id for flashing to RAM.
* @ipc_requested_state: Expected IPC state on CP. * @ipc_requested_state: Expected IPC state on CP.
* @channels: Channel list with UL/DL pipe pairs. * @channels: Channel list with UL/DL pipe pairs.
* @ipc_devlink: IPC Devlink data structure pointer
* @ipc_status: local ipc_status * @ipc_status: local ipc_status
* @nr_of_channels: number of configured channels * @nr_of_channels: number of configured channels
* @startup_timer: startup timer for NAND support. * @startup_timer: startup timer for NAND support.
...@@ -349,9 +351,9 @@ struct iosm_imem { ...@@ -349,9 +351,9 @@ struct iosm_imem {
struct iosm_cdev *ipc_port[IPC_MEM_MAX_CHANNELS]; struct iosm_cdev *ipc_port[IPC_MEM_MAX_CHANNELS];
struct iosm_pcie *pcie; struct iosm_pcie *pcie;
struct device *dev; struct device *dev;
int flash_channel_id;
enum ipc_mem_device_ipc_state ipc_requested_state; enum ipc_mem_device_ipc_state ipc_requested_state;
struct ipc_mem_channel channels[IPC_MEM_MAX_CHANNELS]; struct ipc_mem_channel channels[IPC_MEM_MAX_CHANNELS];
struct iosm_devlink *ipc_devlink;
u32 ipc_status; u32 ipc_status;
u32 nr_of_channels; u32 nr_of_channels;
struct hrtimer startup_timer; struct hrtimer startup_timer;
...@@ -575,4 +577,15 @@ void ipc_imem_ipc_init_check(struct iosm_imem *ipc_imem); ...@@ -575,4 +577,15 @@ void ipc_imem_ipc_init_check(struct iosm_imem *ipc_imem);
*/ */
void ipc_imem_channel_init(struct iosm_imem *ipc_imem, enum ipc_ctype ctype, void ipc_imem_channel_init(struct iosm_imem *ipc_imem, enum ipc_ctype ctype,
struct ipc_chnl_cfg chnl_cfg, u32 irq_moderation); struct ipc_chnl_cfg chnl_cfg, u32 irq_moderation);
/**
* ipc_imem_devlink_trigger_chip_info - Inform devlink that the chip
* information are available if the
* flashing to RAM interworking shall be
* executed.
* @ipc_imem: Pointer to imem structure
*
* Returns: 0 on success, -1 on failure
*/
int ipc_imem_devlink_trigger_chip_info(struct iosm_imem *ipc_imem);
#endif #endif
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include "iosm_ipc_chnl_cfg.h" #include "iosm_ipc_chnl_cfg.h"
#include "iosm_ipc_devlink.h"
#include "iosm_ipc_imem.h" #include "iosm_ipc_imem.h"
#include "iosm_ipc_imem_ops.h" #include "iosm_ipc_imem_ops.h"
#include "iosm_ipc_port.h" #include "iosm_ipc_port.h"
...@@ -331,3 +332,319 @@ int ipc_imem_sys_cdev_write(struct iosm_cdev *ipc_cdev, struct sk_buff *skb) ...@@ -331,3 +332,319 @@ int ipc_imem_sys_cdev_write(struct iosm_cdev *ipc_cdev, struct sk_buff *skb)
out: out:
return ret; return ret;
} }
/* Open a SIO link to CP and return the channel instance */
struct ipc_mem_channel *ipc_imem_sys_devlink_open(struct iosm_imem *ipc_imem)
{
struct ipc_mem_channel *channel;
enum ipc_phase phase;
int channel_id;
phase = ipc_imem_phase_update(ipc_imem);
switch (phase) {
case IPC_P_OFF:
case IPC_P_ROM:
/* Get a channel id as flash id and reserve it. */
channel_id = ipc_imem_channel_alloc(ipc_imem,
IPC_MEM_CTRL_CHL_ID_7,
IPC_CTYPE_CTRL);
if (channel_id < 0) {
dev_err(ipc_imem->dev,
"reservation of a flash channel id failed");
goto error;
}
ipc_imem->ipc_devlink->devlink_sio.channel_id = channel_id;
channel = &ipc_imem->channels[channel_id];
/* Enqueue chip info data to be read */
if (ipc_imem_devlink_trigger_chip_info(ipc_imem)) {
dev_err(ipc_imem->dev, "Enqueue of chip info failed");
channel->state = IMEM_CHANNEL_FREE;
goto error;
}
return channel;
case IPC_P_PSI:
case IPC_P_EBL:
ipc_imem->cp_version = ipc_mmio_get_cp_version(ipc_imem->mmio);
if (ipc_imem->cp_version == -1) {
dev_err(ipc_imem->dev, "invalid CP version");
goto error;
}
channel_id = ipc_imem->ipc_devlink->devlink_sio.channel_id;
return ipc_imem_channel_open(ipc_imem, channel_id,
IPC_HP_CDEV_OPEN);
default:
/* CP is in the wrong state (e.g. CRASH or CD_READY) */
dev_err(ipc_imem->dev, "SIO open refused, phase %d", phase);
}
error:
return NULL;
}
/* Release a SIO channel link to CP. */
void ipc_imem_sys_devlink_close(struct iosm_devlink *ipc_devlink)
{
struct iosm_imem *ipc_imem = ipc_devlink->pcie->imem;
int boot_check_timeout = BOOT_CHECK_DEFAULT_TIMEOUT;
enum ipc_mem_exec_stage exec_stage;
struct ipc_mem_channel *channel;
enum ipc_phase curr_phase;
int status = 0;
u32 tail = 0;
channel = ipc_imem->ipc_devlink->devlink_sio.channel;
curr_phase = ipc_imem->phase;
/* Increase the total wait time to boot_check_timeout */
do {
exec_stage = ipc_mmio_get_exec_stage(ipc_imem->mmio);
if (exec_stage == IPC_MEM_EXEC_STAGE_RUN ||
exec_stage == IPC_MEM_EXEC_STAGE_PSI)
break;
msleep(20);
boot_check_timeout -= 20;
} while (boot_check_timeout > 0);
/* If there are any pending TDs then wait for Timeout/Completion before
* closing pipe.
*/
if (channel->ul_pipe.old_tail != channel->ul_pipe.old_head) {
status = wait_for_completion_interruptible_timeout
(&ipc_imem->ul_pend_sem,
msecs_to_jiffies(IPC_PEND_DATA_TIMEOUT));
if (status == 0) {
dev_dbg(ipc_imem->dev,
"Data Timeout on UL-Pipe:%d Head:%d Tail:%d",
channel->ul_pipe.pipe_nr,
channel->ul_pipe.old_head,
channel->ul_pipe.old_tail);
}
}
ipc_protocol_get_head_tail_index(ipc_imem->ipc_protocol,
&channel->dl_pipe, NULL, &tail);
if (tail != channel->dl_pipe.old_tail) {
status = wait_for_completion_interruptible_timeout
(&ipc_imem->dl_pend_sem,
msecs_to_jiffies(IPC_PEND_DATA_TIMEOUT));
if (status == 0) {
dev_dbg(ipc_imem->dev,
"Data Timeout on DL-Pipe:%d Head:%d Tail:%d",
channel->dl_pipe.pipe_nr,
channel->dl_pipe.old_head,
channel->dl_pipe.old_tail);
}
}
/* Due to wait for completion in messages, there is a small window
* between closing the pipe and updating the channel is closed. In this
* small window there could be HP update from Host Driver. Hence update
* the channel state as CLOSING to aviod unnecessary interrupt
* towards CP.
*/
channel->state = IMEM_CHANNEL_CLOSING;
/* Release the pipe resources */
ipc_imem_pipe_cleanup(ipc_imem, &channel->ul_pipe);
ipc_imem_pipe_cleanup(ipc_imem, &channel->dl_pipe);
}
void ipc_imem_sys_devlink_notify_rx(struct iosm_devlink *ipc_devlink,
struct sk_buff *skb)
{
skb_queue_tail(&ipc_devlink->devlink_sio.rx_list, skb);
complete(&ipc_devlink->devlink_sio.read_sem);
}
/* PSI transfer */
static int ipc_imem_sys_psi_transfer(struct iosm_imem *ipc_imem,
struct ipc_mem_channel *channel,
unsigned char *buf, int count)
{
int psi_start_timeout = PSI_START_DEFAULT_TIMEOUT;
enum ipc_mem_exec_stage exec_stage;
dma_addr_t mapping = 0;
int ret;
ret = ipc_pcie_addr_map(ipc_imem->pcie, buf, count, &mapping,
DMA_TO_DEVICE);
if (ret)
goto pcie_addr_map_fail;
/* Save the PSI information for the CP ROM driver on the doorbell
* scratchpad.
*/
ipc_mmio_set_psi_addr_and_size(ipc_imem->mmio, mapping, count);
ipc_doorbell_fire(ipc_imem->pcie, 0, IPC_MEM_EXEC_STAGE_BOOT);
ret = wait_for_completion_interruptible_timeout
(&channel->ul_sem,
msecs_to_jiffies(IPC_PSI_TRANSFER_TIMEOUT));
if (ret <= 0) {
dev_err(ipc_imem->dev, "Failed PSI transfer to CP, Error-%d",
ret);
goto psi_transfer_fail;
}
/* If the PSI download fails, return the CP boot ROM exit code */
if (ipc_imem->rom_exit_code != IMEM_ROM_EXIT_OPEN_EXT &&
ipc_imem->rom_exit_code != IMEM_ROM_EXIT_CERT_EXT) {
ret = (-1) * ((int)ipc_imem->rom_exit_code);
goto psi_transfer_fail;
}
dev_dbg(ipc_imem->dev, "PSI image successfully downloaded");
/* Wait psi_start_timeout milliseconds until the CP PSI image is
* running and updates the execution_stage field with
* IPC_MEM_EXEC_STAGE_PSI. Verify the execution stage.
*/
do {
exec_stage = ipc_mmio_get_exec_stage(ipc_imem->mmio);
if (exec_stage == IPC_MEM_EXEC_STAGE_PSI)
break;
msleep(20);
psi_start_timeout -= 20;
} while (psi_start_timeout > 0);
if (exec_stage != IPC_MEM_EXEC_STAGE_PSI)
goto psi_transfer_fail; /* Unknown status of CP PSI process. */
ipc_imem->phase = IPC_P_PSI;
/* Enter the PSI phase. */
dev_dbg(ipc_imem->dev, "execution_stage[%X] eq. PSI", exec_stage);
/* Request the RUNNING state from CP and wait until it was reached
* or timeout.
*/
ipc_imem_ipc_init_check(ipc_imem);
ret = wait_for_completion_interruptible_timeout
(&channel->ul_sem, msecs_to_jiffies(IPC_PSI_TRANSFER_TIMEOUT));
if (ret <= 0) {
dev_err(ipc_imem->dev,
"Failed PSI RUNNING state on CP, Error-%d", ret);
goto psi_transfer_fail;
}
if (ipc_mmio_get_ipc_state(ipc_imem->mmio) !=
IPC_MEM_DEVICE_IPC_RUNNING) {
dev_err(ipc_imem->dev,
"ch[%d] %s: unexpected CP IPC state %d, not RUNNING",
channel->channel_id,
ipc_imem_phase_get_string(ipc_imem->phase),
ipc_mmio_get_ipc_state(ipc_imem->mmio));
goto psi_transfer_fail;
}
/* Create the flash channel for the transfer of the images. */
if (!ipc_imem_sys_devlink_open(ipc_imem)) {
dev_err(ipc_imem->dev, "can't open flash_channel");
goto psi_transfer_fail;
}
ret = 0;
psi_transfer_fail:
ipc_pcie_addr_unmap(ipc_imem->pcie, count, mapping, DMA_TO_DEVICE);
pcie_addr_map_fail:
return ret;
}
int ipc_imem_sys_devlink_write(struct iosm_devlink *ipc_devlink,
unsigned char *buf, int count)
{
struct iosm_imem *ipc_imem = ipc_devlink->pcie->imem;
struct ipc_mem_channel *channel;
struct sk_buff *skb;
dma_addr_t mapping;
int ret;
channel = ipc_imem->ipc_devlink->devlink_sio.channel;
/* In the ROM phase the PSI image is passed to CP about a specific
* shared memory area and doorbell scratchpad directly.
*/
if (ipc_imem->phase == IPC_P_ROM) {
ret = ipc_imem_sys_psi_transfer(ipc_imem, channel, buf, count);
/* If the PSI transfer fails then send crash
* Signature.
*/
if (ret > 0)
ipc_imem_msg_send_feature_set(ipc_imem,
IPC_MEM_INBAND_CRASH_SIG,
false);
goto out;
}
/* Allocate skb memory for the uplink buffer. */
skb = ipc_pcie_alloc_skb(ipc_devlink->pcie, count, GFP_KERNEL, &mapping,
DMA_TO_DEVICE, 0);
if (!skb) {
ret = -ENOMEM;
goto out;
}
memcpy(skb_put(skb, count), buf, count);
IPC_CB(skb)->op_type = UL_USR_OP_BLOCKED;
/* Add skb to the uplink skbuf accumulator. */
skb_queue_tail(&channel->ul_list, skb);
/* Inform the IPC tasklet to pass uplink IP packets to CP. */
if (!ipc_imem_call_cdev_write(ipc_imem)) {
ret = wait_for_completion_interruptible(&channel->ul_sem);
if (ret < 0) {
dev_err(ipc_imem->dev,
"ch[%d] no CP confirmation, status = %d",
channel->channel_id, ret);
ipc_pcie_kfree_skb(ipc_devlink->pcie, skb);
goto out;
}
}
ret = 0;
out:
return ret;
}
int ipc_imem_sys_devlink_read(struct iosm_devlink *devlink, u8 *data,
u32 bytes_to_read, u32 *bytes_read)
{
struct sk_buff *skb = NULL;
int rc = 0;
/* check skb is available in rx_list or wait for skb */
devlink->devlink_sio.devlink_read_pend = 1;
while (!skb && !(skb = skb_dequeue(&devlink->devlink_sio.rx_list))) {
if (!wait_for_completion_interruptible_timeout
(&devlink->devlink_sio.read_sem,
msecs_to_jiffies(IPC_READ_TIMEOUT))) {
dev_err(devlink->dev, "Read timedout");
rc = -ETIMEDOUT;
goto devlink_read_fail;
}
}
devlink->devlink_sio.devlink_read_pend = 0;
if (bytes_to_read < skb->len) {
dev_err(devlink->dev, "Invalid size,expected len %d", skb->len);
rc = -EINVAL;
goto devlink_read_fail;
}
*bytes_read = skb->len;
memcpy(data, skb->data, skb->len);
devlink_read_fail:
ipc_pcie_kfree_skb(devlink->pcie, skb);
return rc;
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include "iosm_ipc_mux_codec.h" #include "iosm_ipc_mux_codec.h"
/* Maximum wait time for blocking read */ /* Maximum wait time for blocking read */
#define IPC_READ_TIMEOUT 500 #define IPC_READ_TIMEOUT 3000
/* The delay in ms for defering the unregister */ /* The delay in ms for defering the unregister */
#define SIO_UNREGISTER_DEFER_DELAY_MS 1 #define SIO_UNREGISTER_DEFER_DELAY_MS 1
...@@ -98,4 +98,51 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id, ...@@ -98,4 +98,51 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id,
*/ */
void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
enum ipc_mux_protocol mux_type); enum ipc_mux_protocol mux_type);
/**
* ipc_imem_sys_devlink_open - Open a Flash/CD Channel link to CP
* @ipc_imem: iosm_imem instance
*
* Return: channel instance on success, NULL for failure
*/
struct ipc_mem_channel *ipc_imem_sys_devlink_open(struct iosm_imem *ipc_imem);
/**
* ipc_imem_sys_devlink_close - Release a Flash/CD channel link to CP
* @ipc_devlink: Pointer to ipc_devlink data-struct
*
*/
void ipc_imem_sys_devlink_close(struct iosm_devlink *ipc_devlink);
/**
* ipc_imem_sys_devlink_notify_rx - Receive downlink characters from CP,
* the downlink skbuf is added at the end of the
* downlink or rx list
* @ipc_devlink: Pointer to ipc_devlink data-struct
* @skb: Pointer to sk buffer
*/
void ipc_imem_sys_devlink_notify_rx(struct iosm_devlink *ipc_devlink,
struct sk_buff *skb);
/**
* ipc_imem_sys_devlink_read - Copy the rx data and free the skbuf
* @ipc_devlink: Devlink instance
* @data: Buffer to read the data from modem
* @bytes_to_read: Size of destination buffer
* @bytes_read: Number of bytes read
*
* Return: 0 on success and failure value on error
*/
int ipc_imem_sys_devlink_read(struct iosm_devlink *ipc_devlink, u8 *data,
u32 bytes_to_read, u32 *bytes_read);
/**
* ipc_imem_sys_devlink_write - Route the uplink buffer to CP
* @ipc_devlink: Devlink_sio instance
* @buf: Pointer to buffer
* @count: Number of data bytes to write
* Return: 0 on success and failure value on error
*/
int ipc_imem_sys_devlink_write(struct iosm_devlink *ipc_devlink,
unsigned char *buf, int count);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment