1 // SPDX-License-Identifier: GPL-2.0
2 /* Marvell Octeon EP (EndPoint) Ethernet Driver
3  *
4  * Copyright (C) 2020 Marvell.
5  *
6  */
7 #include <linux/types.h>
8 #include <linux/errno.h>
9 #include <linux/string.h>
10 #include <linux/mutex.h>
11 #include <linux/jiffies.h>
12 #include <linux/sched.h>
13 #include <linux/sched/signal.h>
14 #include <linux/io.h>
15 #include <linux/pci.h>
16 #include <linux/etherdevice.h>
17 
18 #include "octep_ctrl_mbox.h"
19 #include "octep_config.h"
20 #include "octep_main.h"
21 
22 /* Timeout in msecs for message response */
23 #define OCTEP_CTRL_MBOX_MSG_TIMEOUT_MS			100
24 /* Time in msecs to wait for message response */
25 #define OCTEP_CTRL_MBOX_MSG_WAIT_MS			10
26 
27 #define OCTEP_CTRL_MBOX_INFO_MAGIC_NUM_OFFSET(m)	(m)
28 #define OCTEP_CTRL_MBOX_INFO_BARMEM_SZ_OFFSET(m)	((m) + 8)
29 #define OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(m)	((m) + 24)
30 #define OCTEP_CTRL_MBOX_INFO_FW_STATUS_OFFSET(m)	((m) + 144)
31 
32 #define OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)		((m) + OCTEP_CTRL_MBOX_INFO_SZ)
33 #define OCTEP_CTRL_MBOX_H2FQ_PROD_OFFSET(m)		(OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m))
34 #define OCTEP_CTRL_MBOX_H2FQ_CONS_OFFSET(m)		((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 4)
35 #define OCTEP_CTRL_MBOX_H2FQ_ELEM_SZ_OFFSET(m)		((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 8)
36 #define OCTEP_CTRL_MBOX_H2FQ_ELEM_CNT_OFFSET(m)		((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 12)
37 
38 #define OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)		((m) + \
39 							 OCTEP_CTRL_MBOX_INFO_SZ + \
40 							 OCTEP_CTRL_MBOX_H2FQ_INFO_SZ)
41 #define OCTEP_CTRL_MBOX_F2HQ_PROD_OFFSET(m)		(OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m))
42 #define OCTEP_CTRL_MBOX_F2HQ_CONS_OFFSET(m)		((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 4)
43 #define OCTEP_CTRL_MBOX_F2HQ_ELEM_SZ_OFFSET(m)		((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 8)
44 #define OCTEP_CTRL_MBOX_F2HQ_ELEM_CNT_OFFSET(m)		((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 12)
45 
46 #define OCTEP_CTRL_MBOX_Q_OFFSET(m, i)			((m) + \
47 							 (sizeof(struct octep_ctrl_mbox_msg) * (i)))
48 
octep_ctrl_mbox_circq_inc(u32 index,u32 mask)49 static u32 octep_ctrl_mbox_circq_inc(u32 index, u32 mask)
50 {
51 	return (index + 1) & mask;
52 }
53 
octep_ctrl_mbox_circq_space(u32 pi,u32 ci,u32 mask)54 static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 mask)
55 {
56 	return mask - ((pi - ci) & mask);
57 }
58 
octep_ctrl_mbox_circq_depth(u32 pi,u32 ci,u32 mask)59 static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 mask)
60 {
61 	return ((pi - ci) & mask);
62 }
63 
octep_ctrl_mbox_init(struct octep_ctrl_mbox * mbox)64 int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox)
65 {
66 	u64 magic_num, status;
67 
68 	if (!mbox)
69 		return -EINVAL;
70 
71 	if (!mbox->barmem) {
72 		pr_info("octep_ctrl_mbox : Invalid barmem %p\n", mbox->barmem);
73 		return -EINVAL;
74 	}
75 
76 	magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM_OFFSET(mbox->barmem));
77 	if (magic_num != OCTEP_CTRL_MBOX_MAGIC_NUMBER) {
78 		pr_info("octep_ctrl_mbox : Invalid magic number %llx\n", magic_num);
79 		return -EINVAL;
80 	}
81 
82 	status = readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS_OFFSET(mbox->barmem));
83 	if (status != OCTEP_CTRL_MBOX_STATUS_READY) {
84 		pr_info("octep_ctrl_mbox : Firmware is not ready.\n");
85 		return -EINVAL;
86 	}
87 
88 	mbox->barmem_sz = readl(OCTEP_CTRL_MBOX_INFO_BARMEM_SZ_OFFSET(mbox->barmem));
89 
90 	writeq(OCTEP_CTRL_MBOX_STATUS_INIT, OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
91 
92 	mbox->h2fq.elem_cnt = readl(OCTEP_CTRL_MBOX_H2FQ_ELEM_CNT_OFFSET(mbox->barmem));
93 	mbox->h2fq.elem_sz = readl(OCTEP_CTRL_MBOX_H2FQ_ELEM_SZ_OFFSET(mbox->barmem));
94 	mbox->h2fq.mask = (mbox->h2fq.elem_cnt - 1);
95 	mutex_init(&mbox->h2fq_lock);
96 
97 	mbox->f2hq.elem_cnt = readl(OCTEP_CTRL_MBOX_F2HQ_ELEM_CNT_OFFSET(mbox->barmem));
98 	mbox->f2hq.elem_sz = readl(OCTEP_CTRL_MBOX_F2HQ_ELEM_SZ_OFFSET(mbox->barmem));
99 	mbox->f2hq.mask = (mbox->f2hq.elem_cnt - 1);
100 	mutex_init(&mbox->f2hq_lock);
101 
102 	mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD_OFFSET(mbox->barmem);
103 	mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS_OFFSET(mbox->barmem);
104 	mbox->h2fq.hw_q = mbox->barmem +
105 			  OCTEP_CTRL_MBOX_INFO_SZ +
106 			  OCTEP_CTRL_MBOX_H2FQ_INFO_SZ +
107 			  OCTEP_CTRL_MBOX_F2HQ_INFO_SZ;
108 
109 	mbox->f2hq.hw_prod = OCTEP_CTRL_MBOX_F2HQ_PROD_OFFSET(mbox->barmem);
110 	mbox->f2hq.hw_cons = OCTEP_CTRL_MBOX_F2HQ_CONS_OFFSET(mbox->barmem);
111 	mbox->f2hq.hw_q = mbox->h2fq.hw_q +
112 			  ((mbox->h2fq.elem_sz + sizeof(union octep_ctrl_mbox_msg_hdr)) *
113 			   mbox->h2fq.elem_cnt);
114 
115 	/* ensure ready state is seen after everything is initialized */
116 	wmb();
117 	writeq(OCTEP_CTRL_MBOX_STATUS_READY, OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
118 
119 	pr_info("Octep ctrl mbox : Init successful.\n");
120 
121 	return 0;
122 }
123 
octep_ctrl_mbox_send(struct octep_ctrl_mbox * mbox,struct octep_ctrl_mbox_msg * msg)124 int octep_ctrl_mbox_send(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
125 {
126 	unsigned long timeout = msecs_to_jiffies(OCTEP_CTRL_MBOX_MSG_TIMEOUT_MS);
127 	unsigned long period = msecs_to_jiffies(OCTEP_CTRL_MBOX_MSG_WAIT_MS);
128 	struct octep_ctrl_mbox_q *q;
129 	unsigned long expire;
130 	u64 *mbuf, *word0;
131 	u8 __iomem *qidx;
132 	u16 pi, ci;
133 	int i;
134 
135 	if (!mbox || !msg)
136 		return -EINVAL;
137 
138 	q = &mbox->h2fq;
139 	pi = readl(q->hw_prod);
140 	ci = readl(q->hw_cons);
141 
142 	if (!octep_ctrl_mbox_circq_space(pi, ci, q->mask))
143 		return -ENOMEM;
144 
145 	qidx = OCTEP_CTRL_MBOX_Q_OFFSET(q->hw_q, pi);
146 	mbuf = (u64 *)msg->msg;
147 	word0 = &msg->hdr.word0;
148 
149 	mutex_lock(&mbox->h2fq_lock);
150 	for (i = 1; i <= msg->hdr.sizew; i++)
151 		writeq(*mbuf++, (qidx + (i * 8)));
152 
153 	writeq(*word0, qidx);
154 
155 	pi = octep_ctrl_mbox_circq_inc(pi, q->mask);
156 	writel(pi, q->hw_prod);
157 	mutex_unlock(&mbox->h2fq_lock);
158 
159 	/* don't check for notification response */
160 	if (msg->hdr.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_NOTIFY)
161 		return 0;
162 
163 	expire = jiffies + timeout;
164 	while (true) {
165 		*word0 = readq(qidx);
166 		if (msg->hdr.flags == OCTEP_CTRL_MBOX_MSG_HDR_FLAG_RESP)
167 			break;
168 		schedule_timeout_interruptible(period);
169 		if (signal_pending(current) || time_after(jiffies, expire)) {
170 			pr_info("octep_ctrl_mbox: Timed out\n");
171 			return -EBUSY;
172 		}
173 	}
174 	mbuf = (u64 *)msg->msg;
175 	for (i = 1; i <= msg->hdr.sizew; i++)
176 		*mbuf++ = readq(qidx + (i * 8));
177 
178 	return 0;
179 }
180 
octep_ctrl_mbox_recv(struct octep_ctrl_mbox * mbox,struct octep_ctrl_mbox_msg * msg)181 int octep_ctrl_mbox_recv(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
182 {
183 	struct octep_ctrl_mbox_q *q;
184 	u32 count, pi, ci;
185 	u8 __iomem *qidx;
186 	u64 *mbuf;
187 	int i;
188 
189 	if (!mbox || !msg)
190 		return -EINVAL;
191 
192 	q = &mbox->f2hq;
193 	pi = readl(q->hw_prod);
194 	ci = readl(q->hw_cons);
195 	count = octep_ctrl_mbox_circq_depth(pi, ci, q->mask);
196 	if (!count)
197 		return -EAGAIN;
198 
199 	qidx = OCTEP_CTRL_MBOX_Q_OFFSET(q->hw_q, ci);
200 	mbuf = (u64 *)msg->msg;
201 
202 	mutex_lock(&mbox->f2hq_lock);
203 
204 	msg->hdr.word0 = readq(qidx);
205 	for (i = 1; i <= msg->hdr.sizew; i++)
206 		*mbuf++ = readq(qidx + (i * 8));
207 
208 	ci = octep_ctrl_mbox_circq_inc(ci, q->mask);
209 	writel(ci, q->hw_cons);
210 
211 	mutex_unlock(&mbox->f2hq_lock);
212 
213 	if (msg->hdr.flags != OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ || !mbox->process_req)
214 		return 0;
215 
216 	mbox->process_req(mbox->user_ctx, msg);
217 	mbuf = (u64 *)msg->msg;
218 	for (i = 1; i <= msg->hdr.sizew; i++)
219 		writeq(*mbuf++, (qidx + (i * 8)));
220 
221 	writeq(msg->hdr.word0, qidx);
222 
223 	return 0;
224 }
225 
octep_ctrl_mbox_uninit(struct octep_ctrl_mbox * mbox)226 int octep_ctrl_mbox_uninit(struct octep_ctrl_mbox *mbox)
227 {
228 	if (!mbox)
229 		return -EINVAL;
230 
231 	writeq(OCTEP_CTRL_MBOX_STATUS_UNINIT,
232 	       OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
233 	/* ensure uninit state is written before uninitialization */
234 	wmb();
235 
236 	mutex_destroy(&mbox->h2fq_lock);
237 	mutex_destroy(&mbox->f2hq_lock);
238 
239 	writeq(OCTEP_CTRL_MBOX_STATUS_INVALID,
240 	       OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
241 
242 	pr_info("Octep ctrl mbox : Uninit successful.\n");
243 
244 	return 0;
245 }
246