162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021-2023 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/etherdevice.h>
762306a36Sopenharmony_ci#include <linux/netdevice.h>
862306a36Sopenharmony_ci#include <linux/ieee80211.h>
962306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/moduleparam.h>
1262306a36Sopenharmony_ci#include <linux/mei_cl_bus.h>
1362306a36Sopenharmony_ci#include <linux/rcupdate.h>
1462306a36Sopenharmony_ci#include <linux/debugfs.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/wait.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/mm.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <net/cfg80211.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "internal.h"
2362306a36Sopenharmony_ci#include "iwl-mei.h"
2462306a36Sopenharmony_ci#include "trace.h"
2562306a36Sopenharmony_ci#include "trace-data.h"
2662306a36Sopenharmony_ci#include "sap.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("The Intel(R) wireless / CSME firmware interface");
2962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MEI_WLAN_UUID UUID_LE(0x13280904, 0x7792, 0x4fcb, \
3262306a36Sopenharmony_ci			      0xa1, 0xaa, 0x5e, 0x70, 0xcb, 0xb1, 0xe8, 0x65)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* After CSME takes ownership, it won't release it for 60 seconds to avoid
3562306a36Sopenharmony_ci * frequent ownership transitions.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci#define MEI_OWNERSHIP_RETAKE_TIMEOUT_MS	msecs_to_jiffies(60000)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * Since iwlwifi calls iwlmei without any context, hold a pointer to the
4162306a36Sopenharmony_ci * mei_cl_device structure here.
4262306a36Sopenharmony_ci * Define a mutex that will synchronize all the flows between iwlwifi and
4362306a36Sopenharmony_ci * iwlmei.
4462306a36Sopenharmony_ci * Note that iwlmei can't have several instances, so it ok to have static
4562306a36Sopenharmony_ci * variables here.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cistatic struct mei_cl_device *iwl_mei_global_cldev;
4862306a36Sopenharmony_cistatic DEFINE_MUTEX(iwl_mei_mutex);
4962306a36Sopenharmony_cistatic unsigned long iwl_mei_status;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cienum iwl_mei_status_bits {
5262306a36Sopenharmony_ci	IWL_MEI_STATUS_SAP_CONNECTED,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cibool iwl_mei_is_connected(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return test_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_is_connected);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define SAP_VERSION	3
6262306a36Sopenharmony_ci#define SAP_CONTROL_BLOCK_ID 0x21504153 /* SAP! in ASCII */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct iwl_sap_q_ctrl_blk {
6562306a36Sopenharmony_ci	__le32 wr_ptr;
6662306a36Sopenharmony_ci	__le32 rd_ptr;
6762306a36Sopenharmony_ci	__le32 size;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cienum iwl_sap_q_idx {
7162306a36Sopenharmony_ci	SAP_QUEUE_IDX_NOTIF = 0,
7262306a36Sopenharmony_ci	SAP_QUEUE_IDX_DATA,
7362306a36Sopenharmony_ci	SAP_QUEUE_IDX_MAX,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct iwl_sap_dir {
7762306a36Sopenharmony_ci	__le32 reserved;
7862306a36Sopenharmony_ci	struct iwl_sap_q_ctrl_blk q_ctrl_blk[SAP_QUEUE_IDX_MAX];
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cienum iwl_sap_dir_idx {
8262306a36Sopenharmony_ci	SAP_DIRECTION_HOST_TO_ME = 0,
8362306a36Sopenharmony_ci	SAP_DIRECTION_ME_TO_HOST,
8462306a36Sopenharmony_ci	SAP_DIRECTION_MAX,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistruct iwl_sap_shared_mem_ctrl_blk {
8862306a36Sopenharmony_ci	__le32 sap_id;
8962306a36Sopenharmony_ci	__le32 size;
9062306a36Sopenharmony_ci	struct iwl_sap_dir dir[SAP_DIRECTION_MAX];
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * The shared area has the following layout:
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * +-----------------------------------+
9762306a36Sopenharmony_ci * |struct iwl_sap_shared_mem_ctrl_blk |
9862306a36Sopenharmony_ci * +-----------------------------------+
9962306a36Sopenharmony_ci * |Host -> ME data queue              |
10062306a36Sopenharmony_ci * +-----------------------------------+
10162306a36Sopenharmony_ci * |Host -> ME notif queue             |
10262306a36Sopenharmony_ci * +-----------------------------------+
10362306a36Sopenharmony_ci * |ME -> Host data queue              |
10462306a36Sopenharmony_ci * +-----------------------------------+
10562306a36Sopenharmony_ci * |ME -> host notif queue             |
10662306a36Sopenharmony_ci * +-----------------------------------+
10762306a36Sopenharmony_ci * |SAP control block id (SAP!)        |
10862306a36Sopenharmony_ci * +-----------------------------------+
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define SAP_H2M_DATA_Q_SZ	48256
11262306a36Sopenharmony_ci#define SAP_M2H_DATA_Q_SZ	24128
11362306a36Sopenharmony_ci#define SAP_H2M_NOTIF_Q_SZ	2240
11462306a36Sopenharmony_ci#define SAP_M2H_NOTIF_Q_SZ	62720
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define _IWL_MEI_SAP_SHARED_MEM_SZ \
11762306a36Sopenharmony_ci	(sizeof(struct iwl_sap_shared_mem_ctrl_blk) + \
11862306a36Sopenharmony_ci	 SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ + \
11962306a36Sopenharmony_ci	 SAP_M2H_DATA_Q_SZ + SAP_M2H_NOTIF_Q_SZ + 4)
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define IWL_MEI_SAP_SHARED_MEM_SZ \
12262306a36Sopenharmony_ci	(roundup(_IWL_MEI_SAP_SHARED_MEM_SZ, PAGE_SIZE))
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct iwl_mei_shared_mem_ptrs {
12562306a36Sopenharmony_ci	struct iwl_sap_shared_mem_ctrl_blk *ctrl;
12662306a36Sopenharmony_ci	void *q_head[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX];
12762306a36Sopenharmony_ci	size_t q_size[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX];
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistruct iwl_mei_filters {
13162306a36Sopenharmony_ci	struct rcu_head rcu_head;
13262306a36Sopenharmony_ci	struct iwl_sap_oob_filters filters;
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * struct iwl_mei - holds the private date for iwl_mei
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * @get_nvm_wq: the wait queue for the get_nvm flow
13962306a36Sopenharmony_ci * @send_csa_msg_wk: used to defer the transmission of the CHECK_SHARED_AREA
14062306a36Sopenharmony_ci *	message. Used so that we can send CHECK_SHARED_AREA from atomic
14162306a36Sopenharmony_ci *	contexts.
14262306a36Sopenharmony_ci * @get_ownership_wq: the wait queue for the get_ownership_flow
14362306a36Sopenharmony_ci * @shared_mem: the memory that is shared between CSME and the host
14462306a36Sopenharmony_ci * @cldev: the pointer to the MEI client device
14562306a36Sopenharmony_ci * @nvm: the data returned by the CSME for the NVM
14662306a36Sopenharmony_ci * @filters: the filters sent by CSME
14762306a36Sopenharmony_ci * @got_ownership: true if we own the device
14862306a36Sopenharmony_ci * @amt_enabled: true if CSME has wireless enabled
14962306a36Sopenharmony_ci * @csa_throttled: when true, we can't send CHECK_SHARED_AREA over the MEI
15062306a36Sopenharmony_ci *	bus, but rather need to wait until send_csa_msg_wk runs
15162306a36Sopenharmony_ci * @csme_taking_ownership: true when CSME is taking ownership. Used to remember
15262306a36Sopenharmony_ci *	to send CSME_OWNERSHIP_CONFIRMED when the driver completes its down
15362306a36Sopenharmony_ci *	flow.
15462306a36Sopenharmony_ci * @link_prot_state: true when we are in link protection PASSIVE
15562306a36Sopenharmony_ci * @device_down: true if the device is down. Used to remember to send
15662306a36Sopenharmony_ci *	CSME_OWNERSHIP_CONFIRMED when the driver is already down.
15762306a36Sopenharmony_ci * @csa_throttle_end_wk: used when &csa_throttled is true
15862306a36Sopenharmony_ci * @pldr_wq: the wait queue for PLDR flow
15962306a36Sopenharmony_ci * @pldr_active: PLDR flow is in progress
16062306a36Sopenharmony_ci * @data_q_lock: protects the access to the data queues which are
16162306a36Sopenharmony_ci *	accessed without the mutex.
16262306a36Sopenharmony_ci * @netdev_work: used to defer registering and unregistering of the netdev to
16362306a36Sopenharmony_ci *	avoid taking the rtnl lock in the SAP messages handlers.
16462306a36Sopenharmony_ci * @ownership_dwork: used to re-ask for NIC ownership after ownership was taken
16562306a36Sopenharmony_ci *	by CSME or when a previous ownership request failed.
16662306a36Sopenharmony_ci * @sap_seq_no: the sequence number for the SAP messages
16762306a36Sopenharmony_ci * @seq_no: the sequence number for the SAP messages
16862306a36Sopenharmony_ci * @dbgfs_dir: the debugfs dir entry
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistruct iwl_mei {
17162306a36Sopenharmony_ci	wait_queue_head_t get_nvm_wq;
17262306a36Sopenharmony_ci	struct work_struct send_csa_msg_wk;
17362306a36Sopenharmony_ci	wait_queue_head_t get_ownership_wq;
17462306a36Sopenharmony_ci	struct iwl_mei_shared_mem_ptrs shared_mem;
17562306a36Sopenharmony_ci	struct mei_cl_device *cldev;
17662306a36Sopenharmony_ci	struct iwl_mei_nvm *nvm;
17762306a36Sopenharmony_ci	struct iwl_mei_filters __rcu *filters;
17862306a36Sopenharmony_ci	bool got_ownership;
17962306a36Sopenharmony_ci	bool amt_enabled;
18062306a36Sopenharmony_ci	bool csa_throttled;
18162306a36Sopenharmony_ci	bool csme_taking_ownership;
18262306a36Sopenharmony_ci	bool link_prot_state;
18362306a36Sopenharmony_ci	bool device_down;
18462306a36Sopenharmony_ci	struct delayed_work csa_throttle_end_wk;
18562306a36Sopenharmony_ci	wait_queue_head_t pldr_wq;
18662306a36Sopenharmony_ci	bool pldr_active;
18762306a36Sopenharmony_ci	spinlock_t data_q_lock;
18862306a36Sopenharmony_ci	struct work_struct netdev_work;
18962306a36Sopenharmony_ci	struct delayed_work ownership_dwork;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	atomic_t sap_seq_no;
19262306a36Sopenharmony_ci	atomic_t seq_no;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	struct dentry *dbgfs_dir;
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * struct iwl_mei_cache - cache for the parameters from iwlwifi
19962306a36Sopenharmony_ci * @ops: Callbacks to iwlwifi.
20062306a36Sopenharmony_ci * @netdev: The netdev that will be used to transmit / receive packets.
20162306a36Sopenharmony_ci * @conn_info: The connection info message triggered by iwlwifi's association.
20262306a36Sopenharmony_ci * @power_limit: pointer to an array of 10 elements (le16) represents the power
20362306a36Sopenharmony_ci *	restrictions per chain.
20462306a36Sopenharmony_ci * @rf_kill: rf kill state.
20562306a36Sopenharmony_ci * @mcc: MCC info
20662306a36Sopenharmony_ci * @mac_address: interface MAC address.
20762306a36Sopenharmony_ci * @nvm_address: NVM MAC address.
20862306a36Sopenharmony_ci * @priv: A pointer to iwlwifi.
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * This used to cache the configurations coming from iwlwifi's way. The data
21162306a36Sopenharmony_ci * is cached here so that we can buffer the configuration even if we don't have
21262306a36Sopenharmony_ci * a bind from the mei bus and hence, on iwl_mei structure.
21362306a36Sopenharmony_ci */
21462306a36Sopenharmony_cistruct iwl_mei_cache {
21562306a36Sopenharmony_ci	const struct iwl_mei_ops *ops;
21662306a36Sopenharmony_ci	struct net_device __rcu *netdev;
21762306a36Sopenharmony_ci	const struct iwl_sap_notif_connection_info *conn_info;
21862306a36Sopenharmony_ci	const __le16 *power_limit;
21962306a36Sopenharmony_ci	u32 rf_kill;
22062306a36Sopenharmony_ci	u16 mcc;
22162306a36Sopenharmony_ci	u8 mac_address[6];
22262306a36Sopenharmony_ci	u8 nvm_address[6];
22362306a36Sopenharmony_ci	void *priv;
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic struct iwl_mei_cache iwl_mei_cache = {
22762306a36Sopenharmony_ci	.rf_kill = SAP_HW_RFKILL_DEASSERTED | SAP_SW_RFKILL_DEASSERTED
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void iwl_mei_free_shared_mem(struct mei_cl_device *cldev)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (mei_cldev_dma_unmap(cldev))
23562306a36Sopenharmony_ci		dev_err(&cldev->dev, "Couldn't unmap the shared mem properly\n");
23662306a36Sopenharmony_ci	memset(&mei->shared_mem, 0, sizeof(mei->shared_mem));
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci#define HBM_DMA_BUF_ID_WLAN 1
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
24462306a36Sopenharmony_ci	struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	mem->ctrl = mei_cldev_dma_map(cldev, HBM_DMA_BUF_ID_WLAN,
24762306a36Sopenharmony_ci				       IWL_MEI_SAP_SHARED_MEM_SZ);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (IS_ERR(mem->ctrl)) {
25062306a36Sopenharmony_ci		int ret = PTR_ERR(mem->ctrl);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		mem->ctrl = NULL;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		return ret;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	memset(mem->ctrl, 0, IWL_MEI_SAP_SHARED_MEM_SZ);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void iwl_mei_init_shared_mem(struct iwl_mei *mei)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
26562306a36Sopenharmony_ci	struct iwl_sap_dir *h2m;
26662306a36Sopenharmony_ci	struct iwl_sap_dir *m2h;
26762306a36Sopenharmony_ci	int dir, queue;
26862306a36Sopenharmony_ci	u8 *q_head;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mem->ctrl->sap_id = cpu_to_le32(SAP_CONTROL_BLOCK_ID);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	mem->ctrl->size = cpu_to_le32(sizeof(*mem->ctrl));
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	h2m = &mem->ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
27562306a36Sopenharmony_ci	m2h = &mem->ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	h2m->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
27862306a36Sopenharmony_ci		cpu_to_le32(SAP_H2M_DATA_Q_SZ);
27962306a36Sopenharmony_ci	h2m->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
28062306a36Sopenharmony_ci		cpu_to_le32(SAP_H2M_NOTIF_Q_SZ);
28162306a36Sopenharmony_ci	m2h->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
28262306a36Sopenharmony_ci		cpu_to_le32(SAP_M2H_DATA_Q_SZ);
28362306a36Sopenharmony_ci	m2h->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
28462306a36Sopenharmony_ci		cpu_to_le32(SAP_M2H_NOTIF_Q_SZ);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* q_head points to the start of the first queue */
28762306a36Sopenharmony_ci	q_head = (void *)(mem->ctrl + 1);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Initialize the queue heads */
29062306a36Sopenharmony_ci	for (dir = 0; dir < SAP_DIRECTION_MAX; dir++) {
29162306a36Sopenharmony_ci		for (queue = 0; queue < SAP_QUEUE_IDX_MAX; queue++) {
29262306a36Sopenharmony_ci			mem->q_head[dir][queue] = q_head;
29362306a36Sopenharmony_ci			q_head +=
29462306a36Sopenharmony_ci				le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size);
29562306a36Sopenharmony_ci			mem->q_size[dir][queue] =
29662306a36Sopenharmony_ci				le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size);
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	*(__le32 *)q_head = cpu_to_le32(SAP_CONTROL_BLOCK_ID);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic ssize_t iwl_mei_write_cyclic_buf(struct mei_cl_device *cldev,
30462306a36Sopenharmony_ci					struct iwl_sap_q_ctrl_blk *notif_q,
30562306a36Sopenharmony_ci					u8 *q_head,
30662306a36Sopenharmony_ci					const struct iwl_sap_hdr *hdr,
30762306a36Sopenharmony_ci					u32 q_sz)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
31062306a36Sopenharmony_ci	u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
31162306a36Sopenharmony_ci	size_t room_in_buf;
31262306a36Sopenharmony_ci	size_t tx_sz = sizeof(*hdr) + le16_to_cpu(hdr->len);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (rd > q_sz || wr > q_sz) {
31562306a36Sopenharmony_ci		dev_err(&cldev->dev,
31662306a36Sopenharmony_ci			"Pointers are past the end of the buffer\n");
31762306a36Sopenharmony_ci		return -EINVAL;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* we don't have enough room for the data to write */
32362306a36Sopenharmony_ci	if (room_in_buf < tx_sz) {
32462306a36Sopenharmony_ci		dev_err(&cldev->dev,
32562306a36Sopenharmony_ci			"Not enough room in the buffer\n");
32662306a36Sopenharmony_ci		return -ENOSPC;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (wr + tx_sz <= q_sz) {
33062306a36Sopenharmony_ci		memcpy(q_head + wr, hdr, tx_sz);
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		memcpy(q_head + wr, hdr, q_sz - wr);
33362306a36Sopenharmony_ci		memcpy(q_head, (const u8 *)hdr + q_sz - wr, tx_sz - (q_sz - wr));
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz));
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic bool iwl_mei_host_to_me_data_pending(const struct iwl_mei *mei)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct iwl_sap_q_ctrl_blk *notif_q;
34362306a36Sopenharmony_ci	struct iwl_sap_dir *dir;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
34662306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr))
34962306a36Sopenharmony_ci		return true;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
35262306a36Sopenharmony_ci	return READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int iwl_mei_send_check_shared_area(struct mei_cl_device *cldev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
35862306a36Sopenharmony_ci	struct iwl_sap_me_msg_start msg = {
35962306a36Sopenharmony_ci		.hdr.type = cpu_to_le32(SAP_ME_MSG_CHECK_SHARED_AREA),
36062306a36Sopenharmony_ci		.hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)),
36162306a36Sopenharmony_ci	};
36262306a36Sopenharmony_ci	int ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	lockdep_assert_held(&iwl_mei_mutex);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (mei->csa_throttled)
36762306a36Sopenharmony_ci		return 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	trace_iwlmei_me_msg(&msg.hdr, true);
37062306a36Sopenharmony_ci	ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg));
37162306a36Sopenharmony_ci	if (ret != sizeof(msg)) {
37262306a36Sopenharmony_ci		dev_err(&cldev->dev,
37362306a36Sopenharmony_ci			"failed to send the SAP_ME_MSG_CHECK_SHARED_AREA message %d\n",
37462306a36Sopenharmony_ci			ret);
37562306a36Sopenharmony_ci		return ret;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	mei->csa_throttled = true;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	schedule_delayed_work(&mei->csa_throttle_end_wk,
38162306a36Sopenharmony_ci			      msecs_to_jiffies(100));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return 0;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void iwl_mei_csa_throttle_end_wk(struct work_struct *wk)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct iwl_mei *mei =
38962306a36Sopenharmony_ci		container_of(wk, struct iwl_mei, csa_throttle_end_wk.work);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	mei->csa_throttled = false;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (iwl_mei_host_to_me_data_pending(mei))
39662306a36Sopenharmony_ci		iwl_mei_send_check_shared_area(mei->cldev);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int iwl_mei_send_sap_msg_payload(struct mei_cl_device *cldev,
40262306a36Sopenharmony_ci					struct iwl_sap_hdr *hdr)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
40562306a36Sopenharmony_ci	struct iwl_sap_q_ctrl_blk *notif_q;
40662306a36Sopenharmony_ci	struct iwl_sap_dir *dir;
40762306a36Sopenharmony_ci	void *q_head;
40862306a36Sopenharmony_ci	u32 q_sz;
40962306a36Sopenharmony_ci	int ret;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	lockdep_assert_held(&iwl_mei_mutex);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (!mei->shared_mem.ctrl) {
41462306a36Sopenharmony_ci		dev_err(&cldev->dev,
41562306a36Sopenharmony_ci			"No shared memory, can't send any SAP message\n");
41662306a36Sopenharmony_ci		return -EINVAL;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
42062306a36Sopenharmony_ci		dev_err(&cldev->dev,
42162306a36Sopenharmony_ci			"Can't send a SAP message if we're not connected\n");
42262306a36Sopenharmony_ci		return -ENODEV;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
42662306a36Sopenharmony_ci	dev_dbg(&cldev->dev, "Sending %d\n", hdr->type);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
42962306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
43062306a36Sopenharmony_ci	q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
43162306a36Sopenharmony_ci	q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
43262306a36Sopenharmony_ci	ret = iwl_mei_write_cyclic_buf(q_head, notif_q, q_head, hdr, q_sz);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (ret < 0)
43562306a36Sopenharmony_ci		return ret;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	trace_iwlmei_sap_cmd(hdr, true);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return iwl_mei_send_check_shared_area(cldev);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_civoid iwl_mei_add_data_to_ring(struct sk_buff *skb, bool cb_tx)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct iwl_sap_q_ctrl_blk *notif_q;
44562306a36Sopenharmony_ci	struct iwl_sap_dir *dir;
44662306a36Sopenharmony_ci	struct iwl_mei *mei;
44762306a36Sopenharmony_ci	size_t room_in_buf;
44862306a36Sopenharmony_ci	size_t tx_sz;
44962306a36Sopenharmony_ci	size_t hdr_sz;
45062306a36Sopenharmony_ci	u32 q_sz;
45162306a36Sopenharmony_ci	u32 rd;
45262306a36Sopenharmony_ci	u32 wr;
45362306a36Sopenharmony_ci	u8 *q_head;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!iwl_mei_global_cldev)
45662306a36Sopenharmony_ci		return;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/*
46162306a36Sopenharmony_ci	 * We access this path for Rx packets (the more common case)
46262306a36Sopenharmony_ci	 * and from Tx path when we send DHCP packets, the latter is
46362306a36Sopenharmony_ci	 * very unlikely.
46462306a36Sopenharmony_ci	 * Take the lock already here to make sure we see that remove()
46562306a36Sopenharmony_ci	 * might have cleared the IWL_MEI_STATUS_SAP_CONNECTED bit.
46662306a36Sopenharmony_ci	 */
46762306a36Sopenharmony_ci	spin_lock_bh(&mei->data_q_lock);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
47062306a36Sopenharmony_ci		spin_unlock_bh(&mei->data_q_lock);
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * We are in a RCU critical section and the remove from the CSME bus
47662306a36Sopenharmony_ci	 * which would free this memory waits for the readers to complete (this
47762306a36Sopenharmony_ci	 * is done in netdev_rx_handler_unregister).
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
48062306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
48162306a36Sopenharmony_ci	q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA];
48262306a36Sopenharmony_ci	q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA];
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
48562306a36Sopenharmony_ci	wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
48662306a36Sopenharmony_ci	hdr_sz = cb_tx ? sizeof(struct iwl_sap_cb_data) :
48762306a36Sopenharmony_ci			 sizeof(struct iwl_sap_hdr);
48862306a36Sopenharmony_ci	tx_sz = skb->len + hdr_sz;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (rd > q_sz || wr > q_sz) {
49162306a36Sopenharmony_ci		dev_err(&mei->cldev->dev,
49262306a36Sopenharmony_ci			"can't write the data: pointers are past the end of the buffer\n");
49362306a36Sopenharmony_ci		goto out;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* we don't have enough room for the data to write */
49962306a36Sopenharmony_ci	if (room_in_buf < tx_sz) {
50062306a36Sopenharmony_ci		dev_err(&mei->cldev->dev,
50162306a36Sopenharmony_ci			"Not enough room in the buffer for this data\n");
50262306a36Sopenharmony_ci		goto out;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (skb_headroom(skb) < hdr_sz) {
50662306a36Sopenharmony_ci		dev_err(&mei->cldev->dev,
50762306a36Sopenharmony_ci			"Not enough headroom in the skb to write the SAP header\n");
50862306a36Sopenharmony_ci		goto out;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (cb_tx) {
51262306a36Sopenharmony_ci		struct iwl_sap_cb_data *cb_hdr = skb_push(skb, sizeof(*cb_hdr));
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		memset(cb_hdr, 0, sizeof(*cb_hdr));
51562306a36Sopenharmony_ci		cb_hdr->hdr.type = cpu_to_le16(SAP_MSG_CB_DATA_PACKET);
51662306a36Sopenharmony_ci		cb_hdr->hdr.len = cpu_to_le16(skb->len - sizeof(cb_hdr->hdr));
51762306a36Sopenharmony_ci		cb_hdr->hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
51862306a36Sopenharmony_ci		cb_hdr->to_me_filt_status = cpu_to_le32(BIT(CB_TX_DHCP_FILT_IDX));
51962306a36Sopenharmony_ci		cb_hdr->data_len = cpu_to_le32(skb->len - sizeof(*cb_hdr));
52062306a36Sopenharmony_ci		trace_iwlmei_sap_data(skb, IWL_SAP_TX_DHCP);
52162306a36Sopenharmony_ci	} else {
52262306a36Sopenharmony_ci		struct iwl_sap_hdr *hdr = skb_push(skb, sizeof(*hdr));
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		hdr->type = cpu_to_le16(SAP_MSG_DATA_PACKET);
52562306a36Sopenharmony_ci		hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));
52662306a36Sopenharmony_ci		hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
52762306a36Sopenharmony_ci		trace_iwlmei_sap_data(skb, IWL_SAP_TX_DATA_FROM_AIR);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (wr + tx_sz <= q_sz) {
53162306a36Sopenharmony_ci		skb_copy_bits(skb, 0, q_head + wr, tx_sz);
53262306a36Sopenharmony_ci	} else {
53362306a36Sopenharmony_ci		skb_copy_bits(skb, 0, q_head + wr, q_sz - wr);
53462306a36Sopenharmony_ci		skb_copy_bits(skb, q_sz - wr, q_head, tx_sz - (q_sz - wr));
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz));
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciout:
54062306a36Sopenharmony_ci	spin_unlock_bh(&mei->data_q_lock);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int
54462306a36Sopenharmony_ciiwl_mei_send_sap_msg(struct mei_cl_device *cldev, u16 type)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct iwl_sap_hdr msg = {
54762306a36Sopenharmony_ci		.type = cpu_to_le16(type),
54862306a36Sopenharmony_ci	};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return iwl_mei_send_sap_msg_payload(cldev, &msg);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic void iwl_mei_send_csa_msg_wk(struct work_struct *wk)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct iwl_mei *mei =
55662306a36Sopenharmony_ci		container_of(wk, struct iwl_mei, send_csa_msg_wk);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
55962306a36Sopenharmony_ci		return;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	iwl_mei_send_check_shared_area(mei->cldev);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/* Called in a RCU read critical section from netif_receive_skb */
56962306a36Sopenharmony_cistatic rx_handler_result_t iwl_mei_rx_handler(struct sk_buff **pskb)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct sk_buff *skb = *pskb;
57262306a36Sopenharmony_ci	struct iwl_mei *mei =
57362306a36Sopenharmony_ci		rcu_dereference(skb->dev->rx_handler_data);
57462306a36Sopenharmony_ci	struct iwl_mei_filters *filters = rcu_dereference(mei->filters);
57562306a36Sopenharmony_ci	bool rx_for_csme = false;
57662306a36Sopenharmony_ci	rx_handler_result_t res;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * remove() unregisters this handler and synchronize_net, so this
58062306a36Sopenharmony_ci	 * should never happen.
58162306a36Sopenharmony_ci	 */
58262306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
58362306a36Sopenharmony_ci		dev_err(&mei->cldev->dev,
58462306a36Sopenharmony_ci			"Got an Rx packet, but we're not connected to SAP?\n");
58562306a36Sopenharmony_ci		return RX_HANDLER_PASS;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (filters)
58962306a36Sopenharmony_ci		res = iwl_mei_rx_filter(skb, &filters->filters, &rx_for_csme);
59062306a36Sopenharmony_ci	else
59162306a36Sopenharmony_ci		res = RX_HANDLER_PASS;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/*
59462306a36Sopenharmony_ci	 * The data is already on the ring of the shared area, all we
59562306a36Sopenharmony_ci	 * need to do is to tell the CSME firmware to check what we have
59662306a36Sopenharmony_ci	 * there.
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	if (rx_for_csme)
59962306a36Sopenharmony_ci		schedule_work(&mei->send_csa_msg_wk);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (res != RX_HANDLER_PASS) {
60262306a36Sopenharmony_ci		trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_DROPPED_FROM_AIR);
60362306a36Sopenharmony_ci		dev_kfree_skb(skb);
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return res;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void iwl_mei_netdev_work(struct work_struct *wk)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct iwl_mei *mei =
61262306a36Sopenharmony_ci		container_of(wk, struct iwl_mei, netdev_work);
61362306a36Sopenharmony_ci	struct net_device *netdev;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * First take rtnl and only then the mutex to avoid an ABBA
61762306a36Sopenharmony_ci	 * with iwl_mei_set_netdev()
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	rtnl_lock();
62062306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	netdev = rcu_dereference_protected(iwl_mei_cache.netdev,
62362306a36Sopenharmony_ci					   lockdep_is_held(&iwl_mei_mutex));
62462306a36Sopenharmony_ci	if (netdev) {
62562306a36Sopenharmony_ci		if (mei->amt_enabled)
62662306a36Sopenharmony_ci			netdev_rx_handler_register(netdev, iwl_mei_rx_handler,
62762306a36Sopenharmony_ci						   mei);
62862306a36Sopenharmony_ci		else
62962306a36Sopenharmony_ci			netdev_rx_handler_unregister(netdev);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
63362306a36Sopenharmony_ci	rtnl_unlock();
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic void
63762306a36Sopenharmony_ciiwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev,
63862306a36Sopenharmony_ci			   const struct iwl_sap_me_msg_start_ok *rsp,
63962306a36Sopenharmony_ci			   ssize_t len)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	if (len != sizeof(*rsp)) {
64262306a36Sopenharmony_ci		dev_err(&cldev->dev,
64362306a36Sopenharmony_ci			"got invalid SAP_ME_MSG_START_OK from CSME firmware\n");
64462306a36Sopenharmony_ci		dev_err(&cldev->dev,
64562306a36Sopenharmony_ci			"size is incorrect: %zd instead of %zu\n",
64662306a36Sopenharmony_ci			len, sizeof(*rsp));
64762306a36Sopenharmony_ci		return;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (rsp->supported_version != SAP_VERSION) {
65162306a36Sopenharmony_ci		dev_err(&cldev->dev,
65262306a36Sopenharmony_ci			"didn't get the expected version: got %d\n",
65362306a36Sopenharmony_ci			rsp->supported_version);
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
65862306a36Sopenharmony_ci	set_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
65962306a36Sopenharmony_ci	/*
66062306a36Sopenharmony_ci	 * We'll receive AMT_STATE SAP message in a bit and
66162306a36Sopenharmony_ci	 * that will continue the flow
66262306a36Sopenharmony_ci	 */
66362306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void iwl_mei_handle_csme_filters(struct mei_cl_device *cldev,
66762306a36Sopenharmony_ci					const struct iwl_sap_csme_filters *filters)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
67062306a36Sopenharmony_ci	struct iwl_mei_filters *new_filters;
67162306a36Sopenharmony_ci	struct iwl_mei_filters *old_filters;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	old_filters =
67462306a36Sopenharmony_ci		rcu_dereference_protected(mei->filters,
67562306a36Sopenharmony_ci					  lockdep_is_held(&iwl_mei_mutex));
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	new_filters = kzalloc(sizeof(*new_filters), GFP_KERNEL);
67862306a36Sopenharmony_ci	if (!new_filters)
67962306a36Sopenharmony_ci		return;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Copy the OOB filters */
68262306a36Sopenharmony_ci	new_filters->filters = filters->filters;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	rcu_assign_pointer(mei->filters, new_filters);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (old_filters)
68762306a36Sopenharmony_ci		kfree_rcu(old_filters, rcu_head);
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void
69162306a36Sopenharmony_ciiwl_mei_handle_conn_status(struct mei_cl_device *cldev,
69262306a36Sopenharmony_ci			   const struct iwl_sap_notif_conn_status *status)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
69562306a36Sopenharmony_ci	struct iwl_mei_conn_info conn_info = {
69662306a36Sopenharmony_ci		.lp_state = le32_to_cpu(status->link_prot_state),
69762306a36Sopenharmony_ci		.ssid_len = le32_to_cpu(status->conn_info.ssid_len),
69862306a36Sopenharmony_ci		.channel = status->conn_info.channel,
69962306a36Sopenharmony_ci		.band = status->conn_info.band,
70062306a36Sopenharmony_ci		.auth_mode = le32_to_cpu(status->conn_info.auth_mode),
70162306a36Sopenharmony_ci		.pairwise_cipher = le32_to_cpu(status->conn_info.pairwise_cipher),
70262306a36Sopenharmony_ci	};
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (!iwl_mei_cache.ops ||
70562306a36Sopenharmony_ci	    conn_info.ssid_len > ARRAY_SIZE(conn_info.ssid))
70662306a36Sopenharmony_ci		return;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	memcpy(conn_info.ssid, status->conn_info.ssid, conn_info.ssid_len);
70962306a36Sopenharmony_ci	ether_addr_copy(conn_info.bssid, status->conn_info.bssid);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	iwl_mei_cache.ops->me_conn_status(iwl_mei_cache.priv, &conn_info);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	mei->link_prot_state = status->link_prot_state;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/*
71662306a36Sopenharmony_ci	 * Update the Rfkill state in case the host does not own the device:
71762306a36Sopenharmony_ci	 * if we are in Link Protection, ask to not touch the device, else,
71862306a36Sopenharmony_ci	 * unblock rfkill.
71962306a36Sopenharmony_ci	 * If the host owns the device, inform the user space whether it can
72062306a36Sopenharmony_ci	 * roam.
72162306a36Sopenharmony_ci	 */
72262306a36Sopenharmony_ci	if (mei->got_ownership)
72362306a36Sopenharmony_ci		iwl_mei_cache.ops->roaming_forbidden(iwl_mei_cache.priv,
72462306a36Sopenharmony_ci						     status->link_prot_state);
72562306a36Sopenharmony_ci	else
72662306a36Sopenharmony_ci		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv,
72762306a36Sopenharmony_ci					  status->link_prot_state, false);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic void iwl_mei_set_init_conf(struct iwl_mei *mei)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct iwl_sap_notif_host_link_up link_msg = {
73362306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP),
73462306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(link_msg) - sizeof(link_msg.hdr)),
73562306a36Sopenharmony_ci	};
73662306a36Sopenharmony_ci	struct iwl_sap_notif_country_code mcc_msg = {
73762306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE),
73862306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(mcc_msg) - sizeof(mcc_msg.hdr)),
73962306a36Sopenharmony_ci		.mcc = cpu_to_le16(iwl_mei_cache.mcc),
74062306a36Sopenharmony_ci	};
74162306a36Sopenharmony_ci	struct iwl_sap_notif_sar_limits sar_msg = {
74262306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS),
74362306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(sar_msg) - sizeof(sar_msg.hdr)),
74462306a36Sopenharmony_ci	};
74562306a36Sopenharmony_ci	struct iwl_sap_notif_host_nic_info nic_info_msg = {
74662306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO),
74762306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(nic_info_msg) - sizeof(nic_info_msg.hdr)),
74862306a36Sopenharmony_ci	};
74962306a36Sopenharmony_ci	struct iwl_sap_msg_dw rfkill_msg = {
75062306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE),
75162306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(rfkill_msg) - sizeof(rfkill_msg.hdr)),
75262306a36Sopenharmony_ci		.val = cpu_to_le32(iwl_mei_cache.rf_kill),
75362306a36Sopenharmony_ci	};
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* wifi driver has registered already */
75662306a36Sopenharmony_ci	if (iwl_mei_cache.ops) {
75762306a36Sopenharmony_ci		iwl_mei_send_sap_msg(mei->cldev,
75862306a36Sopenharmony_ci				     SAP_MSG_NOTIF_WIFIDR_UP);
75962306a36Sopenharmony_ci		iwl_mei_cache.ops->sap_connected(iwl_mei_cache.priv);
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	iwl_mei_send_sap_msg(mei->cldev, SAP_MSG_NOTIF_WHO_OWNS_NIC);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (iwl_mei_cache.conn_info) {
76562306a36Sopenharmony_ci		link_msg.conn_info = *iwl_mei_cache.conn_info;
76662306a36Sopenharmony_ci		iwl_mei_send_sap_msg_payload(mei->cldev, &link_msg.hdr);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &mcc_msg.hdr);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (iwl_mei_cache.power_limit) {
77262306a36Sopenharmony_ci		memcpy(sar_msg.sar_chain_info_table, iwl_mei_cache.power_limit,
77362306a36Sopenharmony_ci		       sizeof(sar_msg.sar_chain_info_table));
77462306a36Sopenharmony_ci		iwl_mei_send_sap_msg_payload(mei->cldev, &sar_msg.hdr);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (is_valid_ether_addr(iwl_mei_cache.mac_address)) {
77862306a36Sopenharmony_ci		ether_addr_copy(nic_info_msg.mac_address,
77962306a36Sopenharmony_ci				iwl_mei_cache.mac_address);
78062306a36Sopenharmony_ci		ether_addr_copy(nic_info_msg.nvm_address,
78162306a36Sopenharmony_ci				iwl_mei_cache.nvm_address);
78262306a36Sopenharmony_ci		iwl_mei_send_sap_msg_payload(mei->cldev, &nic_info_msg.hdr);
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &rfkill_msg.hdr);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void iwl_mei_handle_amt_state(struct mei_cl_device *cldev,
78962306a36Sopenharmony_ci				     const struct iwl_sap_msg_dw *dw)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (mei->amt_enabled == !!le32_to_cpu(dw->val))
79662306a36Sopenharmony_ci		goto out;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	mei->amt_enabled = dw->val;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (mei->amt_enabled)
80162306a36Sopenharmony_ci		iwl_mei_set_init_conf(mei);
80262306a36Sopenharmony_ci	else if (iwl_mei_cache.ops)
80362306a36Sopenharmony_ci		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	schedule_work(&mei->netdev_work);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ciout:
80862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic void iwl_mei_handle_nic_owner(struct mei_cl_device *cldev,
81262306a36Sopenharmony_ci				     const struct iwl_sap_msg_dw *dw)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	mei->got_ownership = dw->val != cpu_to_le32(SAP_NIC_OWNER_ME);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void iwl_mei_handle_can_release_ownership(struct mei_cl_device *cldev,
82062306a36Sopenharmony_ci						 const void *payload)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	/* We can get ownership and driver is registered, go ahead */
82362306a36Sopenharmony_ci	if (iwl_mei_cache.ops)
82462306a36Sopenharmony_ci		iwl_mei_send_sap_msg(cldev,
82562306a36Sopenharmony_ci				     SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP);
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic void iwl_mei_handle_csme_taking_ownership(struct mei_cl_device *cldev,
82962306a36Sopenharmony_ci						 const void *payload)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	dev_info(&cldev->dev, "CSME takes ownership\n");
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	mei->got_ownership = false;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (iwl_mei_cache.ops && !mei->device_down) {
83862306a36Sopenharmony_ci		/*
83962306a36Sopenharmony_ci		 * Remember to send CSME_OWNERSHIP_CONFIRMED when the wifi
84062306a36Sopenharmony_ci		 * driver is finished taking the device down.
84162306a36Sopenharmony_ci		 */
84262306a36Sopenharmony_ci		mei->csme_taking_ownership = true;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true, true);
84562306a36Sopenharmony_ci	} else {
84662306a36Sopenharmony_ci		iwl_mei_send_sap_msg(cldev,
84762306a36Sopenharmony_ci				     SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED);
84862306a36Sopenharmony_ci		schedule_delayed_work(&mei->ownership_dwork,
84962306a36Sopenharmony_ci				      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic void iwl_mei_handle_nvm(struct mei_cl_device *cldev,
85462306a36Sopenharmony_ci			       const struct iwl_sap_nvm *sap_nvm)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
85762306a36Sopenharmony_ci	const struct iwl_mei_nvm *mei_nvm = (const void *)sap_nvm;
85862306a36Sopenharmony_ci	int i;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	kfree(mei->nvm);
86162306a36Sopenharmony_ci	mei->nvm = kzalloc(sizeof(*mei_nvm), GFP_KERNEL);
86262306a36Sopenharmony_ci	if (!mei->nvm)
86362306a36Sopenharmony_ci		return;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ether_addr_copy(mei->nvm->hw_addr, sap_nvm->hw_addr);
86662306a36Sopenharmony_ci	mei->nvm->n_hw_addrs = sap_nvm->n_hw_addrs;
86762306a36Sopenharmony_ci	mei->nvm->radio_cfg = le32_to_cpu(sap_nvm->radio_cfg);
86862306a36Sopenharmony_ci	mei->nvm->caps = le32_to_cpu(sap_nvm->caps);
86962306a36Sopenharmony_ci	mei->nvm->nvm_version = le32_to_cpu(sap_nvm->nvm_version);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mei->nvm->channels); i++)
87262306a36Sopenharmony_ci		mei->nvm->channels[i] = le32_to_cpu(sap_nvm->channels[i]);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	wake_up_all(&mei->get_nvm_wq);
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev,
87862306a36Sopenharmony_ci					   const struct iwl_sap_msg_dw *dw)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/*
88362306a36Sopenharmony_ci	 * This means that we can't use the wifi device right now, CSME is not
88462306a36Sopenharmony_ci	 * ready to let us use it.
88562306a36Sopenharmony_ci	 */
88662306a36Sopenharmony_ci	if (!dw->val) {
88762306a36Sopenharmony_ci		dev_info(&cldev->dev, "Ownership req denied\n");
88862306a36Sopenharmony_ci		return;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	mei->got_ownership = true;
89262306a36Sopenharmony_ci	wake_up_all(&mei->get_ownership_wq);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	iwl_mei_send_sap_msg(cldev,
89562306a36Sopenharmony_ci			     SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* We can now start the connection, unblock rfkill */
89862306a36Sopenharmony_ci	if (iwl_mei_cache.ops)
89962306a36Sopenharmony_ci		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_cistatic void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev,
90362306a36Sopenharmony_ci				    const struct iwl_sap_pldr_ack_data *ack)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	mei->pldr_active = le32_to_cpu(ack->status) == SAP_PLDR_STATUS_SUCCESS;
90862306a36Sopenharmony_ci	wake_up_all(&mei->pldr_wq);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void iwl_mei_handle_ping(struct mei_cl_device *cldev,
91262306a36Sopenharmony_ci				const struct iwl_sap_hdr *hdr)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	iwl_mei_send_sap_msg(cldev, SAP_MSG_NOTIF_PONG);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic void iwl_mei_handle_sap_msg(struct mei_cl_device *cldev,
91862306a36Sopenharmony_ci				   const struct iwl_sap_hdr *hdr)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	u16 len = le16_to_cpu(hdr->len) + sizeof(*hdr);
92162306a36Sopenharmony_ci	u16 type = le16_to_cpu(hdr->type);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	dev_dbg(&cldev->dev,
92462306a36Sopenharmony_ci		"Got a new SAP message: type %d, len %d, seq %d\n",
92562306a36Sopenharmony_ci		le16_to_cpu(hdr->type), len,
92662306a36Sopenharmony_ci		le32_to_cpu(hdr->seq_num));
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci#define SAP_MSG_HANDLER(_cmd, _handler, _sz)				\
92962306a36Sopenharmony_ci	case SAP_MSG_NOTIF_ ## _cmd:					\
93062306a36Sopenharmony_ci		if (len < _sz) {					\
93162306a36Sopenharmony_ci			dev_err(&cldev->dev,				\
93262306a36Sopenharmony_ci				"Bad size for %d: %u < %u\n",		\
93362306a36Sopenharmony_ci				le16_to_cpu(hdr->type),			\
93462306a36Sopenharmony_ci				(unsigned int)len,			\
93562306a36Sopenharmony_ci				(unsigned int)_sz);			\
93662306a36Sopenharmony_ci			break;						\
93762306a36Sopenharmony_ci		}							\
93862306a36Sopenharmony_ci		mutex_lock(&iwl_mei_mutex);				\
93962306a36Sopenharmony_ci		_handler(cldev, (const void *)hdr);			\
94062306a36Sopenharmony_ci		mutex_unlock(&iwl_mei_mutex);				\
94162306a36Sopenharmony_ci		break
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci#define SAP_MSG_HANDLER_NO_LOCK(_cmd, _handler, _sz)			\
94462306a36Sopenharmony_ci	case SAP_MSG_NOTIF_ ## _cmd:					\
94562306a36Sopenharmony_ci		if (len < _sz) {					\
94662306a36Sopenharmony_ci			dev_err(&cldev->dev,				\
94762306a36Sopenharmony_ci				"Bad size for %d: %u < %u\n",		\
94862306a36Sopenharmony_ci				le16_to_cpu(hdr->type),			\
94962306a36Sopenharmony_ci				(unsigned int)len,			\
95062306a36Sopenharmony_ci				(unsigned int)_sz);			\
95162306a36Sopenharmony_ci			break;						\
95262306a36Sopenharmony_ci		}							\
95362306a36Sopenharmony_ci		_handler(cldev, (const void *)hdr);			\
95462306a36Sopenharmony_ci		break
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci#define SAP_MSG_HANDLER_NO_HANDLER(_cmd, _sz)				\
95762306a36Sopenharmony_ci	case SAP_MSG_NOTIF_ ## _cmd:					\
95862306a36Sopenharmony_ci		if (len < _sz) {					\
95962306a36Sopenharmony_ci			dev_err(&cldev->dev,				\
96062306a36Sopenharmony_ci				"Bad size for %d: %u < %u\n",		\
96162306a36Sopenharmony_ci				le16_to_cpu(hdr->type),			\
96262306a36Sopenharmony_ci				(unsigned int)len,			\
96362306a36Sopenharmony_ci				(unsigned int)_sz);			\
96462306a36Sopenharmony_ci			break;						\
96562306a36Sopenharmony_ci		}							\
96662306a36Sopenharmony_ci		break
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	switch (type) {
96962306a36Sopenharmony_ci	SAP_MSG_HANDLER(PING, iwl_mei_handle_ping, 0);
97062306a36Sopenharmony_ci	SAP_MSG_HANDLER(CSME_FILTERS,
97162306a36Sopenharmony_ci			iwl_mei_handle_csme_filters,
97262306a36Sopenharmony_ci			sizeof(struct iwl_sap_csme_filters));
97362306a36Sopenharmony_ci	SAP_MSG_HANDLER(CSME_CONN_STATUS,
97462306a36Sopenharmony_ci			iwl_mei_handle_conn_status,
97562306a36Sopenharmony_ci			sizeof(struct iwl_sap_notif_conn_status));
97662306a36Sopenharmony_ci	SAP_MSG_HANDLER_NO_LOCK(AMT_STATE,
97762306a36Sopenharmony_ci				iwl_mei_handle_amt_state,
97862306a36Sopenharmony_ci				sizeof(struct iwl_sap_msg_dw));
97962306a36Sopenharmony_ci	SAP_MSG_HANDLER_NO_HANDLER(PONG, 0);
98062306a36Sopenharmony_ci	SAP_MSG_HANDLER(NVM, iwl_mei_handle_nvm,
98162306a36Sopenharmony_ci			sizeof(struct iwl_sap_nvm));
98262306a36Sopenharmony_ci	SAP_MSG_HANDLER(CSME_REPLY_TO_HOST_OWNERSHIP_REQ,
98362306a36Sopenharmony_ci			iwl_mei_handle_rx_host_own_req,
98462306a36Sopenharmony_ci			sizeof(struct iwl_sap_msg_dw));
98562306a36Sopenharmony_ci	SAP_MSG_HANDLER(NIC_OWNER, iwl_mei_handle_nic_owner,
98662306a36Sopenharmony_ci			sizeof(struct iwl_sap_msg_dw));
98762306a36Sopenharmony_ci	SAP_MSG_HANDLER(CSME_CAN_RELEASE_OWNERSHIP,
98862306a36Sopenharmony_ci			iwl_mei_handle_can_release_ownership, 0);
98962306a36Sopenharmony_ci	SAP_MSG_HANDLER(CSME_TAKING_OWNERSHIP,
99062306a36Sopenharmony_ci			iwl_mei_handle_csme_taking_ownership, 0);
99162306a36Sopenharmony_ci	SAP_MSG_HANDLER(PLDR_ACK, iwl_mei_handle_pldr_ack,
99262306a36Sopenharmony_ci			sizeof(struct iwl_sap_pldr_ack_data));
99362306a36Sopenharmony_ci	default:
99462306a36Sopenharmony_ci	/*
99562306a36Sopenharmony_ci	 * This is not really an error, there are message that we decided
99662306a36Sopenharmony_ci	 * to ignore, yet, it is useful to be able to leave a note if debug
99762306a36Sopenharmony_ci	 * is enabled.
99862306a36Sopenharmony_ci	 */
99962306a36Sopenharmony_ci	dev_dbg(&cldev->dev, "Unsupported message: type %d, len %d\n",
100062306a36Sopenharmony_ci		le16_to_cpu(hdr->type), len);
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci#undef SAP_MSG_HANDLER
100462306a36Sopenharmony_ci#undef SAP_MSG_HANDLER_NO_LOCK
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic void iwl_mei_read_from_q(const u8 *q_head, u32 q_sz,
100862306a36Sopenharmony_ci				u32 *_rd, u32 wr,
100962306a36Sopenharmony_ci				void *_buf, u32 len)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	u8 *buf = _buf;
101262306a36Sopenharmony_ci	u32 rd = *_rd;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (rd + len <= q_sz) {
101562306a36Sopenharmony_ci		memcpy(buf, q_head + rd, len);
101662306a36Sopenharmony_ci		rd += len;
101762306a36Sopenharmony_ci	} else {
101862306a36Sopenharmony_ci		memcpy(buf, q_head + rd, q_sz - rd);
101962306a36Sopenharmony_ci		memcpy(buf + q_sz - rd, q_head, len - (q_sz - rd));
102062306a36Sopenharmony_ci		rd = len - (q_sz - rd);
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	*_rd = rd;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci#define QOS_HDR_IV_SNAP_LEN (sizeof(struct ieee80211_qos_hdr) +      \
102762306a36Sopenharmony_ci			     IEEE80211_TKIP_IV_LEN +                 \
102862306a36Sopenharmony_ci			     sizeof(rfc1042_header) + ETH_TLEN)
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic void iwl_mei_handle_sap_data(struct mei_cl_device *cldev,
103162306a36Sopenharmony_ci				    const u8 *q_head, u32 q_sz,
103262306a36Sopenharmony_ci				    u32 rd, u32 wr, ssize_t valid_rx_sz,
103362306a36Sopenharmony_ci				    struct sk_buff_head *tx_skbs)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct iwl_sap_hdr hdr;
103662306a36Sopenharmony_ci	struct net_device *netdev =
103762306a36Sopenharmony_ci		rcu_dereference_protected(iwl_mei_cache.netdev,
103862306a36Sopenharmony_ci					  lockdep_is_held(&iwl_mei_mutex));
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	if (!netdev)
104162306a36Sopenharmony_ci		return;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	while (valid_rx_sz >= sizeof(hdr)) {
104462306a36Sopenharmony_ci		struct ethhdr *ethhdr;
104562306a36Sopenharmony_ci		unsigned char *data;
104662306a36Sopenharmony_ci		struct sk_buff *skb;
104762306a36Sopenharmony_ci		u16 len;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		iwl_mei_read_from_q(q_head, q_sz, &rd, wr, &hdr, sizeof(hdr));
105062306a36Sopenharmony_ci		valid_rx_sz -= sizeof(hdr);
105162306a36Sopenharmony_ci		len = le16_to_cpu(hdr.len);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		if (valid_rx_sz < len) {
105462306a36Sopenharmony_ci			dev_err(&cldev->dev,
105562306a36Sopenharmony_ci				"Data queue is corrupted: valid data len %zd, len %d\n",
105662306a36Sopenharmony_ci				valid_rx_sz, len);
105762306a36Sopenharmony_ci			break;
105862306a36Sopenharmony_ci		}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		if (len < sizeof(*ethhdr)) {
106162306a36Sopenharmony_ci			dev_err(&cldev->dev,
106262306a36Sopenharmony_ci				"Data len is smaller than an ethernet header? len = %d\n",
106362306a36Sopenharmony_ci				len);
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		valid_rx_sz -= len;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		if (le16_to_cpu(hdr.type) != SAP_MSG_DATA_PACKET) {
106962306a36Sopenharmony_ci			dev_err(&cldev->dev, "Unsupported Rx data: type %d, len %d\n",
107062306a36Sopenharmony_ci				le16_to_cpu(hdr.type), len);
107162306a36Sopenharmony_ci			continue;
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		/* We need enough room for the WiFi header + SNAP + IV */
107562306a36Sopenharmony_ci		skb = netdev_alloc_skb(netdev, len + QOS_HDR_IV_SNAP_LEN);
107662306a36Sopenharmony_ci		if (!skb)
107762306a36Sopenharmony_ci			continue;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci		skb_reserve(skb, QOS_HDR_IV_SNAP_LEN);
108062306a36Sopenharmony_ci		ethhdr = skb_push(skb, sizeof(*ethhdr));
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		iwl_mei_read_from_q(q_head, q_sz, &rd, wr,
108362306a36Sopenharmony_ci				    ethhdr, sizeof(*ethhdr));
108462306a36Sopenharmony_ci		len -= sizeof(*ethhdr);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		skb_reset_mac_header(skb);
108762306a36Sopenharmony_ci		skb_reset_network_header(skb);
108862306a36Sopenharmony_ci		skb->protocol = ethhdr->h_proto;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		data = skb_put(skb, len);
109162306a36Sopenharmony_ci		iwl_mei_read_from_q(q_head, q_sz, &rd, wr, data, len);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		/*
109462306a36Sopenharmony_ci		 * Enqueue the skb here so that it can be sent later when we
109562306a36Sopenharmony_ci		 * do not hold the mutex. TX'ing a packet with a mutex held is
109662306a36Sopenharmony_ci		 * possible, but it wouldn't be nice to forbid the TX path to
109762306a36Sopenharmony_ci		 * call any of iwlmei's functions, since every API from iwlmei
109862306a36Sopenharmony_ci		 * needs the mutex.
109962306a36Sopenharmony_ci		 */
110062306a36Sopenharmony_ci		__skb_queue_tail(tx_skbs, skb);
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic void iwl_mei_handle_sap_rx_cmd(struct mei_cl_device *cldev,
110562306a36Sopenharmony_ci				      const u8 *q_head, u32 q_sz,
110662306a36Sopenharmony_ci				      u32 rd, u32 wr, ssize_t valid_rx_sz)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct page *p = alloc_page(GFP_KERNEL);
110962306a36Sopenharmony_ci	struct iwl_sap_hdr *hdr;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (!p)
111262306a36Sopenharmony_ci		return;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	hdr = page_address(p);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	while (valid_rx_sz >= sizeof(*hdr)) {
111762306a36Sopenharmony_ci		u16 len;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr, sizeof(*hdr));
112062306a36Sopenharmony_ci		valid_rx_sz -= sizeof(*hdr);
112162306a36Sopenharmony_ci		len = le16_to_cpu(hdr->len);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		if (valid_rx_sz < len)
112462306a36Sopenharmony_ci			break;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr + 1, len);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		trace_iwlmei_sap_cmd(hdr, false);
112962306a36Sopenharmony_ci		iwl_mei_handle_sap_msg(cldev, hdr);
113062306a36Sopenharmony_ci		valid_rx_sz -= len;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* valid_rx_sz must be 0 now... */
113462306a36Sopenharmony_ci	if (valid_rx_sz)
113562306a36Sopenharmony_ci		dev_err(&cldev->dev,
113662306a36Sopenharmony_ci			"More data in the buffer although we read it all\n");
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	__free_page(p);
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic void iwl_mei_handle_sap_rx(struct mei_cl_device *cldev,
114262306a36Sopenharmony_ci				  struct iwl_sap_q_ctrl_blk *notif_q,
114362306a36Sopenharmony_ci				  const u8 *q_head,
114462306a36Sopenharmony_ci				  struct sk_buff_head *skbs,
114562306a36Sopenharmony_ci				  u32 q_sz)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
114862306a36Sopenharmony_ci	u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
114962306a36Sopenharmony_ci	ssize_t valid_rx_sz;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (rd > q_sz || wr > q_sz) {
115262306a36Sopenharmony_ci		dev_err(&cldev->dev,
115362306a36Sopenharmony_ci			"Pointers are past the buffer limit\n");
115462306a36Sopenharmony_ci		return;
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (rd == wr)
115862306a36Sopenharmony_ci		return;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	valid_rx_sz = wr > rd ? wr - rd : q_sz - rd + wr;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (skbs)
116362306a36Sopenharmony_ci		iwl_mei_handle_sap_data(cldev, q_head, q_sz, rd, wr,
116462306a36Sopenharmony_ci					valid_rx_sz, skbs);
116562306a36Sopenharmony_ci	else
116662306a36Sopenharmony_ci		iwl_mei_handle_sap_rx_cmd(cldev, q_head, q_sz, rd, wr,
116762306a36Sopenharmony_ci					  valid_rx_sz);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* Increment the read pointer to point to the write pointer */
117062306a36Sopenharmony_ci	WRITE_ONCE(notif_q->rd_ptr, cpu_to_le32(wr));
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic void iwl_mei_handle_check_shared_area(struct mei_cl_device *cldev)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
117662306a36Sopenharmony_ci	struct iwl_sap_q_ctrl_blk *notif_q;
117762306a36Sopenharmony_ci	struct sk_buff_head tx_skbs;
117862306a36Sopenharmony_ci	struct iwl_sap_dir *dir;
117962306a36Sopenharmony_ci	void *q_head;
118062306a36Sopenharmony_ci	u32 q_sz;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (!mei->shared_mem.ctrl)
118362306a36Sopenharmony_ci		return;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
118662306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
118762306a36Sopenharmony_ci	q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF];
118862306a36Sopenharmony_ci	q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF];
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/*
119162306a36Sopenharmony_ci	 * Do not hold the mutex here, but rather each and every message
119262306a36Sopenharmony_ci	 * handler takes it.
119362306a36Sopenharmony_ci	 * This allows message handlers to take it at a certain time.
119462306a36Sopenharmony_ci	 */
119562306a36Sopenharmony_ci	iwl_mei_handle_sap_rx(cldev, notif_q, q_head, NULL, q_sz);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
119862306a36Sopenharmony_ci	dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
119962306a36Sopenharmony_ci	notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
120062306a36Sopenharmony_ci	q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA];
120162306a36Sopenharmony_ci	q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA];
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	__skb_queue_head_init(&tx_skbs);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	iwl_mei_handle_sap_rx(cldev, notif_q, q_head, &tx_skbs, q_sz);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (skb_queue_empty(&tx_skbs)) {
120862306a36Sopenharmony_ci		mutex_unlock(&iwl_mei_mutex);
120962306a36Sopenharmony_ci		return;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/*
121362306a36Sopenharmony_ci	 * Take the RCU read lock before we unlock the mutex to make sure that
121462306a36Sopenharmony_ci	 * even if the netdev is replaced by another non-NULL netdev right after
121562306a36Sopenharmony_ci	 * we unlock the mutex, the old netdev will still be valid when we
121662306a36Sopenharmony_ci	 * transmit the frames. We can't allow to replace the netdev here because
121762306a36Sopenharmony_ci	 * the skbs hold a pointer to the netdev.
121862306a36Sopenharmony_ci	 */
121962306a36Sopenharmony_ci	rcu_read_lock();
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (!rcu_access_pointer(iwl_mei_cache.netdev)) {
122462306a36Sopenharmony_ci		dev_err(&cldev->dev, "Can't Tx without a netdev\n");
122562306a36Sopenharmony_ci		skb_queue_purge(&tx_skbs);
122662306a36Sopenharmony_ci		goto out;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	while (!skb_queue_empty(&tx_skbs)) {
123062306a36Sopenharmony_ci		struct sk_buff *skb = __skb_dequeue(&tx_skbs);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_TO_AIR);
123362306a36Sopenharmony_ci		dev_queue_xmit(skb);
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ciout:
123762306a36Sopenharmony_ci	rcu_read_unlock();
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic void iwl_mei_rx(struct mei_cl_device *cldev)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct iwl_sap_me_msg_hdr *hdr;
124362306a36Sopenharmony_ci	u8 msg[100];
124462306a36Sopenharmony_ci	ssize_t ret;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	ret = mei_cldev_recv(cldev, (u8 *)&msg, sizeof(msg));
124762306a36Sopenharmony_ci	if (ret < 0) {
124862306a36Sopenharmony_ci		dev_err(&cldev->dev, "failed to receive data: %zd\n", ret);
124962306a36Sopenharmony_ci		return;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	if (ret == 0) {
125362306a36Sopenharmony_ci		dev_err(&cldev->dev, "got an empty response\n");
125462306a36Sopenharmony_ci		return;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	hdr = (void *)msg;
125862306a36Sopenharmony_ci	trace_iwlmei_me_msg(hdr, false);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	switch (le32_to_cpu(hdr->type)) {
126162306a36Sopenharmony_ci	case SAP_ME_MSG_START_OK:
126262306a36Sopenharmony_ci		BUILD_BUG_ON(sizeof(struct iwl_sap_me_msg_start_ok) >
126362306a36Sopenharmony_ci			     sizeof(msg));
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci		iwl_mei_handle_rx_start_ok(cldev, (void *)msg, ret);
126662306a36Sopenharmony_ci		break;
126762306a36Sopenharmony_ci	case SAP_ME_MSG_CHECK_SHARED_AREA:
126862306a36Sopenharmony_ci		iwl_mei_handle_check_shared_area(cldev);
126962306a36Sopenharmony_ci		break;
127062306a36Sopenharmony_ci	default:
127162306a36Sopenharmony_ci		dev_err(&cldev->dev, "got a RX notification: %d\n",
127262306a36Sopenharmony_ci			le32_to_cpu(hdr->type));
127362306a36Sopenharmony_ci		break;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int iwl_mei_send_start(struct mei_cl_device *cldev)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
128062306a36Sopenharmony_ci	struct iwl_sap_me_msg_start msg = {
128162306a36Sopenharmony_ci		.hdr.type = cpu_to_le32(SAP_ME_MSG_START),
128262306a36Sopenharmony_ci		.hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)),
128362306a36Sopenharmony_ci		.hdr.len = cpu_to_le32(sizeof(msg)),
128462306a36Sopenharmony_ci		.supported_versions[0] = SAP_VERSION,
128562306a36Sopenharmony_ci		.init_data_seq_num = cpu_to_le16(0x100),
128662306a36Sopenharmony_ci		.init_notif_seq_num = cpu_to_le16(0x800),
128762306a36Sopenharmony_ci	};
128862306a36Sopenharmony_ci	int ret;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	trace_iwlmei_me_msg(&msg.hdr, true);
129162306a36Sopenharmony_ci	ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg));
129262306a36Sopenharmony_ci	if (ret != sizeof(msg)) {
129362306a36Sopenharmony_ci		dev_err(&cldev->dev,
129462306a36Sopenharmony_ci			"failed to send the SAP_ME_MSG_START message %d\n",
129562306a36Sopenharmony_ci			ret);
129662306a36Sopenharmony_ci		return ret;
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	return 0;
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic int iwl_mei_enable(struct mei_cl_device *cldev)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	int ret;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	ret = mei_cldev_enable(cldev);
130762306a36Sopenharmony_ci	if (ret < 0) {
130862306a36Sopenharmony_ci		dev_err(&cldev->dev, "failed to enable the device: %d\n", ret);
130962306a36Sopenharmony_ci		return ret;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	ret = mei_cldev_register_rx_cb(cldev, iwl_mei_rx);
131362306a36Sopenharmony_ci	if (ret) {
131462306a36Sopenharmony_ci		dev_err(&cldev->dev,
131562306a36Sopenharmony_ci			"failed to register to the rx cb: %d\n", ret);
131662306a36Sopenharmony_ci		mei_cldev_disable(cldev);
131762306a36Sopenharmony_ci		return ret;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistruct iwl_mei_nvm *iwl_mei_get_nvm(void)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct iwl_mei_nvm *nvm = NULL;
132662306a36Sopenharmony_ci	struct iwl_mei *mei;
132762306a36Sopenharmony_ci	int ret;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
133262306a36Sopenharmony_ci		goto out;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (!mei)
133762306a36Sopenharmony_ci		goto out;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	ret = iwl_mei_send_sap_msg(iwl_mei_global_cldev,
134062306a36Sopenharmony_ci				   SAP_MSG_NOTIF_GET_NVM);
134162306a36Sopenharmony_ci	if (ret)
134262306a36Sopenharmony_ci		goto out;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	ret = wait_event_timeout(mei->get_nvm_wq, mei->nvm, 2 * HZ);
134762306a36Sopenharmony_ci	if (!ret)
134862306a36Sopenharmony_ci		return NULL;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
135362306a36Sopenharmony_ci		goto out;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (!mei)
135862306a36Sopenharmony_ci		goto out;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (mei->nvm)
136162306a36Sopenharmony_ci		nvm = kmemdup(mei->nvm, sizeof(*mei->nvm), GFP_KERNEL);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ciout:
136462306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
136562306a36Sopenharmony_ci	return nvm;
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_get_nvm);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci#define IWL_MEI_PLDR_NUM_RETRIES	3
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ciint iwl_mei_pldr_req(void)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	struct iwl_mei *mei;
137462306a36Sopenharmony_ci	int ret;
137562306a36Sopenharmony_ci	struct iwl_sap_pldr_data msg = {
137662306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR),
137762306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
137862306a36Sopenharmony_ci	};
137962306a36Sopenharmony_ci	int i;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	/* In case we didn't have a bind */
138462306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
138562306a36Sopenharmony_ci		ret = 0;
138662306a36Sopenharmony_ci		goto out;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	if (!mei) {
139262306a36Sopenharmony_ci		ret = -ENODEV;
139362306a36Sopenharmony_ci		goto out;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if (!mei->amt_enabled) {
139762306a36Sopenharmony_ci		ret = 0;
139862306a36Sopenharmony_ci		goto out;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	for (i = 0; i < IWL_MEI_PLDR_NUM_RETRIES; i++) {
140262306a36Sopenharmony_ci		ret = iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
140362306a36Sopenharmony_ci		mutex_unlock(&iwl_mei_mutex);
140462306a36Sopenharmony_ci		if (ret)
140562306a36Sopenharmony_ci			return ret;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		ret = wait_event_timeout(mei->pldr_wq, mei->pldr_active, HZ / 2);
140862306a36Sopenharmony_ci		if (ret)
140962306a36Sopenharmony_ci			break;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		/* Take the mutex for the next iteration */
141262306a36Sopenharmony_ci		mutex_lock(&iwl_mei_mutex);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	if (ret)
141662306a36Sopenharmony_ci		return 0;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	ret = -ETIMEDOUT;
141962306a36Sopenharmony_ciout:
142062306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
142162306a36Sopenharmony_ci	return ret;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_pldr_req);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ciint iwl_mei_get_ownership(void)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct iwl_mei *mei;
142862306a36Sopenharmony_ci	int ret;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* In case we didn't have a bind */
143362306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
143462306a36Sopenharmony_ci		ret = 0;
143562306a36Sopenharmony_ci		goto out;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	if (!mei) {
144162306a36Sopenharmony_ci		ret = -ENODEV;
144262306a36Sopenharmony_ci		goto out;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	if (!mei->amt_enabled) {
144662306a36Sopenharmony_ci		ret = 0;
144762306a36Sopenharmony_ci		goto out;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (mei->got_ownership) {
145162306a36Sopenharmony_ci		ret = 0;
145262306a36Sopenharmony_ci		goto out;
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	ret = iwl_mei_send_sap_msg(mei->cldev,
145662306a36Sopenharmony_ci				   SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP);
145762306a36Sopenharmony_ci	if (ret)
145862306a36Sopenharmony_ci		goto out;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	ret = wait_event_timeout(mei->get_ownership_wq,
146362306a36Sopenharmony_ci				 mei->got_ownership, HZ / 2);
146462306a36Sopenharmony_ci	if (!ret) {
146562306a36Sopenharmony_ci		schedule_delayed_work(&mei->ownership_dwork,
146662306a36Sopenharmony_ci				      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
146762306a36Sopenharmony_ci		return -ETIMEDOUT;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	return 0;
147162306a36Sopenharmony_ciout:
147262306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
147362306a36Sopenharmony_ci	return ret;
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_get_ownership);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_civoid iwl_mei_alive_notif(bool success)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct iwl_mei *mei;
148062306a36Sopenharmony_ci	struct iwl_sap_pldr_end_data msg = {
148162306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR_END),
148262306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
148362306a36Sopenharmony_ci		.status = success ? cpu_to_le32(SAP_PLDR_STATUS_SUCCESS) :
148462306a36Sopenharmony_ci			cpu_to_le32(SAP_PLDR_STATUS_FAILURE),
148562306a36Sopenharmony_ci	};
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
149062306a36Sopenharmony_ci		goto out;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
149362306a36Sopenharmony_ci	if (!mei || !mei->pldr_active)
149462306a36Sopenharmony_ci		goto out;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	mei->pldr_active = false;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
149962306a36Sopenharmony_ciout:
150062306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_alive_notif);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_civoid iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info,
150562306a36Sopenharmony_ci			     const struct iwl_mei_colloc_info *colloc_info)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	struct iwl_sap_notif_host_link_up msg = {
150862306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP),
150962306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
151062306a36Sopenharmony_ci		.conn_info = {
151162306a36Sopenharmony_ci			.ssid_len = cpu_to_le32(conn_info->ssid_len),
151262306a36Sopenharmony_ci			.channel = conn_info->channel,
151362306a36Sopenharmony_ci			.band = conn_info->band,
151462306a36Sopenharmony_ci			.pairwise_cipher = cpu_to_le32(conn_info->pairwise_cipher),
151562306a36Sopenharmony_ci			.auth_mode = cpu_to_le32(conn_info->auth_mode),
151662306a36Sopenharmony_ci		},
151762306a36Sopenharmony_ci	};
151862306a36Sopenharmony_ci	struct iwl_mei *mei;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (conn_info->ssid_len > ARRAY_SIZE(msg.conn_info.ssid))
152162306a36Sopenharmony_ci		return;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	memcpy(msg.conn_info.ssid, conn_info->ssid, conn_info->ssid_len);
152462306a36Sopenharmony_ci	memcpy(msg.conn_info.bssid, conn_info->bssid, ETH_ALEN);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (colloc_info) {
152762306a36Sopenharmony_ci		msg.colloc_channel = colloc_info->channel;
152862306a36Sopenharmony_ci		msg.colloc_band = colloc_info->channel <= 14 ? 0 : 1;
152962306a36Sopenharmony_ci		memcpy(msg.colloc_bssid, colloc_info->bssid, ETH_ALEN);
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
153562306a36Sopenharmony_ci		goto out;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
154062306a36Sopenharmony_ci		goto out;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ciout:
154562306a36Sopenharmony_ci	kfree(iwl_mei_cache.conn_info);
154662306a36Sopenharmony_ci	iwl_mei_cache.conn_info =
154762306a36Sopenharmony_ci		kmemdup(&msg.conn_info, sizeof(msg.conn_info), GFP_KERNEL);
154862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_host_associated);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_civoid iwl_mei_host_disassociated(void)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct iwl_mei *mei;
155562306a36Sopenharmony_ci	struct iwl_sap_notif_host_link_down msg = {
155662306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_DOWN),
155762306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
155862306a36Sopenharmony_ci		.type = HOST_LINK_DOWN_TYPE_TEMPORARY,
155962306a36Sopenharmony_ci	};
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
156462306a36Sopenharmony_ci		goto out;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
156962306a36Sopenharmony_ci		goto out;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ciout:
157462306a36Sopenharmony_ci	kfree(iwl_mei_cache.conn_info);
157562306a36Sopenharmony_ci	iwl_mei_cache.conn_info = NULL;
157662306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_host_disassociated);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_civoid iwl_mei_set_rfkill_state(bool hw_rfkill, bool sw_rfkill)
158162306a36Sopenharmony_ci{
158262306a36Sopenharmony_ci	struct iwl_mei *mei;
158362306a36Sopenharmony_ci	u32 rfkill_state = 0;
158462306a36Sopenharmony_ci	struct iwl_sap_msg_dw msg = {
158562306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE),
158662306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
158762306a36Sopenharmony_ci	};
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	if (!sw_rfkill)
159062306a36Sopenharmony_ci		rfkill_state |= SAP_SW_RFKILL_DEASSERTED;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (!hw_rfkill)
159362306a36Sopenharmony_ci		rfkill_state |= SAP_HW_RFKILL_DEASSERTED;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
159862306a36Sopenharmony_ci		goto out;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	msg.val = cpu_to_le32(rfkill_state);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
160562306a36Sopenharmony_ci		goto out;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ciout:
161062306a36Sopenharmony_ci	iwl_mei_cache.rf_kill = rfkill_state;
161162306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_rfkill_state);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_civoid iwl_mei_set_nic_info(const u8 *mac_address, const u8 *nvm_address)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	struct iwl_mei *mei;
161862306a36Sopenharmony_ci	struct iwl_sap_notif_host_nic_info msg = {
161962306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO),
162062306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
162162306a36Sopenharmony_ci	};
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
162662306a36Sopenharmony_ci		goto out;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	ether_addr_copy(msg.mac_address, mac_address);
162962306a36Sopenharmony_ci	ether_addr_copy(msg.nvm_address, nvm_address);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
163462306a36Sopenharmony_ci		goto out;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ciout:
163962306a36Sopenharmony_ci	ether_addr_copy(iwl_mei_cache.mac_address, mac_address);
164062306a36Sopenharmony_ci	ether_addr_copy(iwl_mei_cache.nvm_address, nvm_address);
164162306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_nic_info);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_civoid iwl_mei_set_country_code(u16 mcc)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	struct iwl_mei *mei;
164862306a36Sopenharmony_ci	struct iwl_sap_notif_country_code msg = {
164962306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE),
165062306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
165162306a36Sopenharmony_ci		.mcc = cpu_to_le16(mcc),
165262306a36Sopenharmony_ci	};
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
165762306a36Sopenharmony_ci		goto out;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
166262306a36Sopenharmony_ci		goto out;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ciout:
166762306a36Sopenharmony_ci	iwl_mei_cache.mcc = mcc;
166862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
166962306a36Sopenharmony_ci}
167062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_country_code);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_civoid iwl_mei_set_power_limit(const __le16 *power_limit)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct iwl_mei *mei;
167562306a36Sopenharmony_ci	struct iwl_sap_notif_sar_limits msg = {
167662306a36Sopenharmony_ci		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS),
167762306a36Sopenharmony_ci		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
167862306a36Sopenharmony_ci	};
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
168362306a36Sopenharmony_ci		goto out;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	if (!mei || !mei->amt_enabled)
168862306a36Sopenharmony_ci		goto out;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	memcpy(msg.sar_chain_info_table, power_limit, sizeof(msg.sar_chain_info_table));
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ciout:
169562306a36Sopenharmony_ci	kfree(iwl_mei_cache.power_limit);
169662306a36Sopenharmony_ci	iwl_mei_cache.power_limit = kmemdup(power_limit,
169762306a36Sopenharmony_ci					    sizeof(msg.sar_chain_info_table), GFP_KERNEL);
169862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
169962306a36Sopenharmony_ci}
170062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_power_limit);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_civoid iwl_mei_set_netdev(struct net_device *netdev)
170362306a36Sopenharmony_ci{
170462306a36Sopenharmony_ci	struct iwl_mei *mei;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	if (!iwl_mei_is_connected()) {
170962306a36Sopenharmony_ci		rcu_assign_pointer(iwl_mei_cache.netdev, netdev);
171062306a36Sopenharmony_ci		goto out;
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (!mei)
171662306a36Sopenharmony_ci		goto out;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (!netdev) {
171962306a36Sopenharmony_ci		struct net_device *dev =
172062306a36Sopenharmony_ci			rcu_dereference_protected(iwl_mei_cache.netdev,
172162306a36Sopenharmony_ci						  lockdep_is_held(&iwl_mei_mutex));
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci		if (!dev)
172462306a36Sopenharmony_ci			goto out;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		netdev_rx_handler_unregister(dev);
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	rcu_assign_pointer(iwl_mei_cache.netdev, netdev);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (netdev && mei->amt_enabled)
173262306a36Sopenharmony_ci		netdev_rx_handler_register(netdev, iwl_mei_rx_handler, mei);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ciout:
173562306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_netdev);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_civoid iwl_mei_device_state(bool up)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	struct iwl_mei *mei;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (!iwl_mei_is_connected())
174662306a36Sopenharmony_ci		goto out;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (!mei)
175162306a36Sopenharmony_ci		goto out;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	mei->device_down = !up;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (up || !mei->csme_taking_ownership)
175662306a36Sopenharmony_ci		goto out;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	iwl_mei_send_sap_msg(mei->cldev,
175962306a36Sopenharmony_ci			     SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED);
176062306a36Sopenharmony_ci	mei->csme_taking_ownership = false;
176162306a36Sopenharmony_ci	schedule_delayed_work(&mei->ownership_dwork,
176262306a36Sopenharmony_ci			      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
176362306a36Sopenharmony_ciout:
176462306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_device_state);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ciint iwl_mei_register(void *priv, const struct iwl_mei_ops *ops)
176962306a36Sopenharmony_ci{
177062306a36Sopenharmony_ci	int ret;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	/*
177362306a36Sopenharmony_ci	 * We must have a non-NULL priv pointer to not crash when there are
177462306a36Sopenharmony_ci	 * multiple WiFi devices.
177562306a36Sopenharmony_ci	 */
177662306a36Sopenharmony_ci	if (!priv)
177762306a36Sopenharmony_ci		return -EINVAL;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	/* do not allow registration if someone else already registered */
178262306a36Sopenharmony_ci	if (iwl_mei_cache.priv || iwl_mei_cache.ops) {
178362306a36Sopenharmony_ci		ret = -EBUSY;
178462306a36Sopenharmony_ci		goto out;
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	iwl_mei_cache.priv = priv;
178862306a36Sopenharmony_ci	iwl_mei_cache.ops = ops;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (iwl_mei_global_cldev) {
179162306a36Sopenharmony_ci		struct iwl_mei *mei =
179262306a36Sopenharmony_ci			mei_cldev_get_drvdata(iwl_mei_global_cldev);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci		/* we have already a SAP connection */
179562306a36Sopenharmony_ci		if (iwl_mei_is_connected()) {
179662306a36Sopenharmony_ci			if (mei->amt_enabled)
179762306a36Sopenharmony_ci				iwl_mei_send_sap_msg(mei->cldev,
179862306a36Sopenharmony_ci						     SAP_MSG_NOTIF_WIFIDR_UP);
179962306a36Sopenharmony_ci			ops->rfkill(priv, mei->link_prot_state, false);
180062306a36Sopenharmony_ci		}
180162306a36Sopenharmony_ci	}
180262306a36Sopenharmony_ci	ret = 0;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ciout:
180562306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
180662306a36Sopenharmony_ci	return ret;
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_register);
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_civoid iwl_mei_start_unregister(void)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	/* At this point, the wifi driver should have removed the netdev */
181562306a36Sopenharmony_ci	if (rcu_access_pointer(iwl_mei_cache.netdev))
181662306a36Sopenharmony_ci		pr_err("Still had a netdev pointer set upon unregister\n");
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	kfree(iwl_mei_cache.conn_info);
181962306a36Sopenharmony_ci	iwl_mei_cache.conn_info = NULL;
182062306a36Sopenharmony_ci	kfree(iwl_mei_cache.power_limit);
182162306a36Sopenharmony_ci	iwl_mei_cache.power_limit = NULL;
182262306a36Sopenharmony_ci	iwl_mei_cache.ops = NULL;
182362306a36Sopenharmony_ci	/* leave iwl_mei_cache.priv non-NULL to prevent any new registration */
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_start_unregister);
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_civoid iwl_mei_unregister_complete(void)
183062306a36Sopenharmony_ci{
183162306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	iwl_mei_cache.priv = NULL;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	if (iwl_mei_global_cldev) {
183662306a36Sopenharmony_ci		struct iwl_mei *mei =
183762306a36Sopenharmony_ci			mei_cldev_get_drvdata(iwl_mei_global_cldev);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		if (mei->amt_enabled)
184062306a36Sopenharmony_ci			iwl_mei_send_sap_msg(mei->cldev,
184162306a36Sopenharmony_ci					     SAP_MSG_NOTIF_WIFIDR_DOWN);
184262306a36Sopenharmony_ci		mei->got_ownership = false;
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_unregister_complete);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DEBUG_FS)
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_cistatic ssize_t
185262306a36Sopenharmony_ciiwl_mei_dbgfs_send_start_message_write(struct file *file,
185362306a36Sopenharmony_ci				       const char __user *user_buf,
185462306a36Sopenharmony_ci				       size_t count, loff_t *ppos)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	int ret;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	if (!iwl_mei_global_cldev) {
186162306a36Sopenharmony_ci		ret = -ENODEV;
186262306a36Sopenharmony_ci		goto out;
186362306a36Sopenharmony_ci	}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	ret = iwl_mei_send_start(iwl_mei_global_cldev);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ciout:
186862306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
186962306a36Sopenharmony_ci	return ret ?: count;
187062306a36Sopenharmony_ci}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_cistatic const struct file_operations iwl_mei_dbgfs_send_start_message_ops = {
187362306a36Sopenharmony_ci	.write = iwl_mei_dbgfs_send_start_message_write,
187462306a36Sopenharmony_ci	.open = simple_open,
187562306a36Sopenharmony_ci	.llseek = default_llseek,
187662306a36Sopenharmony_ci};
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic ssize_t iwl_mei_dbgfs_req_ownership_write(struct file *file,
187962306a36Sopenharmony_ci						 const char __user *user_buf,
188062306a36Sopenharmony_ci						 size_t count, loff_t *ppos)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	iwl_mei_get_ownership();
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	return count;
188562306a36Sopenharmony_ci}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_cistatic const struct file_operations iwl_mei_dbgfs_req_ownership_ops = {
188862306a36Sopenharmony_ci	.write = iwl_mei_dbgfs_req_ownership_write,
188962306a36Sopenharmony_ci	.open = simple_open,
189062306a36Sopenharmony_ci	.llseek = default_llseek,
189162306a36Sopenharmony_ci};
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_cistatic void iwl_mei_dbgfs_register(struct iwl_mei *mei)
189462306a36Sopenharmony_ci{
189562306a36Sopenharmony_ci	mei->dbgfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	if (!mei->dbgfs_dir)
189862306a36Sopenharmony_ci		return;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	debugfs_create_ulong("status", S_IRUSR,
190162306a36Sopenharmony_ci			     mei->dbgfs_dir, &iwl_mei_status);
190262306a36Sopenharmony_ci	debugfs_create_file("send_start_message", S_IWUSR, mei->dbgfs_dir,
190362306a36Sopenharmony_ci			    mei, &iwl_mei_dbgfs_send_start_message_ops);
190462306a36Sopenharmony_ci	debugfs_create_file("req_ownership", S_IWUSR, mei->dbgfs_dir,
190562306a36Sopenharmony_ci			    mei, &iwl_mei_dbgfs_req_ownership_ops);
190662306a36Sopenharmony_ci}
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cistatic void iwl_mei_dbgfs_unregister(struct iwl_mei *mei)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	debugfs_remove_recursive(mei->dbgfs_dir);
191162306a36Sopenharmony_ci	mei->dbgfs_dir = NULL;
191262306a36Sopenharmony_ci}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci#else
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic void iwl_mei_dbgfs_register(struct iwl_mei *mei) {}
191762306a36Sopenharmony_cistatic void iwl_mei_dbgfs_unregister(struct iwl_mei *mei) {}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_cistatic void iwl_mei_ownership_dwork(struct work_struct *wk)
192262306a36Sopenharmony_ci{
192362306a36Sopenharmony_ci	iwl_mei_get_ownership();
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci#define ALLOC_SHARED_MEM_RETRY_MAX_NUM	3
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci/*
192962306a36Sopenharmony_ci * iwl_mei_probe - the probe function called by the mei bus enumeration
193062306a36Sopenharmony_ci *
193162306a36Sopenharmony_ci * This allocates the data needed by iwlmei and sets a pointer to this data
193262306a36Sopenharmony_ci * into the mei_cl_device's drvdata.
193362306a36Sopenharmony_ci * It starts the SAP protocol by sending the SAP_ME_MSG_START without
193462306a36Sopenharmony_ci * waiting for the answer. The answer will be caught later by the Rx callback.
193562306a36Sopenharmony_ci */
193662306a36Sopenharmony_cistatic int iwl_mei_probe(struct mei_cl_device *cldev,
193762306a36Sopenharmony_ci			 const struct mei_cl_device_id *id)
193862306a36Sopenharmony_ci{
193962306a36Sopenharmony_ci	int alloc_retry = ALLOC_SHARED_MEM_RETRY_MAX_NUM;
194062306a36Sopenharmony_ci	struct iwl_mei *mei;
194162306a36Sopenharmony_ci	int ret;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	mei = devm_kzalloc(&cldev->dev, sizeof(*mei), GFP_KERNEL);
194462306a36Sopenharmony_ci	if (!mei)
194562306a36Sopenharmony_ci		return -ENOMEM;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	init_waitqueue_head(&mei->get_nvm_wq);
194862306a36Sopenharmony_ci	INIT_WORK(&mei->send_csa_msg_wk, iwl_mei_send_csa_msg_wk);
194962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mei->csa_throttle_end_wk,
195062306a36Sopenharmony_ci			  iwl_mei_csa_throttle_end_wk);
195162306a36Sopenharmony_ci	init_waitqueue_head(&mei->get_ownership_wq);
195262306a36Sopenharmony_ci	init_waitqueue_head(&mei->pldr_wq);
195362306a36Sopenharmony_ci	spin_lock_init(&mei->data_q_lock);
195462306a36Sopenharmony_ci	INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work);
195562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mei->ownership_dwork, iwl_mei_ownership_dwork);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	mei_cldev_set_drvdata(cldev, mei);
195862306a36Sopenharmony_ci	mei->cldev = cldev;
195962306a36Sopenharmony_ci	mei->device_down = true;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	do {
196262306a36Sopenharmony_ci		ret = iwl_mei_alloc_shared_mem(cldev);
196362306a36Sopenharmony_ci		if (!ret)
196462306a36Sopenharmony_ci			break;
196562306a36Sopenharmony_ci		/*
196662306a36Sopenharmony_ci		 * The CSME firmware needs to boot the internal WLAN client.
196762306a36Sopenharmony_ci		 * This can take time in certain configurations (usually
196862306a36Sopenharmony_ci		 * upon resume and when the whole CSME firmware is shut down
196962306a36Sopenharmony_ci		 * during suspend).
197062306a36Sopenharmony_ci		 *
197162306a36Sopenharmony_ci		 * Wait a bit before retrying and hope we'll succeed next time.
197262306a36Sopenharmony_ci		 */
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci		dev_dbg(&cldev->dev,
197562306a36Sopenharmony_ci			"Couldn't allocate the shared memory: %d, attempt %d / %d\n",
197662306a36Sopenharmony_ci			ret, alloc_retry, ALLOC_SHARED_MEM_RETRY_MAX_NUM);
197762306a36Sopenharmony_ci		msleep(100);
197862306a36Sopenharmony_ci		alloc_retry--;
197962306a36Sopenharmony_ci	} while (alloc_retry);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	if (ret) {
198262306a36Sopenharmony_ci		dev_err(&cldev->dev, "Couldn't allocate the shared memory: %d\n",
198362306a36Sopenharmony_ci			ret);
198462306a36Sopenharmony_ci		goto free;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	iwl_mei_init_shared_mem(mei);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	ret = iwl_mei_enable(cldev);
199062306a36Sopenharmony_ci	if (ret)
199162306a36Sopenharmony_ci		goto free_shared_mem;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	iwl_mei_dbgfs_register(mei);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	/*
199662306a36Sopenharmony_ci	 * We now have a Rx function in place, start the SAP protocol
199762306a36Sopenharmony_ci	 * we expect to get the SAP_ME_MSG_START_OK response later on.
199862306a36Sopenharmony_ci	 */
199962306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
200062306a36Sopenharmony_ci	ret = iwl_mei_send_start(cldev);
200162306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
200262306a36Sopenharmony_ci	if (ret)
200362306a36Sopenharmony_ci		goto debugfs_unregister;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* must be last */
200662306a36Sopenharmony_ci	iwl_mei_global_cldev = cldev;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	return 0;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_cidebugfs_unregister:
201162306a36Sopenharmony_ci	iwl_mei_dbgfs_unregister(mei);
201262306a36Sopenharmony_ci	mei_cldev_disable(cldev);
201362306a36Sopenharmony_cifree_shared_mem:
201462306a36Sopenharmony_ci	iwl_mei_free_shared_mem(cldev);
201562306a36Sopenharmony_cifree:
201662306a36Sopenharmony_ci	mei_cldev_set_drvdata(cldev, NULL);
201762306a36Sopenharmony_ci	devm_kfree(&cldev->dev, mei);
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	return ret;
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci#define SEND_SAP_MAX_WAIT_ITERATION 10
202362306a36Sopenharmony_ci#define IWLMEI_DEVICE_DOWN_WAIT_ITERATION 50
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cistatic void iwl_mei_remove(struct mei_cl_device *cldev)
202662306a36Sopenharmony_ci{
202762306a36Sopenharmony_ci	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
202862306a36Sopenharmony_ci	int i;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	/*
203162306a36Sopenharmony_ci	 * We are being removed while the bus is active, it means we are
203262306a36Sopenharmony_ci	 * going to suspend/ shutdown, so the NIC will disappear.
203362306a36Sopenharmony_ci	 */
203462306a36Sopenharmony_ci	if (mei_cldev_enabled(cldev) && iwl_mei_cache.ops) {
203562306a36Sopenharmony_ci		unsigned int iter = IWLMEI_DEVICE_DOWN_WAIT_ITERATION;
203662306a36Sopenharmony_ci		bool down = false;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci		/*
203962306a36Sopenharmony_ci		 * In case of suspend, wait for the mac to stop and don't remove
204062306a36Sopenharmony_ci		 * the interface. This will allow the interface to come back
204162306a36Sopenharmony_ci		 * on resume.
204262306a36Sopenharmony_ci		 */
204362306a36Sopenharmony_ci		while (!down && iter--) {
204462306a36Sopenharmony_ci			mdelay(1);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci			mutex_lock(&iwl_mei_mutex);
204762306a36Sopenharmony_ci			down = mei->device_down;
204862306a36Sopenharmony_ci			mutex_unlock(&iwl_mei_mutex);
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci		if (!down)
205262306a36Sopenharmony_ci			iwl_mei_cache.ops->nic_stolen(iwl_mei_cache.priv);
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (rcu_access_pointer(iwl_mei_cache.netdev)) {
205662306a36Sopenharmony_ci		struct net_device *dev;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci		/*
205962306a36Sopenharmony_ci		 * First take rtnl and only then the mutex to avoid an ABBA
206062306a36Sopenharmony_ci		 * with iwl_mei_set_netdev()
206162306a36Sopenharmony_ci		 */
206262306a36Sopenharmony_ci		rtnl_lock();
206362306a36Sopenharmony_ci		mutex_lock(&iwl_mei_mutex);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		/*
206662306a36Sopenharmony_ci		 * If we are suspending and the wifi driver hasn't removed it's netdev
206762306a36Sopenharmony_ci		 * yet, do it now. In any case, don't change the cache.netdev pointer.
206862306a36Sopenharmony_ci		 */
206962306a36Sopenharmony_ci		dev = rcu_dereference_protected(iwl_mei_cache.netdev,
207062306a36Sopenharmony_ci						lockdep_is_held(&iwl_mei_mutex));
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci		netdev_rx_handler_unregister(dev);
207362306a36Sopenharmony_ci		mutex_unlock(&iwl_mei_mutex);
207462306a36Sopenharmony_ci		rtnl_unlock();
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	/* Tell CSME that we are going down so that it won't access the
208062306a36Sopenharmony_ci	 * memory anymore, make sure this message goes through immediately.
208162306a36Sopenharmony_ci	 */
208262306a36Sopenharmony_ci	mei->csa_throttled = false;
208362306a36Sopenharmony_ci	iwl_mei_send_sap_msg(mei->cldev,
208462306a36Sopenharmony_ci			     SAP_MSG_NOTIF_HOST_GOES_DOWN);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	for (i = 0; i < SEND_SAP_MAX_WAIT_ITERATION; i++) {
208762306a36Sopenharmony_ci		if (!iwl_mei_host_to_me_data_pending(mei))
208862306a36Sopenharmony_ci			break;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci		msleep(20);
209162306a36Sopenharmony_ci	}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	/* If we couldn't make sure that CSME saw the HOST_GOES_DOWN
209462306a36Sopenharmony_ci	 * message, it means that it will probably keep reading memory
209562306a36Sopenharmony_ci	 * that we are going to unmap and free, expect IOMMU error
209662306a36Sopenharmony_ci	 * messages.
209762306a36Sopenharmony_ci	 */
209862306a36Sopenharmony_ci	if (i == SEND_SAP_MAX_WAIT_ITERATION)
209962306a36Sopenharmony_ci		dev_err(&mei->cldev->dev,
210062306a36Sopenharmony_ci			"Couldn't get ACK from CSME on HOST_GOES_DOWN message\n");
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/*
210562306a36Sopenharmony_ci	 * This looks strange, but this lock is taken here to make sure that
210662306a36Sopenharmony_ci	 * iwl_mei_add_data_to_ring called from the Tx path sees that we
210762306a36Sopenharmony_ci	 * clear the IWL_MEI_STATUS_SAP_CONNECTED bit.
210862306a36Sopenharmony_ci	 * Rx isn't a problem because the rx_handler can't be called after
210962306a36Sopenharmony_ci	 * having been unregistered.
211062306a36Sopenharmony_ci	 */
211162306a36Sopenharmony_ci	spin_lock_bh(&mei->data_q_lock);
211262306a36Sopenharmony_ci	clear_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
211362306a36Sopenharmony_ci	spin_unlock_bh(&mei->data_q_lock);
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	if (iwl_mei_cache.ops)
211662306a36Sopenharmony_ci		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	/*
211962306a36Sopenharmony_ci	 * mei_cldev_disable will return only after all the MEI Rx is done.
212062306a36Sopenharmony_ci	 * It must be called when iwl_mei_mutex is *not* held, since it waits
212162306a36Sopenharmony_ci	 * for our Rx handler to complete.
212262306a36Sopenharmony_ci	 * After it returns, no new Rx will start.
212362306a36Sopenharmony_ci	 */
212462306a36Sopenharmony_ci	mei_cldev_disable(cldev);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	/*
212762306a36Sopenharmony_ci	 * Since the netdev was already removed and the netdev's removal
212862306a36Sopenharmony_ci	 * includes a call to synchronize_net() so that we know there won't be
212962306a36Sopenharmony_ci	 * any new Rx that will trigger the following workers.
213062306a36Sopenharmony_ci	 */
213162306a36Sopenharmony_ci	cancel_work_sync(&mei->send_csa_msg_wk);
213262306a36Sopenharmony_ci	cancel_delayed_work_sync(&mei->csa_throttle_end_wk);
213362306a36Sopenharmony_ci	cancel_work_sync(&mei->netdev_work);
213462306a36Sopenharmony_ci	cancel_delayed_work_sync(&mei->ownership_dwork);
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	/*
213762306a36Sopenharmony_ci	 * If someone waits for the ownership, let him know that we are going
213862306a36Sopenharmony_ci	 * down and that we are not connected anymore. He'll be able to take
213962306a36Sopenharmony_ci	 * the device.
214062306a36Sopenharmony_ci	 */
214162306a36Sopenharmony_ci	wake_up_all(&mei->get_ownership_wq);
214262306a36Sopenharmony_ci	wake_up_all(&mei->pldr_wq);
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	mutex_lock(&iwl_mei_mutex);
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	iwl_mei_global_cldev = NULL;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	wake_up_all(&mei->get_nvm_wq);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	iwl_mei_free_shared_mem(cldev);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	iwl_mei_dbgfs_unregister(mei);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	mei_cldev_set_drvdata(cldev, NULL);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	kfree(mei->nvm);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	kfree(rcu_access_pointer(mei->filters));
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	devm_kfree(&cldev->dev, mei);
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	mutex_unlock(&iwl_mei_mutex);
216362306a36Sopenharmony_ci}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_cistatic const struct mei_cl_device_id iwl_mei_tbl[] = {
216662306a36Sopenharmony_ci	{
216762306a36Sopenharmony_ci		.name = KBUILD_MODNAME,
216862306a36Sopenharmony_ci		.uuid = MEI_WLAN_UUID,
216962306a36Sopenharmony_ci		.version = MEI_CL_VERSION_ANY,
217062306a36Sopenharmony_ci	},
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	/* required last entry */
217362306a36Sopenharmony_ci	{ }
217462306a36Sopenharmony_ci};
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci/*
217762306a36Sopenharmony_ci * Do not export the device table because this module is loaded by
217862306a36Sopenharmony_ci * iwlwifi's dependency.
217962306a36Sopenharmony_ci */
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic struct mei_cl_driver iwl_mei_cl_driver = {
218262306a36Sopenharmony_ci	.id_table = iwl_mei_tbl,
218362306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
218462306a36Sopenharmony_ci	.probe = iwl_mei_probe,
218562306a36Sopenharmony_ci	.remove = iwl_mei_remove,
218662306a36Sopenharmony_ci};
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_cimodule_mei_cl_driver(iwl_mei_cl_driver);
2189