1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2016 Cavium, Inc.
4 */
5#include <linux/module.h>
6#include "cptpf.h"
7
8static void cpt_send_msg_to_vf(struct cpt_device *cpt, int vf,
9			       struct cpt_mbox *mbx)
10{
11	/* Writing mbox(0) causes interrupt */
12	cpt_write_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 1),
13			mbx->data);
14	cpt_write_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 0), mbx->msg);
15}
16
17/* ACKs VF's mailbox message
18 * @vf: VF to which ACK to be sent
19 */
20static void cpt_mbox_send_ack(struct cpt_device *cpt, int vf,
21			      struct cpt_mbox *mbx)
22{
23	mbx->data = 0ull;
24	mbx->msg = CPT_MBOX_MSG_TYPE_ACK;
25	cpt_send_msg_to_vf(cpt, vf, mbx);
26}
27
28static void cpt_clear_mbox_intr(struct cpt_device *cpt, u32 vf)
29{
30	/* W1C for the VF */
31	cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_INTX(0, 0), (1 << vf));
32}
33
34/*
35 *  Configure QLEN/Chunk sizes for VF
36 */
37static void cpt_cfg_qlen_for_vf(struct cpt_device *cpt, int vf, u32 size)
38{
39	union cptx_pf_qx_ctl pf_qx_ctl;
40
41	pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf));
42	pf_qx_ctl.s.size = size;
43	pf_qx_ctl.s.cont_err = true;
44	cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf), pf_qx_ctl.u);
45}
46
47/*
48 * Configure VQ priority
49 */
50static void cpt_cfg_vq_priority(struct cpt_device *cpt, int vf, u32 pri)
51{
52	union cptx_pf_qx_ctl pf_qx_ctl;
53
54	pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf));
55	pf_qx_ctl.s.pri = pri;
56	cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf), pf_qx_ctl.u);
57}
58
59static int cpt_bind_vq_to_grp(struct cpt_device *cpt, u8 q, u8 grp)
60{
61	struct microcode *mcode = cpt->mcode;
62	union cptx_pf_qx_ctl pf_qx_ctl;
63	struct device *dev = &cpt->pdev->dev;
64
65	if (q >= CPT_MAX_VF_NUM) {
66		dev_err(dev, "Queues are more than cores in the group");
67		return -EINVAL;
68	}
69	if (grp >= CPT_MAX_CORE_GROUPS) {
70		dev_err(dev, "Request group is more than possible groups");
71		return -EINVAL;
72	}
73	if (grp >= cpt->next_mc_idx) {
74		dev_err(dev, "Request group is higher than available functional groups");
75		return -EINVAL;
76	}
77	pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, q));
78	pf_qx_ctl.s.grp = mcode[grp].group;
79	cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, q), pf_qx_ctl.u);
80	dev_dbg(dev, "VF %d TYPE %s", q, (mcode[grp].is_ae ? "AE" : "SE"));
81
82	return mcode[grp].is_ae ? AE_TYPES : SE_TYPES;
83}
84
85/* Interrupt handler to handle mailbox messages from VFs */
86static void cpt_handle_mbox_intr(struct cpt_device *cpt, int vf)
87{
88	struct cpt_vf_info *vfx = &cpt->vfinfo[vf];
89	struct cpt_mbox mbx = {};
90	int vftype;
91	struct device *dev = &cpt->pdev->dev;
92	/*
93	 * MBOX[0] contains msg
94	 * MBOX[1] contains data
95	 */
96	mbx.msg  = cpt_read_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 0));
97	mbx.data = cpt_read_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 1));
98	dev_dbg(dev, "%s: Mailbox msg 0x%llx from VF%d", __func__, mbx.msg, vf);
99	switch (mbx.msg) {
100	case CPT_MSG_VF_UP:
101		vfx->state = VF_STATE_UP;
102		try_module_get(THIS_MODULE);
103		cpt_mbox_send_ack(cpt, vf, &mbx);
104		break;
105	case CPT_MSG_READY:
106		mbx.msg  = CPT_MSG_READY;
107		mbx.data = vf;
108		cpt_send_msg_to_vf(cpt, vf, &mbx);
109		break;
110	case CPT_MSG_VF_DOWN:
111		/* First msg in VF teardown sequence */
112		vfx->state = VF_STATE_DOWN;
113		module_put(THIS_MODULE);
114		cpt_mbox_send_ack(cpt, vf, &mbx);
115		break;
116	case CPT_MSG_QLEN:
117		vfx->qlen = mbx.data;
118		cpt_cfg_qlen_for_vf(cpt, vf, vfx->qlen);
119		cpt_mbox_send_ack(cpt, vf, &mbx);
120		break;
121	case CPT_MSG_QBIND_GRP:
122		vftype = cpt_bind_vq_to_grp(cpt, vf, (u8)mbx.data);
123		if ((vftype != AE_TYPES) && (vftype != SE_TYPES))
124			dev_err(dev, "Queue %d binding to group %llu failed",
125				vf, mbx.data);
126		else {
127			dev_dbg(dev, "Queue %d binding to group %llu successful",
128				vf, mbx.data);
129			mbx.msg = CPT_MSG_QBIND_GRP;
130			mbx.data = vftype;
131			cpt_send_msg_to_vf(cpt, vf, &mbx);
132		}
133		break;
134	case CPT_MSG_VQ_PRIORITY:
135		vfx->priority = mbx.data;
136		cpt_cfg_vq_priority(cpt, vf, vfx->priority);
137		cpt_mbox_send_ack(cpt, vf, &mbx);
138		break;
139	default:
140		dev_err(&cpt->pdev->dev, "Invalid msg from VF%d, msg 0x%llx\n",
141			vf, mbx.msg);
142		break;
143	}
144}
145
146void cpt_mbox_intr_handler (struct cpt_device *cpt, int mbx)
147{
148	u64 intr;
149	u8  vf;
150
151	intr = cpt_read_csr64(cpt->reg_base, CPTX_PF_MBOX_INTX(0, 0));
152	dev_dbg(&cpt->pdev->dev, "PF interrupt Mbox%d 0x%llx\n", mbx, intr);
153	for (vf = 0; vf < CPT_MAX_VF_NUM; vf++) {
154		if (intr & (1ULL << vf)) {
155			dev_dbg(&cpt->pdev->dev, "Intr from VF %d\n", vf);
156			cpt_handle_mbox_intr(cpt, vf);
157			cpt_clear_mbox_intr(cpt, vf);
158		}
159	}
160}
161