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