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
87 static int bt_polled_request(struct ipmi_softc *, struct ipmi_request *);
88 static int bt_driver_request(struct ipmi_softc *, struct ipmi_request *);
89 static int bt_wait(struct ipmi_softc *, uint8_t, uint8_t);
90 static int bt_reset(struct ipmi_softc *);
91
92 static void bt_loop(void *);
93 static int bt_startup(struct ipmi_softc *);
94
95 #define BT_DELAY_MIN 1
96 #define BT_DELAY_MAX 256
97
98 static int
bt_wait(struct ipmi_softc * sc,uint8_t mask,uint8_t wanted)99 bt_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
123 static int
bt_reset(struct ipmi_softc * sc)124 bt_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 */
146 static int
bt_polled_request(struct ipmi_softc * sc,struct ipmi_request * req)147 bt_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);
235 fail:
236 bt_reset(sc);
237 IPMI_IO_UNLOCK(sc);
238 return (0);
239 }
240
241 static void
bt_loop(void * arg)242 bt_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);
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
259 static int
bt_startup(struct ipmi_softc * sc)260 bt_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
267 static int
bt_driver_request(struct ipmi_softc * sc,struct ipmi_request * req)268 bt_driver_request(struct ipmi_softc *sc, struct ipmi_request *req)
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
282 int
ipmi_bt_attach(struct ipmi_softc * sc)283 ipmi_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