12bb3e103SShalom Toledo// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
22bb3e103SShalom Toledo/* Copyright (c) 2017-2019 Mellanox Technologies. All rights reserved */
3410ed13cSYotam Gigi
4410ed13cSYotam Gigi#define pr_fmt(fmt) "mlxfw: " fmt
5410ed13cSYotam Gigi
6410ed13cSYotam Gigi#include <linux/kernel.h>
7410ed13cSYotam Gigi#include <linux/module.h>
8410ed13cSYotam Gigi#include <linux/delay.h>
9410ed13cSYotam Gigi
10410ed13cSYotam Gigi#include "mlxfw.h"
11410ed13cSYotam Gigi#include "mlxfw_mfa2.h"
12410ed13cSYotam Gigi
13410ed13cSYotam Gigi#define MLXFW_FSM_STATE_WAIT_CYCLE_MS 200
14410ed13cSYotam Gigi#define MLXFW_FSM_STATE_WAIT_TIMEOUT_MS 30000
15410ed13cSYotam Gigi#define MLXFW_FSM_STATE_WAIT_ROUNDS \
16410ed13cSYotam Gigi	(MLXFW_FSM_STATE_WAIT_TIMEOUT_MS / MLXFW_FSM_STATE_WAIT_CYCLE_MS)
17410ed13cSYotam Gigi#define MLXFW_FSM_MAX_COMPONENT_SIZE (10 * (1 << 20))
18410ed13cSYotam Gigi
1986a1270fSSaeed Mahameedstatic const int mlxfw_fsm_state_errno[] = {
2086a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_ERROR] = -EIO,
2186a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR] = -EBADMSG,
2286a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE] = -ENOENT,
2386a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY] = -ENOKEY,
2486a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED] = -EACCES,
2586a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED] = -EKEYREVOKED,
2686a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE] = -EKEYREJECTED,
2786a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT] = -ENOEXEC,
2886a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET] = -EBUSY,
2986a1270fSSaeed Mahameed	[MLXFW_FSM_STATE_ERR_MAX] = -EINVAL
3086a1270fSSaeed Mahameed};
3186a1270fSSaeed Mahameed
3286a1270fSSaeed Mahameed#define MLXFW_ERR_PRFX "Firmware flash failed: "
336a3f707cSSaeed Mahameed#define MLXFW_ERR_MSG(fwdev, extack, msg, err) do { \
346a3f707cSSaeed Mahameed	mlxfw_err(fwdev, "%s, err (%d)\n", MLXFW_ERR_PRFX msg, err); \
3586a1270fSSaeed Mahameed	NL_SET_ERR_MSG_MOD(extack, MLXFW_ERR_PRFX msg); \
3686a1270fSSaeed Mahameed} while (0)
3786a1270fSSaeed Mahameed
386a3f707cSSaeed Mahameedstatic int mlxfw_fsm_state_err(struct mlxfw_dev *mlxfw_dev,
396a3f707cSSaeed Mahameed			       struct netlink_ext_ack *extack,
4086a1270fSSaeed Mahameed			       enum mlxfw_fsm_state_err err)
4186a1270fSSaeed Mahameed{
4286a1270fSSaeed Mahameed	enum mlxfw_fsm_state_err fsm_state_err;
4386a1270fSSaeed Mahameed
4486a1270fSSaeed Mahameed	fsm_state_err = min_t(enum mlxfw_fsm_state_err, err,
4586a1270fSSaeed Mahameed			      MLXFW_FSM_STATE_ERR_MAX);
4686a1270fSSaeed Mahameed
4786a1270fSSaeed Mahameed	switch (fsm_state_err) {
4886a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_ERROR:
496a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "general error", err);
5086a1270fSSaeed Mahameed		break;
5186a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_DIGEST_ERR:
526a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "component hash mismatch", err);
5386a1270fSSaeed Mahameed		break;
5486a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_NOT_APPLICABLE:
556a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "component not applicable", err);
5686a1270fSSaeed Mahameed		break;
5786a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_UNKNOWN_KEY:
586a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown key", err);
5986a1270fSSaeed Mahameed		break;
6086a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_AUTH_FAILED:
616a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "authentication failed", err);
6286a1270fSSaeed Mahameed		break;
6386a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_UNSIGNED:
646a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "component was not signed", err);
6586a1270fSSaeed Mahameed		break;
6686a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_KEY_NOT_APPLICABLE:
676a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "key not applicable", err);
6886a1270fSSaeed Mahameed		break;
6986a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_REJECTED_BAD_FORMAT:
706a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "bad format", err);
7186a1270fSSaeed Mahameed		break;
7286a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_BLOCKED_PENDING_RESET:
736a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "pending reset", err);
7486a1270fSSaeed Mahameed		break;
75df561f66SGustavo A. R. Silva	case MLXFW_FSM_STATE_ERR_OK:
7686a1270fSSaeed Mahameed	case MLXFW_FSM_STATE_ERR_MAX:
776a3f707cSSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown error", err);
7886a1270fSSaeed Mahameed		break;
7910395e99SZheng Bin	}
8086a1270fSSaeed Mahameed
8186a1270fSSaeed Mahameed	return mlxfw_fsm_state_errno[fsm_state_err];
82410ed13cSYotam Gigi};
83410ed13cSYotam Gigi
84410ed13cSYotam Gigistatic int mlxfw_fsm_state_wait(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
8544f18db5SJiri Pirko				enum mlxfw_fsm_state fsm_state,
8644f18db5SJiri Pirko				struct netlink_ext_ack *extack)
87410ed13cSYotam Gigi{
88410ed13cSYotam Gigi	enum mlxfw_fsm_state_err fsm_state_err;
89410ed13cSYotam Gigi	enum mlxfw_fsm_state curr_fsm_state;
90410ed13cSYotam Gigi	int times;
91410ed13cSYotam Gigi	int err;
92410ed13cSYotam Gigi
93410ed13cSYotam Gigi	times = MLXFW_FSM_STATE_WAIT_ROUNDS;
94410ed13cSYotam Gigiretry:
95410ed13cSYotam Gigi	err = mlxfw_dev->ops->fsm_query_state(mlxfw_dev, fwhandle,
96410ed13cSYotam Gigi					      &curr_fsm_state, &fsm_state_err);
97f7fe7aa8SSaeed Mahameed	if (err) {
985042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM state query failed", err);
99410ed13cSYotam Gigi		return err;
100f7fe7aa8SSaeed Mahameed	}
101410ed13cSYotam Gigi
10286a1270fSSaeed Mahameed	if (fsm_state_err != MLXFW_FSM_STATE_ERR_OK)
1036a3f707cSSaeed Mahameed		return mlxfw_fsm_state_err(mlxfw_dev, extack, fsm_state_err);
10486a1270fSSaeed Mahameed
105410ed13cSYotam Gigi	if (curr_fsm_state != fsm_state) {
106410ed13cSYotam Gigi		if (--times == 0) {
1075042e8b9SSaeed Mahameed			MLXFW_ERR_MSG(mlxfw_dev, extack,
1085042e8b9SSaeed Mahameed				      "Timeout reached on FSM state change", -ETIMEDOUT);
109410ed13cSYotam Gigi			return -ETIMEDOUT;
110410ed13cSYotam Gigi		}
111410ed13cSYotam Gigi		msleep(MLXFW_FSM_STATE_WAIT_CYCLE_MS);
112410ed13cSYotam Gigi		goto retry;
113410ed13cSYotam Gigi	}
114410ed13cSYotam Gigi	return 0;
115410ed13cSYotam Gigi}
116410ed13cSYotam Gigi
117958dfd0dSEran Ben Elishastatic int
118958dfd0dSEran Ben Elishamlxfw_fsm_reactivate_err(struct mlxfw_dev *mlxfw_dev,
119958dfd0dSEran Ben Elisha			 struct netlink_ext_ack *extack, u8 err)
120958dfd0dSEran Ben Elisha{
121958dfd0dSEran Ben Elisha	enum mlxfw_fsm_reactivate_status status;
122958dfd0dSEran Ben Elisha
123958dfd0dSEran Ben Elisha#define MXFW_REACT_PRFX "Reactivate FSM: "
124958dfd0dSEran Ben Elisha#define MLXFW_REACT_ERR(msg, err) \
125958dfd0dSEran Ben Elisha	MLXFW_ERR_MSG(mlxfw_dev, extack, MXFW_REACT_PRFX msg, err)
126958dfd0dSEran Ben Elisha
127958dfd0dSEran Ben Elisha	status = min_t(enum mlxfw_fsm_reactivate_status, err,
128958dfd0dSEran Ben Elisha		       MLXFW_FSM_REACTIVATE_STATUS_MAX);
129958dfd0dSEran Ben Elisha
130958dfd0dSEran Ben Elisha	switch (status) {
131958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_BUSY:
132958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("busy", err);
133958dfd0dSEran Ben Elisha		break;
134958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_PROHIBITED_FW_VER_ERR:
135958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("prohibited fw ver", err);
136958dfd0dSEran Ben Elisha		break;
137958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_COPY_FAILED:
138958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("first page copy failed", err);
139958dfd0dSEran Ben Elisha		break;
140958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_ERASE_FAILED:
141958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("first page erase failed", err);
142958dfd0dSEran Ben Elisha		break;
143958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_FIRST_PAGE_RESTORE_FAILED:
144958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("first page restore failed", err);
145958dfd0dSEran Ben Elisha		break;
146958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_CANDIDATE_FW_DEACTIVATION_FAILED:
147958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("candidate fw deactivation failed", err);
148958dfd0dSEran Ben Elisha		break;
149958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_ERR_DEVICE_RESET_REQUIRED:
150958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("device reset required", err);
151958dfd0dSEran Ben Elisha		break;
152958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_ERR_FW_PROGRAMMING_NEEDED:
153f2ce925aSColin Ian King		MLXFW_REACT_ERR("fw programming needed", err);
154958dfd0dSEran Ben Elisha		break;
155958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED:
156958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("fw already activated", err);
157958dfd0dSEran Ben Elisha		break;
158df561f66SGustavo A. R. Silva	case MLXFW_FSM_REACTIVATE_STATUS_OK:
159958dfd0dSEran Ben Elisha	case MLXFW_FSM_REACTIVATE_STATUS_MAX:
160958dfd0dSEran Ben Elisha		MLXFW_REACT_ERR("unexpected error", err);
161958dfd0dSEran Ben Elisha		break;
16210395e99SZheng Bin	}
163958dfd0dSEran Ben Elisha	return -EREMOTEIO;
164958dfd0dSEran Ben Elisha};
165958dfd0dSEran Ben Elisha
166958dfd0dSEran Ben Elishastatic int mlxfw_fsm_reactivate(struct mlxfw_dev *mlxfw_dev,
167958dfd0dSEran Ben Elisha				struct netlink_ext_ack *extack,
168958dfd0dSEran Ben Elisha				bool *supported)
169958dfd0dSEran Ben Elisha{
170958dfd0dSEran Ben Elisha	u8 status;
171958dfd0dSEran Ben Elisha	int err;
172958dfd0dSEran Ben Elisha
173958dfd0dSEran Ben Elisha	if (!mlxfw_dev->ops->fsm_reactivate)
174958dfd0dSEran Ben Elisha		return 0;
175958dfd0dSEran Ben Elisha
176958dfd0dSEran Ben Elisha	err = mlxfw_dev->ops->fsm_reactivate(mlxfw_dev, &status);
177958dfd0dSEran Ben Elisha	if (err == -EOPNOTSUPP) {
178958dfd0dSEran Ben Elisha		*supported = false;
179958dfd0dSEran Ben Elisha		return 0;
180958dfd0dSEran Ben Elisha	}
181958dfd0dSEran Ben Elisha
182958dfd0dSEran Ben Elisha	if (err) {
183958dfd0dSEran Ben Elisha		MLXFW_ERR_MSG(mlxfw_dev, extack,
184958dfd0dSEran Ben Elisha			      "Could not reactivate firmware flash", err);
185958dfd0dSEran Ben Elisha		return err;
186958dfd0dSEran Ben Elisha	}
187958dfd0dSEran Ben Elisha
188958dfd0dSEran Ben Elisha	if (status == MLXFW_FSM_REACTIVATE_STATUS_OK ||
189958dfd0dSEran Ben Elisha	    status == MLXFW_FSM_REACTIVATE_STATUS_FW_ALREADY_ACTIVATED)
190958dfd0dSEran Ben Elisha		return 0;
191958dfd0dSEran Ben Elisha
192958dfd0dSEran Ben Elisha	return mlxfw_fsm_reactivate_err(mlxfw_dev, extack, status);
193958dfd0dSEran Ben Elisha}
194958dfd0dSEran Ben Elisha
1954ae57566SSaeed Mahameedstatic void mlxfw_status_notify(struct mlxfw_dev *mlxfw_dev,
1964ae57566SSaeed Mahameed				const char *msg, const char *comp_name,
1974ae57566SSaeed Mahameed				u32 done_bytes, u32 total_bytes)
1984ae57566SSaeed Mahameed{
1994ae57566SSaeed Mahameed	devlink_flash_update_status_notify(mlxfw_dev->devlink, msg, comp_name,
2004ae57566SSaeed Mahameed					   done_bytes, total_bytes);
2014ae57566SSaeed Mahameed}
2024ae57566SSaeed Mahameed
203410ed13cSYotam Gigi#define MLXFW_ALIGN_DOWN(x, align_bits) ((x) & ~((1 << (align_bits)) - 1))
204410ed13cSYotam Gigi#define MLXFW_ALIGN_UP(x, align_bits) \
205410ed13cSYotam Gigi		MLXFW_ALIGN_DOWN((x) + ((1 << (align_bits)) - 1), (align_bits))
206410ed13cSYotam Gigi
207410ed13cSYotam Gigistatic int mlxfw_flash_component(struct mlxfw_dev *mlxfw_dev,
208410ed13cSYotam Gigi				 u32 fwhandle,
20944f18db5SJiri Pirko				 struct mlxfw_mfa2_component *comp,
210958dfd0dSEran Ben Elisha				 bool reactivate_supp,
21144f18db5SJiri Pirko				 struct netlink_ext_ack *extack)
212410ed13cSYotam Gigi{
213410ed13cSYotam Gigi	u16 comp_max_write_size;
214410ed13cSYotam Gigi	u8 comp_align_bits;
215410ed13cSYotam Gigi	u32 comp_max_size;
2165853c418SJiri Pirko	char comp_name[8];
217410ed13cSYotam Gigi	u16 block_size;
218410ed13cSYotam Gigi	u8 *block_ptr;
219410ed13cSYotam Gigi	u32 offset;
220410ed13cSYotam Gigi	int err;
221410ed13cSYotam Gigi
2225853c418SJiri Pirko	sprintf(comp_name, "%u", comp->index);
2235853c418SJiri Pirko
224410ed13cSYotam Gigi	err = mlxfw_dev->ops->component_query(mlxfw_dev, comp->index,
225410ed13cSYotam Gigi					      &comp_max_size, &comp_align_bits,
226410ed13cSYotam Gigi					      &comp_max_write_size);
227f7fe7aa8SSaeed Mahameed	if (err) {
2285042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack, "FSM component query failed", err);
229410ed13cSYotam Gigi		return err;
230f7fe7aa8SSaeed Mahameed	}
231410ed13cSYotam Gigi
232410ed13cSYotam Gigi	comp_max_size = min_t(u32, comp_max_size, MLXFW_FSM_MAX_COMPONENT_SIZE);
233410ed13cSYotam Gigi	if (comp->data_size > comp_max_size) {
2345042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
2355042e8b9SSaeed Mahameed			      "Component size is bigger than limit", -EINVAL);
236410ed13cSYotam Gigi		return -EINVAL;
237410ed13cSYotam Gigi	}
238410ed13cSYotam Gigi
239410ed13cSYotam Gigi	comp_max_write_size = MLXFW_ALIGN_DOWN(comp_max_write_size,
240410ed13cSYotam Gigi					       comp_align_bits);
241410ed13cSYotam Gigi
2426a3f707cSSaeed Mahameed	mlxfw_dbg(mlxfw_dev, "Component update\n");
2435853c418SJiri Pirko	mlxfw_status_notify(mlxfw_dev, "Updating component", comp_name, 0, 0);
244410ed13cSYotam Gigi	err = mlxfw_dev->ops->fsm_component_update(mlxfw_dev, fwhandle,
245410ed13cSYotam Gigi						   comp->index,
246410ed13cSYotam Gigi						   comp->data_size);
247f7fe7aa8SSaeed Mahameed	if (err) {
248958dfd0dSEran Ben Elisha		if (!reactivate_supp)
249958dfd0dSEran Ben Elisha			MLXFW_ERR_MSG(mlxfw_dev, extack,
250958dfd0dSEran Ben Elisha				      "FSM component update failed, FW reactivate is not supported",
251958dfd0dSEran Ben Elisha				      err);
252958dfd0dSEran Ben Elisha		else
253958dfd0dSEran Ben Elisha			MLXFW_ERR_MSG(mlxfw_dev, extack,
254958dfd0dSEran Ben Elisha				      "FSM component update failed", err);
255410ed13cSYotam Gigi		return err;
256f7fe7aa8SSaeed Mahameed	}
257410ed13cSYotam Gigi
258410ed13cSYotam Gigi	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
25944f18db5SJiri Pirko				   MLXFW_FSM_STATE_DOWNLOAD, extack);
260410ed13cSYotam Gigi	if (err)
261410ed13cSYotam Gigi		goto err_out;
262410ed13cSYotam Gigi
2636a3f707cSSaeed Mahameed	mlxfw_dbg(mlxfw_dev, "Component download\n");
2645853c418SJiri Pirko	mlxfw_status_notify(mlxfw_dev, "Downloading component",
2655853c418SJiri Pirko			    comp_name, 0, comp->data_size);
266410ed13cSYotam Gigi	for (offset = 0;
267410ed13cSYotam Gigi	     offset < MLXFW_ALIGN_UP(comp->data_size, comp_align_bits);
268410ed13cSYotam Gigi	     offset += comp_max_write_size) {
269410ed13cSYotam Gigi		block_ptr = comp->data + offset;
270410ed13cSYotam Gigi		block_size = (u16) min_t(u32, comp->data_size - offset,
271410ed13cSYotam Gigi					 comp_max_write_size);
272410ed13cSYotam Gigi		err = mlxfw_dev->ops->fsm_block_download(mlxfw_dev, fwhandle,
273410ed13cSYotam Gigi							 block_ptr, block_size,
274410ed13cSYotam Gigi							 offset);
275f7fe7aa8SSaeed Mahameed		if (err) {
2765042e8b9SSaeed Mahameed			MLXFW_ERR_MSG(mlxfw_dev, extack,
2775042e8b9SSaeed Mahameed				      "Component download failed", err);
278410ed13cSYotam Gigi			goto err_out;
279f7fe7aa8SSaeed Mahameed		}
2805853c418SJiri Pirko		mlxfw_status_notify(mlxfw_dev, "Downloading component",
2815853c418SJiri Pirko				    comp_name, offset + block_size,
2825853c418SJiri Pirko				    comp->data_size);
283410ed13cSYotam Gigi	}
284410ed13cSYotam Gigi
2856a3f707cSSaeed Mahameed	mlxfw_dbg(mlxfw_dev, "Component verify\n");
2865853c418SJiri Pirko	mlxfw_status_notify(mlxfw_dev, "Verifying component", comp_name, 0, 0);
287410ed13cSYotam Gigi	err = mlxfw_dev->ops->fsm_component_verify(mlxfw_dev, fwhandle,
288410ed13cSYotam Gigi						   comp->index);
289f7fe7aa8SSaeed Mahameed	if (err) {
2905042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
2915042e8b9SSaeed Mahameed			      "FSM component verify failed", err);
292410ed13cSYotam Gigi		goto err_out;
293f7fe7aa8SSaeed Mahameed	}
294410ed13cSYotam Gigi
29544f18db5SJiri Pirko	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
29644f18db5SJiri Pirko				   MLXFW_FSM_STATE_LOCKED, extack);
297410ed13cSYotam Gigi	if (err)
298410ed13cSYotam Gigi		goto err_out;
299410ed13cSYotam Gigi	return 0;
300410ed13cSYotam Gigi
301410ed13cSYotam Gigierr_out:
302410ed13cSYotam Gigi	mlxfw_dev->ops->fsm_cancel(mlxfw_dev, fwhandle);
303410ed13cSYotam Gigi	return err;
304410ed13cSYotam Gigi}
305410ed13cSYotam Gigi
306410ed13cSYotam Gigistatic int mlxfw_flash_components(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
30744f18db5SJiri Pirko				  struct mlxfw_mfa2_file *mfa2_file,
308958dfd0dSEran Ben Elisha				  bool reactivate_supp,
30944f18db5SJiri Pirko				  struct netlink_ext_ack *extack)
310410ed13cSYotam Gigi{
311410ed13cSYotam Gigi	u32 component_count;
312410ed13cSYotam Gigi	int err;
313410ed13cSYotam Gigi	int i;
314410ed13cSYotam Gigi
315410ed13cSYotam Gigi	err = mlxfw_mfa2_file_component_count(mfa2_file, mlxfw_dev->psid,
316410ed13cSYotam Gigi					      mlxfw_dev->psid_size,
317410ed13cSYotam Gigi					      &component_count);
318410ed13cSYotam Gigi	if (err) {
3195042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
3205042e8b9SSaeed Mahameed			      "Could not find device PSID in MFA2 file", err);
321410ed13cSYotam Gigi		return err;
322410ed13cSYotam Gigi	}
323410ed13cSYotam Gigi
324410ed13cSYotam Gigi	for (i = 0; i < component_count; i++) {
325410ed13cSYotam Gigi		struct mlxfw_mfa2_component *comp;
326410ed13cSYotam Gigi
327410ed13cSYotam Gigi		comp = mlxfw_mfa2_file_component_get(mfa2_file, mlxfw_dev->psid,
328410ed13cSYotam Gigi						     mlxfw_dev->psid_size, i);
329f7fe7aa8SSaeed Mahameed		if (IS_ERR(comp)) {
330f7fe7aa8SSaeed Mahameed			err = PTR_ERR(comp);
3315042e8b9SSaeed Mahameed			MLXFW_ERR_MSG(mlxfw_dev, extack,
3325042e8b9SSaeed Mahameed				      "Failed to get MFA2 component", err);
333f7fe7aa8SSaeed Mahameed			return err;
334f7fe7aa8SSaeed Mahameed		}
335410ed13cSYotam Gigi
3366a3f707cSSaeed Mahameed		mlxfw_info(mlxfw_dev, "Flashing component type %d\n",
3376a3f707cSSaeed Mahameed			   comp->index);
338958dfd0dSEran Ben Elisha		err = mlxfw_flash_component(mlxfw_dev, fwhandle, comp,
339958dfd0dSEran Ben Elisha					    reactivate_supp, extack);
340410ed13cSYotam Gigi		mlxfw_mfa2_file_component_put(comp);
341410ed13cSYotam Gigi		if (err)
342410ed13cSYotam Gigi			return err;
343410ed13cSYotam Gigi	}
344410ed13cSYotam Gigi	return 0;
345410ed13cSYotam Gigi}
346410ed13cSYotam Gigi
347410ed13cSYotam Gigiint mlxfw_firmware_flash(struct mlxfw_dev *mlxfw_dev,
34844f18db5SJiri Pirko			 const struct firmware *firmware,
34944f18db5SJiri Pirko			 struct netlink_ext_ack *extack)
350410ed13cSYotam Gigi{
351410ed13cSYotam Gigi	struct mlxfw_mfa2_file *mfa2_file;
352958dfd0dSEran Ben Elisha	bool reactivate_supp = true;
353410ed13cSYotam Gigi	u32 fwhandle;
354410ed13cSYotam Gigi	int err;
355410ed13cSYotam Gigi
356410ed13cSYotam Gigi	if (!mlxfw_mfa2_check(firmware)) {
3575042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
3585042e8b9SSaeed Mahameed			      "Firmware file is not MFA2", -EINVAL);
359410ed13cSYotam Gigi		return -EINVAL;
360410ed13cSYotam Gigi	}
361410ed13cSYotam Gigi
362410ed13cSYotam Gigi	mfa2_file = mlxfw_mfa2_file_init(firmware);
363f7fe7aa8SSaeed Mahameed	if (IS_ERR(mfa2_file)) {
364f7fe7aa8SSaeed Mahameed		err = PTR_ERR(mfa2_file);
3655042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
3665042e8b9SSaeed Mahameed			      "Failed to initialize MFA2 firmware file", err);
367f7fe7aa8SSaeed Mahameed		return err;
368f7fe7aa8SSaeed Mahameed	}
369410ed13cSYotam Gigi
3706a3f707cSSaeed Mahameed	mlxfw_info(mlxfw_dev, "Initialize firmware flash process\n");
3715853c418SJiri Pirko	mlxfw_status_notify(mlxfw_dev, "Initializing firmware flash process",
3725853c418SJiri Pirko			    NULL, 0, 0);
373410ed13cSYotam Gigi	err = mlxfw_dev->ops->fsm_lock(mlxfw_dev, &fwhandle);
374410ed13cSYotam Gigi	if (err) {
3755042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
3765042e8b9SSaeed Mahameed			      "Could not lock the firmware FSM", err);
377410ed13cSYotam Gigi		goto err_fsm_lock;
378410ed13cSYotam Gigi	}
379410ed13cSYotam Gigi
380410ed13cSYotam Gigi	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
38144f18db5SJiri Pirko				   MLXFW_FSM_STATE_LOCKED, extack);
382410ed13cSYotam Gigi	if (err)
383410ed13cSYotam Gigi		goto err_state_wait_idle_to_locked;
384410ed13cSYotam Gigi
385958dfd0dSEran Ben Elisha	err = mlxfw_fsm_reactivate(mlxfw_dev, extack, &reactivate_supp);
386958dfd0dSEran Ben Elisha	if (err)
387958dfd0dSEran Ben Elisha		goto err_fsm_reactivate;
388958dfd0dSEran Ben Elisha
389958dfd0dSEran Ben Elisha	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
390958dfd0dSEran Ben Elisha				   MLXFW_FSM_STATE_LOCKED, extack);
391958dfd0dSEran Ben Elisha	if (err)
392958dfd0dSEran Ben Elisha		goto err_state_wait_reactivate_to_locked;
393958dfd0dSEran Ben Elisha
394958dfd0dSEran Ben Elisha	err = mlxfw_flash_components(mlxfw_dev, fwhandle, mfa2_file,
395958dfd0dSEran Ben Elisha				     reactivate_supp, extack);
396410ed13cSYotam Gigi	if (err)
397410ed13cSYotam Gigi		goto err_flash_components;
398410ed13cSYotam Gigi
3996a3f707cSSaeed Mahameed	mlxfw_dbg(mlxfw_dev, "Activate image\n");
4005853c418SJiri Pirko	mlxfw_status_notify(mlxfw_dev, "Activating image", NULL, 0, 0);
401410ed13cSYotam Gigi	err = mlxfw_dev->ops->fsm_activate(mlxfw_dev, fwhandle);
402410ed13cSYotam Gigi	if (err) {
4035042e8b9SSaeed Mahameed		MLXFW_ERR_MSG(mlxfw_dev, extack,
4045042e8b9SSaeed Mahameed			      "Could not activate the downloaded image", err);
405410ed13cSYotam Gigi		goto err_fsm_activate;
406410ed13cSYotam Gigi	}
407410ed13cSYotam Gigi
40844f18db5SJiri Pirko	err = mlxfw_fsm_state_wait(mlxfw_dev, fwhandle,
40944f18db5SJiri Pirko				   MLXFW_FSM_STATE_LOCKED, extack);
410410ed13cSYotam Gigi	if (err)
411410ed13cSYotam Gigi		goto err_state_wait_activate_to_locked;
412410ed13cSYotam Gigi
4136a3f707cSSaeed Mahameed	mlxfw_dbg(mlxfw_dev, "Handle release\n");
414410ed13cSYotam Gigi	mlxfw_dev->ops->fsm_release(mlxfw_dev, fwhandle);
415