xref: /freebsd/sys/dev/ipmi/ipmi_bt.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
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/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/condvar.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/kthread.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/selinfo.h>
40 #include <machine/bus.h>
41 
42 #include <sys/ipmi.h>
43 #include <dev/ipmi/ipmivars.h>
44 
45 /*
46  * BT interface
47  */
48 
49 #define	DMSG0(sc, fmt, ...)	do {				\
50 	device_printf((sc)->ipmi_dev, "BT: %s: " fmt "\n",	\
51 	    __func__, ## __VA_ARGS__);				\
52 } while (0)
53 
54 #define	DMSGV(...)		if (bootverbose) {		\
55 	DMSG0(__VA_ARGS__);					\
56 }
57 
58 #ifdef IPMI_BT_DEBUG
59 #define	DMSG(...)		DMSG0(__VA_ARGS__)
60 #else
61 #define	DMSG(...)
62 #endif
63 
64 #define	BT_IO_BASE		0xe4
65 
66 #define	BT_CTRL_REG		0
67 #define	  BT_C_CLR_WR_PTR	(1L << 0)
68 #define	  BT_C_CLR_RD_PTR	(1L << 1)
69 #define	  BT_C_H2B_ATN		(1L << 2)
70 #define	  BT_C_B2H_ATN		(1L << 3)
71 #define	  BT_C_SMS_ATN		(1L << 4)
72 #define	  BT_C_OEM0		(1L << 5)
73 #define	  BT_C_H_BUSY		(1L << 6)
74 #define	  BT_C_B_BUSY		(1L << 7)
75 
76 #define	BT_CTRL_BITS		"\20\01CLR_WR_PTR\02CLR_RD_PTR\03H2B_ATN\04B2H_ATN"\
77 				"\05SMS_ATN\06OEM0\07H_BUSY\010B_BUSY"
78 
79 #define	BT_DATA_REG		1
80 #define	 BTMSG_REQLEN		3
81 #define	 BTMSG_REPLEN		4
82 
83 #define	BT_INTMASK_REG		2
84 #define	 BT_IM_B2H_IRQ_EN	(1L << 0)
85 #define	 BT_IM_B2H_IRQ		(1L << 1)
86 #define	 BT_IM_BMC_HWRST	(1L << 7)
87 
88 static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
89 static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *, int);
90 static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
91 static int bt_reset(struct ipmi_softc *);
92 
93 static void bt_loop(void *);
94 static int bt_startup(struct ipmi_softc *);
95 
96 #define	BT_DELAY_MIN	1
97 #define	BT_DELAY_MAX	256
98 
99 static int
100 bt_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t wanted)
101 {
102 	volatile uint8_t value;
103 	int delay = BT_DELAY_MIN;
104 	int count = 20000; /* about 5 seconds */
105 
106 	while (count--) {
107 		value = INB(sc, BT_CTRL_REG);
108 		if ((value & mask) == wanted)
109 			return (value);
110 		/*
111 		 * The wait delay is increased exponentially to avoid putting
112 		 * significant load on I/O bus.
113 		 */
114 		DELAY(delay);
115 		if (delay < BT_DELAY_MAX)
116 			delay <<= 1;
117 	}
118 	DMSGV(sc, "failed: m=%b w=%b v=0x%02x\n",
119 	    mask, BT_CTRL_BITS, wanted, BT_CTRL_BITS, value);
120 	return (-1);
121 
122 }
123 
124 static int
125 bt_reset(struct ipmi_softc *sc)
126 {
127 	uint8_t v;
128 
129 	v = INB(sc, BT_CTRL_REG);
130 	DMSG(sc, "ctrl: %b", v, BT_CTRL_BITS);
131 	v &= BT_C_H_BUSY; /* clear H_BUSY iff it set */
132 	v |= BT_C_CLR_WR_PTR | BT_C_CLR_RD_PTR | BT_C_B2H_ATN | BT_C_H2B_ATN;
133 
134 	bt_wait(sc, BT_C_B_BUSY, 0);
135 	OUTB(sc, BT_CTRL_REG, v);
136 
137 	v = BT_IM_B2H_IRQ | BT_IM_BMC_HWRST;
138 	OUTB(sc, BT_INTMASK_REG, v);
139 
140 	return (0);
141 }
142 
143 /*
144  * Send a request message and collect the reply. Returns 1 if we
145  * succeed.
146  */
147 static int
148 bt_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
149 {
150 	uint8_t addr, cmd, seq, v;
151 	int i;
152 
153 	IPMI_IO_LOCK(sc);
154 
155 	/*
156 	 * Send the request:
157 	 *
158 	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5:N
159 	 * -------+-----------+--------+--------+---------
160 	 * Length | NetFn/LUN | Seq    | Cmd    | Data
161 	 */
162 
163 	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
164 		DMSG(sc, "failed to start write transfer");
165 		goto fail;
166 	}
167 	DMSG(sc, "request: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x",
168 	    (int)req->ir_requestlen, req->ir_addr, sc->ipmi_bt_seq, req->ir_command);
169 	OUTB(sc, BT_CTRL_REG, BT_C_CLR_WR_PTR);
170 	OUTB(sc, BT_DATA_REG, req->ir_requestlen + BTMSG_REQLEN);
171 	OUTB(sc, BT_DATA_REG, req->ir_addr);
172 	OUTB(sc, BT_DATA_REG, sc->ipmi_bt_seq);
173 	OUTB(sc, BT_DATA_REG, req->ir_command);
174 	for (i = 0; i < req->ir_requestlen; i++)
175 		OUTB(sc, BT_DATA_REG, req->ir_request[i]);
176 	OUTB(sc, BT_CTRL_REG, BT_C_H2B_ATN);
177 
178 	if (bt_wait(sc, BT_C_B_BUSY | BT_C_H2B_ATN, 0) < 0) {
179 		DMSG(sc, "failed to finish write transfer");
180 		goto fail;
181 	}
182 
183 	/*
184 	 * Read the reply:
185 	 *
186 	 * Byte 1 | Byte 2    | Byte 3 | Byte 4 | Byte 5          | Byte 6:N
187 	 * -------+-----------+--------+--------+-----------------+---------
188 	 * Length | NetFn/LUN | Seq    | Cmd    | Completion code | Data
189 	 */
190 	if (bt_wait(sc, BT_C_B2H_ATN, BT_C_B2H_ATN) < 0) {
191 		DMSG(sc, "got no reply from BMC");
192 		goto fail;
193 	}
194 	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
195 	OUTB(sc, BT_CTRL_REG, BT_C_B2H_ATN);
196 	OUTB(sc, BT_CTRL_REG, BT_C_CLR_RD_PTR);
197 
198 	i = INB(sc, BT_DATA_REG);
199 	if (i < BTMSG_REPLEN) {
200 		DMSG(sc, "wrong data length: %d", i);
201 		goto fail;
202 	}
203 	req->ir_replylen = i - BTMSG_REPLEN;
204 	DMSG(sc, "data length: %d, frame length: %d", req->ir_replylen, i);
205 
206 	addr = INB(sc, BT_DATA_REG);
207 	if (addr != IPMI_REPLY_ADDR(req->ir_addr)) {
208 		DMSGV(sc, "address doesn't match: addr=0x%02x vs. 0x%02x",
209 		    req->ir_addr, addr);
210 	}
211 
212 	seq = INB(sc, BT_DATA_REG);
213 	if (seq != sc->ipmi_bt_seq) {
214 		DMSGV(sc, "seq number doesn't match: seq=0x%02x vs. 0x%02x",
215 		    sc->ipmi_bt_seq, seq);
216 	}
217 
218 	cmd = INB(sc, BT_DATA_REG);
219 	if (cmd != req->ir_command) {
220 		DMSGV(sc, "command doesn't match: cmd=0x%02x vs. 0x%02x",
221 		    req->ir_command, cmd);
222 	}
223 
224 	req->ir_compcode = INB(sc, BT_DATA_REG);
225 	for (i = 0; i < req->ir_replylen; i++) {
226 		v = INB(sc, BT_DATA_REG);
227 		if (i < req->ir_replybuflen)
228 			req->ir_reply[i] = v;
229 	}
230 
231 	OUTB(sc, BT_CTRL_REG, BT_C_H_BUSY);
232 	IPMI_IO_UNLOCK(sc);
233 	DMSG(sc, "reply: length=%d, addr=0x%02x, seq=%u, cmd=0x%02x, code=0x%02x",
234 	    (int)req->ir_replylen, addr, seq, req->ir_command, req->ir_compcode);
235 	return (1);
236 fail:
237 	bt_reset(sc);
238 	IPMI_IO_UNLOCK(sc);
239 	return (0);
240 }
241 
242 static void
243 bt_loop(void *arg)
244 {
245 	struct ipmi_softc *sc = arg;
246 	struct ipmi_request *req;
247 
248 	IPMI_LOCK(sc);
249 	while ((req = ipmi_dequeue_request(sc)) != NULL) {
250 		IPMI_UNLOCK(sc);
251 		(void)bt_driver_request(sc, req, 0);
252 		IPMI_LOCK(sc);
253 		sc->ipmi_bt_seq++;
254 		ipmi_complete_request(sc, req);
255 	}
256 	IPMI_UNLOCK(sc);
257 	kproc_exit(0);
258 }
259 
260 static int
261 bt_startup(struct ipmi_softc *sc)
262 {
263 
264 	return (kproc_create(bt_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: bt",
265 	    device_get_nameunit(sc->ipmi_dev)));
266 }
267 
268 static int
269 bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo __unused)
270 {
271 	int i, ok;
272 
273 	ok = 0;
274 	for (i = 0; i < 3 && !ok; i++)
275 		ok = bt_polled_request(sc, req);
276 	if (ok)
277 		req->ir_error = 0;
278 	else
279 		req->ir_error = EIO;
280 	return (req->ir_error);
281 }
282 
283 int
284 ipmi_bt_attach(struct ipmi_softc *sc)
285 {
286 	/* Setup function pointers. */
287 	sc->ipmi_startup = bt_startup;
288 	sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
289 	sc->ipmi_driver_request = bt_driver_request;
290 	sc->ipmi_driver_requests_polled = 1;
291 	sc->ipmi_bt_seq = 1;
292 
293 	return (bt_reset(sc));
294 }
295