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/* Size of mbox info in bytes */
28#define OCTEP_CTRL_MBOX_INFO_SZ				256
29/* Size of mbox host to fw queue info in bytes */
30#define OCTEP_CTRL_MBOX_H2FQ_INFO_SZ			16
31/* Size of mbox fw to host queue info in bytes */
32#define OCTEP_CTRL_MBOX_F2HQ_INFO_SZ			16
33
34#define OCTEP_CTRL_MBOX_TOTAL_INFO_SZ	(OCTEP_CTRL_MBOX_INFO_SZ + \
35					 OCTEP_CTRL_MBOX_H2FQ_INFO_SZ + \
36					 OCTEP_CTRL_MBOX_F2HQ_INFO_SZ)
37
38#define OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(m)	(m)
39#define OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(m)	((m) + 8)
40#define OCTEP_CTRL_MBOX_INFO_HOST_VERSION(m)   ((m) + 16)
41#define OCTEP_CTRL_MBOX_INFO_HOST_STATUS(m)	((m) + 24)
42#define OCTEP_CTRL_MBOX_INFO_FW_VERSION(m)     ((m) + 136)
43#define OCTEP_CTRL_MBOX_INFO_FW_STATUS(m)	((m) + 144)
44
45#define OCTEP_CTRL_MBOX_H2FQ_INFO(m)	((m) + OCTEP_CTRL_MBOX_INFO_SZ)
46#define OCTEP_CTRL_MBOX_H2FQ_PROD(m)	(OCTEP_CTRL_MBOX_H2FQ_INFO(m))
47#define OCTEP_CTRL_MBOX_H2FQ_CONS(m)	((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 4)
48#define OCTEP_CTRL_MBOX_H2FQ_SZ(m)	((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 8)
49
50#define OCTEP_CTRL_MBOX_F2HQ_INFO(m)	((m) + \
51					 OCTEP_CTRL_MBOX_INFO_SZ + \
52					 OCTEP_CTRL_MBOX_H2FQ_INFO_SZ)
53#define OCTEP_CTRL_MBOX_F2HQ_PROD(m)	(OCTEP_CTRL_MBOX_F2HQ_INFO(m))
54#define OCTEP_CTRL_MBOX_F2HQ_CONS(m)	((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 4)
55#define OCTEP_CTRL_MBOX_F2HQ_SZ(m)	((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 8)
56
57static const u32 mbox_hdr_sz = sizeof(union octep_ctrl_mbox_msg_hdr);
58
59static u32 octep_ctrl_mbox_circq_inc(u32 index, u32 inc, u32 sz)
60{
61	return (index + inc) % sz;
62}
63
64static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 sz)
65{
66	return sz - (abs(pi - ci) % sz);
67}
68
69static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 sz)
70{
71	return (abs(pi - ci) % sz);
72}
73
74int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox)
75{
76	u64 magic_num, status, fw_versions;
77
78	if (!mbox)
79		return -EINVAL;
80
81	if (!mbox->barmem) {
82		pr_info("octep_ctrl_mbox : Invalid barmem %p\n", mbox->barmem);
83		return -EINVAL;
84	}
85
86	magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(mbox->barmem));
87	if (magic_num != OCTEP_CTRL_MBOX_MAGIC_NUMBER) {
88		pr_info("octep_ctrl_mbox : Invalid magic number %llx\n", magic_num);
89		return -EINVAL;
90	}
91
92	status = readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem));
93	if (status != OCTEP_CTRL_MBOX_STATUS_READY) {
94		pr_info("octep_ctrl_mbox : Firmware is not ready.\n");
95		return -EINVAL;
96	}
97
98	fw_versions = readq(OCTEP_CTRL_MBOX_INFO_FW_VERSION(mbox->barmem));
99	mbox->min_fw_version = ((fw_versions & 0xffffffff00000000ull) >> 32);
100	mbox->max_fw_version = (fw_versions & 0xffffffff);
101	mbox->barmem_sz = readl(OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(mbox->barmem));
102
103	writeq(OCTEP_CTRL_MBOX_STATUS_INIT,
104	       OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem));
105
106	mutex_init(&mbox->h2fq_lock);
107	mutex_init(&mbox->f2hq_lock);
108
109	mbox->h2fq.sz = readl(OCTEP_CTRL_MBOX_H2FQ_SZ(mbox->barmem));
110	mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD(mbox->barmem);
111	mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS(mbox->barmem);
112	mbox->h2fq.hw_q = mbox->barmem + OCTEP_CTRL_MBOX_TOTAL_INFO_SZ;
113
114	mbox->f2hq.sz = readl(OCTEP_CTRL_MBOX_F2HQ_SZ(mbox->barmem));
115	mbox->f2hq.hw_prod = OCTEP_CTRL_MBOX_F2HQ_PROD(mbox->barmem);
116	mbox->f2hq.hw_cons = OCTEP_CTRL_MBOX_F2HQ_CONS(mbox->barmem);
117	mbox->f2hq.hw_q = mbox->barmem +
118			  OCTEP_CTRL_MBOX_TOTAL_INFO_SZ +
119			  mbox->h2fq.sz;
120
121	writeq(mbox->version, OCTEP_CTRL_MBOX_INFO_HOST_VERSION(mbox->barmem));
122	/* ensure ready state is seen after everything is initialized */
123	wmb();
124	writeq(OCTEP_CTRL_MBOX_STATUS_READY,
125	       OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem));
126
127	pr_info("Octep ctrl mbox : Init successful.\n");
128
129	return 0;
130}
131
132static void
133octep_write_mbox_data(struct octep_ctrl_mbox_q *q, u32 *pi, u32 ci, void *buf, u32 w_sz)
134{
135	u8 __iomem *qbuf;
136	u32 cp_sz;
137
138	/* Assumption: Caller has ensured enough write space */
139	qbuf = (q->hw_q + *pi);
140	if (*pi < ci) {
141		/* copy entire w_sz */
142		memcpy_toio(qbuf, buf, w_sz);
143		*pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz);
144	} else {
145		/* copy up to end of queue */
146		cp_sz = min((q->sz - *pi), w_sz);
147		memcpy_toio(qbuf, buf, cp_sz);
148		w_sz -= cp_sz;
149		*pi = octep_ctrl_mbox_circq_inc(*pi, cp_sz, q->sz);
150		if (w_sz) {
151			/* roll over and copy remaining w_sz */
152			buf += cp_sz;
153			qbuf = (q->hw_q + *pi);
154			memcpy_toio(qbuf, buf, w_sz);
155			*pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz);
156		}
157	}
158}
159
160int octep_ctrl_mbox_send(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
161{
162	struct octep_ctrl_mbox_msg_buf *sg;
163	struct octep_ctrl_mbox_q *q;
164	u32 pi, ci, buf_sz, w_sz;
165	int s;
166
167	if (!mbox || !msg)
168		return -EINVAL;
169
170	if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY)
171		return -EIO;
172
173	mutex_lock(&mbox->h2fq_lock);
174	q = &mbox->h2fq;
175	pi = readl(q->hw_prod);
176	ci = readl(q->hw_cons);
177
178	if (octep_ctrl_mbox_circq_space(pi, ci, q->sz) < (msg->hdr.s.sz + mbox_hdr_sz)) {
179		mutex_unlock(&mbox->h2fq_lock);
180		return -EAGAIN;
181	}
182
183	octep_write_mbox_data(q, &pi, ci, (void *)&msg->hdr, mbox_hdr_sz);
184	buf_sz = msg->hdr.s.sz;
185	for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) {
186		sg = &msg->sg_list[s];
187		w_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz;
188		octep_write_mbox_data(q, &pi, ci, sg->msg, w_sz);
189		buf_sz -= w_sz;
190	}
191	writel(pi, q->hw_prod);
192	mutex_unlock(&mbox->h2fq_lock);
193
194	return 0;
195}
196
197static void
198octep_read_mbox_data(struct octep_ctrl_mbox_q *q, u32 pi, u32 *ci, void *buf, u32 r_sz)
199{
200	u8 __iomem *qbuf;
201	u32 cp_sz;
202
203	/* Assumption: Caller has ensured enough read space */
204	qbuf = (q->hw_q + *ci);
205	if (*ci < pi) {
206		/* copy entire r_sz */
207		memcpy_fromio(buf, qbuf, r_sz);
208		*ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz);
209	} else {
210		/* copy up to end of queue */
211		cp_sz = min((q->sz - *ci), r_sz);
212		memcpy_fromio(buf, qbuf, cp_sz);
213		r_sz -= cp_sz;
214		*ci = octep_ctrl_mbox_circq_inc(*ci, cp_sz, q->sz);
215		if (r_sz) {
216			/* roll over and copy remaining r_sz */
217			buf += cp_sz;
218			qbuf = (q->hw_q + *ci);
219			memcpy_fromio(buf, qbuf, r_sz);
220			*ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz);
221		}
222	}
223}
224
225int octep_ctrl_mbox_recv(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
226{
227	struct octep_ctrl_mbox_msg_buf *sg;
228	u32 pi, ci, r_sz, buf_sz, q_depth;
229	struct octep_ctrl_mbox_q *q;
230	int s;
231
232	if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY)
233		return -EIO;
234
235	mutex_lock(&mbox->f2hq_lock);
236	q = &mbox->f2hq;
237	pi = readl(q->hw_prod);
238	ci = readl(q->hw_cons);
239
240	q_depth = octep_ctrl_mbox_circq_depth(pi, ci, q->sz);
241	if (q_depth < mbox_hdr_sz) {
242		mutex_unlock(&mbox->f2hq_lock);
243		return -EAGAIN;
244	}
245
246	octep_read_mbox_data(q, pi, &ci, (void *)&msg->hdr, mbox_hdr_sz);
247	buf_sz = msg->hdr.s.sz;
248	for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) {
249		sg = &msg->sg_list[s];
250		r_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz;
251		octep_read_mbox_data(q, pi, &ci, sg->msg, r_sz);
252		buf_sz -= r_sz;
253	}
254	writel(ci, q->hw_cons);
255	mutex_unlock(&mbox->f2hq_lock);
256
257	return 0;
258}
259
260int octep_ctrl_mbox_uninit(struct octep_ctrl_mbox *mbox)
261{
262	if (!mbox)
263		return -EINVAL;
264	if (!mbox->barmem)
265		return -EINVAL;
266
267	writeq(0, OCTEP_CTRL_MBOX_INFO_HOST_VERSION(mbox->barmem));
268	writeq(OCTEP_CTRL_MBOX_STATUS_INVALID,
269	       OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem));
270	/* ensure uninit state is written before uninitialization */
271	wmb();
272
273	mutex_destroy(&mbox->h2fq_lock);
274	mutex_destroy(&mbox->f2hq_lock);
275
276	pr_info("Octep ctrl mbox : Uninit successful.\n");
277
278	return 0;
279}
280