1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Marvell OcteonTX CPT driver |
3 | * |
4 | * Copyright (C) 2019 Marvell International Ltd. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. |
9 | */ |
10 | |
11 | #include <linux/delay.h> |
12 | #include "otx_cptvf.h" |
13 | |
14 | #define CPT_MBOX_MSG_TIMEOUT 2000 |
15 | |
16 | static char *get_mbox_opcode_str(int msg_opcode) |
17 | { |
18 | char *str = "Unknown" ; |
19 | |
20 | switch (msg_opcode) { |
21 | case OTX_CPT_MSG_VF_UP: |
22 | str = "UP" ; |
23 | break; |
24 | |
25 | case OTX_CPT_MSG_VF_DOWN: |
26 | str = "DOWN" ; |
27 | break; |
28 | |
29 | case OTX_CPT_MSG_READY: |
30 | str = "READY" ; |
31 | break; |
32 | |
33 | case OTX_CPT_MSG_QLEN: |
34 | str = "QLEN" ; |
35 | break; |
36 | |
37 | case OTX_CPT_MSG_QBIND_GRP: |
38 | str = "QBIND_GRP" ; |
39 | break; |
40 | |
41 | case OTX_CPT_MSG_VQ_PRIORITY: |
42 | str = "VQ_PRIORITY" ; |
43 | break; |
44 | |
45 | case OTX_CPT_MSG_PF_TYPE: |
46 | str = "PF_TYPE" ; |
47 | break; |
48 | |
49 | case OTX_CPT_MSG_ACK: |
50 | str = "ACK" ; |
51 | break; |
52 | |
53 | case OTX_CPT_MSG_NACK: |
54 | str = "NACK" ; |
55 | break; |
56 | } |
57 | return str; |
58 | } |
59 | |
60 | static void dump_mbox_msg(struct otx_cpt_mbox *mbox_msg, int vf_id) |
61 | { |
62 | char raw_data_str[OTX_CPT_MAX_MBOX_DATA_STR_SIZE]; |
63 | |
64 | hex_dump_to_buffer(buf: mbox_msg, len: sizeof(struct otx_cpt_mbox), rowsize: 16, groupsize: 8, |
65 | linebuf: raw_data_str, OTX_CPT_MAX_MBOX_DATA_STR_SIZE, ascii: false); |
66 | if (vf_id >= 0) |
67 | pr_debug("MBOX msg %s received from VF%d raw_data %s" , |
68 | get_mbox_opcode_str(mbox_msg->msg), vf_id, |
69 | raw_data_str); |
70 | else |
71 | pr_debug("MBOX msg %s received from PF raw_data %s" , |
72 | get_mbox_opcode_str(mbox_msg->msg), raw_data_str); |
73 | } |
74 | |
75 | static void cptvf_send_msg_to_pf(struct otx_cptvf *cptvf, |
76 | struct otx_cpt_mbox *mbx) |
77 | { |
78 | /* Writing mbox(1) causes interrupt */ |
79 | writeq(val: mbx->msg, addr: cptvf->reg_base + OTX_CPT_VFX_PF_MBOXX(0, 0)); |
80 | writeq(val: mbx->data, addr: cptvf->reg_base + OTX_CPT_VFX_PF_MBOXX(0, 1)); |
81 | } |
82 | |
83 | /* Interrupt handler to handle mailbox messages from VFs */ |
84 | void otx_cptvf_handle_mbox_intr(struct otx_cptvf *cptvf) |
85 | { |
86 | struct otx_cpt_mbox mbx = {}; |
87 | |
88 | /* |
89 | * MBOX[0] contains msg |
90 | * MBOX[1] contains data |
91 | */ |
92 | mbx.msg = readq(addr: cptvf->reg_base + OTX_CPT_VFX_PF_MBOXX(0, 0)); |
93 | mbx.data = readq(addr: cptvf->reg_base + OTX_CPT_VFX_PF_MBOXX(0, 1)); |
94 | |
95 | dump_mbox_msg(mbox_msg: &mbx, vf_id: -1); |
96 | |
97 | switch (mbx.msg) { |
98 | case OTX_CPT_MSG_VF_UP: |
99 | cptvf->pf_acked = true; |
100 | cptvf->num_vfs = mbx.data; |
101 | break; |
102 | case OTX_CPT_MSG_READY: |
103 | cptvf->pf_acked = true; |
104 | cptvf->vfid = mbx.data; |
105 | dev_dbg(&cptvf->pdev->dev, "Received VFID %d\n" , cptvf->vfid); |
106 | break; |
107 | case OTX_CPT_MSG_QBIND_GRP: |
108 | cptvf->pf_acked = true; |
109 | cptvf->vftype = mbx.data; |
110 | dev_dbg(&cptvf->pdev->dev, "VF %d type %s group %d\n" , |
111 | cptvf->vfid, |
112 | ((mbx.data == OTX_CPT_SE_TYPES) ? "SE" : "AE" ), |
113 | cptvf->vfgrp); |
114 | break; |
115 | case OTX_CPT_MSG_ACK: |
116 | cptvf->pf_acked = true; |
117 | break; |
118 | case OTX_CPT_MSG_NACK: |
119 | cptvf->pf_nacked = true; |
120 | break; |
121 | default: |
122 | dev_err(&cptvf->pdev->dev, "Invalid msg from PF, msg 0x%llx\n" , |
123 | mbx.msg); |
124 | break; |
125 | } |
126 | } |
127 | |
128 | static int cptvf_send_msg_to_pf_timeout(struct otx_cptvf *cptvf, |
129 | struct otx_cpt_mbox *mbx) |
130 | { |
131 | int timeout = CPT_MBOX_MSG_TIMEOUT; |
132 | int sleep = 10; |
133 | |
134 | cptvf->pf_acked = false; |
135 | cptvf->pf_nacked = false; |
136 | cptvf_send_msg_to_pf(cptvf, mbx); |
137 | /* Wait for previous message to be acked, timeout 2sec */ |
138 | while (!cptvf->pf_acked) { |
139 | if (cptvf->pf_nacked) |
140 | return -EINVAL; |
141 | msleep(msecs: sleep); |
142 | if (cptvf->pf_acked) |
143 | break; |
144 | timeout -= sleep; |
145 | if (!timeout) { |
146 | dev_err(&cptvf->pdev->dev, |
147 | "PF didn't ack to mbox msg %llx from VF%u\n" , |
148 | mbx->msg, cptvf->vfid); |
149 | return -EBUSY; |
150 | } |
151 | } |
152 | return 0; |
153 | } |
154 | |
155 | /* |
156 | * Checks if VF is able to comminicate with PF |
157 | * and also gets the CPT number this VF is associated to. |
158 | */ |
159 | int otx_cptvf_check_pf_ready(struct otx_cptvf *cptvf) |
160 | { |
161 | struct otx_cpt_mbox mbx = {}; |
162 | |
163 | mbx.msg = OTX_CPT_MSG_READY; |
164 | |
165 | return cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
166 | } |
167 | |
168 | /* |
169 | * Communicate VQs size to PF to program CPT(0)_PF_Q(0-15)_CTL of the VF. |
170 | * Must be ACKed. |
171 | */ |
172 | int otx_cptvf_send_vq_size_msg(struct otx_cptvf *cptvf) |
173 | { |
174 | struct otx_cpt_mbox mbx = {}; |
175 | |
176 | mbx.msg = OTX_CPT_MSG_QLEN; |
177 | mbx.data = cptvf->qsize; |
178 | |
179 | return cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
180 | } |
181 | |
182 | /* |
183 | * Communicate VF group required to PF and get the VQ binded to that group |
184 | */ |
185 | int otx_cptvf_send_vf_to_grp_msg(struct otx_cptvf *cptvf, int group) |
186 | { |
187 | struct otx_cpt_mbox mbx = {}; |
188 | int ret; |
189 | |
190 | mbx.msg = OTX_CPT_MSG_QBIND_GRP; |
191 | /* Convey group of the VF */ |
192 | mbx.data = group; |
193 | ret = cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
194 | if (ret) |
195 | return ret; |
196 | cptvf->vfgrp = group; |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | /* |
202 | * Communicate VF group required to PF and get the VQ binded to that group |
203 | */ |
204 | int otx_cptvf_send_vf_priority_msg(struct otx_cptvf *cptvf) |
205 | { |
206 | struct otx_cpt_mbox mbx = {}; |
207 | |
208 | mbx.msg = OTX_CPT_MSG_VQ_PRIORITY; |
209 | /* Convey group of the VF */ |
210 | mbx.data = cptvf->priority; |
211 | |
212 | return cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
213 | } |
214 | |
215 | /* |
216 | * Communicate to PF that VF is UP and running |
217 | */ |
218 | int otx_cptvf_send_vf_up(struct otx_cptvf *cptvf) |
219 | { |
220 | struct otx_cpt_mbox mbx = {}; |
221 | |
222 | mbx.msg = OTX_CPT_MSG_VF_UP; |
223 | |
224 | return cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
225 | } |
226 | |
227 | /* |
228 | * Communicate to PF that VF is DOWN and running |
229 | */ |
230 | int otx_cptvf_send_vf_down(struct otx_cptvf *cptvf) |
231 | { |
232 | struct otx_cpt_mbox mbx = {}; |
233 | |
234 | mbx.msg = OTX_CPT_MSG_VF_DOWN; |
235 | |
236 | return cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx); |
237 | } |
238 | |