Commit 733eb54f authored by Avraham Stern's avatar Avraham Stern Committed by Gregory Greenman
Browse files

wifi: iwlwifi: mei: implement PLDR flow



If the FW needs to do OTP re-read, the driver must notify CSME before
loading the FW so CSME will not try to access the NIC during the
re-read. Once the alive notification is received, CSME is notified
that NIC access is allowed again.

Signed-off-by: default avatarAvraham Stern <avraham.stern@intel.com>
Signed-off-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20221102165239.49eb8c6d455f.I7f0a5debb2d3d662a4151199bbec24613f324c13@changeid
parent ef2e7a51
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -377,6 +377,7 @@ enum {
#define PREG_PRPH_WPROT_22000		0xA04D00

#define SB_MODIFY_CFG_FLAG		0xA03088
#define SB_CFG_RESIDES_IN_OTP_MASK	0x10
#define SB_CPU_1_STATUS			0xA01E30
#define SB_CPU_2_STATUS			0xA01E34
#define UMAG_SB_CPU_1_STATUS		0xA038C0
@@ -500,4 +501,7 @@ enum {

#define REG_OTP_MINOR 0xA0333C

#define WFPM_LMAC2_PD_NOTIFICATION 0xA033CC
#define WFPM_LMAC2_PD_RE_READ BIT(31)

#endif				/* __iwl_prph_h__ */
+21 −0
Original line number Diff line number Diff line
@@ -453,6 +453,21 @@ void iwl_mei_host_disassociated(void);
 */
void iwl_mei_device_state(bool up);

/**
 * iwl_mei_pldr_req() - must be called before loading the fw
 *
 * Return: 0 if the PLDR flow was successful and the fw can be loaded, negative
 *	value otherwise.
 */
int iwl_mei_pldr_req(void);

/**
 * iwl_mei_alive_notif() - must be called when alive notificaiton is received
 * @success: true if received alive notification, false if waiting for the
 *	notificaiton timed out.
 */
void iwl_mei_alive_notif(bool success);

#else

static inline bool iwl_mei_is_connected(void)
@@ -503,6 +518,12 @@ static inline void iwl_mei_host_disassociated(void)
static inline void iwl_mei_device_state(bool up)
{}

static inline int iwl_mei_pldr_req(void)
{ return 0; }

static inline void iwl_mei_alive_notif(bool success)
{}

#endif /* CONFIG_IWLMEI */

#endif /* __iwl_mei_h__ */
+100 −0
Original line number Diff line number Diff line
@@ -150,6 +150,8 @@ struct iwl_mei_filters {
 * @device_down: true if the device is down. Used to remember to send
 *	CSME_OWNERSHIP_CONFIRMED when the driver is already down.
 * @csa_throttle_end_wk: used when &csa_throttled is true
 * @pldr_wq: the wait queue for PLDR flow
 * @pldr_active: PLDR flow is in progress
 * @data_q_lock: protects the access to the data queues which are
 *	accessed without the mutex.
 * @netdev_work: used to defer registering and unregistering of the netdev to
@@ -173,6 +175,8 @@ struct iwl_mei {
	bool link_prot_state;
	bool device_down;
	struct delayed_work csa_throttle_end_wk;
	wait_queue_head_t pldr_wq;
	bool pldr_active;
	spinlock_t data_q_lock;
	struct work_struct netdev_work;

@@ -881,6 +885,15 @@ static void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev,
		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false);
}

static void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev,
				    const struct iwl_sap_pldr_ack_data *ack)
{
	struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);

	mei->pldr_active = le32_to_cpu(ack->status) == SAP_PLDR_STATUS_SUCCESS;
	wake_up_all(&mei->pldr_wq);
}

static void iwl_mei_handle_ping(struct mei_cl_device *cldev,
				const struct iwl_sap_hdr *hdr)
{
@@ -961,6 +974,8 @@ static void iwl_mei_handle_sap_msg(struct mei_cl_device *cldev,
			iwl_mei_handle_can_release_ownership, 0);
	SAP_MSG_HANDLER(CSME_TAKING_OWNERSHIP,
			iwl_mei_handle_csme_taking_ownership, 0);
	SAP_MSG_HANDLER(PLDR_ACK, iwl_mei_handle_pldr_ack,
			sizeof(struct iwl_sap_pldr_ack_data));
	default:
	/*
	 * This is not really an error, there are message that we decided
@@ -1337,6 +1352,62 @@ struct iwl_mei_nvm *iwl_mei_get_nvm(void)
}
EXPORT_SYMBOL_GPL(iwl_mei_get_nvm);

#define IWL_MEI_PLDR_NUM_RETRIES	3

int iwl_mei_pldr_req(void)
{
	struct iwl_mei *mei;
	int ret;
	struct iwl_sap_pldr_data msg = {
		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR),
		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
	};
	int i;

	mutex_lock(&iwl_mei_mutex);

	/* In case we didn't have a bind */
	if (!iwl_mei_is_connected()) {
		ret = 0;
		goto out;
	}

	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);

	if (!mei) {
		ret = -ENODEV;
		goto out;
	}

	if (!mei->amt_enabled) {
		ret = 0;
		goto out;
	}

	for (i = 0; i < IWL_MEI_PLDR_NUM_RETRIES; i++) {
		ret = iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
		mutex_unlock(&iwl_mei_mutex);
		if (ret)
			return ret;

		ret = wait_event_timeout(mei->pldr_wq, mei->pldr_active, HZ / 2);
		if (ret)
			break;

		/* Take the mutex for the next iteration */
		mutex_lock(&iwl_mei_mutex);
	}

	if (ret)
		return 0;

	ret = -ETIMEDOUT;
out:
	mutex_unlock(&iwl_mei_mutex);
	return ret;
}
EXPORT_SYMBOL_GPL(iwl_mei_pldr_req);

int iwl_mei_get_ownership(void)
{
	struct iwl_mei *mei;
@@ -1402,6 +1473,33 @@ int iwl_mei_get_ownership(void)
}
EXPORT_SYMBOL_GPL(iwl_mei_get_ownership);

void iwl_mei_alive_notif(bool success)
{
	struct iwl_mei *mei;
	struct iwl_sap_pldr_end_data msg = {
		.hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR_END),
		.hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
		.status = success ? cpu_to_le32(SAP_PLDR_STATUS_SUCCESS) :
			cpu_to_le32(SAP_PLDR_STATUS_FAILURE),
	};

	mutex_lock(&iwl_mei_mutex);

	if (!iwl_mei_is_connected())
		goto out;

	mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
	if (!mei || !mei->pldr_active)
		goto out;

	mei->pldr_active = false;

	iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
out:
	mutex_unlock(&iwl_mei_mutex);
}
EXPORT_SYMBOL_GPL(iwl_mei_alive_notif);

void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info,
			     const struct iwl_mei_colloc_info *colloc_info)
{
@@ -1841,6 +1939,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev,
	INIT_DELAYED_WORK(&mei->csa_throttle_end_wk,
			  iwl_mei_csa_throttle_end_wk);
	init_waitqueue_head(&mei->get_ownership_wq);
	init_waitqueue_head(&mei->pldr_wq);
	spin_lock_init(&mei->data_q_lock);
	INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work);

@@ -2013,6 +2112,7 @@ static void iwl_mei_remove(struct mei_cl_device *cldev)
	 * the device.
	 */
	wake_up_all(&mei->get_ownership_wq);
	wake_up_all(&mei->pldr_wq);

	mutex_lock(&iwl_mei_mutex);

+51 −0
Original line number Diff line number Diff line
@@ -203,6 +203,7 @@ struct iwl_sap_me_msg_start_ok {
 * @SAP_MSG_NOTIF_NIC_OWNER: Payload is a DW. See &enum iwl_sap_nic_owner.
 * @SAP_MSG_NOTIF_CSME_CONN_STATUS: See &struct iwl_sap_notif_conn_status.
 * @SAP_MSG_NOTIF_NVM: See &struct iwl_sap_nvm.
 * @SAP_MSG_NOTIF_PLDR_ACK: See &struct iwl_sap_pldr_ack_data.
 * @SAP_MSG_NOTIF_FROM_CSME_MAX: Not used.
 *
 * @SAP_MSG_NOTIF_FROM_HOST_MIN: Not used.
@@ -226,6 +227,8 @@ struct iwl_sap_me_msg_start_ok {
 * @SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED: No payload.
 * @SAP_MSG_NOTIF_SAR_LIMITS: See &struct iwl_sap_notif_sar_limits.
 * @SAP_MSG_NOTIF_GET_NVM: No payload. Triggers %SAP_MSG_NOTIF_NVM.
 * @SAP_MSG_NOTIF_PLDR: See &struct iwl_sap_pldr_data.
 * @SAP_MSG_NOTIF_PLDR_END: See &struct iwl_sap_pldr_end_data.
 * @SAP_MSG_NOTIF_FROM_HOST_MAX: Not used.
 *
 * @SAP_MSG_DATA_MIN: Not used.
@@ -258,6 +261,8 @@ enum iwl_sap_msg {
	SAP_MSG_NOTIF_NIC_OWNER				= 511,
	SAP_MSG_NOTIF_CSME_CONN_STATUS			= 512,
	SAP_MSG_NOTIF_NVM				= 513,
	/* 514 - 517 not supported */
	SAP_MSG_NOTIF_PLDR_ACK				= 518,
	SAP_MSG_NOTIF_FROM_CSME_MAX,

	SAP_MSG_NOTIF_FROM_HOST_MIN			= 1000,
@@ -279,6 +284,9 @@ enum iwl_sap_msg {
	SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED		= 1015,
	SAP_MSG_NOTIF_SAR_LIMITS			= 1016,
	SAP_MSG_NOTIF_GET_NVM				= 1017,
	/* 1018 - 1023 not supported */
	SAP_MSG_NOTIF_PLDR				= 1024,
	SAP_MSG_NOTIF_PLDR_END				= 1025,
	SAP_MSG_NOTIF_FROM_HOST_MAX,

	SAP_MSG_DATA_MIN				= 2000,
@@ -732,4 +740,47 @@ struct iwl_sap_cb_data {
	u8 payload[];
};

/**
 * struct iwl_sap_pldr_data - payload of %SAP_MSG_NOTIF_PLDR
 * @hdr: The SAP header.
 * @version: SAP message version
 */
struct iwl_sap_pldr_data {
	struct iwl_sap_hdr hdr;
	__le32 version;
} __packed;

/**
 * enum iwl_sap_pldr_status -
 * @SAP_PLDR_STATUS_SUCCESS: PLDR started/ended successfully
 * @SAP_PLDR_STATUS_FAILURE: PLDR failed to start/end
 */
enum iwl_sap_pldr_status {
	SAP_PLDR_STATUS_SUCCESS	= 0,
	SAP_PLDR_STATUS_FAILURE	= 1,
};

/*
 * struct iwl_sap_pldr_end_data - payload of %SAP_MSG_NOTIF_PLDR_END
 * @hdr: The SAP header.
 * @version: SAP message version
 * @status: PLDR end status
 */
struct iwl_sap_pldr_end_data {
	struct iwl_sap_hdr hdr;
	__le32 version;
	__le32 status;
} __packed;

/*
 * struct iwl_sap_pldr_ack_data - payload of %SAP_MSG_NOTIF_PLDR_ACK
 * @version: SAP message version
 * @status: CSME accept/refuse to the PLDR request
 */
struct iwl_sap_pldr_ack_data {
	struct iwl_sap_hdr hdr;
	__le32 version;
	__le32 status;
} __packed;

#endif /* __sap_h__ */
+7 −0
Original line number Diff line number Diff line
@@ -404,6 +404,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
		return -EIO;
	}

	iwl_mei_alive_notif(!ret);

	ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait);
	if (ret) {
		IWL_ERR(mvm, "Timeout waiting for PNVM load!\n");
@@ -1456,6 +1458,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
	struct ieee80211_channel *chan;
	struct cfg80211_chan_def chandef;
	struct ieee80211_supported_band *sband = NULL;
	u32 sb_cfg;

	lockdep_assert_held(&mvm->mutex);

@@ -1463,6 +1466,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
	if (ret)
		return ret;

	sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
	if (!(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK) && iwl_mei_pldr_req())
		return ret;

	ret = iwl_mvm_load_rt_fw(mvm);
	if (ret) {
		IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);