mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: wwan: iosm: transport layer support for fw flashing/cd
Implements transport layer protocol for fw flashing/coredump collection. Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									09e7b002ff
								
							
						
					
					
						commit
						8d9be06341
					
				
					 6 changed files with 464 additions and 30 deletions
				
			
		| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
#include "iosm_ipc_chnl_cfg.h"
 | 
			
		||||
 | 
			
		||||
/* 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_AT_BUF_SIZE 2048
 | 
			
		||||
#define IPC_MEM_MAX_DL_RPC_BUF_SIZE (32 * 1024)
 | 
			
		||||
| 
						 | 
				
			
			@ -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_MAX_TDS_MBIM, IPC_MEM_MAX_TDS_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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ enum ipc_channel_id {
 | 
			
		|||
	IPC_MEM_CTRL_CHL_ID_4,
 | 
			
		||||
	IPC_MEM_CTRL_CHL_ID_5,
 | 
			
		||||
	IPC_MEM_CTRL_CHL_ID_6,
 | 
			
		||||
	IPC_MEM_CTRL_CHL_ID_7,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@
 | 
			
		|||
#include <linux/delay.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_port.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -263,9 +265,12 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem,
 | 
			
		|||
	switch (pipe->channel->ctype) {
 | 
			
		||||
	case IPC_CTYPE_CTRL:
 | 
			
		||||
		port_id = pipe->channel->channel_id;
 | 
			
		||||
 | 
			
		||||
		/* Pass the packet to the wwan layer. */
 | 
			
		||||
		wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port, skb);
 | 
			
		||||
		if (port_id == IPC_MEM_CTRL_CHL_ID_7)
 | 
			
		||||
			ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink,
 | 
			
		||||
						       skb);
 | 
			
		||||
		else
 | 
			
		||||
			wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port,
 | 
			
		||||
				     skb);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case IPC_CTYPE_WWAN:
 | 
			
		||||
| 
						 | 
				
			
			@ -399,19 +404,8 @@ static void ipc_imem_rom_irq_exec(struct iosm_imem *ipc_imem)
 | 
			
		|||
{
 | 
			
		||||
	struct ipc_mem_channel *channel;
 | 
			
		||||
 | 
			
		||||
	if (ipc_imem->flash_channel_id < 0) {
 | 
			
		||||
		ipc_imem->rom_exit_code = IMEM_ROM_EXIT_FAIL;
 | 
			
		||||
		dev_err(ipc_imem->dev, "Missing flash app:%d",
 | 
			
		||||
			ipc_imem->flash_channel_id);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channel = ipc_imem->ipc_devlink->devlink_sio.channel;
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -572,7 +566,7 @@ static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq)
 | 
			
		|||
	enum ipc_phase old_phase, phase;
 | 
			
		||||
	bool retry_allocation = false;
 | 
			
		||||
	bool ul_pending = false;
 | 
			
		||||
	int ch_id, i;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (irq != IMEM_IRQ_DONT_CARE)
 | 
			
		||||
		ipc_imem->ev_irq_pending[irq] = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -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) &&
 | 
			
		||||
	    ipc_imem->ipc_requested_state == IPC_MEM_DEVICE_IPC_RUNNING &&
 | 
			
		||||
	    ipc_mmio_get_ipc_state(ipc_imem->mmio) ==
 | 
			
		||||
		    IPC_MEM_DEVICE_IPC_RUNNING &&
 | 
			
		||||
	    ipc_imem->flash_channel_id >= 0) {
 | 
			
		||||
		/* Wake up the flash app to open the pipes. */
 | 
			
		||||
		ch_id = ipc_imem->flash_channel_id;
 | 
			
		||||
		complete(&ipc_imem->channels[ch_id].ul_sem);
 | 
			
		||||
						IPC_MEM_DEVICE_IPC_RUNNING) {
 | 
			
		||||
		complete(&ipc_imem->ipc_devlink->devlink_sio.channel->ul_sem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset the expected CP state. */
 | 
			
		||||
| 
						 | 
				
			
			@ -1176,6 +1167,9 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem)
 | 
			
		|||
		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_channel_reset(ipc_imem);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1258,6 +1252,7 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
 | 
			
		|||
				void __iomem *mmio, struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct iosm_imem *ipc_imem = kzalloc(sizeof(*pcie->imem), GFP_KERNEL);
 | 
			
		||||
	enum ipc_mem_exec_stage stage;
 | 
			
		||||
 | 
			
		||||
	if (!ipc_imem)
 | 
			
		||||
		return NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -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->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 */
 | 
			
		||||
	ipc_imem->nr_of_channels = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1328,8 +1320,21 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
 | 
			
		|||
		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:
 | 
			
		||||
	hrtimer_cancel(&ipc_imem->td_alloc_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)
 | 
			
		|||
{
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ struct ipc_chnl_cfg;
 | 
			
		|||
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +98,7 @@ struct ipc_chnl_cfg;
 | 
			
		|||
#define IPC_MEM_DL_ETH_OFFSET 16
 | 
			
		||||
 | 
			
		||||
#define IPC_CB(skb) ((struct ipc_skb_cb *)((skb)->cb))
 | 
			
		||||
#define IOSM_CHIP_INFO_SIZE_MAX 100
 | 
			
		||||
 | 
			
		||||
#define FULLY_FUNCTIONAL 0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -304,9 +305,9 @@ enum ipc_phase {
 | 
			
		|||
 * @ipc_port:			IPC PORT data structure pointer
 | 
			
		||||
 * @pcie:			IPC PCIe
 | 
			
		||||
 * @dev:			Pointer to device structure
 | 
			
		||||
 * @flash_channel_id:		Reserved channel id for flashing to RAM.
 | 
			
		||||
 * @ipc_requested_state:	Expected IPC state on CP.
 | 
			
		||||
 * @channels:			Channel list with UL/DL pipe pairs.
 | 
			
		||||
 * @ipc_devlink:		IPC Devlink data structure pointer
 | 
			
		||||
 * @ipc_status:			local ipc_status
 | 
			
		||||
 * @nr_of_channels:		number of configured channels
 | 
			
		||||
 * @startup_timer:		startup timer for NAND support.
 | 
			
		||||
| 
						 | 
				
			
			@ -349,9 +350,9 @@ struct iosm_imem {
 | 
			
		|||
	struct iosm_cdev *ipc_port[IPC_MEM_MAX_CHANNELS];
 | 
			
		||||
	struct iosm_pcie *pcie;
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	int flash_channel_id;
 | 
			
		||||
	enum ipc_mem_device_ipc_state ipc_requested_state;
 | 
			
		||||
	struct ipc_mem_channel channels[IPC_MEM_MAX_CHANNELS];
 | 
			
		||||
	struct iosm_devlink *ipc_devlink;
 | 
			
		||||
	u32 ipc_status;
 | 
			
		||||
	u32 nr_of_channels;
 | 
			
		||||
	struct hrtimer startup_timer;
 | 
			
		||||
| 
						 | 
				
			
			@ -575,4 +576,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,
 | 
			
		||||
			   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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
#include <linux/delay.h>
 | 
			
		||||
 | 
			
		||||
#include "iosm_ipc_chnl_cfg.h"
 | 
			
		||||
#include "iosm_ipc_devlink.h"
 | 
			
		||||
#include "iosm_ipc_imem.h"
 | 
			
		||||
#include "iosm_ipc_imem_ops.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)
 | 
			
		|||
out:
 | 
			
		||||
	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 @@
 | 
			
		|||
#include "iosm_ipc_mux_codec.h"
 | 
			
		||||
 | 
			
		||||
/* Maximum wait time for blocking read */
 | 
			
		||||
#define IPC_READ_TIMEOUT 500
 | 
			
		||||
#define IPC_READ_TIMEOUT 3000
 | 
			
		||||
 | 
			
		||||
/* The delay in ms for defering the unregister */
 | 
			
		||||
#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,
 | 
			
		|||
 */
 | 
			
		||||
void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
 | 
			
		||||
				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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue