18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/circ_buf.h>
58c2ecf20Sopenharmony_ci#include <linux/device.h>
68c2ecf20Sopenharmony_ci#include <linux/firmware.h>
78c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/pci.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "prestera.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define PRESTERA_MSG_MAX_SIZE 1500
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define PRESTERA_SUPP_FW_MAJ_VER	2
178c2ecf20Sopenharmony_ci#define PRESTERA_SUPP_FW_MIN_VER	0
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define PRESTERA_FW_PATH_FMT	"mrvl/prestera/mvsw_prestera_fw-v%u.%u.img"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define PRESTERA_FW_HDR_MAGIC		0x351D9D06
228c2ecf20Sopenharmony_ci#define PRESTERA_FW_DL_TIMEOUT_MS	50000
238c2ecf20Sopenharmony_ci#define PRESTERA_FW_BLK_SZ		1024
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define PRESTERA_FW_VER_MAJ_MUL 1000000
268c2ecf20Sopenharmony_ci#define PRESTERA_FW_VER_MIN_MUL 1000
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define PRESTERA_FW_VER_MAJ(v)	((v) / PRESTERA_FW_VER_MAJ_MUL)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define PRESTERA_FW_VER_MIN(v) \
318c2ecf20Sopenharmony_ci	(((v) - (PRESTERA_FW_VER_MAJ(v) * PRESTERA_FW_VER_MAJ_MUL)) / \
328c2ecf20Sopenharmony_ci			PRESTERA_FW_VER_MIN_MUL)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define PRESTERA_FW_VER_PATCH(v) \
358c2ecf20Sopenharmony_ci	((v) - (PRESTERA_FW_VER_MAJ(v) * PRESTERA_FW_VER_MAJ_MUL) - \
368c2ecf20Sopenharmony_ci			(PRESTERA_FW_VER_MIN(v) * PRESTERA_FW_VER_MIN_MUL))
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cienum prestera_pci_bar_t {
398c2ecf20Sopenharmony_ci	PRESTERA_PCI_BAR_FW = 2,
408c2ecf20Sopenharmony_ci	PRESTERA_PCI_BAR_PP = 4,
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct prestera_fw_header {
448c2ecf20Sopenharmony_ci	__be32 magic_number;
458c2ecf20Sopenharmony_ci	__be32 version_value;
468c2ecf20Sopenharmony_ci	u8 reserved[8];
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct prestera_ldr_regs {
508c2ecf20Sopenharmony_ci	u32 ldr_ready;
518c2ecf20Sopenharmony_ci	u32 pad1;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	u32 ldr_img_size;
548c2ecf20Sopenharmony_ci	u32 ldr_ctl_flags;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	u32 ldr_buf_offs;
578c2ecf20Sopenharmony_ci	u32 ldr_buf_size;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	u32 ldr_buf_rd;
608c2ecf20Sopenharmony_ci	u32 pad2;
618c2ecf20Sopenharmony_ci	u32 ldr_buf_wr;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	u32 ldr_status;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define PRESTERA_LDR_REG_OFFSET(f)	offsetof(struct prestera_ldr_regs, f)
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define PRESTERA_LDR_READY_MAGIC	0xf00dfeed
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define PRESTERA_LDR_STATUS_IMG_DL	BIT(0)
718c2ecf20Sopenharmony_ci#define PRESTERA_LDR_STATUS_START_FW	BIT(1)
728c2ecf20Sopenharmony_ci#define PRESTERA_LDR_STATUS_INVALID_IMG	BIT(2)
738c2ecf20Sopenharmony_ci#define PRESTERA_LDR_STATUS_NOMEM	BIT(3)
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define PRESTERA_LDR_REG_BASE(fw)	((fw)->ldr_regs)
768c2ecf20Sopenharmony_ci#define PRESTERA_LDR_REG_ADDR(fw, reg)	(PRESTERA_LDR_REG_BASE(fw) + (reg))
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* fw loader registers */
798c2ecf20Sopenharmony_ci#define PRESTERA_LDR_READY_REG		PRESTERA_LDR_REG_OFFSET(ldr_ready)
808c2ecf20Sopenharmony_ci#define PRESTERA_LDR_IMG_SIZE_REG	PRESTERA_LDR_REG_OFFSET(ldr_img_size)
818c2ecf20Sopenharmony_ci#define PRESTERA_LDR_CTL_REG		PRESTERA_LDR_REG_OFFSET(ldr_ctl_flags)
828c2ecf20Sopenharmony_ci#define PRESTERA_LDR_BUF_SIZE_REG	PRESTERA_LDR_REG_OFFSET(ldr_buf_size)
838c2ecf20Sopenharmony_ci#define PRESTERA_LDR_BUF_OFFS_REG	PRESTERA_LDR_REG_OFFSET(ldr_buf_offs)
848c2ecf20Sopenharmony_ci#define PRESTERA_LDR_BUF_RD_REG		PRESTERA_LDR_REG_OFFSET(ldr_buf_rd)
858c2ecf20Sopenharmony_ci#define PRESTERA_LDR_BUF_WR_REG		PRESTERA_LDR_REG_OFFSET(ldr_buf_wr)
868c2ecf20Sopenharmony_ci#define PRESTERA_LDR_STATUS_REG		PRESTERA_LDR_REG_OFFSET(ldr_status)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define PRESTERA_LDR_CTL_DL_START	BIT(0)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define PRESTERA_EVT_QNUM_MAX	4
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistruct prestera_fw_evtq_regs {
938c2ecf20Sopenharmony_ci	u32 rd_idx;
948c2ecf20Sopenharmony_ci	u32 pad1;
958c2ecf20Sopenharmony_ci	u32 wr_idx;
968c2ecf20Sopenharmony_ci	u32 pad2;
978c2ecf20Sopenharmony_ci	u32 offs;
988c2ecf20Sopenharmony_ci	u32 len;
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistruct prestera_fw_regs {
1028c2ecf20Sopenharmony_ci	u32 fw_ready;
1038c2ecf20Sopenharmony_ci	u32 pad;
1048c2ecf20Sopenharmony_ci	u32 cmd_offs;
1058c2ecf20Sopenharmony_ci	u32 cmd_len;
1068c2ecf20Sopenharmony_ci	u32 evt_offs;
1078c2ecf20Sopenharmony_ci	u32 evt_qnum;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	u32 cmd_req_ctl;
1108c2ecf20Sopenharmony_ci	u32 cmd_req_len;
1118c2ecf20Sopenharmony_ci	u32 cmd_rcv_ctl;
1128c2ecf20Sopenharmony_ci	u32 cmd_rcv_len;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	u32 fw_status;
1158c2ecf20Sopenharmony_ci	u32 rx_status;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	struct prestera_fw_evtq_regs evtq_list[PRESTERA_EVT_QNUM_MAX];
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#define PRESTERA_FW_REG_OFFSET(f)	offsetof(struct prestera_fw_regs, f)
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#define PRESTERA_FW_READY_MAGIC		0xcafebabe
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* fw registers */
1258c2ecf20Sopenharmony_ci#define PRESTERA_FW_READY_REG		PRESTERA_FW_REG_OFFSET(fw_ready)
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#define PRESTERA_CMD_BUF_OFFS_REG	PRESTERA_FW_REG_OFFSET(cmd_offs)
1288c2ecf20Sopenharmony_ci#define PRESTERA_CMD_BUF_LEN_REG	PRESTERA_FW_REG_OFFSET(cmd_len)
1298c2ecf20Sopenharmony_ci#define PRESTERA_EVT_BUF_OFFS_REG	PRESTERA_FW_REG_OFFSET(evt_offs)
1308c2ecf20Sopenharmony_ci#define PRESTERA_EVT_QNUM_REG		PRESTERA_FW_REG_OFFSET(evt_qnum)
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#define PRESTERA_CMD_REQ_CTL_REG	PRESTERA_FW_REG_OFFSET(cmd_req_ctl)
1338c2ecf20Sopenharmony_ci#define PRESTERA_CMD_REQ_LEN_REG	PRESTERA_FW_REG_OFFSET(cmd_req_len)
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define PRESTERA_CMD_RCV_CTL_REG	PRESTERA_FW_REG_OFFSET(cmd_rcv_ctl)
1368c2ecf20Sopenharmony_ci#define PRESTERA_CMD_RCV_LEN_REG	PRESTERA_FW_REG_OFFSET(cmd_rcv_len)
1378c2ecf20Sopenharmony_ci#define PRESTERA_FW_STATUS_REG		PRESTERA_FW_REG_OFFSET(fw_status)
1388c2ecf20Sopenharmony_ci#define PRESTERA_RX_STATUS_REG		PRESTERA_FW_REG_OFFSET(rx_status)
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* PRESTERA_CMD_REQ_CTL_REG flags */
1418c2ecf20Sopenharmony_ci#define PRESTERA_CMD_F_REQ_SENT		BIT(0)
1428c2ecf20Sopenharmony_ci#define PRESTERA_CMD_F_REPL_RCVD	BIT(1)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* PRESTERA_CMD_RCV_CTL_REG flags */
1458c2ecf20Sopenharmony_ci#define PRESTERA_CMD_F_REPL_SENT	BIT(0)
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#define PRESTERA_EVTQ_REG_OFFSET(q, f)			\
1488c2ecf20Sopenharmony_ci	(PRESTERA_FW_REG_OFFSET(evtq_list) +		\
1498c2ecf20Sopenharmony_ci	 (q) * sizeof(struct prestera_fw_evtq_regs) +	\
1508c2ecf20Sopenharmony_ci	 offsetof(struct prestera_fw_evtq_regs, f))
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci#define PRESTERA_EVTQ_RD_IDX_REG(q)	PRESTERA_EVTQ_REG_OFFSET(q, rd_idx)
1538c2ecf20Sopenharmony_ci#define PRESTERA_EVTQ_WR_IDX_REG(q)	PRESTERA_EVTQ_REG_OFFSET(q, wr_idx)
1548c2ecf20Sopenharmony_ci#define PRESTERA_EVTQ_OFFS_REG(q)	PRESTERA_EVTQ_REG_OFFSET(q, offs)
1558c2ecf20Sopenharmony_ci#define PRESTERA_EVTQ_LEN_REG(q)	PRESTERA_EVTQ_REG_OFFSET(q, len)
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#define PRESTERA_FW_REG_BASE(fw)	((fw)->dev.ctl_regs)
1588c2ecf20Sopenharmony_ci#define PRESTERA_FW_REG_ADDR(fw, reg)	PRESTERA_FW_REG_BASE((fw)) + (reg)
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci#define PRESTERA_FW_CMD_DEFAULT_WAIT_MS	30000
1618c2ecf20Sopenharmony_ci#define PRESTERA_FW_READY_WAIT_MS	20000
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct prestera_fw_evtq {
1648c2ecf20Sopenharmony_ci	u8 __iomem *addr;
1658c2ecf20Sopenharmony_ci	size_t len;
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistruct prestera_fw {
1698c2ecf20Sopenharmony_ci	struct workqueue_struct *wq;
1708c2ecf20Sopenharmony_ci	struct prestera_device dev;
1718c2ecf20Sopenharmony_ci	u8 __iomem *ldr_regs;
1728c2ecf20Sopenharmony_ci	u8 __iomem *ldr_ring_buf;
1738c2ecf20Sopenharmony_ci	u32 ldr_buf_len;
1748c2ecf20Sopenharmony_ci	u32 ldr_wr_idx;
1758c2ecf20Sopenharmony_ci	struct mutex cmd_mtx; /* serialize access to dev->send_req */
1768c2ecf20Sopenharmony_ci	size_t cmd_mbox_len;
1778c2ecf20Sopenharmony_ci	u8 __iomem *cmd_mbox;
1788c2ecf20Sopenharmony_ci	struct prestera_fw_evtq evt_queue[PRESTERA_EVT_QNUM_MAX];
1798c2ecf20Sopenharmony_ci	u8 evt_qnum;
1808c2ecf20Sopenharmony_ci	struct work_struct evt_work;
1818c2ecf20Sopenharmony_ci	u8 __iomem *evt_buf;
1828c2ecf20Sopenharmony_ci	u8 *evt_msg;
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int prestera_fw_load(struct prestera_fw *fw);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void prestera_fw_write(struct prestera_fw *fw, u32 reg, u32 val)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	writel(val, PRESTERA_FW_REG_ADDR(fw, reg));
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic u32 prestera_fw_read(struct prestera_fw *fw, u32 reg)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	return readl(PRESTERA_FW_REG_ADDR(fw, reg));
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic u32 prestera_fw_evtq_len(struct prestera_fw *fw, u8 qid)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	return fw->evt_queue[qid].len;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic u32 prestera_fw_evtq_avail(struct prestera_fw *fw, u8 qid)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u32 wr_idx = prestera_fw_read(fw, PRESTERA_EVTQ_WR_IDX_REG(qid));
2058c2ecf20Sopenharmony_ci	u32 rd_idx = prestera_fw_read(fw, PRESTERA_EVTQ_RD_IDX_REG(qid));
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return CIRC_CNT(wr_idx, rd_idx, prestera_fw_evtq_len(fw, qid));
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic void prestera_fw_evtq_rd_set(struct prestera_fw *fw,
2118c2ecf20Sopenharmony_ci				    u8 qid, u32 idx)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u32 rd_idx = idx & (prestera_fw_evtq_len(fw, qid) - 1);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	prestera_fw_write(fw, PRESTERA_EVTQ_RD_IDX_REG(qid), rd_idx);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic u8 __iomem *prestera_fw_evtq_buf(struct prestera_fw *fw, u8 qid)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	return fw->evt_queue[qid].addr;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic u32 prestera_fw_evtq_read32(struct prestera_fw *fw, u8 qid)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	u32 rd_idx = prestera_fw_read(fw, PRESTERA_EVTQ_RD_IDX_REG(qid));
2268c2ecf20Sopenharmony_ci	u32 val;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	val = readl(prestera_fw_evtq_buf(fw, qid) + rd_idx);
2298c2ecf20Sopenharmony_ci	prestera_fw_evtq_rd_set(fw, qid, rd_idx + 4);
2308c2ecf20Sopenharmony_ci	return val;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic ssize_t prestera_fw_evtq_read_buf(struct prestera_fw *fw,
2348c2ecf20Sopenharmony_ci					 u8 qid, void *buf, size_t len)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	u32 idx = prestera_fw_read(fw, PRESTERA_EVTQ_RD_IDX_REG(qid));
2378c2ecf20Sopenharmony_ci	u8 __iomem *evtq_addr = prestera_fw_evtq_buf(fw, qid);
2388c2ecf20Sopenharmony_ci	u32 *buf32 = buf;
2398c2ecf20Sopenharmony_ci	int i;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	for (i = 0; i < len / 4; buf32++, i++) {
2428c2ecf20Sopenharmony_ci		*buf32 = readl_relaxed(evtq_addr + idx);
2438c2ecf20Sopenharmony_ci		idx = (idx + 4) & (prestera_fw_evtq_len(fw, qid) - 1);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	prestera_fw_evtq_rd_set(fw, qid, idx);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return i;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic u8 prestera_fw_evtq_pick(struct prestera_fw *fw)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	int qid;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	for (qid = 0; qid < fw->evt_qnum; qid++) {
2568c2ecf20Sopenharmony_ci		if (prestera_fw_evtq_avail(fw, qid) >= 4)
2578c2ecf20Sopenharmony_ci			return qid;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return PRESTERA_EVT_QNUM_MAX;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void prestera_fw_evt_work_fn(struct work_struct *work)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct prestera_fw *fw;
2668c2ecf20Sopenharmony_ci	void *msg;
2678c2ecf20Sopenharmony_ci	u8 qid;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	fw = container_of(work, struct prestera_fw, evt_work);
2708c2ecf20Sopenharmony_ci	msg = fw->evt_msg;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	while ((qid = prestera_fw_evtq_pick(fw)) < PRESTERA_EVT_QNUM_MAX) {
2738c2ecf20Sopenharmony_ci		u32 idx;
2748c2ecf20Sopenharmony_ci		u32 len;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		len = prestera_fw_evtq_read32(fw, qid);
2778c2ecf20Sopenharmony_ci		idx = prestera_fw_read(fw, PRESTERA_EVTQ_RD_IDX_REG(qid));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		WARN_ON(prestera_fw_evtq_avail(fw, qid) < len);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		if (WARN_ON(len > PRESTERA_MSG_MAX_SIZE)) {
2828c2ecf20Sopenharmony_ci			prestera_fw_evtq_rd_set(fw, qid, idx + len);
2838c2ecf20Sopenharmony_ci			continue;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		prestera_fw_evtq_read_buf(fw, qid, msg, len);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		if (fw->dev.recv_msg)
2898c2ecf20Sopenharmony_ci			fw->dev.recv_msg(&fw->dev, msg, len);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int prestera_fw_wait_reg32(struct prestera_fw *fw, u32 reg, u32 cmp,
2948c2ecf20Sopenharmony_ci				  unsigned int waitms)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	u8 __iomem *addr = PRESTERA_FW_REG_ADDR(fw, reg);
2978c2ecf20Sopenharmony_ci	u32 val;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return readl_poll_timeout(addr, val, cmp == val,
3008c2ecf20Sopenharmony_ci				  1 * USEC_PER_MSEC, waitms * USEC_PER_MSEC);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic int prestera_fw_cmd_send(struct prestera_fw *fw,
3048c2ecf20Sopenharmony_ci				void *in_msg, size_t in_size,
3058c2ecf20Sopenharmony_ci				void *out_msg, size_t out_size,
3068c2ecf20Sopenharmony_ci				unsigned int waitms)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	u32 ret_size;
3098c2ecf20Sopenharmony_ci	int err;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!waitms)
3128c2ecf20Sopenharmony_ci		waitms = PRESTERA_FW_CMD_DEFAULT_WAIT_MS;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (ALIGN(in_size, 4) > fw->cmd_mbox_len)
3158c2ecf20Sopenharmony_ci		return -EMSGSIZE;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* wait for finish previous reply from FW */
3188c2ecf20Sopenharmony_ci	err = prestera_fw_wait_reg32(fw, PRESTERA_CMD_RCV_CTL_REG, 0, 30);
3198c2ecf20Sopenharmony_ci	if (err) {
3208c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "finish reply from FW is timed out\n");
3218c2ecf20Sopenharmony_ci		return err;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	prestera_fw_write(fw, PRESTERA_CMD_REQ_LEN_REG, in_size);
3258c2ecf20Sopenharmony_ci	memcpy_toio(fw->cmd_mbox, in_msg, in_size);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	prestera_fw_write(fw, PRESTERA_CMD_REQ_CTL_REG, PRESTERA_CMD_F_REQ_SENT);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* wait for reply from FW */
3308c2ecf20Sopenharmony_ci	err = prestera_fw_wait_reg32(fw, PRESTERA_CMD_RCV_CTL_REG,
3318c2ecf20Sopenharmony_ci				     PRESTERA_CMD_F_REPL_SENT, waitms);
3328c2ecf20Sopenharmony_ci	if (err) {
3338c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "reply from FW is timed out\n");
3348c2ecf20Sopenharmony_ci		goto cmd_exit;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ret_size = prestera_fw_read(fw, PRESTERA_CMD_RCV_LEN_REG);
3388c2ecf20Sopenharmony_ci	if (ret_size > out_size) {
3398c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "ret_size (%u) > out_len(%zu)\n",
3408c2ecf20Sopenharmony_ci			ret_size, out_size);
3418c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
3428c2ecf20Sopenharmony_ci		goto cmd_exit;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	memcpy_fromio(out_msg, fw->cmd_mbox + in_size, ret_size);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cicmd_exit:
3488c2ecf20Sopenharmony_ci	prestera_fw_write(fw, PRESTERA_CMD_REQ_CTL_REG, PRESTERA_CMD_F_REPL_RCVD);
3498c2ecf20Sopenharmony_ci	return err;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int prestera_fw_send_req(struct prestera_device *dev,
3538c2ecf20Sopenharmony_ci				void *in_msg, size_t in_size, void *out_msg,
3548c2ecf20Sopenharmony_ci				size_t out_size, unsigned int waitms)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct prestera_fw *fw;
3578c2ecf20Sopenharmony_ci	ssize_t ret;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	fw = container_of(dev, struct prestera_fw, dev);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	mutex_lock(&fw->cmd_mtx);
3628c2ecf20Sopenharmony_ci	ret = prestera_fw_cmd_send(fw, in_msg, in_size, out_msg, out_size, waitms);
3638c2ecf20Sopenharmony_ci	mutex_unlock(&fw->cmd_mtx);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return ret;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int prestera_fw_init(struct prestera_fw *fw)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	u8 __iomem *base;
3718c2ecf20Sopenharmony_ci	int err;
3728c2ecf20Sopenharmony_ci	u8 qid;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	fw->dev.send_req = prestera_fw_send_req;
3758c2ecf20Sopenharmony_ci	fw->ldr_regs = fw->dev.ctl_regs;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	err = prestera_fw_load(fw);
3788c2ecf20Sopenharmony_ci	if (err)
3798c2ecf20Sopenharmony_ci		return err;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	err = prestera_fw_wait_reg32(fw, PRESTERA_FW_READY_REG,
3828c2ecf20Sopenharmony_ci				     PRESTERA_FW_READY_MAGIC,
3838c2ecf20Sopenharmony_ci				     PRESTERA_FW_READY_WAIT_MS);
3848c2ecf20Sopenharmony_ci	if (err) {
3858c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "FW failed to start\n");
3868c2ecf20Sopenharmony_ci		return err;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	base = fw->dev.ctl_regs;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	fw->cmd_mbox = base + prestera_fw_read(fw, PRESTERA_CMD_BUF_OFFS_REG);
3928c2ecf20Sopenharmony_ci	fw->cmd_mbox_len = prestera_fw_read(fw, PRESTERA_CMD_BUF_LEN_REG);
3938c2ecf20Sopenharmony_ci	mutex_init(&fw->cmd_mtx);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	fw->evt_buf = base + prestera_fw_read(fw, PRESTERA_EVT_BUF_OFFS_REG);
3968c2ecf20Sopenharmony_ci	fw->evt_qnum = prestera_fw_read(fw, PRESTERA_EVT_QNUM_REG);
3978c2ecf20Sopenharmony_ci	fw->evt_msg = kmalloc(PRESTERA_MSG_MAX_SIZE, GFP_KERNEL);
3988c2ecf20Sopenharmony_ci	if (!fw->evt_msg)
3998c2ecf20Sopenharmony_ci		return -ENOMEM;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	for (qid = 0; qid < fw->evt_qnum; qid++) {
4028c2ecf20Sopenharmony_ci		u32 offs = prestera_fw_read(fw, PRESTERA_EVTQ_OFFS_REG(qid));
4038c2ecf20Sopenharmony_ci		struct prestera_fw_evtq *evtq = &fw->evt_queue[qid];
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		evtq->len = prestera_fw_read(fw, PRESTERA_EVTQ_LEN_REG(qid));
4068c2ecf20Sopenharmony_ci		evtq->addr = fw->evt_buf + offs;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void prestera_fw_uninit(struct prestera_fw *fw)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	kfree(fw->evt_msg);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic irqreturn_t prestera_pci_irq_handler(int irq, void *dev_id)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct prestera_fw *fw = dev_id;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (prestera_fw_read(fw, PRESTERA_RX_STATUS_REG)) {
4228c2ecf20Sopenharmony_ci		prestera_fw_write(fw, PRESTERA_RX_STATUS_REG, 0);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (fw->dev.recv_pkt)
4258c2ecf20Sopenharmony_ci			fw->dev.recv_pkt(&fw->dev);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	queue_work(fw->wq, &fw->evt_work);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void prestera_ldr_write(struct prestera_fw *fw, u32 reg, u32 val)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	writel(val, PRESTERA_LDR_REG_ADDR(fw, reg));
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic u32 prestera_ldr_read(struct prestera_fw *fw, u32 reg)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	return readl(PRESTERA_LDR_REG_ADDR(fw, reg));
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int prestera_ldr_wait_reg32(struct prestera_fw *fw,
4448c2ecf20Sopenharmony_ci				   u32 reg, u32 cmp, unsigned int waitms)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	u8 __iomem *addr = PRESTERA_LDR_REG_ADDR(fw, reg);
4478c2ecf20Sopenharmony_ci	u32 val;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return readl_poll_timeout(addr, val, cmp == val,
4508c2ecf20Sopenharmony_ci				  10 * USEC_PER_MSEC, waitms * USEC_PER_MSEC);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic u32 prestera_ldr_wait_buf(struct prestera_fw *fw, size_t len)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	u8 __iomem *addr = PRESTERA_LDR_REG_ADDR(fw, PRESTERA_LDR_BUF_RD_REG);
4568c2ecf20Sopenharmony_ci	u32 buf_len = fw->ldr_buf_len;
4578c2ecf20Sopenharmony_ci	u32 wr_idx = fw->ldr_wr_idx;
4588c2ecf20Sopenharmony_ci	u32 rd_idx;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return readl_poll_timeout(addr, rd_idx,
4618c2ecf20Sopenharmony_ci				 CIRC_SPACE(wr_idx, rd_idx, buf_len) >= len,
4628c2ecf20Sopenharmony_ci				 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int prestera_ldr_wait_dl_finish(struct prestera_fw *fw)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	u8 __iomem *addr = PRESTERA_LDR_REG_ADDR(fw, PRESTERA_LDR_STATUS_REG);
4688c2ecf20Sopenharmony_ci	unsigned long mask = ~(PRESTERA_LDR_STATUS_IMG_DL);
4698c2ecf20Sopenharmony_ci	u32 val;
4708c2ecf20Sopenharmony_ci	int err;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	err = readl_poll_timeout(addr, val, val & mask, 10 * USEC_PER_MSEC,
4738c2ecf20Sopenharmony_ci				 PRESTERA_FW_DL_TIMEOUT_MS * USEC_PER_MSEC);
4748c2ecf20Sopenharmony_ci	if (err) {
4758c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "Timeout to load FW img [state=%d]",
4768c2ecf20Sopenharmony_ci			prestera_ldr_read(fw, PRESTERA_LDR_STATUS_REG));
4778c2ecf20Sopenharmony_ci		return err;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic void prestera_ldr_wr_idx_move(struct prestera_fw *fw, unsigned int n)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	fw->ldr_wr_idx = (fw->ldr_wr_idx + (n)) & (fw->ldr_buf_len - 1);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void prestera_ldr_wr_idx_commit(struct prestera_fw *fw)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	prestera_ldr_write(fw, PRESTERA_LDR_BUF_WR_REG, fw->ldr_wr_idx);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic u8 __iomem *prestera_ldr_wr_ptr(struct prestera_fw *fw)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	return fw->ldr_ring_buf + fw->ldr_wr_idx;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int prestera_ldr_send(struct prestera_fw *fw, const u8 *buf, size_t len)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	int err;
5018c2ecf20Sopenharmony_ci	int i;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	err = prestera_ldr_wait_buf(fw, len);
5048c2ecf20Sopenharmony_ci	if (err) {
5058c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "failed wait for sending firmware\n");
5068c2ecf20Sopenharmony_ci		return err;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += 4) {
5108c2ecf20Sopenharmony_ci		writel_relaxed(*(u32 *)(buf + i), prestera_ldr_wr_ptr(fw));
5118c2ecf20Sopenharmony_ci		prestera_ldr_wr_idx_move(fw, 4);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	prestera_ldr_wr_idx_commit(fw);
5158c2ecf20Sopenharmony_ci	return 0;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic int prestera_ldr_fw_send(struct prestera_fw *fw,
5198c2ecf20Sopenharmony_ci				const char *img, u32 fw_size)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	u32 status;
5228c2ecf20Sopenharmony_ci	u32 pos;
5238c2ecf20Sopenharmony_ci	int err;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	err = prestera_ldr_wait_reg32(fw, PRESTERA_LDR_STATUS_REG,
5268c2ecf20Sopenharmony_ci				      PRESTERA_LDR_STATUS_IMG_DL,
5278c2ecf20Sopenharmony_ci				      5 * MSEC_PER_SEC);
5288c2ecf20Sopenharmony_ci	if (err) {
5298c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "Loader is not ready to load image\n");
5308c2ecf20Sopenharmony_ci		return err;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	for (pos = 0; pos < fw_size; pos += PRESTERA_FW_BLK_SZ) {
5348c2ecf20Sopenharmony_ci		if (pos + PRESTERA_FW_BLK_SZ > fw_size)
5358c2ecf20Sopenharmony_ci			break;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci		err = prestera_ldr_send(fw, img + pos, PRESTERA_FW_BLK_SZ);
5388c2ecf20Sopenharmony_ci		if (err)
5398c2ecf20Sopenharmony_ci			return err;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (pos < fw_size) {
5438c2ecf20Sopenharmony_ci		err = prestera_ldr_send(fw, img + pos, fw_size - pos);
5448c2ecf20Sopenharmony_ci		if (err)
5458c2ecf20Sopenharmony_ci			return err;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	err = prestera_ldr_wait_dl_finish(fw);
5498c2ecf20Sopenharmony_ci	if (err)
5508c2ecf20Sopenharmony_ci		return err;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	status = prestera_ldr_read(fw, PRESTERA_LDR_STATUS_REG);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	switch (status) {
5558c2ecf20Sopenharmony_ci	case PRESTERA_LDR_STATUS_INVALID_IMG:
5568c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "FW img has bad CRC\n");
5578c2ecf20Sopenharmony_ci		return -EINVAL;
5588c2ecf20Sopenharmony_ci	case PRESTERA_LDR_STATUS_NOMEM:
5598c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "Loader has no enough mem\n");
5608c2ecf20Sopenharmony_ci		return -ENOMEM;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic void prestera_fw_rev_parse(const struct prestera_fw_header *hdr,
5678c2ecf20Sopenharmony_ci				  struct prestera_fw_rev *rev)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	u32 version = be32_to_cpu(hdr->version_value);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	rev->maj = PRESTERA_FW_VER_MAJ(version);
5728c2ecf20Sopenharmony_ci	rev->min = PRESTERA_FW_VER_MIN(version);
5738c2ecf20Sopenharmony_ci	rev->sub = PRESTERA_FW_VER_PATCH(version);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic int prestera_fw_rev_check(struct prestera_fw *fw)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct prestera_fw_rev *rev = &fw->dev.fw_rev;
5798c2ecf20Sopenharmony_ci	u16 maj_supp = PRESTERA_SUPP_FW_MAJ_VER;
5808c2ecf20Sopenharmony_ci	u16 min_supp = PRESTERA_SUPP_FW_MIN_VER;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (rev->maj == maj_supp && rev->min >= min_supp)
5838c2ecf20Sopenharmony_ci		return 0;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	dev_err(fw->dev.dev, "Driver supports FW version only '%u.%u.x'",
5868c2ecf20Sopenharmony_ci		PRESTERA_SUPP_FW_MAJ_VER, PRESTERA_SUPP_FW_MIN_VER);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return -EINVAL;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic int prestera_fw_hdr_parse(struct prestera_fw *fw,
5928c2ecf20Sopenharmony_ci				 const struct firmware *img)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct prestera_fw_header *hdr = (struct prestera_fw_header *)img->data;
5958c2ecf20Sopenharmony_ci	struct prestera_fw_rev *rev = &fw->dev.fw_rev;
5968c2ecf20Sopenharmony_ci	u32 magic;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	magic = be32_to_cpu(hdr->magic_number);
5998c2ecf20Sopenharmony_ci	if (magic != PRESTERA_FW_HDR_MAGIC) {
6008c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "FW img hdr magic is invalid");
6018c2ecf20Sopenharmony_ci		return -EINVAL;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	prestera_fw_rev_parse(hdr, rev);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	dev_info(fw->dev.dev, "FW version '%u.%u.%u'\n",
6078c2ecf20Sopenharmony_ci		 rev->maj, rev->min, rev->sub);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return prestera_fw_rev_check(fw);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int prestera_fw_load(struct prestera_fw *fw)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	size_t hlen = sizeof(struct prestera_fw_header);
6158c2ecf20Sopenharmony_ci	const struct firmware *f;
6168c2ecf20Sopenharmony_ci	char fw_path[128];
6178c2ecf20Sopenharmony_ci	int err;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	err = prestera_ldr_wait_reg32(fw, PRESTERA_LDR_READY_REG,
6208c2ecf20Sopenharmony_ci				      PRESTERA_LDR_READY_MAGIC,
6218c2ecf20Sopenharmony_ci				      5 * MSEC_PER_SEC);
6228c2ecf20Sopenharmony_ci	if (err) {
6238c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "waiting for FW loader is timed out");
6248c2ecf20Sopenharmony_ci		return err;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	fw->ldr_ring_buf = fw->ldr_regs +
6288c2ecf20Sopenharmony_ci		prestera_ldr_read(fw, PRESTERA_LDR_BUF_OFFS_REG);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	fw->ldr_buf_len =
6318c2ecf20Sopenharmony_ci		prestera_ldr_read(fw, PRESTERA_LDR_BUF_SIZE_REG);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	fw->ldr_wr_idx = 0;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	snprintf(fw_path, sizeof(fw_path), PRESTERA_FW_PATH_FMT,
6368c2ecf20Sopenharmony_ci		 PRESTERA_SUPP_FW_MAJ_VER, PRESTERA_SUPP_FW_MIN_VER);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	err = request_firmware_direct(&f, fw_path, fw->dev.dev);
6398c2ecf20Sopenharmony_ci	if (err) {
6408c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "failed to request firmware file\n");
6418c2ecf20Sopenharmony_ci		return err;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	err = prestera_fw_hdr_parse(fw, f);
6458c2ecf20Sopenharmony_ci	if (err) {
6468c2ecf20Sopenharmony_ci		dev_err(fw->dev.dev, "FW image header is invalid\n");
6478c2ecf20Sopenharmony_ci		goto out_release;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	prestera_ldr_write(fw, PRESTERA_LDR_IMG_SIZE_REG, f->size - hlen);
6518c2ecf20Sopenharmony_ci	prestera_ldr_write(fw, PRESTERA_LDR_CTL_REG, PRESTERA_LDR_CTL_DL_START);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	dev_info(fw->dev.dev, "Loading %s ...", fw_path);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	err = prestera_ldr_fw_send(fw, f->data + hlen, f->size - hlen);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ciout_release:
6588c2ecf20Sopenharmony_ci	release_firmware(f);
6598c2ecf20Sopenharmony_ci	return err;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic int prestera_pci_probe(struct pci_dev *pdev,
6638c2ecf20Sopenharmony_ci			      const struct pci_device_id *id)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	const char *driver_name = pdev->driver->name;
6668c2ecf20Sopenharmony_ci	struct prestera_fw *fw;
6678c2ecf20Sopenharmony_ci	int err;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	err = pcim_enable_device(pdev);
6708c2ecf20Sopenharmony_ci	if (err)
6718c2ecf20Sopenharmony_ci		return err;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	err = pcim_iomap_regions(pdev, BIT(PRESTERA_PCI_BAR_FW) |
6748c2ecf20Sopenharmony_ci				 BIT(PRESTERA_PCI_BAR_PP),
6758c2ecf20Sopenharmony_ci				 pci_name(pdev));
6768c2ecf20Sopenharmony_ci	if (err)
6778c2ecf20Sopenharmony_ci		return err;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(30));
6808c2ecf20Sopenharmony_ci	if (err) {
6818c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "fail to set DMA mask\n");
6828c2ecf20Sopenharmony_ci		goto err_dma_mask;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	pci_set_master(pdev);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL);
6888c2ecf20Sopenharmony_ci	if (!fw) {
6898c2ecf20Sopenharmony_ci		err = -ENOMEM;
6908c2ecf20Sopenharmony_ci		goto err_pci_dev_alloc;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	fw->dev.ctl_regs = pcim_iomap_table(pdev)[PRESTERA_PCI_BAR_FW];
6948c2ecf20Sopenharmony_ci	fw->dev.pp_regs = pcim_iomap_table(pdev)[PRESTERA_PCI_BAR_PP];
6958c2ecf20Sopenharmony_ci	fw->dev.dev = &pdev->dev;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, fw);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	err = prestera_fw_init(fw);
7008c2ecf20Sopenharmony_ci	if (err)
7018c2ecf20Sopenharmony_ci		goto err_prestera_fw_init;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	dev_info(fw->dev.dev, "Prestera FW is ready\n");
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	fw->wq = alloc_workqueue("prestera_fw_wq", WQ_HIGHPRI, 1);
7068c2ecf20Sopenharmony_ci	if (!fw->wq) {
7078c2ecf20Sopenharmony_ci		err = -ENOMEM;
7088c2ecf20Sopenharmony_ci		goto err_wq_alloc;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	INIT_WORK(&fw->evt_work, prestera_fw_evt_work_fn);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
7148c2ecf20Sopenharmony_ci	if (err < 0) {
7158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "MSI IRQ init failed\n");
7168c2ecf20Sopenharmony_ci		goto err_irq_alloc;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	err = request_irq(pci_irq_vector(pdev, 0), prestera_pci_irq_handler,
7208c2ecf20Sopenharmony_ci			  0, driver_name, fw);
7218c2ecf20Sopenharmony_ci	if (err) {
7228c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "fail to request IRQ\n");
7238c2ecf20Sopenharmony_ci		goto err_request_irq;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	err = prestera_device_register(&fw->dev);
7278c2ecf20Sopenharmony_ci	if (err)
7288c2ecf20Sopenharmony_ci		goto err_prestera_dev_register;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	return 0;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cierr_prestera_dev_register:
7338c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(pdev, 0), fw);
7348c2ecf20Sopenharmony_cierr_request_irq:
7358c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
7368c2ecf20Sopenharmony_cierr_irq_alloc:
7378c2ecf20Sopenharmony_ci	destroy_workqueue(fw->wq);
7388c2ecf20Sopenharmony_cierr_wq_alloc:
7398c2ecf20Sopenharmony_ci	prestera_fw_uninit(fw);
7408c2ecf20Sopenharmony_cierr_prestera_fw_init:
7418c2ecf20Sopenharmony_cierr_pci_dev_alloc:
7428c2ecf20Sopenharmony_cierr_dma_mask:
7438c2ecf20Sopenharmony_ci	return err;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic void prestera_pci_remove(struct pci_dev *pdev)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct prestera_fw *fw = pci_get_drvdata(pdev);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	prestera_device_unregister(&fw->dev);
7518c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(pdev, 0), fw);
7528c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
7538c2ecf20Sopenharmony_ci	destroy_workqueue(fw->wq);
7548c2ecf20Sopenharmony_ci	prestera_fw_uninit(fw);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic const struct pci_device_id prestera_pci_devices[] = {
7588c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0xC804) },
7598c2ecf20Sopenharmony_ci	{ }
7608c2ecf20Sopenharmony_ci};
7618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, prestera_pci_devices);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic struct pci_driver prestera_pci_driver = {
7648c2ecf20Sopenharmony_ci	.name     = "Prestera DX",
7658c2ecf20Sopenharmony_ci	.id_table = prestera_pci_devices,
7668c2ecf20Sopenharmony_ci	.probe    = prestera_pci_probe,
7678c2ecf20Sopenharmony_ci	.remove   = prestera_pci_remove,
7688c2ecf20Sopenharmony_ci};
7698c2ecf20Sopenharmony_cimodule_pci_driver(prestera_pci_driver);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
7728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell Prestera switch PCI interface");
773