xref: /linux/drivers/net/ethernet/marvell/octeontx2/af/mbox.c (revision 48dea9a700c8728cc31a1dd44588b97578de86ee)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Marvell OcteonTx2 RVU Admin Function driver
3  *
4  * Copyright (C) 2018 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/module.h>
12 #include <linux/interrupt.h>
13 #include <linux/pci.h>
14 
15 #include "rvu_reg.h"
16 #include "mbox.h"
17 
18 static const u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
19 
20 void otx2_mbox_reset(struct otx2_mbox *mbox, int devid)
21 {
22 	void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE);
23 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
24 	struct mbox_hdr *tx_hdr, *rx_hdr;
25 
26 	tx_hdr = hw_mbase + mbox->tx_start;
27 	rx_hdr = hw_mbase + mbox->rx_start;
28 
29 	spin_lock(&mdev->mbox_lock);
30 	mdev->msg_size = 0;
31 	mdev->rsp_size = 0;
32 	tx_hdr->num_msgs = 0;
33 	tx_hdr->msg_size = 0;
34 	rx_hdr->num_msgs = 0;
35 	rx_hdr->msg_size = 0;
36 	spin_unlock(&mdev->mbox_lock);
37 }
38 EXPORT_SYMBOL(otx2_mbox_reset);
39 
40 void otx2_mbox_destroy(struct otx2_mbox *mbox)
41 {
42 	mbox->reg_base = NULL;
43 	mbox->hwbase = NULL;
44 
45 	kfree(mbox->dev);
46 	mbox->dev = NULL;
47 }
48 EXPORT_SYMBOL(otx2_mbox_destroy);
49 
50 int otx2_mbox_init(struct otx2_mbox *mbox, void *hwbase, struct pci_dev *pdev,
51 		   void *reg_base, int direction, int ndevs)
52 {
53 	struct otx2_mbox_dev *mdev;
54 	int devid;
55 
56 	switch (direction) {
57 	case MBOX_DIR_AFPF:
58 	case MBOX_DIR_PFVF:
59 		mbox->tx_start = MBOX_DOWN_TX_START;
60 		mbox->rx_start = MBOX_DOWN_RX_START;
61 		mbox->tx_size  = MBOX_DOWN_TX_SIZE;
62 		mbox->rx_size  = MBOX_DOWN_RX_SIZE;
63 		break;
64 	case MBOX_DIR_PFAF:
65 	case MBOX_DIR_VFPF:
66 		mbox->tx_start = MBOX_DOWN_RX_START;
67 		mbox->rx_start = MBOX_DOWN_TX_START;
68 		mbox->tx_size  = MBOX_DOWN_RX_SIZE;
69 		mbox->rx_size  = MBOX_DOWN_TX_SIZE;
70 		break;
71 	case MBOX_DIR_AFPF_UP:
72 	case MBOX_DIR_PFVF_UP:
73 		mbox->tx_start = MBOX_UP_TX_START;
74 		mbox->rx_start = MBOX_UP_RX_START;
75 		mbox->tx_size  = MBOX_UP_TX_SIZE;
76 		mbox->rx_size  = MBOX_UP_RX_SIZE;
77 		break;
78 	case MBOX_DIR_PFAF_UP:
79 	case MBOX_DIR_VFPF_UP:
80 		mbox->tx_start = MBOX_UP_RX_START;
81 		mbox->rx_start = MBOX_UP_TX_START;
82 		mbox->tx_size  = MBOX_UP_RX_SIZE;
83 		mbox->rx_size  = MBOX_UP_TX_SIZE;
84 		break;
85 	default:
86 		return -ENODEV;
87 	}
88 
89 	switch (direction) {
90 	case MBOX_DIR_AFPF:
91 	case MBOX_DIR_AFPF_UP:
92 		mbox->trigger = RVU_AF_AFPF_MBOX0;
93 		mbox->tr_shift = 4;
94 		break;
95 	case MBOX_DIR_PFAF:
96 	case MBOX_DIR_PFAF_UP:
97 		mbox->trigger = RVU_PF_PFAF_MBOX1;
98 		mbox->tr_shift = 0;
99 		break;
100 	case MBOX_DIR_PFVF:
101 	case MBOX_DIR_PFVF_UP:
102 		mbox->trigger = RVU_PF_VFX_PFVF_MBOX0;
103 		mbox->tr_shift = 12;
104 		break;
105 	case MBOX_DIR_VFPF:
106 	case MBOX_DIR_VFPF_UP:
107 		mbox->trigger = RVU_VF_VFPF_MBOX1;
108 		mbox->tr_shift = 0;
109 		break;
110 	default:
111 		return -ENODEV;
112 	}
113 
114 	mbox->reg_base = reg_base;
115 	mbox->hwbase = hwbase;
116 	mbox->pdev = pdev;
117 
118 	mbox->dev = kcalloc(ndevs, sizeof(struct otx2_mbox_dev), GFP_KERNEL);
119 	if (!mbox->dev) {
120 		otx2_mbox_destroy(mbox);
121 		return -ENOMEM;
122 	}
123 
124 	mbox->ndevs = ndevs;
125 	for (devid = 0; devid < ndevs; devid++) {
126 		mdev = &mbox->dev[devid];
127 		mdev->mbase = mbox->hwbase + (devid * MBOX_SIZE);
128 		spin_lock_init(&mdev->mbox_lock);
129 		/* Init header to reset value */
130 		otx2_mbox_reset(mbox, devid);
131 	}
132 
133 	return 0;
134 }
135 EXPORT_SYMBOL(otx2_mbox_init);
136 
137 int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid)
138 {
139 	unsigned long timeout = jiffies + msecs_to_jiffies(MBOX_RSP_TIMEOUT);
140 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
141 	struct device *sender = &mbox->pdev->dev;
142 
143 	while (!time_after(jiffies, timeout)) {
144 		if (mdev->num_msgs == mdev->msgs_acked)
145 			return 0;
146 		usleep_range(800, 1000);
147 	}
148 	dev_dbg(sender, "timed out while waiting for rsp\n");
149 	return -EIO;
150 }
151 EXPORT_SYMBOL(otx2_mbox_wait_for_rsp);
152 
153 int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid)
154 {
155 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
156 	unsigned long timeout = jiffies + 1 * HZ;
157 
158 	while (!time_after(jiffies, timeout)) {
159 		if (mdev->num_msgs == mdev->msgs_acked)
160 			return 0;
161 		cpu_relax();
162 	}
163 	return -EIO;
164 }
165 EXPORT_SYMBOL(otx2_mbox_busy_poll_for_rsp);
166 
167 void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid)
168 {
169 	void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE);
170 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
171 	struct mbox_hdr *tx_hdr, *rx_hdr;
172 
173 	tx_hdr = hw_mbase + mbox->tx_start;
174 	rx_hdr = hw_mbase + mbox->rx_start;
175 
176 	/* If bounce buffer is implemented copy mbox messages from
177 	 * bounce buffer to hw mbox memory.
178 	 */
179 	if (mdev->mbase != hw_mbase)
180 		memcpy(hw_mbase + mbox->tx_start + msgs_offset,
181 		       mdev->mbase + mbox->tx_start + msgs_offset,
182 		       mdev->msg_size);
183 
184 	spin_lock(&mdev->mbox_lock);
185 
186 	tx_hdr->msg_size = mdev->msg_size;
187 
188 	/* Reset header for next messages */
189 	mdev->msg_size = 0;
190 	mdev->rsp_size = 0;
191 	mdev->msgs_acked = 0;
192 
193 	/* Sync mbox data into memory */
194 	smp_wmb();
195 
196 	/* num_msgs != 0 signals to the peer that the buffer has a number of
197 	 * messages.  So this should be written after writing all the messages
198 	 * to the shared memory.
199 	 */
200 	tx_hdr->num_msgs = mdev->num_msgs;
201 	rx_hdr->num_msgs = 0;
202 	spin_unlock(&mdev->mbox_lock);
203 
204 	/* The interrupt should be fired after num_msgs is written
205 	 * to the shared memory
206 	 */
207 	writeq(1, (void __iomem *)mbox->reg_base +
208 	       (mbox->trigger | (devid << mbox->tr_shift)));
209 }
210 EXPORT_SYMBOL(otx2_mbox_msg_send);
211 
212 struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid,
213 					    int size, int size_rsp)
214 {
215 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
216 	struct mbox_msghdr *msghdr = NULL;
217 
218 	spin_lock(&mdev->mbox_lock);
219 	size = ALIGN(size, MBOX_MSG_ALIGN);
220 	size_rsp = ALIGN(size_rsp, MBOX_MSG_ALIGN);
221 	/* Check if there is space in mailbox */
222 	if ((mdev->msg_size + size) > mbox->tx_size - msgs_offset)
223 		goto exit;
224 	if ((mdev->rsp_size + size_rsp) > mbox->rx_size - msgs_offset)
225 		goto exit;
226 
227 	if (mdev->msg_size == 0)
228 		mdev->num_msgs = 0;
229 	mdev->num_msgs++;
230 
231 	msghdr = mdev->mbase + mbox->tx_start + msgs_offset + mdev->msg_size;
232 
233 	/* Clear the whole msg region */
234 	memset(msghdr, 0, size);
235 	/* Init message header with reset values */
236 	msghdr->ver = OTX2_MBOX_VERSION;
237 	mdev->msg_size += size;
238 	mdev->rsp_size += size_rsp;
239 	msghdr->next_msgoff = mdev->msg_size + msgs_offset;
240 exit:
241 	spin_unlock(&mdev->mbox_lock);
242 
243 	return msghdr;
244 }
245 EXPORT_SYMBOL(otx2_mbox_alloc_msg_rsp);
246 
247 struct mbox_msghdr *otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid,
248 				      struct mbox_msghdr *msg)
249 {
250 	unsigned long imsg = mbox->tx_start + msgs_offset;
251 	unsigned long irsp = mbox->rx_start + msgs_offset;
252 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
253 	u16 msgs;
254 
255 	spin_lock(&mdev->mbox_lock);
256 
257 	if (mdev->num_msgs != mdev->msgs_acked)
258 		goto error;
259 
260 	for (msgs = 0; msgs < mdev->msgs_acked; msgs++) {
261 		struct mbox_msghdr *pmsg = mdev->mbase + imsg;
262 		struct mbox_msghdr *prsp = mdev->mbase + irsp;
263 
264 		if (msg == pmsg) {
265 			if (pmsg->id != prsp->id)
266 				goto error;
267 			spin_unlock(&mdev->mbox_lock);
268 			return prsp;
269 		}
270 
271 		imsg = mbox->tx_start + pmsg->next_msgoff;
272 		irsp = mbox->rx_start + prsp->next_msgoff;
273 	}
274 
275 error:
276 	spin_unlock(&mdev->mbox_lock);
277 	return ERR_PTR(-ENODEV);
278 }
279 EXPORT_SYMBOL(otx2_mbox_get_rsp);
280 
281 int otx2_mbox_check_rsp_msgs(struct otx2_mbox *mbox, int devid)
282 {
283 	unsigned long ireq = mbox->tx_start + msgs_offset;
284 	unsigned long irsp = mbox->rx_start + msgs_offset;
285 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
286 	int rc = -ENODEV;
287 	u16 msgs;
288 
289 	spin_lock(&mdev->mbox_lock);
290 
291 	if (mdev->num_msgs != mdev->msgs_acked)
292 		goto exit;
293 
294 	for (msgs = 0; msgs < mdev->msgs_acked; msgs++) {
295 		struct mbox_msghdr *preq = mdev->mbase + ireq;
296 		struct mbox_msghdr *prsp = mdev->mbase + irsp;
297 
298 		if (preq->id != prsp->id)
299 			goto exit;
300 		if (prsp->rc) {
301 			rc = prsp->rc;
302 			goto exit;
303 		}
304 
305 		ireq = mbox->tx_start + preq->next_msgoff;
306 		irsp = mbox->rx_start + prsp->next_msgoff;
307 	}
308 	rc = 0;
309 exit:
310 	spin_unlock(&mdev->mbox_lock);
311 	return rc;
312 }
313 EXPORT_SYMBOL(otx2_mbox_check_rsp_msgs);
314 
315 int
316 otx2_reply_invalid_msg(struct otx2_mbox *mbox, int devid, u16 pcifunc, u16 id)
317 {
318 	struct msg_rsp *rsp;
319 
320 	rsp = (struct msg_rsp *)
321 	       otx2_mbox_alloc_msg(mbox, devid, sizeof(*rsp));
322 	if (!rsp)
323 		return -ENOMEM;
324 	rsp->hdr.id = id;
325 	rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
326 	rsp->hdr.rc = MBOX_MSG_INVALID;
327 	rsp->hdr.pcifunc = pcifunc;
328 	return 0;
329 }
330 EXPORT_SYMBOL(otx2_reply_invalid_msg);
331 
332 bool otx2_mbox_nonempty(struct otx2_mbox *mbox, int devid)
333 {
334 	struct otx2_mbox_dev *mdev = &mbox->dev[devid];
335 	bool ret;
336 
337 	spin_lock(&mdev->mbox_lock);
338 	ret = mdev->num_msgs != 0;
339 	spin_unlock(&mdev->mbox_lock);
340 
341 	return ret;
342 }
343 EXPORT_SYMBOL(otx2_mbox_nonempty);
344 
345 const char *otx2_mbox_id2name(u16 id)
346 {
347 	switch (id) {
348 #define M(_name, _id, _1, _2, _3) case _id: return # _name;
349 	MBOX_MESSAGES
350 #undef M
351 	default:
352 		return "INVALID ID";
353 	}
354 }
355 EXPORT_SYMBOL(otx2_mbox_id2name);
356 
357 MODULE_AUTHOR("Marvell International Ltd.");
358 MODULE_LICENSE("GPL v2");
359