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 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 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 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 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 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 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 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