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