drivers/net/wwan/iosm/iosm_ipc_imem.c

Source file repositories/reference/linux-study-clean/drivers/net/wwan/iosm/iosm_ipc_imem.c

File Facts

System
Linux kernel
Corpus path
drivers/net/wwan/iosm/iosm_ipc_imem.c
Extension
.c
Size
41370 bytes
Lines
1501
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (!ipc_imem_check_wwan_ips(channel)) {
			hpda_ctrl_pending |=
				ipc_protocol_ul_td_send(ipc_imem->ipc_protocol,
							pipe, ul_list);
		} else {
			hpda_pending |=
				ipc_protocol_ul_td_send(ipc_imem->ipc_protocol,
							pipe, ul_list);
		}
	}

	/* forced HP update needed for non data channels */
	if (hpda_ctrl_pending) {
		hpda_pending = false;
		ipc_protocol_doorbell_trigger(ipc_imem->ipc_protocol,
					      IPC_HP_UL_WRITE_TD);
	}

	return hpda_pending;
}

void ipc_imem_ipc_init_check(struct iosm_imem *ipc_imem)
{
	int timeout = IPC_MODEM_BOOT_TIMEOUT;

	ipc_imem->ipc_requested_state = IPC_MEM_DEVICE_IPC_INIT;

	/* Trigger the CP interrupt to enter the init state. */
	ipc_doorbell_fire(ipc_imem->pcie, IPC_DOORBELL_IRQ_IPC,
			  IPC_MEM_DEVICE_IPC_INIT);
	/* Wait for the CP update. */
	do {
		if (ipc_mmio_get_ipc_state(ipc_imem->mmio) ==
		    ipc_imem->ipc_requested_state) {
			/* Prepare the MMIO space */
			ipc_mmio_config(ipc_imem->mmio);

			/* Trigger the CP irq to enter the running state. */
			ipc_imem->ipc_requested_state =
				IPC_MEM_DEVICE_IPC_RUNNING;
			ipc_doorbell_fire(ipc_imem->pcie, IPC_DOORBELL_IRQ_IPC,
					  IPC_MEM_DEVICE_IPC_RUNNING);

			return;
		}
		msleep(20);
	} while (--timeout);

	/* timeout */
	dev_err(ipc_imem->dev, "%s: ipc_status(%d) ne. IPC_MEM_DEVICE_IPC_INIT",
		ipc_imem_phase_get_string(ipc_imem->phase),
		ipc_mmio_get_ipc_state(ipc_imem->mmio));

	ipc_uevent_send(ipc_imem->dev, UEVENT_MDM_TIMEOUT);
}

/* Analyze the packet type and distribute it. */
static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem,
				    struct ipc_pipe *pipe, struct sk_buff *skb)
{
	u16 port_id;

	if (!skb)
		return;

	/* An AT/control or IP packet is expected. */
	switch (pipe->channel->ctype) {
	case IPC_CTYPE_CTRL:
		port_id = pipe->channel->channel_id;
		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)
			ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink,
						       skb);
		else if (ipc_is_trace_channel(ipc_imem, port_id))
			ipc_trace_port_rx(ipc_imem, skb);
		else
			wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port,
				     skb);
		break;

	case IPC_CTYPE_WWAN:
		if (pipe->channel->if_id == IPC_MEM_MUX_IP_CH_IF_ID)
			ipc_mux_dl_decode(ipc_imem->mux, skb);
		break;
	default:
		dev_err(ipc_imem->dev, "Invalid channel type");
		break;
	}

Annotation

Implementation Notes