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