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