1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2023 Yandex LLC
5 * Copyright (c) 2023 Andrey V. Elsukov <ae@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/bus.h>
32#include <sys/condvar.h>
33#include <sys/eventhandler.h>
34#include <sys/kernel.h>
35#include <sys/kthread.h>
36#include <sys/module.h>
37#include <sys/rman.h>
38#include <sys/selinfo.h>
39#include <machine/bus.h>
40
41#include <sys/ipmi.h>
42#include <dev/ipmi/ipmivars.h>
43
44/*
45 * BT interface
46 */
47
48#define	DMSG0(sc, fmt, ...)	do {				\
49	device_printf((sc)->ipmi_dev, "BT: %s: " fmt "\n",	\
50	    __func__, ## __VA_ARGS__);				\
51} while (0)
52
53#define	DMSGV(...)		if (bootverbose) {		\
54	DMSG0(__VA_ARGS__);					\
55}
56
57#ifdef IPMI_BT_DEBUG
58#define	DMSG(...)		DMSG0(__VA_ARGS__)
59#else
60#define	DMSG(...)
61#endif
62
63#define	BT_IO_BASE		0xe4
64
65#define	BT_CTRL_REG		0
66#define	  BT_C_CLR_WR_PTR	(1L << 0)
67#define	  BT_C_CLR_RD_PTR	(1L << 1)
68#define	  BT_C_H2B_ATN		(1L << 2)
69#define	  BT_C_B2H_ATN		(1L << 3)
70#define	  BT_C_SMS_ATN		(1L << 4)
71#define	  BT_C_OEM0		(1L << 5)
72#define	  BT_C_H_BUSY		(1L << 6)
73#define	  BT_C_B_BUSY		(1L << 7)
74
75#define	BT_CTRL_BITS		"\20\01CLR_WR_PTR\02CLR_RD_PTR\03H2B_ATN\04B2H_ATN"\
76				"\05SMS_ATN\06OEM0\07H_BUSY\010B_BUSY"
77
78#define	BT_DATA_REG		1
79#define	 BTMSG_REQLEN		3
80#define	 BTMSG_REPLEN		4
81
82#define	BT_INTMASK_REG		2
83#define	 BT_IM_B2H_IRQ_EN	(1L << 0)
84#define	 BT_IM_B2H_IRQ		(1L << 1)
85#define	 BT_IM_BMC_HWRST	(1L << 7)
86
87static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
88static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *, int);
89static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
90static int bt_reset(struct ipmi_softc *);
91
92static void bt_loop(void *);
93static int bt_startup(struct ipmi_softc *);
94
95#define	BT_DELAY_MIN	1
96#define	BT_DELAY_MAX	256
97
98static int
99bt_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t wanted)
100{
101	volatile uint8_t value;
102	int delay = BT_DELAY_MIN;
103	int count = 20000; /* about 5 seconds */
104
105	while (count--) {
106		value = INB(sc, BT_CTRL_REG);
107		if ((value & mask) == wanted)
108			return (value);
109		/*
110		 * The wait delay is increased exponentially to avoid putting
111		 * significant load on I/O bus.
112		 */
113		DELAY(delay);
114		if (delay < BT_DELAY_MAX)
115			delay <<= 1;
116	}
117	DMSGV(sc, "failed: m=%b w=%b v=0x%02x\n",
118	    mask, BT_CTRL_BITS, wanted, BT_CTRL_BITS, value);
119	return (-1);
120
121}
122
123static int
124bt_reset(struct ipmi_softc *sc)
125{
126	uint8_t v;
127
128	v = INB(sc, BT_CTRL_REG);
129	DMSG(sc, "ctrl: %b", v, BT_CTRL_BITS);
130	v &= BT_C_H_BUSY; /* clear H_BUSY iff it set */
131	v |= BT_C_CLR_WR_PTR | BT_C_CLR_RD_PTR | BT_C_B2H_ATN | BT_C_H2B_ATN;
132
133	bt_wait(sc, BT_C_B_BUSY, 0);
134	OUTB(sc, BT_CTRL_REG, v);
135
136	v = BT_IM_B2H_IRQ | BT_IM_BMC_HWRST;
137	OUTB(sc, BT_INTMASK_REG, v);
138
139	return (0);
140}
141
142/*
143 * Send a request message and collect the reply. Returns 1 if we
144 * succeed.
145 */
146static int
147bt_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
148{
149	uint8_t addr, cmd, seq, v;
150	int i;
151
152	IPMI_IO_LOCK(sc);
153
154	/*
155	 * Send the request:
156	 *
157	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5:N
158	 * -------+-----------+--------+--------+---------
159	 * Length | NetFn/LUN | Seq    | Cmd    | Data
160	 */
161
162	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
163		DMSG(sc, "failed to start write transfer");
164		goto fail;
165	}
166	DMSG(sc, "request: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x",
167	    (int)req->ir_requestlen, req->ir_addr, sc->ipmi_bt_seq, req->ir_command);
168	OUTB(sc, BT_CTRL_REG, BT_C_CLR_WR_PTR);
169	OUTB(sc, BT_DATA_REG, req->ir_requestlen + BTMSG_REQLEN);
170	OUTB(sc, BT_DATA_REG, req->ir_addr);
171	OUTB(sc, BT_DATA_REG, sc->ipmi_bt_seq);
172	OUTB(sc, BT_DATA_REG, req->ir_command);
173	for (i = 0; i < req->ir_requestlen; i++)
174		OUTB(sc, BT_DATA_REG, req->ir_request[i]);
175	OUTB(sc, BT_CTRL_REG, BT_C_H2B_ATN);
176
177	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
178		DMSG(sc, "failed to finish write transfer");
179		goto fail;
180	}
181
182	/*
183	 * Read the reply:
184	 *
185	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5          | Byte 6:N
186	 * -------+-----------+--------+--------+-----------------+---------
187	 * Length | NetFn/LUN | Seq    | Cmd    | Completion code | Data
188	 */
189	if (bt_wait(sc, BT_C_B2H_ATN, BT_C_B2H_ATN) < 0) {
190		DMSG(sc, "got no reply from BMC");
191		goto fail;
192	}
193	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
194	OUTB(sc, BT_CTRL_REG, BT_C_B2H_ATN);
195	OUTB(sc, BT_CTRL_REG, BT_C_CLR_RD_PTR);
196
197	i = INB(sc, BT_DATA_REG);
198	if (i < BTMSG_REPLEN) {
199		DMSG(sc, "wrong data length: %d", i);
200		goto fail;
201	}
202	req->ir_replylen = i - BTMSG_REPLEN;
203	DMSG(sc, "data length: %d, frame length: %d", req->ir_replylen, i);
204
205	addr = INB(sc, BT_DATA_REG);
206	if (addr != IPMI_REPLY_ADDR(req->ir_addr)) {
207		DMSGV(sc, "address doesn't match: addr=0x%02x vs. 0x%02x",
208		    req->ir_addr, addr);
209	}
210
211	seq = INB(sc, BT_DATA_REG);
212	if (seq != sc->ipmi_bt_seq) {
213		DMSGV(sc, "seq number doesn't match: seq=0x%02x vs. 0x%02x",
214		    sc->ipmi_bt_seq, seq);
215	}
216
217	cmd = INB(sc, BT_DATA_REG);
218	if (cmd != req->ir_command) {
219		DMSGV(sc, "command doesn't match: cmd=0x%02x vs. 0x%02x",
220		    req->ir_command, cmd);
221	}
222
223	req->ir_compcode = INB(sc, BT_DATA_REG);
224	for (i = 0; i < req->ir_replylen; i++) {
225		v = INB(sc, BT_DATA_REG);
226		if (i < req->ir_replybuflen)
227			req->ir_reply[i] = v;
228	}
229
230	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
231	IPMI_IO_UNLOCK(sc);
232	DMSG(sc, "reply: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x, code=0x%02x",
233	    (int)req->ir_replylen, addr, seq, req->ir_command, req->ir_compcode);
234	return (1);
235fail:
236	bt_reset(sc);
237	IPMI_IO_UNLOCK(sc);
238	return (0);
239}
240
241static void
242bt_loop(void *arg)
243{
244	struct ipmi_softc *sc = arg;
245	struct ipmi_request *req;
246
247	IPMI_LOCK(sc);
248	while ((req = ipmi_dequeue_request(sc)) != NULL) {
249		IPMI_UNLOCK(sc);
250		(void)bt_driver_request(sc, req, 0);
251		IPMI_LOCK(sc);
252		sc->ipmi_bt_seq++;
253		ipmi_complete_request(sc, req);
254	}
255	IPMI_UNLOCK(sc);
256	kproc_exit(0);
257}
258
259static int
260bt_startup(struct ipmi_softc *sc)
261{
262
263	return (kproc_create(bt_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: bt",
264	    device_get_nameunit(sc->ipmi_dev)));
265}
266
267static int
268bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo __unused)
269{
270	int i, ok;
271
272	ok = 0;
273	for (i = 0; i < 3 && !ok; i++)
274		ok = bt_polled_request(sc, req);
275	if (ok)
276		req->ir_error = 0;
277	else
278		req->ir_error = EIO;
279	return (req->ir_error);
280}
281
282int
283ipmi_bt_attach(struct ipmi_softc *sc)
284{
285	/* Setup function pointers. */
286	sc->ipmi_startup = bt_startup;
287	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
288	sc->ipmi_driver_request = bt_driver_request;
289	sc->ipmi_driver_requests_polled = 1;
290	sc->ipmi_bt_seq = 1;
291
292	return (bt_reset(sc));
293}
294